@hassangameryt/freeflow 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.cursor-plugin/plugin.json +1 -1
  5. package/NOTICE +14 -0
  6. package/README.md +26 -31
  7. package/capabilities/cognitive-routing/SKILL.md +106 -161
  8. package/capabilities/cognitive-routing/references/both-mode.md +36 -0
  9. package/capabilities/cognitive-routing/references/executor-mode.md +48 -0
  10. package/capabilities/cognitive-routing/references/helper-mode.md +47 -0
  11. package/capabilities/compaction/SKILL.md +49 -0
  12. package/capabilities/compaction/references/summary-format.md +239 -0
  13. package/command-surface.json +4 -0
  14. package/gemini-extension.json +1 -1
  15. package/hooks/shared/runtime-context.mjs +28 -30
  16. package/licenses/Apache-2.0.txt +55 -0
  17. package/package.json +22 -9
  18. package/pi-extension/README.md +41 -0
  19. package/pi-extension/dist/cognitive-routing/assembler.js +393 -0
  20. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/config.js +5 -2
  21. package/pi-extension/dist/cognitive-routing/economics.js +45 -0
  22. package/pi-extension/dist/cognitive-routing/event-schema.js +388 -0
  23. package/pi-extension/dist/{session-sources/events.js → cognitive-routing/event-store.js} +103 -40
  24. package/pi-extension/dist/cognitive-routing/gate.js +241 -0
  25. package/pi-extension/dist/cognitive-routing/handlers.js +899 -0
  26. package/pi-extension/dist/cognitive-routing/handoffs.js +350 -0
  27. package/pi-extension/dist/cognitive-routing/model-control.js +467 -0
  28. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/projection.js +229 -56
  29. package/pi-extension/dist/cognitive-routing/provenance.js +81 -0
  30. package/pi-extension/dist/cognitive-routing/runtime.js +396 -0
  31. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/schemas.js +43 -7
  32. package/pi-extension/dist/cognitive-routing/session.js +221 -0
  33. package/pi-extension/dist/{session-sources → cognitive-routing}/sources.js +99 -85
  34. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/state.js +37 -378
  35. package/pi-extension/dist/cognitive-routing/status.js +80 -0
  36. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/tools.js +12 -12
  37. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/types.js +5 -11
  38. package/pi-extension/dist/compaction/carry.js +191 -0
  39. package/pi-extension/dist/compaction/config.js +22 -0
  40. package/pi-extension/dist/compaction/controller.js +471 -0
  41. package/pi-extension/dist/compaction/harness.js +77 -0
  42. package/pi-extension/dist/compaction/results.js +158 -0
  43. package/pi-extension/dist/compaction/thresholds.js +35 -0
  44. package/pi-extension/dist/compaction/tool.js +57 -0
  45. package/pi-extension/dist/host/branch.js +13 -0
  46. package/pi-extension/dist/host/canonical.js +12 -0
  47. package/pi-extension/dist/host/catalog.js +77 -0
  48. package/pi-extension/dist/host/config.js +374 -0
  49. package/pi-extension/dist/host/projection-tags.js +60 -0
  50. package/pi-extension/dist/host/prompts.js +82 -0
  51. package/pi-extension/dist/{session-sources → host}/read-only-session.js +112 -1
  52. package/pi-extension/dist/{runtime → host}/request-history.js +121 -39
  53. package/pi-extension/dist/host/runtime-state.js +189 -0
  54. package/pi-extension/dist/{settings/settings-ui.js → host/settings/freeflow-command.js} +620 -410
  55. package/pi-extension/dist/{settings/settings-tui.js → host/settings/settings-view.js} +186 -73
  56. package/pi-extension/dist/host/staging.js +28 -0
  57. package/pi-extension/dist/host/status.js +101 -0
  58. package/pi-extension/dist/index.js +423 -173
  59. package/pi-extension/dist/provider-support/cache/anchor.js +21 -0
  60. package/pi-extension/dist/provider-support/cache/anthropic.js +102 -0
  61. package/pi-extension/dist/provider-support/cache/health.js +73 -0
  62. package/pi-extension/dist/provider-support/cache/index.js +58 -0
  63. package/pi-extension/dist/provider-support/cache/keepalive.js +210 -0
  64. package/pi-extension/dist/provider-support/cache/monitor.js +17 -0
  65. package/pi-extension/dist/provider-support/cache/openai-responses.js +48 -0
  66. package/pi-extension/dist/provider-support/effort.js +13 -0
  67. package/pi-extension/dist/provider-support/index.js +44 -3
  68. package/pi-extension/dist/provider-support/{astra → openai}/adapter.js +46 -29
  69. package/pi-extension/dist/provider-support/{astra → openai}/history.js +10 -8
  70. package/pi-extension/dist/provider-support/{astra → openai}/index.js +8 -4
  71. package/pi-extension/dist/provider-support/{astra → openai}/session-state.js +29 -38
  72. package/pi-extension/dist/provider-support/routes.js +17 -0
  73. package/pi-extension/dist/tool-execution/apply-patch/grammar.js +23 -0
  74. package/pi-extension/dist/tool-execution/apply-patch/match.js +38 -0
  75. package/pi-extension/dist/tool-execution/apply-patch/parser.js +177 -0
  76. package/pi-extension/dist/tool-execution/apply-patch/plan.js +201 -0
  77. package/pi-extension/dist/tool-execution/apply-patch/tool.js +84 -0
  78. package/pi-extension/dist/tool-execution/background.js +238 -0
  79. package/pi-extension/dist/tool-execution/bash-guard.js +23 -0
  80. package/pi-extension/dist/tool-execution/config.js +22 -0
  81. package/pi-extension/dist/tool-execution/edit-messages.js +120 -0
  82. package/pi-extension/dist/tool-execution/file-state.js +138 -0
  83. package/pi-extension/dist/tool-execution/file-tracking.js +143 -0
  84. package/pi-extension/dist/tool-execution/prompt.js +54 -0
  85. package/pi-extension/dist/tool-execution/tools.js +16 -0
  86. package/pi-extension/freeflow/README.md +9 -0
  87. package/plugin.json +1 -1
  88. package/runtime/prompts/cognitive-routing.md +4 -4
  89. package/runtime/prompts/core.md +22 -75
  90. package/runtime/prompts/interaction-contract.md +15 -8
  91. package/runtime/prompts/tool-execution.md +38 -0
  92. package/runtime/prompts/working-method.md +108 -0
  93. package/skills/action-selection/SKILL.md +39 -47
  94. package/skills/action-selection/references/trajectory-stalls.md +2 -0
  95. package/skills/bypass/SKILL.md +2 -2
  96. package/skills/commit-work/SKILL.md +31 -83
  97. package/skills/commit-work/references/git-state-edges.md +1 -1
  98. package/skills/commit-work/references/simple-push.md +2 -0
  99. package/skills/commit-work/references/staging-decisions.md +1 -1
  100. package/skills/decision-gate/SKILL.md +31 -50
  101. package/skills/design-for-depth/SKILL.md +44 -53
  102. package/skills/design-for-depth/references/design-pressure-signals.md +46 -144
  103. package/skills/design-for-depth/references/implementation-decisions.md +1 -1
  104. package/skills/design-for-depth/references/interface-design-loop.md +5 -36
  105. package/skills/design-for-depth/references/module-and-dependency-design.md +2 -32
  106. package/skills/design-for-depth/references/state-and-failure-boundaries.md +2 -13
  107. package/skills/diagnose-failure/SKILL.md +39 -24
  108. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +2 -0
  109. package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +2 -2
  110. package/skills/discuss/SKILL.md +35 -11
  111. package/skills/discuss/references/discussion-continuity.md +7 -27
  112. package/skills/evaluate-skill/SKILL.md +49 -80
  113. package/skills/evaluate-skill/references/evaluation-definition-schema.md +35 -5
  114. package/skills/evaluate-skill/references/evaluation-design.md +14 -14
  115. package/skills/evaluate-skill/references/execution-and-evidence.md +26 -5
  116. package/skills/evaluate-skill/references/review-and-revision.md +3 -1
  117. package/skills/evaluate-skill/scripts/lib/definitions.mjs +49 -8
  118. package/skills/evaluate-skill/scripts/lib/pi.mjs +14 -7
  119. package/skills/evaluate-skill/scripts/lib/review.mjs +196 -0
  120. package/skills/evaluate-skill/scripts/lib/runner.mjs +45 -8
  121. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +2 -0
  122. package/skills/evaluate-skill/scripts/lib/trials.mjs +103 -0
  123. package/skills/evaluate-skill/scripts/lib/view.mjs +67 -1
  124. package/skills/evaluate-skill/scripts/pi-command-tool.mjs +144 -0
  125. package/skills/evaluate-skill/scripts/pi-guard.mjs +30 -13
  126. package/skills/evaluate-skill/scripts/skill-eval.mjs +114 -34
  127. package/skills/execute-work/SKILL.md +41 -9
  128. package/skills/execute-work/references/code-practices.md +2 -0
  129. package/skills/execute-work/references/execute-work-edges.md +11 -1
  130. package/skills/handoff/SKILL.md +1 -1
  131. package/skills/handoff/references/handoff-templates.md +1 -1
  132. package/skills/review-artifact/SKILL.md +42 -41
  133. package/skills/review-artifact/references/adjudicate-artifact-review.md +1 -1
  134. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +1 -1
  135. package/skills/review-work/SKILL.md +38 -32
  136. package/skills/review-work/references/adjudicate-work-review.md +1 -1
  137. package/skills/review-work/references/independent-work-reviewer-contract.md +1 -1
  138. package/skills/review-work/references/security-risk-lens.md +2 -4
  139. package/skills/setup-freeflow/SKILL.md +3 -1
  140. package/skills/setup-freeflow/references/activation-contract.md +7 -13
  141. package/skills/setup-freeflow/references/host-setup.md +3 -3
  142. package/skills/track-work/SKILL.md +113 -161
  143. package/skills/track-work/references/working-record-format.md +189 -350
  144. package/skills/track-work/scripts/lib/document.mjs +62 -6
  145. package/skills/track-work/scripts/lib/format.mjs +16 -1
  146. package/skills/track-work/scripts/lib/store.mjs +9 -8
  147. package/skills/track-work/scripts/lib/transitions.mjs +79 -4
  148. package/skills/track-work/scripts/working-record.mjs +34 -12
  149. package/skills/verify-work/SKILL.md +29 -20
  150. package/skills/verify-work/references/browser-runtime-evidence.md +1 -5
  151. package/skills/verify-work/references/integration-evidence.md +2 -0
  152. package/skills/verify-work/references/performance-evidence.md +3 -1
  153. package/skills/verify-work/references/test-design.md +3 -1
  154. package/skills/workflow/SKILL.md +65 -21
  155. package/skills/workflow/references/domain-skill-composition.md +5 -3
  156. package/skills/write-docs/SKILL.md +83 -0
  157. package/skills/write-docs/references/documentation-shapes.md +119 -0
  158. package/skills/write-docs/scripts/docs-check.mjs +139 -0
  159. package/skills/write-plan/SKILL.md +40 -40
  160. package/skills/write-plan/references/plan-shapes.md +1 -1
  161. package/skills/write-skill/SKILL.md +107 -147
  162. package/skills/write-skill/references/activation-boundaries.md +2 -0
  163. package/skills/write-skill/references/agent-first-instructions.md +22 -10
  164. package/skills/write-skill/references/progressive-disclosure.md +4 -2
  165. package/skills/write-skill/references/skill-dependency-graphs.md +2 -0
  166. package/skills/write-skill/scripts/lib/similarity.mjs +56 -0
  167. package/skills/write-skill/scripts/skill-author.mjs +18 -2
  168. package/skills/write-spec/SKILL.md +34 -41
  169. package/skills/write-spec/references/artifact-standards.md +1 -1
  170. package/skills/write-spec/references/decision-records.md +2 -0
  171. package/skills/write-spec/references/spec-shapes.md +1 -1
  172. package/capabilities/context-virtualization/SKILL.md +0 -103
  173. package/capabilities/conversation-history/SKILL.md +0 -53
  174. package/hooks/tests/check-host-adapters.test.mjs +0 -82
  175. package/hooks/tests/check-runtime-context-hook.sh +0 -212
  176. package/pi-extension/dist/cognitive-routing-v2/runtime.js +0 -2181
  177. package/pi-extension/dist/context-virtualization/commands.js +0 -89
  178. package/pi-extension/dist/context-virtualization/projector.js +0 -42
  179. package/pi-extension/dist/context-virtualization/runtime.js +0 -378
  180. package/pi-extension/dist/context-virtualization/state.js +0 -57
  181. package/pi-extension/dist/context-virtualization/types.js +0 -10
  182. package/pi-extension/dist/conversation-history/passages.js +0 -66
  183. package/pi-extension/dist/conversation-history/ranking.js +0 -105
  184. package/pi-extension/dist/conversation-history/runtime.js +0 -226
  185. package/pi-extension/dist/conversation-history/search.js +0 -56
  186. package/pi-extension/dist/conversation-history/types.js +0 -1
  187. package/pi-extension/dist/freeflow-context/resolver.js +0 -120
  188. package/pi-extension/dist/freeflow-context/runtime.js +0 -80
  189. package/pi-extension/dist/freeflow-context/source-projector.js +0 -95
  190. package/pi-extension/dist/freeflow-context/tool.js +0 -314
  191. package/pi-extension/dist/freeflow-context/types.js +0 -12
  192. package/pi-extension/dist/freeflow-context/visibility.js +0 -28
  193. package/pi-extension/dist/integrations/mcp-config.js +0 -29
  194. package/pi-extension/dist/runtime/runtime-context.js +0 -754
  195. package/pi-extension/dist/runtime/runtime-identity.js +0 -5
  196. package/pi-extension/dist/session-sources/provenance.js +0 -32
  197. package/pi-extension/dist/ui/text-component.js +0 -57
  198. package/pi-extension/src/cognitive-routing-v2/budget.ts +0 -42
  199. package/pi-extension/src/cognitive-routing-v2/config.ts +0 -173
  200. package/pi-extension/src/cognitive-routing-v2/projection.ts +0 -375
  201. package/pi-extension/src/cognitive-routing-v2/render.ts +0 -277
  202. package/pi-extension/src/cognitive-routing-v2/runtime.ts +0 -2241
  203. package/pi-extension/src/cognitive-routing-v2/schemas.ts +0 -180
  204. package/pi-extension/src/cognitive-routing-v2/state.ts +0 -839
  205. package/pi-extension/src/cognitive-routing-v2/tools.ts +0 -62
  206. package/pi-extension/src/cognitive-routing-v2/types.ts +0 -254
  207. package/pi-extension/src/context-virtualization/commands.ts +0 -111
  208. package/pi-extension/src/context-virtualization/projector.ts +0 -62
  209. package/pi-extension/src/context-virtualization/runtime.ts +0 -428
  210. package/pi-extension/src/context-virtualization/state.ts +0 -79
  211. package/pi-extension/src/context-virtualization/types.ts +0 -92
  212. package/pi-extension/src/conversation-history/passages.ts +0 -81
  213. package/pi-extension/src/conversation-history/ranking.ts +0 -155
  214. package/pi-extension/src/conversation-history/runtime.ts +0 -250
  215. package/pi-extension/src/conversation-history/search.ts +0 -79
  216. package/pi-extension/src/conversation-history/types.ts +0 -31
  217. package/pi-extension/src/freeflow-context/resolver.ts +0 -139
  218. package/pi-extension/src/freeflow-context/runtime.ts +0 -100
  219. package/pi-extension/src/freeflow-context/source-projector.ts +0 -106
  220. package/pi-extension/src/freeflow-context/tool.ts +0 -366
  221. package/pi-extension/src/freeflow-context/types.ts +0 -70
  222. package/pi-extension/src/freeflow-context/visibility.ts +0 -53
  223. package/pi-extension/src/index.ts +0 -390
  224. package/pi-extension/src/integrations/mcp-config.ts +0 -32
  225. package/pi-extension/src/provider-support/astra/adapter.ts +0 -108
  226. package/pi-extension/src/provider-support/astra/history.ts +0 -145
  227. package/pi-extension/src/provider-support/astra/index.ts +0 -12
  228. package/pi-extension/src/provider-support/astra/session-state.ts +0 -113
  229. package/pi-extension/src/provider-support/index.ts +0 -5
  230. package/pi-extension/src/runtime/request-history.ts +0 -243
  231. package/pi-extension/src/runtime/runtime-context.ts +0 -860
  232. package/pi-extension/src/runtime/runtime-identity.ts +0 -8
  233. package/pi-extension/src/session-sources/events.ts +0 -230
  234. package/pi-extension/src/session-sources/provenance.ts +0 -39
  235. package/pi-extension/src/session-sources/read-only-session.ts +0 -170
  236. package/pi-extension/src/session-sources/sources.ts +0 -334
  237. package/pi-extension/src/settings/settings-tui.ts +0 -501
  238. package/pi-extension/src/settings/settings-ui.ts +0 -1652
  239. package/pi-extension/src/ui/text-component.ts +0 -66
  240. package/pi-extension/tests/cache-reuse/history.test.js +0 -117
  241. package/pi-extension/tests/cache-reuse/native.test.js +0 -91
  242. package/pi-extension/tests/cache-reuse/settings.test.js +0 -211
  243. package/pi-extension/tests/cognitive-routing-v2/communication.test.js +0 -577
  244. package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +0 -183
  245. package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +0 -557
  246. package/pi-extension/tests/cognitive-routing-v2/native.test.js +0 -474
  247. package/pi-extension/tests/cognitive-routing-v2/performance.test.js +0 -87
  248. package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +0 -300
  249. package/pi-extension/tests/cognitive-routing-v2/projection.test.js +0 -241
  250. package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +0 -1034
  251. package/pi-extension/tests/cognitive-routing-v2/render.test.js +0 -244
  252. package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +0 -423
  253. package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +0 -521
  254. package/pi-extension/tests/cognitive-routing-v2/state.test.js +0 -586
  255. package/pi-extension/tests/context-virtualization/extension.test.js +0 -775
  256. package/pi-extension/tests/context-virtualization/projector.test.js +0 -110
  257. package/pi-extension/tests/context-virtualization/runtime.test.js +0 -230
  258. package/pi-extension/tests/conversation-history/runtime.test.js +0 -98
  259. package/pi-extension/tests/conversation-history/search.test.js +0 -97
  260. package/pi-extension/tests/fixtures/pi-host.js +0 -4
  261. package/pi-extension/tests/fixtures/routing-native.js +0 -191
  262. package/pi-extension/tests/integration/mode-free-core.test.js +0 -220
  263. package/pi-extension/tests/integration/pi-extension.test.js +0 -1447
  264. package/pi-extension/tests/integration/prompt-architecture.test.js +0 -447
  265. package/pi-extension/tests/integration/unaffected-regressions.test.js +0 -478
  266. package/pi-extension/tests/provider-support/astra-boundaries.test.js +0 -75
  267. package/pi-extension/tests/provider-support/astra.test.js +0 -306
  268. package/pi-extension/tests/provider-support/cli.test.js +0 -92
  269. package/pi-extension/tests/provider-support/native.test.js +0 -183
  270. package/pi-extension/tests/settings/settings-tui.test.js +0 -243
  271. package/pi-extension/tsconfig.json +0 -16
  272. package/runtime/prompts/context-virtualization.md +0 -3
  273. package/runtime/prompts/conversation-history.md +0 -3
  274. package/skills/design-for-depth/references/software-design-philosophy.md +0 -202
  275. /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/budget.js +0 -0
  276. /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/render.js +0 -0
