@khoavu882/doflow 2.0.0-beta.3 → 2.0.0-beta.5

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 (236) hide show
  1. package/bin/doflow.js +2 -2
  2. package/core/.antigravity-plugin/plugin.json +0 -1
  3. package/core/.claude-plugin/plugin.json +1 -1
  4. package/core/.codex-plugin/plugin.json +1 -1
  5. package/core/.plugin/plugin.json +1 -1
  6. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
  7. package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
  8. package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
  9. package/core/harnesses/claude/hooks/session-start.sh +5 -3
  10. package/core/harnesses/claude/hooks/stop-check.sh +5 -3
  11. package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
  12. package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
  13. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
  14. package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
  15. package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
  16. package/core/harnesses/codex/hooks/session-start.sh +5 -3
  17. package/core/harnesses/codex/hooks/stop-check.sh +6 -5
  18. package/core/harnesses/codex/hooks/subagent-audit.sh +17 -6
  19. package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
  20. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +7 -18
  21. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +7 -18
  22. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +7 -19
  23. package/core/harnesses/gemini/hooks/session-start.sh +5 -3
  24. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +20 -5
  25. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +20 -5
  26. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +20 -5
  27. package/core/harnesses/kiro/hooks/session-start.sh +11 -5
  28. package/core/harnesses/kiro/hooks/stop-check.sh +21 -30
  29. package/core/harnesses/pi/extensions/doflow-guard.js +159 -0
  30. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -11
  31. package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
  32. package/core/harnesses/shared/hooks/bridge/bridge.sh +184 -0
  33. package/core/harnesses/shared/hooks/bridge/envelope.jq +60 -0
  34. package/core/harnesses/shared/hooks/bridge/respond.sh +62 -0
  35. package/core/harnesses/shared/hooks/policies/capture-failure.sh +4 -2
  36. package/core/harnesses/shared/hooks/policies/contract.sh +75 -0
  37. package/core/harnesses/shared/hooks/policies/deny-json.sh +39 -4
  38. package/core/harnesses/shared/hooks/policies/lib.sh +4 -2
  39. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +46 -34
  40. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +75 -25
  41. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +67 -73
  42. package/core/harnesses/shared/hooks/policies/session-context.sh +37 -7
  43. package/core/harnesses/shared/hooks/policies/stop-check.sh +42 -15
  44. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +35 -5
  45. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +40 -7
  46. package/core/harnesses/shared/hooks/stream-hook-runner.js +69 -181
  47. package/core/registry/assets.json +19 -0
  48. package/core/registry/contracts.json +2 -2
  49. package/core/registry/harnesses.json +196 -10
  50. package/core/registry/lifecycle.json +2626 -7
  51. package/core/registry/storage.json +110 -83
  52. package/core/registry/verification.json +1 -1
  53. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -2
  54. package/core/shared/scripts/doflow/bin/doflow-run +10 -6
  55. package/core/shared/skills/do-design/SKILL.md +6 -6
  56. package/core/shared/skills/do-execute-plan/SKILL.md +11 -10
  57. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +2 -0
  58. package/core/shared/skills/do-execute-plan/references/scaffold.md +2 -2
  59. package/core/shared/skills/do-implement/SKILL.md +3 -2
  60. package/core/shared/skills/do-plan/SKILL.md +7 -8
  61. package/package.json +4 -2
  62. package/src/adapters/antigravity/{index.ts → index.js} +141 -105
  63. package/src/adapters/claude/{index.ts → index.js} +63 -49
  64. package/src/adapters/claude/{mcp.ts → mcp.js} +16 -16
  65. package/src/adapters/codex/{agents.ts → agents.js} +24 -24
  66. package/src/adapters/codex/{config.ts → config.js} +49 -49
  67. package/src/adapters/codex/{hooks.ts → hooks.js} +62 -27
  68. package/src/adapters/codex/{index.ts → index.js} +148 -105
  69. package/src/adapters/codex/{mcp.ts → mcp.js} +38 -38
  70. package/src/adapters/codex/{native-projection.ts → native-projection.js} +5 -5
  71. package/src/adapters/copilot/{index.ts → index.js} +42 -42
  72. package/src/adapters/{copy-tree.ts → copy-tree.js} +74 -74
  73. package/src/adapters/gemini/{hooks.ts → hooks.js} +78 -37
  74. package/src/adapters/gemini/{index.ts → index.js} +96 -57
  75. package/src/adapters/{hook-commands.ts → hook-commands.js} +8 -8
  76. package/src/adapters/{index.ts → index.js} +13 -13
  77. package/src/adapters/kiro/{index.ts → index.js} +49 -35
  78. package/src/adapters/{mcp-entries.ts → mcp-entries.js} +55 -55
  79. package/src/adapters/opencode/{index.ts → index.js} +52 -52
  80. package/src/adapters/pi/hooks.js +119 -0
  81. package/src/adapters/pi/{index.ts → index.js} +64 -39
  82. package/src/adapters/pi/{json-members.ts → json-members.js} +23 -23
  83. package/src/adapters/pi/{mcp.ts → mcp.js} +70 -70
  84. package/src/adapters/{resolve-source.ts → resolve-source.js} +1 -1
  85. package/src/cli/commands/{install.ts → install.js} +25 -25
  86. package/src/cli/commands/{list-backups.ts → list-backups.js} +4 -4
  87. package/src/cli/commands/{reconcile.ts → reconcile.js} +19 -19
  88. package/src/cli/commands/{remove.ts → remove.js} +10 -10
  89. package/src/cli/commands/{rollback.ts → rollback.js} +17 -17
  90. package/src/cli/commands/{self-update.ts → self-update.js} +3 -3
  91. package/src/cli/commands/{status.ts → status.js} +24 -24
  92. package/src/cli/commands/{tools.ts → tools.js} +16 -16
  93. package/src/cli/commands/{update.ts → update.js} +20 -20
  94. package/src/cli/{index.ts → index.js} +43 -31
  95. package/src/cli/{runtime-commands.ts → runtime-commands.js} +97 -58
  96. package/src/cli/{shared.ts → shared.js} +40 -40
  97. package/src/helper/{bash-helper.ts → bash-helper.js} +7 -7
  98. package/src/helper/{git.ts → git.js} +2 -2
  99. package/src/helper/{guardrails.ts → guardrails.js} +10 -10
  100. package/src/helper/{harness-paths.ts → harness-paths.js} +24 -24
  101. package/src/helper/{marker-merge.ts → marker-merge.js} +4 -4
  102. package/src/helper/{prompt.ts → prompt.js} +5 -5
  103. package/src/helper/{repo-root.ts → repo-root.js} +2 -2
  104. package/src/helper/{settings-merge.ts → settings-merge.js} +27 -27
  105. package/src/helper/{settings-scope.ts → settings-scope.js} +3 -3
  106. package/src/helper/{toml.ts → toml.js} +48 -48
  107. package/src/install/{backup-set.ts → backup-set.js} +16 -16
  108. package/src/install/{backup.ts → backup.js} +65 -65
  109. package/src/install/{context.ts → context.js} +15 -15
  110. package/src/install/{manifest.ts → manifest.js} +27 -27
  111. package/src/install/{mcp.ts → mcp.js} +30 -30
  112. package/src/install/{paths.ts → paths.js} +15 -15
  113. package/src/install/{targets.ts → targets.js} +10 -10
  114. package/src/install/{tool-lifecycle.ts → tool-lifecycle.js} +20 -20
  115. package/src/lifecycle/{bash-availability.ts → bash-availability.js} +1 -1
  116. package/src/lifecycle/coverage.js +57 -0
  117. package/src/lifecycle/hook-step.js +61 -0
  118. package/src/lifecycle/{index.ts → index.js} +160 -146
  119. package/src/lifecycle/{mcp-index.ts → mcp-index.js} +1 -1
  120. package/src/lifecycle/{policies.ts → policies.js} +11 -11
  121. package/src/lifecycle/{view.ts → view.js} +28 -28
  122. package/src/registry/{index.ts → index.js} +195 -66
  123. package/src/release/build-package.js +91 -0
  124. package/src/release/changelog-section.js +70 -0
  125. package/src/release/{sync-plugin-versions.ts → sync-plugin-versions.js} +4 -4
  126. package/src/runtime/advice/{cli.ts → cli.js} +31 -31
  127. package/src/runtime/advice/{client.ts → client.js} +44 -44
  128. package/src/runtime/advice/{config.ts → config.js} +16 -16
  129. package/src/runtime/advice/{doctor.ts → doctor.js} +32 -32
  130. package/src/runtime/advice/{endpoint-guard.ts → endpoint-guard.js} +20 -20
  131. package/src/runtime/advice/{kinds.ts → kinds.js} +16 -16
  132. package/src/runtime/advice/providers/{ollama-systemone.ts → ollama-systemone.js} +21 -21
  133. package/src/runtime/advice/{record.ts → record.js} +19 -19
  134. package/src/runtime/advice/{registry.ts → registry.js} +16 -16
  135. package/src/runtime/advice/{strength.ts → strength.js} +7 -7
  136. package/src/runtime/{brief-tools.ts → brief-tools.js} +50 -50
  137. package/src/runtime/{c4-project.ts → c4-project.js} +27 -27
  138. package/src/runtime/candidate.js +505 -0
  139. package/src/runtime/{capability-router.ts → capability-router.js} +39 -39
  140. package/src/runtime/{checkouts.ts → checkouts.js} +21 -21
  141. package/src/runtime/{claims.ts → claims.js} +44 -35
  142. package/src/runtime/{cli-result.ts → cli-result.js} +15 -8
  143. package/src/runtime/{cli.ts → cli.js} +59 -292
  144. package/src/runtime/{command-detect.ts → command-detect.js} +47 -47
  145. package/src/runtime/context/budget.js +203 -0
  146. package/src/runtime/context/compose.js +1367 -0
  147. package/src/runtime/context/delivery-store.js +102 -0
  148. package/src/runtime/context/mandatory.js +594 -0
  149. package/src/runtime/context/pointer.js +142 -0
  150. package/src/runtime/context-pack.js +1277 -0
  151. package/src/runtime/{decision-register.ts → decision-register.js} +67 -67
  152. package/src/runtime/evidence-fields.js +39 -0
  153. package/src/runtime/{evidence-ledger.ts → evidence-ledger.js} +32 -25
  154. package/src/runtime/failure/{capture.ts → capture.js} +29 -29
  155. package/src/runtime/failure/{classifier.ts → classifier.js} +4 -4
  156. package/src/runtime/failure/{cli.ts → cli.js} +30 -30
  157. package/src/runtime/failure/{home.ts → home.js} +6 -6
  158. package/src/runtime/failure/{store.ts → store.js} +32 -32
  159. package/src/runtime/{feature-resolve.ts → feature-resolve.js} +6 -6
  160. package/src/runtime/{freshness.ts → freshness.js} +59 -22
  161. package/src/runtime/{health.ts → health.js} +72 -72
  162. package/src/runtime/{history-compactor.ts → history-compactor.js} +25 -25
  163. package/src/runtime/{implementation-gate.ts → implementation-gate.js} +30 -30
  164. package/src/runtime/inventory/{coverage.ts → coverage.js} +10 -10
  165. package/src/runtime/inventory/{identity.ts → identity.js} +2 -2
  166. package/src/runtime/inventory/{index.ts → index.js} +36 -36
  167. package/src/runtime/inventory/{precedence.ts → precedence.js} +2 -2
  168. package/src/runtime/inventory/{read-scopes.ts → read-scopes.js} +9 -9
  169. package/src/runtime/inventory/{siblings.ts → siblings.js} +15 -15
  170. package/src/runtime/knowledge/{chunker.ts → chunker.js} +12 -12
  171. package/src/runtime/knowledge/{hybrid.ts → hybrid.js} +17 -17
  172. package/src/runtime/knowledge/{index-store.ts → index-store.js} +23 -23
  173. package/src/runtime/knowledge/{retrieval.ts → retrieval.js} +27 -27
  174. package/src/runtime/{leak-scan.ts → leak-scan.js} +12 -12
  175. package/src/runtime/lifecycle/{cli.ts → cli.js} +32 -32
  176. package/src/runtime/lifecycle/{event-store.ts → event-store.js} +48 -48
  177. package/src/runtime/lifecycle/{fold.ts → fold.js} +41 -41
  178. package/src/runtime/lifecycle/{followup.ts → followup.js} +54 -54
  179. package/src/runtime/lifecycle/{goal.ts → goal.js} +50 -50
  180. package/src/runtime/lifecycle/{intent-writer.ts → intent-writer.js} +5 -5
  181. package/src/runtime/lifecycle/{overview.ts → overview.js} +47 -47
  182. package/src/runtime/lifecycle/{release.ts → release.js} +29 -29
  183. package/src/runtime/lifecycle/{report-store.ts → report-store.js} +25 -25
  184. package/src/runtime/lifecycle/{retention.ts → retention.js} +37 -37
  185. package/src/runtime/lifecycle/{root.ts → root.js} +9 -9
  186. package/src/runtime/lifecycle/{status.ts → status.js} +64 -64
  187. package/src/runtime/lifecycle/{store-upkeep.ts → store-upkeep.js} +46 -46
  188. package/src/runtime/{locator-resolve.ts → locator-resolve.js} +7 -7
  189. package/src/runtime/{mask.ts → mask.js} +22 -22
  190. package/src/runtime/{mcp-consultations.ts → mcp-consultations.js} +54 -54
  191. package/src/runtime/{mcp-reach.ts → mcp-reach.js} +24 -24
  192. package/src/runtime/{model-router.ts → model-router.js} +20 -20
  193. package/src/runtime/{outcome.ts → outcome.js} +27 -27
  194. package/src/runtime/{reach.ts → reach.js} +21 -21
  195. package/src/runtime/{readiness-record.ts → readiness-record.js} +12 -12
  196. package/src/runtime/{readiness.ts → readiness.js} +113 -43
  197. package/src/runtime/{recovery.ts → recovery.js} +15 -15
  198. package/src/runtime/{research-request.ts → research-request.js} +34 -34
  199. package/src/runtime/{retrieval-plan.ts → retrieval-plan.js} +453 -104
  200. package/src/runtime/retrieval-record.js +223 -0
  201. package/src/runtime/scaffold/{artifacts.ts → artifacts.js} +22 -19
  202. package/src/runtime/scaffold/{fingerprint.ts → fingerprint.js} +3 -3
  203. package/src/runtime/scaffold/{generate.ts → generate.js} +109 -109
  204. package/src/runtime/scaffold/{languages.ts → languages.js} +34 -34
  205. package/src/runtime/source-version.js +289 -0
  206. package/src/runtime/storage/{cli.ts → cli.js} +9 -9
  207. package/src/runtime/storage/{engine.ts → engine.js} +37 -37
  208. package/src/runtime/storage/{registry.ts → registry.js} +19 -19
  209. package/src/runtime/storage/{roots.ts → roots.js} +8 -8
  210. package/src/runtime/storage/{rules.ts → rules.js} +26 -26
  211. package/src/runtime/storage/{upkeep.ts → upkeep.js} +35 -35
  212. package/src/runtime/storage/{work-status.ts → work-status.js} +39 -39
  213. package/src/runtime/{task-attempts.ts → task-attempts.js} +62 -62
  214. package/src/runtime/{task-classifier.ts → task-classifier.js} +34 -34
  215. package/src/runtime/{task-scope.ts → task-scope.js} +40 -15
  216. package/src/runtime/{task-state.ts → task-state.js} +86 -21
  217. package/src/runtime/trace/{indicators.ts → indicators.js} +22 -22
  218. package/src/runtime/trace/{ledger.ts → ledger.js} +42 -42
  219. package/src/runtime/trace/{render.ts → render.js} +7 -7
  220. package/src/runtime/trace/{views.ts → views.js} +40 -40
  221. package/src/runtime/verification/{contract-runner.ts → contract-runner.js} +23 -23
  222. package/src/runtime/verification/{engine.ts → engine.js} +143 -143
  223. package/src/runtime/verification/{registry.ts → registry.js} +6 -6
  224. package/src/runtime/verification/{scope-bound.ts → scope-bound.js} +50 -50
  225. package/src/runtime/verification/{test-selection.ts → test-selection.js} +28 -28
  226. package/src/runtime/{workflow-engine.ts → workflow-engine.js} +50 -50
  227. package/src/runtime/{workflow-orchestrator.ts → workflow-orchestrator.js} +91 -91
  228. package/src/runtime/{worktree.ts → worktree.js} +31 -31
  229. package/src/state/{index.ts → index.js} +26 -26
  230. package/src/state/{lockfile.ts → lockfile.js} +20 -20
  231. package/src/state/{run-lock.ts → run-lock.js} +44 -44
  232. package/core/.antigravity-plugin/hooks.json +0 -39
  233. package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
  234. package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
  235. package/core/harnesses/shared/hooks/hooks.json +0 -47
  236. package/src/runtime/context-pack.ts +0 -492
