@deftai/directive-core 0.105.0 → 0.107.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 (173) hide show
  1. package/dist/authz/classify.js +591 -34
  2. package/dist/check/gate-lists.js +2 -0
  3. package/dist/check/named-cause.js +1 -1
  4. package/dist/design-critique/exclusive-chip.d.ts +33 -0
  5. package/dist/design-critique/exclusive-chip.js +68 -0
  6. package/dist/design-critique/parent-audit.d.ts +59 -0
  7. package/dist/design-critique/parent-audit.js +121 -0
  8. package/dist/doctor/checks.d.ts +6 -0
  9. package/dist/doctor/checks.js +35 -0
  10. package/dist/forward-coverage/diff-coverage.d.ts +39 -0
  11. package/dist/forward-coverage/diff-coverage.js +112 -0
  12. package/dist/forward-coverage/diff-lines.d.ts +19 -0
  13. package/dist/forward-coverage/diff-lines.js +173 -0
  14. package/dist/forward-coverage/evaluate.d.ts +24 -9
  15. package/dist/forward-coverage/evaluate.js +125 -19
  16. package/dist/hooks/dest-form.d.ts +46 -0
  17. package/dist/hooks/dest-form.js +575 -0
  18. package/dist/hooks/dispatcher.d.ts +17 -1
  19. package/dist/hooks/dispatcher.js +82 -11
  20. package/dist/hooks/index.d.ts +1 -0
  21. package/dist/hooks/index.js +1 -0
  22. package/dist/init-deposit/agent-hooks.d.ts +1 -1
  23. package/dist/init-deposit/agent-hooks.js +1 -1
  24. package/dist/intake/clause-derivation.d.ts +9 -0
  25. package/dist/intake/clause-derivation.js +30 -3
  26. package/dist/intake/github-auth-modes-cli.js +14 -2
  27. package/dist/intake/github-auth-modes.d.ts +54 -9
  28. package/dist/intake/github-auth-modes.js +326 -123
  29. package/dist/lifecycle/completed-tracked-on-delivery.d.ts +26 -0
  30. package/dist/lifecycle/completed-tracked-on-delivery.js +37 -14
  31. package/dist/lifecycle/completed-write-guard.d.ts +48 -0
  32. package/dist/lifecycle/completed-write-guard.js +373 -0
  33. package/dist/lifecycle/index.d.ts +1 -0
  34. package/dist/lifecycle/index.js +1 -0
  35. package/dist/lifecycle-visible/evaluate.d.ts +98 -0
  36. package/dist/lifecycle-visible/evaluate.js +710 -0
  37. package/dist/lifecycle-visible/index.d.ts +2 -0
  38. package/dist/lifecycle-visible/index.js +2 -0
  39. package/dist/literal-acceptance/capture.d.ts +16 -0
  40. package/dist/literal-acceptance/capture.js +78 -10
  41. package/dist/literal-acceptance/evaluate.js +25 -3
  42. package/dist/literal-acceptance/index.d.ts +2 -2
  43. package/dist/literal-acceptance/index.js +2 -2
  44. package/dist/literal-acceptance/run.js +9 -3
  45. package/dist/literal-acceptance/types.d.ts +6 -0
  46. package/dist/literal-acceptance/types.js +6 -0
  47. package/dist/orchestration/judgment-policy.d.ts +13 -0
  48. package/dist/orchestration/judgment-policy.js +15 -0
  49. package/dist/orchestration/probe-session.js +13 -8
  50. package/dist/orchestration/verify-judgment-gates.d.ts +4 -0
  51. package/dist/orchestration/verify-judgment-gates.js +43 -11
  52. package/dist/policy/ceremony-dial.js +7 -7
  53. package/dist/policy/host-hooks.d.ts +20 -0
  54. package/dist/policy/host-hooks.js +121 -2
  55. package/dist/policy/org-force-on-migration.js +2 -1
  56. package/dist/policy/plan-extensions.d.ts +1 -1
  57. package/dist/policy/plan-extensions.js +2 -0
  58. package/dist/policy/product-signal.js +6 -7
  59. package/dist/policy/require-human-merge.js +8 -6
  60. package/dist/policy/resolve.d.ts +7 -2
  61. package/dist/policy/resolve.js +25 -8
  62. package/dist/policy/runtime-authority.d.ts +18 -0
  63. package/dist/policy/runtime-authority.js +11 -0
  64. package/dist/policy/value-feedback.js +7 -8
  65. package/dist/policy/write-fence.js +3 -0
  66. package/dist/preflight-cache/evaluate.d.ts +12 -0
  67. package/dist/preflight-cache/evaluate.js +24 -4
  68. package/dist/preflight-cache/index.d.ts +1 -1
  69. package/dist/preflight-cache/index.js +1 -1
  70. package/dist/product-first-done-gate/evaluate.d.ts +4 -0
  71. package/dist/product-first-done-gate/evaluate.js +82 -10
  72. package/dist/release/closed-verb-gate.d.ts +12 -0
  73. package/dist/release/closed-verb-gate.js +51 -0
  74. package/dist/release/index.d.ts +1 -0
  75. package/dist/release/index.js +1 -0
  76. package/dist/release/pipeline.js +7 -0
  77. package/dist/release/types.d.ts +12 -0
  78. package/dist/render/constants.d.ts +2 -1
  79. package/dist/render/constants.js +2 -1
  80. package/dist/render/framework-commands.js +4 -0
  81. package/dist/render/spec-validate.js +23 -12
  82. package/dist/run-summary/types.d.ts +4 -0
  83. package/dist/scm/design-critique-chip.d.ts +37 -0
  84. package/dist/scm/design-critique-chip.js +171 -0
  85. package/dist/scm/index.d.ts +1 -0
  86. package/dist/scm/index.js +1 -0
  87. package/dist/scm/main.d.ts +3 -0
  88. package/dist/scm/main.js +15 -1
  89. package/dist/scm/readiness-cli.d.ts +2 -0
  90. package/dist/scm/readiness-cli.js +42 -1
  91. package/dist/scm/readiness.d.ts +2 -1
  92. package/dist/scm/readiness.js +7 -1
  93. package/dist/scope/decompose.js +2 -1
  94. package/dist/scope/delivery-evidence.d.ts +27 -1
  95. package/dist/scope/index.d.ts +1 -0
  96. package/dist/scope/index.js +1 -0
  97. package/dist/scope/lifecycle-write.d.ts +24 -0
  98. package/dist/scope/lifecycle-write.js +64 -0
  99. package/dist/scope/main.js +18 -6
  100. package/dist/scope/promote-from-issue.js +5 -5
  101. package/dist/scope/transition.js +90 -11
  102. package/dist/scope-provenance/evaluate.js +8 -4
  103. package/dist/session/ac-pass-banking.d.ts +5 -0
  104. package/dist/session/ac-pass-banking.js +3 -2
  105. package/dist/session/git.d.ts +18 -0
  106. package/dist/session/git.js +81 -1
  107. package/dist/session/occupancy.d.ts +2 -1
  108. package/dist/session/occupancy.js +11 -4
  109. package/dist/session/process-cost.d.ts +32 -10
  110. package/dist/session/process-cost.js +189 -1
  111. package/dist/session/product-state-hash.js +41 -24
  112. package/dist/session/ritual-entrypoint.js +16 -1
  113. package/dist/session/session-start.d.ts +18 -0
  114. package/dist/session/session-start.js +49 -2
  115. package/dist/session/verify-session-ritual.d.ts +46 -4
  116. package/dist/session/verify-session-ritual.js +118 -10
  117. package/dist/swarm/finalize-cohort-cli.d.ts +7 -2
  118. package/dist/swarm/finalize-cohort-cli.js +165 -35
  119. package/dist/swarm/finalize-cohort.d.ts +1 -0
  120. package/dist/swarm/finalize-cohort.js +14 -2
  121. package/dist/swarm/launch.d.ts +69 -0
  122. package/dist/swarm/launch.js +191 -1
  123. package/dist/swarm/routing-set-cli.js +2 -0
  124. package/dist/swarm/routing-verify.d.ts +1 -1
  125. package/dist/swarm/routing-verify.js +2 -2
  126. package/dist/triage/actions/index.d.ts +4 -0
  127. package/dist/triage/actions/index.js +6 -0
  128. package/dist/triage/bootstrap/index.js +6 -6
  129. package/dist/triage/evaluate/evaluate.d.ts +11 -0
  130. package/dist/triage/evaluate/evaluate.js +169 -0
  131. package/dist/triage/evaluate/github.d.ts +16 -0
  132. package/dist/triage/evaluate/github.js +153 -0
  133. package/dist/triage/evaluate/index.d.ts +9 -0
  134. package/dist/triage/evaluate/index.js +8 -0
  135. package/dist/triage/evaluate/paths.d.ts +7 -0
  136. package/dist/triage/evaluate/paths.js +25 -0
  137. package/dist/triage/evaluate/sink.d.ts +5 -0
  138. package/dist/triage/evaluate/sink.js +52 -0
  139. package/dist/triage/evaluate/types.d.ts +93 -0
  140. package/dist/triage/evaluate/types.js +13 -0
  141. package/dist/triage/evaluate/validity.d.ts +8 -0
  142. package/dist/triage/evaluate/validity.js +87 -0
  143. package/dist/triage/evaluate/value.d.ts +12 -0
  144. package/dist/triage/evaluate/value.js +33 -0
  145. package/dist/triage/evaluate/wip-census.d.ts +5 -0
  146. package/dist/triage/evaluate/wip-census.js +39 -0
  147. package/dist/triage/evaluate/worktrees.d.ts +8 -0
  148. package/dist/triage/evaluate/worktrees.js +68 -0
  149. package/dist/triage/evaluate/xbrief-refs.d.ts +8 -0
  150. package/dist/triage/evaluate/xbrief-refs.js +60 -0
  151. package/dist/triage/help/registry-data.d.ts +22 -8
  152. package/dist/triage/help/registry-data.js +54 -6
  153. package/dist/triage/index.d.ts +1 -0
  154. package/dist/triage/index.js +1 -0
  155. package/dist/triage/welcome/writers.d.ts +1 -1
  156. package/dist/triage/welcome/writers.js +10 -5
  157. package/dist/value/feedback-file.d.ts +17 -2
  158. package/dist/value/feedback-file.js +80 -6
  159. package/dist/value/readback.d.ts +4 -1
  160. package/dist/value/readback.js +51 -12
  161. package/dist/vbrief-reconcile/labels.d.ts +1 -0
  162. package/dist/vbrief-reconcile/labels.js +30 -0
  163. package/dist/vbrief-validate/schema.d.ts +4 -0
  164. package/dist/vbrief-validate/schema.js +1 -1
  165. package/dist/vbrief-validation/story-quality.d.ts +7 -0
  166. package/dist/vbrief-validation/story-quality.js +8 -1
  167. package/dist/verify-env/agent-hook-readiness.d.ts +2 -2
  168. package/dist/verify-env/agent-hook-readiness.js +12 -8
  169. package/dist/verify-env/agent-hooks-live-probe.d.ts +5 -1
  170. package/dist/verify-env/agent-hooks-live-probe.js +26 -5
  171. package/dist/verify-env/agent-hooks.js +3 -4
  172. package/dist/xbrief-migrate/agents-header.js +69 -7
  173. package/package.json +7 -3