@@ -11,11 +11,15 @@ import {
11
11
  HISTORICAL_SLICE_FIELDS,
12
12
  HISTORICAL_SLICE_STATES,
13
13
  HISTORY_HEADINGS,
14
+ LEGACY_CHECKPOINT_TYPES,
15
+ OCCURRED_PATTERN,
14
16
  PROPOSED_SLICE_FIELDS,
17
+ RECORDED_PATTERN,
15
18
  SCHEMA_VERSION,
16
19
  SLICE_TYPES,
17
20
  TASK_STATES,
18
21
  TOP_LEVEL_SECTIONS,
22
+ isCalendarDate,
19
23
  joinLines,
20
24
  nextId,
21
25
  parseId,
@@ -273,12 +277,35 @@ function parseContext(lines, section) {
273
277
  return { ...section, headings: statements };
274
278
  }
275
279
 
280
+ const CURRENT_WORK_LAYOUTS = [
281
+ ["Current Slice", "Next useful action"],
282
+ ["Current Slice", "Recovery sources", "Next useful action"],
283
+ ];
284
+
276
285
  function parseCurrentWork(lines, section) {
277
286
  const headings = findHeadings(lines, section.start, section.end, 3);
278
- if (headings.length !== 2 || headings[0].title !== "Current Slice" || headings[1].title !== "Next useful action")
279
- fail("invalid-current-work", "Current Work must contain Current Slice followed by Next useful action");
287
+ const titles = headings.map((heading) => heading.title);
288
+ if (!CURRENT_WORK_LAYOUTS.some((layout) => layout.join("\n") === titles.join("\n")))
289
+ fail(
290
+ "invalid-current-work",
291
+ "Current Work must contain Current Slice, optional Recovery sources, then Next useful action",
292
+ );
293
+ const nextActionHeading = headings[headings.length - 1];
294
+ const recoveryHeading = headings.length === 3 ? headings[1] : null;
280
295
  const sliceContentStart = headings[0].index + 1;
281
296
  const sliceContentEnd = headings[1].index;
297
+ let recoverySources = null;
298
+ if (recoveryHeading) {
299
+ recoverySources = {
300
+ headingIndex: recoveryHeading.index,
301
+ start: recoveryHeading.index + 1,
302
+ end: nextActionHeading.index,
303
+ };
304
+ for (let index = recoverySources.start; index < recoverySources.end; index += 1) {
305
+ if (lineHeading(lines[index]))
306
+ fail("invalid-recovery-sources", `Recovery sources cannot contain headings: ${lines[index]}`);
307
+ }
308
+ }
282
309
  const content = nonBlankLines(lines.slice(sliceContentStart, sliceContentEnd));
283
310
  let currentSlice = null;
284
311
  if (content.length === 1 && content[0] === "None") {
@@ -304,9 +331,11 @@ function parseCurrentWork(lines, section) {
304
331
  return {
305
332
  ...section,
306
333
  sliceHeadingIndex: headings[0].index,
307
- nextActionHeadingIndex: headings[1].index,
334
+ sliceEnd: sliceContentEnd,
335
+ nextActionHeadingIndex: nextActionHeading.index,
308
336
  currentSlice,
309
- nextAction: { start: headings[1].index + 1, end: section.end },
337
+ recoverySources,
338
+ nextAction: { start: nextActionHeading.index + 1, end: section.end },
310
339
  };
311
340
  }
312
341
 
@@ -385,6 +414,8 @@ function parseHistory(lines, section) {
385
414
  };
386
415
  }
387
416
 
417
+ const READABLE_CHECKPOINT_TYPES = new Set([...CHECKPOINT_TYPES, ...LEGACY_CHECKPOINT_TYPES]);
418
+
388
419
  function validateChoice(block, field, choices, required = true) {
389
420
  const value = scalarValue(block, field);
390
421
  if (!value && !required) return "";
@@ -459,6 +490,27 @@ function validateReferences(document) {
459
490
  }
460
491
  }
461
492
 
493
+ export function validateOccurred(value, path) {
494
+ const match = OCCURRED_PATTERN.exec(value ?? "");
495
+ if (!match || !isCalendarDate(match[1]) || (match[2] && !isCalendarDate(match[2])))
496
+ fail("invalid-occurred", `Occurred in ${path} must be YYYY-MM-DD or YYYY-MM-DD to YYYY-MM-DD: ${value}`);
497
+ if (match[2] && match[2] < match[1]) fail("invalid-occurred", `Occurred in ${path} ends before it starts: ${value}`);
498
+ return match[2] ?? match[1];
499
+ }
500
+
501
+ function validateRecordedTime(slice) {
502
+ const occurred = scalarValue(slice, "Occurred");
503
+ const recorded = scalarValue(slice, "Recorded");
504
+ if (!occurred && !recorded) return;
505
+ if (!occurred || !recorded)
506
+ fail("missing-field", `Occurred and Recorded appear together in ${slice.path}; only slice record writes them`);
507
+ const end = validateOccurred(occurred, slice.path);
508
+ const match = RECORDED_PATTERN.exec(recorded);
509
+ if (!match || !isCalendarDate(match[1]))
510
+ fail("invalid-recorded", `Recorded in ${slice.path} must be "retroactively on YYYY-MM-DD": ${recorded}`);
511
+ if (end > match[1]) fail("invalid-occurred", `Occurred in ${slice.path} ends after it was recorded: ${occurred}`);
512
+ }
513
+
462
514
  export function validateDocument(document) {
463
515
  const { header, context, current, future, history } = document;
464
516
  if (!header.name) fail("malformed-header", "Task name cannot be empty");
@@ -469,6 +521,9 @@ export function validateDocument(document) {
469
521
  fail("missing-field", "Next useful action must contain meaningful content");
470
522
  for (const block of allBlocks(document)) {
471
523
  if (block.id && !parseId(block.id, block.kind)) fail("invalid-id", `Invalid ${block.kind} ID ${block.id}`);
524
+ for (const [name, field] of block.fields) {
525
+ if (!valueText(field)) fail("empty-field", `Empty field ${name} in ${block.path}; omit fields without a value`);
526
+ }
472
527
  }
473
528
 
474
529
  const ids = new Set();
@@ -489,7 +544,7 @@ export function validateDocument(document) {
489
544
  } else {
490
545
  validateFields(item, ["State"], {});
491
546
  validateChoice(item, "State", FUTURE_CHECKPOINT_STATES);
492
- validateChoice(item, "Type", CHECKPOINT_TYPES);
547
+ validateChoice(item, "Type", READABLE_CHECKPOINT_TYPES);
493
548
  validateFields(item, ["Condition", "Applies to"]);
494
549
  if (item.id && !parseId(item.id, "checkpoint")) fail("invalid-id", `Invalid Checkpoint ID ${item.id}`);
495
550
  const checkpointState = scalarValue(item, "State");
@@ -530,7 +585,7 @@ export function validateDocument(document) {
530
585
 
531
586
  for (const checkpoint of history.checkpoints.blocks) {
532
587
  validateChoice(checkpoint, "State", HISTORICAL_CHECKPOINT_STATES);
533
- validateChoice(checkpoint, "Type", CHECKPOINT_TYPES);
588
+ validateChoice(checkpoint, "Type", READABLE_CHECKPOINT_TYPES);
534
589
  validateFields(checkpoint, ["Condition", "Applies to", "Result", "Task effect"]);
535
590
  const state = scalarValue(checkpoint, "State");
536
591
  if (state === "cancelled" || state === "replaced") requiredValue(checkpoint, "Reason");
@@ -543,6 +598,7 @@ export function validateDocument(document) {
543
598
  const state = scalarValue(slice, "State");
544
599
  if (state === "blocked") requiredValue(slice, "Resume when");
545
600
  if (state === "abandoned") requiredValue(slice, "Reason");
601
+ validateRecordedTime(slice);
546
602
  }
547
603
 
548
604
  if (header.state === "paused" && current.currentSlice && scalarValue(current.currentSlice, "State") !== "paused")
@@ -16,7 +16,9 @@ export const TASK_STATES = new Set(["active", "paused", "completed", "abandoned"
16
16
  export const SLICE_TYPES = new Set(["learning", "delivery", "deepening"]);
17
17
  export const CURRENT_SLICE_STATES = new Set(["in_progress", "paused"]);
18
18
  export const HISTORICAL_SLICE_STATES = new Set(["completed", "blocked", "abandoned"]);
19
- export const CHECKPOINT_TYPES = new Set(["independent_review", "local_commit", "user_decision", "continuity"]);
19
+ export const CHECKPOINT_TYPES = new Set(["preserve", "publish", "review", "decision", "continuity"]);
20
+ // Earlier type names remain readable so existing records stay valid.
21
+ export const LEGACY_CHECKPOINT_TYPES = new Set(["local_commit", "independent_review", "user_decision"]);
20
22
  export const FUTURE_CHECKPOINT_STATES = new Set(["proposed", "pending", "deferred"]);
21
23
  export const HISTORICAL_CHECKPOINT_STATES = new Set(["completed", "cancelled", "replaced"]);
22
24
  export const DECISION_STATES = new Set(["active", "superseded", "retired"]);
@@ -60,6 +62,8 @@ export const DECISION_FIELDS = new Set([
60
62
  export const HISTORICAL_SLICE_FIELDS = new Set([
61
63
  "State",
62
64
  "Type",
65
+ "Occurred",
66
+ "Recorded",
63
67
  "Intended result",
64
68
  "Authority source",
65
69
  "Result",
@@ -71,6 +75,15 @@ export const HISTORICAL_SLICE_FIELDS = new Set([
71
75
  "Residual effects",
72
76
  ]);
73
77
 
78
+ // A Slice recorded after the fact carries when it happened and the date it was recorded.
79
+ export const OCCURRED_PATTERN = /^(\d{4}-\d{2}-\d{2})(?: to (\d{4}-\d{2}-\d{2}))?$/;
80
+ export const RECORDED_PATTERN = /^retroactively on (\d{4}-\d{2}-\d{2})$/;
81
+
82
+ export function isCalendarDate(value) {
83
+ const date = new Date(`${value}T00:00:00Z`);
84
+ return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value;
85
+ }
86
+
74
87
  export const IDS = {
75
88
  slice: /^S-(\d{3,})$/,
76
89
  checkpoint: /^C-(\d{3,})$/,
@@ -111,6 +124,8 @@ export function canonicalLines(name, state = "active", lastUpdated = new Date().
111
124
  "",
112
125
  "None",
113
126
  "",
127
+ "### Recovery sources",
128
+ "",
114
129
  "### Next useful action",
115
130
  "",
116
131
  "## Future Work",
@@ -94,9 +94,9 @@ export function resolveRecordPath(root, supplied) {
94
94
  const recordPath = basename(candidate) === "record.md" ? candidate : join(candidate, "record.md");
95
95
  const canonicalRecord = canonicalPath(recordPath);
96
96
  if (!inside(tasks, canonicalRecord) || basename(canonicalRecord) !== "record.md")
97
- fail("unsafe-path", `Record must be a record.md below ${tasks}`);
97
+ fail("unsafe-path", `Record must be a record.md below ${tasks}; pass --root or an absolute --record path`);
98
98
  const taskDirectory = basename(dirname(canonicalRecord));
99
- if (!/^task-\d{3}-[a-z0-9][a-z0-9-]*$/.test(taskDirectory))
99
+ if (!/^task-\d{3,}-[a-z0-9][a-z0-9-]*$/.test(taskDirectory))
100
100
  fail("unsafe-path", `Invalid task directory: ${taskDirectory}`);
101
101
  return canonicalRecord;
102
102
  }
@@ -144,14 +144,13 @@ async function nextTaskNumber(tasks) {
144
144
  } catch (error) {
145
145
  if (error?.code !== "ENOENT") throw error;
146
146
  }
147
- const used = new Set();
147
+ // Numbers are never reused, so an old reference to a deleted task cannot point at a new one.
148
+ let highest = 0;
148
149
  for (const entry of entries) {
149
- const match = /^task-(\d{3})-/.exec(entry.name);
150
- if (entry.isDirectory() && match) used.add(Number(match[1]));
150
+ const match = /^task-(\d{3,})-/.exec(entry.name);
151
+ if (entry.isDirectory() && match) highest = Math.max(highest, Number(match[1]));
151
152
  }
152
- let number = 1;
153
- while (used.has(number)) number += 1;
154
- return number;
153
+ return highest + 1;
155
154
  }
156
155
 
157
156
  export async function createRecordPath(root, taskName, slug) {
@@ -169,6 +168,8 @@ export async function createRecordPath(root, taskName, slug) {
169
168
  if (error?.code === "EEXIST") fail("task-already-exists", `Task directory already exists: ${directory}`);
170
169
  throw error;
171
170
  }
171
+ // The task's place for temporary work, ignored by Git with the rest of the task directory.
172
+ await mkdir(join(directory, "scratch"));
172
173
  return { path, taskName };
173
174
  }
174
175
 
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  ACTIVE_SLICE_FIELDS,
3
+ CHECKPOINT_TYPES,
3
4
  DECISION_FIELDS,
4
5
  HISTORICAL_CHECKPOINT_FIELDS,
5
6
  HISTORICAL_CHECKPOINT_STATES,
@@ -18,6 +19,7 @@ import {
18
19
  parseDocument,
19
20
  parseFragment,
20
21
  renderBlock,
22
+ validateOccurred,
21
23
  } from "./document.mjs";
22
24
  import { writeAtomically } from "./store.mjs";
23
25
 
@@ -60,6 +62,8 @@ const DECISION_ORDER = [
60
62
  const HISTORICAL_SLICE_ORDER = [
61
63
  "State",
62
64
  "Type",
65
+ "Occurred",
66
+ "Recorded",
63
67
  "Intended result",
64
68
  "Authority source",
65
69
  "Result",
@@ -331,7 +335,7 @@ async function startSlice(options, input, loaded) {
331
335
  { start: proposal.start, end: proposal.end, lines: [] },
332
336
  {
333
337
  start: document.current.sliceHeadingIndex + 1,
334
- end: document.current.nextActionHeadingIndex,
338
+ end: document.current.sliceEnd,
335
339
  lines: ["", ...currentLines, ""],
336
340
  },
337
341
  nextActionReplacement(document, nextAction),
@@ -393,7 +397,7 @@ async function startDirectSlice(options, input, loaded) {
393
397
  const candidate = withLineChanges(document, [
394
398
  {
395
399
  start: document.current.sliceHeadingIndex + 1,
396
- end: document.current.nextActionHeadingIndex,
400
+ end: document.current.sliceEnd,
397
401
  lines: ["", ...currentLines, ""],
398
402
  },
399
403
  nextActionReplacement(document, nextAction),
@@ -476,7 +480,7 @@ async function closeSlice(options, input, loaded) {
476
480
  const candidate = withLineChanges(document, [
477
481
  {
478
482
  start: document.current.sliceHeadingIndex + 1,
479
- end: document.current.nextActionHeadingIndex,
483
+ end: document.current.sliceEnd,
480
484
  lines: ["", "None", ""],
481
485
  },
482
486
  { start: document.history.slices.end, end: document.history.slices.end, lines: historical },
@@ -485,6 +489,65 @@ async function closeSlice(options, input, loaded) {
485
489
  return publish(loaded, candidate, `Closed Slice: ${slice.id} — ${state}`);
486
490
  }
487
491
 
492
+ function localDate(date = new Date()) {
493
+ const pad = (value) => String(value).padStart(2, "0");
494
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
495
+ }
496
+
497
+ async function recordSlice(options, input, loaded) {
498
+ const document = parseDocument(loaded.text);
499
+ if (!["active", "paused"].includes(document.header.state))
500
+ fail("invalid-task-state", `Cannot record a Slice while task state is ${document.header.state}`);
501
+ const title = requiredStringOption(options, "--title");
502
+ const state = requiredStringOption(options, "--state");
503
+ if (!HISTORICAL_SLICE_STATES.has(state)) fail("invalid-state", `Invalid historical Slice state: ${state}`);
504
+ const fragment = parseInput(
505
+ input,
506
+ new Set([
507
+ "Type",
508
+ "Occurred",
509
+ "Intended result",
510
+ "Authority source",
511
+ "Result",
512
+ "Evidence and limits",
513
+ "Task effect",
514
+ "Resume when",
515
+ "Reason",
516
+ "Residual effects",
517
+ ]),
518
+ "slice record",
519
+ );
520
+ const values = { State: state };
521
+ for (const field of [
522
+ "Occurred",
523
+ "Intended result",
524
+ "Authority source",
525
+ "Result",
526
+ "Evidence and limits",
527
+ "Task effect",
528
+ ])
529
+ values[field] = requireFragment(fragment, field, "slice record");
530
+ for (const field of ["Type", "Resume when", "Reason", "Residual effects"]) {
531
+ const lines = fragmentValue(fragment, field);
532
+ if (lines?.length) values[field] = lines;
533
+ }
534
+ if (state === "blocked") values["Resume when"] = requireFragment(fragment, "Resume when", "slice record");
535
+ if (state === "abandoned") values.Reason = requireFragment(fragment, "Reason", "slice record");
536
+ const recordedOn = localDate();
537
+ const occurredEnd = validateOccurred(fragmentText(fragment, "Occurred"), "slice record input");
538
+ if (occurredEnd > recordedOn) fail("invalid-occurred", `Occurred cannot end after today (${recordedOn})`);
539
+ values.Recorded = [`retroactively on ${recordedOn}`];
540
+ const id = nextEntityId(document, "slice");
541
+ const candidate = withLineChanges(document, [
542
+ {
543
+ start: document.history.slices.end,
544
+ end: document.history.slices.end,
545
+ lines: renderHistoricalSlice(values, title, id),
546
+ },
547
+ ]);
548
+ return publish(loaded, candidate, `Recorded Slice: ${id} — ${title} (${state}, retroactive)`);
549
+ }
550
+
488
551
  async function reopenSlice(options, input, loaded) {
489
552
  const document = parseDocument(loaded.text);
490
553
  assertTaskActive(document, "reopen a Slice");
@@ -528,7 +591,7 @@ async function reopenSlice(options, input, loaded) {
528
591
  const candidate = withLineChanges(document, [
529
592
  {
530
593
  start: document.current.sliceHeadingIndex + 1,
531
- end: document.current.nextActionHeadingIndex,
594
+ end: document.current.sliceEnd,
532
595
  lines: ["", ...currentLines, ""],
533
596
  },
534
597
  nextActionReplacement(document, nextAction),
@@ -546,6 +609,9 @@ async function proposeCheckpoint(options, input, loaded) {
546
609
  assertUniqueFutureTitle(document, title);
547
610
  const fragment = checkpointInput(input, "checkpoint propose");
548
611
  requireFragment(fragment, "Type", "checkpoint propose");
612
+ const type = fragmentText(fragment, "Type");
613
+ if (!CHECKPOINT_TYPES.has(type))
614
+ fail("invalid-type", `Checkpoint Type must be one of ${[...CHECKPOINT_TYPES].join(", ")}; found ${type}`);
549
615
  requireFragment(fragment, "Condition", "checkpoint propose");
550
616
  requireFragment(fragment, "Applies to", "checkpoint propose");
551
617
  const values = {
@@ -616,6 +682,7 @@ async function closeCheckpoint(options, input, loaded) {
616
682
  new Set(["Result", "Evidence", "Task effect", "Reason", "Replaced by"]),
617
683
  "checkpoint close",
618
684
  );
685
+ if (state === "replaced") assertOpenReplacement(document, id, fragmentText(fragment, "Replaced by"));
619
686
  const values = {
620
687
  State: state,
621
688
  Type: fieldLines(checkpoint, "Type"),
@@ -640,6 +707,13 @@ async function closeCheckpoint(options, input, loaded) {
640
707
  return publish(loaded, candidate, `Closed Checkpoint: ${id} — ${state}`);
641
708
  }
642
709
 
710
+ // A replacement must still be able to carry the boundary; a closed Checkpoint cannot.
711
+ function assertOpenReplacement(document, id, replacementId) {
712
+ const replacement = document.future.items.find((item) => item.kind === "checkpoint" && item.id === replacementId);
713
+ if (!replacement || replacementId === id || !["pending", "deferred"].includes(fieldValue(replacement, "State")))
714
+ fail("invalid-reference", `Replaced by must name another Checkpoint that is pending or deferred: ${replacementId}`);
715
+ }
716
+
643
717
  function decisionInput(input, command) {
644
718
  return parseInput(
645
719
  input,
@@ -772,6 +846,7 @@ export async function runTransition(command, positionals, options, input, loaded
772
846
  if (operation === "resume") return resumeSlice(options, input, loaded);
773
847
  if (operation === "close") return closeSlice(options, input, loaded);
774
848
  if (operation === "reopen") return reopenSlice(options, input, loaded);
849
+ if (operation === "record") return recordSlice(options, input, loaded);
775
850
  }
776
851
  if (command === "checkpoint") {
777
852
  if (operation === "propose") return proposeCheckpoint(options, input, loaded);
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { rm } from "node:fs/promises";
4
4
  import { cwd } from "node:process";
5
- import { dirname } from "node:path";
6
- import { canonicalLines, joinLines, slugify, TASK_STATES } from "./lib/format.mjs";
5
+ import { dirname, isAbsolute, sep } from "node:path";
6
+ import { canonicalLines, joinLines, slugify } from "./lib/format.mjs";
7
7
  import { parseDocument, validateDocument } from "./lib/document.mjs";
8
8
  import { runTransition } from "./lib/transitions.mjs";
9
9
  import {
@@ -16,6 +16,9 @@ import {
16
16
  } from "./lib/store.mjs";
17
17
  import { renderFull, renderResume } from "./lib/views.mjs";
18
18
 
19
+ const ROOT_HELP =
20
+ "--record is resolved against --root, which defaults to the current directory; an absolute --record path finds its own root.";
21
+
19
22
  function usage() {
20
23
  return [
21
24
  "Usage:",
@@ -23,6 +26,8 @@ function usage() {
23
26
  " working-record.mjs view resume|full --record <record.md>",
24
27
  " working-record.mjs validate --record <record.md>",
25
28
  " working-record.mjs <slice|checkpoint|decision|task> <operation> ...",
29
+ "",
30
+ ROOT_HELP,
26
31
  ].join("\n");
27
32
  }
28
33
 
@@ -31,7 +36,7 @@ const COMMAND_HELP = new Map([
31
36
  "slice",
32
37
  [
33
38
  "Usage: working-record.mjs slice <operation> ...",
34
- "Operations: slice propose, slice start, slice start-direct, slice pause, slice resume, slice close, slice reopen.",
39
+ "Operations: slice propose, slice start, slice start-direct, slice pause, slice resume, slice close, slice reopen, slice record.",
35
40
  "Run working-record.mjs <group> <operation> --help for options and input fields.",
36
41
  "Use --input - to read Markdown fragments from stdin.",
37
42
  ].join("\n"),
@@ -64,13 +69,13 @@ const COMMAND_HELP = new Map([
64
69
  ],
65
70
  [
66
71
  "init",
67
- "Usage: init --root <path> --name <task name> [--state <state>] [--input <file|->]\nInput: Goal and Next useful action required; the seven Current Context headings are accepted. Use --input - to read Markdown fragments from stdin.",
72
+ "Usage: init --root <path> --name <task name> [--input <file|->]\nInput: Goal and Next useful action required; the seven Current Context headings and Recovery sources are accepted, and every line must sit under one of them. The record starts active. Use --input - to read Markdown fragments from stdin.",
68
73
  ],
69
74
  ["view", "Usage: view resume|full --record <record.md>"],
70
75
  ["validate", "Usage: validate --record <record.md>"],
71
76
  [
72
77
  "slice propose",
73
- "Usage: slice propose --record <record.md> --title <title> --input <file|->\nInput: Intended result required; Type, Expected evidence, and Dependencies optional. Use --input - to read the fragment from stdin.",
78
+ "Usage: slice propose --record <record.md> --title <title> --input <file|->\nInput: Intended result required; Type, Expected evidence, and Dependencies optional. Commands set State; input never includes it. Use --input - to read the fragment from stdin.",
74
79
  ],
75
80
  [
76
81
  "slice start",
@@ -93,9 +98,13 @@ const COMMAND_HELP = new Map([
93
98
  "slice reopen",
94
99
  "Usage: slice reopen --record <record.md> --id <S-NNN> --next-action <text> --input <file|->\nInput: fresh Authority source, Scope, Expected evidence, Stop condition, and Starting state required. Use --input - to read the fragment from stdin.",
95
100
  ],
101
+ [
102
+ "slice record",
103
+ "Usage: slice record --record <record.md> --title <title> --state <completed|blocked|abandoned> --input <file|->\nRecords a Slice that finished before it could be recorded, directly into History. The Current Slice, Future Work, and Next useful action are unchanged; the ID follows recording order and the command adds Recorded: retroactively on <today>.\nInput: Occurred (YYYY-MM-DD or YYYY-MM-DD to YYYY-MM-DD), Intended result, Authority source, Result, Evidence and limits, and Task effect required; Type and Residual effects optional; Resume when required for blocked; Reason required for abandoned. Use --input - to read the fragment from stdin.",
104
+ ],
96
105
  [
97
106
  "checkpoint propose",
98
- "Usage: checkpoint propose --record <record.md> --title <title> --input <file|->\nInput: Type, Condition, and Applies to required. Use --input - to read the fragment from stdin.",
107
+ "Usage: checkpoint propose --record <record.md> --title <title> --input <file|->\nInput: Type (preserve, publish, review, decision, or continuity), Condition, and Applies to required. Use --input - to read the fragment from stdin.",
99
108
  ],
100
109
  [
101
110
  "checkpoint activate",
@@ -123,7 +132,9 @@ const COMMAND_HELP = new Map([
123
132
  ]);
124
133
 
125
134
  function commandHelp(command, operation) {
126
- return `${COMMAND_HELP.get([command, operation].filter(Boolean).join(" ")) ?? COMMAND_HELP.get(command) ?? usage()}\n`;
135
+ const help = COMMAND_HELP.get([command, operation].filter(Boolean).join(" ")) ?? COMMAND_HELP.get(command);
136
+ if (!help) return `${usage()}\n`;
137
+ return command === "init" ? `${help}\n` : `${help}\n${ROOT_HELP}\n`;
127
138
  }
128
139
 
129
140
  class CliError extends Error {
@@ -159,7 +170,7 @@ function parseOptions(args) {
159
170
  function allowedOptions(command, operation) {
160
171
  const common = new Set(["--root", "--record", "--input", "--help"]);
161
172
  const withNextAction = new Set([...common, "--next-action"]);
162
- if (command === "init") return new Set(["--root", "--name", "--state", "--input", "--help"]);
173
+ if (command === "init") return new Set(["--root", "--name", "--input", "--help"]);
163
174
  if (command === "view") return new Set(["--root", "--record", "--view", "--help"]);
164
175
  if (command === "validate") return new Set(["--root", "--record", "--help"]);
165
176
  if (command === "slice") {
@@ -169,6 +180,7 @@ function allowedOptions(command, operation) {
169
180
  if (operation === "resume") return new Set([...withNextAction, "--resolution"]);
170
181
  if (operation === "close") return new Set([...withNextAction, "--state"]);
171
182
  if (operation === "reopen") return new Set([...withNextAction, "--id", "--title"]);
183
+ if (operation === "record") return new Set([...common, "--title", "--state"]);
172
184
  }
173
185
  if (command === "checkpoint") {
174
186
  if (operation === "propose") return new Set([...common, "--title"]);
@@ -202,7 +214,14 @@ function requireOption(options, name) {
202
214
  }
203
215
 
204
216
  function rootOption(options) {
205
- return options["--root"] ?? cwd();
217
+ if (options["--root"]) return options["--root"];
218
+ const record = options["--record"];
219
+ if (record && isAbsolute(record)) {
220
+ const marker = `${sep}.freeflow${sep}tasks${sep}`;
221
+ const index = record.lastIndexOf(marker);
222
+ if (index > 0) return record.slice(0, index);
223
+ }
224
+ return cwd();
206
225
  }
207
226
 
208
227
  function initialContent(skeletonLines, input) {
@@ -217,15 +236,20 @@ function initialContent(skeletonLines, input) {
217
236
  "Open",
218
237
  "Current direction",
219
238
  "Boundaries",
239
+ "Recovery sources",
220
240
  "Next useful action",
221
241
  ];
222
242
  const allowed = new Set(names);
243
+ let seenHeading = false;
223
244
  for (let index = 0; index < inputLines.length; index += 1) {
224
245
  const levelThree = /^### (.+?)\s*$/.exec(inputLines[index]);
246
+ if (!levelThree && !seenHeading && inputLines[index].trim() !== "")
247
+ throw new CliError(`Initialization text outside a heading would be lost: ${inputLines[index].trim()}`);
225
248
  if (levelThree) {
226
249
  const name = levelThree[1];
227
250
  if (!allowed.has(name)) throw new CliError(`Unknown initialization heading: ${name}`);
228
251
  if (targets.has(name)) throw new CliError(`Duplicate initialization heading: ${name}`);
252
+ seenHeading = true;
229
253
  const content = [];
230
254
  for (let next = index + 1; next < inputLines.length; next += 1) {
231
255
  if (/^### /.test(inputLines[next]) || /^## /.test(inputLines[next])) break;
@@ -255,10 +279,8 @@ function initialContent(skeletonLines, input) {
255
279
  async function initialize(options) {
256
280
  const root = rootOption(options);
257
281
  const name = requireOption(options, "--name");
258
- const state = options["--state"] ?? "active";
259
- if (!TASK_STATES.has(state)) throw new CliError(`Invalid task state: ${state}`);
260
282
  const input = await readInput(options["--input"], cwd());
261
- const text = initialContent(canonicalLines(name, state), input);
283
+ const text = initialContent(canonicalLines(name), input);
262
284
  parseDocument(text);
263
285
  const created = await createRecordPath(root, name, slugify(name));
264
286
  try {
@@ -7,29 +7,35 @@ description: "Use when verifying work or a claim after implementation, tests, bu
7
7
 
8
8
  Determine what fresh direct evidence establishes about a specific claim at the boundary where it must hold.
9
9
 
10
- Verification establishes facts, not review judgment, authority, or permission to continue. A passing command is not automatically a supported outcome. Test order is not evidence of test validity or implementation correctness.
10
+ The **Rules** below are binding; if one cannot be met, stop and say why. Everything else is **judgment**: follow its stated goal, and adapt when a different choice serves that goal better.
11
+
12
+ ## Rules
13
+
14
+ - Verification establishes facts, not review judgment, authority, or permission to continue. A Supported claim permits judgment of the result; it does not establish a review Pass, close a Slice, or authorize delivery.
15
+ - Never encode a guess as an expected result. When expected behavior is unsettled or sources conflict, return the issue through [Workflow](../../skills/workflow/SKILL.md) or [Decision Gate](../../skills/decision-gate/SKILL.md).
16
+ - Run active checks (tests, reproductions, benchmarks, instrumentation) only when the current agreement covers them. Otherwise return the purpose, observer, expected evidence, and stop condition to Workflow and wait.
17
+ - Never rerun until unfavorable evidence disappears, and never change valid tests, implementation, Specs, policy, or acceptance merely to make a signal green. Preserve failures, contradictions, and partial or reduced-fidelity results.
18
+ - If a revised check establishes less than the original, the original claim stays Inconclusive unless other evidence establishes it at the required boundary; expose that limitation before acceptance rather than carry the stronger claim into the report.
19
+ - Missing evidence is not zero, safe, passed, or probably correct. Never invent a receipt or an earlier failing run that was not observed. If the user skips a check, leave the corresponding claim unverified.
20
+ - Base reports on actual assertions, outputs, and the state examined, not on test names or on the requested work phrased as accomplishments.
11
21
 
12
22
  ## Establish The Claim And Its Source
13
23
 
14
- Before choosing a check, identify the behavior or property, the required observing boundary, and the source of its expectation. Keep this compact when obvious; do not let an available test silently narrow a broader claim.
24
+ Goal: know exactly what is being claimed, where it must hold, and why it is expected, before choosing a check.
15
25
 
16
- Claims may concern behavior, a reported failure, structural validity, interfaces, integration, host lifecycle, installed artifacts, failure or recovery semantics, performance, or repository state.
26
+ Identify the behavior or property, the required observing boundary, and the source of its expectation. Keep this compact when obvious; do not let an available test silently narrow a broader claim. Claims may concern behavior, a reported failure, structural validity, interfaces, integration, host lifecycle, installed artifacts, failure or recovery semantics, performance, or repository state.
17
27
 
18
28
  Distinguish required acceptance from an optional stronger assertion. If evidence for an optional claim is unavailable, qualify or withdraw that claim rather than automatically add implementation. A required guarantee remains unresolved until supported or explicitly changed by its owner.
19
29
 
20
- When expected behavior is unsettled or sources conflict, return the issue through [Workflow](../../skills/workflow/SKILL.md) or [Decision Gate](../../skills/decision-gate/SKILL.md). Do not encode a guess as an expected result.
21
-
22
30
  ## Choose Sufficient Evidence
23
31
 
24
- Use the smallest direct observation that can disagree with the claim. Before relying on an unfamiliar fixture for substantial dependent work, establish that it reaches the required observing stage and distinguishes the relevant wrong behavior. Reuse an adequate existing fixture; observer preparation is not a mandatory separate prototype.
25
-
26
- For saved evidence, identify what code/artifact, check, configuration, and environment it examined, using existing outputs and source identities where available. Compare relevant subsequent changes, including new files and generated output. HEAD alone does not identify an uncommitted candidate. Reuse results whose applicability remains supported; rerun only affected evidence when authorized. Do not repeat checks merely because another activity or context began.
32
+ Goal: the smallest observation that could actually disagree with the claim, at the right boundary.
27
33
 
28
- Keep two facts separate: the observation occurred, and it applies to the current state. A later edit does not erase an earlier pass; uncertain applicability does not certify the new candidate. Report missing identity or output rather than inventing a receipt or building a new evidence store.
34
+ Use the smallest direct observation that can disagree with the claim. Before relying on an unfamiliar fixture for substantial dependent work, establish that it reaches the required observing stage and distinguishes the relevant wrong behavior. Reuse an adequate existing fixture; observer preparation is not a mandatory separate prototype.
29
35
 
30
- Run active checks only when covered by the current agreement, including contained verification. Otherwise return the purpose, observer, expected evidence, and stop condition to Workflow and wait. A test, reproduction, benchmark, or instrumentation is active evidence generation even before production edits.
36
+ For saved evidence, identify what code or artifact, check, configuration, and environment it examined, using existing outputs and source identities where available. Compare relevant later changes, including new files and generated output; HEAD alone does not identify an uncommitted candidate. Keep two facts separate: the observation occurred, and it applies to the current state. A later edit does not erase an earlier pass, and uncertain applicability does not certify the new candidate. Report missing identity or output rather than building a new evidence store. Reuse results whose applicability remains supported; rerun only affected evidence, and do not repeat checks merely because another activity or context began.
31
37
 
32
- Use [Action Selection](../../skills/action-selection/SKILL.md) when several observers are plausible or the likely interaction is broad. It selects and bounds the interaction; Verify Work retains the claim and interprets support. An already-selected focused check needs no extra tool-selection ceremony.
38
+ Use [Action Selection](../../skills/action-selection/SKILL.md) when several observers are plausible or the likely interaction is broad; Verify Work keeps the claim and interprets support. An already-selected focused check needs no extra selection.
33
39
 
34
40
  Read the relevant reference before designing or selecting evidence:
35
41
 
@@ -40,10 +46,12 @@ Read the relevant reference before designing or selecting evidence:
40
46
 
41
47
  Browser-performance claims need both browser fidelity and performance methodology. Loading a reference does not expand the required claim.
42
48
 
43
- Passing tests do not prove behavior they do not exercise. Source inspection does not prove runtime execution; a helper call does not prove registration, native dispatch, or installed-package behavior. A happy path does not establish failure handling.
49
+ Passing tests do not prove behavior they do not exercise. Source inspection does not prove runtime execution; a helper call does not prove registration, native dispatch, or installed-package behavior; a happy path does not establish failure handling.
44
50
 
45
51
  ## Run Without Manufacturing A Signal
46
52
 
53
+ Goal: the observation reflects the candidate, not the harness or the schedule.
54
+
47
55
  Before parallel checks, compare what each may write or remove. Serialize checks that share generated directories, caches, outputs, package roots, fixture state, ports, databases, or intentionally stale artifacts. Reproduce a concurrency-induced failure under a non-overlapping schedule before attributing it to production code.
48
56
 
49
57
  For each covered check:
@@ -52,14 +60,15 @@ For each covered check:
52
60
  2. Inspect output, exit status, assertions, and lower-level evidence that could contradict its summary.
53
61
  3. Confirm that the intended path ran and the observer matched the claim.
54
62
  4. Compare the observation with the independent expectation and its source.
55
- 5. Preserve failures, contradictions, and partial or reduced-fidelity results.
56
63
 
57
- Do not rerun until unfavorable evidence disappears or change valid tests, implementation, Specs, policy, or acceptance merely to make a signal green. Before revising a material assertion after failure, use Test Design to compare what the old and new checks establish. Repair source-backed observer or expectation errors; a weaker replacement leaves the original claim Inconclusive unless other evidence establishes it. Expose that limitation before acceptance rather than carry the stronger claim into the report.
64
+ Before revising a material assertion after failure, use Test Design to compare what the old and new checks establish. Repair source-backed observer or expectation errors.
58
65
 
59
- Tests may be written before or after implementation as the task warrants. No formal test-first sequence is required. If using a failure to support diagnosis or regression claims, actually observe it at the relevant boundary; do not invent an earlier failing run from a later passing test.
66
+ Tests may be written before or after implementation as the task warrants; no test-first sequence is required, and test order is not evidence of validity. When a failure supports a diagnosis or regression claim, actually observe it at the relevant boundary.
60
67
 
61
68
  ## Classify Execution And Support Separately
62
69
 
70
+ Goal: never let a check's outcome stand in for what the evidence proves.
71
+
63
72
  **Check result** describes the selected check's defined assertion:
64
73
 
65
74
  - **Passed:** it executed validly and the assertion held.
@@ -76,15 +85,15 @@ A check may Pass by observing an expected rejection or failure.
76
85
  - **Inconclusive:** available evidence cannot establish or refute it at that boundary.
77
86
  - **Unavailable:** required evidence cannot currently be obtained safely or reliably.
78
87
 
79
- A Passed check can leave the claim Inconclusive. A Failed check can expose a defect, invalid expectation, environment problem, or source conflict. Classify only the supported conclusion. Missing evidence is not zero, safe, passed, or probably correct.
80
-
81
- If the user skips a check, respect that choice and leave the corresponding claim unverified. Use [Diagnose Failure](../../skills/diagnose-failure/SKILL.md) when contradictions are unexplained or failure repeats.
88
+ A Passed check can leave the claim Inconclusive. A Failed check can expose a defect, an invalid expectation, an environment problem, or a source conflict; classify only the supported conclusion. Use [Diagnose Failure](../../skills/diagnose-failure/SKILL.md) when contradictions are unexplained or failure repeats.
82
89
 
83
90
  ## Return Evidence To Its Owner
84
91
 
85
- Return the observation and its limits to the activity using the claim. Base the report on actual assertions, outputs, and the state examined—not test names or the requested work phrased as accomplishments. Distinguish implemented behavior, exercised checks, and supported claims; list missing material cases even when the suite passes. Supported verification permits judgment of the result; it does not itself establish review Pass, close a Slice, or authorize delivery.
92
+ Goal: the activity using the claim gets the observation and its limits, and nothing it did not ask for.
93
+
94
+ Return the observation and its limits to the activity using the claim. Distinguish implemented behavior, exercised checks, and supported claims, and list missing material cases even when the suite passes.
86
95
 
87
- When answering a review Needs evidence item, preserve its pointer, claim, required observing boundary, previous evidence limit, and why the gap mattered. Return the result to the receiving agent for adjudication:
96
+ When answering a review Needs evidence item, preserve its pointer, claim, required observing boundary, previous evidence limit, and why the gap mattered, and return the result to the receiving agent for adjudication:
88
97
 
89
98
  - Supported may settle the factual gap without settling the review.
90
99
  - Contradicted may establish or invalidate a finding or expose a source conflict.