@momentiq/dark-factory-cli 0.1.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +150 -0
  3. package/dist/adapters/_shared.d.ts +45 -0
  4. package/dist/adapters/_shared.d.ts.map +1 -0
  5. package/dist/adapters/_shared.js +200 -0
  6. package/dist/adapters/_shared.js.map +1 -0
  7. package/dist/adapters/codex-sdk.d.ts +279 -0
  8. package/dist/adapters/codex-sdk.d.ts.map +1 -0
  9. package/dist/adapters/codex-sdk.js +930 -0
  10. package/dist/adapters/codex-sdk.js.map +1 -0
  11. package/dist/adapters/critic-result-schema.d.ts +215 -0
  12. package/dist/adapters/critic-result-schema.d.ts.map +1 -0
  13. package/dist/adapters/critic-result-schema.js +153 -0
  14. package/dist/adapters/critic-result-schema.js.map +1 -0
  15. package/dist/adapters/critic.d.ts +62 -0
  16. package/dist/adapters/critic.d.ts.map +1 -0
  17. package/dist/adapters/critic.js +79 -0
  18. package/dist/adapters/critic.js.map +1 -0
  19. package/dist/adapters/cursor-sdk.d.ts +153 -0
  20. package/dist/adapters/cursor-sdk.d.ts.map +1 -0
  21. package/dist/adapters/cursor-sdk.js +818 -0
  22. package/dist/adapters/cursor-sdk.js.map +1 -0
  23. package/dist/adapters/gemini-sdk.d.ts +113 -0
  24. package/dist/adapters/gemini-sdk.d.ts.map +1 -0
  25. package/dist/adapters/gemini-sdk.js +532 -0
  26. package/dist/adapters/gemini-sdk.js.map +1 -0
  27. package/dist/adapters/grok-direct-sdk.d.ts +148 -0
  28. package/dist/adapters/grok-direct-sdk.d.ts.map +1 -0
  29. package/dist/adapters/grok-direct-sdk.js +694 -0
  30. package/dist/adapters/grok-direct-sdk.js.map +1 -0
  31. package/dist/branch-protection/audit_branch_protection.py +759 -0
  32. package/dist/branch-protection/index.d.ts +25 -0
  33. package/dist/branch-protection/index.d.ts.map +1 -0
  34. package/dist/branch-protection/index.js +70 -0
  35. package/dist/branch-protection/index.js.map +1 -0
  36. package/dist/branch-protection/spec-default.yaml +314 -0
  37. package/dist/cli.d.ts +3 -0
  38. package/dist/cli.d.ts.map +1 -0
  39. package/dist/cli.js +581 -0
  40. package/dist/cli.js.map +1 -0
  41. package/dist/cycle-doc-validator/index.d.ts +43 -0
  42. package/dist/cycle-doc-validator/index.d.ts.map +1 -0
  43. package/dist/cycle-doc-validator/index.js +69 -0
  44. package/dist/cycle-doc-validator/index.js.map +1 -0
  45. package/dist/cycle-doc-validator/validate_cycle_doc.py +1260 -0
  46. package/dist/cycle-tracker-sync/attribute_pr_cycle_ref.py +384 -0
  47. package/dist/cycle-tracker-sync/index.d.ts +20 -0
  48. package/dist/cycle-tracker-sync/index.d.ts.map +1 -0
  49. package/dist/cycle-tracker-sync/index.js +71 -0
  50. package/dist/cycle-tracker-sync/index.js.map +1 -0
  51. package/dist/cycle-tracker-sync/sync_cycle_trackers.py +1093 -0
  52. package/dist/evidence/audit-trail.d.ts +59 -0
  53. package/dist/evidence/audit-trail.d.ts.map +1 -0
  54. package/dist/evidence/audit-trail.js +283 -0
  55. package/dist/evidence/audit-trail.js.map +1 -0
  56. package/dist/evidence/index.d.ts +4 -0
  57. package/dist/evidence/index.d.ts.map +1 -0
  58. package/dist/evidence/index.js +29 -0
  59. package/dist/evidence/index.js.map +1 -0
  60. package/dist/evidence/per-sha.d.ts +13 -0
  61. package/dist/evidence/per-sha.d.ts.map +1 -0
  62. package/dist/evidence/per-sha.js +97 -0
  63. package/dist/evidence/per-sha.js.map +1 -0
  64. package/dist/evidence/quality-gates.d.ts +19 -0
  65. package/dist/evidence/quality-gates.d.ts.map +1 -0
  66. package/dist/evidence/quality-gates.js +212 -0
  67. package/dist/evidence/quality-gates.js.map +1 -0
  68. package/dist/git.d.ts +40 -0
  69. package/dist/git.d.ts.map +1 -0
  70. package/dist/git.js +414 -0
  71. package/dist/git.js.map +1 -0
  72. package/dist/glob.d.ts +4 -0
  73. package/dist/glob.d.ts.map +1 -0
  74. package/dist/glob.js +99 -0
  75. package/dist/glob.js.map +1 -0
  76. package/dist/index.d.ts +17 -0
  77. package/dist/index.d.ts.map +1 -0
  78. package/dist/index.js +33 -0
  79. package/dist/index.js.map +1 -0
  80. package/dist/paths.d.ts +12 -0
  81. package/dist/paths.d.ts.map +1 -0
  82. package/dist/paths.js +42 -0
  83. package/dist/paths.js.map +1 -0
  84. package/dist/policy/baseline.d.ts +15 -0
  85. package/dist/policy/baseline.d.ts.map +1 -0
  86. package/dist/policy/baseline.js +115 -0
  87. package/dist/policy/baseline.js.map +1 -0
  88. package/dist/policy/config.d.ts +64 -0
  89. package/dist/policy/config.d.ts.map +1 -0
  90. package/dist/policy/config.js +363 -0
  91. package/dist/policy/config.js.map +1 -0
  92. package/dist/policy/gate.d.ts +80 -0
  93. package/dist/policy/gate.d.ts.map +1 -0
  94. package/dist/policy/gate.js +1019 -0
  95. package/dist/policy/gate.js.map +1 -0
  96. package/dist/policy/index.d.ts +7 -0
  97. package/dist/policy/index.d.ts.map +1 -0
  98. package/dist/policy/index.js +14 -0
  99. package/dist/policy/index.js.map +1 -0
  100. package/dist/policy/merge-queue.d.ts +183 -0
  101. package/dist/policy/merge-queue.d.ts.map +1 -0
  102. package/dist/policy/merge-queue.js +310 -0
  103. package/dist/policy/merge-queue.js.map +1 -0
  104. package/dist/policy/profile.d.ts +98 -0
  105. package/dist/policy/profile.d.ts.map +1 -0
  106. package/dist/policy/profile.js +156 -0
  107. package/dist/policy/profile.js.map +1 -0
  108. package/dist/policy/tdd-classifier.d.ts +18 -0
  109. package/dist/policy/tdd-classifier.d.ts.map +1 -0
  110. package/dist/policy/tdd-classifier.js +79 -0
  111. package/dist/policy/tdd-classifier.js.map +1 -0
  112. package/dist/prompt.d.ts +13 -0
  113. package/dist/prompt.d.ts.map +1 -0
  114. package/dist/prompt.js +175 -0
  115. package/dist/prompt.js.map +1 -0
  116. package/dist/report.d.ts +88 -0
  117. package/dist/report.d.ts.map +1 -0
  118. package/dist/report.js +376 -0
  119. package/dist/report.js.map +1 -0
  120. package/dist/runner.d.ts +30 -0
  121. package/dist/runner.d.ts.map +1 -0
  122. package/dist/runner.js +456 -0
  123. package/dist/runner.js.map +1 -0
  124. package/dist/security.d.ts +2 -0
  125. package/dist/security.d.ts.map +1 -0
  126. package/dist/security.js +19 -0
  127. package/dist/security.js.map +1 -0
  128. package/dist/trusted-surface/rebind.d.ts +10 -0
  129. package/dist/trusted-surface/rebind.d.ts.map +1 -0
  130. package/dist/trusted-surface/rebind.js +154 -0
  131. package/dist/trusted-surface/rebind.js.map +1 -0
  132. package/package.json +78 -0