@@ -0,0 +1,2 @@
1
+ export { type CheckIgnoreMatch, displayIgnoreSource, evaluateLifecycleVisible, formatLifecycleVisibleSessionLines, indexFlagKind, isSelectiveLifecyclePath, LIFECYCLE_ROOT_PREFIXES, LIFECYCLE_STAGE_DIRS, type LifecycleHideFinding, type LifecycleHideKind, type LifecycleVisibleOptions, type LifecycleVisibleResult, type LsFilesVerboseRecord, lifecycleRootRelPaths, parseCheckIgnoreVerboseLine, parseLsFilesVerboseRecord, } from "./evaluate.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { displayIgnoreSource, evaluateLifecycleVisible, formatLifecycleVisibleSessionLines, indexFlagKind, isSelectiveLifecyclePath, LIFECYCLE_ROOT_PREFIXES, LIFECYCLE_STAGE_DIRS, lifecycleRootRelPaths, parseCheckIgnoreVerboseLine, parseLsFilesVerboseRecord, } from "./evaluate.js";
2
+ //# sourceMappingURL=index.js.map
@@ -9,6 +9,17 @@
9
9
  * so distinct execution contexts are not collapsed.
10
10
  */
11
11
  import type { LiteralAcceptanceCommand, RejectedLiteralCommand } from "./types.js";
12
+ /** Reason stamped when an inline backtick is recorded as a mention, not AC (#3721). */
13
+ export declare const INLINE_PROSE_MENTION_REASON = "inline backtick in prose is a mention, not a stated acceptance command (#3721)";
14
+ /**
15
+ * True when a capture-only row came from an inline backtick in prose (#3721).
16
+ * Those mentions are not stated acceptance commands. Labeled, prompt, and fence
17
+ * spans stay stated.
18
+ */
19
+ export declare function isInlineProseMention(cmd: {
20
+ readonly source?: string;
21
+ readonly sourceSpan?: string | null;
22
+ }): boolean;
12
23
  /** Result of capture including the operator-visible rejected ledger. */
