@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/NOTICE +14 -0
- package/README.md +26 -31
- package/capabilities/cognitive-routing/SKILL.md +106 -161
- package/capabilities/cognitive-routing/references/both-mode.md +36 -0
- package/capabilities/cognitive-routing/references/executor-mode.md +48 -0
- package/capabilities/cognitive-routing/references/helper-mode.md +47 -0
- package/capabilities/compaction/SKILL.md +49 -0
- package/capabilities/compaction/references/summary-format.md +239 -0
- package/command-surface.json +4 -0
- package/gemini-extension.json +1 -1
- package/hooks/shared/runtime-context.mjs +28 -30
- package/licenses/Apache-2.0.txt +55 -0
- package/package.json +22 -9
- package/pi-extension/README.md +41 -0
- package/pi-extension/dist/cognitive-routing/assembler.js +393 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/config.js +5 -2
- package/pi-extension/dist/cognitive-routing/economics.js +45 -0
- package/pi-extension/dist/cognitive-routing/event-schema.js +388 -0
- package/pi-extension/dist/{session-sources/events.js → cognitive-routing/event-store.js} +103 -40
- package/pi-extension/dist/cognitive-routing/gate.js +241 -0
- package/pi-extension/dist/cognitive-routing/handlers.js +899 -0
- package/pi-extension/dist/cognitive-routing/handoffs.js +350 -0
- package/pi-extension/dist/cognitive-routing/model-control.js +467 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/projection.js +229 -56
- package/pi-extension/dist/cognitive-routing/provenance.js +81 -0
- package/pi-extension/dist/cognitive-routing/runtime.js +396 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/schemas.js +43 -7
- package/pi-extension/dist/cognitive-routing/session.js +221 -0
- package/pi-extension/dist/{session-sources → cognitive-routing}/sources.js +99 -85
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/state.js +37 -378
- package/pi-extension/dist/cognitive-routing/status.js +80 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/tools.js +12 -12
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/types.js +5 -11
- package/pi-extension/dist/compaction/carry.js +191 -0
- package/pi-extension/dist/compaction/config.js +22 -0
- package/pi-extension/dist/compaction/controller.js +471 -0
- package/pi-extension/dist/compaction/harness.js +77 -0
- package/pi-extension/dist/compaction/results.js +158 -0
- package/pi-extension/dist/compaction/thresholds.js +35 -0
- package/pi-extension/dist/compaction/tool.js +57 -0
- package/pi-extension/dist/host/branch.js +13 -0
- package/pi-extension/dist/host/canonical.js +12 -0
- package/pi-extension/dist/host/catalog.js +77 -0
- package/pi-extension/dist/host/config.js +374 -0
- package/pi-extension/dist/host/projection-tags.js +60 -0
- package/pi-extension/dist/host/prompts.js +82 -0
- package/pi-extension/dist/{session-sources → host}/read-only-session.js +112 -1
- package/pi-extension/dist/{runtime → host}/request-history.js +121 -39
- package/pi-extension/dist/host/runtime-state.js +189 -0
- package/pi-extension/dist/{settings/settings-ui.js → host/settings/freeflow-command.js} +620 -410
- package/pi-extension/dist/{settings/settings-tui.js → host/settings/settings-view.js} +186 -73
- package/pi-extension/dist/host/staging.js +28 -0
- package/pi-extension/dist/host/status.js +101 -0
- package/pi-extension/dist/index.js +423 -173
- package/pi-extension/dist/provider-support/cache/anchor.js +21 -0
- package/pi-extension/dist/provider-support/cache/anthropic.js +102 -0
- package/pi-extension/dist/provider-support/cache/health.js +73 -0
- package/pi-extension/dist/provider-support/cache/index.js +58 -0
- package/pi-extension/dist/provider-support/cache/keepalive.js +210 -0
- package/pi-extension/dist/provider-support/cache/monitor.js +17 -0
- package/pi-extension/dist/provider-support/cache/openai-responses.js +48 -0
- package/pi-extension/dist/provider-support/effort.js +13 -0
- package/pi-extension/dist/provider-support/index.js +44 -3
- package/pi-extension/dist/provider-support/{astra → openai}/adapter.js +46 -29
- package/pi-extension/dist/provider-support/{astra → openai}/history.js +10 -8
- package/pi-extension/dist/provider-support/{astra → openai}/index.js +8 -4
- package/pi-extension/dist/provider-support/{astra → openai}/session-state.js +29 -38
- package/pi-extension/dist/provider-support/routes.js +17 -0
- package/pi-extension/dist/tool-execution/apply-patch/grammar.js +23 -0
- package/pi-extension/dist/tool-execution/apply-patch/match.js +38 -0
- package/pi-extension/dist/tool-execution/apply-patch/parser.js +177 -0
- package/pi-extension/dist/tool-execution/apply-patch/plan.js +201 -0
- package/pi-extension/dist/tool-execution/apply-patch/tool.js +84 -0
- package/pi-extension/dist/tool-execution/background.js +238 -0
- package/pi-extension/dist/tool-execution/bash-guard.js +23 -0
- package/pi-extension/dist/tool-execution/config.js +22 -0
- package/pi-extension/dist/tool-execution/edit-messages.js +120 -0
- package/pi-extension/dist/tool-execution/file-state.js +138 -0
- package/pi-extension/dist/tool-execution/file-tracking.js +143 -0
- package/pi-extension/dist/tool-execution/prompt.js +54 -0
- package/pi-extension/dist/tool-execution/tools.js +16 -0
- package/pi-extension/freeflow/README.md +9 -0
- package/plugin.json +1 -1
- package/runtime/prompts/cognitive-routing.md +4 -4
- package/runtime/prompts/core.md +22 -75
- package/runtime/prompts/interaction-contract.md +15 -8
- package/runtime/prompts/tool-execution.md +38 -0
- package/runtime/prompts/working-method.md +108 -0
- package/skills/action-selection/SKILL.md +39 -47
- package/skills/action-selection/references/trajectory-stalls.md +2 -0
- package/skills/bypass/SKILL.md +2 -2
- package/skills/commit-work/SKILL.md +31 -83
- package/skills/commit-work/references/git-state-edges.md +1 -1
- package/skills/commit-work/references/simple-push.md +2 -0
- package/skills/commit-work/references/staging-decisions.md +1 -1
- package/skills/decision-gate/SKILL.md +31 -50
- package/skills/design-for-depth/SKILL.md +44 -53
- package/skills/design-for-depth/references/design-pressure-signals.md +46 -144
- package/skills/design-for-depth/references/implementation-decisions.md +1 -1
- package/skills/design-for-depth/references/interface-design-loop.md +5 -36
- package/skills/design-for-depth/references/module-and-dependency-design.md +2 -32
- package/skills/design-for-depth/references/state-and-failure-boundaries.md +2 -13
- package/skills/diagnose-failure/SKILL.md +39 -24
- package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +2 -0
- package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +2 -2
- package/skills/discuss/SKILL.md +35 -11
- package/skills/discuss/references/discussion-continuity.md +7 -27
- package/skills/evaluate-skill/SKILL.md +49 -80
- package/skills/evaluate-skill/references/evaluation-definition-schema.md +35 -5
- package/skills/evaluate-skill/references/evaluation-design.md +14 -14
- package/skills/evaluate-skill/references/execution-and-evidence.md +26 -5
- package/skills/evaluate-skill/references/review-and-revision.md +3 -1
- package/skills/evaluate-skill/scripts/lib/definitions.mjs +49 -8
- package/skills/evaluate-skill/scripts/lib/pi.mjs +14 -7
- package/skills/evaluate-skill/scripts/lib/review.mjs +196 -0
- package/skills/evaluate-skill/scripts/lib/runner.mjs +45 -8
- package/skills/evaluate-skill/scripts/lib/sandbox.mjs +2 -0
- package/skills/evaluate-skill/scripts/lib/trials.mjs +103 -0
- package/skills/evaluate-skill/scripts/lib/view.mjs +67 -1
- package/skills/evaluate-skill/scripts/pi-command-tool.mjs +144 -0
- package/skills/evaluate-skill/scripts/pi-guard.mjs +30 -13
- package/skills/evaluate-skill/scripts/skill-eval.mjs +114 -34
- package/skills/execute-work/SKILL.md +41 -9
- package/skills/execute-work/references/code-practices.md +2 -0
- package/skills/execute-work/references/execute-work-edges.md +11 -1
- package/skills/handoff/SKILL.md +1 -1
- package/skills/handoff/references/handoff-templates.md +1 -1
- package/skills/review-artifact/SKILL.md +42 -41
- package/skills/review-artifact/references/adjudicate-artifact-review.md +1 -1
- package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +1 -1
- package/skills/review-work/SKILL.md +38 -32
- package/skills/review-work/references/adjudicate-work-review.md +1 -1
- package/skills/review-work/references/independent-work-reviewer-contract.md +1 -1
- package/skills/review-work/references/security-risk-lens.md +2 -4
- package/skills/setup-freeflow/SKILL.md +3 -1
- package/skills/setup-freeflow/references/activation-contract.md +7 -13
- package/skills/setup-freeflow/references/host-setup.md +3 -3
- package/skills/track-work/SKILL.md +113 -161
- package/skills/track-work/references/working-record-format.md +189 -350
- package/skills/track-work/scripts/lib/document.mjs +62 -6
- package/skills/track-work/scripts/lib/format.mjs +16 -1
- package/skills/track-work/scripts/lib/store.mjs +9 -8
- package/skills/track-work/scripts/lib/transitions.mjs +79 -4
- package/skills/track-work/scripts/working-record.mjs +34 -12
- package/skills/verify-work/SKILL.md +29 -20
- package/skills/verify-work/references/browser-runtime-evidence.md +1 -5
- package/skills/verify-work/references/integration-evidence.md +2 -0
- package/skills/verify-work/references/performance-evidence.md +3 -1
- package/skills/verify-work/references/test-design.md +3 -1
- package/skills/workflow/SKILL.md +65 -21
- package/skills/workflow/references/domain-skill-composition.md +5 -3
- package/skills/write-docs/SKILL.md +83 -0
- package/skills/write-docs/references/documentation-shapes.md +119 -0
- package/skills/write-docs/scripts/docs-check.mjs +139 -0
- package/skills/write-plan/SKILL.md +40 -40
- package/skills/write-plan/references/plan-shapes.md +1 -1
- package/skills/write-skill/SKILL.md +107 -147
- package/skills/write-skill/references/activation-boundaries.md +2 -0
- package/skills/write-skill/references/agent-first-instructions.md +22 -10
- package/skills/write-skill/references/progressive-disclosure.md +4 -2
- package/skills/write-skill/references/skill-dependency-graphs.md +2 -0
- package/skills/write-skill/scripts/lib/similarity.mjs +56 -0
- package/skills/write-skill/scripts/skill-author.mjs +18 -2
- package/skills/write-spec/SKILL.md +34 -41
- package/skills/write-spec/references/artifact-standards.md +1 -1
- package/skills/write-spec/references/decision-records.md +2 -0
- package/skills/write-spec/references/spec-shapes.md +1 -1
- package/capabilities/context-virtualization/SKILL.md +0 -103
- package/capabilities/conversation-history/SKILL.md +0 -53
- package/hooks/tests/check-host-adapters.test.mjs +0 -82
- package/hooks/tests/check-runtime-context-hook.sh +0 -212
- package/pi-extension/dist/cognitive-routing-v2/runtime.js +0 -2181
- package/pi-extension/dist/context-virtualization/commands.js +0 -89
- package/pi-extension/dist/context-virtualization/projector.js +0 -42
- package/pi-extension/dist/context-virtualization/runtime.js +0 -378
- package/pi-extension/dist/context-virtualization/state.js +0 -57
- package/pi-extension/dist/context-virtualization/types.js +0 -10
- package/pi-extension/dist/conversation-history/passages.js +0 -66
- package/pi-extension/dist/conversation-history/ranking.js +0 -105
- package/pi-extension/dist/conversation-history/runtime.js +0 -226
- package/pi-extension/dist/conversation-history/search.js +0 -56
- package/pi-extension/dist/conversation-history/types.js +0 -1
- package/pi-extension/dist/freeflow-context/resolver.js +0 -120
- package/pi-extension/dist/freeflow-context/runtime.js +0 -80
- package/pi-extension/dist/freeflow-context/source-projector.js +0 -95
- package/pi-extension/dist/freeflow-context/tool.js +0 -314
- package/pi-extension/dist/freeflow-context/types.js +0 -12
- package/pi-extension/dist/freeflow-context/visibility.js +0 -28
- package/pi-extension/dist/integrations/mcp-config.js +0 -29
- package/pi-extension/dist/runtime/runtime-context.js +0 -754
- package/pi-extension/dist/runtime/runtime-identity.js +0 -5
- package/pi-extension/dist/session-sources/provenance.js +0 -32
- package/pi-extension/dist/ui/text-component.js +0 -57
- package/pi-extension/src/cognitive-routing-v2/budget.ts +0 -42
- package/pi-extension/src/cognitive-routing-v2/config.ts +0 -173
- package/pi-extension/src/cognitive-routing-v2/projection.ts +0 -375
- package/pi-extension/src/cognitive-routing-v2/render.ts +0 -277
- package/pi-extension/src/cognitive-routing-v2/runtime.ts +0 -2241
- package/pi-extension/src/cognitive-routing-v2/schemas.ts +0 -180
- package/pi-extension/src/cognitive-routing-v2/state.ts +0 -839
- package/pi-extension/src/cognitive-routing-v2/tools.ts +0 -62
- package/pi-extension/src/cognitive-routing-v2/types.ts +0 -254
- package/pi-extension/src/context-virtualization/commands.ts +0 -111
- package/pi-extension/src/context-virtualization/projector.ts +0 -62
- package/pi-extension/src/context-virtualization/runtime.ts +0 -428
- package/pi-extension/src/context-virtualization/state.ts +0 -79
- package/pi-extension/src/context-virtualization/types.ts +0 -92
- package/pi-extension/src/conversation-history/passages.ts +0 -81
- package/pi-extension/src/conversation-history/ranking.ts +0 -155
- package/pi-extension/src/conversation-history/runtime.ts +0 -250
- package/pi-extension/src/conversation-history/search.ts +0 -79
- package/pi-extension/src/conversation-history/types.ts +0 -31
- package/pi-extension/src/freeflow-context/resolver.ts +0 -139
- package/pi-extension/src/freeflow-context/runtime.ts +0 -100
- package/pi-extension/src/freeflow-context/source-projector.ts +0 -106
- package/pi-extension/src/freeflow-context/tool.ts +0 -366
- package/pi-extension/src/freeflow-context/types.ts +0 -70
- package/pi-extension/src/freeflow-context/visibility.ts +0 -53
- package/pi-extension/src/index.ts +0 -390
- package/pi-extension/src/integrations/mcp-config.ts +0 -32
- package/pi-extension/src/provider-support/astra/adapter.ts +0 -108
- package/pi-extension/src/provider-support/astra/history.ts +0 -145
- package/pi-extension/src/provider-support/astra/index.ts +0 -12
- package/pi-extension/src/provider-support/astra/session-state.ts +0 -113
- package/pi-extension/src/provider-support/index.ts +0 -5
- package/pi-extension/src/runtime/request-history.ts +0 -243
- package/pi-extension/src/runtime/runtime-context.ts +0 -860
- package/pi-extension/src/runtime/runtime-identity.ts +0 -8
- package/pi-extension/src/session-sources/events.ts +0 -230
- package/pi-extension/src/session-sources/provenance.ts +0 -39
- package/pi-extension/src/session-sources/read-only-session.ts +0 -170
- package/pi-extension/src/session-sources/sources.ts +0 -334
- package/pi-extension/src/settings/settings-tui.ts +0 -501
- package/pi-extension/src/settings/settings-ui.ts +0 -1652
- package/pi-extension/src/ui/text-component.ts +0 -66
- package/pi-extension/tests/cache-reuse/history.test.js +0 -117
- package/pi-extension/tests/cache-reuse/native.test.js +0 -91
- package/pi-extension/tests/cache-reuse/settings.test.js +0 -211
- package/pi-extension/tests/cognitive-routing-v2/communication.test.js +0 -577
- package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +0 -183
- package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +0 -557
- package/pi-extension/tests/cognitive-routing-v2/native.test.js +0 -474
- package/pi-extension/tests/cognitive-routing-v2/performance.test.js +0 -87
- package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +0 -300
- package/pi-extension/tests/cognitive-routing-v2/projection.test.js +0 -241
- package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +0 -1034
- package/pi-extension/tests/cognitive-routing-v2/render.test.js +0 -244
- package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +0 -423
- package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +0 -521
- package/pi-extension/tests/cognitive-routing-v2/state.test.js +0 -586
- package/pi-extension/tests/context-virtualization/extension.test.js +0 -775
- package/pi-extension/tests/context-virtualization/projector.test.js +0 -110
- package/pi-extension/tests/context-virtualization/runtime.test.js +0 -230
- package/pi-extension/tests/conversation-history/runtime.test.js +0 -98
- package/pi-extension/tests/conversation-history/search.test.js +0 -97
- package/pi-extension/tests/fixtures/pi-host.js +0 -4
- package/pi-extension/tests/fixtures/routing-native.js +0 -191
- package/pi-extension/tests/integration/mode-free-core.test.js +0 -220
- package/pi-extension/tests/integration/pi-extension.test.js +0 -1447
- package/pi-extension/tests/integration/prompt-architecture.test.js +0 -447
- package/pi-extension/tests/integration/unaffected-regressions.test.js +0 -478
- package/pi-extension/tests/provider-support/astra-boundaries.test.js +0 -75
- package/pi-extension/tests/provider-support/astra.test.js +0 -306
- package/pi-extension/tests/provider-support/cli.test.js +0 -92
- package/pi-extension/tests/provider-support/native.test.js +0 -183
- package/pi-extension/tests/settings/settings-tui.test.js +0 -243
- package/pi-extension/tsconfig.json +0 -16
- package/runtime/prompts/context-virtualization.md +0 -3
- package/runtime/prompts/conversation-history.md +0 -3
- package/skills/design-for-depth/references/software-design-philosophy.md +0 -202
- /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/budget.js +0 -0
- /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
|
-
|
|
279
|
-
|
|
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
|
-
|
|
334
|
+
sliceEnd: sliceContentEnd,
|
|
335
|
+
nextActionHeadingIndex: nextActionHeading.index,
|
|
308
336
|
currentSlice,
|
|
309
|
-
|
|
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",
|
|
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",
|
|
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(["
|
|
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
|
-
|
|
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)
|
|
150
|
+
const match = /^task-(\d{3,})-/.exec(entry.name);
|
|
151
|
+
if (entry.isDirectory() && match) highest = Math.max(highest, Number(match[1]));
|
|
151
152
|
}
|
|
152
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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> [--
|
|
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
|
-
|
|
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", "--
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|