@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
@@ -37,9 +37,9 @@ const FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})/;
37
37
  * are true when a fence or an HTML comment is never closed, so everything after it was swallowed;
38
38
  * `openAt` is the line index where that construct opened (-1 when none is left open)
39
39
  */
40
- function scan(lines: string[]): { kinds: string[]; openFence: boolean; openComment: boolean; openAt: number } {
41
- const kinds: string[] = new Array(lines.length);
42
- let fence: { ch: string; len: number } | null = null;
40
+ function scan(lines ) {
41
+ const kinds = new Array(lines.length);
42
+ let fence = null;
43
43
  let comment = false;
44
44
  let openedAt = -1;
45
45
  for (let i = 0; i < lines.length; i += 1) {
@@ -73,14 +73,14 @@ function scan(lines: string[]): { kinds: string[]; openFence: boolean; openComme
73
73
  * exact canonical pointer for this artifact is the pointer; any other line, including one that
74
74
  * merely looks like a pointer, moves, so no text is dropped.
75
75
  */
76
- function classify(bodyLines: string[], kinds: string[], canonicalPointer: string): { comments: string[][]; moved: string[]; pointer: string | null } {
77
- const comments: string[][] = [];
78
- const moved: string[] = [];
79
- let pointer: string | null = null;
80
- let block: string[] | null = null;
76
+ function classify(bodyLines , kinds , canonicalPointer ) {
77
+ const comments = [];
78
+ const moved = [];
79
+ let pointer = null;
80
+ let block = null;
81
81
  bodyLines.forEach((line, i) => {
82
82
  const kind = kinds[i];
83
- if (kind === 'comment-cont') { (block as string[]).push(line); return; }
83
+ if (kind === 'comment-cont') { (block ).push(line); return; }
84
84
  block = null;
85
85
  if (kind === 'comment') { block = [line]; comments.push(block); return; }
86
86
  if (kind === 'fence') { moved.push(line); return; }
@@ -93,19 +93,19 @@ function classify(bodyLines: string[], kinds: string[], canonicalPointer: string
93
93
  return { comments, moved, pointer };
94
94
  }
95
95
 
96
- function writeAtomic(fsImpl: any, file: string, content: string): void {
96
+ function writeAtomic(fsImpl , file , content ) {
97
97
  fsImpl.mkdirSync(path.dirname(file), { recursive: true });
98
98
  const tmp = `${file}.${process.pid}.tmp`;
99
99
  try {
100
100
  fsImpl.writeFileSync(tmp, content, 'utf8');
101
101
  fsImpl.renameSync(tmp, file);
102
- } catch (error: any) {
102
+ } catch (error ) {
103
103
  try { fsImpl.rmSync(tmp, { force: true }); } catch { /* nothing to clean */ }
104
104
  throw error;
105
105
  }
106
106
  }
107
107
 
108
- function toPosix(p: string): string {
108
+ function toPosix(p ) {
109
109
  return p.split(path.sep).join('/');
110
110
  }
111
111
 
@@ -119,7 +119,7 @@ function toPosix(p: string): string {
119
119
  * a line inside a code fence, and that must not be read as the start of a chunk. The dedupe and the
120
120
  * post-write confirmation both use this one predicate.
121
121
  */
122
- function lastChunkIs(archiveText: string, blockLF: string): boolean {
122
+ function lastChunkIs(archiveText , blockLF ) {
123
123
  const lines = archiveText.replace(/\r\n/g, '\n').split('\n');
124
124
  const { kinds } = scan(lines);
125
125
  let header = -1;
@@ -135,25 +135,25 @@ function lastChunkIs(archiveText: string, blockLF: string): boolean {
135
135
  * @returns {{artifact:string, path:string, archive:string, lines:number}|null} null when there was
136
136
  * nothing to move
137
137
  */
138
- function compactArtifact({ fsImpl, featureDir, file, date }: { fsImpl: any; featureDir: string; file: string; date: string }): { artifact: string; path: string; archive: string; lines: number } | null {
138
+ function compactArtifact({ fsImpl, featureDir, file, date } ) {
139
139
  const text = fsImpl.readFileSync(file, 'utf8');
140
140
  const crlf = text.includes('\r\n');
141
141
  const lines = text.split('\n');
142
142
  const { kinds, openFence, openComment, openAt } = scan(lines);
143
- const unclosed: string | null = openFence ? 'a code fence' : openComment ? 'an HTML comment' : null;
144
- const refuse = (): never => {
143
+ const unclosed = openFence ? 'a code fence' : openComment ? 'an HTML comment' : null;
144
+ const refuse = () => {
145
145
  throw new Error(`${unclosed} is never closed, so the end of the History section cannot be told`);
146
146
  };
147
- const start = lines.findIndex((l: string, i: number) => kinds[i] === 'text' && HISTORY_HEADING.test(l));
147
+ const start = lines.findIndex((l , i ) => kinds[i] === 'text' && HISTORY_HEADING.test(l));
148
148
  // An unclosed construct swallows everything after it. It matters only when it opens before the
149
149
  // History section ends (FR-001): then the section's end cannot be told. A History heading it
150
150
  // swallowed is such a case; one that simply is not there, or that closed before the construct
151
151
  // opened, is not, and the construct is treated as absent.
152
152
  if (start === -1) {
153
- if (unclosed && lines.some((l: string, i: number) => i > openAt && HISTORY_HEADING.test(l))) refuse();
153
+ if (unclosed && lines.some((l , i ) => i > openAt && HISTORY_HEADING.test(l))) refuse();
154
154
  return null;
155
155
  }
156
- const section = Number((HISTORY_HEADING.exec(lines[start]) as RegExpExecArray)[1]);
156
+ const section = Number((HISTORY_HEADING.exec(lines[start]) )[1]);
157
157
  let end = lines.length;
158
158
  for (let i = start + 1; i < lines.length; i += 1) {
159
159
  if (kinds[i] === 'text' && ANY_H2.test(lines[i])) { end = i; break; }
@@ -172,7 +172,7 @@ function compactArtifact({ fsImpl, featureDir, file, date }: { fsImpl: any; feat
172
172
  // Chunks are built with LF and converted to the artifact's own line ending on write, so a CRLF
173
173
  // artifact gets a CRLF archive and no file ends up mixed.
174
174
  const eol = crlf ? '\r\n' : '\n';
175
- const toEol = (t: string) => (crlf ? t.replace(/\n/g, '\r\n') : t);
175
+ const toEol = (t ) => (crlf ? t.replace(/\n/g, '\r\n') : t);
176
176
  const blockLF = moved.map((l) => l.replace(/\r$/, '')).join('\n');
177
177
  const chunk = `## Compacted ${date} from ${rel} §${section}\n\n${blockLF}\n`;
178
178
  const existing = fsImpl.existsSync(archiveFile) ? fsImpl.readFileSync(archiveFile, 'utf8') : null;
@@ -213,18 +213,18 @@ function compactArtifact({ fsImpl, featureDir, file, date }: { fsImpl: any; feat
213
213
  * @returns {{status:'compacted'|'unchanged'|'partial', moved:Object[], failed:Array<{artifact:string, path:string, message:string}>}}
214
214
  * `partial` means at least one artifact failed
215
215
  */
216
- function compactHistory({ repoRoot, featureDir, paths, date, fsImpl = nodeFs }: { repoRoot: string; featureDir: string; paths: any; date?: string; fsImpl?: any }): { status: 'compacted' | 'unchanged' | 'partial'; moved: any[]; failed: Array<{ artifact: string; path: string; message: string }> } {
216
+ function compactHistory({ repoRoot, featureDir, paths, date, fsImpl = nodeFs } ) {
217
217
  const stamp = date || new Date().toISOString().slice(0, 10);
218
- const moved: any[] = [];
219
- const failed: any[] = [];
218
+ const moved = [];
219
+ const failed = [];
220
220
  for (const key of TARGET_KEYS) {
221
221
  if (!paths || !paths[key]) continue;
222
222
  const file = path.resolve(repoRoot, paths[key]);
223
223
  if (!fsImpl.existsSync(file)) continue;
224
- let result: any;
224
+ let result ;
225
225
  try {
226
226
  result = compactArtifact({ fsImpl, featureDir, file, date: stamp });
227
- } catch (error: any) {
227
+ } catch (error ) {
228
228
  const name = toPosix(path.relative(featureDir, file));
229
229
  failed.push({ artifact: path.basename(file), path: name, message: `could not compact ${name}: ${error.message}; the artifact was left unchanged` });
230
230
  continue;
@@ -4,12 +4,12 @@
4
4
 
5
5
  const fs = require('node:fs');
6
6
  const path = require('node:path');
7
- const { readReadinessRecord, newerVersionOf, RECORD_VERSION } = require('./readiness-record.ts');
8
- const { listCheckouts } = require('./checkouts.ts');
9
- const { readTaskState } = require('./task-state.ts');
10
- const { resolveActiveFeature } = require('./feature-resolve.ts');
11
- const { isSafeSlug } = require('./task-scope.ts');
12
- const { REPO_ROOT } = require('../helper/repo-root.ts');
7
+ const { readReadinessRecord, newerVersionOf, RECORD_VERSION } = require('./readiness-record.js');
8
+ const { listCheckouts } = require('./checkouts.js');
9
+ const { readTaskState } = require('./task-state.js');
10
+ const { resolveActiveFeature } = require('./feature-resolve.js');
11
+ const { isSafeSlug } = require('./task-scope.js');
12
+ const { REPO_ROOT } = require('../helper/repo-root.js');
13
13
 
14
14
  // The implementation gate: which stage of a workflow needs a READY readiness record before it is
15
15
  // handed off, whether a task has one, and the words every refusal uses.
@@ -30,8 +30,8 @@ const NEWER_NEXT = 'run this command with the DoFlow that wrote the record, or u
30
30
  * @param {{editTime?: boolean}} [options]
31
31
  * @returns {{id: string, readinessTemplate: string, editTimeGate: boolean}|null}
32
32
  */
33
- function gatedStage(stages: any, { editTime = false }: any = {}) {
34
- const stage = (stages || []).find((s: any) => s && s.mutatesSource === true
33
+ function gatedStage(stages , { editTime = false } = {}) {
34
+ const stage = (stages || []).find((s ) => s && s.mutatesSource === true
35
35
  && typeof s.readinessTemplate === 'string' && s.readinessTemplate !== ''
36
36
  && (!editTime || s.editTimeGate !== false));
37
37
  return stage ? { id: stage.id, readinessTemplate: stage.readinessTemplate, editTimeGate: stage.editTimeGate !== false } : null;
@@ -43,12 +43,12 @@ function gatedStage(stages: any, { editTime = false }: any = {}) {
43
43
  * @param {{editTime?: boolean}} [options]
44
44
  * @returns {Array<string>}
45
45
  */
46
- function heldClasses(engine: any, { editTime = false }: any = {}) {
47
- return engine.listClasses().filter((cls: any) => gatedStage(engine.resolveWorkflow(cls).stages, { editTime }) !== null);
46
+ function heldClasses(engine , { editTime = false } = {}) {
47
+ return engine.listClasses().filter((cls ) => gatedStage(engine.resolveWorkflow(cls).stages, { editTime }) !== null);
48
48
  }
49
49
 
50
50
  /** The command that clears every readiness refusal. */
51
- function nextCommand({ taskId, slug, template }: any) {
51
+ function nextCommand({ taskId, slug, template } ) {
52
52
  const withSlug = slug && slug !== taskId ? ` --slug=${slug}` : '';
53
53
  return `doflow-run readiness --task-class ${template} --task-id ${taskId}${withSlug}`;
54
54
  }
@@ -68,7 +68,7 @@ function nextCommand({ taskId, slug, template }: any) {
68
68
  * replace such a record, so the text does not send the caller there
69
69
  * @returns {string}
70
70
  */
71
- function refusalText(code: any, { taskId, slug = null, template, record = null, candidates = [], detail = null, file = null, newerVersion = null }: any): string {
71
+ function refusalText(code , { taskId, slug = null, template, record = null, candidates = [], detail = null, file = null, newerVersion = null } ) {
72
72
  const next = nextCommand({ taskId, slug, template });
73
73
  const tail = `Next: ${next}, then gather what it lists until it reports READY. Nothing was changed.`;
74
74
  if (code === 'unusable' && newerVersion !== null) {
@@ -103,14 +103,14 @@ function refusalText(code: any, { taskId, slug = null, template, record = null,
103
103
  * @param {{repoRoot?: string}} [options]
104
104
  * @returns {string}
105
105
  */
106
- function harnessHookNote({ repoRoot = REPO_ROOT }: any = {}): string {
106
+ function harnessHookNote({ repoRoot = REPO_ROOT } = {}) {
107
107
  try {
108
108
  const { policies } = JSON.parse(fs.readFileSync(path.join(repoRoot, 'core', 'registry', 'lifecycle.json'), 'utf8'));
109
- const { mappings } = policies.find((p: any) => p.id === 'pre-implementation-gate');
109
+ const { mappings } = policies.find((p ) => p.id === 'pre-implementation-gate');
110
110
  const runs = [];
111
111
  const gated = new Map();
112
112
  const none = [];
113
- for (const [id, m] of (Object.entries(mappings) as [string, any][])) {
113
+ for (const [id, m] of (Object.entries(mappings) )) {
114
114
  const prerequisites = Array.isArray(m.prerequisites) ? m.prerequisites : [];
115
115
  if (m.status === 'unavailable' || (m.status !== 'supported' && m.status !== 'different')) none.push(id);
116
116
  else if (prerequisites.length === 0) runs.push(id);
@@ -129,17 +129,17 @@ function harnessHookNote({ repoRoot = REPO_ROOT }: any = {}): string {
129
129
  }
130
130
  }
131
131
 
132
- const isDir = (dir: any) => {
132
+ const isDir = (dir ) => {
133
133
  try {
134
134
  return fs.statSync(dir).isDirectory();
135
135
  } catch {
136
136
  return false;
137
137
  }
138
138
  };
139
- const folderOf = (root: any, slug: any) => path.join(root, 'agent-docs', 'doflow', slug);
139
+ const folderOf = (root , slug ) => path.join(root, 'agent-docs', 'doflow', slug);
140
140
 
141
141
  /** The branch's feature slug as the resolver names it, or null. */
142
- function branchFeatureSlug(cwd: any) {
142
+ function branchFeatureSlug(cwd ) {
143
143
  try {
144
144
  const found = resolveActiveFeature({ projectRoot: cwd });
145
145
  const s = !found.error && found.paths ? found.paths.feature_slug : null;
@@ -155,7 +155,7 @@ function branchFeatureSlug(cwd: any) {
155
155
  * @param {{projectRoot: string, taskId?: string|null, slug?: string|null}} options
156
156
  * @returns {string|null}
157
157
  */
158
- function featureSlugFor({ projectRoot, taskId = null, slug = null }: any) {
158
+ function featureSlugFor({ projectRoot, taskId = null, slug = null } ) {
159
159
  if (slug) return slug;
160
160
  if (isSafeSlug(taskId) && isDir(folderOf(projectRoot, taskId))) return taskId;
161
161
  return branchFeatureSlug(projectRoot);
@@ -163,7 +163,7 @@ function featureSlugFor({ projectRoot, taskId = null, slug = null }: any) {
163
163
 
164
164
  /** The feature a run belongs to: the slug recorded when it started, else its task id when that names
165
165
  * a feature folder in the checkout holding the run. */
166
- function runSlugOf(run: any, root: any) {
166
+ function runSlugOf(run , root ) {
167
167
  if (run && typeof run.featureSlug === 'string' && run.featureSlug !== '') return run.featureSlug;
168
168
  return run && isSafeSlug(run.taskId) && isDir(folderOf(root, run.taskId)) ? run.taskId : null;
169
169
  }
@@ -176,7 +176,7 @@ function runSlugOf(run: any, root: any) {
176
176
  * @returns {{status: 'found'|'missing'|'ambiguous', file: string|null, root: string|null,
177
177
  * origin: 'current'|'other'|null, candidates: Array<string>}}
178
178
  */
179
- function findRunFile({ stateRoot, taskId, slug, exec }: any) {
179
+ function findRunFile({ stateRoot, taskId, slug, exec } ) {
180
180
  const rel = path.join('.doflow', 'state', 'orchestration', `${taskId}.json`);
181
181
  const missing = { status: 'missing', file: null, root: null, origin: null, candidates: [] };
182
182
  if (fs.existsSync(path.join(stateRoot, rel))) {
@@ -185,7 +185,7 @@ function findRunFile({ stateRoot, taskId, slug, exec }: any) {
185
185
  if (!slug) return missing;
186
186
  const checkouts = listCheckouts({ cwd: stateRoot, ...(exec ? { exec } : {}) });
187
187
  if (!checkouts.ok || checkouts.sandbox) return missing;
188
- const matches = checkouts.others.filter((root: any) => {
188
+ const matches = checkouts.others.filter((root ) => {
189
189
  const file = path.join(root, rel);
190
190
  if (!fs.existsSync(file)) return false;
191
191
  try {
@@ -195,13 +195,13 @@ function findRunFile({ stateRoot, taskId, slug, exec }: any) {
195
195
  }
196
196
  });
197
197
  if (matches.length === 0) return missing;
198
- if (matches.length > 1) return { ...missing, status: 'ambiguous', candidates: matches.map((root: any) => path.join(root, rel)) };
198
+ if (matches.length > 1) return { ...missing, status: 'ambiguous', candidates: matches.map((root ) => path.join(root, rel)) };
199
199
  return { status: 'found', file: path.join(matches[0], rel), root: matches[0], origin: 'other', candidates: [] };
200
200
  }
201
201
 
202
202
  /** The slug a refusal's next command names: one whose feature folder exists in this checkout or in
203
203
  * the main checkout, which is when `readiness --slug` resolves it the same way the edit hook does. */
204
- function nextSlugFor({ stateRoot, taskId, slug, exec }: any) {
204
+ function nextSlugFor({ stateRoot, taskId, slug, exec } ) {
205
205
  if (!slug || slug === taskId) return null;
206
206
  if (isDir(folderOf(stateRoot, slug))) return slug;
207
207
  const checkouts = listCheckouts({ cwd: stateRoot, ...(exec ? { exec } : {}) });
@@ -209,7 +209,7 @@ function nextSlugFor({ stateRoot, taskId, slug, exec }: any) {
209
209
  }
210
210
 
211
211
  /** Why a record cannot be read as an evaluation, or null when it can. */
212
- function unusableDetail(record: any, now: any) {
212
+ function unusableDetail(record , now ) {
213
213
  if (!record || typeof record !== 'object' || Array.isArray(record)) return 'not a JSON object';
214
214
  if (record.version !== RECORD_VERSION) return `version ${JSON.stringify(record.version)}, this runtime reads ${RECORD_VERSION}`;
215
215
  for (const key of ['state', 'taskClass', 'evaluatedAt']) {
@@ -235,11 +235,11 @@ function unusableDetail(record: any, now: any) {
235
235
  * record: Object|null, file: string|null, origin: string|null, candidates: Array<string>,
236
236
  * detail: string|null, nextSlug: string|null, message: string|null}}
237
237
  */
238
- function checkReadiness({ stateRoot, taskId, slug = null, template, now = new Date(), exec }: any) {
238
+ function checkReadiness({ stateRoot, taskId, slug = null, template, now = new Date(), exec } ) {
239
239
  const read = readReadinessRecord({ stateRoot, taskId, slug, exec });
240
240
  const newerVersion = read.status === 'found' ? newerVersionOf(read.record) : null;
241
241
  const nextSlug = nextSlugFor({ stateRoot, taskId, slug, exec });
242
- const result = (code: any, detail: any = null) => ({
242
+ const result = (code , detail = null) => ({
243
243
  ok: code === 'ready',
244
244
  code,
245
245
  record: read.record,
@@ -274,7 +274,7 @@ function checkReadiness({ stateRoot, taskId, slug = null, template, now = new Da
274
274
  * @returns {Object} `{applies: false, reason}` or `{applies: true, runTaskId, stage, template, ok, code,
275
275
  * record, message}`
276
276
  */
277
- function verifyReadinessCheck({ stateRoot, taskId, slug = null, now = new Date(), exec }: any) {
277
+ function verifyReadinessCheck({ stateRoot, taskId, slug = null, now = new Date(), exec } ) {
278
278
  let found = findRunFile({ stateRoot, taskId, slug, exec });
279
279
  if (found.status === 'missing' && slug && slug !== taskId) found = findRunFile({ stateRoot, taskId: slug, slug, exec });
280
280
  if (found.status === 'ambiguous') {
@@ -286,10 +286,10 @@ function verifyReadinessCheck({ stateRoot, taskId, slug = null, now = new Date()
286
286
  if (found.status === 'missing') return { applies: false, reason: 'no-run' };
287
287
  const run = readTaskState(fs, found.file);
288
288
  if (run.state === 'COMPLETED' || run.state === 'REJECTED') return { applies: false, reason: 'run-finished' };
289
- const stageNodes = (run.program || []).filter((n: any) => n.type === 'stage');
289
+ const stageNodes = (run.program || []).filter((n ) => n.type === 'stage');
290
290
  const stage = gatedStage(stageNodes);
291
291
  if (!stage) return { applies: false, reason: 'no-gated-stage' };
292
- const node = stageNodes.find((n: any) => n.id === stage.id);
292
+ const node = stageNodes.find((n ) => n.id === stage.id);
293
293
  if (node.status === 'completed' || node.status === 'skipped') return { applies: false, reason: 'gated-stage-done' };
294
294
 
295
295
  const template = stage.readinessTemplate;
@@ -45,7 +45,7 @@
45
45
  */
46
46
 
47
47
  const os = require('node:os');
48
- const { readLock } = require('../../state/lockfile.ts');
48
+ const { readLock } = require('../../state/lockfile.js');
49
49
 
50
50
  /** The scopes a report is assembled from. Mirrors `readScopes`'s pair; a finding naming anything
51
51
  * else is a caller bug and is reported as such rather than silently counted either way. */
@@ -111,9 +111,9 @@ const REPAIR_REASONS = Object.freeze({
111
111
 
112
112
  /** The harnesses a lock document pins, and how it pins them. Mirrors `reconcile`'s own reading:
113
113
  * a missing lock and a lock with an empty `targets` array are equally unable to repair anything. */
114
- function scopePin(scope: any, lock: any): any {
114
+ function scopePin(scope , lock ) {
115
115
  const targets = Array.isArray(lock?.targets)
116
- ? lock.targets.map((entry: any) => entry?.harness).filter((harness: any) => typeof harness === 'string' && harness !== '')
116
+ ? lock.targets.map((entry ) => entry?.harness).filter((harness ) => typeof harness === 'string' && harness !== '')
117
117
  : [];
118
118
  const state = !lock ? 'absent' : (targets.length === 0 ? 'empty' : 'present');
119
119
  return { scope, lock: state, pinned: state === 'present', targets, findings: 0, repairable: 0 };
@@ -122,7 +122,7 @@ function scopePin(scope: any, lock: any): any {
122
122
  /** Decide one finding against the pins. Repair-path reach is tested before lock reach: a finding
123
123
  * no command performs is unreachable for a more fundamental reason than a narrow pin, and naming
124
124
  * the pin would imply widening it is the fix. */
125
- function classify(finding: any, pins: any): any {
125
+ function classify(finding , pins ) {
126
126
  const pin = pins[finding?.scope];
127
127
  if (!pin) return { repairable: false, reason: REPAIR_REASONS.UNKNOWN_SCOPE };
128
128
  const declared = finding.repairPath;
@@ -139,7 +139,7 @@ function classify(finding: any, pins: any): any {
139
139
 
140
140
  /** One sentence a reader meets before the detail, so the gap cannot be missed by someone who does
141
141
  * not read the counts. States what is repairable and by what; never suggests a way to widen it. */
142
- function summarize(total: number, repairable: number): string {
142
+ function summarize(total , repairable ) {
143
143
  if (total === 0) return 'No findings — repair coverage does not arise.';
144
144
  if (repairable === total) {
145
145
  return `All ${total} finding(s) are repairable by \`doflow reconcile\`.`;
@@ -164,7 +164,7 @@ function summarize(total: number, repairable: number): string {
164
164
  * @throws when a lock file exists but cannot be parsed or validated. Propagated deliberately:
165
165
  * `reconcile` would fail on the same document, so guessing its reach would be fiction.
166
166
  */
167
- function readScopeLocks({ projectRoot, homeDir = os.homedir() }: any, { fsImpl }: any = {}): any {
167
+ function readScopeLocks({ projectRoot, homeDir = os.homedir() } , { fsImpl } = {}) {
168
168
  return {
169
169
  global: readLock({ scope: 'global', homeDir }, { fsImpl }),
170
170
  project: readLock({ scope: 'project', projectRoot }, { fsImpl }),
@@ -180,12 +180,12 @@ function readScopeLocks({ projectRoot, homeDir = os.homedir() }: any, { fsImpl }
180
180
  * as `readScopeLocks` returns them. A missing or `null` entry is an unpinned scope.
181
181
  * @returns {RepairCoverage}
182
182
  */
183
- function repairCoverage({ findings = [], locks = {} }: any = {}): any {
183
+ function repairCoverage({ findings = [], locks = {} } = {}) {
184
184
  const rows = Array.isArray(findings) ? findings : [];
185
185
  const pins = Object.fromEntries(SCOPES.map((scope) => [scope, scopePin(scope, locks?.[scope] ?? null)]));
186
186
 
187
- const reasons: any = {};
188
- const details = rows.map((finding: any) => {
187
+ const reasons = {};
188
+ const details = rows.map((finding ) => {
189
189
  const { repairable, reason } = classify(finding, pins);
190
190
  const pin = pins[finding?.scope];
191
191
  if (pin) {
@@ -202,7 +202,7 @@ function repairCoverage({ findings = [], locks = {} }: any = {}): any {
202
202
  };
203
203
  });
204
204
 
205
- const repairable = details.filter((row: any) => row.repairable).length;
205
+ const repairable = details.filter((row ) => row.repairable).length;
206
206
  return {
207
207
  findings: details.length,
208
208
  repairable,
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * Two recorded resources describe the same logical asset when two parts agree: the harness, and the
9
9
  * ownership identity the adapter recorded for that resource. That pair is not a new key — it is the
10
- * stored ownership key (`src/state/index.ts#ownershipKey`, five components) with its two
10
+ * stored ownership key (`src/state/index.js#ownershipKey`, five components) with its two
11
11
  * scope-bearing components dropped: the `scope`, which names itself, and the absolute `target`,
12
12
  * which differs between a global and a project copy of the same asset by construction. So this is a
13
13
  * projection of a key the ledger already holds, not a value reconstructed from a path; nothing is
@@ -53,7 +53,7 @@
53
53
  * user can hand-edit. `null` is a signal to the caller to report the row as unresolved, never a
54
54
  * value to join or compare against another `null`.
55
55
  */
56
- function deriveAssetIdentity(resource: any): string | null {
56
+ function deriveAssetIdentity(resource ) {
57
57
  if (!resource || typeof resource !== 'object') return null;
58
58
  const { harness, ownershipIdentity } = resource;
59
59
  if (typeof harness !== 'string' || harness === '') return null;
@@ -59,13 +59,13 @@
59
59
  const fs = require('node:fs');
60
60
  const path = require('node:path');
61
61
  const crypto = require('node:crypto');
62
- const { harnessFor, loadRegistry } = require('../../registry/index.ts');
63
- const { readScopes } = require('./read-scopes.ts');
64
- const { deriveAssetIdentity } = require('./identity.ts');
65
- const { resolveShadowVerdict } = require('./precedence.ts');
66
- const { inspectSiblings } = require('./siblings.ts');
67
- const { repairCoverage, readScopeLocks } = require('./coverage.ts');
68
- const { finishRuntime, usageError } = require('../cli-result.ts');
62
+ const { harnessFor, loadRegistry } = require('../../registry/index.js');
63
+ const { readScopes } = require('./read-scopes.js');
64
+ const { deriveAssetIdentity } = require('./identity.js');
65
+ const { resolveShadowVerdict } = require('./precedence.js');
66
+ const { inspectSiblings } = require('./siblings.js');
67
+ const { repairCoverage, readScopeLocks } = require('./coverage.js');
68
+ const { finishRuntime, usageError } = require('../cli-result.js');
69
69
 
70
70
  /** The three currency values of IC-002, and never a fourth. The third is not a weaker second: a
71
71
  * copy nothing in this scope's plan spoke about has not "diverged", it cannot be judged at all.
@@ -110,14 +110,14 @@ const RECONCILE = 'reconcile';
110
110
  const MANUAL = 'manual';
111
111
 
112
112
  /** Report vocabulary -> registry vocabulary (`scopePrecedence.order` names the global scope `user`). */
113
- function toRegistryScope(scope: any): any { return scope === 'global' ? 'user' : scope; }
113
+ function toRegistryScope(scope ) { return scope === 'global' ? 'user' : scope; }
114
114
  /** Registry vocabulary -> report vocabulary. The inverse of the above, applied to every scope name
115
115
  * `precedence.js` hands back, so a verdict never names a scope the rest of the report does not. */
116
- function toReportScope(scope: any): any { return scope === 'user' ? 'global' : scope; }
116
+ function toReportScope(scope ) { return scope === 'user' ? 'global' : scope; }
117
117
 
118
118
  /** The four fields that identify one recorded resource, and the same four an adapter change carries.
119
119
  * Not `ownershipKey` from src/state: that one includes the scope, and a plan change has none. */
120
- function recordKey(record: any): string {
120
+ function recordKey(record ) {
121
121
  return [record?.harness, record?.assetId, record?.target, record?.ownershipIdentity].join('\u0000');
122
122
  }
123
123
 
@@ -141,7 +141,7 @@ function recordKey(record: any): string {
141
141
  * component boundaries here. A prerequisite is deliberately *not* disqualifying — it says the change
142
142
  * cannot yet be applied, not that the plan could not determine it.
143
143
  */
144
- function planBasis(snapshot: any): any {
144
+ function planBasis(snapshot ) {
145
145
  const changes = new Map();
146
146
  for (const change of snapshot?.plan?.changes ?? []) {
147
147
  if (change && typeof change === 'object') changes.set(recordKey(change), change);
@@ -181,7 +181,7 @@ function planBasis(snapshot: any): any {
181
181
  * @returns {boolean} false whenever the destination cannot be read, or the change names no
182
182
  * fingerprint — an absent or unreadable file is no evidence that its content is current.
183
183
  */
184
- function contentAlreadyCurrent(change: any, fsImpl: any): boolean {
184
+ function contentAlreadyCurrent(change , fsImpl ) {
185
185
  const intended = change?.fingerprint;
186
186
  if (typeof intended !== 'string' || intended === '') return false;
187
187
  let observed;
@@ -231,7 +231,7 @@ function contentAlreadyCurrent(change: any, fsImpl: any): boolean {
231
231
  * `null` for the other two values — a copy that was judged needs no basis, and a diverged one's
232
232
  * remedy is the restore `remedyFor` already states.
233
233
  */
234
- function currencyOf(resource: any, basis: any, fsImpl: any = fs): any {
234
+ function currencyOf(resource , basis , fsImpl = fs) {
235
235
  const recorded = resource?.fingerprint;
236
236
  if (typeof recorded !== 'string' || recorded === '') {
237
237
  return {
@@ -294,13 +294,13 @@ function currencyOf(resource: any, basis: any, fsImpl: any = fs): any {
294
294
  }
295
295
 
296
296
  /** The literal command that would converge one scope. Text only — this verb never runs it. */
297
- function reconcileCommand(scope: any, projectRoot: any): string {
297
+ function reconcileCommand(scope , projectRoot ) {
298
298
  return scope === 'global' ? 'doflow reconcile -g' : `doflow reconcile ${projectRoot}`;
299
299
  }
300
300
 
301
301
  /** One copy of a logical asset, as IC-002 requires: where it is, and how current it is — plus, where
302
302
  * it could not be judged at all, what would give a later run a basis for judging it. */
303
- function copyEntry(resource: any, scope: any, basis: any, fsImpl: any): any {
303
+ function copyEntry(resource , scope , basis , fsImpl ) {
304
304
  const currency = currencyOf(resource, basis, fsImpl);
305
305
  return {
306
306
  scope,
@@ -321,9 +321,9 @@ function copyEntry(resource: any, scope: any, basis: any, fsImpl: any): any {
321
321
  * @returns {{groups: Map<string, Object>, unidentified: Array<Object>}} `unidentified` holds one
322
322
  * row per resource whose identity could not be derived — never grouped, since `null` is not a key.
323
323
  */
324
- function groupByIdentity(snapshots: any, fsImpl: any): any {
324
+ function groupByIdentity(snapshots , fsImpl ) {
325
325
  const groups = new Map();
326
- const unidentified: any[] = [];
326
+ const unidentified = [];
327
327
  for (const snapshot of snapshots) {
328
328
  const basis = planBasis(snapshot);
329
329
  for (const resource of snapshot.resources ?? []) {
@@ -351,15 +351,15 @@ function groupByIdentity(snapshots: any, fsImpl: any): any {
351
351
  /** The harness's recorded precedence block, or `null` when it declares none — the fifth IC-003
352
352
  * state (the question has not been asked), which `precedence.js` must see as an absence rather than
353
353
  * as an empty object it could misread as a block with no mode. */
354
- function precedenceFor(registry: any, harness: any): any {
354
+ function precedenceFor(registry , harness ) {
355
355
  try { return harnessFor(registry, harness).scopePrecedence ?? null; }
356
356
  catch { return null; }
357
357
  }
358
358
 
359
359
  /** Every unmanaged entry sharing a directory with one of this asset's copies, attributed to the
360
360
  * scope whose copy put it in view. Never carries a currency (data model: `UNMANAGED_CANDIDATE`). */
361
- function unmanagedFor(copies: any, candidatesByTarget: any): any[] {
362
- const found: any[] = [];
361
+ function unmanagedFor(copies , candidatesByTarget ) {
362
+ const found = [];
363
363
  for (const copy of copies) {
364
364
  for (const candidate of candidatesByTarget[copy.location] ?? []) {
365
365
  found.push({ ...candidate, scope: copy.scope });
@@ -371,8 +371,8 @@ function unmanagedFor(copies: any, candidatesByTarget: any): any[] {
371
371
  /** The literal actions that would resolve this asset's findings, in the order a reader should take
372
372
  * them. One object per asset (IC-002's "the remedy"), holding every step rather than the first, so
373
373
  * an asset that is both diverged and shadowed does not have half its remedy silently dropped. */
374
- function remedyFor({ copies, verdict, unmanaged, projectRoot }: any): any {
375
- const actions: any[] = [];
374
+ function remedyFor({ copies, verdict, unmanaged, projectRoot } ) {
375
+ const actions = [];
376
376
  for (const copy of copies) {
377
377
  if (copy.currency === CURRENCY.DIVERGED) {
378
378
  actions.push({
@@ -393,7 +393,7 @@ function remedyFor({ copies, verdict, unmanaged, projectRoot }: any): any {
393
393
  }
394
394
  }
395
395
  if (verdict?.shadowed && verdict.winner) {
396
- const losers = copies.filter((copy: any) => copy.scope !== verdict.winner).map((copy: any) => copy.location);
396
+ const losers = copies.filter((copy ) => copy.scope !== verdict.winner).map((copy ) => copy.location);
397
397
  actions.push({
398
398
  finding: FINDING_KINDS.SHADOW,
399
399
  action: `edit the copy the harness actually loads — the ${verdict.winner} one — or delete the `
@@ -404,7 +404,7 @@ function remedyFor({ copies, verdict, unmanaged, projectRoot }: any): any {
404
404
  if (verdict?.shadowed && !verdict.winner) {
405
405
  actions.push({
406
406
  finding: FINDING_KINDS.WITHHELD_VERDICT,
407
- action: `keep exactly one copy — ${copies.map((copy: any) => copy.location).join(' or ')} — because `
407
+ action: `keep exactly one copy — ${copies.map((copy ) => copy.location).join(' or ')} — because `
408
408
  + `${verdict.withheldReason}, so which one takes effect cannot be stated`,
409
409
  command: null,
410
410
  });
@@ -449,8 +449,8 @@ function remedyFor({ copies, verdict, unmanaged, projectRoot }: any): any {
449
449
  * finding about it. The harness is in the key because a directory two harnesses both load
450
450
  * wholesale makes the same file operative twice, which is two facts.
451
451
  */
452
- function findingsFor({ identity, harness, copies, verdict, unmanaged }: any, seenUnmanaged: any): any[] {
453
- const findings: any[] = [];
452
+ function findingsFor({ identity, harness, copies, verdict, unmanaged } , seenUnmanaged ) {
453
+ const findings = [];
454
454
  for (const copy of copies) {
455
455
  if (copy.currency === CURRENCY.DIVERGED) {
456
456
  findings.push({ id: identity, kind: FINDING_KINDS.DIVERGENCE, harness, scope: copy.scope, repairPath: RECONCILE });
@@ -484,7 +484,7 @@ function findingsFor({ identity, harness, copies, verdict, unmanaged }: any, see
484
484
  * A restriction to named harnesses is named in the sentence, because the sentence is where a reader
485
485
  * decides whether to act. "nothing requires attention" read off a run restricted to one harness — or
486
486
  * to a harness that is not installed — would otherwise be taken for a statement about the install. */
487
- function summarize({ findings, assets, unidentified, targets }: any): string {
487
+ function summarize({ findings, assets, unidentified, targets } ) {
488
488
  const counts = new Map();
489
489
  for (const finding of findings) counts.set(finding.kind, (counts.get(finding.kind) ?? 0) + 1);
490
490
  const subject = `${assets.length} logical asset(s)${targets ? ` recorded for ${targets.join(', ')}` : ''}`;
@@ -531,7 +531,7 @@ function summarize({ findings, assets, unidentified, targets }: any): string {
531
531
  * exists but cannot be read is the same class as a ledger that cannot: the report would have to
532
532
  * invent the selection the plan is computed from.
533
533
  */
534
- function buildInventoryReport({ repoRoot, projectRoot, targets, registry, locks, fsImpl }: any = {}): any {
534
+ function buildInventoryReport({ repoRoot, projectRoot, targets, registry, locks, fsImpl } = {}) {
535
535
  const loaded = registry ?? loadRegistry({ repoRoot });
536
536
  const requested = Array.isArray(targets) && targets.length > 0 ? targets : undefined;
537
537
  // Validated before anything is read, so an unknown harness fails as the argument error it is
@@ -550,12 +550,12 @@ function buildInventoryReport({ repoRoot, projectRoot, targets, registry, locks,
550
550
 
551
551
  const { groups, unidentified } = groupByIdentity(scopes, fsImpl ?? fs);
552
552
  const siblings = inspectSiblings({
553
- resources: scopes.flatMap((snapshot: any) => snapshot.resources ?? []),
553
+ resources: scopes.flatMap((snapshot ) => snapshot.resources ?? []),
554
554
  ...(fsImpl ? { fsImpl } : {}),
555
555
  });
556
556
 
557
- const assets: any[] = [];
558
- const findings: any[] = [];
557
+ const assets = [];
558
+ const findings = [];
559
559
  // One finding per unmanaged file across the whole report, not per asset that lists it (FR-007;
560
560
  // see the header's note 3). Assets are walked in identity order, so which entry carries the
561
561
  // finding is deterministic.
@@ -563,7 +563,7 @@ function buildInventoryReport({ repoRoot, projectRoot, targets, registry, locks,
563
563
  for (const identity of [...groups.keys()].sort()) {
564
564
  const { harness, assetId, copies } = groups.get(identity);
565
565
  // Both crossings of the vocabulary seam, in one place: in, then straight back out.
566
- const raw = resolveShadowVerdict(copies.map((copy: any) => toRegistryScope(copy.scope)), precedenceFor(loaded, harness));
566
+ const raw = resolveShadowVerdict(copies.map((copy ) => toRegistryScope(copy.scope)), precedenceFor(loaded, harness));
567
567
  const verdict = raw && {
568
568
  presentAt: raw.presentAt.map(toReportScope),
569
569
  shadowed: raw.shadowed,
@@ -603,7 +603,7 @@ function buildInventoryReport({ repoRoot, projectRoot, targets, registry, locks,
603
603
  status: findings.length === 0 ? 'CLEAN' : 'FINDINGS',
604
604
  exitCode: findings.length === 0 ? 0 : 1,
605
605
  summary: summarize({ findings, assets, unidentified, targets: requested }),
606
- scopes: scopes.map((snapshot: any) => ({
606
+ scopes: scopes.map((snapshot ) => ({
607
607
  scope: snapshot.scope,
608
608
  root: snapshot.scopeRoot,
609
609
  present: snapshot.recorded,
@@ -618,7 +618,7 @@ function buildInventoryReport({ repoRoot, projectRoot, targets, registry, locks,
618
618
 
619
619
  /** Human rendering. The same facts as `--json`, in the same order, so neither shape is the one that
620
620
  * tells the truth. */
621
- function printReport(report: any): void {
621
+ function printReport(report ) {
622
622
  console.log('\nDoFlow cross-scope inventory:');
623
623
  console.log('═'.repeat(78));
624
624
  console.log(report.summary);
@@ -679,7 +679,7 @@ function printReport(report: any): void {
679
679
  * question from the one the caller asked.
680
680
  * @returns {number} the exit status, already set on the process
681
681
  */
682
- function handleInventoryCommand({ repoRoot, projectRoot, targets, json = false, global: globalScope = false }: any = {}): any {
682
+ function handleInventoryCommand({ repoRoot, projectRoot, targets, json = false, global: globalScope = false } = {}) {
683
683
  if (globalScope) {
684
684
  return usageError('inventory', '-g/--global is not accepted: this verb reads both scopes in one '
685
685
  + 'invocation, which is its entire purpose. Re-run without the flag.', json);
@@ -691,7 +691,7 @@ function handleInventoryCommand({ repoRoot, projectRoot, targets, json = false,
691
691
  } catch (error) {
692
692
  // IC-001 assigns an unreadable registry, an unparseable ledger and an undeclared harness alike
693
693
  // to exit 2. A scope with nothing recorded never reaches here — `readScopes` returns it empty.
694
- return usageError('inventory', (error as any).message, json, error);
694
+ return usageError('inventory', (error ).message, json, error);
695
695
  }
696
696
 
697
697
  if (json) console.log(JSON.stringify(report, null, 2));