Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/loopover-miner/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,24 @@ See [`docs/operations-runbook.md`](docs/operations-runbook.md) for operational s
See [`docs/sizing.md`](docs/sizing.md) for measured CPU/RAM/disk numbers for laptop mode vs. fleet mode at
different worker counts, with the exact commands used to reproduce them.

## Environment variable reference

All optional. The per-store `LOOPOVER_MINER_<NAME>_DB` path overrides (e.g. `LOOPOVER_MINER_PORTFOLIO_QUEUE_DB`)
and `LOOPOVER_MINER_CONFIG_DIR` are covered above under the fleet/state notes; the rest are:

| Variable | Read by | Purpose |
| --- | --- | --- |
| `LOOPOVER_MINER_AMS_POLICY_PATH` | `lib/ams-policy.js` | Path to the operator's `.loopover-ams.yml` policy spec. |
| `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. |
| `LOOPOVER_MINER_CHAT_ACTIONS` | `lib/chat-action-dispatch.js` | Truthy string enables the chat-action-dispatch flag (off by default). |
| `LOOPOVER_MINER_LEDGER_RETENTION_DAYS`, `LOOPOVER_MINER_LEDGER_RETENTION_MAX_ROWS` | `lib/store-maintenance.js` | Opt-in ledger retention window / row cap. |
| `LOOPOVER_MINER_LOG_LEVEL` | `lib/logger.js` | Log verbosity override. |
| `LOOPOVER_MINER_NO_UPDATE_CHECK` | `lib/update-check.js` | Truthy string disables the background update check. |
| `LOOPOVER_MINER_REPO_CLONE_DIR` | `lib/repo-clone.js` | Base directory for cloned target repos. |
| `LOOPOVER_MINER_WORKTREE_DIR` | `lib/worktree-allocator.js` | Base directory for per-attempt git worktrees. |
| `LOOPOVER_MINER_SENTRY_DSN`, `LOOPOVER_MINER_SENTRY_ENVIRONMENT` | `lib/sentry.js` | Optional Sentry error reporting (DSN + environment tag). |
| `LOOPOVER_MINER_VERSION` | `lib/version.js` | Overrides the reported release id (e.g. for a custom build). |

## Optional hosted discovery plane (opt-in)

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).
3 changes: 3 additions & 0 deletions packages/loopover-miner/lib/deployment-docs-audit.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export type DeploymentDocsClaims = {
/** Filesystem-independent view of the live source tree the parsed claims are checked against. */
export type DeploymentDocsReality = {
hasEnvRead: (name: string) => boolean;
/** The enumerable set of every real env-var read, so the reverse audit direction (#6601) can diff a real
* `LOOPOVER_MINER_*` read that DEPLOYMENT.md never documents, not just probe one documented name at a time. */
envReads: Iterable<string>;
pathExists: (relativePath: string) => boolean;
isRegisteredCommand: (name: string) => boolean;
};
Expand Down
12 changes: 12 additions & 0 deletions packages/loopover-miner/lib/deployment-docs-audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ export function auditDeploymentDocs(claims, reality) {
);
}
}
// Reverse direction (#6601): a real `LOOPOVER_MINER_*` env-var read that DEPLOYMENT.md never documents. Scoped
// to the `LOOPOVER_MINER_` prefix and excluding the `*_DB` family (documented generically via one pattern
// sentence, not enumerated) and the bare `MINER_*` alias namespace (which also matches non-env event/metric/
// filename constants). `reality.envReads` is the enumerable set of real reads the forward `hasEnvRead` probes.
const documented = new Set(claims.envVars);
for (const name of reality.envReads) {
if (name.startsWith("LOOPOVER_MINER_") && !name.endsWith("_DB") && !documented.has(name)) {
failures.push(
`env var "${name}" is read under packages/loopover-miner/** but is not documented in DEPLOYMENT.md`,
);
}
}
return { ok: failures.length === 0, failures };
}

Expand Down
2 changes: 2 additions & 0 deletions scripts/check-miner-deployment-docs.d.mts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export type MinerDeploymentReality = {
hasEnvRead: (name: string) => boolean;
/** Enumerable set of every real env-var read, for the reverse audit direction (#6601). */
envReads: Iterable<string>;
pathExists: (relativePath: string) => boolean;
isRegisteredCommand: (name: string) => boolean;
};
Expand Down
3 changes: 3 additions & 0 deletions scripts/check-miner-deployment-docs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export function buildLiveMinerDeploymentReality() {
const registered = scanRegisteredCommands(readFileSync(BIN_ENTRY, "utf8"));
return {
hasEnvRead: (name) => envReads.has(name),
// The full enumerable read-set (#6601), so auditDeploymentDocs can diff the reverse direction — a real
// `LOOPOVER_MINER_*` read missing from DEPLOYMENT.md — not just probe one documented name at a time.
envReads,
pathExists: (relativePath) => existsSync(resolve(MINER_DIR, relativePath)),
isRegisteredCommand: (name) => registered.has(name),
};
Expand Down
11 changes: 11 additions & 0 deletions test/unit/check-miner-deployment-docs.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it, vi } from "vitest";
import {
buildLiveMinerDeploymentReality,
main,
runMinerDeploymentDocsAudit,
} from "../../scripts/check-miner-deployment-docs.mjs";
Expand All @@ -14,6 +15,16 @@ describe("check-miner-deployment-docs (#6158)", () => {
expect(result.claimCounts.subcommands).toBeGreaterThan(0);
});