@@ -12,23 +12,23 @@
12
12
 
13
13
  const fs = require('node:fs');
14
14
  const path = require('node:path');
15
- const { WorkflowEngine } = require('./workflow-engine.ts');
16
- const { updateTaskState, readTaskState } = require('./task-state.ts');
17
- const { ResearchRequestStore } = require('./research-request.ts');
18
- const { REPO_ROOT } = require('../helper/repo-root.ts');
19
- const { resolveActiveFeature } = require('./feature-resolve.ts');
20
- const { compactDecisions } = require('./decision-register.ts');
21
- const { EvidenceLedger } = require('./evidence-ledger.ts');
22
- const { resolveConsultations } = require('./mcp-consultations.ts');
23
- const { mcpReach } = require('./mcp-reach.ts');
24
- const { loadRegistry } = require('../registry/index.ts');
25
- const { loadStorageRegistry } = require('./storage/registry.ts');
26
- const { resolveRoots, resolveStorePath } = require('./storage/roots.ts');
27
- const { checkCompletion: checkTaskAttemptCompletion } = require('./task-attempts.ts');
28
- const { appendOutcome, findPick, readWindow } = require('./advice/record.ts');
15
+ const { WorkflowEngine } = require('./workflow-engine.js');
16
+ const { updateTaskState, readTaskState } = require('./task-state.js');
17
+ const { ResearchRequestStore } = require('./research-request.js');
18
+ const { REPO_ROOT } = require('../helper/repo-root.js');
19
+ const { resolveActiveFeature } = require('./feature-resolve.js');
20
+ const { compactDecisions } = require('./decision-register.js');
21
+ const { EvidenceLedger } = require('./evidence-ledger.js');
22
+ const { resolveConsultations } = require('./mcp-consultations.js');
23
+ const { mcpReach } = require('./mcp-reach.js');
24
+ const { loadRegistry } = require('../registry/index.js');
25
+ const { loadStorageRegistry } = require('./storage/registry.js');
26
+ const { resolveRoots, resolveStorePath } = require('./storage/roots.js');
27
+ const { checkCompletion: checkTaskAttemptCompletion } = require('./task-attempts.js');
28
+ const { appendOutcome, findPick, readWindow } = require('./advice/record.js');
29
29
  const {
30
30
  checkReadiness, harnessHookNote, featureSlugFor, runSlugOf, findRunFile,
31
- } = require('./implementation-gate.ts');
31
+ } = require('./implementation-gate.js');
32
32
 
