@momentiq/dark-factory-cli 2.17.0 → 3.0.0

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 (136) hide show
  1. package/dist/adapters/codex-sdk.d.ts +30 -5
  2. package/dist/adapters/codex-sdk.d.ts.map +1 -1
  3. package/dist/adapters/codex-sdk.js +50 -15
  4. package/dist/adapters/codex-sdk.js.map +1 -1
  5. package/dist/adapters/cursor-sdk.d.ts.map +1 -1
  6. package/dist/adapters/cursor-sdk.js +14 -2
  7. package/dist/adapters/cursor-sdk.js.map +1 -1
  8. package/dist/adapters/index.d.ts +1 -0
  9. package/dist/adapters/index.d.ts.map +1 -1
  10. package/dist/adapters/index.js +6 -0
  11. package/dist/adapters/index.js.map +1 -1
  12. package/dist/adapters/mode-scope.d.ts +86 -0
  13. package/dist/adapters/mode-scope.d.ts.map +1 -0
  14. package/dist/adapters/mode-scope.js +456 -0
  15. package/dist/adapters/mode-scope.js.map +1 -0
  16. package/dist/branch-protection/audit_branch_protection.py +80 -2
  17. package/dist/cli.d.ts +5 -0
  18. package/dist/cli.d.ts.map +1 -1
  19. package/dist/cli.js +31 -1
  20. package/dist/cli.js.map +1 -1
  21. package/dist/commands/mode.d.ts +2 -0
  22. package/dist/commands/mode.d.ts.map +1 -0
  23. package/dist/commands/mode.js +235 -0
  24. package/dist/commands/mode.js.map +1 -0
  25. package/dist/commands/objectives.d.ts +27 -3
  26. package/dist/commands/objectives.d.ts.map +1 -1
  27. package/dist/commands/objectives.js +347 -53
  28. package/dist/commands/objectives.js.map +1 -1
  29. package/dist/commands/onboard.d.ts.map +1 -1
  30. package/dist/commands/onboard.js +3 -1
  31. package/dist/commands/onboard.js.map +1 -1
  32. package/dist/cycle-doc-validator/validate_cycle_doc.py +128 -11
  33. package/dist/doctor.d.ts.map +1 -1
  34. package/dist/doctor.js +15 -7
  35. package/dist/doctor.js.map +1 -1
  36. package/dist/evidence/index.d.ts +1 -0
  37. package/dist/evidence/index.d.ts.map +1 -1
  38. package/dist/evidence/index.js +5 -0
  39. package/dist/evidence/index.js.map +1 -1
  40. package/dist/evidence/ui-visual/embed.d.ts +65 -0
  41. package/dist/evidence/ui-visual/embed.d.ts.map +1 -0
  42. package/dist/evidence/ui-visual/embed.js +176 -0
  43. package/dist/evidence/ui-visual/embed.js.map +1 -0
  44. package/dist/evidence/ui-visual/index.d.ts +2 -0
  45. package/dist/evidence/ui-visual/index.d.ts.map +1 -0
  46. package/dist/evidence/ui-visual/index.js +6 -0
  47. package/dist/evidence/ui-visual/index.js.map +1 -0
  48. package/dist/index.d.ts +1 -1
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +1 -1
  51. package/dist/index.js.map +1 -1
  52. package/dist/mcp/cycle-doc/parser.d.ts +3 -3
  53. package/dist/mcp/cycle-doc/parser.d.ts.map +1 -1
  54. package/dist/mcp/cycle-doc/parser.js +2 -2
  55. package/dist/mcp/cycle-doc/parser.js.map +1 -1
  56. package/dist/mcp/tools/doctor.d.ts +5 -0
  57. package/dist/mcp/tools/doctor.d.ts.map +1 -1
  58. package/dist/mcp/tools/doctor.js +10 -1
  59. package/dist/mcp/tools/doctor.js.map +1 -1
  60. package/dist/mcp/tools/review-bypass.d.ts +5 -0
  61. package/dist/mcp/tools/review-bypass.d.ts.map +1 -1
  62. package/dist/mcp/tools/review-bypass.js +9 -1
  63. package/dist/mcp/tools/review-bypass.js.map +1 -1
  64. package/dist/mode/apply.d.ts +8 -0
  65. package/dist/mode/apply.d.ts.map +1 -0
  66. package/dist/mode/apply.js +118 -0
  67. package/dist/mode/apply.js.map +1 -0
  68. package/dist/mode/context.d.ts +50 -0
  69. package/dist/mode/context.d.ts.map +1 -0
  70. package/dist/mode/context.js +127 -0
  71. package/dist/mode/context.js.map +1 -0
  72. package/dist/mode/critic-registration.d.ts +66 -0
  73. package/dist/mode/critic-registration.d.ts.map +1 -0
  74. package/dist/mode/critic-registration.js +279 -0
  75. package/dist/mode/critic-registration.js.map +1 -0
  76. package/dist/mode/guard.d.ts +15 -0
  77. package/dist/mode/guard.d.ts.map +1 -0
  78. package/dist/mode/guard.js +160 -0
  79. package/dist/mode/guard.js.map +1 -0
  80. package/dist/mode/init.d.ts +17 -0
  81. package/dist/mode/init.d.ts.map +1 -0
  82. package/dist/mode/init.js +147 -0
  83. package/dist/mode/init.js.map +1 -0
  84. package/dist/mode/loadConfig.d.ts +34 -0
  85. package/dist/mode/loadConfig.d.ts.map +1 -0
  86. package/dist/mode/loadConfig.js +38 -0
  87. package/dist/mode/loadConfig.js.map +1 -0
  88. package/dist/mode/prompts/designer-baseline.md +47 -0
  89. package/dist/mode/resolve.d.ts +24 -0
  90. package/dist/mode/resolve.d.ts.map +1 -0
  91. package/dist/mode/resolve.js +65 -0
  92. package/dist/mode/resolve.js.map +1 -0
  93. package/dist/mode/scope-policy.d.ts +41 -0
  94. package/dist/mode/scope-policy.d.ts.map +1 -0
  95. package/dist/mode/scope-policy.js +97 -0
  96. package/dist/mode/scope-policy.js.map +1 -0
  97. package/dist/mode/status.d.ts +14 -0
  98. package/dist/mode/status.d.ts.map +1 -0
  99. package/dist/mode/status.js +13 -0
  100. package/dist/mode/status.js.map +1 -0
  101. package/dist/onboard/seeders/design-md.d.ts +3 -0
  102. package/dist/onboard/seeders/design-md.d.ts.map +1 -0
  103. package/dist/onboard/seeders/design-md.js +138 -0
  104. package/dist/onboard/seeders/design-md.js.map +1 -0
  105. package/dist/onboard/seeders/index.d.ts +8 -0
  106. package/dist/onboard/seeders/index.d.ts.map +1 -1
  107. package/dist/onboard/seeders/index.js +3 -0
  108. package/dist/onboard/seeders/index.js.map +1 -1
  109. package/dist/onboard/validate.d.ts +36 -1
  110. package/dist/onboard/validate.d.ts.map +1 -1
  111. package/dist/onboard/validate.js +136 -42
  112. package/dist/onboard/validate.js.map +1 -1
  113. package/dist/skills/config.d.ts +2 -110
  114. package/dist/skills/config.d.ts.map +1 -1
  115. package/dist/skills/config.js +20 -58
  116. package/dist/skills/config.js.map +1 -1
  117. package/dist/skills/index.d.ts +1 -1
  118. package/dist/skills/index.d.ts.map +1 -1
  119. package/dist/skills/index.js +1 -1
  120. package/dist/skills/index.js.map +1 -1
  121. package/dist/skills/install.d.ts.map +1 -1
  122. package/dist/skills/install.js +7 -0
  123. package/dist/skills/install.js.map +1 -1
  124. package/package.json +3 -3
  125. package/skills/designer-brief/SKILL.md.tmpl +74 -0
  126. package/skills/designer-brief/skill.json +17 -0
  127. package/skills/designer-build/SKILL.md.tmpl +63 -0
  128. package/skills/designer-build/skill.json +17 -0
  129. package/skills/designer-handoff/SKILL.md.tmpl +64 -0
  130. package/skills/designer-handoff/skill.json +17 -0
  131. package/skills/designer-ship/SKILL.md.tmpl +77 -0
  132. package/skills/designer-ship/skill.json +17 -0
  133. package/skills/objectives/SKILL.md.tmpl +34 -6
  134. package/skills/verify/producer/ui-visual-surfaces.ts +261 -0
  135. package/skills/verify/producer/ui-visual.config.ts +22 -0
  136. package/skills/verify/producer/ui-visual.producer.spec.ts +137 -0
