@deftai/directive-core 0.104.0 → 0.106.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 (287) hide show
  1. package/dist/authz/classify.js +1108 -67
  2. package/dist/check/cached-orchestrator.d.ts +1 -1
  3. package/dist/check/cached-orchestrator.js +2 -2
  4. package/dist/check/named-cause.d.ts +1 -1
  5. package/dist/check/named-cause.js +34 -4
  6. package/dist/codebase/default-extractor.d.ts +5 -0
  7. package/dist/codebase/default-extractor.js +7 -15
  8. package/dist/deposit/copy-tree.js +60 -0
  9. package/dist/deposit/python-free.js +26 -18
  10. package/dist/doctor/checks.d.ts +8 -0
  11. package/dist/doctor/checks.js +37 -25
  12. package/dist/doctor/doctor-state.d.ts +4 -0
  13. package/dist/doctor/doctor-state.js +7 -1
  14. package/dist/doctor/main.js +9 -19
  15. package/dist/doctor/types.d.ts +9 -0
  16. package/dist/forward-coverage/diff-coverage.d.ts +39 -0
  17. package/dist/forward-coverage/diff-coverage.js +112 -0
  18. package/dist/forward-coverage/diff-lines.d.ts +19 -0
  19. package/dist/forward-coverage/diff-lines.js +173 -0
  20. package/dist/forward-coverage/evaluate.d.ts +24 -9
  21. package/dist/forward-coverage/evaluate.js +125 -19
  22. package/dist/fs/contained-write.d.ts +115 -1
  23. package/dist/fs/contained-write.js +241 -5
  24. package/dist/fs/mutation-ledger.d.ts +54 -0
  25. package/dist/fs/mutation-ledger.js +133 -0
  26. package/dist/fs/non-product-dirs.d.ts +79 -0
  27. package/dist/fs/non-product-dirs.js +96 -0
  28. package/dist/hooks/dispatcher.d.ts +3 -1
  29. package/dist/hooks/dispatcher.js +14 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +2 -0
  32. package/dist/init-deposit/agent-hooks.d.ts +1 -1
  33. package/dist/init-deposit/agent-hooks.js +18 -16
  34. package/dist/init-deposit/core-guard-pin-content.d.ts +7 -0
  35. package/dist/init-deposit/core-guard-pin-content.js +393 -0
  36. package/dist/init-deposit/gitignore.js +9 -0
  37. package/dist/init-deposit/hygiene.d.ts +47 -11
  38. package/dist/init-deposit/hygiene.js +172 -37
  39. package/dist/init-deposit/legacy-detect.js +6 -2
  40. package/dist/init-deposit/refresh.d.ts +21 -1
  41. package/dist/init-deposit/refresh.js +106 -9
  42. package/dist/init-deposit/scaffold.js +28 -279
  43. package/dist/init-deposit/slash-deposit.js +13 -7
  44. package/dist/init-deposit/xbrief-projections.d.ts +8 -1
  45. package/dist/init-deposit/xbrief-projections.js +24 -11
  46. package/dist/intake/clause-derivation.d.ts +47 -1
  47. package/dist/intake/clause-derivation.js +238 -9
  48. package/dist/intake/issue-ingest.d.ts +3 -1
  49. package/dist/intake/issue-ingest.js +35 -6
  50. package/dist/lifecycle/completed-tracked-on-delivery.d.ts +5 -0
  51. package/dist/lifecycle/completed-tracked-on-delivery.js +85 -18
  52. package/dist/lifecycle-visible/evaluate.d.ts +98 -0
  53. package/dist/lifecycle-visible/evaluate.js +710 -0
  54. package/dist/lifecycle-visible/index.d.ts +2 -0
  55. package/dist/lifecycle-visible/index.js +2 -0
  56. package/dist/literal-acceptance/capture.d.ts +10 -0
  57. package/dist/literal-acceptance/capture.js +226 -77
  58. package/dist/literal-acceptance/evaluate.d.ts +29 -4
  59. package/dist/literal-acceptance/evaluate.js +70 -2
  60. package/dist/literal-acceptance/index.d.ts +3 -3
  61. package/dist/literal-acceptance/index.js +3 -3
  62. package/dist/literal-acceptance/safety.d.ts +41 -0
  63. package/dist/literal-acceptance/safety.js +126 -2
  64. package/dist/literal-acceptance/types.d.ts +8 -0
  65. package/dist/orchestration/judgment-policy.d.ts +13 -0
  66. package/dist/orchestration/judgment-policy.js +15 -0
  67. package/dist/orchestration/probe-session.js +13 -8
  68. package/dist/orchestration/verify-judgment-gates.d.ts +4 -0
  69. package/dist/orchestration/verify-judgment-gates.js +43 -11
  70. package/dist/orphan-active/evaluate.d.ts +7 -1
  71. package/dist/orphan-active/evaluate.js +102 -23
  72. package/dist/platform/platform-capabilities.js +2 -3
  73. package/dist/policy/base-branch.d.ts +37 -0
  74. package/dist/policy/base-branch.js +114 -0
  75. package/dist/policy/branch-sync.d.ts +80 -0
  76. package/dist/policy/branch-sync.js +211 -0
  77. package/dist/policy/ceremony-dial-escalation.js +1 -0
  78. package/dist/policy/ceremony-dial.js +11 -21
  79. package/dist/policy/delivery-branch.d.ts +5 -3
  80. package/dist/policy/delivery-branch.js +7 -3
  81. package/dist/policy/file-size-thresholds.d.ts +24 -0
  82. package/dist/policy/file-size-thresholds.js +23 -0
  83. package/dist/policy/forge-outage-retry.d.ts +47 -0
  84. package/dist/policy/forge-outage-retry.js +157 -0
  85. package/dist/policy/host-hooks.d.ts +20 -0
  86. package/dist/policy/host-hooks.js +121 -2
  87. package/dist/policy/index.d.ts +7 -0
  88. package/dist/policy/index.js +66 -7
  89. package/dist/policy/org-force-on-migration.js +2 -1
  90. package/dist/policy/plan-extensions.d.ts +1 -1
  91. package/dist/policy/plan-extensions.js +2 -0
  92. package/dist/policy/product-signal.js +6 -7
  93. package/dist/policy/project-invariants.d.ts +50 -0
  94. package/dist/policy/project-invariants.js +205 -0
  95. package/dist/policy/require-human-merge.js +8 -6
  96. package/dist/policy/resolve.d.ts +7 -2
  97. package/dist/policy/resolve.js +25 -8
  98. package/dist/policy/sync-default.d.ts +129 -0
  99. package/dist/policy/sync-default.js +452 -0
  100. package/dist/policy/sync-max-files.d.ts +93 -0
  101. package/dist/policy/sync-max-files.js +192 -0
  102. package/dist/policy/value-feedback.js +7 -8
  103. package/dist/pr-monitor/absent-required.d.ts +11 -0
  104. package/dist/pr-monitor/absent-required.js +61 -0
  105. package/dist/pr-monitor/constants.d.ts +7 -0
  106. package/dist/pr-monitor/constants.js +7 -0
  107. package/dist/pr-monitor/index.d.ts +2 -1
  108. package/dist/pr-monitor/index.js +2 -1
  109. package/dist/pr-monitor/main.js +4 -0
  110. package/dist/pr-monitor/monitor.js +24 -1
  111. package/dist/pr-wait-mergeable/classify.js +6 -0
  112. package/dist/preflight/evaluate.d.ts +4 -0
  113. package/dist/preflight/evaluate.js +35 -1
  114. package/dist/preflight/index.d.ts +4 -0
  115. package/dist/preflight/index.js +2 -0
  116. package/dist/preflight/intended-placement.d.ts +62 -0
  117. package/dist/preflight/intended-placement.js +275 -0
  118. package/dist/preflight/project-invariants-gate.d.ts +29 -0
  119. package/dist/preflight/project-invariants-gate.js +82 -0
  120. package/dist/preflight-cache/evaluate.d.ts +12 -0
  121. package/dist/preflight-cache/evaluate.js +15 -1
  122. package/dist/preflight-cache/index.d.ts +1 -1
  123. package/dist/preflight-cache/index.js +1 -1
  124. package/dist/product-first-done-gate/acceptance-resolver.d.ts +128 -0
  125. package/dist/product-first-done-gate/acceptance-resolver.js +144 -0
  126. package/dist/product-first-done-gate/acceptance.d.ts +7 -4
  127. package/dist/product-first-done-gate/acceptance.js +79 -8
  128. package/dist/product-first-done-gate/empty-resolution.d.ts +4 -1
  129. package/dist/product-first-done-gate/empty-resolution.js +12 -1
  130. package/dist/product-first-done-gate/evaluate.d.ts +16 -0
  131. package/dist/product-first-done-gate/evaluate.js +298 -34
  132. package/dist/product-first-done-gate/index.d.ts +2 -0
  133. package/dist/product-first-done-gate/index.js +1 -0
  134. package/dist/release/build-dist.d.ts +101 -0
  135. package/dist/release/build-dist.js +428 -46
  136. package/dist/release/closed-verb-gate.d.ts +12 -0
  137. package/dist/release/closed-verb-gate.js +51 -0
  138. package/dist/release/index.d.ts +1 -0
  139. package/dist/release/index.js +1 -0
  140. package/dist/release/pipeline.js +7 -0
  141. package/dist/release/types.d.ts +12 -0
  142. package/dist/render/constants.d.ts +2 -1
  143. package/dist/render/constants.js +2 -1
  144. package/dist/render/framework-commands.js +4 -0
  145. package/dist/render/spec-validate.js +23 -12
  146. package/dist/run-summary/emit.d.ts +16 -10
  147. package/dist/run-summary/emit.js +26 -15
  148. package/dist/run-summary/index.d.ts +3 -2
  149. package/dist/run-summary/index.js +2 -1
  150. package/dist/run-summary/session-id.d.ts +21 -0
  151. package/dist/run-summary/session-id.js +92 -0
  152. package/dist/run-summary/types.d.ts +33 -4
  153. package/dist/run-summary/types.js +1 -0
  154. package/dist/scope/acceptance-evidence.d.ts +19 -7
  155. package/dist/scope/acceptance-evidence.js +38 -11
  156. package/dist/scope/delivery-evidence.d.ts +3 -3
  157. package/dist/scope/delivery-evidence.js +22 -27
  158. package/dist/scope/index.d.ts +1 -0
  159. package/dist/scope/index.js +1 -0
  160. package/dist/scope/main.js +15 -5
  161. package/dist/scope/parent-lineage.js +7 -6
  162. package/dist/scope/project-invariant-coverage.d.ts +54 -0
  163. package/dist/scope/project-invariant-coverage.js +322 -0
  164. package/dist/scope/promote-from-issue.js +5 -5
  165. package/dist/scope/transition.js +1 -1
  166. package/dist/scope-provenance/compare-intent.d.ts +27 -0
  167. package/dist/scope-provenance/compare-intent.js +233 -0
  168. package/dist/scope-provenance/digest.d.ts +18 -1
  169. package/dist/scope-provenance/digest.js +16 -11
  170. package/dist/scope-provenance/evaluate.d.ts +5 -1
  171. package/dist/scope-provenance/evaluate.js +67 -7
  172. package/dist/scope-provenance/extract-intent.d.ts +43 -0
  173. package/dist/scope-provenance/extract-intent.js +262 -0
  174. package/dist/scope-provenance/index.d.ts +7 -1
  175. package/dist/scope-provenance/index.js +7 -1
  176. package/dist/scope-provenance/intent-digest.d.ts +12 -0
  177. package/dist/scope-provenance/intent-digest.js +33 -0
  178. package/dist/scope-provenance/intent-evaluate.d.ts +43 -0
  179. package/dist/scope-provenance/intent-evaluate.js +149 -0
  180. package/dist/scope-provenance/json-tokenizer.d.ts +19 -0
  181. package/dist/scope-provenance/json-tokenizer.js +225 -0
  182. package/dist/scope-provenance/known-machine.d.ts +38 -0
  183. package/dist/scope-provenance/known-machine.js +157 -0
  184. package/dist/scope-provenance/mint-artifacts.d.ts +102 -0
  185. package/dist/scope-provenance/mint-artifacts.js +425 -0
  186. package/dist/session/ac-pass-banking.d.ts +9 -36
  187. package/dist/session/ac-pass-banking.js +33 -79
  188. package/dist/session/ac-pass-reuse.d.ts +53 -0
  189. package/dist/session/ac-pass-reuse.js +95 -0
  190. package/dist/session/git.js +19 -4
  191. package/dist/session/index.d.ts +4 -0
  192. package/dist/session/index.js +4 -0
  193. package/dist/session/occupancy-stress-worker.d.ts +7 -0
  194. package/dist/session/occupancy-stress-worker.js +36 -0
  195. package/dist/session/occupancy.d.ts +97 -0
  196. package/dist/session/occupancy.js +422 -0
  197. package/dist/session/openclaw-soft-rebind-deposit.js +6 -6
  198. package/dist/session/process-cost.d.ts +32 -10
  199. package/dist/session/process-cost.js +189 -1
  200. package/dist/session/product-state-hash.d.ts +26 -0
  201. package/dist/session/product-state-hash.js +700 -0
  202. package/dist/session/ritual-entrypoint.js +16 -1
  203. package/dist/session/session-ready.d.ts +2 -0
  204. package/dist/session/session-ready.js +52 -30
  205. package/dist/session/session-start.d.ts +13 -0
  206. package/dist/session/session-start.js +103 -9
  207. package/dist/session/verify-ac-session-cache.d.ts +42 -0
  208. package/dist/session/verify-ac-session-cache.js +126 -0
  209. package/dist/session/verify-session-ritual.d.ts +28 -0
  210. package/dist/session/verify-session-ritual.js +97 -5
  211. package/dist/slash/openclaw-deposit.js +13 -9
  212. package/dist/slice/constants.d.ts +2 -2
  213. package/dist/slice/constants.js +2 -2
  214. package/dist/slice/existing.js +9 -3
  215. package/dist/slice/lock.d.ts +16 -1
  216. package/dist/slice/lock.js +137 -11
  217. package/dist/story-ready/evaluate.d.ts +2 -0
  218. package/dist/story-ready/evaluate.js +13 -0
  219. package/dist/swarm/complete-cohort.d.ts +3 -0
  220. package/dist/swarm/complete-cohort.js +59 -11
  221. package/dist/swarm/finalize-cohort-cli.d.ts +7 -2
  222. package/dist/swarm/finalize-cohort-cli.js +165 -35
  223. package/dist/swarm/finalize-cohort.d.ts +1 -0
  224. package/dist/swarm/finalize-cohort.js +47 -4
  225. package/dist/swarm/launch.d.ts +22 -0
  226. package/dist/swarm/launch.js +155 -0
  227. package/dist/swarm/routing-set-cli.d.ts +11 -1
  228. package/dist/swarm/routing-set-cli.js +15 -3
  229. package/dist/swarm/routing-verify.d.ts +13 -1
  230. package/dist/swarm/routing-verify.js +60 -21
  231. package/dist/swarm/routing.d.ts +14 -0
  232. package/dist/swarm/routing.js +53 -3
  233. package/dist/telemetry-coverage/fake-trial.js +10 -0
  234. package/dist/telemetry-coverage/kinds.js +2 -0
  235. package/dist/triage/actions/index.js +4 -1
  236. package/dist/triage/bootstrap/index.js +6 -6
  237. package/dist/triage/help/registry-data.d.ts +1 -1
  238. package/dist/triage/help/registry-data.js +1 -1
  239. package/dist/triage/welcome/writers.d.ts +1 -1
  240. package/dist/triage/welcome/writers.js +10 -5
  241. package/dist/ts-check-lane/index.d.ts +3 -0
  242. package/dist/ts-check-lane/index.js +2 -0
  243. package/dist/ts-check-lane/progress-reporter.d.ts +17 -0
  244. package/dist/ts-check-lane/progress-reporter.js +38 -0
  245. package/dist/ts-check-lane/progress.d.ts +32 -0
  246. package/dist/ts-check-lane/progress.js +57 -0
  247. package/dist/ts-check-lane/run-lane.d.ts +3 -1
  248. package/dist/ts-check-lane/run-lane.js +8 -6
  249. package/dist/validate-content/validate-links.d.ts +8 -0
  250. package/dist/validate-content/validate-links.js +9 -15
  251. package/dist/value/feedback-file.js +12 -2
  252. package/dist/value/readback.d.ts +4 -1
  253. package/dist/value/readback.js +51 -12
  254. package/dist/vbrief-build/index.d.ts +1 -0
  255. package/dist/vbrief-build/index.js +1 -0
  256. package/dist/vbrief-build/project-invariants-io.d.ts +16 -0
  257. package/dist/vbrief-build/project-invariants-io.js +41 -0
  258. package/dist/vbrief-reconcile/graph.js +18 -1
  259. package/dist/vbrief-reconcile/index.d.ts +1 -1
  260. package/dist/vbrief-reconcile/index.js +1 -1
  261. package/dist/vbrief-reconcile/types.d.ts +13 -0
  262. package/dist/vbrief-reconcile/umbrellas.d.ts +14 -0
  263. package/dist/vbrief-reconcile/umbrellas.js +261 -26
  264. package/dist/vbrief-validate/index.d.ts +1 -1
  265. package/dist/vbrief-validate/index.js +1 -1
  266. package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
  267. package/dist/vbrief-validate/plan-hooks.js +68 -0
  268. package/dist/vbrief-validate/schema.d.ts +4 -0
  269. package/dist/vbrief-validate/schema.js +1 -1
  270. package/dist/verify-ac/evaluate.d.ts +7 -1
  271. package/dist/verify-ac/evaluate.js +34 -26
  272. package/dist/verify-ac/flag.d.ts +4 -0
  273. package/dist/verify-ac/flag.js +16 -0
  274. package/dist/verify-ac/index.d.ts +2 -2
  275. package/dist/verify-ac/index.js +2 -2
  276. package/dist/verify-env/agent-hook-readiness.d.ts +2 -2
  277. package/dist/verify-env/agent-hook-readiness.js +12 -8
  278. package/dist/verify-env/agent-hooks-live-probe.d.ts +5 -1
  279. package/dist/verify-env/agent-hooks-live-probe.js +26 -5
  280. package/dist/verify-env/agent-hooks.js +3 -4
  281. package/dist/verify-source/contained-writes.js +2 -0
  282. package/dist/verify-source/verify-stubs.d.ts +15 -1
  283. package/dist/verify-source/verify-stubs.js +20 -16
  284. package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +5 -6
  285. package/dist/vitest-runner/win32-coverage-tmp-setup.js +5 -6
  286. package/dist/xbrief-migrate/migrate-project.js +2 -5
  287. package/package.json +7 -3