33
33
  /** Appended to the note of an implementation stage completed with no task-attempt ledger (FR-006). */
34
34
  const NO_LEDGER_NOTE = 'No task-attempt ledger exists for this feature, so its tasks were not verified; outcome recorded as unverified.';
@@ -49,7 +49,7 @@ const RUN_STATES = Object.freeze(['RUNNING', 'AWAITING_GATE', 'COMPLETED', 'REJE
49
49
  * @param {string} state
50
50
  * @returns {string} the same state, once it is known to be one
51
51
  */
52
- function runState(state: any) {
52
+ function runState(state ) {
53
53
  if (!RUN_STATES.includes(state)) {
54
54
  throw new Error(`Unknown run state '${state}' (expected one of: ${RUN_STATES.join(', ')})`);
55
55
  }
@@ -63,16 +63,16 @@ const ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
63
63
  /** Why a handoff with no run and no class records nothing, as the result and the CLI line say. */
64
64
  const STANDALONE_REASON = 'no run exists for this task and no --task-class was given, so nothing was recorded and no readiness was required';
65
65
 
66
- function assertSafeId(value: any, label: any) {
66
+ function assertSafeId(value , label ) {
67
67
  if (!ID_PATTERN.test(String(value ?? ''))) throw new Error(`Invalid ${label}: '${value}'`);
68
68
  return String(value);
69
69
  }
70
70
 
71
- function iso(now: any) { return (now ?? new Date()).toISOString(); }
71
+ function iso(now ) { return (now ?? new Date()).toISOString(); }
72
72
 
73
73
  /** Interleave stages with their gates: each gate sits immediately after the stage named by its
74
74
  * `afterStage`. A gate anchored to an unknown stage is a compile error here, not a silent orphan. */
75
- function compileProgram(workflow: any) {
75
+ function compileProgram(workflow ) {
76
76
  const gates = [...(workflow.gates || [])];
77
77
  const nodes = [];
78
78
  for (const stage of workflow.stages || []) {
@@ -99,15 +99,15 @@ function compileProgram(workflow: any) {
99
99
  }
100
100
 
101
101
  class WorkflowOrchestrator {
102
- declare fsImpl: any;
103
- declare repoRoot: any;
104
- declare projectRoot: any;
105
- declare engine: any;
106
- declare stateDir: any;
107
- declare readinessEvaluate: any;
108
- declare taskAttemptCheck: any;
109
- declare featureSlug: any;
110
- declare buildAdapters: any;
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+
111
111
  /**
112
112
  * @param {Object} [options]
113
113
  * @param {string} [options.repoRoot] install root owning core/registry + default state dir.
@@ -128,7 +128,7 @@ class WorkflowOrchestrator {
128
128
  * @param {() => object} [options.buildAdapters] builds the adapter registry `mcpReach` reads installs
129
129
  * through; without it install facts are unknown and no outcome is inferred from them
130
130
  */
131
- constructor({ repoRoot = REPO_ROOT, projectRoot, stateDir, engine, readinessEvaluate, taskAttemptCheck, fsImpl, featureSlug = null, buildAdapters = null }: any = {}) {
131
+ constructor({ repoRoot = REPO_ROOT, projectRoot, stateDir, engine, readinessEvaluate, taskAttemptCheck, fsImpl, featureSlug = null, buildAdapters = null } = {}) {
132
132
  this.fsImpl = fsImpl || fs;
133
133
  this.repoRoot = repoRoot;
134
134
  this.projectRoot = projectRoot || (stateDir?.endsWith(path.join('.doflow', 'state', 'orchestration'))
@@ -141,21 +141,21 @@ class WorkflowOrchestrator {
141
141
  this.buildAdapters = buildAdapters;
142
142
  }
143
143
 
144
- runFile(taskId: any) {
144
+ runFile(taskId ) {
145
145
  return path.join(this.stateDir, `${assertSafeId(taskId, 'taskId')}.json`);
146
146
  }
147
147
 
148
- readRun(taskId: any) {
148
+ readRun(taskId ) {
149
149
  const file = this.runFile(taskId);
150
150
  if (!this.fsImpl.existsSync(file)) return null;
151
151
  return readTaskState(this.fsImpl, file);
152
152
  }
153
153
 
154
- writeRun(run: any, now: any, beforeWrite?: any) {
154
+ writeRun(run , now , beforeWrite ) {
155
155
  run.updatedAt = iso(now);
156
156
  this.fsImpl.mkdirSync(this.stateDir, { recursive: true });
157
157
  const expectedRevision = run.revision || 0;
158
- updateTaskState({ file: this.runFile(run.taskId), fsImpl: this.fsImpl, build: (disk: any) => {
158
+ updateTaskState({ file: this.runFile(run.taskId), fsImpl: this.fsImpl, build: (disk ) => {
159
159
  if ((disk?.revision || 0) !== expectedRevision) {
160
160
  throw new Error(`Concurrent workflow update for '${run.taskId}'; reload the run before retrying. Nothing was written.`);
161
161
  }
@@ -167,7 +167,7 @@ class WorkflowOrchestrator {
167
167
  }
168
168
 
169
169
  /** Begin a new run. An existing run for the same task id is refused — resume instead. */
170
- start({ taskId, taskClass, now }: any = {}) {
170
+ start({ taskId, taskClass, now } = {}) {
171
171
  assertSafeId(taskId, 'taskId');
172
172
  if (this.readRun(taskId)) throw new Error(`Workflow run '${taskId}' already exists; resume it instead of restarting`);
173
173
  const workflow = this.engine.resolveWorkflow(taskClass); // unknown classes are rejected loudly
@@ -190,9 +190,9 @@ class WorkflowOrchestrator {
190
190
  return this.snapshot(run);
191
191
  }
192
192
 
193
- snapshot(run: any) {
193
+ snapshot(run ) {
194
194
  const current = run.cursor < run.program.length ? run.program[run.cursor] : null;
195
- const done = run.program.filter((n: any) => n.status === 'completed' || n.status === 'skipped' || n.status === 'approved').length;
195
+ const done = run.program.filter((n ) => n.status === 'completed' || n.status === 'skipped' || n.status === 'approved').length;
196
196
  return {
197
197
  taskId: run.taskId,
198
198
  taskClass: run.taskClass,
@@ -205,7 +205,7 @@ class WorkflowOrchestrator {
205
205
  };
206
206
  }
207
207
 
208
- status(taskId: any) {
208
+ status(taskId ) {
209
209
  const run = this.requireRun(taskId);
210
210
  return this.snapshot(run);
211
211
  }
@@ -217,7 +217,7 @@ class WorkflowOrchestrator {
217
217
  * entry this handoff writes, and returned as `consultations` and `mcpWarnings`. Only a handoff
218
218
  * that completes a stage or annotates one stores a record; a standalone one reports and stores
219
219
  * nothing, a deferred one does neither. Nothing in that path can fail or refuse the handoff. */
220
- handoff({ taskId, taskClass, callingSkill, note, outcome, mcpOutcomes, now }: any = {}) {
220
+ handoff({ taskId, taskClass, callingSkill, note, outcome, mcpOutcomes, now } = {}) {
221
221
  assertSafeId(taskId, 'taskId');
222
222
  assertSafeId(callingSkill, 'callingSkill');
223
223
  if (!note || !String(note).trim()) throw new Error('handoff requires a note describing the work recorded');
@@ -232,9 +232,9 @@ class WorkflowOrchestrator {
232
232
  }
233
233
  if (existing && taskClass && taskClass !== existing.taskClass) throw new Error(`Task class '${taskClass}' does not match run '${existing.taskClass}'`);
234
234
  const resolvedClass = existing?.taskClass || taskClass;
235
- const stages = existing ? existing.program.filter((n: any) => n.type === 'stage')
235
+ const stages = existing ? existing.program.filter((n ) => n.type === 'stage')
236
236
  : this.engine.resolveWorkflow(resolvedClass).stages;
237
- const candidates = stages.filter((n: any) => n.skill === callingSkill).map((n: any) => n.id);
237
+ const candidates = stages.filter((n ) => n.skill === callingSkill).map((n ) => n.id);
238
238
  if (!candidates.length) throw new Error(`Skill '${callingSkill}' owns no stage in '${resolvedClass}'`);
239
239
  // The stage this handoff would complete is known before anything is written, so a gated stage
240
240
  // without readiness is refused before catch-up starts a run or backfills a stage.
@@ -251,7 +251,7 @@ class WorkflowOrchestrator {
251
251
  const since = existing ? this.stageSince(taskId, positioned.caughtUpTo) : null;
252
252
  const mcp = this.resolveMcp({ taskId, stageId: positioned.caughtUpTo, skill: callingSkill, flags: mcpOutcomes, since });
253
253
  const consultations = mcp ? this.storeToolsUsed(mcp) : null;
254
- const snapshot: any = this.completeStage({ taskId, stageId: positioned.caughtUpTo, note, outcome, consultations, now });
254
+ const snapshot = this.completeStage({ taskId, stageId: positioned.caughtUpTo, note, outcome, consultations, now });
255
255
  return { ...snapshot, disposition: 'completed', recordedStage: positioned.caughtUpTo, consultations, mcpWarnings: mcp ? mcp.messages : [] };
256
256
  }
257
257
  if (positioned.reason.startsWith('already-completed:')) {
@@ -275,12 +275,12 @@ class WorkflowOrchestrator {
275
275
  * contributes nothing, and a registry that cannot load means no record at all (NFR-003).
276
276
  * @returns {{record: object, messages: object[], evidenceToAdd: object[], ledger: EvidenceLedger}|null}
277
277
  */
278
- resolveMcp({ taskId, stageId, skill, flags, since = null }: any) {
278
+ resolveMcp({ taskId, stageId, skill, flags, since = null } ) {
279
279
  let registry;
280
280
  try { registry = loadRegistry({ repoRoot: this.repoRoot }); } catch { return null; }
281
281
  const ledger = new EvidenceLedger({ repoRoot: this.projectRoot, slug: this.featureSlug, fsImpl: this.fsImpl });
282
282
  const loaded = new Set();
283
- let reachMemo: any;
283
+ let reachMemo ;
284
284
  const reachOf = () => {
285
285
  if (reachMemo === undefined) {
286
286
  try {
@@ -291,8 +291,8 @@ class WorkflowOrchestrator {
291
291
  }
292
292
  return reachMemo;
293
293
  };
294
- const displayName = (id: any) => {
295
- const harness = (registry.harnesses || []).find((item: any) => item.id === id);
294
+ const displayName = (id ) => {
295
+ const harness = (registry.harnesses || []).find((item ) => item.id === id);
296
296
  return harness ? harness.displayName || harness.id : id;
297
297
  };
298
298
  const { briefRecords, repoRoot } = stageId ? this.readBriefRecords(taskId) : { briefRecords: [], repoRoot: this.projectRoot };
@@ -300,31 +300,31 @@ class WorkflowOrchestrator {
300
300
  try {
301
301
  const result = resolveConsultations({
302
302
  taskId, stageId, skill, flags, registry, ledger: {
303
- queryEvidence: ({ taskId: id }: any) => {
303
+ queryEvidence: ({ taskId: id } ) => {
304
304
  if (!loaded.has(id)) { loaded.add(id); ledger.load(id); }
305
305
  return ledger.queryEvidence({ taskId: id });
306
306
  },
307
307
  },
308
308
  requests: new ResearchRequestStore({ projectRoot: this.projectRoot, fsImpl: this.fsImpl, slug: this.featureSlug }),
309
309
  briefRecords,
310
- readReport: (file: any) => {
310
+ readReport: (file ) => {
311
311
  const target = path.resolve(repoRoot, file);
312
312
  // A brief record is project data: a report path that leaves the project is not read.
313
313
  if (path.relative(repoRoot, target).startsWith('..') || path.isAbsolute(path.relative(repoRoot, target))) return null;
314
314
  try { return this.fsImpl.readFileSync(target, 'utf8'); } catch { return null; }
315
315
  },
316
316
  reach: {
317
- anyHarnessHas: (server: any) => { const reach = reachOf(); return reach ? reach.anyHarnessHas(server) : null; },
318
- harnessWith: (server: any) => {
319
- const row = reachOf()?.harnesses.find((item: any) => ['selected', 'configured'].includes(item.servers[server]));
317
+ anyHarnessHas: (server ) => { const reach = reachOf(); return reach ? reach.anyHarnessHas(server) : null; },
318
+ harnessWith: (server ) => {
319
+ const row = reachOf()?.harnesses.find((item ) => ['selected', 'configured'].includes(item.servers[server]));
320
320
  return row ? displayName(row.id) : null;
321
321
  },
322
322
  },
323
323
  harnessName: displayName,
324
324
  since,
325
- addCommand: (server: any) => {
325
+ addCommand: (server ) => {
326
326
  const reach = reachOf();
327
- const row = reach?.harnesses.find((item: any) => !['selected', 'configured'].includes(item.servers[server]));
327
+ const row = reach?.harnesses.find((item ) => !['selected', 'configured'].includes(item.servers[server]));
328
328
  return row ? reach.addCommand(row.id, row.scope, server) : null;
329
329
  },
330
330
  });
@@ -335,7 +335,7 @@ class WorkflowOrchestrator {
335
335
  /** When this stage run began, for counting its evidence (IC-008 rule 3): the `at` of the last
336
336
  * stage completed or skipped before it that was not backfilled, else the run start. A stage
337
337
  * already completed (an annotation) is measured from the completion before its own. */
338
- stageSince(taskId: any, stageId: any) {
338
+ stageSince(taskId , stageId ) {
339
339
  try {
340
340
  const run = this.readRun(taskId);
341
341
  const history = Array.isArray(run?.history) ? run.history : [];
@@ -354,21 +354,21 @@ class WorkflowOrchestrator {
354
354
  /** Record advice outcomes for the same brief records the IC-008 handoff pass consumes. This is
355
355
  * advisory telemetry only: every storage/read failure is ignored and never affects the handoff.
356
356
  */
357
- recordBriefTierAdvice({ briefRecords, taskId, stageId, repoRoot }: any) {
357
+ recordBriefTierAdvice({ briefRecords, taskId, stageId, repoRoot } ) {
358
358
  try {
359
359
  const storage = loadStorageRegistry({ repoRoot: this.repoRoot });
360
360
  const roots = resolveRoots({ registry: storage, projectRoot: repoRoot || this.projectRoot });
361
- const declaredStores = new Map((storage.stores || []).map((store: any) => [store.id, store]));
361
+ const declaredStores = new Map((storage.stores || []).map((store ) => [store.id, store]));
362
362
  const safeRoots = { ...roots };
363
363
  for (const id of ['advice-picks-project', 'advice-picks-user']) {
364
- const store: any = declaredStores.get(id);
364
+ const store = declaredStores.get(id);
365
365
  if (!store) return;
366
366
  if (!resolveStorePath({ store, roots, fsImpl: this.fsImpl })) safeRoots[store.root] = null;
367
367
  }
368
368
 
369
369
  const existing = readWindow({ roots: safeRoots, fsImpl: this.fsImpl }).records
370
- .filter(({ record }: any) => record.type === 'outcome')
371
- .map(({ record }: any) => record.pickId);
370
+ .filter(({ record } ) => record.type === 'outcome')
371
+ .map(({ record } ) => record.pickId);
372
372
  const seen = new Set(existing);
373
373
  for (const record of briefRecords) {
374
374
  const advice = record?.advice;
@@ -391,7 +391,7 @@ class WorkflowOrchestrator {
391
391
  /** The brief records (`*-brief.json`) of the feature this run belongs to, parsed; unreadable ones
392
392
  * are left out. Resolving the feature spawns the resolver, so it is only done where a feature
393
393
  * folder exists above the project. */
394
- readBriefRecords(taskId: any) {
394
+ readBriefRecords(taskId ) {
395
395
  const none = { briefRecords: [], repoRoot: this.projectRoot };
396
396
  try {
397
397
  let dir = path.resolve(this.projectRoot);
@@ -407,7 +407,7 @@ class WorkflowOrchestrator {
407
407
  if (feature.error) return none;
408
408
  const exec = path.join(feature.featureDir, 'exec');
409
409
  const briefRecords = [];
410
- for (const name of this.fsImpl.readdirSync(exec).filter((entry: any) => entry.endsWith('-brief.json')).sort()) {
410
+ for (const name of this.fsImpl.readdirSync(exec).filter((entry ) => entry.endsWith('-brief.json')).sort()) {
411
411
  try { briefRecords.push(JSON.parse(this.fsImpl.readFileSync(path.join(exec, name), 'utf8'))); } catch { /* an unreadable record contributes nothing */ }
412
412
  }
413
413
  return { briefRecords, repoRoot: feature.repoRoot };
@@ -418,7 +418,7 @@ class WorkflowOrchestrator {
418
418
  * its evidence id. Evidence goes in before the stage entry is written, so the entry carries the
419
419
  * ids; a retry after a refused completion finds the items and reports them `already-recorded`.
420
420
  * A ledger that cannot be written leaves the rows without an id and never stops the handoff. */
421
- storeToolsUsed({ record, evidenceToAdd, ledger }: any) {
421
+ storeToolsUsed({ record, evidenceToAdd, ledger } ) {
422
422
  if (!evidenceToAdd.length) return record;
423
423
  try {
424
424
  const idsByTask = new Map();
@@ -434,17 +434,17 @@ class WorkflowOrchestrator {
434
434
  return record;
435
435
  }
436
436
 
437
- requireRun(taskId: any) {
437
+ requireRun(taskId ) {
438
438
  const run = this.readRun(taskId);
439
439
  if (!run) throw new Error(`No workflow run for task '${taskId}'`);
440
440
  return run;
441
441
  }
442
442
 
443
- currentNode(run: any) {
443
+ currentNode(run ) {
444
444
  return run.cursor < run.program.length ? run.program[run.cursor] : null;
445
445
  }
446
446
 
447
- expectOpenStage(run: any, stageId: any) {
447
+ expectOpenStage(run , stageId ) {
448
448
  if (run.state !== 'RUNNING') throw new Error(`Run '${run.taskId}' is ${run.state}, not RUNNING`);
449
449
  const node = this.currentNode(run);
450
450
  if (!node || node.type !== 'stage') throw new Error(`Run '${run.taskId}' has no stage to complete next`);
@@ -464,7 +464,7 @@ class WorkflowOrchestrator {
464
464
  * cannot import a verification. A 'failed' outcome still advances — recording the failure
465
465
  * honestly beats refusing to record it, and a bug reproduction legitimately completes by
466
466
  * observing the expected failure — but the record no longer reads as success. */
467
- completeStage({ taskId, stageId, note, outcome, backfilled = false, consultations, now }: any = {}) {
467
+ completeStage({ taskId, stageId, note, outcome, backfilled = false, consultations, now } = {}) {
468
468
  const run = this.requireRun(taskId);
469
469
  const node = this.expectOpenStage(run, stageId);
470
470
  if (outcome !== undefined && !STAGE_OUTCOMES.has(outcome)) {
@@ -496,7 +496,7 @@ class WorkflowOrchestrator {
496
496
 
497
497
  /** Skip the current OPTIONAL stage without running it. Gates anchored directly to a skipped
498
498
  * stage cannot fire, so they are recorded as skipped too rather than blocking forever. */
499
- skipStage({ taskId, stageId, reason, backfilled = false, now }: any = {}) {
499
+ skipStage({ taskId, stageId, reason, backfilled = false, now } = {}) {
500
500
  const run = this.requireRun(taskId);
501
501
  const node = this.expectOpenStage(run, stageId);
502
502
  if (!node.optional) throw new Error(`Stage '${node.id}' is required and cannot be skipped`);
@@ -539,7 +539,7 @@ class WorkflowOrchestrator {
539
539
  * (that stage mutates source and only its owner may complete it), or a terminal run state. An
540
540
  * optional non-candidate stage is skipped rather than completed, matching what its own skill
541
541
  * would have done with it. */
542
- catchUp({ taskId, taskClass, candidateStageIds, note, now }: any = {}) {
542
+ catchUp({ taskId, taskClass, candidateStageIds, note, now } = {}) {
543
543
  if (!Array.isArray(candidateStageIds) || candidateStageIds.length === 0) {
544
544
  throw new Error('catch-up requires at least one candidate stage id');
545
545
  }
@@ -561,11 +561,11 @@ class WorkflowOrchestrator {
561
561
  // through them instead of refusing.
562
562
  let unknownCandidates;
563
563
  if (run) {
564
- const programStageIds = new Set(run.program.filter((n: any) => n.type === 'stage').map((n: any) => n.id));
564
+ const programStageIds = new Set(run.program.filter((n ) => n.type === 'stage').map((n ) => n.id));
565
565
  unknownCandidates = candidateStageIds.filter((id) => !programStageIds.has(id));
566
566
  } else {
567
567
  const workflow = this.engine.resolveWorkflow(taskClass); // unknown classes are rejected loudly, same as start()
568
- const workflowStageIds = new Set((workflow.stages || []).map((s: any) => s.id));
568
+ const workflowStageIds = new Set((workflow.stages || []).map((s ) => s.id));
569
569
  unknownCandidates = candidateStageIds.filter((id) => !workflowStageIds.has(id));
570
570
  }
571
571
  if (unknownCandidates.length > 0) {
@@ -596,9 +596,9 @@ class WorkflowOrchestrator {
596
596
  // before the walk below can complete anything past it as a side effect. A caller with more than
597
597
  // one candidate (do-test owns two occurrences in `bug`/`refactor`) is not done until every
598
598
  // occurrence is: one completed and one still pending must still walk forward to the pending one.
599
- const candidateNodes = run.program.filter((n: any) => candidates.has(n.id));
600
- if (candidateNodes.length > 0 && candidateNodes.every((n: any) => n.status === 'completed' || n.status === 'approved' || n.status === 'skipped')) {
601
- return { ...this.snapshot(run), caughtUpTo: null, reason: `already-completed:${candidateNodes.map((n: any) => n.id).join(',')}` };
599
+ const candidateNodes = run.program.filter((n ) => candidates.has(n.id));
600
+ if (candidateNodes.length > 0 && candidateNodes.every((n ) => n.status === 'completed' || n.status === 'approved' || n.status === 'skipped')) {
601
+ return { ...this.snapshot(run), caughtUpTo: null, reason: `already-completed:${candidateNodes.map((n ) => n.id).join(',')}` };
602
602
  }
603
603
  for (;;) {
604
604
  if (run.state === 'COMPLETED' || run.state === 'REJECTED') {
@@ -640,7 +640,7 @@ class WorkflowOrchestrator {
640
640
  * `forced` decision (e.g. approving despite unresolved clarification markers) requires an
641
641
  * explicit `note` reason — never a silent override — and is flagged in history for
642
642
  * `render-audit.sh` to distinguish from a routine handoff. */
643
- decideGate({ taskId, gateId, decision, note, forced = false, now }: any = {}) {
643
+ decideGate({ taskId, gateId, decision, note, forced = false, now } = {}) {
644
644
  assertSafeId(gateId, 'gateId');
645
645
  if (!GATE_DECISIONS.includes(decision)) throw new Error(`Invalid gate decision '${decision}'; valid: ${GATE_DECISIONS.join(', ')}`);
646
646
  if (forced && (!note || !String(note).trim())) throw new Error('A forced gate decision requires a --note reason');
@@ -667,13 +667,13 @@ class WorkflowOrchestrator {
667
667
  * hand-edited after that stage's own handoff — without touching program/cursor/state. Not a
668
668
  * stage completion or a gate decision, so it never collides with the state machine's ordering
669
669
  * rules (D3). `forced` requires an explicit `note` reason, same rule as decideGate. */
670
- annotate({ taskId, node: nodeId, note, forced = false, consultations, now }: any = {}) {
670
+ annotate({ taskId, node: nodeId, note, forced = false, consultations, now } = {}) {
671
671
  // Id validated before the run is loaded, matching decideGate's order exactly: an unsafe id is
672
672
  // the caller's problem to hear about first, whether or not the run happens to exist.
673
673
  assertSafeId(nodeId, 'node');
674
674
  if (forced && (!note || !String(note).trim())) throw new Error('A forced annotation requires a --note reason');
675
675
  const run = this.requireRun(taskId);
676
- const found = run.program.find((n: any) => n.id === nodeId);
676
+ const found = run.program.find((n ) => n.id === nodeId);
677
677
  if (!found) throw new Error(`Invalid node: '${nodeId}' is not part of the program for run '${taskId}'`);
678
678
  run.history.push({ at: iso(now), action: 'annotate', node: nodeId, note, forced: Boolean(forced), ...(consultations ? { consultations } : {}) });
679
679
  this.writeRun(run, now);
@@ -682,7 +682,7 @@ class WorkflowOrchestrator {
682
682
 
683
683
  /** After any advance: walk past finished nodes, land on the next pending action point, and set
684
684
  * the run state from what is found there. Cursor always sits ON the node awaiting action. */
685
- settleCursor(run: any) {
685
+ settleCursor(run ) {
686
686
  for (;;) {
687
687
  if (run.cursor >= run.program.length) { run.state = runState('COMPLETED'); return; }
688
688
  const node = run.program[run.cursor];
@@ -693,7 +693,7 @@ class WorkflowOrchestrator {
693
693
  }
694
694
  }
695
695
 
696
- advance(run: any, now: any) {
696
+ advance(run , now ) {
697
697
  run.cursor += 1;
698
698
  this.settleCursor(run);
699
699
  if (run.state === 'COMPLETED') run.history.push({ at: iso(now), action: 'complete-run' });
@@ -701,7 +701,7 @@ class WorkflowOrchestrator {
701
701
 
702
702
  /** Refuses a gated stage unless the evaluator answers READY. The evaluator returns a verdict
703
703
  * string, or `{ verdict, message }`: `message` replaces the generic refusal. */
704
- assertReady(node: any, run: any) {
704
+ assertReady(node , run ) {
705
705
  if (!this.readinessEvaluate) {
706
706
  // Fail closed: without an evaluator there is no way to know the tree is safe to mutate.
707
707
  throw new Error(`Stage '${node.id}' is gated by the '${node.readinessTemplate}' readiness template but no readiness evaluator is wired into this orchestrator`);
@@ -717,7 +717,7 @@ class WorkflowOrchestrator {
717
717
  /** FR-006: the implementation stage of a run (the `do-execute-plan` stage that mutates source)
718
718
  * cannot complete while its feature's task-attempt ledger exists and is incomplete. Returns the
719
719
  * checker's answer for a stage the rule applies to, `null` otherwise. */
720
- assertTaskAttempts(node: any, run: any) {
720
+ assertTaskAttempts(node , run ) {
721
721
  if (!this.taskAttemptCheck || node.skill !== 'do-execute-plan' || !node.mutatesSource) return null;
722
722
  const answer = this.taskAttemptCheck(node, run);
723
723
  if (!answer || !['none', 'complete', 'incomplete'].includes(answer.state)) {
@@ -735,17 +735,17 @@ class WorkflowOrchestrator {
735
735
  * of a fresh run's) without writing anything. Same stop rules and reason vocabulary as `catchUp`;
736
736
  * `stageId` is what `catchUp` reports as `caughtUpTo`, and `node` the program node it stops on.
737
737
  * @returns {{reason: string, stageId: string|null, node: Object|null}} */
738
- previewHandoff({ taskId, taskClass, candidateStageIds }: any) {
738
+ previewHandoff({ taskId, taskClass, candidateStageIds } ) {
739
739
  const existing = this.readRun(taskId);
740
740
  const run = existing
741
741
  ? { state: existing.state, cursor: existing.cursor, program: structuredClone(existing.program) }
742
742
  : { state: 'RUNNING', cursor: 0, program: compileProgram(this.engine.resolveWorkflow(taskClass)) };
743
743
  const candidates = new Set(candidateStageIds);
744
- const stop = (reason: any, node: any = null) => ({ reason, stageId: reason === 'reached-candidate' ? node.id : null, node });
744
+ const stop = (reason , node = null) => ({ reason, stageId: reason === 'reached-candidate' ? node.id : null, node });
745
745
  if (run.state === 'REJECTED') return stop('run-rejected');
746
- const candidateNodes = run.program.filter((n: any) => candidates.has(n.id));
747
- if (candidateNodes.length > 0 && candidateNodes.every((n: any) => n.status === 'completed' || n.status === 'approved' || n.status === 'skipped')) {
748
- return stop(`already-completed:${candidateNodes.map((n: any) => n.id).join(',')}`);
746
+ const candidateNodes = run.program.filter((n ) => candidates.has(n.id));
747
+ if (candidateNodes.length > 0 && candidateNodes.every((n ) => n.status === 'completed' || n.status === 'approved' || n.status === 'skipped')) {
748
+ return stop(`already-completed:${candidateNodes.map((n ) => n.id).join(',')}`);
749
749
  }
750
750
  for (;;) {
751
751
  if (run.state === 'COMPLETED' || run.state === 'REJECTED') return stop(`run-${run.state.toLowerCase()}`);
@@ -775,7 +775,7 @@ class WorkflowOrchestrator {
775
775
  * @returns {{status:'compacted'|'unchanged'|'partial'|'skipped'|'failed', reason?:string, moved?:Array,
776
776
  * failed?:Array<{artifact:string, path:string, message:string}>}} `partial` carries `failed`, one entry per refused artifact
777
777
  */
778
- function compactAfterHandoff({ taskId, projectRoot }: any) {
778
+ function compactAfterHandoff({ taskId, projectRoot } ) {
779
779
  try {
780
780
  const feature = resolveActiveFeature({ projectRoot, slug: taskId });
781
781
  if (feature.error) return { status: 'skipped', reason: feature.message };
@@ -792,7 +792,7 @@ function compactAfterHandoff({ taskId, projectRoot }: any) {
792
792
  }
793
793
  if (result.finding) return { status: 'failed', reason: result.message };
794
794
  return { status: result.status, moved: result.moved };
795
- } catch (error: any) {
795
+ } catch (error ) {
796
796
  return { status: 'failed', reason: error.message };
797
797
  }
798
798
  }
@@ -804,8 +804,8 @@ function compactAfterHandoff({ taskId, projectRoot }: any) {
804
804
  function handleOrchestrateCommand({
805
805
  action = 'status', taskId, taskClass, stage, gate, node, decision, note, reason, forced = false,
806
806
  verificationPlan, scope, invariants, result, mcpOutcome, callingSkill, json = false, repoRoot, stateRoot, slug = null, buildAdapters = null,
807
- }: any = {}) {
808
- const { finishRuntime, usageError } = require('./cli-result.ts');
807
+ } = {}) {
808
+ const { finishRuntime, usageError } = require('./cli-result.js');
809
809
 
810
810
  const root = repoRoot || REPO_ROOT;
811
811
  // Same scope rule every other stateful command uses (evidenceRoot(o) in runtime-commands.js):
@@ -834,7 +834,7 @@ function handleOrchestrateCommand({
834
834
  stateDir: path.join(runRoot, '.doflow', 'state', 'orchestration'),
835
835
  featureSlug, buildAdapters,
836
836
  });
837
- orchestrator.readinessEvaluate = (node: any, run: any) => {
837
+ orchestrator.readinessEvaluate = (node , run ) => {
838
838
  // This closure only ever runs for a stage that is actually gated (evaluateReadiness only calls
839
839
  // it for a mutating stage carrying a readiness template) — so a missing --task-class here is a
840
840
  // caller mistake on THIS command, not a fact about the task's readiness. Reported before the
@@ -861,7 +861,7 @@ function handleOrchestrateCommand({
861
861
 
862
862
  // FR-006. The ledger lives under the main worktree, so the plan and the store are found from any
863
863
  // linked worktree; a run whose feature cannot be named has no ledger to check.
864
- orchestrator.taskAttemptCheck = (node: any, run: any) => checkTaskAttemptCompletion({
864
+ orchestrator.taskAttemptCheck = (node , run ) => checkTaskAttemptCompletion({
865
865
  slug: runSlugOf(run, runRoot) || featureSlug, cwd: state,
866
866
  });
867
867
 
@@ -942,7 +942,7 @@ function handleOrchestrateCommand({
942
942
  // MCP warnings go to stderr and notices to stdout, after everything else (IC-011).
943
943
  for (const message of snapshot.mcpWarnings || []) (message.text.startsWith('[WARN]') ? console.error : console.log)(message.text);
944
944
  return finishRuntime(0);
945
- } catch (error: any) {
945
+ } catch (error ) {
946
946
  // Transition refusals are findings, not crashes: out-of-order stages and unready gates are
947
947
  // exactly what an operator asked about when they ran this.
948
948
  console.error(`[ERROR] orchestrate: ${error.message}`);