@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
@@ -1,3 +1,4 @@
1
+ import { type LifecycleVisibleResult } from "../lifecycle-visible/evaluate.js";
1
2
  import { type HostContentSurfaceSeams } from "../platform/host-content-surface.js";
2
3
  import { type EnvironmentContext } from "../platform/shell-context.js";
3
4
  import { type CeremonyDialInputs, type CeremonyDialSelection, type ProvisionalCeremonyEstimateHints } from "../policy/ceremony-dial.js";
@@ -23,8 +24,20 @@ export type SessionCeremonyTier = (typeof SESSION_CEREMONY_TIERS)[number];
23
24
  export declare const COLD_CEREMONY_TIER: SessionCeremonyTier;
24
25
  export declare const REARM_CEREMONY_TIER: SessionCeremonyTier;
25
26
  export declare const QUICK_STEPS: readonly ["alignment", "branch_policy", "triage_welcome", "verify_tools"];
27
+ /** Session / full gated ritual set. Write dispatch uses a per-surface subset (#3738). */
26
28
  export declare const GATED_STEPS: readonly ["agent_hooks", "doctor", "cache_fresh"];
27
29
  export type GatedStepName = (typeof GATED_STEPS)[number];
30
+ /**
31
+ * Gated steps a write/spawn mutation must prove from recorded ritual state (#3738).
32
+ * `cache_fresh` is a work-selection precondition, not a write-authorization one.
33
+ */
34
+ export declare const WRITE_GATED_REQUIRED_STEPS: readonly ["agent_hooks", "doctor"];
35
+ /**
36
+ * Gated steps the write path may execute (#3738).
37
+ * Hook readiness is deliberately non-cacheable; doctor and cache_fresh stay
38
+ * session-surface only.
39
+ */
40
+ export declare const WRITE_GATED_EXECUTE_STEPS: readonly ["agent_hooks"];
28
41
  /** Env opt-in for optional session:start network (release probe + triage cache hydrate) (#2991). */
29
42
  export declare const ENV_SESSION_START_NETWORK = "DEFT_SESSION_START_NETWORK";
30
43
  /** Human-readable skip notice when optional network is off the hot path (#2991). */