@@ -0,0 +1,86 @@
1
+ import { type CriticConfig, type CriticResult, type DarkFactoryConfig, type DoctorCheck, type ReviewPacket } from "@momentiq/dark-factory-schemas";
2
+ import type { CriticAdapter, CriticReviewOptions } from "./critic.js";
3
+ export declare const MODE_SCOPE_ADAPTER_ID = "mode-scope";
4
+ /** The git-trailer key that activates this critic (case-insensitive). */
5
+ export declare const MODE_TRAILER_KEY = "mode";
6
+ /**
7
+ * Count changed (added + removed) content lines in a unified diff — the churn
8
+ * the `diffBudget.maxLines` limit governs.
9
+ *
10
+ * HUNK-AWARE, not a global prefix filter. A `---`/`+++` prefix means a file
11
+ * marker ONLY in a file's header region; inside a hunk body those same
12
+ * prefixes are real content — a REMOVED line whose text starts with `--`
13
+ * renders as `---…` (e.g. deleting a CSS custom property `--token: …`), and an
14
+ * ADDED line whose text starts with `++` renders as `+++…`. A blanket
15
+ * "skip every `---`/`+++` line" undercounts that churn and lets an over-budget
16
+ * operator-mode PR slip under `maxLines`. So we track hunk state:
17
+ *
18
+ * - `diff --git a/… b/…` begins a new file → HEADER mode. `packet.diff` is
19
+ * produced by `git diff --patch` (git.ts), which ALWAYS emits this
20
+ * per-file separator, so it is the authoritative file boundary — the
21
+ * `--- a/…`/`+++ b/…` markers that follow are skipped as headers.
22
+ * - `@@ …` (or `@@@` for a combined diff) opens a hunk → BODY mode.
23
+ * - HEADER mode: every line is metadata (`index`, `--- a/…`, `+++ b/…`,
24
+ * mode/rename/binary lines). None is churn — count nothing.
25
+ * - BODY mode: every line carries a leading ` `/`+`/`-`/`\` column, so a
26
+ * `+`/`-` line is UNAMBIGUOUSLY added/removed content (counted regardless
27
+ * of what follows the marker), ` ` is context and `\ No newline…` a marker
28
+ * (skipped).
29
+ *
30
+ * Monotonic under truncation: `packet.diff` may be capped (`diffTruncated`),
31
+ * in which case this returns a LOWER BOUND on the true churn — safe for the
32
+ * budget check (a truncated diff can only hide MORE churn), and the caller
33
+ * fails closed on an under-visible-budget-but-truncated diff. `maxFiles`
34
+ * (counted from the untruncated `changedFiles` list) is the coarse backstop.
35
+ *
36
+ * Exported for unit-testability.
37
+ */
38
+ export declare function countDiffLines(diff: string): number;
39
+ export interface ModeScopeAdapterOptions {
40
+ /**
41
+ * Catalog loader override — injected in tests so a unit test can supply a
42
+ * `modes` catalog without writing a darkfactory.yaml to disk. Defaults to
43
+ * reading `<repoRoot>/darkfactory.yaml` and parsing it through the
44
+ * canonical schemas parser. Throwing here (e.g. file missing) is caught by
45
+ * `review()` and surfaced as a fail-closed `blocker`.
46
+ */
47
+ loadCatalog?: (repoRoot: string) => DarkFactoryConfig;
48
+ }
49
+ export declare class ModeScopeAdapter implements CriticAdapter {
50
+ readonly id = "mode-scope";
51
+ readonly requiredEnvVars: readonly string[];
52
+ private readonly loadCatalog;
53
+ constructor(options?: ModeScopeAdapterOptions);
54
+ review(packet: ReviewPacket, critic: CriticConfig, options: CriticReviewOptions): Promise<CriticResult>;
55
+ /**
56
+ * Resolve the active `Mode:` trailer for the reviewed change, or undefined
57
+ * (→ no-op). Two sources, in order:
58
+ *
59
+ * 1. `packet.commit` — the reviewed commit's own message. In a local
60
+ * `df gate-push` / `df review`, HEAD is the contributor's real commit,
61
+ * so this is authoritative and needs no git I/O.
62
+ * 2. The reviewed RANGE (`packet.range` = `parent..HEAD`). In CI the
63
+ * `agent-critic` checkout's HEAD is a synthetic merge ref whose message
64
+ * lacks the trailer, but the contributor's commit lives INSIDE the
65
+ * range — so scan every commit message there (newest-first; first match
66
+ * wins). Reuses `commitMessagesInRange`, the established trailer-walk
67
+ * helper. Any commit declaring a mode enforces it on the whole PR diff.
68
+ *
69
+ * ALWAYS-COMPLETE: the range scan shells out to git; any failure (not a
70
+ * repo, unreachable range) is caught and degrades to the source-1 result —
71
+ * never throws, never worse than reading HEAD alone. A root commit (bare
72
+ * `sha` range, no `..`) skips the scan rather than walking all history.
73
+ */
74
+ private resolveModeTrailer;
75
+ /**
76
+ * Pure core. NEVER throws — the mode branch is wrapped so any unexpected
77
+ * failure fails CLOSED as a blocker rather than propagating an error
78
+ * status (always-complete invariant). `rawMode` is pre-resolved by
79
+ * `resolveModeTrailer` (undefined → no-op).
80
+ */
81
+ private computeFindings;
82
+ private validateDiff;
83
+ private summaryFor;
84
+ doctor(_critic: CriticConfig): Promise<DoctorCheck[]>;
85
+ }
86
+ //# sourceMappingURL=mode-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode-scope.d.ts","sourceRoot":"","sources":["../../src/adapters/mode-scope.ts"],"names":[],"mappings":"AAmEA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAGhB,KAAK,YAAY,EAClB,MAAM,gCAAgC,CAAC;AAMxC,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEtE,eAAO,MAAM,qBAAqB,eAAe,CAAC;AAElD,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,SAAS,CAAC;AAKvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqBnD;AAuED,MAAM,WAAW,uBAAuB;IACtC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,iBAAiB,CAAC;CACvD;AAOD,qBAAa,gBAAiB,YAAW,aAAa;IACpD,QAAQ,CAAC,EAAE,gBAAyB;IACpC,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAM;IAEjD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0C;gBAE1D,OAAO,GAAE,uBAA4B;IAI3C,MAAM,CACV,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,YAAY,CAAC;IAiDxB;;;;;;;;;;;;;;;;;;OAkBG;YACW,kBAAkB;IAiChC;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAsEvB,OAAO,CAAC,YAAY;IAgFpB,OAAO,CAAC,UAAU;IAoBZ,MAAM,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;CAqB5D"}
@@ -0,0 +1,456 @@
1
+ // Cycle 27 Phase C — deterministic operator-mode scope critic.
2
+ //
3
+ // The hard gate of Designer Mode (spec §4.3 item 5 / §4.4 layer 4). A
4
+ // designer-driven PR carries a `Mode: <name>` git trailer on one of its
5
+ // commits; this adapter reads that trailer, loads the repo's `modes`
6
+ // catalog from darkfactory.yaml, and validates the DIFF against the named
7
+ // mode's writeScopes / protectedPaths / diffBudget / dependencyPolicy.
8
+ // Any violation is a `blocker` — out-of-scope code cannot merge even from
9
+ // a jailbroken local session, because this runs inside the required CI
10
+ // `agent-critic` check + the local pre-push gate.
11
+ //
12
+ // TRAILER RESOLUTION across the reviewed range (why not just HEAD). In a
13
+ // local `df gate-push` / `df review`, HEAD IS the contributor's commit, so
14
+ // its trailer is authoritative. But in CI the `agent-critic` reusable
15
+ // workflow reviews a `pull_request` (and `merge_group`) checkout whose HEAD
16
+ // is a SYNTHETIC merge ref — its commit message does NOT carry the
17
+ // contributor's `Mode:` trailer. Reading only `packet.commit` there would
18
+ // silently no-op and layer 4 would not exist. So the trailer is resolved by
19
+ // scanning EVERY commit message in the reviewed range (`packet.range` =
20
+ // `parent..HEAD`, the exact range whose diff this critic already judges),
21
+ // reusing `commitMessagesInRange` — the established trailer-walk pattern.
22
+ // The range contains the PR's real commits on every event, so the trailer
23
+ // is found regardless of the merge-ref HEAD.
24
+ //
25
+ // Posture (mirrors adapters/static-schema-lint.ts — the precedent
26
+ // deterministic adapter):
27
+ // - `requiredEnvVars: []` — no auth, no network, no LLM call.
28
+ // - Pure function over the packet (+ the on-disk catalog it names): same
29
+ // input → same output. No retries, no flake, no calibration window.
30
+ // - Registered `required: true` in the consumer config (a HARD GATE, wired
31
+ // by `df mode init`): its `blocker` findings veto under EVERY aggregation
32
+ // policy — including `block-if-any`, which demotes an optional critic's
33
+ // findings to warnings. Safe under `min-complete-quorum` too: it always
34
+ // completes, so it only ADDS a veto, never removes a quorum slot.
35
+ //
36
+ // THE NO-OP SAFETY INVARIANT (proven by unit test — an injected catalog
37
+ // loader that throws if reached stays unreached): a reviewed range with NO
38
+ // `Mode:` trailer on any commit is judged as a normal PR — the adapter
39
+ // returns APPROVED with zero findings and performs NO catalog I/O. This is
40
+ // what makes it safe to register in a repo's live critic fleet: it cannot
41
+ // destabilize the engineer path. (Note: this adapter is intentionally NOT
42
+ // wired into THIS repo's own `.agent-review/config.json`, so this PR's CI
43
+ // `agent-critic` run does not exercise it — the unit test is the no-op
44
+ // proof, not CI here.)
45
+ //
46
+ // ALWAYS-COMPLETE INVARIANT: the adapter NEVER returns `status: "error"`
47
+ // and never throws out of `review()`. When a `Mode:` trailer names a mode
48
+ // that cannot be validated (missing/unparseable catalog, unknown mode,
49
+ // hostile slug), it FAILS CLOSED as a `blocker` on a `status: "complete"`
50
+ // result — never as an error. This matters for quorum math: a
51
+ // deterministic critic that sometimes errored would drop the completed
52
+ // count below quorum and could wedge the gate. It always completes, so it
53
+ // only ever ADDS a veto, never removes a quorum slot.
54
+ //
55
+ // Hosted-App-gated consumers (spec §4.3.5): whether the hosted worker
56
+ // (private `dark-factory-worker`, authoritative check `dark-factory/critic`)
57
+ // honors consumer-config deterministic adapters is NOT verifiable from this
58
+ // OSS repo. Until it is, a dogfood repo MUST keep the CI `agent-critic`
59
+ // required check (where this adapter runs via the consumer's
60
+ // `.agent-review/config.json`) rather than relying on the non-required
61
+ // hosted `dark-factory/critic` for scope enforcement. See docs/CONSUMER-
62
+ // ADOPTION.md §13.7 and this PR's body.
63
+ import { readFileSync } from "node:fs";
64
+ import { join } from "node:path";
65
+ import { parse as parseYaml } from "yaml";
66
+ import { MODE_NAME_RE, parseDarkFactoryConfig, } from "@momentiq/dark-factory-schemas";
67
+ import { collectChangedPaths, getTrailer, parseCommitTrailers } from "../evidence/per-sha.js";
68
+ import { commitMessagesInRange } from "../git.js";
69
+ import { classifyModePath } from "../mode/scope-policy.js";
70
+ import { resolveModeWithConfig } from "../mode/resolve.js";
71
+ export const MODE_SCOPE_ADAPTER_ID = "mode-scope";
72
+ /** The git-trailer key that activates this critic (case-insensitive). */
73
+ export const MODE_TRAILER_KEY = "mode";
74
+ // ---------------------------------------------------------------------------
75
+ // Diff line counting.
76
+ /**
77
+ * Count changed (added + removed) content lines in a unified diff — the churn
78
+ * the `diffBudget.maxLines` limit governs.
79
+ *
80
+ * HUNK-AWARE, not a global prefix filter. A `---`/`+++` prefix means a file
81
+ * marker ONLY in a file's header region; inside a hunk body those same
82
+ * prefixes are real content — a REMOVED line whose text starts with `--`
83
+ * renders as `---…` (e.g. deleting a CSS custom property `--token: …`), and an
84
+ * ADDED line whose text starts with `++` renders as `+++…`. A blanket
85
+ * "skip every `---`/`+++` line" undercounts that churn and lets an over-budget
86
+ * operator-mode PR slip under `maxLines`. So we track hunk state:
87
+ *
88
+ * - `diff --git a/… b/…` begins a new file → HEADER mode. `packet.diff` is
89
+ * produced by `git diff --patch` (git.ts), which ALWAYS emits this
90
+ * per-file separator, so it is the authoritative file boundary — the
91
+ * `--- a/…`/`+++ b/…` markers that follow are skipped as headers.
92
+ * - `@@ …` (or `@@@` for a combined diff) opens a hunk → BODY mode.
93
+ * - HEADER mode: every line is metadata (`index`, `--- a/…`, `+++ b/…`,
94
+ * mode/rename/binary lines). None is churn — count nothing.
95
+ * - BODY mode: every line carries a leading ` `/`+`/`-`/`\` column, so a
96
+ * `+`/`-` line is UNAMBIGUOUSLY added/removed content (counted regardless
97
+ * of what follows the marker), ` ` is context and `\ No newline…` a marker
98
+ * (skipped).
99
+ *
100
+ * Monotonic under truncation: `packet.diff` may be capped (`diffTruncated`),
101
+ * in which case this returns a LOWER BOUND on the true churn — safe for the
102
+ * budget check (a truncated diff can only hide MORE churn), and the caller
103
+ * fails closed on an under-visible-budget-but-truncated diff. `maxFiles`
104
+ * (counted from the untruncated `changedFiles` list) is the coarse backstop.
105
+ *
106
+ * Exported for unit-testability.
107
+ */
108
+ export function countDiffLines(diff) {
109
+ if (!diff)
110
+ return 0;
111
+ let count = 0;
112
+ let inHunk = false;
113
+ for (const line of diff.split("\n")) {
114
+ if (line.startsWith("diff --git ")) {
115
+ // New file section — reset to header mode so the following `--- a/…` /
116
+ // `+++ b/…` markers are treated as headers, not churn.
117
+ inHunk = false;
118
+ continue;
119
+ }
120
+ if (line.startsWith("@@")) {
121
+ inHunk = true; // hunk header — opens the body; not itself churn.
122
+ continue;
123
+ }
124
+ if (!inHunk)
125
+ continue; // header region — metadata only, never churn.
126
+ if (line.startsWith("\\"))
127
+ continue; // "".
128
+ if (line.startsWith("+") || line.startsWith("-"))
129
+ count++;
130
+ // else: context (leading space) or blank separator — not churn.
131
+ }
132
+ return count;
133
+ }
134
+ // ---------------------------------------------------------------------------
135
+ // Finding construction.
136
+ const CATEGORY = "mode-scope";
137
+ // The catalog file — the natural anchor for findings not tied to a single
138
+ // diffed path (diff-budget overruns, fail-closed catalog errors).
139
+ const CATALOG_FILE = "darkfactory.yaml";
140
+ function blocker(args) {
141
+ return {
142
+ severity: "blocker",
143
+ category: CATEGORY,
144
+ file: args.file,
145
+ // A concrete `line` is REQUIRED for a blocker to survive the v2
146
+ // `enforceFindingRubric` stripper (the `file + line + evidence`
147
+ // code-review shape — see policy/gate.ts). Without it the gate would
148
+ // strip the blocker and FAIL OPEN. Scope violations implicate the file
149
+ // as a whole, so line 1 is the honest anchor — the same convention
150
+ // static-schema-lint uses for its whole-file findings.
151
+ line: args.line ?? 1,
152
+ evidence: args.evidence,
153
+ impact: args.impact,
154
+ requiredFix: args.requiredFix,
155
+ };
156
+ }
157
+ const HANDOFF_FIX = "Keep the change inside the mode's write scope, or escalate the out-of-scope part to an engineer via `df handoff` and drop it from this PR.";
158
+ function pathViolationFinding(path, modeName, kind) {
159
+ switch (kind) {
160
+ case "protected":
161
+ return blocker({
162
+ file: path,
163
+ evidence: `"${path}" matches a \`protectedPaths\` pattern for mode "${modeName}" (protectedPaths always wins over writeScopes on overlap).`,
164
+ impact: `Mode "${modeName}" is explicitly barred from protected areas (auth, api, migrations, CI, config, dependency manifests). A designer-driven change here would touch a security- or infra-sensitive surface without engineer review — exactly the vibe-platform failure Dark Factory exists to prevent.`,
165
+ requiredFix: HANDOFF_FIX,
166
+ });
167
+ case "dependency":
168
+ return blocker({
169
+ file: path,
170
+ evidence: `"${path}" is a dependency manifest/lockfile and mode "${modeName}" declares \`dependencyPolicy: "none"\`.`,
171
+ impact: `Adding or changing installed packages under an operator mode with dependencyPolicy "none" is out of bounds — dependency changes are an engineer surface (supply-chain + build risk).`,
172
+ requiredFix: HANDOFF_FIX,
173
+ });
174
+ case "out-of-scope":
175
+ return blocker({
176
+ file: path,
177
+ evidence: `"${path}" matches no \`writeScopes\` entry for mode "${modeName}".`,
178
+ impact: `Mode "${modeName}" may only edit files inside its declared writeScopes. An out-of-scope edit means the diff reaches code the operator is not authorized to change under this mode.`,
179
+ requiredFix: HANDOFF_FIX,
180
+ });
181
+ }
182
+ }
183
+ function defaultLoadCatalog(repoRoot) {
184
+ const text = readFileSync(join(repoRoot, "darkfactory.yaml"), "utf8");
185
+ return parseDarkFactoryConfig(parseYaml(text));
186
+ }
187
+ export class ModeScopeAdapter {
188
+ id = MODE_SCOPE_ADAPTER_ID;
189
+ requiredEnvVars = [];
190
+ loadCatalog;
191
+ constructor(options = {}) {
192
+ this.loadCatalog = options.loadCatalog ?? defaultLoadCatalog;
193
+ }
194
+ async review(packet, critic, options) {
195
+ const startMs = Date.now();
196
+ options.emit?.({
197
+ ts: new Date().toISOString(),
198
+ event: "critic_run_started",
199
+ commit: packet.commit.sha,
200
+ criticId: critic.id,
201
+ adapter: this.id,
202
+ model: critic.model.id,
203
+ });
204
+ const rawMode = await this.resolveModeTrailer(packet);
205
+ const findings = this.computeFindings(packet, rawMode);
206
+ const verdict = findings.some((f) => options.blockingSeverities.includes(f.severity))
207
+ ? "CHANGES_REQUESTED"
208
+ : "APPROVED";
209
+ const durationMs = Date.now() - startMs;
210
+ options.emit?.({
211
+ ts: new Date().toISOString(),
212
+ event: "critic_run_finished",
213
+ commit: packet.commit.sha,
214
+ criticId: critic.id,
215
+ adapter: this.id,
216
+ model: critic.model.id,
217
+ durationMs,
218
+ verdict,
219
+ });
220
+ return {
221
+ criticId: critic.id,
222
+ status: "complete",
223
+ verdict,
224
+ requiresHumanJudgment: false,
225
+ reviewer: {
226
+ name: critic.name,
227
+ adapter: critic.adapter,
228
+ model: critic.model,
229
+ runtime: critic.runtime,
230
+ },
231
+ summary: this.summaryFor(packet, rawMode, findings, verdict),
232
+ findings,
233
+ validation: { qualityGateResults: [], qualityGatesMissing: [] },
234
+ confidence: "high",
235
+ durationMs,
236
+ };
237
+ }
238
+ /**
239
+ * Resolve the active `Mode:` trailer for the reviewed change, or undefined
240
+ * (→ no-op). Two sources, in order:
241
+ *
242
+ * 1. `packet.commit` — the reviewed commit's own message. In a local
243
+ * `df gate-push` / `df review`, HEAD is the contributor's real commit,
244
+ * so this is authoritative and needs no git I/O.
245
+ * 2. The reviewed RANGE (`packet.range` = `parent..HEAD`). In CI the
246
+ * `agent-critic` checkout's HEAD is a synthetic merge ref whose message
247
+ * lacks the trailer, but the contributor's commit lives INSIDE the
248
+ * range — so scan every commit message there (newest-first; first match
249
+ * wins). Reuses `commitMessagesInRange`, the established trailer-walk
250
+ * helper. Any commit declaring a mode enforces it on the whole PR diff.
251
+ *
252
+ * ALWAYS-COMPLETE: the range scan shells out to git; any failure (not a
253
+ * repo, unreachable range) is caught and degrades to the source-1 result —
254
+ * never throws, never worse than reading HEAD alone. A root commit (bare
255
+ * `sha` range, no `..`) skips the scan rather than walking all history.
256
+ */
257
+ async resolveModeTrailer(packet) {
258
+ const fromHead = getTrailer(parseCommitTrailers(`${packet.commit.subject}\n\n${packet.commit.body}`), MODE_TRAILER_KEY);
259
+ if (fromHead !== undefined)
260
+ return fromHead;
261
+ try {
262
+ const sep = packet.range.indexOf("..");
263
+ if (sep < 0)
264
+ return undefined; // bounded ranges only — never walk all history
265
+ const base = packet.range.slice(0, sep);
266
+ const messages = await commitMessagesInRange(base, packet.commit.sha, packet.repoRoot);
267
+ for (const msg of messages) {
268
+ const m = getTrailer(parseCommitTrailers(msg), MODE_TRAILER_KEY);
269
+ if (m !== undefined)
270
+ return m;
271
+ }
272
+ }
273
+ catch {
274
+ // DELIBERATELY degrade to HEAD-only (fall through to undefined → no-op);
275
+ // do NOT fail closed here. A git failure means the range is unreadable
276
+ // (packet built outside a git repo — e.g. unit tests — or a transient git
277
+ // error). Failing CLOSED on this path would block EVERY normal engineer
278
+ // PR (which legitimately carries no `Mode:` trailer) on a git hiccup,
279
+ // breaking the no-op safety guarantee that makes this critic safe to
280
+ // register live — a far worse failure than the narrow, already-covered
281
+ // case this recovers. In CI the checkout is a real repo, so the scan
282
+ // succeeds and the merge-ref trailer is found; this degrade is the
283
+ // fail-safe for the rare unreadable-range case. (Fail-closed IS applied
284
+ // where it belongs: once a `Mode:` trailer IS resolved, an unvalidatable
285
+ // catalog/mode/budget fails closed as a blocker — see `computeFindings`.)
286
+ }
287
+ return undefined;
288
+ }
289
+ /**
290
+ * Pure core. NEVER throws — the mode branch is wrapped so any unexpected
291
+ * failure fails CLOSED as a blocker rather than propagating an error
292
+ * status (always-complete invariant). `rawMode` is pre-resolved by
293
+ * `resolveModeTrailer` (undefined → no-op).
294
+ */
295
+ computeFindings(packet, rawMode) {
296
+ // NO-OP SAFETY PATH — evaluated first, before any catalog I/O. No `Mode:`
297
+ // trailer anywhere in the reviewed range → normal PR: contribute nothing.
298
+ if (rawMode === undefined)
299
+ return [];
300
+ try {
301
+ // Hostile / malformed slug: never interpolate it into a filesystem
302
+ // path or catalog lookup. Fail closed.
303
+ if (!MODE_NAME_RE.test(rawMode)) {
304
+ return [
305
+ blocker({
306
+ file: CATALOG_FILE,
307
+ evidence: `Commit ${packet.commit.sha.slice(0, 12)} carries a \`Mode:\` trailer whose value ${JSON.stringify(rawMode)} is not a valid mode name (must match ${MODE_NAME_RE.source}).`,
308
+ impact: "A malformed Mode: trailer cannot be resolved to a catalog entry, so the diff cannot be proven in-scope. The scope critic fails closed rather than letting an unverifiable claim pass.",
309
+ requiredFix: "Fix the `Mode:` trailer to a valid lowercase mode slug (e.g. `Mode: designer`), or remove it if this is not an operator-mode PR.",
310
+ }),
311
+ ];
312
+ }
313
+ let config;
314
+ try {
315
+ config = this.loadCatalog(packet.repoRoot);
316
+ }
317
+ catch (err) {
318
+ return [
319
+ blocker({
320
+ file: CATALOG_FILE,
321
+ evidence: `Commit carries \`Mode: ${rawMode}\` but the modes catalog could not be loaded: ${err.message}`,
322
+ impact: "A PR that claims an operator mode must resolve to a committed catalog so the diff can be validated against its guardrails. A missing/unparseable darkfactory.yaml means the scope critic cannot verify the change — fail closed.",
323
+ requiredFix: "Ensure darkfactory.yaml exists and parses, and that `df mode init <name>` has seeded the mode. If this is not an operator-mode PR, remove the `Mode:` trailer.",
324
+ }),
325
+ ];
326
+ }
327
+ let resolved;
328
+ try {
329
+ resolved = resolveModeWithConfig(config, rawMode);
330
+ }
331
+ catch (err) {
332
+ return [
333
+ blocker({
334
+ file: CATALOG_FILE,
335
+ evidence: `Commit carries \`Mode: ${rawMode}\` but that mode is not in the catalog: ${err.message}`,
336
+ impact: "The Mode: trailer names a mode with no catalog entry, so there are no writeScopes/protectedPaths to validate the diff against — the change cannot be proven in-scope.",
337
+ requiredFix: "Seed the mode with `df mode init " + rawMode + "` (engineer reviews the scopes in the init PR), or correct the `Mode:` trailer to an existing mode.",
338
+ }),
339
+ ];
340
+ }
341
+ return this.validateDiff(packet, resolved.modeName, resolved.mode);
342
+ }
343
+ catch (err) {
344
+ // Belt-and-suspenders: any unforeseen throw fails CLOSED as a blocker
345
+ // on a complete result — never an error status.
346
+ return [
347
+ blocker({
348
+ file: CATALOG_FILE,
349
+ evidence: `mode-scope critic errored while validating \`Mode: ${rawMode}\`: ${err.message}`,
350
+ impact: "The scope critic could not complete its check for a mode-tagged PR. Failing closed prevents an out-of-scope change from slipping through on an internal error.",
351
+ requiredFix: "Re-run the gate; if it persists, file a bug against the mode-scope adapter with the commit SHA and darkfactory.yaml.",
352
+ }),
353
+ ];
354
+ }
355
+ }
356
+ validateDiff(packet, modeName, mode) {
357
+ const findings = [];
358
+ // Per-path scope classification. `collectChangedPaths` yields the new
359
+ // path AND (for renames/copies) the old path, so a rename OUT of a
360
+ // protected area or writeScope is caught on both endpoints — a
361
+ // path-only check would miss the vacated side.
362
+ const paths = collectChangedPaths(packet.changedFiles);
363
+ const seen = new Set();
364
+ for (const path of paths) {
365
+ if (seen.has(path))
366
+ continue;
367
+ seen.add(path);
368
+ const classification = classifyModePath(path, mode);
369
+ if (classification.kind !== "allow") {
370
+ findings.push(pathViolationFinding(path, modeName, classification.kind));
371
+ }
372
+ }
373
+ // Diff budget. maxFiles uses the authoritative (untruncated)
374
+ // changedFiles count; maxLines uses the churn visible in the diff.
375
+ // Budget findings aren't tied to one path, so anchor them to the first
376
+ // changed file (falling back to the catalog) — a `file` is required for
377
+ // the finding to survive the v2 rubric.
378
+ const anchor = packet.changedFiles[0]?.path ?? CATALOG_FILE;
379
+ const budget = mode.diffBudget;
380
+ if (budget?.maxFiles !== undefined) {
381
+ const fileCount = packet.changedFiles.length;
382
+ if (fileCount > budget.maxFiles) {
383
+ findings.push(blocker({
384
+ file: anchor,
385
+ evidence: `This PR changes ${fileCount} file(s), over mode "${modeName}" \`diffBudget.maxFiles\` = ${budget.maxFiles}.`,
386
+ impact: "Operator-mode changes are capped to a small, reviewable surface. A change over the file budget is too large for the mode's risk profile.",
387
+ requiredFix: `Split the change into smaller PRs each within ${budget.maxFiles} file(s), or escalate to an engineer via \`df handoff\`.`,
388
+ }));
389
+ }
390
+ }
391
+ if (budget?.maxLines !== undefined) {
392
+ const lineCount = countDiffLines(packet.diff);
393
+ if (lineCount > budget.maxLines) {
394
+ findings.push(blocker({
395
+ file: anchor,
396
+ evidence: `This PR changes ${lineCount}+ line(s), over mode "${modeName}" \`diffBudget.maxLines\` = ${budget.maxLines}.${packet.diffTruncated ? " (diff was truncated; the true count is at least this.)" : ""}`,
397
+ impact: "Operator-mode changes are capped to a small, reviewable churn. A change over the line budget is too large for the mode's risk profile.",
398
+ requiredFix: `Reduce the change to at most ${budget.maxLines} changed line(s), split into smaller PRs, or escalate to an engineer via \`df handoff\`.`,
399
+ }));
400
+ }
401
+ else if (packet.diffTruncated) {
402
+ // FAIL CLOSED on a truncated diff whose VISIBLE churn is under budget.
403
+ // `countDiffLines` on a truncated `packet.diff` is only a LOWER bound,
404
+ // so an under-visible-budget-but-truncated diff cannot be PROVEN within
405
+ // `maxLines` — the true churn may exceed it. Approving here would let an
406
+ // over-budget operator-mode change through whenever the trusted-surface
407
+ // diff is capped before the visible count crosses the limit. (This gap
408
+ // is specific to `maxLines`; `maxFiles` above is exact — it counts the
409
+ // untruncated `changedFiles` list — so there is no truncation twin
410
+ // there.)
411
+ findings.push(blocker({
412
+ file: anchor,
413
+ evidence: `Mode "${modeName}" sets \`diffBudget.maxLines\` = ${budget.maxLines}, but the reviewed diff was TRUNCATED (${lineCount} line(s) visible); the true churn cannot be proven within budget.`,
414
+ impact: "A truncated diff can hide churn above the mode's hard line budget, so approving it would let an over-budget change merge. The scope critic fails closed until the change is provably within budget.",
415
+ requiredFix: `Shrink the change so its FULL diff is within ${budget.maxLines} line(s) (small enough that the diff is not truncated), split into smaller PRs, or escalate to an engineer via \`df handoff\`.`,
416
+ }));
417
+ }
418
+ }
419
+ return findings;
420
+ }
421
+ summaryFor(packet, rawMode, findings, verdict) {
422
+ // `rawMode` is the SAME resolved trailer `review()` fed to `computeFindings`
423
+ // (`resolveModeTrailer` — HEAD trailer OR any commit in the reviewed range),
424
+ // NOT a re-parse of `packet.commit`. On the merge-ref path the trailer lives
425
+ // on a range commit, not on HEAD; recomputing from `packet.commit` here would
426
+ // make the summary claim "no-op" while `findings` actually block the PR. The
427
+ // summary MUST narrate the same mode the findings were judged against.
428
+ if (rawMode === undefined) {
429
+ return "mode-scope: no `Mode:` trailer — judged as a normal PR (no-op).";
430
+ }
431
+ return verdict === "APPROVED"
432
+ ? `mode-scope: \`Mode: ${rawMode}\` diff is within scope (${packet.changedFiles.length} file(s), 0 violations).`
433
+ : `mode-scope: \`Mode: ${rawMode}\` — ${findings.length} scope violation(s) block this PR.`;
434
+ }
435
+ async doctor(_critic) {
436
+ // Dependency-light: no env, no subprocess, no network. The smoke check
437
+ // confirms the shared scope classifier is wired correctly on this host.
438
+ const protectedHit = classifyModePath("src/auth/login.ts", {
439
+ writeScopes: ["src/**"],
440
+ protectedPaths: ["**/auth/**"],
441
+ }).kind === "protected";
442
+ return [
443
+ {
444
+ name: "mode_scope_classifier",
445
+ passed: protectedHit,
446
+ detail: protectedHit
447
+ ? "scope classifier resolves protectedPaths over writeScopes as expected."
448
+ : "scope classifier did NOT resolve protectedPaths over writeScopes — precedence bug.",
449
+ ...(protectedHit
450
+ ? {}
451
+ : { remediation: "Investigate packages/cli/src/mode/scope-policy.ts classifyModePath precedence." }),
452
+ },
453
+ ];
454
+ }
455
+ }
456
+ //# sourceMappingURL=mode-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode-scope.js","sourceRoot":"","sources":["../../src/adapters/mode-scope.ts"],"names":[],"mappings":"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"}