Skip to content

Commit 2e12f63

Browse files
authored
Merge pull request #6726 from shin-core/fix/deployment-docs-reverse-audit-6601-v2
fix(miner): flag real LOOPOVER_MINER_* env vars missing from DEPLOYMENT.md (#6601)
2 parents 3588574 + b586810 commit 2e12f63

7 files changed

Lines changed: 87 additions & 1 deletion

packages/loopover-miner/DEPLOYMENT.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,24 @@ See [`docs/operations-runbook.md`](docs/operations-runbook.md) for operational s
186186
See [`docs/sizing.md`](docs/sizing.md) for measured CPU/RAM/disk numbers for laptop mode vs. fleet mode at
187187
different worker counts, with the exact commands used to reproduce them.
188188

189+
## Environment variable reference
190+
191+
All optional. The per-store `LOOPOVER_MINER_<NAME>_DB` path overrides (e.g. `LOOPOVER_MINER_PORTFOLIO_QUEUE_DB`)
192+
and `LOOPOVER_MINER_CONFIG_DIR` are covered above under the fleet/state notes; the rest are:
193+
194+
| Variable | Read by | Purpose |
195+
| --- | --- | --- |
196+
| `LOOPOVER_MINER_AMS_POLICY_PATH` | `lib/ams-policy.js` | Path to the operator's `.loopover-ams.yml` policy spec. |
197+
| `LOOPOVER_MINER_AMS_COLLECTOR_URL`, `LOOPOVER_MINER_AMS_COLLECTOR_TOKEN` | `lib/orb-export.js` | Endpoint + bearer for pushing fleet state to an AMS/Orb collector. |
198+
| `LOOPOVER_MINER_CHAT_ACTIONS` | `lib/chat-action-dispatch.js` | Truthy string enables the chat-action-dispatch flag (off by default). |
199+
| `LOOPOVER_MINER_LEDGER_RETENTION_DAYS`, `LOOPOVER_MINER_LEDGER_RETENTION_MAX_ROWS` | `lib/store-maintenance.js` | Opt-in ledger retention window / row cap. |
200+
| `LOOPOVER_MINER_LOG_LEVEL` | `lib/logger.js` | Log verbosity override. |
201+
| `LOOPOVER_MINER_NO_UPDATE_CHECK` | `lib/update-check.js` | Truthy string disables the background update check. |
202+
| `LOOPOVER_MINER_REPO_CLONE_DIR` | `lib/repo-clone.js` | Base directory for cloned target repos. |
203+
| `LOOPOVER_MINER_WORKTREE_DIR` | `lib/worktree-allocator.js` | Base directory for per-attempt git worktrees. |
204+
| `LOOPOVER_MINER_SENTRY_DSN`, `LOOPOVER_MINER_SENTRY_ENVIRONMENT` | `lib/sentry.js` | Optional Sentry error reporting (DSN + environment tag). |
205+
| `LOOPOVER_MINER_VERSION` | `lib/version.js` | Overrides the reported release id (e.g. for a custom build). |
206+
189207
## Optional hosted discovery plane (opt-in)
190208

191209
The Phase 6 **hosted discovery-index** is **off by default** — unlike Orb fleet export (`ORB_AIR_GAP` is the only opt-out). Operators who want cross-fleet metadata queries or soft-claim coordination must opt in explicitly. See [`docs/discovery-plane-operator-guide.md`](docs/discovery-plane-operator-guide.md) ([#4309](https://github.com/JSONbored/gittensory/issues/4309), placeholder until [#4300](https://github.com/JSONbored/gittensory/issues/4300) / [#4301](https://github.com/JSONbored/gittensory/issues/4301) / [#4302](https://github.com/JSONbored/gittensory/issues/4302) ship).

packages/loopover-miner/lib/deployment-docs-audit.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ export type DeploymentDocsClaims = {
88
/** Filesystem-independent view of the live source tree the parsed claims are checked against. */
99
export type DeploymentDocsReality = {
1010
hasEnvRead: (name: string) => boolean;
11+
/** The enumerable set of every real env-var read, so the reverse audit direction (#6601) can diff a real
12+
* `LOOPOVER_MINER_*` read that DEPLOYMENT.md never documents, not just probe one documented name at a time. */
13+
envReads: Iterable<string>;
1114
pathExists: (relativePath: string) => boolean;
1215
isRegisteredCommand: (name: string) => boolean;
1316
};

packages/loopover-miner/lib/deployment-docs-audit.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@ export function auditDeploymentDocs(claims, reality) {
102102
);
103103
}
104104
}
105+
// Reverse direction (#6601): a real `LOOPOVER_MINER_*` env-var read that DEPLOYMENT.md never documents. Scoped
106+
// to the `LOOPOVER_MINER_` prefix and excluding the `*_DB` family (documented generically via one pattern
107+
// sentence, not enumerated) and the bare `MINER_*` alias namespace (which also matches non-env event/metric/
108+
// filename constants). `reality.envReads` is the enumerable set of real reads the forward `hasEnvRead` probes.
109+
const documented = new Set(claims.envVars);
110+
for (const name of reality.envReads) {
111+
if (name.startsWith("LOOPOVER_MINER_") && !name.endsWith("_DB") && !documented.has(name)) {
112+
failures.push(
113+
`env var "${name}" is read under packages/loopover-miner/** but is not documented in DEPLOYMENT.md`,
114+
);
115+
}
116+
}
105117
return { ok: failures.length === 0, failures };
106118
}
107119

scripts/check-miner-deployment-docs.d.mts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export type MinerDeploymentReality = {
22
hasEnvRead: (name: string) => boolean;
3+
/** Enumerable set of every real env-var read, for the reverse audit direction (#6601). */
4+
envReads: Iterable<string>;
35
pathExists: (relativePath: string) => boolean;
46
isRegisteredCommand: (name: string) => boolean;
57
};

scripts/check-miner-deployment-docs.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export function buildLiveMinerDeploymentReality() {
3939
const registered = scanRegisteredCommands(readFileSync(BIN_ENTRY, "utf8"));
4040
return {
4141
hasEnvRead: (name) => envReads.has(name),
42+
// The full enumerable read-set (#6601), so auditDeploymentDocs can diff the reverse direction — a real
43+
// `LOOPOVER_MINER_*` read missing from DEPLOYMENT.md — not just probe one documented name at a time.
44+
envReads,
4245
pathExists: (relativePath) => existsSync(resolve(MINER_DIR, relativePath)),
4346
isRegisteredCommand: (name) => registered.has(name),
4447
};

test/unit/check-miner-deployment-docs.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, expect, it, vi } from "vitest";
22
import {
3+
buildLiveMinerDeploymentReality,
34
main,
45
runMinerDeploymentDocsAudit,
56
} from "../../scripts/check-miner-deployment-docs.mjs";
@@ -14,6 +15,16 @@ describe("check-miner-deployment-docs (#6158)", () => {
1415
expect(result.claimCounts.subcommands).toBeGreaterThan(0);
1516
});
1617

18+
it("exposes the live env-var read set as an enumerable field for the reverse audit (#6601)", () => {
19+
const reality = buildLiveMinerDeploymentReality();
20+
const reads = [...reality.envReads];
21+
// Populated, and includes a var the reverse check now requires DEPLOYMENT.md to document.
22+
expect(reads.length).toBeGreaterThan(0);
23+
expect(reads).toContain("LOOPOVER_MINER_LOG_LEVEL");
24+
// The enumerable set and the boolean probe agree.
25+
expect(reads.every((name) => reality.hasEnvRead(name))).toBe(true);
26+
});
27+
1728
it("fails when env-var backing reads are forced missing (drift fixture)", () => {
1829
expect(() => runMinerDeploymentDocsAudit({ testMode: "missing-env" })).toThrow(/DEPLOYMENT\.md is out of sync/i);
1930
});

test/unit/miner-deployment-docs-audit.test.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@ function buildLiveReality(): DeploymentDocsReality {
4343
const registered = scanRegisteredCommands(readFileSync(BIN_ENTRY, "utf8"));
4444
return {
4545
hasEnvRead: (name) => envReads.has(name),
46+
envReads,
4647
pathExists: (relativePath) => existsSync(resolve(MINER_DIR, relativePath)),
4748
isRegisteredCommand: (name) => registered.has(name),
4849
};
4950
}
5051

5152
const ALWAYS_IN_SYNC: DeploymentDocsReality = {
5253
hasEnvRead: () => true,
54+
envReads: [],
5355
pathExists: () => true,
5456
isRegisteredCommand: () => true,
5557
};
@@ -184,11 +186,46 @@ describe("loopover-miner DEPLOYMENT.md docs-accuracy audit (#5180)", () => {
184186
expect(result.failures[0]).toContain("not registered");
185187
});
186188

189+
it("reverse (#6601): flags a real LOOPOVER_MINER_* read that DEPLOYMENT.md never documents", () => {
190+
const result = auditDeploymentDocs(
191+
{ envVars: [], filePaths: [], subcommands: [] },
192+
{ ...ALWAYS_IN_SYNC, envReads: ["LOOPOVER_MINER_LOG_LEVEL"] },
193+
);
194+
expect(result.ok).toBe(false);
195+
expect(result.failures).toHaveLength(1);
196+
expect(result.failures[0]).toContain("LOOPOVER_MINER_LOG_LEVEL");
197+
expect(result.failures[0]).toContain("is not documented");
198+
});
199+
200+
it("reverse (#6601): does NOT flag a real read that IS documented", () => {
201+
const result = auditDeploymentDocs(
202+
{ envVars: ["LOOPOVER_MINER_LOG_LEVEL"], filePaths: [], subcommands: [] },
203+
{ ...ALWAYS_IN_SYNC, envReads: ["LOOPOVER_MINER_LOG_LEVEL"] },
204+
);
205+
expect(result).toEqual({ ok: true, failures: [] });
206+
});
207+
208+
it("reverse (#6601): does NOT flag an undocumented LOOPOVER_MINER_*_DB read (generic-pattern exemption)", () => {
209+
const result = auditDeploymentDocs(
210+
{ envVars: [], filePaths: [], subcommands: [] },
211+
{ ...ALWAYS_IN_SYNC, envReads: ["LOOPOVER_MINER_PORTFOLIO_QUEUE_DB"] },
212+
);
213+
expect(result).toEqual({ ok: true, failures: [] });
214+
});
215+
216+
it("reverse (#6601): does NOT flag an undocumented bare MINER_* read (no LOOPOVER_MINER_ prefix)", () => {
217+
const result = auditDeploymentDocs(
218+
{ envVars: [], filePaths: [], subcommands: [] },
219+
{ ...ALWAYS_IN_SYNC, envReads: ["MINER_PR_OUTCOME_EVENT"] },
220+
);
221+
expect(result).toEqual({ ok: true, failures: [] });
222+
});
223+
187224
it("assertDeploymentDocsInSync throws and names every stale claim at once", () => {
188225
expect(() =>
189226
assertDeploymentDocsInSync(
190227
{ envVars: ["LOOPOVER_MINER_GONE"], filePaths: ["gone.yml"], subcommands: ["gone"] },
191-
{ hasEnvRead: () => false, pathExists: () => false, isRegisteredCommand: () => false },
228+
{ hasEnvRead: () => false, envReads: [], pathExists: () => false, isRegisteredCommand: () => false },
192229
),
193230
).toThrow(/LOOPOVER_MINER_GONE[\s\S]*gone\.yml[\s\S]*loopover-miner gone/);
194231
});

0 commit comments

Comments
 (0)