package/dist/paths.js ADDED
@@ -0,0 +1,42 @@
1
+ import { resolve } from "node:path";
2
+ import { gitCommonDir, gitDir } from "./git.js";
3
+ export async function resolveArtifactRoot(loaded) {
4
+ if (loaded.config.git.artifactScope === "git-common-dir") {
5
+ return gitCommonDir(loaded.repoRoot);
6
+ }
7
+ return gitDir(loaded.repoRoot);
8
+ }
9
+ export async function resolveArtifactDir(loaded) {
10
+ return resolve(await resolveArtifactRoot(loaded), loaded.config.git.artifactDir);
11
+ }
12
+ export function artifactJsonPath(artifactDir, sha) {
13
+ return resolve(artifactDir, `${sha}.json`);
14
+ }
15
+ export function artifactMarkdownPath(artifactDir, sha) {
16
+ return resolve(artifactDir, `${sha}.md`);
17
+ }
18
+ export function artifactLockPath(artifactDir, sha) {
19
+ return resolve(artifactDir, `${sha}.lock`);
20
+ }
21
+ export function diagnosticsDir(artifactDir) {
22
+ return resolve(artifactDir, "diagnostics");
23
+ }
24
+ export function telemetryPath(artifactDir) {
25
+ return resolve(artifactDir, "_runs.ndjson");
26
+ }
27
+ export async function resolveValidationResultPath(loaded) {
28
+ const root = await resolveArtifactRoot(loaded);
29
+ return resolve(root, loaded.config.validation.resultFile);
30
+ }
31
+ // Cycle 332 — per-PR finding-cache directory. Lives under the
32
+ // artifact dir (git-common-dir) so a single worktree can hold caches
33
+ // for multiple PRs simultaneously without collision. The CI runner
34
+ // is the primary writer; the local critic does not touch this path
35
+ // in Phase 2 (Q7 of the cycle doc).
36
+ export function findingCacheDir(artifactDir, prNumber) {
37
+ return resolve(artifactDir, `_pr-${prNumber}`);
38
+ }
39
+ export function findingCachePath(artifactDir, prNumber) {
40
+ return resolve(findingCacheDir(artifactDir, prNumber), "findings.ndjson");
41
+ }
42
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEhD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAoB;IAC5D,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,KAAK,gBAAgB,EAAE,CAAC;QACzD,OAAO,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAoB;IAC3D,OAAO,OAAO,CAAC,MAAM,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,WAAmB,EAAE,GAAW;IAC/D,OAAO,OAAO,CAAC,WAAW,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,WAAmB,EAAE,GAAW;IACnE,OAAO,OAAO,CAAC,WAAW,EAAE,GAAG,GAAG,KAAK,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,WAAmB,EAAE,GAAW;IAC/D,OAAO,OAAO,CAAC,WAAW,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,WAAmB;IAChD,OAAO,OAAO,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,WAAmB;IAC/C,OAAO,OAAO,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,MAAoB;IACpE,MAAM,IAAI,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC/C,OAAO,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC5D,CAAC;AAED,8DAA8D;AAC9D,qEAAqE;AACrE,mEAAmE;AACnE,mEAAmE;AACnE,oCAAoC;AACpC,MAAM,UAAU,eAAe,CAAC,WAAmB,EAAE,QAAgB;IACnE,OAAO,OAAO,CAAC,WAAW,EAAE,OAAO,QAAQ,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,WAAmB,EAAE,QAAgB;IACpE,OAAO,OAAO,CAAC,eAAe,CAAC,WAAW,EAAE,QAAQ,CAAC,EAAE,iBAAiB,CAAC,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { type LoadedConfig } from "./config.js";
2
+ export declare const TRUSTED_SCRIPT_PATHS: ReadonlySet<string>;
3
+ export interface PolicyBaseline {
4
+ loaded: LoadedConfig;
5
+ baselineRef?: string;
6
+ triggeredBy: string[];
7
+ }
8
+ export interface ResolveBaselineOptions {
9
+ loaded: LoadedConfig;
10
+ sha: string;
11
+ cwd: string;
12
+ warn?: (message: string) => void;
13
+ }
14
+ export declare function resolvePolicyBaseline(options: ResolveBaselineOptions): Promise<PolicyBaseline>;
15
+ //# sourceMappingURL=baseline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../../src/policy/baseline.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AAiCrB,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAYnD,CAAC;AAcH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,YAAY,CAAC;IAIrB,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,YAAY,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IAGZ,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,cAAc,CAAC,CAuEzB"}
@@ -0,0 +1,115 @@
1
+ import { CONFIG_RELATIVE_PATH, loadAgentReviewConfigFromRef, } from "./config.js";
2
+ import { changedFiles, commitParent } from "../git.js";
3
+ // Cycle 318.4 trusted-surface extension.
4
+ //
5
+ // Three different enforcement surfaces have OVERLAPPING-BUT-DIFFERENT
6
+ // concerns and therefore intentionally different path lists:
7
+ //
8
+ // 1. This file (`TRUSTED_SCRIPT_PATHS`) — paths that should trigger
9
+ // the LOCAL critic's self-modification guard (review against
10
+ // parent baseline). Limited to the declarative policy artifacts
11
+ // and the two enforcement scripts + their tests + the three
12
+ // workflows. Build-time files (Makefile, dist/, lockfiles) are
13
+ // excluded because they're not policy code per se.
14
+ //
15
+ // 2. `.github/workflows/branch-protection-audit.yml` "Detect spec-
16
+ // relevant changes" regex — paths that should trigger the LIVE
17
+ // ruleset audit (PR-time). Wider than (1): covers spec.yaml +
18
+ // ANY `.github/workflows/*.{yml,yaml}` (including `*.disabled`
19
+ // renames) + the audit/validator scripts. Broadened in issue
20
+ // #1385 (Codex P1 #1) so that a PR adding `[skip ci]` checks to
21
+ // ANY workflow (e.g. `pr.yml`) cannot bypass the audit.
22
+ //
23
+ // 3. `.github/workflows/agent-critic.yml` "Detect trusted-surface
24
+ // changes" regex — paths that could weaponize the workflow
25
+ // runtime to exfiltrate `CURSOR_API_KEY`. Broader: includes the
26
+ // Makefile, the entire `tools/agent-review/` source tree, and
27
+ // `.husky/` hooks, because any of those run during gate-prepare
28
+ // / review / gate-push with the secret in env.
29
+ //
30
+ // Codex P1 (PR #1380 review) flagged the surfaces as drifting; the
31
+ // drift is intentional. Updates to (1) MUST be considered for (2) and
32
+ // (3) too — but they don't have to match byte-for-byte.
33
+ export const TRUSTED_SCRIPT_PATHS = new Set([
34
+ "tools/branch-protection/spec.yaml",
35
+ "scripts/ci/audit_branch_protection.py",
36
+ "scripts/ci/test_audit_branch_protection.py",
37
+ "scripts/ci/validate_cycle_doc.py",
38
+ "scripts/ci/test_validate_cycle_doc.py",
39
+ // Cycle 318.4 workflow files themselves are merge-boundary policy.
40
+ // A commit that weakens these workflows is a policy change; force the
41
+ // critic baseline reload AND the live ruleset audit on that PR.
42
+ ".github/workflows/agent-critic.yml",
43
+ ".github/workflows/cycle-doc-validation.yml",
44
+ ".github/workflows/branch-protection-audit.yml",
45
+ ]);
46
+ export async function resolvePolicyBaseline(options) {
47
+ const { loaded, sha, cwd } = options;
48
+ const warn = options.warn ?? ((m) => process.stderr.write(m));
49
+ let parent;
50
+ try {
51
+ parent = await commitParent(sha, cwd);
52
+ }
53
+ catch {
54
+ return { loaded, triggeredBy: [] };
55
+ }
56
+ if (!parent)
57
+ return { loaded, triggeredBy: [] };
58
+ const files = await changedFiles(parent, sha, cwd, { readContent: false });
59
+ const trustedPaths = new Set([
60
+ CONFIG_RELATIVE_PATH,
61
+ ...loaded.config.context.guidanceFiles,
62
+ ...loaded.config.context.promptFragments,
63
+ ]);
64
+ const triggeredBy = [];
65
+ for (const f of files) {
66
+ if (trustedPaths.has(f.path) || f.path.startsWith(".agent-review/")) {
67
+ triggeredBy.push(f.path);
68
+ continue;
69
+ }
70
+ // Cycle 318.4: extend trusted-surface coverage to the branch-protection
71
+ // spec and the two policy-enforcement scripts (and their tests). This
72
+ // set is INTENTIONALLY NARROWER than the workflow regex in
73
+ // `.github/workflows/branch-protection-audit.yml`'s "Detect spec-
74
+ // relevant changes" step — issue #1385 broadened that regex to cover
75
+ // any `.github/workflows/*.{yml,yaml}` while this set still enumerates
76
+ // only the declarative policy artifacts + their enforcement scripts.
77
+ // See the header comment block above for why the two surfaces drift
78
+ // intentionally; the compensating control is that the PR-time live
79
+ // ruleset audit catches workflow edits that don't trigger the local
80
+ // policy-baseline reload.
81
+ if (TRUSTED_SCRIPT_PATHS.has(f.path)) {
82
+ triggeredBy.push(f.path);
83
+ }
84
+ }
85
+ if (triggeredBy.length === 0)
86
+ return { loaded, triggeredBy: [] };
87
+ // Self-modification detected. Try to load policy from parent.
88
+ let parentLoaded;
89
+ try {
90
+ // Cycle 322.7 Phase C — thread the typo-guard `warn` callback through
91
+ // to the parent-ref load so an operator who mistyped
92
+ // `AGENT_REVIEW_AGGREGATION_POLICY` sees the diagnostic regardless of
93
+ // which load path the run took. Without this, the baseline-reload
94
+ // path would silently swallow the warning (Cursor critic MEDIUM on
95
+ // dce8fd9e).
96
+ parentLoaded = await loadAgentReviewConfigFromRef(loaded.repoRoot, parent, { warn });
97
+ }
98
+ catch (err) {
99
+ // Parent has no config (commit introduces it for the first time) —
100
+ // fall back to HEAD policy with a loud warning. Self-modification
101
+ // check is skipped, but the warning makes this visible to operators
102
+ // (and to the artifact, since stderr is captured by the post-commit
103
+ // hook log).
104
+ warn(`agent-review: WARNING — commit ${sha.slice(0, 12)} modifies trusted policy ` +
105
+ `surface (${triggeredBy.join(", ")}) but parent policy is unavailable ` +
106
+ `(${err.message}); falling back to HEAD policy. ` +
107
+ `Self-modification check skipped.\n`);
108
+ return { loaded, triggeredBy };
109
+ }
110
+ warn(`agent-review: commit ${sha.slice(0, 12)} modifies trusted policy surface ` +
111
+ `(${triggeredBy.join(", ")}); reviewing against parent baseline ${parent.slice(0, 12)} ` +
112
+ `to prevent self-modification.\n`);
113
+ return { loaded: parentLoaded, baselineRef: parent, triggeredBy };
114
+ }
115
+ //# sourceMappingURL=baseline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.js","sourceRoot":"","sources":["../../src/policy/baseline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { type AgentReviewConfig, type AggregationPolicy } from "@momentiq/dark-factory-schemas";
2
+ export declare const CONFIG_RELATIVE_PATH = ".agent-review/config.json";
3
+ export declare const POLICY_OVERRIDE_ENV = "AGENT_REVIEW_AGGREGATION_POLICY";
4
+ export interface LoadedConfig {
5
+ config: AgentReviewConfig;
6
+ repoRoot: string;
7
+ configPath: string;
8
+ loadedFromRef?: string;
9
+ policyOverride?: PolicyOverrideRecord;
10
+ }
11
+ export interface PolicyOverrideRecord {
12
+ configured: AggregationPolicy;
13
+ overridden: AggregationPolicy;
14
+ autoPromotedCritics: string[];
15
+ }
16
+ export interface ApplyEnvOverridesOptions {
17
+ warn?: (message: string) => void;
18
+ }
19
+ export interface ApplyEnvOverridesResult {
20
+ config: AgentReviewConfig;
21
+ applied: boolean;
22
+ configured: AggregationPolicy;
23
+ overridden?: AggregationPolicy;
24
+ autoPromotedCritics: string[];
25
+ }
26
+ /**
27
+ * Apply environment-variable overrides to a loaded config.
28
+ *
29
+ * Cycle 322.7 Phase C — Emergency revert toggle.
30
+ *
31
+ * Reads `AGENT_REVIEW_AGGREGATION_POLICY` from `env`. When set to a
32
+ * recognized AggregationPolicy value, returns a config with
33
+ * `aggregation.policy` stamped to the overridden value. When unset or
34
+ * unrecognized, returns the config unchanged (and warns on the
35
+ * unrecognized path so a typo doesn't silently fall through).
36
+ *
37
+ * Safety invariant — when the override is `block-if-any` AND no critic
38
+ * has `required: true` in the source config, auto-promote
39
+ * `cursor-local-chief-engineer` to `required: true`. Under `block-if-any`,
40
+ * `gate.ts:evaluateCommitGate` only blocks pushes on required critics;
41
+ * the auto-promotion preserves the "single trusted critic gates the
42
+ * push" semantics that operators expect during an emergency revert.
43
+ * This complements the load-time schema validation (which rejects a
44
+ * config with `policy: "block-if-any"` and zero required critics
45
+ * outright) — the runtime auto-promotion is the safety net for the
46
+ * env-override path specifically.
47
+ *
48
+ * Mutation-free: the returned config is a fresh object; the input is
49
+ * never modified. The returned record is suitable for emitting an
50
+ * `aggregation_policy_overridden` telemetry event when `applied: true`.
51
+ */
52
+ export declare function applyEnvOverrides(config: AgentReviewConfig, env: Record<string, string | undefined>, options?: ApplyEnvOverridesOptions): ApplyEnvOverridesResult;
53
+ export interface LoadConfigOptions {
54
+ cwd?: string;
55
+ validateGuidanceFiles?: boolean;
56
+ env?: Record<string, string | undefined>;
57
+ warn?: (message: string) => void;
58
+ }
59
+ export declare function loadAgentReviewConfig(options?: LoadConfigOptions): Promise<LoadedConfig>;
60
+ export declare function loadAgentReviewConfigFromRef(rootDir: string, ref: string, options?: {
61
+ env?: Record<string, string | undefined>;
62
+ warn?: (message: string) => void;
63
+ }): Promise<LoadedConfig>;
64
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/policy/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACvB,MAAM,gCAAgC,CAAC;AAExC,eAAO,MAAM,oBAAoB,8BAA8B,CAAC;AAchE,eAAO,MAAM,mBAAmB,oCAAoC,CAAC;AAWrE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IAKnB,aAAa,CAAC,EAAE,MAAM,CAAC;IAKvB,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAMD,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,iBAAiB,CAAC;IAC9B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,wBAAwB;IAMvC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,uBAAuB;IAGtC,MAAM,EAAE,iBAAiB,CAAC;IAE1B,OAAO,EAAE,OAAO,CAAC;IAEjB,UAAU,EAAE,iBAAiB,CAAC;IAE9B,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAK/B,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,iBAAiB,EACzB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,OAAO,GAAE,wBAA6B,GACrC,uBAAuB,CA8NzB;AAMD,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAMhC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAKzC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,wBAAsB,qBAAqB,CACzC,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,YAAY,CAAC,CAoDvB;AAWD,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO,GAC3F,OAAO,CAAC,YAAY,CAAC,CAkCvB"}
@@ -0,0 +1,363 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { gitShowFile, repoRoot } from "../git.js";
4
+ import { AGGREGATION_POLICIES, parseAgentReviewConfig, } from "@momentiq/dark-factory-schemas";
5
+ export const CONFIG_RELATIVE_PATH = ".agent-review/config.json";
6
+ // Cycle 322.7 Phase C — Emergency revert env var. When set to a
7
+ // recognized AggregationPolicy value (`block-if-any` or
8
+ // `min-complete-quorum`), the config-load path overrides
9
+ // `aggregation.policy` at runtime so operators can revert the live
10
+ // policy WITHOUT shipping a new PR.
11
+ //
12
+ // Primary use case: a newly-added critic generates spurious blockers
13
+ // in the first 48h post-policy-flip; the team needs to roll back to
14
+ // the previous policy immediately. Setting this env var in the shell
15
+ // (or as part of a single `git push` invocation) accomplishes the
16
+ // revert; subsequent telemetry events surface the override as an
17
+ // `aggregation_policy_overridden` event for audit traceability.
18
+ export const POLICY_OVERRIDE_ENV = "AGENT_REVIEW_AGGREGATION_POLICY";
19
+ // Critic id that gets auto-promoted to `required: true` when the env
20
+ // override flips policy to `block-if-any` AND the source config has
21
+ // zero required critics. The historically-trusted critic (single
22
+ // blocker since the pre-multi-critic days) is the cleanest restoration
23
+ // of the "single trusted critic gates the push" semantics operators
24
+ // expect during an emergency revert. Documented in CONTRIBUTING.md
25
+ // "Reverting the agent-review aggregation policy (emergency)".
26
+ const AUTO_PROMOTE_CRITIC_ID = "cursor-local-chief-engineer";
27
+ /**
28
+ * Apply environment-variable overrides to a loaded config.
29
+ *
30
+ * Cycle 322.7 Phase C — Emergency revert toggle.
31
+ *
32
+ * Reads `AGENT_REVIEW_AGGREGATION_POLICY` from `env`. When set to a
33
+ * recognized AggregationPolicy value, returns a config with
34
+ * `aggregation.policy` stamped to the overridden value. When unset or
35
+ * unrecognized, returns the config unchanged (and warns on the
36
+ * unrecognized path so a typo doesn't silently fall through).
37
+ *
38
+ * Safety invariant — when the override is `block-if-any` AND no critic
39
+ * has `required: true` in the source config, auto-promote
40
+ * `cursor-local-chief-engineer` to `required: true`. Under `block-if-any`,
41
+ * `gate.ts:evaluateCommitGate` only blocks pushes on required critics;
42
+ * the auto-promotion preserves the "single trusted critic gates the
43
+ * push" semantics that operators expect during an emergency revert.
44
+ * This complements the load-time schema validation (which rejects a
45
+ * config with `policy: "block-if-any"` and zero required critics
46
+ * outright) — the runtime auto-promotion is the safety net for the
47
+ * env-override path specifically.
48
+ *
49
+ * Mutation-free: the returned config is a fresh object; the input is
50
+ * never modified. The returned record is suitable for emitting an
51
+ * `aggregation_policy_overridden` telemetry event when `applied: true`.
52
+ */
53
+ export function applyEnvOverrides(config, env, options = {}) {
54
+ const configured = config.aggregation.policy;
55
+ const raw = env[POLICY_OVERRIDE_ENV];
56
+ // Unset OR empty-string: silent fall-through. Empty string is the
57
+ // "unset" sentinel in many shell pipelines (e.g., `VAR=${X:-} make`
58
+ // patterns) so we deliberately do NOT warn — that would create noise
59
+ // on every invocation that propagates env vars from a CI runner.
60
+ if (raw === undefined || raw === "") {
61
+ return {
62
+ config,
63
+ applied: false,
64
+ configured,
65
+ autoPromotedCritics: [],
66
+ };
67
+ }
68
+ if (!isAggregationPolicy(raw)) {
69
+ // Unrecognized non-empty value: warn so the operator notices the
70
+ // typo, but never throw. The config policy stands (defense in depth
71
+ // against misconfiguration aborting the pre-commit critic).
72
+ if (options.warn) {
73
+ options.warn(`${POLICY_OVERRIDE_ENV}=${raw} is not a recognized aggregation policy; ` +
74
+ `expected one of: ${AGGREGATION_POLICIES.join(", ")}. ` +
75
+ `Falling through to configured policy "${configured}".`);
76
+ }
77
+ return {
78
+ config,
79
+ applied: false,
80
+ configured,
81
+ autoPromotedCritics: [],
82
+ };
83
+ }
84
+ // Recognized override — apply it. Build the new critic list first
85
+ // so the resulting config is fully consistent.
86
+ const autoPromotedCritics = [];
87
+ let critics = config.critics;
88
+ if (raw === "block-if-any") {
89
+ // Safety invariant: under block-if-any, the gate only blocks pushes
90
+ // on `required: true` critics. Two ways the override path could
91
+ // produce an unsafe runtime:
92
+ //
93
+ // (a) FULL critic list has zero required critics → gate sees
94
+ // only optional critics in the artifact → blocker findings
95
+ // silently demote to warnings.
96
+ //
97
+ // (b) FULL list HAS a required critic, but the ACTIVE PROFILE
98
+ // narrows critics to an all-optional subset → same outcome
99
+ // in the profile-filtered runtime view. Schema validation
100
+ // (schema.ts:931-965) catches this for the on-disk
101
+ // `block-if-any` shape, but the env-override path needs its
102
+ // own runtime guarantee because the on-disk policy may be
103
+ // `min-complete-quorum` (which has no profile invariant) at
104
+ // the time of override.
105
+ //
106
+ // Handle (a) by auto-promoting `cursor-local-chief-engineer` (the
107
+ // historically-trusted critic) in the full list. Handle (b) by
108
+ // additionally promoting one critic per all-optional profile so
109
+ // every profile carries at least one required critic. The promoted
110
+ // ids surface in the `aggregation_policy_overridden` telemetry
111
+ // event's `autoPromotedCritics` array — that's the audit trail
112
+ // operators grep `_runs.ndjson` for (CONTRIBUTING.md "Audit trail"
113
+ // section). The stderr `warn` callback fires only on the
114
+ // REFUSED-fail-closed and unrecognized-value paths; successful
115
+ // promotions are observable in telemetry, not stderr (avoiding
116
+ // noise on every push when the override is the normal path).
117
+ // (Codex finding on 7e780bd3 / security §11 is closed by the
118
+ // promotion logic; visibility is via telemetry, not stderr.)
119
+ if (!critics.some((c) => c.required)) {
120
+ critics = critics.map((c) => c.id === AUTO_PROMOTE_CRITIC_ID ? { ...c, required: true } : c);
121
+ if (critics.some((c) => c.id === AUTO_PROMOTE_CRITIC_ID && c.required)) {
122
+ autoPromotedCritics.push(AUTO_PROMOTE_CRITIC_ID);
123
+ }
124
+ }
125
+ // Profile coverage check — required for the case where the on-disk
126
+ // config has a required critic OUTSIDE the active-profile set, or
127
+ // where cursor (the auto-promotion target) isn't in every profile.
128
+ if (config.profiles) {
129
+ const requiredIds = new Set(critics.filter((c) => c.required).map((c) => c.id));
130
+ // Sort profile names for deterministic auto-promotion order
131
+ // (mirrors schema.ts:947 sort for determinism in error messages).
132
+ const profileNames = Object.keys(config.profiles).sort();
133
+ for (const name of profileNames) {
134
+ const profile = config.profiles[name];
135
+ const hasRequired = profile.criticIds.some((id) => requiredIds.has(id));
136
+ if (!hasRequired) {
137
+ // Promote the FIRST critic in the profile's `criticIds` list.
138
+ // The profile author defined this ordering; respecting it is
139
+ // less surprising than picking alphabetically or by adapter.
140
+ const promoteTarget = profile.criticIds[0];
141
+ if (promoteTarget !== undefined) {
142
+ critics = critics.map((c) => c.id === promoteTarget ? { ...c, required: true } : c);
143
+ if (!autoPromotedCritics.includes(promoteTarget)) {
144
+ autoPromotedCritics.push(promoteTarget);
145
+ }
146
+ requiredIds.add(promoteTarget);
147
+ }
148
+ }
149
+ }
150
+ }
151
+ // FAIL-CLOSED CHECK — Codex critic HIGH on 33dcb1b9 (security §0).
152
+ //
153
+ // If the override would result in a `block-if-any` config with ZERO
154
+ // required critics (neither the hard-coded cursor target nor any
155
+ // profile-first critic was promotable), refuse the override outright.
156
+ // Without this, the gate would silently downgrade blocker findings
157
+ // to warnings — the exact unsafe runtime state the auto-promotion
158
+ // is meant to prevent. Operators get a clear warning and the on-disk
159
+ // policy stands; they can re-apply after promoting a critic on-disk.
160
+ //
161
+ // This rejection only fires for the no-profiles + no-cursor edge
162
+ // case (the schema's profile-coverage invariant catches the rest at
163
+ // load time). Returning `applied: false` keeps the result type
164
+ // identical to the unset/garbage paths so the caller's audit code
165
+ // doesn't need a new branch.
166
+ if (!critics.some((c) => c.required)) {
167
+ if (options.warn) {
168
+ options.warn(`${POLICY_OVERRIDE_ENV}=block-if-any: REFUSED — no critic could be auto-promoted to required. ` +
169
+ `Neither "${AUTO_PROMOTE_CRITIC_ID}" nor any profile-first critic was available in this config. ` +
170
+ `Under block-if-any with zero required critics, the gate would silently downgrade blocker ` +
171
+ `findings to warnings, defeating the revert's safety purpose. ` +
172
+ `Falling through to configured policy "${configured}". ` +
173
+ `Promote a critic on-disk (set \`required: true\`) before re-applying the revert.`);
174
+ }
175
+ return {
176
+ config,
177
+ applied: false,
178
+ configured,
179
+ autoPromotedCritics: [],
180
+ };
181
+ }
182
+ }
183
+ // Build the new aggregation block. Schema invariants (per
184
+ // parseAgentReviewConfig):
185
+ // - `block-if-any` REJECTS a `quorum` field (a stale quorum after
186
+ // a policy roll-back would be a silent foot-gun).
187
+ // - `min-complete-quorum` REQUIRES a quorum integer >= 2.
188
+ //
189
+ // To keep the result config round-trippable through the same schema
190
+ // validation, strip `quorum` on `block-if-any` overrides. For
191
+ // `min-complete-quorum` overrides, preserve a pre-existing valid
192
+ // `quorum` OR synthesize one from the critic count when the source
193
+ // config had `block-if-any` shape (no quorum) — refusing the override
194
+ // would be hostile to operators who actually want to PIN
195
+ // min-complete-quorum during a calibration window. The synthesized
196
+ // value MUST satisfy the schema's `>= 2 && <= critics.length` bounds.
197
+ // (Codex HIGH on c849d47d / schema §0.)
198
+ let newAggregation;
199
+ if (raw === "block-if-any") {
200
+ newAggregation = {
201
+ policy: raw,
202
+ blockingSeverities: config.aggregation.blockingSeverities,
203
+ };
204
+ }
205
+ else {
206
+ // raw === "min-complete-quorum"
207
+ const sourceQuorum = config.aggregation.quorum;
208
+ const valid = typeof sourceQuorum === "number" &&
209
+ Number.isInteger(sourceQuorum) &&
210
+ sourceQuorum >= 2 &&
211
+ sourceQuorum <= critics.length;
212
+ if (valid) {
213
+ newAggregation = {
214
+ ...config.aggregation,
215
+ policy: raw,
216
+ };
217
+ }
218
+ else if (critics.length >= 2) {
219
+ // Synthesize a safe quorum: 2 (the schema minimum). The profile
220
+ // selector overrides this at runtime if a profile is active;
221
+ // otherwise the runner uses this for hypothetical-quorum
222
+ // telemetry. Safe per schema: 2 <= critics.length is guaranteed
223
+ // by the branch condition.
224
+ newAggregation = {
225
+ ...config.aggregation,
226
+ policy: raw,
227
+ quorum: 2,
228
+ };
229
+ }
230
+ else {
231
+ // critics.length < 2 — cannot synthesize a schema-valid quorum.
232
+ // Refuse the override and fall through to configured policy.
233
+ if (options.warn) {
234
+ options.warn(`${POLICY_OVERRIDE_ENV}=min-complete-quorum: REFUSED — cannot synthesize a schema-valid quorum ` +
235
+ `(min-complete-quorum requires quorum >= 2 and <= critics.length, but only ${critics.length} ` +
236
+ `critic(s) are configured). Falling through to configured policy "${configured}". ` +
237
+ `Add a second critic to the config or pin quorum on-disk before re-applying the revert.`);
238
+ }
239
+ return {
240
+ config,
241
+ applied: false,
242
+ configured,
243
+ autoPromotedCritics: [],
244
+ };
245
+ }
246
+ }
247
+ const newConfig = {
248
+ ...config,
249
+ critics,
250
+ aggregation: newAggregation,
251
+ };
252
+ return {
253
+ config: newConfig,
254
+ applied: true,
255
+ configured,
256
+ overridden: raw,
257
+ autoPromotedCritics,
258
+ };
259
+ }
260
+ function isAggregationPolicy(s) {
261
+ return AGGREGATION_POLICIES.includes(s);
262
+ }
263
+ export async function loadAgentReviewConfig(options = {}) {
264
+ const root = await repoRoot(options.cwd ?? process.cwd());
265
+ const configPath = resolve(root, CONFIG_RELATIVE_PATH);
266
+ if (!existsSync(configPath)) {
267
+ throw new Error(`agent-review config not found at ${configPath}. ` +
268
+ "Run from a repository that contains .agent-review/config.json or copy the template.");
269
+ }
270
+ const raw = readFileSync(configPath, "utf8");
271
+ const parsedConfig = parseConfigJson(raw, configPath);
272
+ if (options.validateGuidanceFiles ?? true) {
273
+ const missing = [];
274
+ for (const rel of parsedConfig.context.guidanceFiles) {
275
+ if (!existsSync(resolve(root, rel)))
276
+ missing.push(rel);
277
+ }
278
+ for (const rel of parsedConfig.context.promptFragments) {
279
+ if (!existsSync(resolve(root, rel)))
280
+ missing.push(rel);
281
+ }
282
+ if (missing.length > 0) {
283
+ throw new Error(`agent-review config references missing guidance files: ${missing.join(", ")}`);
284
+ }
285
+ }
286
+ // Cycle 322.7 Phase C — apply env-var overrides AFTER schema parse
287
+ // but BEFORE the runner sees the config. The override path is
288
+ // applied here (not deeper) so all consumers of `LoadedConfig`
289
+ // (runReview, runCommitGate, evals) see the overridden policy
290
+ // automatically without needing to re-thread the env source.
291
+ const overrideResult = applyEnvOverrides(parsedConfig, options.env ?? process.env, {
292
+ ...(options.warn !== undefined ? { warn: options.warn } : {}),
293
+ });
294
+ return {
295
+ config: overrideResult.config,
296
+ repoRoot: root,
297
+ configPath,
298
+ ...(overrideResult.applied
299
+ ? {
300
+ policyOverride: {
301
+ configured: overrideResult.configured,
302
+ // `overridden` is guaranteed-set when applied=true; the
303
+ // type system can't see that, so we assert here.
304
+ overridden: overrideResult.overridden,
305
+ autoPromotedCritics: overrideResult.autoPromotedCritics,
306
+ },
307
+ }
308
+ : {}),
309
+ };
310
+ }
311
+ // Load config from a specific git ref (e.g., the parent of the reviewed
312
+ // commit). Used by the self-modification guard in `runReview`: if the
313
+ // commit being reviewed touches `.agent-review/**`, we refuse to use HEAD's
314
+ // (potentially-weakened) policy and instead review against the prior baseline.
315
+ //
316
+ // Throws if the ref doesn't have the config file, leaving the caller to
317
+ // decide how to handle (typically: fall back to working-tree policy with a
318
+ // loud warning, since the commit is introducing the config for the first
319
+ // time).
320
+ export async function loadAgentReviewConfigFromRef(rootDir, ref, options = {}) {
321
+ const raw = await gitShowFile(ref, CONFIG_RELATIVE_PATH, rootDir);
322
+ if (raw === null) {
323
+ throw new Error(`agent-review config not present at ref ${ref}:${CONFIG_RELATIVE_PATH}`);
324
+ }
325
+ const parsedConfig = parseConfigJson(raw, `${ref}:${CONFIG_RELATIVE_PATH}`);
326
+ // Cycle 322.7 Phase C — env override applies to the policy-baseline
327
+ // path too. The self-modification guard wants to evaluate against
328
+ // the parent ref's config, but the operator's emergency revert
329
+ // still applies: if the env says block-if-any, the parent-ref
330
+ // policy is also stamped to block-if-any. Without this, an operator
331
+ // who reverted to block-if-any via env would see the gate run
332
+ // min-complete-quorum against a baseline that triggered the
333
+ // self-modification guard — inconsistent and confusing.
334
+ const overrideResult = applyEnvOverrides(parsedConfig, options.env ?? process.env, {
335
+ ...(options.warn !== undefined ? { warn: options.warn } : {}),
336
+ });
337
+ return {
338
+ config: overrideResult.config,
339
+ repoRoot: rootDir,
340
+ configPath: `${ref}:${CONFIG_RELATIVE_PATH}`,
341
+ loadedFromRef: ref,
342
+ ...(overrideResult.applied
343
+ ? {
344
+ policyOverride: {
345
+ configured: overrideResult.configured,
346
+ overridden: overrideResult.overridden,
347
+ autoPromotedCritics: overrideResult.autoPromotedCritics,
348
+ },
349
+ }
350
+ : {}),
351
+ };
352
+ }
353
+ function parseConfigJson(raw, source) {
354
+ let parsed;
355
+ try {
356
+ parsed = JSON.parse(raw);
357
+ }
358
+ catch (err) {
359
+ throw new Error(`agent-review config at ${source} is not valid JSON: ${err.message}`);
360
+ }
361
+ return parseAgentReviewConfig(parsed);
362
+ }
363
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/policy/config.ts"],"names":[],"mappings":"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"}