it("exposes the live env-var read set as an enumerable field for the reverse audit (#6601)", () => {
const reality = buildLiveMinerDeploymentReality();
const reads = [...reality.envReads];
// Populated, and includes a var the reverse check now requires DEPLOYMENT.md to document.
expect(reads.length).toBeGreaterThan(0);
expect(reads).toContain("LOOPOVER_MINER_LOG_LEVEL");
// The enumerable set and the boolean probe agree.
expect(reads.every((name) => reality.hasEnvRead(name))).toBe(true);
});

it("fails when env-var backing reads are forced missing (drift fixture)", () => {
expect(() => runMinerDeploymentDocsAudit({ testMode: "missing-env" })).toThrow(/DEPLOYMENT\.md is out of sync/i);
});
Expand Down
39 changes: 38 additions & 1 deletion test/unit/miner-deployment-docs-audit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@ function buildLiveReality(): DeploymentDocsReality {
const registered = scanRegisteredCommands(readFileSync(BIN_ENTRY, "utf8"));
return {
hasEnvRead: (name) => envReads.has(name),
envReads,
pathExists: (relativePath) => existsSync(resolve(MINER_DIR, relativePath)),
isRegisteredCommand: (name) => registered.has(name),
};
}

const ALWAYS_IN_SYNC: DeploymentDocsReality = {
hasEnvRead: () => true,
envReads: [],
pathExists: () => true,
isRegisteredCommand: () => true,
};
Expand Down Expand Up @@ -184,11 +186,46 @@ describe("loopover-miner DEPLOYMENT.md docs-accuracy audit (#5180)", () => {
expect(result.failures[0]).toContain("not registered");
});

it("reverse (#6601): flags a real LOOPOVER_MINER_* read that DEPLOYMENT.md never documents", () => {
const result = auditDeploymentDocs(
{ envVars: [], filePaths: [], subcommands: [] },
{ ...ALWAYS_IN_SYNC, envReads: ["LOOPOVER_MINER_LOG_LEVEL"] },
);
expect(result.ok).toBe(false);
expect(result.failures).toHaveLength(1);
expect(result.failures[0]).toContain("LOOPOVER_MINER_LOG_LEVEL");
expect(result.failures[0]).toContain("is not documented");
});

it("reverse (#6601): does NOT flag a real read that IS documented", () => {
const result = auditDeploymentDocs(
{ envVars: ["LOOPOVER_MINER_LOG_LEVEL"], filePaths: [], subcommands: [] },
{ ...ALWAYS_IN_SYNC, envReads: ["LOOPOVER_MINER_LOG_LEVEL"] },
);
expect(result).toEqual({ ok: true, failures: [] });
});

it("reverse (#6601): does NOT flag an undocumented LOOPOVER_MINER_*_DB read (generic-pattern exemption)", () => {
const result = auditDeploymentDocs(
{ envVars: [], filePaths: [], subcommands: [] },
{ ...ALWAYS_IN_SYNC, envReads: ["LOOPOVER_MINER_PORTFOLIO_QUEUE_DB"] },
);
expect(result).toEqual({ ok: true, failures: [] });
});

it("reverse (#6601): does NOT flag an undocumented bare MINER_* read (no LOOPOVER_MINER_ prefix)", () => {
const result = auditDeploymentDocs(
{ envVars: [], filePaths: [], subcommands: [] },
{ ...ALWAYS_IN_SYNC, envReads: ["MINER_PR_OUTCOME_EVENT"] },
);
expect(result).toEqual({ ok: true, failures: [] });
});

it("assertDeploymentDocsInSync throws and names every stale claim at once", () => {
expect(() =>
assertDeploymentDocsInSync(
{ envVars: ["LOOPOVER_MINER_GONE"], filePaths: ["gone.yml"], subcommands: ["gone"] },
{ hasEnvRead: () => false, pathExists: () => false, isRegisteredCommand: () => false },
{ hasEnvRead: () => false, envReads: [], pathExists: () => false, isRegisteredCommand: () => false },
),
).toThrow(/LOOPOVER_MINER_GONE[\s\S]*gone\.yml[\s\S]*loopover-miner gone/);
});
Expand Down