@deftai/directive-core 0.104.0 → 0.105.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 (225) hide show
  1. package/dist/authz/classify.js +526 -42
  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 +33 -3
  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/fs/contained-write.d.ts +115 -1
  17. package/dist/fs/contained-write.js +241 -5
  18. package/dist/fs/mutation-ledger.d.ts +54 -0
  19. package/dist/fs/mutation-ledger.js +133 -0
  20. package/dist/fs/non-product-dirs.d.ts +79 -0
  21. package/dist/fs/non-product-dirs.js +96 -0
  22. package/dist/hooks/dispatcher.d.ts +3 -1
  23. package/dist/hooks/dispatcher.js +14 -0
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.js +2 -0
  26. package/dist/init-deposit/agent-hooks.js +17 -15
  27. package/dist/init-deposit/core-guard-pin-content.d.ts +7 -0
  28. package/dist/init-deposit/core-guard-pin-content.js +393 -0
  29. package/dist/init-deposit/gitignore.js +9 -0
  30. package/dist/init-deposit/hygiene.d.ts +47 -11
  31. package/dist/init-deposit/hygiene.js +172 -37
  32. package/dist/init-deposit/legacy-detect.js +6 -2
  33. package/dist/init-deposit/refresh.d.ts +21 -1
  34. package/dist/init-deposit/refresh.js +106 -9
  35. package/dist/init-deposit/scaffold.js +28 -279
  36. package/dist/init-deposit/slash-deposit.js +13 -7
  37. package/dist/init-deposit/xbrief-projections.d.ts +8 -1
  38. package/dist/init-deposit/xbrief-projections.js +24 -11
  39. package/dist/intake/clause-derivation.d.ts +38 -1
  40. package/dist/intake/clause-derivation.js +211 -9
  41. package/dist/intake/issue-ingest.d.ts +3 -1
  42. package/dist/intake/issue-ingest.js +35 -6
  43. package/dist/lifecycle/completed-tracked-on-delivery.d.ts +5 -0
  44. package/dist/lifecycle/completed-tracked-on-delivery.js +85 -18
  45. package/dist/literal-acceptance/capture.d.ts +10 -0
  46. package/dist/literal-acceptance/capture.js +224 -75
  47. package/dist/literal-acceptance/evaluate.d.ts +29 -4
  48. package/dist/literal-acceptance/evaluate.js +70 -2
  49. package/dist/literal-acceptance/index.d.ts +3 -3
  50. package/dist/literal-acceptance/index.js +3 -3
  51. package/dist/literal-acceptance/safety.d.ts +41 -0
  52. package/dist/literal-acceptance/safety.js +126 -2
  53. package/dist/literal-acceptance/types.d.ts +8 -0
  54. package/dist/orphan-active/evaluate.d.ts +7 -1
  55. package/dist/orphan-active/evaluate.js +102 -23
  56. package/dist/platform/platform-capabilities.js +2 -3
  57. package/dist/policy/base-branch.d.ts +37 -0
  58. package/dist/policy/base-branch.js +114 -0
  59. package/dist/policy/branch-sync.d.ts +80 -0
  60. package/dist/policy/branch-sync.js +211 -0
  61. package/dist/policy/ceremony-dial-escalation.js +1 -0
  62. package/dist/policy/ceremony-dial.js +4 -14
  63. package/dist/policy/delivery-branch.d.ts +5 -3
  64. package/dist/policy/delivery-branch.js +7 -3
  65. package/dist/policy/file-size-thresholds.d.ts +24 -0
  66. package/dist/policy/file-size-thresholds.js +23 -0
  67. package/dist/policy/forge-outage-retry.d.ts +47 -0
  68. package/dist/policy/forge-outage-retry.js +157 -0
  69. package/dist/policy/index.d.ts +7 -0
  70. package/dist/policy/index.js +66 -7
  71. package/dist/policy/project-invariants.d.ts +50 -0
  72. package/dist/policy/project-invariants.js +205 -0
  73. package/dist/policy/sync-default.d.ts +129 -0
  74. package/dist/policy/sync-default.js +452 -0
  75. package/dist/policy/sync-max-files.d.ts +93 -0
  76. package/dist/policy/sync-max-files.js +192 -0
  77. package/dist/pr-monitor/absent-required.d.ts +11 -0
  78. package/dist/pr-monitor/absent-required.js +61 -0
  79. package/dist/pr-monitor/constants.d.ts +7 -0
  80. package/dist/pr-monitor/constants.js +7 -0
  81. package/dist/pr-monitor/index.d.ts +2 -1
  82. package/dist/pr-monitor/index.js +2 -1
  83. package/dist/pr-monitor/main.js +4 -0
  84. package/dist/pr-monitor/monitor.js +24 -1
  85. package/dist/pr-wait-mergeable/classify.js +6 -0
  86. package/dist/preflight/evaluate.d.ts +4 -0
  87. package/dist/preflight/evaluate.js +35 -1
  88. package/dist/preflight/index.d.ts +4 -0
  89. package/dist/preflight/index.js +2 -0
  90. package/dist/preflight/intended-placement.d.ts +62 -0
  91. package/dist/preflight/intended-placement.js +275 -0
  92. package/dist/preflight/project-invariants-gate.d.ts +29 -0
  93. package/dist/preflight/project-invariants-gate.js +82 -0
  94. package/dist/product-first-done-gate/acceptance-resolver.d.ts +128 -0
  95. package/dist/product-first-done-gate/acceptance-resolver.js +144 -0
  96. package/dist/product-first-done-gate/acceptance.d.ts +7 -4
  97. package/dist/product-first-done-gate/acceptance.js +79 -8
  98. package/dist/product-first-done-gate/empty-resolution.d.ts +4 -1
  99. package/dist/product-first-done-gate/empty-resolution.js +12 -1
  100. package/dist/product-first-done-gate/evaluate.d.ts +12 -0
  101. package/dist/product-first-done-gate/evaluate.js +254 -31
  102. package/dist/product-first-done-gate/index.d.ts +2 -0
  103. package/dist/product-first-done-gate/index.js +1 -0
  104. package/dist/release/build-dist.d.ts +101 -0
  105. package/dist/release/build-dist.js +428 -46
  106. package/dist/run-summary/emit.d.ts +16 -10
  107. package/dist/run-summary/emit.js +26 -15
  108. package/dist/run-summary/index.d.ts +3 -2
  109. package/dist/run-summary/index.js +2 -1
  110. package/dist/run-summary/session-id.d.ts +21 -0
  111. package/dist/run-summary/session-id.js +92 -0
  112. package/dist/run-summary/types.d.ts +29 -4
  113. package/dist/run-summary/types.js +1 -0
  114. package/dist/scope/acceptance-evidence.d.ts +19 -7
  115. package/dist/scope/acceptance-evidence.js +38 -11
  116. package/dist/scope/delivery-evidence.d.ts +3 -3
  117. package/dist/scope/delivery-evidence.js +22 -27
  118. package/dist/scope/index.d.ts +1 -0
  119. package/dist/scope/index.js +1 -0
  120. package/dist/scope/parent-lineage.js +7 -6
  121. package/dist/scope/project-invariant-coverage.d.ts +54 -0
  122. package/dist/scope/project-invariant-coverage.js +322 -0
  123. package/dist/scope/transition.js +1 -1
  124. package/dist/scope-provenance/compare-intent.d.ts +27 -0
  125. package/dist/scope-provenance/compare-intent.js +233 -0
  126. package/dist/scope-provenance/digest.d.ts +18 -1
  127. package/dist/scope-provenance/digest.js +16 -11
  128. package/dist/scope-provenance/evaluate.d.ts +5 -1
  129. package/dist/scope-provenance/evaluate.js +64 -4
  130. package/dist/scope-provenance/extract-intent.d.ts +43 -0
  131. package/dist/scope-provenance/extract-intent.js +262 -0
  132. package/dist/scope-provenance/index.d.ts +7 -1
  133. package/dist/scope-provenance/index.js +7 -1
  134. package/dist/scope-provenance/intent-digest.d.ts +12 -0
  135. package/dist/scope-provenance/intent-digest.js +33 -0
  136. package/dist/scope-provenance/intent-evaluate.d.ts +43 -0
  137. package/dist/scope-provenance/intent-evaluate.js +149 -0
  138. package/dist/scope-provenance/json-tokenizer.d.ts +19 -0
  139. package/dist/scope-provenance/json-tokenizer.js +225 -0
  140. package/dist/scope-provenance/known-machine.d.ts +38 -0
  141. package/dist/scope-provenance/known-machine.js +157 -0
  142. package/dist/scope-provenance/mint-artifacts.d.ts +102 -0
  143. package/dist/scope-provenance/mint-artifacts.js +425 -0
  144. package/dist/session/ac-pass-banking.d.ts +4 -36
  145. package/dist/session/ac-pass-banking.js +30 -77
  146. package/dist/session/ac-pass-reuse.d.ts +53 -0
  147. package/dist/session/ac-pass-reuse.js +95 -0
  148. package/dist/session/git.js +19 -4
  149. package/dist/session/index.d.ts +4 -0
  150. package/dist/session/index.js +4 -0
  151. package/dist/session/occupancy-stress-worker.d.ts +7 -0
  152. package/dist/session/occupancy-stress-worker.js +36 -0
  153. package/dist/session/occupancy.d.ts +97 -0
  154. package/dist/session/occupancy.js +422 -0
  155. package/dist/session/openclaw-soft-rebind-deposit.js +6 -6
  156. package/dist/session/product-state-hash.d.ts +26 -0
  157. package/dist/session/product-state-hash.js +683 -0
  158. package/dist/session/session-ready.d.ts +2 -0
  159. package/dist/session/session-ready.js +52 -30
  160. package/dist/session/session-start.d.ts +7 -0
  161. package/dist/session/session-start.js +71 -7
  162. package/dist/session/verify-ac-session-cache.d.ts +42 -0
  163. package/dist/session/verify-ac-session-cache.js +126 -0
  164. package/dist/session/verify-session-ritual.d.ts +5 -0
  165. package/dist/session/verify-session-ritual.js +9 -0
  166. package/dist/slash/openclaw-deposit.js +13 -9
  167. package/dist/slice/constants.d.ts +2 -2
  168. package/dist/slice/constants.js +2 -2
  169. package/dist/slice/existing.js +9 -3
  170. package/dist/slice/lock.d.ts +16 -1
  171. package/dist/slice/lock.js +137 -11
  172. package/dist/story-ready/evaluate.d.ts +2 -0
  173. package/dist/story-ready/evaluate.js +13 -0
  174. package/dist/swarm/complete-cohort.d.ts +3 -0
  175. package/dist/swarm/complete-cohort.js +59 -11
  176. package/dist/swarm/finalize-cohort.js +33 -2
  177. package/dist/swarm/launch.d.ts +22 -0
  178. package/dist/swarm/launch.js +155 -0
  179. package/dist/swarm/routing-set-cli.d.ts +11 -1
  180. package/dist/swarm/routing-set-cli.js +13 -3
  181. package/dist/swarm/routing-verify.d.ts +12 -0
  182. package/dist/swarm/routing-verify.js +59 -20
  183. package/dist/swarm/routing.d.ts +14 -0
  184. package/dist/swarm/routing.js +53 -3
  185. package/dist/telemetry-coverage/fake-trial.js +10 -0
  186. package/dist/telemetry-coverage/kinds.js +2 -0
  187. package/dist/triage/actions/index.js +4 -1
  188. package/dist/ts-check-lane/index.d.ts +3 -0
  189. package/dist/ts-check-lane/index.js +2 -0
  190. package/dist/ts-check-lane/progress-reporter.d.ts +17 -0
  191. package/dist/ts-check-lane/progress-reporter.js +38 -0
  192. package/dist/ts-check-lane/progress.d.ts +32 -0
  193. package/dist/ts-check-lane/progress.js +57 -0
  194. package/dist/ts-check-lane/run-lane.d.ts +3 -1
  195. package/dist/ts-check-lane/run-lane.js +8 -6
  196. package/dist/validate-content/validate-links.d.ts +8 -0
  197. package/dist/validate-content/validate-links.js +9 -15
  198. package/dist/value/feedback-file.js +12 -2
  199. package/dist/vbrief-build/index.d.ts +1 -0
  200. package/dist/vbrief-build/index.js +1 -0
  201. package/dist/vbrief-build/project-invariants-io.d.ts +16 -0
  202. package/dist/vbrief-build/project-invariants-io.js +41 -0
  203. package/dist/vbrief-reconcile/graph.js +18 -1
  204. package/dist/vbrief-reconcile/index.d.ts +1 -1
  205. package/dist/vbrief-reconcile/index.js +1 -1
  206. package/dist/vbrief-reconcile/types.d.ts +13 -0
  207. package/dist/vbrief-reconcile/umbrellas.d.ts +14 -0
  208. package/dist/vbrief-reconcile/umbrellas.js +261 -26
  209. package/dist/vbrief-validate/index.d.ts +1 -1
  210. package/dist/vbrief-validate/index.js +1 -1
  211. package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
  212. package/dist/vbrief-validate/plan-hooks.js +68 -0
  213. package/dist/verify-ac/evaluate.d.ts +7 -1
  214. package/dist/verify-ac/evaluate.js +34 -26
  215. package/dist/verify-ac/flag.d.ts +4 -0
  216. package/dist/verify-ac/flag.js +16 -0
  217. package/dist/verify-ac/index.d.ts +2 -2
  218. package/dist/verify-ac/index.js +2 -2
  219. package/dist/verify-source/contained-writes.js +2 -0
  220. package/dist/verify-source/verify-stubs.d.ts +15 -1
  221. package/dist/verify-source/verify-stubs.js +20 -16
  222. package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +5 -6
  223. package/dist/vitest-runner/win32-coverage-tmp-setup.js +5 -6
  224. package/dist/xbrief-migrate/migrate-project.js +2 -5
  225. package/package.json +3 -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;