@@ -5,6 +5,7 @@ import { inferGithubAuthMode } from "../intake/github-auth-modes.js";
5
5
  import { getPlatformCapabilities } from "../intake/platform-capabilities.js";
6
6
  import { hasArtifactSuffix, resolveLifecycleFolder, stripArtifactSuffix, } from "../layout/resolve.js";
7
7
  import { evaluate as preflightEvaluate } from "../preflight/evaluate.js";
8
+ import { applyWorktreeOccupancy } from "../session/occupancy.js";
8
9
  import { issueNumbersFromPlan, scopeMetadataRank } from "../triage/queue/scope-walk.js";
9
10
  import { selectionOrderingKey } from "../triage/queue/selection.js";
10
11
  import { DEFAULT_BASE_BRANCH, EXIT_CONFIG_ERROR, EXIT_GATE_FAILED, EXIT_OK, GATE_ADVISE, GATE_ENFORCE, LEAF_CODING_WORKER_ROLE, } from "./constants.js";
@@ -334,6 +335,121 @@ function deriveBranch(group, sid) {
334
335
  function defaultWorktree(projectRoot, sid) {
335
336
  return join(projectRoot, ".deft-scratch", "worktrees", safeSegment(sid)).replace(/\\/g, "/");
336
337
  }
338
+ export const SWARM_LAUNCH_MANIFEST_RELPATH = [".deft", "swarm-launch-manifest.json"];
339
+ export const SWARM_LAUNCH_OCCUPANCY_DIR = [".deft", "swarm-launch-occupancy"];
340
+ export function swarmLaunchManifestPath(projectRoot) {
341
+ return join(resolve(projectRoot), ...SWARM_LAUNCH_MANIFEST_RELPATH);
342
+ }
343
+ export function occupancyCohortKey(allocationPlanId, storyIds = []) {
344
+ const plan = allocationPlanId?.trim() ?? "";
345
+ if (plan.length > 0)
346
+ return `plan:${plan}`;
347
+ const stories = [...storyIds].map((id) => id.trim()).filter((id) => id.length > 0);
348
+ stories.sort();
349
+ return `stories:${stories.join(",")}`;
350
+ }
351
+ export function launchOccupancyRecordRelpath(cohortKey) {
352
+ return [...SWARM_LAUNCH_OCCUPANCY_DIR, `${safeSegment(cohortKey)}.json`];
353
+ }
354
+ function parseLaunchOccupancyRecord(payload) {
355
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload))
356
+ return null;
357
+ const obj = payload;
358
+ const sessionId = typeof obj.occupancy_session_id === "string" ? obj.occupancy_session_id.trim() : "";
359
+ if (sessionId.length === 0)
360
+ return null;
361
+ const cohortKey = typeof obj.cohort_key === "string" ? obj.cohort_key.trim() : "";
362
+ if (cohortKey.length === 0)
363
+ return null;
364
+ const planRaw = obj.allocation_plan_id;
365
+ const allocationPlanId = typeof planRaw === "string" && planRaw.trim().length > 0 ? planRaw.trim() : null;
366
+ const storyIds = Array.isArray(obj.story_ids)
367
+ ? obj.story_ids.filter((id) => typeof id === "string" && id.trim().length > 0)
368
+ : [];
369
+ return {
370
+ allocation_plan_id: allocationPlanId,
371
+ occupancy_session_id: sessionId,
372
+ story_ids: storyIds,
373
+ cohort_key: cohortKey,
374
+ };
375
+ }
376
+ function storySetEquals(left, right) {
377
+ if (left.length !== right.size)
378
+ return false;
379
+ return left.every((id) => right.has(id));
380
+ }
381
+ function readLaunchOccupancyFile(projectRoot, cohortKey) {
382
+ const path = join(resolve(projectRoot), ...launchOccupancyRecordRelpath(cohortKey));
383
+ try {
384
+ if (!existsSync(path))
385
+ return null;
386
+ return parseLaunchOccupancyRecord(JSON.parse(readFileSync(path, { encoding: "utf8" })));
387
+ }
388
+ catch {
389
+ return null;
390
+ }
391
+ }
392
+ function listLaunchOccupancyRecords(projectRoot) {
393
+ const dir = join(resolve(projectRoot), ...SWARM_LAUNCH_OCCUPANCY_DIR);
394
+ if (!existsSync(dir))
395
+ return [];
396
+ const out = [];
397
+ for (const name of readdirSync(dir)) {
398
+ if (!name.endsWith(".json"))
399
+ continue;
400
+ try {
401
+ const parsed = parseLaunchOccupancyRecord(JSON.parse(readFileSync(join(dir, name), { encoding: "utf8" })));
402
+ if (parsed !== null)
403
+ out.push(parsed);
404
+ }
405
+ catch {
406
+ /* skip unreadable cohort slot */
407
+ }
408
+ }
409
+ return out;
410
+ }
411
+ export function persistLaunchOccupancyRecord(projectRoot, record) {
412
+ const relpath = launchOccupancyRecordRelpath(record.cohort_key);
413
+ const absDir = join(resolve(projectRoot), ...SWARM_LAUNCH_OCCUPANCY_DIR);
414
+ mkdirSync(absDir, { recursive: true });
415
+ containedWrite({
416
+ root: projectRoot,
417
+ target: join(...relpath),
418
+ data: `${JSON.stringify(record, null, 2)}\n`,
419
+ mode: "replace",
420
+ });
421
+ }
422
+ export function resolveLaunchOccupancySessionId(projectRoot, query = {}) {
423
+ const storyIds = (query.storyIds ?? []).map((id) => id.trim()).filter((id) => id.length > 0);
424
+ const requestedKey = occupancyCohortKey(query.allocationPlanId, storyIds);
425
+ const exact = readLaunchOccupancyFile(projectRoot, requestedKey);
426
+ if (exact !== null) {
427
+ const wantedPlan = query.allocationPlanId?.trim() ?? "";
428
+ if (exact.cohort_key !== requestedKey) {
429
+ return { sessionId: "", reason: "wrong-cohort" };
430
+ }
431
+ if (wantedPlan.length > 0 && exact.allocation_plan_id !== wantedPlan) {
432
+ return { sessionId: "", reason: "wrong-cohort" };
433
+ }
434
+ return { sessionId: exact.occupancy_session_id, reason: "ok" };
435
+ }
436
+ if ((query.allocationPlanId?.trim() ?? "").length > 0) {
437
+ return { sessionId: "", reason: "missing" };
438
+ }
439
+ if (storyIds.length === 0) {
440
+ return { sessionId: "", reason: "missing" };
441
+ }
442
+ const wanted = new Set(storyIds);
443
+ const matches = listLaunchOccupancyRecords(projectRoot).filter((rec) => storySetEquals(rec.story_ids, wanted));
444
+ if (matches.length === 0) {
445
+ return { sessionId: "", reason: "missing" };
446
+ }
447
+ const sessionIds = new Set(matches.map((rec) => rec.occupancy_session_id));
448
+ if (sessionIds.size !== 1) {
449
+ return { sessionId: "", reason: "wrong-cohort" };
450
+ }
451
+ return { sessionId: matches[0]?.occupancy_session_id ?? "", reason: "ok" };
452
+ }
337
453
  export function buildManifest(resolved, options) {
338
454
  const cohortVbriefs = resolved.map((s) => s.relpath);
339
455
  const records = options.worktreeRecords ?? new Map();
@@ -360,6 +476,9 @@ export function buildManifest(resolved, options) {
360
476
  branch: deriveBranch(options.group ?? null, story.story_id),
361
477
  allocation_context: allocationContext,
362
478
  };
479
+ if (options.occupancySessionId !== undefined && options.occupancySessionId !== null) {
480
+ entry.occupancy_session_id = options.occupancySessionId;
481
+ }
363
482
  if (options.subagentBackend !== undefined && options.subagentBackend !== null) {
364
483
  entry.subagent_backend = options.subagentBackend;
365
484
  }
@@ -562,6 +681,17 @@ export function swarmLaunch(args) {
562
681
  ? dispatchProviderFor(backend.backend_id)
563
682
  : null;
564
683
  const workerRoleValue = routingFile !== null || backend !== null ? LEAF_CODING_WORKER_ROLE : null;
684
+ const occupancy = applyWorktreeOccupancy(projectRoot, {
685
+ env: args.environ ?? process.env,
686
+ intent: "swarm",
687
+ });
688
+ if (occupancy.code !== 0) {
689
+ return {
690
+ exitCode: EXIT_GATE_FAILED,
691
+ stdout: "",
692
+ stderr: `${occupancy.message}\n`,
693
+ };
694
+ }
565
695
  const manifest = buildManifest(ordered, {
566
696
  projectRoot,
567
697
  group: args.group ?? null,
@@ -578,8 +708,33 @@ export function swarmLaunch(args) {
578
708
  modelSource,
579
709
  runtimeMode,
580
710
  githubAuthMode,
711
+ occupancySessionId: occupancy.sessionId,
581
712
  });
582
713
  const rendered = `${JSON.stringify(manifest, null, 2)}\n`;
714
+ const storyIds = ordered.map((story) => story.story_id);
715
+ const cohortKey = occupancyCohortKey(allocationPlanId, storyIds);
716
+ try {
717
+ mkdirSync(dirname(swarmLaunchManifestPath(projectRoot)), { recursive: true });
718
+ containedWrite({
719
+ root: projectRoot,
720
+ target: join(...SWARM_LAUNCH_MANIFEST_RELPATH),
721
+ data: rendered,
722
+ mode: "replace",
723
+ });
724
+ persistLaunchOccupancyRecord(projectRoot, {
725
+ allocation_plan_id: allocationPlanId,
726
+ occupancy_session_id: occupancy.sessionId,
727
+ story_ids: storyIds,
728
+ cohort_key: cohortKey,
729
+ });
730
+ }
731
+ catch (exc) {
732
+ return {
733
+ exitCode: EXIT_CONFIG_ERROR,
734
+ stdout: "",
735
+ stderr: `Error: could not persist launch occupancy_session_id: ${String(exc)}\n`,
736
+ };
737
+ }
583
738
  if (args.output !== undefined && args.output !== null) {
584
739
  try {
585
740
  // #2980 wave D: product write sink routes through containedWrite.
@@ -1,3 +1,13 @@
1
1
  #!/usr/bin/env node
2
- export declare function routingSetMain(argv?: string[]): number;
2
+ /**
3
+ * `routing set` CLI entry.
4
+ *
5
+ * `environ` is the same injected-environment seam `resolveDispatchProvider` and
6
+ * `resolveRoutingPath` already carry (default `process.env`, so production
7
+ * behaviour is unchanged). Tests drive this entry with a constructed
8
+ * environment instead of subtracting known host vars from the ambient one --
9
+ * a denylist that silently rots every time a new host signal is added (#3494,
10
+ * after #3134 / #3492).
11
+ */
12
+ export declare function routingSetMain(argv?: string[], environ?: NodeJS.ProcessEnv): number;
3
13
  //# sourceMappingURL=routing-set-cli.d.ts.map
@@ -4,7 +4,17 @@ import { fileURLToPath } from "node:url";
4
4
  import { PROJECTION_CONTAINMENT_REFUSED_EXIT_CODE, ProjectionContainmentError, } from "../fs/projection-containment.js";
5
5
  import { EXIT_CONFIG_ERROR, EXIT_OK } from "./constants.js";
6
6
  import { HARNESS_BOUND_PROVIDERS, ROUTING_MODE_HARNESS_DEFAULT, ROUTING_MODE_PINNED, resolveDispatchProvider, resolveRoutingPath, SWARM_WORKER_ROLES, writeModelDecision, } from "./routing.js";
7
- export function routingSetMain(argv = process.argv.slice(2)) {
7
+ /**
8
+ * `routing set` CLI entry.
9
+ *
10
+ * `environ` is the same injected-environment seam `resolveDispatchProvider` and
11
+ * `resolveRoutingPath` already carry (default `process.env`, so production
12
+ * behaviour is unchanged). Tests drive this entry with a constructed
13
+ * environment instead of subtracting known host vars from the ambient one --
14
+ * a denylist that silently rots every time a new host signal is added (#3494,
15
+ * after #3134 / #3492).
16
+ */
17
+ export function routingSetMain(argv = process.argv.slice(2), environ = process.env) {
8
18
  let projectRoot = ".";
9
19
  let provider = null;
10
20
  let role = null;
@@ -12,6 +22,8 @@ export function routingSetMain(argv = process.argv.slice(2)) {
12
22
  let harnessDefault = false;
13
23
  for (let i = 0; i < argv.length; i += 1) {
14
24
  const arg = argv[i];
25
+ if (arg === "--")
26
+ continue;
15
27
  if (arg === "--project-root" && argv[i + 1] !== undefined) {
16
28
  projectRoot = argv[i + 1] ?? ".";
17
29
  i += 1;
@@ -49,7 +61,7 @@ export function routingSetMain(argv = process.argv.slice(2)) {
49
61
  // Same key as launch + verify:routing: OPENCLAW / sessions_spawn → openclaw
50
62
  // (#2875 Greptile P1). Do not map via runtimeMode alone — OpenClaw-only envs
51
63
  // are often local-unsandboxed while still dispatching under provider openclaw.
52
- resolvedProvider = resolveDispatchProvider(process.env);
64
+ resolvedProvider = resolveDispatchProvider(environ);
53
65
  }
54
66
  if (harnessDefault) {
55
67
  if (model !== null) {
@@ -63,7 +75,7 @@ export function routingSetMain(argv = process.argv.slice(2)) {
63
75
  return EXIT_CONFIG_ERROR;
64
76
  }
65
77
  const root = resolve(projectRoot);
66
- const path = resolveRoutingPath(root);
78
+ const path = resolveRoutingPath(root, environ);
67
79
  try {
68
80
  writeModelDecision(root, path, resolvedProvider, role, {
69
81
  model,
@@ -1,4 +1,5 @@
1
- export declare const ROUTING_SET_CMD = "task swarm:routing-set -- --role <role> --model <slug> (or --harness-default)";
1
+ import { type RoutingFile } from "./routing.js";
2
+ export declare const ROUTING_SET_CMD = "deft swarm:routing-set --role <role> --model <slug> (or --harness-default)";
2
3
  /** Roles the pre-dispatch gate checks by default: the actual model lever. */
3
4
  export declare const DEFAULT_GATED_ROLES: readonly ["leaf-implementation"];
4
5
  export interface VerifyRoutingOptions {
@@ -16,5 +17,16 @@ export interface VerifyRoutingResult {
16
17
  exitCode: number;
17
18
  report: string;
18
19
  }
20
+ /**
21
+ * Dedicated host-unrecognized honesty line (#3469). Disclosure, not a gate:
22
+ * names the empty probes, that dispatch is still allowed, and the pin or
23
+ * inherit that will be used. Must not be folded into "all roles decided."
24
+ */
25
+ export declare function formatHostUnrecognizedHonestyLine(options: {
26
+ environ?: NodeJS.ProcessEnv;
27
+ provider: string;
28
+ roles: readonly string[];
29
+ file: RoutingFile | null;
30
+ }): string;
19
31
  export declare function verifyRouting(options: VerifyRoutingOptions): VerifyRoutingResult;
20
32
  //# sourceMappingURL=routing-verify.d.ts.map
@@ -5,12 +5,14 @@
5
5
  * - enforce (pre-dispatch): a hard gate in the pre-`start_agent` /
6
6
  * `swarm:launch` gate stack. Three-state exit:
7
7
  * 0 = every in-scope role is decided (pinned or explicit harness-default)
8
+ * OR provider is unrecognized (`unknown`) -- disclosure, not a gate (#3469)
8
9
  * 1 = at least one in-scope role is undecided / not dispatchable
9
10
  * 2 = config error (unreadable / malformed route file)
11
+ * Unrecognized host never exits 1/2 solely because the host was unknown.
10
12
  */
11
13
  import { EXIT_CONFIG_ERROR, EXIT_GATE_FAILED, EXIT_OK } from "./constants.js";
12
- import { dispatchProviderFromRuntime, HARNESS_BOUND_PROVIDERS, loadRoutingFile, ROUTING_MODE_HARNESS_DEFAULT, resolveDispatchProvider, resolveModelRoute, resolveRoutingPath, } from "./routing.js";
13
- export const ROUTING_SET_CMD = "task swarm:routing-set -- --role <role> --model <slug> (or --harness-default)";
14
+ import { dispatchProviderFromRuntime, emptyHostDetectProbes, HARNESS_BOUND_PROVIDERS, loadRoutingFile, ROUTING_MODE_HARNESS_DEFAULT, resolveDispatchProvider, resolveModelRoute, resolveRoutingPath, } from "./routing.js";
15
+ export const ROUTING_SET_CMD = "deft swarm:routing-set --role <role> --model <slug> (or --harness-default)";
14
16
  /** Roles the pre-dispatch gate checks by default: the actual model lever. */
15
17
  export const DEFAULT_GATED_ROLES = ["leaf-implementation"];
16
18
  function resolveProvider(options) {
@@ -29,6 +31,29 @@ function resolveProvider(options) {
29
31
  }
30
32
  return resolveDispatchProvider(options.environ ?? process.env);
31
33
  }
34
+ function isUnrecognizedHost(provider) {
35
+ return provider === "unknown";
36
+ }
37
+ function formatUsedPin(file, provider, role) {
38
+ const resolution = resolveModelRoute(file, provider, role);
39
+ if (resolution.decided && resolution.source !== "invalid") {
40
+ const value = resolution.model ?? "harness-default";
41
+ return `${provider}.${role}=${value}`;
42
+ }
43
+ return `inherit (no ${provider}.${role} pin)`;
44
+ }
45
+ /**
46
+ * Dedicated host-unrecognized honesty line (#3469). Disclosure, not a gate:
47
+ * names the empty probes, that dispatch is still allowed, and the pin or
48
+ * inherit that will be used. Must not be folded into "all roles decided."
49
+ */
50
+ export function formatHostUnrecognizedHonestyLine(options) {
51
+ const empty = emptyHostDetectProbes(options.environ ?? process.env);
52
+ const emptyText = empty.length > 0 ? empty.join(", ") : "(none)";
53
+ const used = options.roles.map((role) => formatUsedPin(options.file, options.provider, role));
54
+ return (`[deft routing] host unrecognized (#3469): empty probes: ${emptyText}. ` +
55
+ `Dispatch still allowed. Using ${used.join("; ")}.`);
56
+ }
32
57
  export function verifyRouting(options) {
33
58
  const roles = options.roles && options.roles.length > 0 ? options.roles : DEFAULT_GATED_ROLES;
34
59
  const provider = resolveProvider(options);
@@ -64,6 +89,15 @@ export function verifyRouting(options) {
64
89
  const modelText = resolution.model ?? "<runtime default>";
65
90
  resolvedLines.push(` ${role}: model ${modelText} (resolved-via ${resolution.source})`);
66
91
  }
92
+ const unrecognized = isUnrecognizedHost(provider);
93
+ const honesty = unrecognized
94
+ ? formatHostUnrecognizedHonestyLine({
95
+ environ: options.environ ?? process.env,
96
+ provider,
97
+ roles,
98
+ file: data,
99
+ })
100
+ : "";
67
101
  if (invalid.length > 0 && !options.advise) {
68
102
  return {
69
103
  exitCode: EXIT_CONFIG_ERROR,
@@ -71,25 +105,29 @@ export function verifyRouting(options) {
71
105
  };
72
106
  }
73
107
  if (options.advise) {
108
+ const note = "[deft routing] NOTE: plan.policy.swarmSubagentBackend enum is deprecated (#1891); use 'deft swarm:routing-set' / .deft/routing.local.json instead.";
109
+ let body;
74
110
  if (undecided.length === 0 && invalid.length === 0) {
75
- return {
76
- exitCode: EXIT_OK,
77
- report: `[deft routing] provider '${provider}': all ${roles.length} gated role(s) decided.\n[deft routing] NOTE: plan.policy.swarmSubagentBackend enum is deprecated (#1891); use 'task swarm:routing-set' / .deft/routing.local.json instead.`,
78
- };
111
+ body = `[deft routing] provider '${provider}': all ${roles.length} gated role(s) decided.`;
79
112
  }
80
- const parts = [];
81
- if (undecided.length > 0) {
82
- parts.push(`undecided role(s): ${undecided.join(", ")}`);
113
+ else {
114
+ const parts = [];
115
+ if (undecided.length > 0) {
116
+ parts.push(`undecided role(s): ${undecided.join(", ")}`);
117
+ }
118
+ if (invalid.length > 0) {
119
+ parts.push(`invalid: ${invalid.length}`);
120
+ }
121
+ // Unrecognized host is disclosure: do not force routing-set (#3469).
122
+ const decide = unrecognized
123
+ ? "Dispatch still allowed."
124
+ : `Decide before swarm dispatch: ${ROUTING_SET_CMD}`;
125
+ body = `[deft routing] provider '${provider}' -- ${parts.join("; ")}. ${decide}`;
83
126
  }
84
- if (invalid.length > 0) {
85
- parts.push(`invalid: ${invalid.length}`);
86
- }
87
- return {
88
- exitCode: EXIT_OK,
89
- report: `[deft routing] provider '${provider}' -- ${parts.join("; ")}. Decide before swarm dispatch: ${ROUTING_SET_CMD}\n[deft routing] NOTE: plan.policy.swarmSubagentBackend enum is deprecated (#1891); use 'task swarm:routing-set' / .deft/routing.local.json instead.`,
90
- };
127
+ const report = honesty.length > 0 ? `${honesty}\n${body}\n${note}` : `${body}\n${note}`;
128
+ return { exitCode: EXIT_OK, report };
91
129
  }
92
- if (undecided.length > 0) {
130
+ if (undecided.length > 0 && !unrecognized) {
93
131
  return {
94
132
  exitCode: EXIT_GATE_FAILED,
95
133
  report: `routing gate: provider '${provider}' has undecided role(s): ${undecided.join(", ")}.\n` +
@@ -97,9 +135,10 @@ export function verifyRouting(options) {
97
135
  `Decide: ${ROUTING_SET_CMD}`,
98
136
  };
99
137
  }
100
- return {
101
- exitCode: EXIT_OK,
102
- report: `routing gate: provider '${provider}' -- all gated role(s) decided.\n${resolvedLines.join("\n")}`,
103
- };
138
+ const decidedBody = undecided.length > 0
139
+ ? `routing gate: provider '${provider}' -- dispatch allowed (host unrecognized is disclosure, not a gate).`
140
+ : `routing gate: provider '${provider}' -- all gated role(s) decided.\n${resolvedLines.join("\n")}`;
141
+ const report = honesty.length > 0 ? `${honesty}\n${decidedBody}` : decidedBody;
142
+ return { exitCode: EXIT_OK, report };
104
143
  }
105
144
  //# sourceMappingURL=routing-verify.js.map
@@ -9,6 +9,19 @@ export declare const ROUTING_MODE_HARNESS_DEFAULT = "harness-default";
9
9
  export declare const ROUTING_FILENAME = "routing.local.json";
10
10
  /** Providers whose model is harness-bound -- deft cannot pin or verify a slug. */
11
11
  export declare const HARNESS_BOUND_PROVIDERS: Set<string>;
12
+ /**
13
+ * Live routing key for Grok Build. A `grok-build` file key is a trap (#3469):
14
+ * read it as `grok` when the live provider is grok, or ignore it without
15
+ * blocking launch.
16
+ */
17
+ export declare const GROK_ROUTING_KEY = "grok";
18
+ export declare const GROK_BUILD_FILE_KEY = "grok-build";
19
+ /**
20
+ * Env / probe names that identify Grok Build. Same set `probeMonitoringTier`
21
+ * already uses, plus `GROK_BUILD` / `DEFT_AGENT_RUNTIME=grok-build`.
22
+ * Named on the host-unrecognized honesty line when empty (#3469).
23
+ */
24
+ export declare const HOST_DETECT_PROBE_NAMES: readonly ["GROK_BUILD", "DEFT_AGENT_RUNTIME", "DEFT_HAS_SPAWN_SUBAGENT", "DEFT_PROBE_GROK_BUILD", "DEFT_PROBE_SPAWN_SUBAGENT"];
12
25
  /** Providers whose per-role model must be decided before sub-agent dispatch (#1739 / #1877 / #2875 / #3134). */
13
26
  export declare const ROUTING_GATED_DISPATCH_PROVIDERS: Set<string>;
14
27
  export interface RouteDecision {
@@ -41,6 +54,7 @@ export declare function loadRoutingFile(path: string): {
41
54
  data: RoutingFile | null;
42
55
  error: string | null;
43
56
  };
57
+ export declare function emptyHostDetectProbes(environ?: NodeJS.ProcessEnv): string[];
44
58
  /**
45
59
  * Resolve a (provider, role) route. Tri-state by KEY PRESENCE:
46
60
  * - key present, model "<slug>" -> pinned
@@ -33,6 +33,25 @@ export const ROUTING_MODE_HARNESS_DEFAULT = "harness-default";
33
33
  export const ROUTING_FILENAME = "routing.local.json";
34
34
  /** Providers whose model is harness-bound -- deft cannot pin or verify a slug. */
35
35
  export const HARNESS_BOUND_PROVIDERS = new Set(["grok"]);
36
+ /**
37
+ * Live routing key for Grok Build. A `grok-build` file key is a trap (#3469):
38
+ * read it as `grok` when the live provider is grok, or ignore it without
39
+ * blocking launch.
40
+ */
41
+ export const GROK_ROUTING_KEY = "grok";
42
+ export const GROK_BUILD_FILE_KEY = "grok-build";
43
+ /**
44
+ * Env / probe names that identify Grok Build. Same set `probeMonitoringTier`
45
+ * already uses, plus `GROK_BUILD` / `DEFT_AGENT_RUNTIME=grok-build`.
46
+ * Named on the host-unrecognized honesty line when empty (#3469).
47
+ */
48
+ export const HOST_DETECT_PROBE_NAMES = [
49
+ "GROK_BUILD",
50
+ "DEFT_AGENT_RUNTIME",
51
+ "DEFT_HAS_SPAWN_SUBAGENT",
52
+ "DEFT_PROBE_GROK_BUILD",
53
+ "DEFT_PROBE_SPAWN_SUBAGENT",
54
+ ];
36
55
  /** Providers whose per-role model must be decided before sub-agent dispatch (#1739 / #1877 / #2875 / #3134). */
37
56
  export const ROUTING_GATED_DISPATCH_PROVIDERS = new Set([
38
57
  "cursor",
@@ -100,6 +119,12 @@ function providerBlockOf(file, provider) {
100
119
  }
101
120
  return block;
102
121
  }
122
+ export function emptyHostDetectProbes(environ = process.env) {
123
+ return HOST_DETECT_PROBE_NAMES.filter((name) => (environ[name] ?? "").trim().length === 0);
124
+ }
125
+ function decisionLooksPinned(decision) {
126
+ return typeof decision.model === "string" && decision.model.trim().length > 0;
127
+ }
103
128
  /**
104
129
  * Resolve a (provider, role) route. Tri-state by KEY PRESENCE:
105
130
  * - key present, model "<slug>" -> pinned
@@ -107,7 +132,28 @@ function providerBlockOf(file, provider) {
107
132
  * - key absent -> undecided (fail loud upstream)
108
133
  */
109
134
  export function resolveModelRoute(file, provider, role) {
110
- const block = providerBlockOf(file, provider);
135
+ let block = providerBlockOf(file, provider);
136
+ if ((block === null || !(role in block)) && provider === GROK_ROUTING_KEY) {
137
+ const alias = providerBlockOf(file, GROK_BUILD_FILE_KEY);
138
+ if (alias !== null && role in alias) {
139
+ const aliasDecision = alias[role];
140
+ // Dead `grok-build` pins must not fail-close a harness-bound grok
141
+ // provider (#3469). Read the pin as grok harness-default.
142
+ if (typeof aliasDecision === "object" &&
143
+ aliasDecision !== null &&
144
+ !Array.isArray(aliasDecision) &&
145
+ decisionLooksPinned(aliasDecision)) {
146
+ return {
147
+ decided: true,
148
+ model: null,
149
+ mode: ROUTING_MODE_HARNESS_DEFAULT,
150
+ source: "harness-default explicit",
151
+ error: null,
152
+ };
153
+ }
154
+ block = alias;
155
+ }
156
+ }
111
157
  if (block === null || !(role in block)) {
112
158
  return { decided: false, model: null, mode: null, source: "undecided", error: null };
113
159
  }
@@ -198,8 +244,12 @@ export function resolveDispatchProvider(environ = process.env) {
198
244
  runtime === "openclaw") {
199
245
  return "openclaw";
200
246
  }
201
- if (envTruthy(environ, "GROK_BUILD") || runtime === "grok-build") {
202
- return "grok";
247
+ if (envTruthy(environ, "GROK_BUILD") ||
248
+ envTruthy(environ, "DEFT_HAS_SPAWN_SUBAGENT") ||
249
+ envTruthy(environ, "DEFT_PROBE_GROK_BUILD") ||
250
+ envTruthy(environ, "DEFT_PROBE_SPAWN_SUBAGENT") ||
251
+ runtime === "grok-build") {
252
+ return GROK_ROUTING_KEY;
203
253
  }
204
254
  if (runtime === "cloud" || runtime === "headless") {
205
255
  return "cloud-headless";
@@ -85,6 +85,16 @@ export const DEFAULT_TRIAL_STEPS = [
85
85
  emitter.emitKnownToolTurnDenominator();
86
86
  },
87
87
  },
88
+ {
89
+ kind: "ac_pass_bank",
90
+ invoke: (emitter) => {
91
+ emitter.emitAcPassBank({
92
+ scope_id: "fake-trial",
93
+ banked_at: "2026-08-16T00:00:00Z",
94
+ next_action: "finalize_and_ship",
95
+ });
96
+ },
97
+ },
88
98
  ];
89
99
  function createConsumerShapedRoot() {
90
100
  const root = mkdtempSync(join(tmpdir(), "deft-telemetry-trial-"));
@@ -13,6 +13,7 @@ export const EVENT_KIND_TO_EMITTER_METHODS = {
13
13
  verification: ["emitVerification"],
14
14
  acceptance: ["emitAcceptance"],
15
15
  acceptance_stamp: ["emitAcceptanceStamp"],
16
+ ac_pass_bank: ["emitAcPassBank"],
16
17
  };
17
18
  /**
18
19
  * Kinds asserted by the shared fake-trial harness.
@@ -29,6 +30,7 @@ export const ENROLLED_FIELD_FIXTURE_KINDS = [
29
30
  "verification",
30
31
  "acceptance",
31
32
  "acceptance_stamp",
33
+ "ac_pass_bank",
32
34
  ];
33
35
  const GENERIC_EMIT_METHODS = new Set(["emit"]);
34
36
  /** True when a discovered class method is a distinct event surface (not the generic emit). */
@@ -64,7 +64,10 @@ function defaultIssueIngest() {
64
64
  // Delegate to the native TS intake path (#2350). The legacy Python
65
65
  // `scripts/issue_ingest.py` shell-out was orphaned when #1933 removed the
66
66
  // Python surface, leaving `triage:accept` raising ModuleNotFoundError.
67
- const [, path] = ingestSingleForAcceptTs(issueNumber, repo, { projectRoot });
67
+ const [, path, msg] = ingestSingleForAcceptTs(issueNumber, repo, { projectRoot });
68
+ if (typeof msg === "string" && msg.includes("\n")) {
69
+ process.stderr.write(`${msg}\n`);
70
+ }
68
71
  return path;
69
72
  },
70
73
  };
@@ -370,12 +370,12 @@ export function formatSummary(result) {
370
370
  if (result.exitCode === 0) {
371
371
  lines.push("");
372
372
  lines.push("Next steps:");
373
- lines.push(" task cache:fetch-all -- --source=github-issue --repo OWNER/NAME # refresh the cache (#883 Story 2)");
374
- lines.push(" task cache:get -- github-issue OWNER/NAME/<N> # inspect cached issue N");
375
- lines.push(" task triage:accept -- --issue <N> --repo OWNER/NAME # accept issue N (#845 Story 3)");
376
- lines.push(" task triage:reject -- --issue <N> --repo OWNER/NAME --reason 'why' # reject issue N");
377
- lines.push(" task triage:bulk-accept -- --repo OWNER/NAME --label adoption-blocker # bulk accept");
378
- lines.push(" task triage:refresh-active # pre-swarm freshness gate");
373
+ lines.push(" deft cache:fetch-all --source=github-issue --repo OWNER/NAME # refresh the cache (#883 Story 2)");
374
+ lines.push(" deft cache:get github-issue OWNER/NAME/<N> # inspect cached issue N");
375
+ lines.push(" deft triage:accept --issue <N> --repo OWNER/NAME # accept issue N (#845 Story 3)");
376
+ lines.push(" deft triage:reject --issue <N> --repo OWNER/NAME --reason 'why' # reject issue N");
377
+ lines.push(" deft triage:bulk-accept --repo OWNER/NAME --label adoption-blocker # bulk accept");
378
+ lines.push(" deft triage:refresh-active # pre-swarm freshness gate");
379
379
  }
380
380
  return lines.join("\n");
381
381
  }
@@ -357,7 +357,7 @@ export declare const registryData: {
357
357
  readonly name: "task triage:metrics";
358
358
  readonly summary: "Attributed-value trend from the events ledger";
359
359
  readonly refs: "(#1709)";
360
- readonly description: "Report value-feedback attribution trends from .deft-cache/events.jsonl: total signal count plus per-class (value, bypass, adoption, friction) and per-event breakdowns over a time window. Alias of the value:show handler. Requires plan.policy.valueFeedback.enabled (default OFF) — exits blocked when disabled; an empty ledger prints a no-signals message. In the maintainer repo, skipped unless DEFT_VALUE_SELF_DOGFOOD=1.";
360
+ readonly description: "Report value-feedback attribution trends from .deft-cache/events.jsonl: total signal count plus per-class (value, bypass, adoption, friction) and per-event breakdowns over a time window. Also composes a ceremony-cost rollup (#3508): last cold vs re-arm CLI process time, per-step ms, blocked-ritual count, recovery-tier distribution. Alias of the value:show handler. Attribution requires plan.policy.valueFeedback.enabled (default OFF) — exits blocked when disabled, but still prints the ceremony-cost section. An empty attribution ledger prints a no-signals message. In the maintainer repo, attribution is skipped unless DEFT_VALUE_SELF_DOGFOOD=1.";
361
361
  readonly usage: "task triage:metrics [-- --window=7d] [--format=text|json]";
362
362
  readonly flags: readonly [readonly ["--window WINDOW", "7d", "Time window (e.g. 7d, 30d, 24h)."], readonly ["--format text|json", "text", "Output shape (json emits the full trend object)."]];
363
363
  readonly examples: readonly ["task triage:metrics", "task triage:metrics -- --window=30d --format=json"];
@@ -628,7 +628,7 @@ export const registryData = {
628
628
  name: "task triage:metrics",
629
629
  summary: "Attributed-value trend from the events ledger",
630
630
  refs: "(#1709)",
631
- description: "Report value-feedback attribution trends from .deft-cache/events.jsonl: total signal count plus per-class (value, bypass, adoption, friction) and per-event breakdowns over a time window. Alias of the value:show handler. Requires plan.policy.valueFeedback.enabled (default OFF) — exits blocked when disabled; an empty ledger prints a no-signals message. In the maintainer repo, skipped unless DEFT_VALUE_SELF_DOGFOOD=1.",
631
+ description: "Report value-feedback attribution trends from .deft-cache/events.jsonl: total signal count plus per-class (value, bypass, adoption, friction) and per-event breakdowns over a time window. Also composes a ceremony-cost rollup (#3508): last cold vs re-arm CLI process time, per-step ms, blocked-ritual count, recovery-tier distribution. Alias of the value:show handler. Attribution requires plan.policy.valueFeedback.enabled (default OFF) — exits blocked when disabled, but still prints the ceremony-cost section. An empty attribution ledger prints a no-signals message. In the maintainer repo, attribution is skipped unless DEFT_VALUE_SELF_DOGFOOD=1.",
632
632
  usage: "task triage:metrics [-- --window=7d] [--format=text|json]",
633
633
  flags: [
634
634
  ["--window WINDOW", "7d", "Time window (e.g. 7d, 30d, 24h)."],
@@ -1,4 +1,4 @@
1
- export declare function appendAuditEntry(projectRoot: string, entry: string): string;
1
+ export declare function appendAuditEntry(projectRoot: string, entry: string, changed?: boolean): string;
2
2
  export declare function writeTriageScope(projectRoot: string, rules: Array<Record<string, unknown>>, options?: {
3
3
  presetLabel: string;
4
4
  actor?: string;