13
24
  export interface CaptureLiteralAcceptanceResult {
14
25
  readonly commands: readonly LiteralAcceptanceCommand[];
@@ -57,6 +68,11 @@ export declare function isProseDerivedRejection(rejected: RejectedLiteralCommand
57
68
  * prose-derived rejections no longer depends on this (#3511).
58
69
  */
59
70
  export declare function hasStructuredAcceptanceCommands(plan: Record<string, unknown> | null | undefined): boolean;
71
+ /**
72
+ * Command strings the operator recorded as not acceptance commands (#3721).
73
+ * Listing a string here dispositions a capture without asserting it is AC.
74
+ */
75
+ export declare function readNotAcceptanceCommands(plan: Record<string, unknown> | null | undefined): ReadonlySet<string>;
60
76
  /** Format rejected ledger for CLI / complete-gate messages. */
61
77
  export declare function formatRejectedLedger(rejected: readonly RejectedLiteralCommand[]): string;
62
78
  //# sourceMappingURL=capture.d.ts.map
@@ -9,7 +9,7 @@
9
9
  * so distinct execution contexts are not collapsed.
10
10
  */
11
11
  import { evaluateCommandSafety } from "./safety.js";
12
- import { EXECUTABLE_LITERAL_SOURCES, LITERAL_ACCEPTANCE_REJECTED_METADATA_KEY } from "./types.js";
12
+ import { EXECUTABLE_LITERAL_SOURCES, LITERAL_ACCEPTANCE_NOT_COMMANDS_METADATA_KEY, LITERAL_ACCEPTANCE_REJECTED_METADATA_KEY, } from "./types.js";
13
13
  /** Labeled keywords (single-token, linear match). */
14
14
  const LABELED_KEYWORDS = new Set(["verify", "command", "run", "check", "exec", "shell"]);
15
15
  /** Words that look like a CLI invocation start (not prose). */
@@ -85,8 +85,24 @@ function pushCommand(buckets, cmd) {
85
85
  return;
86
86
  }
87
87
  const key = commandDedupeKey(cmd);
88
- if (buckets.seen.has(key))
88
+ if (buckets.seen.has(key)) {
89
+ const idx = buckets.out.findIndex((row) => commandDedupeKey(row) === key);
90
+ const existing = idx >= 0 ? buckets.out[idx] : undefined;
91
+ if (existing !== undefined &&
92
+ idx >= 0 &&
93
+ isInlineProseMention(existing) &&
94
+ isExecutableSource(cmd.source)) {
95
+ buckets.out[idx] = {
96
+ command: cmd.command,
97
+ cwd: cmd.cwd ?? null,
98
+ expectedStdout: cmd.expectedStdout ?? null,
99
+ expectedExitCode: cmd.expectedExitCode ?? 0,
100
+ source: cmd.source,
101
+ sourceSpan: cmd.sourceSpan ?? null,
102
+ };
103
+ }
89
104
  return;
105
+ }
90
106
  buckets.seen.add(key);
91
107
  buckets.out.push({
92
108
  command: cmd.command,
@@ -167,12 +183,12 @@ function matchLabeledCommand(line) {
167
183
  return null;
168
184
  return line.slice(i).trimEnd();
169
185
  }
170
- /** `$ cmd` or `> cmd` prompt lines. */
186
+ /** `$ cmd` prompt lines. Markdown blockquote `>` is not a prompt (#3572). */
171
187
  function matchPromptCommand(line) {
172
188
  let i = skipWs(line, 0);
173
189
  if (i >= line.length)
174
190
  return null;
175
- if (line[i] !== "$" && line[i] !== ">")
191
+ if (line[i] !== "$")
176
192
  return null;
177
193
  i = skipWs(line, i + 1);
178
194
  if (i >= line.length)
@@ -259,7 +275,7 @@ function flushFenceBody(body, fenceLang, fenceStartLine, requireRegion, regionAc
259
275
  return;
260
276
  if (!isShellFenceLang(fenceLang))
261
277
  return;
262
- // Transcript lines are skipped. `$`/`>` prompts inside a transcript fence are
278
+ // Transcript lines are skipped. `$` prompts inside a transcript fence are
263
279
  // suggested-fixes (biome migrate), not stated AC (#3511). Comments do not
264
280
  // mark a fence as transcript, so a genuine command next to `# Error:` still
265
281
  // captures.
@@ -382,8 +398,9 @@ function matchMarkdownHeading(line) {
382
398
  * marker). Mid-line label capture is deliberately not attempted (#3484): a
383
399
  * `verify:<verb>` token inside ordinary prose has no terminator, so the capture
384
400
  * swallowed the rest of the paragraph and the phantom then blocked completion on
385
- * the safety ledger. Commands stated mid-sentence must live in a fence, a `$`
386
- * prompt, or an inline backtick span all of which have real delimiters.
401
+ * the safety ledger. Commands stated mid-sentence must live in a fence or a `$`
402
+ * prompt. An inline backtick inside a prose sentence is a reference, not a
403
+ * stated command (#3721).
387
404
  */
388
405
  function extractFromLabeledLines(text, buckets) {
389
406
  const lines = text.split(/\r?\n/);
@@ -412,9 +429,13 @@ function extractFromLabeledLines(text, buckets) {
412
429
  }
413
430
  }
414
431
  }
432
+ /** Reason stamped when an inline backtick is recorded as a mention, not AC (#3721). */
433
+ export const INLINE_PROSE_MENTION_REASON = "inline backtick in prose is a mention, not a stated acceptance command (#3721)";
415
434
  /**
416
435
  * Extract inline `` `command` `` spans that follow verify/run language on the same line.
417
- * Example: `run \`task check\` before done`
436
+ * These are references, not stated acceptance commands (#3721). Recorded on the
437
+ * rejected ledger with an `inline@` span so #3511 demotes them to advisory.
438
+ * They must not enter the stated-command list (that would require promotion).
418
439
  */
419
440
  function extractInlineVerifySpans(text, buckets) {
420
441
  const lines = text.split(/\r?\n/);
@@ -440,10 +461,23 @@ function extractInlineVerifySpans(text, buckets) {
440
461
  const normalized = normalizeCommand(inner);
441
462
  if (normalized === null || !looksLikeShellCommand(normalized))
442
463
  continue;
443
- pushUnique(buckets, normalized, "task_statement", `inline@L${i + 1}`);
464
+ recordRejected(buckets, normalized, INLINE_PROSE_MENTION_REASON, `inline@L${i + 1}`);
444
465
  }
445
466
  }
446
467
  }
468
+ /**
469
+ * True when a capture-only row came from an inline backtick in prose (#3721).
470
+ * Those mentions are not stated acceptance commands. Labeled, prompt, and fence
471
+ * spans stay stated.
472
+ */
473
+ export function isInlineProseMention(cmd) {
474
+ if (cmd.source !== "task_statement")
475
+ return false;
476
+ const span = cmd.sourceSpan;
477
+ if (span === null || span === undefined)
478
+ return false;
479
+ return span.startsWith("inline@");
480
+ }
447
481
  function emptyBuckets() {
448
482
  return {
449
483
  out: [],
@@ -519,7 +553,7 @@ export function readStoredLiteralAcceptanceDetailed(plan) {
519
553
  // swarm.verify_commands is a string list (legacy). Prefer richer
520
554
  // literal_acceptance_commands rows already loaded — do not invent a
521
555
  // null-cwd duplicate for the same command text.
522
- const alreadyHasCommand = (command) => buckets.out.some((c) => c.command === command);
556
+ const alreadyHasCommand = (command) => buckets.out.some((c) => c.command === command && !isInlineProseMention(c));
523
557
  for (const cmd of coerceCommandList(swarm.verify_commands, "verify_commands", "swarm.verify_commands")) {
524
558
  if (alreadyHasCommand(cmd.command))
525
559
  continue;
@@ -793,6 +827,40 @@ export function hasStructuredAcceptanceCommands(plan) {
793
827
  return false;
794
828
  return hasNonEmptyCommandList(swarm.verify_commands);
795
829
  }
830
+ /**
831
+ * Command strings the operator recorded as not acceptance commands (#3721).
832
+ * Listing a string here dispositions a capture without asserting it is AC.
833
+ */
834
+ export function readNotAcceptanceCommands(plan) {
835
+ const rec = asRecord(plan);
836
+ if (rec === null)
837
+ return new Set();
838
+ const metadata = asRecord(rec.metadata);
839
+ if (metadata === null)
840
+ return new Set();
841
+ const raw = metadata[LITERAL_ACCEPTANCE_NOT_COMMANDS_METADATA_KEY] ??
842
+ metadata.literalAcceptanceNotCommands ??
843
+ null;
844
+ const out = new Set();
845
+ if (typeof raw === "string" && raw.trim().length > 0) {
846
+ out.add(raw.trim());
847
+ return out;
848
+ }
849
+ if (!Array.isArray(raw))
850
+ return out;
851
+ for (const entry of raw) {
852
+ if (typeof entry === "string" && entry.trim().length > 0) {
853
+ out.add(entry.trim());
854
+ continue;
855
+ }
856
+ const row = asRecord(entry);
857
+ if (row === null)
858
+ continue;
859
+ if (isNonEmptyString(row.command))
860
+ out.add(row.command.trim());
861
+ }
862
+ return out;
863
+ }
796
864
  /** Format rejected ledger for CLI / complete-gate messages. */
797
865
  export function formatRejectedLedger(rejected) {
798
866
  if (rejected.length === 0)
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
  import { resolve } from "node:path";
6
- import { captureLiteralAcceptanceCommandsDetailed, formatRejectedLedger, isProseDerivedRejection, readStoredLiteralAcceptanceDetailed, } from "./capture.js";
6
+ import { captureLiteralAcceptanceCommandsDetailed, formatRejectedLedger, INLINE_PROSE_MENTION_REASON, isInlineProseMention, isProseDerivedRejection, readNotAcceptanceCommands, readStoredLiteralAcceptanceDetailed, } from "./capture.js";
7
7
  import { runLiteralAcceptanceCommands } from "./run.js";
8
8
  function asRecord(value) {
9
9
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
@@ -40,11 +40,33 @@ function partitionRejected(rejected) {
40
40
  */
41
41
  export function resolveLiteralAcceptanceDetailed(plan, options = {}) {
42
42
  const raw = resolveRawLiteralAcceptance(plan, options);
43
+ const notCommands = readNotAcceptanceCommands(plan);
44
+ const stated = [];
45
+ const mentionAdvisory = [];
46
+ for (const c of raw.commands) {
47
+ if (isInlineProseMention(c)) {
48
+ mentionAdvisory.push({
49
+ command: c.command,
50
+ reason: INLINE_PROSE_MENTION_REASON,
51
+ sourceSpan: c.sourceSpan ?? null,
52
+ });
53
+ continue;
54
+ }
55
+ if (c.source === "task_statement" && notCommands.has(c.command)) {
56
+ mentionAdvisory.push({
57
+ command: c.command,
58
+ reason: "operator recorded this capture as not an acceptance command (#3721)",
59
+ sourceSpan: c.sourceSpan ?? null,
60
+ });
61
+ continue;
62
+ }
63
+ stated.push(c);
64
+ }
43
65
  const split = partitionRejected(raw.rejected);
44
66
  return {
45
- commands: raw.commands,
67
+ commands: stated,
46
68
  rejected: split.blocking,
47
- advisoryRejected: split.advisory,
69
+ advisoryRejected: [...split.advisory, ...mentionAdvisory],
48
70
  transcriptPromptSkipped: raw.transcriptPromptSkipped,
49
71
  };
50
72
  }
@@ -4,9 +4,9 @@
4
4
  * Capture exact stated shell commands at intake; run them verbatim before done.
5
5
  * Extends #973; survives ceremony dial rapid/minimal (#3214).
6
6
  */
7
- export { attachLiteralAcceptanceCommands, type CaptureLiteralAcceptanceResult, captureAndAttachLiteralAcceptance, captureLiteralAcceptanceCommands, captureLiteralAcceptanceCommandsDetailed, formatRejectedLedger, hasStructuredAcceptanceCommands, isProseDerivedRejection, readStoredLiteralAcceptanceCommands, readStoredLiteralAcceptanceDetailed, } from "./capture.js";
7
+ export { attachLiteralAcceptanceCommands, type CaptureLiteralAcceptanceResult, captureAndAttachLiteralAcceptance, captureLiteralAcceptanceCommands, captureLiteralAcceptanceCommandsDetailed, formatRejectedLedger, hasStructuredAcceptanceCommands, INLINE_PROSE_MENTION_REASON, isInlineProseMention, isProseDerivedRejection, readNotAcceptanceCommands, readStoredLiteralAcceptanceCommands, readStoredLiteralAcceptanceDetailed, } from "./capture.js";
8
8
  export { appendLiteralAcceptanceAdvisory, type EvaluateLiteralAcceptanceOptions, evaluateLiteralAcceptanceFromPath, evaluateLiteralAcceptanceFromPlan, formatLiteralAcceptanceAdvisory, isLiteralAcceptanceRequiredAtCeremonyDepth, LITERAL_ACCEPTANCE_ADVISORY_MARKER, type ResolvedLiteralAcceptance, resolveLiteralAcceptanceCommands, resolveLiteralAcceptanceDetailed, stripLiteralAcceptanceAdvisory, } from "./evaluate.js";
9
9
  export { defaultLiteralAcceptanceRunner, runLiteralAcceptanceCommand, runLiteralAcceptanceCommands, } from "./run.js";
10
10
  export { type CommandSafetyResult, evaluateCommandSafety, evaluateNoopDenylist, evaluateStampAcceptanceSafety, isExecutableLiteralSource, isNoopRefusalReason, isVerbatimStatementSpan, NOOP_ACCEPTANCE_REMEDIATION, REJECTED_NOOP_OUTCOME, type RejectedNoopOutcome, type StampAcceptanceCommand, type StampAcceptanceSafetyInput, type StampAcceptanceSafetyResult, type StampSourceRung, } from "./safety.js";
11
- export { EXECUTABLE_LITERAL_SOURCES, LITERAL_ACCEPTANCE_METADATA_KEY, LITERAL_ACCEPTANCE_METADATA_KEY_CAMEL, LITERAL_ACCEPTANCE_REJECTED_METADATA_KEY, type LiteralAcceptanceCommand, type LiteralAcceptanceGateResult, type LiteralAcceptanceRunner, type LiteralAcceptanceRunResult, type LiteralAcceptanceSource, type RejectedLiteralCommand, } from "./types.js";
11
+ export { EXECUTABLE_LITERAL_SOURCES, LITERAL_ACCEPTANCE_METADATA_KEY, LITERAL_ACCEPTANCE_METADATA_KEY_CAMEL, LITERAL_ACCEPTANCE_NOT_COMMANDS_METADATA_KEY, LITERAL_ACCEPTANCE_REJECTED_METADATA_KEY, type LiteralAcceptanceCommand, type LiteralAcceptanceGateResult, type LiteralAcceptanceRunner, type LiteralAcceptanceRunResult, type LiteralAcceptanceSource, type RejectedLiteralCommand, } from "./types.js";
12
12
  //# sourceMappingURL=index.d.ts.map
@@ -4,9 +4,9 @@
4
4
  * Capture exact stated shell commands at intake; run them verbatim before done.
5
5
  * Extends #973; survives ceremony dial rapid/minimal (#3214).
6
6
  */
7
- export { attachLiteralAcceptanceCommands, captureAndAttachLiteralAcceptance, captureLiteralAcceptanceCommands, captureLiteralAcceptanceCommandsDetailed, formatRejectedLedger, hasStructuredAcceptanceCommands, isProseDerivedRejection, readStoredLiteralAcceptanceCommands, readStoredLiteralAcceptanceDetailed, } from "./capture.js";
7
+ export { attachLiteralAcceptanceCommands, captureAndAttachLiteralAcceptance, captureLiteralAcceptanceCommands, captureLiteralAcceptanceCommandsDetailed, formatRejectedLedger, hasStructuredAcceptanceCommands, INLINE_PROSE_MENTION_REASON, isInlineProseMention, isProseDerivedRejection, readNotAcceptanceCommands, readStoredLiteralAcceptanceCommands, readStoredLiteralAcceptanceDetailed, } from "./capture.js";
8
8
  export { appendLiteralAcceptanceAdvisory, evaluateLiteralAcceptanceFromPath, evaluateLiteralAcceptanceFromPlan, formatLiteralAcceptanceAdvisory, isLiteralAcceptanceRequiredAtCeremonyDepth, LITERAL_ACCEPTANCE_ADVISORY_MARKER, resolveLiteralAcceptanceCommands, resolveLiteralAcceptanceDetailed, stripLiteralAcceptanceAdvisory, } from "./evaluate.js";
9
9
  export { defaultLiteralAcceptanceRunner, runLiteralAcceptanceCommand, runLiteralAcceptanceCommands, } from "./run.js";
10
10
  export { evaluateCommandSafety, evaluateNoopDenylist, evaluateStampAcceptanceSafety, isExecutableLiteralSource, isNoopRefusalReason, isVerbatimStatementSpan, NOOP_ACCEPTANCE_REMEDIATION, REJECTED_NOOP_OUTCOME, } from "./safety.js";
11
- export { EXECUTABLE_LITERAL_SOURCES, LITERAL_ACCEPTANCE_METADATA_KEY, LITERAL_ACCEPTANCE_METADATA_KEY_CAMEL, LITERAL_ACCEPTANCE_REJECTED_METADATA_KEY, } from "./types.js";
11
+ export { EXECUTABLE_LITERAL_SOURCES, LITERAL_ACCEPTANCE_METADATA_KEY, LITERAL_ACCEPTANCE_METADATA_KEY_CAMEL, LITERAL_ACCEPTANCE_NOT_COMMANDS_METADATA_KEY, LITERAL_ACCEPTANCE_REJECTED_METADATA_KEY, } from "./types.js";
12
12
  //# sourceMappingURL=index.js.map
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { spawnSync } from "node:child_process";
9
9
  import { isAbsolute, resolve } from "node:path";
10
+ import { isInlineProseMention } from "./capture.js";
10
11
  import { evaluateCommandSafety, isExecutableLiteralSource } from "./safety.js";
11
12
  /** Default shell runner: exact command string via platform shell. */
12
13
  export function defaultLiteralAcceptanceRunner(input) {
@@ -95,7 +96,9 @@ export function runLiteralAcceptanceCommands(commands, options) {
95
96
  runs: [],
96
97
  };
97
98
  }
98
- const untrusted = commands.filter((c) => c.source === "task_statement" && options.allowTaskStatement !== true);
99
+ const untrusted = commands.filter((c) => c.source === "task_statement" &&
100
+ options.allowTaskStatement !== true &&
101
+ !isInlineProseMention(c));
99
102
  const executable = commands.filter((c) => isExecutableLiteralSource(c.source) ||
100
103
  (c.source === "task_statement" && options.allowTaskStatement === true));
101
104
  // Fail closed: every capture-only stated command must have a matching agent-promoted
@@ -149,8 +152,11 @@ export function runLiteralAcceptanceCommands(commands, options) {
149
152
  code: 1,
150
153
  message: `Literal acceptance-command gate FAILED (#3267): ${unpromoted.length} stated command(s) ` +
151
154
  `are capture-only (source=task_statement) and have no matching agent-promoted peer.\n` +
152
- `Promote the exact command strings (and cwd/expectedStdout/expectedExitCode when stated) ` +
153
- `into plan.metadata.swarm.verify_commands (or plan item / explicit metadata), then re-run.\n` +
155
+ `If the command is this story's acceptance, promote the exact strings ` +
156
+ `(and cwd/expectedStdout/expectedExitCode when stated) into ` +
157
+ `plan.metadata.swarm.verify_commands (or plan item / explicit metadata), then re-run.\n` +
158
+ `If the capture is not an acceptance command, record the exact string in ` +
159
+ `plan.metadata.literal_acceptance_not_commands without promoting it (#3721).\n` +
154
160
  `Note: an unrelated executable peer or a same-text peer with different context does not waive.\n` +
155
161
  listed,
156
162
  commands,
@@ -74,6 +74,12 @@ export interface LiteralAcceptanceGateResult {
74
74
  }
75
75
  /** Metadata key for operator-visible rejected command ledger (#3267). */
76
76
  export declare const LITERAL_ACCEPTANCE_REJECTED_METADATA_KEY: "literal_acceptance_rejected";
77
+ /**
78
+ * Metadata key: command strings the operator recorded as *not* acceptance
79
+ * commands (#3721). Disposition without promotion — listing a string here does
80
+ * not assert it is the story's verification.
81
+ */
82
+ export declare const LITERAL_ACCEPTANCE_NOT_COMMANDS_METADATA_KEY: "literal_acceptance_not_commands";
77
83
  /** Injectable shell runner for tests (default: spawnSync shell). */
78
84
  export type LiteralAcceptanceRunner = (input: {
79
85
  readonly command: string;
@@ -19,6 +19,12 @@ export const EXECUTABLE_LITERAL_SOURCES = [
19
19
  ];
20
20
  /** Metadata key for operator-visible rejected command ledger (#3267). */
21
21
  export const LITERAL_ACCEPTANCE_REJECTED_METADATA_KEY = "literal_acceptance_rejected";
22
+ /**
23
+ * Metadata key: command strings the operator recorded as *not* acceptance
24
+ * commands (#3721). Disposition without promotion — listing a string here does
25
+ * not assert it is the story's verification.
26
+ */
27
+ export const LITERAL_ACCEPTANCE_NOT_COMMANDS_METADATA_KEY = "literal_acceptance_not_commands";
22
28
  /** Metadata key on plan.metadata for captured literal commands (#3267). */
23
29
  export const LITERAL_ACCEPTANCE_METADATA_KEY = "literal_acceptance_commands";
24
30
  /** Alternate camelCase key accepted on read. */
@@ -2,6 +2,19 @@ export declare const GATE_CLASSES: Set<string>;
2
2
  export declare const GATE_TIERS: Set<string>;
3
3
  export declare const GATE_MATCH_PREDICATES: Set<string>;
4
4
  export declare const GATE_MATCH_STATES: Set<string>;
5
+ /** ADR-005 / #3434 Story 1 -- author-stamped marker vocabulary. */
6
+ export declare const DESIGN_CRITIQUE_GATE_ID = "design-critique";
7
+ /** List-visible mirror; this is what judgmentGates predicates match. */
8
+ export declare const DESIGN_CRITIQUE_MARKER_LABEL = "design-critique:mechanism-shaped";
9
+ /** Authoritative body-text field in the #1423 triage write-back comment. */
10
+ export declare const DESIGN_CRITIQUE_MARKER_FIELD = "mechanism-shaped: true";
11
+ /**
12
+ * Clearance line shape (ADR-005). Presence and shape only -- a non-empty
13
+ * because-clause is required; its semantic content is never evaluated.
14
+ */
15
+ export declare const DESIGN_CRITIQUE_CLEARANCE_SHAPE: RegExp;
16
+ /** True when `line` has clearance presence+shape. Never scores the reason text. */
17
+ export declare function designCritiqueClearanceShapeOk(line: string): boolean;
5
18
  export interface JudgmentGate {
6
19
  readonly gate_id: string;
7
20
  readonly gate_class: string;
@@ -7,6 +7,21 @@ export const GATE_CLASSES = new Set(["mechanical", "declared"]);
7
7
  export const GATE_TIERS = new Set(["auto", "review", "block"]);
8
8
  export const GATE_MATCH_PREDICATES = new Set(["labels", "body-text", "paths", "state", "age-days"]);
9
9
  export const GATE_MATCH_STATES = new Set(["open", "closed"]);
10
+ /** ADR-005 / #3434 Story 1 -- author-stamped marker vocabulary. */
11
+ export const DESIGN_CRITIQUE_GATE_ID = "design-critique";
12
+ /** List-visible mirror; this is what judgmentGates predicates match. */
13
+ export const DESIGN_CRITIQUE_MARKER_LABEL = "design-critique:mechanism-shaped";
14
+ /** Authoritative body-text field in the #1423 triage write-back comment. */
15
+ export const DESIGN_CRITIQUE_MARKER_FIELD = "mechanism-shaped: true";
16
+ /**
17
+ * Clearance line shape (ADR-005). Presence and shape only -- a non-empty
18
+ * because-clause is required; its semantic content is never evaluated.
19
+ */
20
+ export const DESIGN_CRITIQUE_CLEARANCE_SHAPE = /^design-critique:\s*(warranted|not warranted),\s*because\s+\S/i;
21
+ /** True when `line` has clearance presence+shape. Never scores the reason text. */
22
+ export function designCritiqueClearanceShapeOk(line) {
23
+ return DESIGN_CRITIQUE_CLEARANCE_SHAPE.test(line.trim());
24
+ }
10
25
  function validateStrList(value, prefix, key) {
11
26
  if (!Array.isArray(value) || value.length === 0) {
12
27
  return [`${prefix}.${key} must be a non-empty list of strings`];
@@ -12,6 +12,11 @@ export const SESSION_RELPATH = [".deft", "probe-session.json"];
12
12
  export const STATE_INTERROGATE = "interrogate";
13
13
  export const STATE_COMPLETE = "complete";
14
14
  export const VALID_DECISION_STATUSES = new Set(["locked", "deferred", "risk-accepted"]);
15
+ /** Live CLI teach-string (#3556). The Python helper was deleted. */
16
+ const PROBE_SESSION_START = "`deft probe-session start --target <scope>`";
17
+ const PROBE_SESSION_RECORD = "`deft probe-session record ...`";
18
+ const PROBE_SESSION_COMPLETE = "`deft probe-session complete`";
19
+ const NO_SESSION_START = `No active probe session. Start one with ${PROBE_SESSION_START}.`;
15
20
  export class ProbeHandoffBlockedError extends Error {
16
21
  session;
17
22
  constructor(message, session = null) {
@@ -249,7 +254,7 @@ export function startSession(projectRoot, options) {
249
254
  export function recordDecision(projectRoot, options) {
250
255
  const session = readSession(projectRoot);
251
256
  if (session === null) {
252
- throw new ProbeHandoffBlockedError("No active probe session. Start one with `uv run python scripts/probe_session.py start --target <scope>`.");
257
+ throw new ProbeHandoffBlockedError(NO_SESSION_START);
253
258
  }
254
259
  if (session.state !== STATE_INTERROGATE) {
255
260
  throw new ProbeHandoffBlockedError("Probe session is already complete; decisions cannot be appended.", session);
@@ -275,7 +280,7 @@ export function recordDecision(projectRoot, options) {
275
280
  export function setCurrentBranch(projectRoot, branch) {
276
281
  const session = readSession(projectRoot);
277
282
  if (session === null) {
278
- throw new ProbeHandoffBlockedError("No active probe session. Start one with `uv run python scripts/probe_session.py start --target <scope>`.");
283
+ throw new ProbeHandoffBlockedError(NO_SESSION_START);
279
284
  }
280
285
  if (session.state !== STATE_INTERROGATE) {
281
286
  throw new ProbeHandoffBlockedError("Probe session is already complete; current branch cannot change.", session);
@@ -287,7 +292,7 @@ export function setCurrentBranch(projectRoot, branch) {
287
292
  export function markComplete(projectRoot, now) {
288
293
  const session = readSession(projectRoot);
289
294
  if (session === null) {
290
- throw new ProbeHandoffBlockedError("No active probe session. Start one with `uv run python scripts/probe_session.py start --target <scope>`.");
295
+ throw new ProbeHandoffBlockedError(NO_SESSION_START);
291
296
  }
292
297
  if (session.state === STATE_COMPLETE) {
293
298
  return session;
@@ -307,7 +312,7 @@ function pythonRepr(value) {
307
312
  function blockedMessage(session, action) {
308
313
  if (session === null) {
309
314
  return (`Probe handoff blocked for ${action}: no active probe session. ` +
310
- "Start interrogation with `uv run python scripts/probe_session.py start --target <scope>` " +
315
+ `Start interrogation with ${PROBE_SESSION_START} ` +
311
316
  "and finish with `... complete` only after transition criteria are met.");
312
317
  }
313
318
  return (`Probe handoff blocked for ${action}: session state is ` +
@@ -315,9 +320,9 @@ function blockedMessage(session, action) {
315
320
  `currentBranch=${pythonRepr(session.current_branch)}, ` +
316
321
  `resolvedDecisions=${session.resolved_decisions.length}). ` +
317
322
  "Continue interrogation until transition criteria are met, record decisions " +
318
- "with `uv run python scripts/probe_session.py record ...`, then run " +
319
- "`uv run python scripts/probe_session.py complete` before writing artifacts " +
320
- "or updating completedStrategies.probe in plan.vbrief.json.");
323
+ `with ${PROBE_SESSION_RECORD}, then run ` +
324
+ `${PROBE_SESSION_COMPLETE} before writing artifacts ` +
325
+ "or updating completedStrategies.probe in plan.xbrief.json.");
321
326
  }
322
327
  export function requireHandoffAllowed(projectRoot, action) {
323
328
  const session = readSession(projectRoot);
@@ -330,7 +335,7 @@ export function guardProbeArtifact(projectRoot, artifactPath) {
330
335
  return requireHandoffAllowed(projectRoot, `probe artifact write (${artifactPath})`);
331
336
  }
332
337
  export function guardPlanProbeRegistration(projectRoot) {
333
- return requireHandoffAllowed(projectRoot, "completedStrategies.probe registration in plan.vbrief.json");
338
+ return requireHandoffAllowed(projectRoot, "completedStrategies.probe registration in plan.xbrief.json");
334
339
  }
335
340
  export function sessionSummary(session) {
336
341
  return {
@@ -2,6 +2,10 @@
2
2
  * Risk-tiered judgment-gate engine (#1419 Slice 3). Port of scripts/verify_judgment_gates.py.
3
3
  */
4
4
  import { type JudgmentGatesPolicy } from "./judgment-policy.js";
5
+ /** Thrown when a design-critique clearance reason fails presence/shape (never content). */
6
+ export declare class JudgmentClearanceShapeError extends Error {
7
+ constructor(message: string);
8
+ }
5
9
  /** Display/back-compat constant; resolution flows through resolveAuditDir (#2109). */
6
10
  export declare const AUDIT_DIR_REL = "vbrief/.audit";
7
11
  export declare const CLEARANCE_LOG_NAME = "judgment-gate-clearances.jsonl";
@@ -7,8 +7,15 @@ import { appendFileSync, existsSync, lstatSync, mkdirSync, readFileSync, statSyn
7
7
  import { join, resolve } from "node:path";
8
8
  import { assertProjectionContained, assertWriteTargetSafe, ProjectionContainmentError, } from "../fs/projection-containment.js";
9
9
  import { resolveAuditDir } from "../layout/resolve.js";
10
- import { resolveJudgmentGates } from "./judgment-policy.js";
10
+ import { DESIGN_CRITIQUE_GATE_ID, designCritiqueClearanceShapeOk, resolveJudgmentGates, } from "./judgment-policy.js";
11
11
  import { matchAny } from "./pathspec.js";
12
+ /** Thrown when a design-critique clearance reason fails presence/shape (never content). */
13
+ export class JudgmentClearanceShapeError extends Error {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "JudgmentClearanceShapeError";
17
+ }
18
+ }
12
19
  /** Display/back-compat constant; resolution flows through resolveAuditDir (#2109). */
13
20
  export const AUDIT_DIR_REL = "vbrief/.audit";
14
21
  export const CLEARANCE_LOG_NAME = "judgment-gate-clearances.jsonl";
@@ -163,6 +170,12 @@ export function readClearances(projectRoot, logPath) {
163
170
  return out;
164
171
  }
165
172
  export function recordClearance(projectRoot, options) {
173
+ if (options.gate_id === DESIGN_CRITIQUE_GATE_ID &&
174
+ !designCritiqueClearanceShapeOk(options.reason ?? "")) {
175
+ throw new JudgmentClearanceShapeError("design-critique clearance reason must match shape " +
176
+ "'design-critique: warranted | not warranted, because ...' " +
177
+ "(presence and shape only; the because-clause is not scored)");
178
+ }
166
179
  const path = options.log_path ?? clearanceLogPath(projectRoot);
167
180
  const auditDir = resolveAuditDir(projectRoot);
168
181
  assertProjectionContained(projectRoot, auditDir);
@@ -202,6 +215,15 @@ export function fingerprintScope(evidence) {
202
215
  const payload = JSON.stringify(sorted);
203
216
  return createHash("sha256").update(payload, "utf8").digest("hex");
204
217
  }
218
+ function acceptClearanceShape(gateId, clearance) {
219
+ if (clearance === null) {
220
+ return null;
221
+ }
222
+ if (gateId !== DESIGN_CRITIQUE_GATE_ID) {
223
+ return clearance;
224
+ }
225
+ return designCritiqueClearanceShapeOk(String(clearance.reason ?? "")) ? clearance : null;
226
+ }
205
227
  function lookupClearance(clearances, gateId, scope) {
206
228
  let valid = null;
207
229
  let stale = null;
@@ -383,7 +405,8 @@ export function buildReport(projectRoot, candidate, options = {}) {
383
405
  continue;
384
406
  }
385
407
  const scope = fingerprintScope(evidence);
386
- const [valid, stale] = lookupClearance(records, String(gate.id), scope);
408
+ const [validRaw, stale] = lookupClearance(records, String(gate.id), scope);
409
+ const valid = acceptClearanceShape(String(gate.id), validRaw);
387
410
  outcomes.push({
388
411
  gate_id: String(gate.id),
389
412
  gate_class: String(gate.class),
@@ -605,15 +628,24 @@ function clearMain(argv) {
605
628
  if (args.updatedAt !== null)
606
629
  evidence["age-days"] = args.updatedAt;
607
630
  const scope = fingerprintScope(evidence);
608
- const entry = recordClearance(projectRoot, {
609
- gate_id: args.gateId,
610
- cleared_scope: scope,
611
- reviewers: args.reviewers,
612
- actor: args.actor,
613
- reason: args.reason,
614
- });
615
- process.stdout.write(`recorded clearance ${String(entry.clearance_id)} for gate ${JSON.stringify(args.gateId)} (cleared_scope=${scope.slice(0, 12)}...)\n`);
616
- return 0;
631
+ try {
632
+ const entry = recordClearance(projectRoot, {
633
+ gate_id: args.gateId,
634
+ cleared_scope: scope,
635
+ reviewers: args.reviewers,
636
+ actor: args.actor,
637
+ reason: args.reason,
638
+ });
639
+ process.stdout.write(`recorded clearance ${String(entry.clearance_id)} for gate ${JSON.stringify(args.gateId)} (cleared_scope=${scope.slice(0, 12)}...)\n`);
640
+ return 0;
641
+ }
642
+ catch (err) {
643
+ if (err instanceof JudgmentClearanceShapeError) {
644
+ process.stderr.write(`${err.message}\n`);
645
+ return 1;
646
+ }
647
+ throw err;
648
+ }
617
649
  }
618
650
  function parseEvalArgs(argv) {
619
651
  const parsed = {
@@ -23,7 +23,7 @@ import { RunSummaryEmitter } from "../run-summary/emit.js";
23
23
  import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
24
24
  import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
25
25
  import { policyColonInvocation } from "./policy-invocation.js";
26
- import { appendAuditLog, loadProjectDefinition, projectDefinitionPath } from "./resolve.js";
26
+ import { appendAuditLog, loadProjectDefinition, POLICY_AUDIT_NOOP_STDOUT, projectDefinitionPath, } from "./resolve.js";
27
27
  /** Canonical dotted policy field name. */
28
28
  export const FIELD_CEREMONY_DIAL = "plan.policy.ceremonyDial";
29
29
  /** Short alias for `policy:show --field=ceremonyDial`. */
@@ -527,7 +527,7 @@ export function setCeremonyDial(projectRoot, options) {
527
527
  }
528
528
  }
529
529
  const plan = data.plan;
530
- migrateLegacyPolicyKey(plan);
530
+ const legacyKeyMigrated = migrateLegacyPolicyKey(plan);
531
531
  const existingPolicy = plan[PLAN_POLICY_KEY];
532
532
  if (typeof existingPolicy !== "object" ||
533
533
  existingPolicy === null ||
@@ -564,7 +564,9 @@ export function setCeremonyDial(projectRoot, options) {
564
564
  override: nextOverride,
565
565
  };
566
566
  const prevParsed = parseConfig(previous).config;
567
- const changedFlag = prevParsed.enabled !== nextBlock.enabled || prevParsed.override !== nextBlock.override;
567
+ const changedFlag = prevParsed.enabled !== nextBlock.enabled ||
568
+ prevParsed.override !== nextBlock.override ||
569
+ legacyKeyMigrated;
568
570
  policyBlock.ceremonyDial = nextBlock;
569
571
  if (changedFlag) {
570
572
  atomicWriteProjectDefinition(path, data);
@@ -580,15 +582,13 @@ export function setCeremonyDial(projectRoot, options) {
580
582
  if (note) {
581
583
  parts.push(`note=${note.replace(/\n/g, " ").replace(/\r/g, " ")}`);
582
584
  }
583
- appendAuditLog(projectRoot, parts.join(" "));
585
+ appendAuditLog(projectRoot, parts.join(" "), changedFlag);
584
586
  return { changed: changedFlag };
585
587
  });
586
588
  const resolved = resolveCeremonyDial(projectRoot);
587
589
  const lines = [
588
590
  `\u2713 ${FIELD_CEREMONY_DIAL} updated.`,
589
- changed
590
- ? " audit: meta/policy-changes.log updated."
591
- : " no-op: value already matched (audit entry still appended for trail).",
591
+ changed ? " audit: meta/policy-changes.log updated." : POLICY_AUDIT_NOOP_STDOUT,
592
592
  formatCeremonyDialStatusLine(resolved),
593
593
  ];
594
594
  return { exitCode: 0, stdout: `${lines.join("\n")}\n`, changed };
@@ -1,6 +1,12 @@
1
1
  import type { HookHost } from "../hooks/dispatcher.js";
2
+ export type { HookHost };
2
3
  export declare const FIELD_HOST_HOOKS = "plan.policy.hostHooks";
3
4
  export declare const FIELD_HOST_HOOKS_CLI_ALIAS = "hostHooks";
5
+ export declare const DISABLE_HOST_HOOKS_SUBCOMMAND = "disable-host-hooks";
6
+ /** Confirm-gated disable verb for unused-host recovery copy (#3571). CLI form: no go-task `--`. */
7
+ export declare function disableHostHooksInvocation(trailing?: string): string;
8
+ export declare const HOST_HOOKS_DISABLE_CAPABILITY_COST_DISCLOSURE: string;
9
+ export declare const UNUSED_HOST_HOOKS_RECOVERY: string;
4
10
  /** Per-host Directive hook deposit toggles (#2752). */
5
11
  export type HostHooksPolicy = Record<HookHost, boolean>;
6
12
  export declare const DEFAULT_HOST_HOOKS_POLICY: HostHooksPolicy;
@@ -18,4 +24,18 @@ export declare function isHostHookDepositEnabled(host: HookHost, policy?: HostHo
18
24
  export declare function inspectHostHooks(data: Record<string, unknown> | null): HostHooksPolicyField;
19
25
  /** Resolve host hook deposit policy from PROJECT-DEFINITION on disk. */
20
26
  export declare function loadHostHooksPolicyFromProject(projectRoot: string): HostHooksPolicy;
27
+ export declare function parseHookHost(value: string | undefined): HookHost | null;
28
+ export interface DisableHostHooksOptions {
29
+ readonly host: HookHost;
30
+ readonly confirm: boolean;
31
+ readonly actor?: string;
32
+ readonly note?: string;
33
+ }
34
+ export interface DisableHostHooksResult {
35
+ readonly exitCode: 0 | 1 | 2;
36
+ readonly stdout: string;
37
+ readonly changed: boolean;
38
+ }
39
+ /** Persist `hostHooks.<host>=false` after capability-cost disclosure (#3571). */
40
+ export declare function disableHostHooks(projectRoot: string, options: DisableHostHooksOptions): DisableHostHooksResult;
21
41
  //# sourceMappingURL=host-hooks.d.ts.map