@@ -49,7 +59,7 @@ export function routingSetMain(argv = process.argv.slice(2)) {
49
59
  // Same key as launch + verify:routing: OPENCLAW / sessions_spawn → openclaw
50
60
  // (#2875 Greptile P1). Do not map via runtimeMode alone — OpenClaw-only envs
51
61
  // are often local-unsandboxed while still dispatching under provider openclaw.
52
- resolvedProvider = resolveDispatchProvider(process.env);
62
+ resolvedProvider = resolveDispatchProvider(environ);
53
63
  }
54
64
  if (harnessDefault) {
55
65
  if (model !== null) {
@@ -63,7 +73,7 @@ export function routingSetMain(argv = process.argv.slice(2)) {
63
73
  return EXIT_CONFIG_ERROR;
64
74
  }
65
75
  const root = resolve(projectRoot);
66
- const path = resolveRoutingPath(root);
76
+ const path = resolveRoutingPath(root, environ);
67
77
  try {
68
78
  writeModelDecision(root, path, resolvedProvider, role, {
69
79
  model,
@@ -1,3 +1,4 @@
1
+ import { type RoutingFile } from "./routing.js";
1
2
  export declare const ROUTING_SET_CMD = "task 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"];
@@ -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,11 +5,13 @@
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";
14
+ import { dispatchProviderFromRuntime, emptyHostDetectProbes, HARNESS_BOUND_PROVIDERS, loadRoutingFile, ROUTING_MODE_HARNESS_DEFAULT, resolveDispatchProvider, resolveModelRoute, resolveRoutingPath, } from "./routing.js";
13
15
  export const ROUTING_SET_CMD = "task 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"];
@@ -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 'task 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
  };
@@ -1,3 +1,6 @@
1
+ export type { ProgressLineSink, ProgressTick, WriteFlushedLineOptions, } from "./progress.js";
2
+ export { buildTestLaneCommand, formatProgressLine, nextProgressTick, PROGRESS_BAND_PERCENT, PROGRESS_REPORTER_RELATIVE_PATH, PROGRESS_UNIT, resolveTestLaneCommand, writeFlushedLine, } from "./progress.js";
3
+ export { TsCheckLaneProgressReporter } from "./progress-reporter.js";
1
4
  export type { LaneRunner, ResolvePnpmOptions, RunnerResult, RunTsLaneOptions } from "./run-lane.js";
2
5
  export { LANE_COMMANDS, resolvePnpm, runTsLane, SKIP_NOTICE, sanitizeTsLaneEnv, } from "./run-lane.js";
3
6
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,4 @@
1
+ export { buildTestLaneCommand, formatProgressLine, nextProgressTick, PROGRESS_BAND_PERCENT, PROGRESS_REPORTER_RELATIVE_PATH, PROGRESS_UNIT, resolveTestLaneCommand, writeFlushedLine, } from "./progress.js";
2
+ export { TsCheckLaneProgressReporter } from "./progress-reporter.js";
1
3
  export { LANE_COMMANDS, resolvePnpm, runTsLane, SKIP_NOTICE, sanitizeTsLaneEnv, } from "./run-lane.js";
2
4
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Vitest reporter that emits coarse flushed progress for ts:check-lane (#3470).
3
+ *
4
+ * Wired from the lane via `--reporter` so vitest.config.ts stays unchanged.
5
+ * Does not print test names and does not change pass/fail.
6
+ */
7
+ export declare class TsCheckLaneProgressReporter {
8
+ private total;
9
+ private completed;
10
+ private lastEmittedPercent;
11
+ private readonly sink?;
12
+ constructor(sink?: unknown);
13
+ onTestRunStart(specifications?: ReadonlyArray<unknown>): void;
14
+ onTestModuleEnd(): void;
15
+ }
16
+ export default TsCheckLaneProgressReporter;
17
+ //# sourceMappingURL=progress-reporter.d.ts.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Vitest reporter that emits coarse flushed progress for ts:check-lane (#3470).
3
+ *
4
+ * Wired from the lane via `--reporter` so vitest.config.ts stays unchanged.
5
+ * Does not print test names and does not change pass/fail.
6
+ */
7
+ import { formatProgressLine, nextProgressTick, writeFlushedLine, } from "./progress.js";
8
+ function isLineSink(value) {
9
+ return (typeof value === "object" &&
10
+ value !== null &&
11
+ typeof value.write === "function");
12
+ }
13
+ export class TsCheckLaneProgressReporter {
14
+ total = 0;
15
+ completed = 0;
16
+ lastEmittedPercent = 0;
17
+ sink;
18
+ constructor(sink) {
19
+ // Vitest constructs reporters with its options object; only tests pass a sink.
20
+ this.sink = isLineSink(sink) ? sink : undefined;
21
+ }
22
+ onTestRunStart(specifications = []) {
23
+ this.total = specifications.length;
24
+ this.completed = 0;
25
+ this.lastEmittedPercent = 0;
26
+ }
27
+ onTestModuleEnd() {
28
+ this.completed += 1;
29
+ const tick = nextProgressTick(this.completed, this.total, this.lastEmittedPercent);
30
+ if (tick === null) {
31
+ return;
32
+ }
33
+ this.lastEmittedPercent = tick.percent;
34
+ writeFlushedLine(formatProgressLine(tick), this.sink);
35
+ }
36
+ }
37
+ export default TsCheckLaneProgressReporter;
38
+ //# sourceMappingURL=progress-reporter.js.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Coarse percent/count ticks for ts:check-lane (#3470).
3
+ *
4
+ * Band math is independent of vitest so tests can cover cadence and flush
5
+ * without running the suite.
6
+ */
7
+ /** Relative to the repo root; wired onto `pnpm run test` from the lane. */
8
+ export declare const PROGRESS_REPORTER_RELATIVE_PATH = "packages/core/src/ts-check-lane/progress-reporter.ts";
9
+ /** One line every 20% of known files -- coarse enough for a log snapshot. */
10
+ export declare const PROGRESS_BAND_PERCENT = 20;
11
+ export declare const PROGRESS_UNIT = "files";
12
+ export interface ProgressTick {
13
+ readonly percent: number;
14
+ readonly completed: number;
15
+ readonly total: number;
16
+ }
17
+ export interface ProgressLineSink {
18
+ write(chunk: string): void;
19
+ flush?: () => void;
20
+ }
21
+ export declare function nextProgressTick(completed: number, total: number, lastEmittedPercent: number, bandPercent?: number): ProgressTick | null;
22
+ export declare function formatProgressLine(tick: ProgressTick, unit?: string): string;
23
+ export interface WriteFlushedLineOptions {
24
+ readonly fd?: number;
25
+ readonly syncWrite?: (fd: number, payload: string) => void;
26
+ }
27
+ /** writeSync so a non-TTY capture sees each tick immediately (#1353). */
28
+ export declare function writeFlushedLine(line: string, sink?: ProgressLineSink, options?: WriteFlushedLineOptions): void;
29
+ export declare function buildTestLaneCommand(reporterPath?: string): readonly string[];
30
+ /** Attach the reporter only when the source file is present (framework checkout). */
31
+ export declare function resolveTestLaneCommand(projectRoot: string, exists?: (path: string) => boolean): readonly string[];
32
+ //# sourceMappingURL=progress.d.ts.map