@@ -117,6 +130,11 @@ export interface SessionStartOptions {
117
130
  * network is enabled. Inject in tests.
118
131
  */
119
132
  readonly probeScm?: (options: ProbeScmReadinessOptions) => ScmReadinessReport;
133
+ /**
134
+ * #3505: per-clone lifecycle-root visibility (ignore / skip-worktree).
135
+ * Warn-only; never blocks session:start. Inject in tests.
136
+ */
137
+ readonly probeLifecycleVisible?: (projectRoot: string, runGit: GitRunner) => LifecycleVisibleResult;
120
138
  /**
121
139
  * #3214: ceremony dial inputs (task size × model tier × project shape).
122
140
  * Missing fields are filled by the headless provisional classifier
@@ -3,6 +3,7 @@ import { emitSessionEvalReadback } from "../eval/readback.js";
3
3
  import { bindSessionGeneration } from "../freshness/bind.js";
4
4
  import { readLiveGeneration } from "../freshness/generation.js";
5
5
  import { MIGRATE_COMPLETION_NUDGE, shouldEmitMigrateNudge } from "../init-deposit/migrate.js";
6
+ import { evaluateLifecycleVisible, formatLifecycleVisibleSessionLines, } from "../lifecycle-visible/evaluate.js";
6
7
  import { hostContentSurfaceToDict, maybeFormatHostContentSurfaceLines, } from "../platform/host-content-surface.js";
7
8
  import { detectEnvironmentContext, environmentContextToDict, formatEnvironmentContext, } from "../platform/shell-context.js";
8
9
  import { ceremonyDialToDict, formatCeremonyDialStatusLine, mergeCeremonyDialDeferrals, resolveCeremonyDial, resolveSessionCeremonyDialInputs, } from "../policy/ceremony-dial.js";
@@ -26,8 +27,8 @@ import { ceremonyDialEvidenceToDict, collectCeremonyDialConsumerEvidence, format
26
27
  import { effortBudgetToDict, maybeFormatEffortBudgetLines, resolveProductionHostEffortDescriptor, } from "./effort-budget.js";
27
28
  import { defaultGitRunner, gitHead, gitIsAncestor, worktreePath } from "./git.js";
28
29
  import { applyWorktreeOccupancy, } from "./occupancy.js";
29
- import { runOrientationCompression, } from "./orientation-compression.js";
30
- import { emitSessionStartProcessCost } from "./process-cost.js";
30
+ import { resolveSessionCompact, runOrientationCompression, } from "./orientation-compression.js";
31
+ import { emitSessionStartProcessCost, formatSessionStartCeremonyCostLine } from "./process-cost.js";
31
32
  import { probeSessionReleaseAvailability, } from "./release-availability.js";
32
33
  import { newRitualStatePayload, readRitualState, ritualStatePath, ritualStep, writeRitualState, } from "./ritual-sentinel.js";
33
34
  import { timestampIso } from "./time.js";
@@ -49,7 +50,36 @@ export const QUICK_STEPS = [
49
50
  "triage_welcome",
50
51
  "verify_tools",
51
52
  ];
53
+ /** Session / full gated ritual set. Write dispatch uses a per-surface subset (#3738). */
52
54
  export const GATED_STEPS = ["agent_hooks", "doctor", "cache_fresh"];
55
+ /**
56
+ * Gated steps a write/spawn mutation must prove from recorded ritual state (#3738).
57
+ * `cache_fresh` is a work-selection precondition, not a write-authorization one.
58
+ */
59
+ export const WRITE_GATED_REQUIRED_STEPS = [
60
+ "agent_hooks",
61
+ "doctor",
62
+ ];
63
+ /**
64
+ * Gated steps the write path may execute (#3738).
65
+ * Hook readiness is deliberately non-cacheable; doctor and cache_fresh stay
66
+ * session-surface only.
67
+ */
68
+ export const WRITE_GATED_EXECUTE_STEPS = [
69
+ "agent_hooks",
70
+ ];
71
+ /** Per-clone ignore/index hide of lifecycle roots — warn-only (#3505). */
72
+ function pushLifecycleVisibleAdvisory(lines, projectRoot, options, runGit) {
73
+ try {
74
+ const probe = options.probeLifecycleVisible ??
75
+ ((root, git) => evaluateLifecycleVisible({ projectRoot: root, runGit: git }));
76
+ const result = probe(projectRoot, runGit);
77
+ lines.push(...formatLifecycleVisibleSessionLines(result));
78
+ }
79
+ catch {
80
+ // best-effort — session:start must not abort on a clone-local advisory
81
+ }
82
+ }
53
83
  /** Standing one-liner when coverageDebt/checkResume is non-default (#3314). */
54
84
  function pushCoverageCheckResumeDisclosure(lines, projectRoot) {
55
85
  try {
@@ -494,6 +524,8 @@ function runReadOnlySessionStart(projectRoot, options, instant, environment) {
494
524
  lines.push(...formatScmReadinessLines(scm));
495
525
  lines.push(...hostSurface.lines);
496
526
  lines.push(...effortBudget.lines);
527
+ const runGit = options.runGit ?? defaultGitRunner;
528
+ pushLifecycleVisibleAdvisory(lines, projectRoot, options, runGit);
497
529
  const resultPayload = {
498
530
  ready: true,
499
531
  exit_code: 0,
@@ -576,6 +608,7 @@ function runSessionRearm(projectRoot, options, instant, environment) {
576
608
  ...effortBudget.lines,
577
609
  REARM_SKIPPED_FAT_PATH_MESSAGE,
578
610
  ];
611
+ pushLifecycleVisibleAdvisory(lines, projectRoot, options, runGit);
579
612
  // Light branch-policy disclosure (local only) so re-arm still surfaces policy state.
580
613
  const policyResult = resolvePolicy(projectRoot);
581
614
  const policyMessage = disclosureLine(policyResult);
@@ -739,6 +772,10 @@ function runSessionRearm(projectRoot, options, instant, environment) {
739
772
  optionalNetwork: false,
740
773
  steps: stepTimings,
741
774
  }, { projectRoot });
775
+ // #3508: operator-visible CLI process time. ⊗ not #3286 Later graduation input.
776
+ if (!resolveSessionCompact({ compact: options.compact, env: options.env })) {
777
+ lines.push(formatSessionStartCeremonyCostLine(REARM_CEREMONY_TIER, totalMs));
778
+ }
742
779
  return {
743
780
  code,
744
781
  payload: {
@@ -976,6 +1013,12 @@ export function runSessionStart(projectRoot, options = {}) {
976
1013
  name: "effort_budget",
977
1014
  duration_ms: elapsedMs(effortBudgetStepStarted),
978
1015
  });
1016
+ const lifecycleVisibleStarted = performance.now();
1017
+ pushLifecycleVisibleAdvisory(lines, projectRoot, options, runGit);
1018
+ stepTimings.push({
1019
+ name: "lifecycle_visible",
1020
+ duration_ms: elapsedMs(lifecycleVisibleStarted),
1021
+ });
979
1022
  if (!quickSteps.branch_policy) {
980
1023
  const stepStarted = performance.now();
981
1024
  const result = resolvePolicy(projectRoot);
@@ -1461,6 +1504,10 @@ export function runSessionStart(projectRoot, options = {}) {
1461
1504
  optionalNetwork: allowOptionalNetwork,
1462
1505
  steps: stepTimings,
1463
1506
  }, { projectRoot });
1507
+ // #3508: operator-visible CLI process time. ⊗ not #3286 Later graduation input.
1508
+ if (!resolveSessionCompact({ compact: options.compact, env: options.env })) {
1509
+ lines.push(formatSessionStartCeremonyCostLine(COLD_CEREMONY_TIER, totalMs));
1510
+ }
1464
1511
  return { code, payload: resultPayload, lines };
1465
1512
  }
1466
1513
  export { ritualStatePath };
@@ -1,11 +1,32 @@
1
1
  import { type ActiveCliCheckResult, type ActiveCliCheckSeams } from "./active-cli.js";
2
2
  import { type GitRunner } from "./git.js";
3
3
  import { type DirectivePosture } from "./posture.js";
4
- import { type GatedStepName, type SessionCeremonyTier } from "./session-start.js";
4
+ import { type GatedStepName, type SessionCeremonyTier, WRITE_GATED_EXECUTE_STEPS, WRITE_GATED_REQUIRED_STEPS } from "./session-start.js";
5
5
  export { formatCacheFetchAllRecoveryCommand, recoveryHintForStaleFailure, } from "./cache-recovery.js";
6
+ export { WRITE_GATED_EXECUTE_STEPS, WRITE_GATED_REQUIRED_STEPS };
6
7
  export declare const ENV_SKIP = "DEFT_SESSION_RITUAL_SKIP";
7
8
  export { ENTRYPOINT_TIMEOUT_EXIT_CODE, ENTRYPOINT_TIMEOUT_SECONDS, } from "./ritual-entrypoint.js";
8
9
  export declare const GATED_ENTRYPOINT_COMMANDS: Readonly<Record<GatedStepName, readonly string[]>>;
10
+ /** Explicit skip flag threaded through the gated `cache_fresh` argv (#3507). */
11
+ export declare const SKIP_DRIFT_PROBE_FLAG = "--skip-drift-probe";
12
+ /** Explicit work-selection flag threaded through the gated `cache_fresh` argv (#3507). */
13
+ export declare const WORK_SELECTION_FLAG = "--work-selection";
14
+ /** Distinct ritual-state field — never `deferred_reason` (#3507). */
15
+ export declare const DRIFT_PROBE_SKIPPED_NO_WORK_SELECTION = "skipped-no-work-selection";
16
+ export type WorkSelectionKind = "none" | "active-story" | "triage-queue" | "ordered-plan" | "for-issue" | "explicit-flag";
17
+ export interface WorkSelectionSignal {
18
+ readonly inPlay: boolean;
19
+ readonly kind: WorkSelectionKind;
20
+ }
21
+ export type DetectWorkSelection = (projectRoot: string) => WorkSelectionSignal;
22
+ /**
23
+ * Default work-selection detector for the gated `cache_fresh` seam (#3507).
24
+ * Active story xBRIEF (scope:activate residue), unexhausted ordered-plan /
25
+ * triage sequence. `--for-issue` is a positive evaluate override, not this
26
+ * detector — the gated argv never carries it.
27
+ */
28
+ export declare function detectWorkSelection(projectRoot: string): WorkSelectionSignal;
29
+ export declare function resolveGatedEntrypointCommand(stepName: GatedStepName, projectRoot: string, detect?: DetectWorkSelection): string[];
9
30
  export interface VerifyResult {
10
31
  readonly code: number;
11
32
  readonly message: string;
@@ -30,7 +51,8 @@ export type RitualRunner = (command: readonly string[], projectRoot: string) =>
30
51
  export declare function formatRitualRecoveryInstruction(tier?: SessionCeremonyTier): string;
31
52
  /**
32
53
  * Sanctioned audited defer copy for gated `cache_fresh` failure (#3506 / #3507).
33
- * The gated argv is always `["verify:cache-fresh"]`, so this is unconditional.
54
+ * Independent of the #3507 work-selection argv seam — defer is still the
55
+ * operator-audited escape, not the drift-probe skip.
34
56
  */
35
57
  export declare function formatCacheFreshDeferSoftPath(): string;
36
58
  export interface VerifySessionRitualOptions {
@@ -58,7 +80,26 @@ export interface VerifySessionRitualOptions {
58
80
  * {@link checkActiveCliAgainstTarget}.
59
81
  */
60
82
  readonly checkActiveCli?: (targetVersion: string | null, seams?: ActiveCliCheckSeams) => ActiveCliCheckResult;
83
+ /** Injectable work-selection detector for gated `cache_fresh` (#3507). */
84
+ readonly detectWorkSelection?: DetectWorkSelection;
85
+ /**
86
+ * Required gated steps for this surface. Defaults to all {@link GATED_STEPS}
87
+ * (session / full gated verify). Write dispatch uses
88
+ * {@link WRITE_GATED_REQUIRED_STEPS} (#3738).
89
+ */
90
+ readonly requiredGatedSteps?: readonly GatedStepName[];
91
+ /**
92
+ * Gated steps this surface may execute. Defaults to all {@link GATED_STEPS}.
93
+ * Write dispatch uses {@link WRITE_GATED_EXECUTE_STEPS} so `cache_fresh` and
94
+ * `doctor` cannot run as write-path repair (#3738).
95
+ */
96
+ readonly executeGatedSteps?: readonly GatedStepName[];
61
97
  }
98
+ /**
99
+ * Options for the write/spawn mutation verifier (#3738).
100
+ * Required set omits `cache_fresh`; execution authority is `agent_hooks` only.
101
+ */
102
+ export declare function writeGateRitualOptions(extras?: Omit<VerifySessionRitualOptions, "requiredGatedSteps" | "executeGatedSteps" | "tier">): VerifySessionRitualOptions;
62
103
  export interface InspectSessionRitualOptions {
63
104
  readonly tier?: "quick" | "gated";
64
105
  readonly now?: Date;
@@ -71,8 +112,9 @@ export interface InspectSessionRitualOptions {
71
112
  * Read-only ritual-state inspection for host hooks.
72
113
  *
73
114
  * Unlike {@link verifySessionRitual}, this never runs missing gated entrypoints
74
- * and never rewrites `.deft/ritual-state.json`. A PreToolUse decision must be a
75
- * probe, not a hidden `doctor` / cache-refresh mutation boundary.
115
+ * and never rewrites `.deft/ritual-state.json`. Write dispatch does **not** use
116
+ * this function: it must still execute `agent_hooks` (#3738). A PreToolUse
117
+ * decision must not become a hidden `doctor` / cache-refresh mutation boundary.
76
118
  */
77
119
  export declare function inspectSessionRitual(projectRoot: string, options?: InspectSessionRitualOptions): VerifyResult;
78
120
  export declare function verifySessionRitual(projectRoot: string, options?: VerifySessionRitualOptions): VerifyResult;
@@ -1,5 +1,8 @@
1
- import { existsSync } from "node:fs";
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { join } from "node:path";
2
3
  import { readCorePackageVersion } from "../engine-version.js";
4
+ import { hasArtifactSuffix } from "../layout/resolve.js";
5
+ import { readPlanSequence } from "../plan-sequence/store.js";
3
6
  import { formatFrameworkCommand } from "../render/framework-commands.js";
4
7
  import { checkActiveCliAgainstTarget, } from "./active-cli.js";
5
8
  import { defaultGitRunner, gitHead, gitIsAncestor, worktreePath } from "./git.js";
@@ -7,9 +10,10 @@ import { pythonJsonDump } from "./json.js";
7
10
  import { ENV_SESSION_POSTURE, readOnlyPostureMessage, resolveSessionPosture, ritualStateIsPostureAuthority, } from "./posture.js";
8
11
  import { defaultRitualRunner } from "./ritual-entrypoint.js";
9
12
  import { readRitualState, ritualStateMarksRearmNeeded, ritualStatePath, ritualStep, writeRitualState, } from "./ritual-sentinel.js";
10
- import { formatSessionStartRecoveryCommand, GATED_STEPS, QUICK_STEPS, } from "./session-start.js";
13
+ import { formatSessionStartRecoveryCommand, GATED_STEPS, QUICK_STEPS, WRITE_GATED_EXECUTE_STEPS, WRITE_GATED_REQUIRED_STEPS, } from "./session-start.js";
11
14
  import { resolveSessionRitualStalenessHours } from "./staleness.js";
12
15
  export { formatCacheFetchAllRecoveryCommand, recoveryHintForStaleFailure, } from "./cache-recovery.js";
16
+ export { WRITE_GATED_EXECUTE_STEPS, WRITE_GATED_REQUIRED_STEPS };
13
17
  export const ENV_SKIP = "DEFT_SESSION_RITUAL_SKIP";
14
18
  export { ENTRYPOINT_TIMEOUT_EXIT_CODE, ENTRYPOINT_TIMEOUT_SECONDS, } from "./ritual-entrypoint.js";
15
19
  export const GATED_ENTRYPOINT_COMMANDS = {
@@ -17,6 +21,64 @@ export const GATED_ENTRYPOINT_COMMANDS = {
17
21
  doctor: ["doctor"],
18
22
  cache_fresh: ["verify:cache-fresh"],
19
23
  };
24
+ /** Explicit skip flag threaded through the gated `cache_fresh` argv (#3507). */
25
+ export const SKIP_DRIFT_PROBE_FLAG = "--skip-drift-probe";
26
+ /** Explicit work-selection flag threaded through the gated `cache_fresh` argv (#3507). */
27
+ export const WORK_SELECTION_FLAG = "--work-selection";
28
+ /** Distinct ritual-state field — never `deferred_reason` (#3507). */
29
+ export const DRIFT_PROBE_SKIPPED_NO_WORK_SELECTION = "skipped-no-work-selection";
30
+ function hasActiveStoryArtifact(projectRoot) {
31
+ for (const dirName of ["xbrief", "vbrief"]) {
32
+ const activeDir = join(projectRoot, dirName, "active");
33
+ try {
34
+ for (const entry of readdirSync(activeDir, { withFileTypes: true })) {
35
+ if (entry.isFile() && hasArtifactSuffix(entry.name))
36
+ return true;
37
+ }
38
+ }
39
+ catch {
40
+ /* missing or unreadable active folder is not work selection */
41
+ }
42
+ }
43
+ return false;
44
+ }
45
+ function hasUnexhaustedPlanSequence(projectRoot) {
46
+ try {
47
+ const sequence = readPlanSequence(projectRoot);
48
+ if (sequence === null || sequence.exhausted === true)
49
+ return null;
50
+ return sequence.sequence_kind === "triage" ? "triage-queue" : "ordered-plan";
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ /**
57
+ * Default work-selection detector for the gated `cache_fresh` seam (#3507).
58
+ * Active story xBRIEF (scope:activate residue), unexhausted ordered-plan /
59
+ * triage sequence. `--for-issue` is a positive evaluate override, not this
60
+ * detector — the gated argv never carries it.
61
+ */
62
+ export function detectWorkSelection(projectRoot) {
63
+ if (hasActiveStoryArtifact(projectRoot)) {
64
+ return { inPlay: true, kind: "active-story" };
65
+ }
66
+ const planKind = hasUnexhaustedPlanSequence(projectRoot);
67
+ if (planKind !== null) {
68
+ return { inPlay: true, kind: planKind };
69
+ }
70
+ return { inPlay: false, kind: "none" };
71
+ }
72
+ export function resolveGatedEntrypointCommand(stepName, projectRoot, detect = detectWorkSelection) {
73
+ const base = [...GATED_ENTRYPOINT_COMMANDS[stepName]];
74
+ if (stepName !== "cache_fresh")
75
+ return base;
76
+ const selection = detect(projectRoot);
77
+ if (selection.inPlay) {
78
+ return [...base, WORK_SELECTION_FLAG];
79
+ }
80
+ return [...base, SKIP_DRIFT_PROBE_FLAG];
81
+ }
20
82
  function truthy(raw) {
21
83
  return new Set(["1", "true", "yes", "on"]).has((raw ?? "").trim().toLowerCase());
22
84
  }
@@ -25,6 +87,7 @@ function stepPasses(step) {
25
87
  return false;
26
88
  if (step.deferred_reason)
27
89
  return true;
90
+ // `drift_probe` is a distinct diagnostic; it must never satisfy a step (#3507).
28
91
  return step.ok === true;
29
92
  }
30
93
  function failedStepMessage(tierName, stepName, step) {
@@ -52,14 +115,29 @@ export function formatRitualRecoveryInstruction(tier = "cold") {
52
115
  }
53
116
  /**
54
117
  * Sanctioned audited defer copy for gated `cache_fresh` failure (#3506 / #3507).
55
- * The gated argv is always `["verify:cache-fresh"]`, so this is unconditional.
118
+ * Independent of the #3507 work-selection argv seam — defer is still the
119
+ * operator-audited escape, not the drift-probe skip.
56
120
  */
57
121
  export function formatCacheFreshDeferSoftPath() {
58
122
  const cmd = formatFrameworkCommand(["session:start", "--", "--defer", "cache_fresh=<reason>"]);
59
123
  return `Soft path (audited): \`${cmd}\`.`;
60
124
  }
61
- function runGatedStep(projectRoot, payload, stepName, runner, now) {
62
- const command = [...GATED_ENTRYPOINT_COMMANDS[stepName]];
125
+ function applyCacheFreshDriftProbeField(payload, command) {
126
+ if (command.includes(SKIP_DRIFT_PROBE_FLAG)) {
127
+ payload.drift_probe = DRIFT_PROBE_SKIPPED_NO_WORK_SELECTION;
128
+ return;
129
+ }
130
+ if (payload.drift_probe === DRIFT_PROBE_SKIPPED_NO_WORK_SELECTION) {
131
+ delete payload.drift_probe;
132
+ }
133
+ }
134
+ function shouldRearmDriftProbe(payload, projectRoot, detect) {
135
+ if (payload.drift_probe !== DRIFT_PROBE_SKIPPED_NO_WORK_SELECTION)
136
+ return false;
137
+ return detect(projectRoot).inPlay;
138
+ }
139
+ function runGatedStep(projectRoot, payload, stepName, runner, now, detect = detectWorkSelection) {
140
+ const command = resolveGatedEntrypointCommand(stepName, projectRoot, detect);
63
141
  const { code, stdout, stderr } = runner(command, projectRoot);
64
142
  const message = stdout.trim() || stderr.trim() || `${command[0]} exited ${code}`;
65
143
  const gated = payload.gated_steps ?? {};
@@ -71,6 +149,9 @@ function runGatedStep(projectRoot, payload, stepName, runner, now) {
71
149
  command,
72
150
  });
73
151
  payload.gated_steps = gated;
152
+ if (stepName === "cache_fresh") {
153
+ applyCacheFreshDriftProbeField(payload, command);
154
+ }
74
155
  try {
75
156
  writeRitualState(projectRoot, payload);
76
157
  }
@@ -164,7 +245,8 @@ function evaluateLoadedState(projectRoot, state, input) {
164
245
  }
165
246
  }
166
247
  if (input.tier === "gated") {
167
- for (const stepName of GATED_STEPS) {
248
+ const requiredGated = input.requiredGatedSteps ?? GATED_STEPS;
249
+ for (const stepName of requiredGated) {
168
250
  const step = state.gatedSteps[stepName];
169
251
  if (!stepPasses(step)) {
170
252
  return {
@@ -181,12 +263,31 @@ function evaluateLoadedState(projectRoot, state, input) {
181
263
  recoveryTier: null,
182
264
  };
183
265
  }
266
+ /**
267
+ * Options for the write/spawn mutation verifier (#3738).
268
+ * Required set omits `cache_fresh`; execution authority is `agent_hooks` only.
269
+ */
270
+ export function writeGateRitualOptions(extras = {}) {
271
+ return {
272
+ ...extras,
273
+ tier: "gated",
274
+ posture: extras.posture ?? "mutation",
275
+ requiredGatedSteps: WRITE_GATED_REQUIRED_STEPS,
276
+ executeGatedSteps: WRITE_GATED_EXECUTE_STEPS,
277
+ // Write dispatch already refuses skip (dispatcher passes bypass: false).
278
+ // Default the same here so CI DEFT_SESSION_RITUAL_SKIP=1 cannot empty
279
+ // the execute loop in tests that use this helper (#3738).
280
+ bypass: extras.bypass ?? false,
281
+ envSkip: extras.envSkip ?? "",
282
+ };
283
+ }
184
284
  /**
185
285
  * Read-only ritual-state inspection for host hooks.
186
286
  *
187
287
  * Unlike {@link verifySessionRitual}, this never runs missing gated entrypoints
188
- * and never rewrites `.deft/ritual-state.json`. A PreToolUse decision must be a
189
- * probe, not a hidden `doctor` / cache-refresh mutation boundary.
288
+ * and never rewrites `.deft/ritual-state.json`. Write dispatch does **not** use
289
+ * this function: it must still execute `agent_hooks` (#3738). A PreToolUse
290
+ * decision must not become a hidden `doctor` / cache-refresh mutation boundary.
190
291
  */
191
292
  export function inspectSessionRitual(projectRoot, options = {}) {
192
293
  const tier = options.tier ?? "quick";
@@ -361,16 +462,22 @@ export function verifySessionRitual(projectRoot, options = {}) {
361
462
  payload.gated_steps = gated;
362
463
  const runCmd = options.runner ?? defaultRitualRunner;
363
464
  const forced = new Set(options.forceGatedSteps ?? []);
465
+ const detect = options.detectWorkSelection ?? detectWorkSelection;
466
+ const executable = new Set(options.executeGatedSteps ?? [...GATED_STEPS]);
364
467
  for (const stepName of GATED_STEPS) {
468
+ if (!executable.has(stepName))
469
+ continue;
365
470
  const step = gated[stepName];
366
471
  if (step?.deferred_reason && stepName !== "agent_hooks")
367
472
  continue;
368
473
  // Hook readiness is a live mutation prerequisite, not a cacheable doctor
369
474
  // result. Re-run it at every gated boundary so registration/shim drift
370
475
  // cannot remain hidden behind an earlier green ritual record.
371
- if (stepName !== "agent_hooks" && stepPasses(step) && !forced.has(stepName))
476
+ const rearmDrift = stepName === "cache_fresh" && shouldRearmDriftProbe(payload, projectRoot, detect);
477
+ if (stepName !== "agent_hooks" && stepPasses(step) && !forced.has(stepName) && !rearmDrift) {
372
478
  continue;
373
- const writeError = runGatedStep(projectRoot, payload, stepName, runCmd, instant);
479
+ }
480
+ const writeError = runGatedStep(projectRoot, payload, stepName, runCmd, instant, detect);
374
481
  if (writeError !== null) {
375
482
  return {
376
483
  code: 2,
@@ -436,6 +543,7 @@ export function verifySessionRitual(projectRoot, options = {}) {
436
543
  now: instant,
437
544
  runGit: options.runGit,
438
545
  rebindForwardHead: true,
546
+ requiredGatedSteps: options.requiredGatedSteps,
439
547
  });
440
548
  if (isBypassed) {
441
549
  return {
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { finalizeCohort } from "./finalize-cohort.js";
3
- export declare function parseFinalizeCohortArgv(argv: readonly string[]): Parameters<typeof finalizeCohort>[0];
2
+ import { type FinalizeCohortArgs } from "./finalize-cohort.js";
3
+ export interface ParsedFinalizeCohortArgv extends FinalizeCohortArgs {
4
+ readonly help: boolean;
5
+ readonly error: string | null;
6
+ }
7
+ export declare const FINALIZE_COHORT_USAGE = "Usage: task swarm:finalize-cohort -- [--pr <n>[,<n>...]] [--stories <ids|paths>] [options]\n\nAfter cohort PRs merge, sweep story briefs active/ -> completed/ and open the lifecycle PR.\n\nOptions:\n --pr <n>[,<n>...] Merged PR numbers (closing issues map to active stories)\n --stories <ids|paths> Explicit story tokens (repeatable)\n --repo OWNER/REPO GitHub repo (or $GH_REPO)\n --project-root <path> Project root (default: cwd)\n --base-branch <name> Sweep/PR base (default: resolved delivery branch)\n --delivery-branch <name> Delivery-branch override when policy is untyped\n --label <name> Feature-branch label\n --dry-run Print plan; no git mutation\n --no-commit Sweep without committing / opening a PR\n --no-open-pr Commit without opening a PR\n --json Machine-readable result\n -h, --help Show this help\n";
8
+ export declare function parseFinalizeCohortArgv(argv: readonly string[]): ParsedFinalizeCohortArgv;
4
9
  export declare function finalizeCohortMain(argv?: string[]): number;
5
10
  //# sourceMappingURL=finalize-cohort-cli.d.ts.map