@haposoft/cafekit 0.16.7 → 0.18.1
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/README.md +14 -22
- package/bin/__tests__/codex-hooks-ownership.test.js +2 -2
- package/bin/__tests__/codex-hooks.test.js +169 -272
- package/bin/__tests__/codex-native.test.js +100 -202
- package/bin/__tests__/develop-contract.test.js +122 -1413
- package/bin/__tests__/docs-sync-on-demand.test.js +93 -0
- package/bin/__tests__/grok-envelope.test.js +0 -56
- package/bin/__tests__/hook-payload.test.js +4 -6
- package/bin/__tests__/legacy-authority-decoupled.test.js +154 -0
- package/bin/__tests__/legacy-hooks-retired.test.js +221 -0
- package/bin/__tests__/omp-bridge.test.js +20 -17
- package/bin/__tests__/omp-hooks.test.js +0 -19
- package/bin/__tests__/optional-skill-inventory.test.js +112 -4
- package/bin/__tests__/orca-session.test.js +0 -2
- package/bin/__tests__/package-inventory.test.js +90 -925
- package/bin/__tests__/runtime-dir.test.js +0 -3
- package/bin/__tests__/runtime-schema.test.js +3 -2
- package/bin/__tests__/skill-routing-source.test.js +27 -15
- package/bin/__tests__/spec-narrowing.test.js +30 -161
- package/bin/__tests__/specs-v2-execution-closeout.test.js +0 -437
- package/bin/__tests__/test-runtime-dependency-closure.cjs +1 -2
- package/bin/__tests__/ui-ux-skill.test.js +101 -0
- package/bin/__tests__/usage-hook-retired.test.js +114 -0
- package/bin/__tests__/validator-grounding.test.js +18 -2949
- package/bin/lib/managed-writer.js +2 -3
- package/bin/phases/codex-runtime.js +27 -0
- package/bin/phases/copy-payload.js +13 -28
- package/bin/phases/omp-runtime.js +25 -0
- package/package.json +2 -3
- package/src/claude/CLAUDE.md +3 -10
- package/src/claude/agents/brainstormer.md +7 -3
- package/src/claude/agents/code-auditor.md +44 -56
- package/src/claude/agents/debugger.md +8 -88
- package/src/claude/agents/deployer.md +3 -5
- package/src/claude/agents/docs-keeper.md +6 -14
- package/src/claude/agents/git-ops.md +2 -4
- package/src/claude/agents/implementer.md +12 -16
- package/src/claude/agents/inspector.md +4 -3
- package/src/claude/agents/project-manager.md +9 -17
- package/src/claude/agents/researcher.md +9 -3
- package/src/claude/agents/spec-maker.md +15 -15
- package/src/claude/agents/test-runner.md +8 -11
- package/src/claude/agents/ui-ux-designer.md +0 -1
- package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -1
- package/src/claude/hooks/__tests__/spec-gate.test.js +244 -333
- package/src/claude/hooks/__tests__/spec-state-touched.test.js +160 -0
- package/src/claude/hooks/__tests__/state.test.js +68 -0
- package/src/claude/hooks/__tests__/statusline.test.js +140 -11
- package/src/claude/hooks/agent.cjs +2 -1
- package/src/claude/hooks/lib/hook-payload.cjs +2 -2
- package/src/claude/hooks/rules.cjs +2 -1
- package/src/claude/hooks/session.cjs +0 -1
- package/src/claude/hooks/spec-gate.cjs +17 -83
- package/src/claude/hooks/spec-state.cjs +142 -39
- package/src/claude/hooks/state.cjs +21 -2
- package/src/claude/migration-manifest.json +42 -22
- package/src/claude/output-styles/coding-level-0-eli5.md +9 -9
- package/src/claude/output-styles/coding-level-1-junior.md +3 -3
- package/src/claude/output-styles/coding-level-2-mid.md +3 -3
- package/src/claude/output-styles/coding-level-3-senior.md +2 -2
- package/src/claude/output-styles/coding-level-4-lead.md +5 -5
- package/src/claude/output-styles/coding-level-5-god.md +6 -6
- package/src/claude/rules/ai-dev-rules.md +2 -3
- package/src/claude/rules/hook-protocols.md +8 -15
- package/src/claude/rules/manage-docs.md +9 -23
- package/src/claude/rules/orchestrator.md +3 -4
- package/src/claude/rules/review-audit-self-decision.md +3 -3
- package/src/claude/rules/skill-domain-routing.md +1 -3
- package/src/claude/rules/skill-workflow-routing.md +4 -5
- package/src/claude/rules/state-sync.md +3 -12
- package/src/claude/rules/workflow.md +11 -19
- package/src/claude/runtime.schema.json +5 -3
- package/src/claude/scripts/generate-skill-catalog.cjs +7 -6
- package/src/claude/scripts/provenance.cjs +23 -5
- package/src/claude/scripts/spec-receipt.cjs +24 -121
- package/src/claude/scripts/spec-resolver.cjs +41 -458
- package/src/claude/scripts/workflow-policy.cjs +3 -1801
- package/src/claude/settings/settings.json +1 -32
- package/src/claude/skills/agent-browser/SKILL.md +1 -1
- package/src/claude/skills/{question → ask}/SKILL.md +9 -75
- package/src/claude/skills/brainstorm/SKILL.md +43 -131
- package/src/claude/skills/chrome-devtools/SKILL.md +14 -31
- package/src/claude/skills/code-review/SKILL.md +29 -81
- package/src/claude/skills/code-review/references/spec-compliance-review.md +4 -8
- package/src/claude/skills/debug/SKILL.md +29 -149
- package/src/claude/skills/develop/SKILL.md +26 -83
- package/src/claude/skills/develop/references/parallel-waves.md +0 -8
- package/src/claude/skills/develop/references/quality-gate.md +15 -13
- package/src/claude/skills/develop/references/subagent-patterns.md +0 -6
- package/src/claude/skills/docs/SKILL.md +2 -2
- package/src/claude/skills/docs/references/update-workflow.md +1 -1
- package/src/claude/skills/fix/SKILL.md +153 -0
- package/src/claude/skills/{hotfix → fix}/references/diagnosis-protocol.md +2 -2
- package/src/claude/skills/{hotfix → fix}/references/parallel-patterns.md +2 -2
- package/src/claude/skills/{hotfix → fix}/references/prevention-gate.md +3 -3
- package/src/claude/skills/{hotfix → fix}/references/review-cycle.md +1 -1
- package/src/claude/skills/git/SKILL.md +48 -9
- package/src/claude/skills/git/references/commit-protocols.md +35 -16
- package/src/claude/skills/git/references/worktree-blueprint.md +42 -6
- package/src/claude/skills/orca/SKILL.md +9 -8
- package/src/claude/skills/research/SKILL.md +17 -46
- package/src/claude/skills/scout/SKILL.md +61 -0
- package/src/claude/skills/{inspect → scout}/references/internal-inspection.md +26 -10
- package/src/claude/skills/specs/SKILL.md +22 -29
- package/src/claude/skills/specs/references/review.md +8 -6
- package/src/claude/skills/specs/references/templates.md +34 -41
- package/src/claude/skills/sync/SKILL.md +16 -45
- package/src/claude/skills/sync/references/rebind-and-audit.md +59 -0
- package/src/claude/skills/sync/references/sync-protocols.md +4 -13
- package/src/claude/skills/test/SKILL.md +39 -104
- package/src/claude/skills/test/references/execution-strategy.md +93 -21
- package/src/claude/skills/test/references/failure-triage.md +1 -1
- package/src/claude/skills/ui-ux/LICENSE +21 -0
- package/src/claude/skills/ui-ux/SKILL.md +497 -0
- package/src/claude/skills/ui-ux/references/brand-tokens.md +90 -0
- package/src/claude/skills/ui-ux/references/budgets.md +113 -0
- package/src/claude/skills/ui-ux/references/checklist.md +271 -0
- package/src/claude/skills/ui-ux/references/components/accordion.md +246 -0
- package/src/claude/skills/ui-ux/references/components/avatar.md +153 -0
- package/src/claude/skills/ui-ux/references/components/banner.md +43 -0
- package/src/claude/skills/ui-ux/references/components/breadcrumb.md +201 -0
- package/src/claude/skills/ui-ux/references/components/button.md +142 -0
- package/src/claude/skills/ui-ux/references/components/card.md +113 -0
- package/src/claude/skills/ui-ux/references/components/charts.md +376 -0
- package/src/claude/skills/ui-ux/references/components/chat.md +132 -0
- package/src/claude/skills/ui-ux/references/components/choice-controls.md +369 -0
- package/src/claude/skills/ui-ux/references/components/comment-thread.md +17 -0
- package/src/claude/skills/ui-ux/references/components/description-list.md +116 -0
- package/src/claude/skills/ui-ux/references/components/empty-state.md +98 -0
- package/src/claude/skills/ui-ux/references/components/file-upload.md +107 -0
- package/src/claude/skills/ui-ux/references/components/inline-edit.md +128 -0
- package/src/claude/skills/ui-ux/references/components/input.md +161 -0
- package/src/claude/skills/ui-ux/references/components/list-row.md +56 -0
- package/src/claude/skills/ui-ux/references/components/loading.md +198 -0
- package/src/claude/skills/ui-ux/references/components/logo.md +147 -0
- package/src/claude/skills/ui-ux/references/components/otp-input.md +43 -0
- package/src/claude/skills/ui-ux/references/components/quantity-input.md +100 -0
- package/src/claude/skills/ui-ux/references/components/range-slider.md +16 -0
- package/src/claude/skills/ui-ux/references/components/small-controls.md +340 -0
- package/src/claude/skills/ui-ux/references/components/sortable-header.md +86 -0
- package/src/claude/skills/ui-ux/references/components/tag-input.md +14 -0
- package/src/claude/skills/ui-ux/references/components/timeline.md +38 -0
- package/src/claude/skills/ui-ux/references/components/tree.md +30 -0
- package/src/claude/skills/ui-ux/references/design-process.md +650 -0
- package/src/claude/skills/ui-ux/references/layouts/app-kanban.html +303 -0
- package/src/claude/skills/ui-ux/references/layouts/app.md +1369 -0
- package/src/claude/skills/ui-ux/references/layouts/form.md +430 -0
- package/src/claude/skills/ui-ux/references/layouts/overlay.md +552 -0
- package/src/claude/skills/ui-ux/references/layouts/pricing.md +204 -0
- package/src/claude/skills/ui-ux/references/locked-rules.md +35 -0
- package/src/claude/skills/ui-ux/references/principles.md +408 -0
- package/src/claude/skills/ui-ux/references/refactor.md +245 -0
- package/src/claude/skills/ui-ux/references/responsive.md +195 -0
- package/src/claude/skills/ui-ux/references/review.md +528 -0
- package/src/claude/skills/ui-ux/references/rules-color.md +623 -0
- package/src/claude/skills/ui-ux/references/rules-form.md +235 -0
- package/src/claude/skills/ui-ux/references/rules-state.md +680 -0
- package/src/claude/skills/ui-ux/references/rules-type.md +369 -0
- package/src/claude/skills/ui-ux/references/styles.md +311 -0
- package/src/claude/skills/ui-ux/references/system.md +219 -0
- package/src/claude/skills/ui-ux/references/tailwind-v4-traps.md +237 -0
- package/src/claude/skills/ui-ux/references/tokens.css +362 -0
- package/src/claude/skills/ui-ux/scripts/lint-skill.mjs +216 -0
- package/src/claude/skills/ui-ux/scripts/package.json +9 -0
- package/src/claude/skills/ui-ux/scripts/probe.mjs +4447 -0
- package/src/claude/skills/ui-ux-pro-max/SKILL.md +25 -80
- package/src/claude/skills/web-testing/SKILL.md +2 -20
- package/src/claude/status.cjs +149 -144
- package/src/codex/AGENTS.md +8 -13
- package/src/codex/hooks/agent.cjs +2 -1
- package/src/codex/hooks/lib/spec-receipt.cjs +4 -32
- package/src/codex/hooks/lib/spec-utils.cjs +6 -52
- package/src/codex/hooks/rules.cjs +2 -1
- package/src/codex/hooks/session.cjs +0 -2
- package/src/codex/hooks/spec-gate.cjs +13 -81
- package/src/codex/hooks/spec-state.cjs +37 -41
- package/src/codex/hooks.json +0 -20
- package/src/codex/rules/state-sync.md +3 -12
- package/src/common/AGENTS.md +6 -12
- package/src/omp/extensions/cafekit-bridge.mjs +6 -6
- package/bin/__tests__/change-firewall.semantic-firewall.test.js +0 -2133
- package/bin/__tests__/finalizer-pass-gate.semantic-firewall.test.js +0 -342
- package/bin/__tests__/release-preflight.semantic-firewall.test.js +0 -586
- package/bin/__tests__/review-receipt-c2.semantic-firewall.test.js +0 -378
- package/bin/__tests__/specs-v2-policy-and-scaffold.test.js +0 -665
- package/bin/__tests__/specs-v2-validator-grounder-contract.test.js +0 -1132
- package/src/claude/hooks/__tests__/completion-authority.test.js +0 -990
- package/src/claude/hooks/__tests__/semantic-review-authority.test.js +0 -259
- package/src/claude/hooks/completion-authority-check.cjs +0 -56
- package/src/claude/hooks/completion-authority-state.cjs +0 -527
- package/src/claude/hooks/completion-authority.cjs +0 -163
- package/src/claude/hooks/lib/context.cjs +0 -616
- package/src/claude/hooks/lib/detect.cjs +0 -474
- package/src/claude/hooks/semantic-review-authority.cjs +0 -154
- package/src/claude/hooks/task-scaffold-guard.cjs +0 -114
- package/src/claude/hooks/usage.cjs +0 -197
- package/src/claude/scripts/change-firewall.cjs +0 -1748
- package/src/claude/scripts/spec-authoring-digest.cjs +0 -151
- package/src/claude/scripts/spec-authoring-validation.cjs +0 -126
- package/src/claude/scripts/spec-final-state.cjs +0 -290
- package/src/claude/scripts/spec-ground.cjs +0 -650
- package/src/claude/scripts/spec-readiness.cjs +0 -425
- package/src/claude/scripts/spec-scaffold.cjs +0 -2317
- package/src/claude/scripts/spec-semantic-model.cjs +0 -396
- package/src/claude/scripts/validate-spec-output.cjs +0 -3941
- package/src/claude/skills/code-review/references/verification-gate.md +0 -46
- package/src/claude/skills/delegate/SKILL.md +0 -124
- package/src/claude/skills/delegate/references/codex-delegation.md +0 -71
- package/src/claude/skills/delegate/references/grok-delegation.md +0 -84
- package/src/claude/skills/hotfix/SKILL.md +0 -289
- package/src/claude/skills/inspect/SKILL.md +0 -142
- package/src/claude/skills/loop/SKILL.md +0 -114
- package/src/claude/skills/loop/references/bounded-loop-protocol.md +0 -104
- package/src/claude/skills/loop/references/metric-and-guard-contract.md +0 -76
- package/src/claude/skills/route/SKILL.md +0 -94
- package/src/claude/skills/route/references/agent-timing.md +0 -46
- package/src/claude/skills/route/references/chaining-patterns.md +0 -53
- package/src/claude/skills/route/references/task-taxonomy.md +0 -48
- package/src/claude/skills/specs/templates/design.md +0 -82
- package/src/claude/skills/specs/templates/requirements-init.md +0 -8
- package/src/claude/skills/specs/templates/requirements.md +0 -42
- package/src/claude/skills/specs/templates/spec-state.json +0 -56
- package/src/claude/skills/specs/templates/task.md +0 -47
- package/src/codex/hooks/completion-authority-check.cjs +0 -67
- package/src/codex/hooks/completion-authority-state.cjs +0 -527
- package/src/codex/hooks/completion-authority.cjs +0 -109
- package/src/codex/hooks/semantic-review-authority.cjs +0 -130
- package/src/codex/hooks/task-scaffold-guard.cjs +0 -63
- /package/src/claude/skills/{question → ask}/templates/question.md +0 -0
- /package/src/claude/skills/{hotfix → fix}/references/escalation-tactics.md +0 -0
- /package/src/claude/skills/{hotfix → fix}/references/workflow-specialized.md +0 -0
|
@@ -34,15 +34,14 @@ Keep implementation simple, scoped, and verifiable.
|
|
|
34
34
|
|
|
35
35
|
## Skill And Tooling Use
|
|
36
36
|
|
|
37
|
-
-
|
|
38
|
-
- If a skill plausibly matches the task, prefer its workflow and references over an ad hoc plan.
|
|
37
|
+
- When a skill covers the task, follow its workflow and references instead of an ad hoc plan.
|
|
39
38
|
- Use documentation lookup when current external docs matter.
|
|
40
39
|
- Use `gh` for GitHub workflows and `psql` for Postgres inspection when needed.
|
|
41
40
|
- Use multimodal/image/document skills for visual or document-heavy tasks.
|
|
42
41
|
|
|
43
42
|
## Git Hygiene
|
|
44
43
|
|
|
45
|
-
- Lint before commit.
|
|
44
|
+
- Lint before commit when the project has a linter.
|
|
46
45
|
- Run required tests before push.
|
|
47
46
|
- Keep commits focused.
|
|
48
47
|
- Use conventional commits.
|
|
@@ -4,28 +4,21 @@ Hooks are instruction boundaries. Do not bypass or work around them.
|
|
|
4
4
|
|
|
5
5
|
## Privacy Block Hook
|
|
6
6
|
|
|
7
|
-
When a tool call
|
|
7
|
+
When a tool call touches a sensitive file, or the hook cannot classify it safely,
|
|
8
|
+
the privacy hook returns a permission decision of `ask` with the reason, and the
|
|
9
|
+
host asks the user to approve or deny that one call. Do not ask again yourself.
|
|
10
|
+
On a host whose hook can only deny, the call is blocked; ask the user instead of
|
|
11
|
+
retrying it another way.
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
...
|
|
12
|
-
@@PRIVACY_PROMPT_END@@
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Required flow:
|
|
16
|
-
|
|
17
|
-
1. Parse the JSON payload from the marker.
|
|
18
|
-
2. Ask the user for approval using the available user-question tool and the prompt/options from the payload.
|
|
19
|
-
3. If approved, retry only the blocked action.
|
|
20
|
-
4. If denied, continue without reading that file or performing that blocked action.
|
|
13
|
+
- If approved, the call runs; do not widen it to other files.
|
|
14
|
+
- If denied, continue without reading that file or performing that blocked action.
|
|
21
15
|
|
|
22
16
|
Never use another command, path, encoding, or side channel to access a privacy-blocked file without explicit approval.
|
|
23
17
|
|
|
24
18
|
## State And Spec Hooks
|
|
25
19
|
|
|
26
20
|
- If a hook reports state drift, run the appropriate sync/audit flow before continuing.
|
|
27
|
-
- If a hook rejects task completion, keep the task `in_progress` or `blocked` until proof exists.
|
|
28
|
-
- Do not mark a task `done` unless the matching task file contains a valid verification receipt.
|
|
21
|
+
- If a hook rejects task completion, keep the task `in_progress` or `blocked` until proof exists; the receipt contract lives in `state-sync.md`.
|
|
29
22
|
|
|
30
23
|
## Hook Failure Handling
|
|
31
24
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Living Documents
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
When the project keeps these core documents in `./docs`, keep them current (a project may have only some of them):
|
|
6
6
|
|
|
7
7
|
| Document | Purpose |
|
|
8
8
|
|----------|---------|
|
|
@@ -14,8 +14,6 @@ The project maintains these core documents in `./docs`:
|
|
|
14
14
|
### Freshness Rule
|
|
15
15
|
|
|
16
16
|
- Before updating any doc, check its last modified date
|
|
17
|
-
- If a doc hasn't been updated in >2 weeks while development is active, flag it for review
|
|
18
|
-
- The `docs-keeper` agent should proactively scan for stale docs during weekly reviews
|
|
19
17
|
|
|
20
18
|
## When to Update
|
|
21
19
|
|
|
@@ -26,7 +24,6 @@ The `docs-keeper` agent is responsible for keeping these documents current. Trig
|
|
|
26
24
|
- A significant feature ships or a critical bug is resolved
|
|
27
25
|
- Security patches are applied or dependencies change
|
|
28
26
|
- Project scope or timeline shifts
|
|
29
|
-
- Weekly progress reviews are due
|
|
30
27
|
|
|
31
28
|
### Update Discipline
|
|
32
29
|
|
|
@@ -43,7 +40,7 @@ Use [Keep a Changelog](https://keepachangelog.com/) convention:
|
|
|
43
40
|
## [version] - YYYY-MM-DD
|
|
44
41
|
|
|
45
42
|
### Added
|
|
46
|
-
- Feature description (#PR-number)
|
|
43
|
+
- Feature description (#PR-number when there is one)
|
|
47
44
|
|
|
48
45
|
### Fixed
|
|
49
46
|
- Bug fix description (#issue-number)
|
|
@@ -74,38 +71,27 @@ specs/
|
|
|
74
71
|
|
|
75
72
|
Task files are direct children beside `plan.md`; do not place them in a task
|
|
76
73
|
subdirectory.
|
|
77
|
-
The plan records the
|
|
74
|
+
The plan records the GATE-SCOPE scope decision, explicit exclusions, acceptance
|
|
78
75
|
criteria, and task mapping. Each task owns one usable outcome, bounded paths,
|
|
79
76
|
dependencies, acceptance, and a runnable Verification Plan.
|
|
80
77
|
|
|
81
78
|
### Gates and execution handoff
|
|
82
79
|
|
|
83
|
-
-
|
|
84
|
-
-
|
|
80
|
+
- GATE-SCOPE: the user chooses EXPAND, KEEP, or CUT before the plan is written.
|
|
81
|
+
- GATE-REVIEW: the user accepts, rejects, or revises deduplicated adversarial findings.
|
|
85
82
|
- Specs stops after planning; implementation begins only through a new explicit
|
|
86
83
|
Develop invocation.
|
|
87
|
-
-
|
|
84
|
+
- GATE-DONE: after execution, current receipts and limitations are shown and the user
|
|
88
85
|
decides whether the feature is complete.
|
|
89
86
|
|
|
90
87
|
### Task state and proof
|
|
91
88
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
`Verification: PASS`, runtime-derived Base and Head values, and non-empty
|
|
95
|
-
fenced current output before the task becomes done.
|
|
89
|
+
Task status, the inline `## Receipt` and its proof contract are defined in
|
|
90
|
+
`state-sync.md`.
|
|
96
91
|
|
|
97
92
|
Sync only observed state with surgical edits. Re-read the changed task after
|
|
98
93
|
every update; never infer missing proof, approval, or readiness. When a done
|
|
99
94
|
task changes user-facing behavior, architecture, API contracts, operations, or
|
|
100
95
|
project status, classify docs impact and update only affected existing docs.
|
|
101
96
|
|
|
102
|
-
Comply with the overarching rules in
|
|
103
|
-
|
|
104
|
-
## Legacy specification layout
|
|
105
|
-
|
|
106
|
-
Existing features containing `spec.json`, nested `tasks/task-R*.md`, or other
|
|
107
|
-
legacy kernel artifacts keep their installed adapters and original layout.
|
|
108
|
-
Preserve `task_registry`, `semantic_model`, `planning_depth`, lane,
|
|
109
|
-
`execution_tier`, machine authority, typed topology, separate receipts, and
|
|
110
|
-
feature closeout files. Do not migrate them as part of unrelated documentation
|
|
111
|
-
work.
|
|
97
|
+
Comply with the overarching rules in `ai-dev-rules.md`.
|
|
@@ -22,10 +22,9 @@ Example prompt:
|
|
|
22
22
|
|
|
23
23
|
## Resource Constraints
|
|
24
24
|
|
|
25
|
-
- Each subagent has
|
|
26
|
-
- Spawning many parallel agents degrades system performance — check available CPU/memory before scaling out. For parallel implementation waves the cap is 3 concurrent agents by default, never more than 5 (each agent carries
|
|
25
|
+
- Each subagent has its own context window — scope tasks to fit comfortably within it
|
|
26
|
+
- Spawning many parallel agents degrades system performance — check available CPU/memory before scaling out. For parallel implementation waves the cap is 3 concurrent agents by default, never more than 5 (each agent carries its own context window)
|
|
27
27
|
- Prefer fewer, well-scoped agents over many overlapping ones
|
|
28
|
-
- Include system resource info (from hook injection) when delegating tasks so subagents can self-regulate
|
|
29
28
|
|
|
30
29
|
---
|
|
31
30
|
|
|
@@ -132,7 +131,7 @@ Docs: [docs path]
|
|
|
132
131
|
|
|
133
132
|
| ❌ Vague | ✅ Precise |
|
|
134
133
|
|----------|-----------|
|
|
135
|
-
| "Pick up where we left off" | "Add the `/users` POST endpoint per `
|
|
134
|
+
| "Pick up where we left off" | "Add the `/users` POST endpoint per `task-02-api.md`" |
|
|
136
135
|
| "Fix the issues we talked about" | "Add null-check in `auth.ts:45` — root cause: missing input validation" |
|
|
137
136
|
| "Explore the codebase" | "Read `src/api/routes.ts`, then add the missing handler" |
|
|
138
137
|
| Dumping 50+ lines of chat history | A 5-line summary with file paths |
|
|
@@ -12,10 +12,10 @@ When rejecting an audit concern, name the verification source briefly.
|
|
|
12
12
|
|
|
13
13
|
## User Decisions
|
|
14
14
|
|
|
15
|
-
Do not silently undo an explicit user decision. This covers the
|
|
16
|
-
|
|
15
|
+
Do not silently undo an explicit user decision. This covers the GATE-SCOPE scope choice,
|
|
16
|
+
GATE-REVIEW finding dispositions, and GATE-DONE completion judgement, along with thresholds,
|
|
17
17
|
selected libraries, feature scope, schema shape, pricing, timelines, compliance
|
|
18
|
-
choices, and UX trade-offs. Those gates are defined in `
|
|
18
|
+
choices, and UX trade-offs. Those gates are defined in `state-sync.md`; this rule
|
|
19
19
|
governs only how a later review may treat them.
|
|
20
20
|
|
|
21
21
|
If an audit suggests reversing a user decision, present:
|
|
@@ -27,6 +27,4 @@ an entry condition for the primary. If zero valid capabilities match, continue
|
|
|
27
27
|
inline when safe or name the gap. If multiple catalog entries expose the same
|
|
28
28
|
public identity, do not auto-route; require explicit user disambiguation.
|
|
29
29
|
|
|
30
|
-
Never infer an optional document capability from this rule.
|
|
31
|
-
explicit-only numeric optimization capability unless the user invokes it with
|
|
32
|
-
its required bounded metric/guard contract.
|
|
30
|
+
Never infer an optional document capability from this rule.
|
|
@@ -9,9 +9,9 @@ workflow.
|
|
|
9
9
|
1. If the user names a valid installed skill, use it directly.
|
|
10
10
|
2. If one obvious low-risk installed skill covers the intent, use it directly.
|
|
11
11
|
3. For direct factual conversation, answer directly or use the installed
|
|
12
|
-
evidence-answer capability; do not
|
|
13
|
-
4. For ambiguous, multi-step, multi-domain, or elevated/high-risk work,
|
|
14
|
-
|
|
12
|
+
evidence-answer capability; do not spawn agents for ceremony.
|
|
13
|
+
4. For ambiguous, multi-step, multi-domain, or elevated/high-risk work, classify
|
|
14
|
+
it yourself and compose the shortest valid chain below.
|
|
15
15
|
|
|
16
16
|
Resolve every abstract link against the current runtime catalog. An absent,
|
|
17
17
|
invalid, folder-mismatched, duplicate, or retired entry is not routable. Continue
|
|
@@ -28,5 +28,4 @@ owner, entry condition, and observable exit per retained link. Diagnosis does
|
|
|
28
28
|
not authorize repair; implementation does not authorize commit, push, deploy,
|
|
29
29
|
publish, or release; no route expands the user's existing authority.
|
|
30
30
|
|
|
31
|
-
Use `skill-domain-routing.md` to resolve domain capability slots.
|
|
32
|
-
optimization capability remains explicit-only and must never be auto-routed.
|
|
31
|
+
Use `skill-domain-routing.md` to resolve domain capability slots.
|
|
@@ -51,19 +51,10 @@ proves only the boundary it executed.
|
|
|
51
51
|
|
|
52
52
|
## Human gates and workflow ownership
|
|
53
53
|
|
|
54
|
-
-
|
|
55
|
-
-
|
|
54
|
+
- GATE-SCOPE records the user's scope decision before the plan is written.
|
|
55
|
+
- GATE-REVIEW records the user's disposition of deduplicated adversarial findings.
|
|
56
56
|
- Develop starts only after a new explicit user invocation and executes one
|
|
57
57
|
unblocked task at a time.
|
|
58
58
|
- Sync changes only observed file state and never starts implementation.
|
|
59
|
-
-
|
|
59
|
+
- GATE-DONE occurs after current receipts and limitations are shown; the user decides
|
|
60
60
|
whether the requested feature is complete.
|
|
61
|
-
|
|
62
|
-
## Legacy compatibility
|
|
63
|
-
|
|
64
|
-
Existing packets containing `spec.json`, nested `tasks/task-R*.md`, or other
|
|
65
|
-
legacy kernel artifacts stay on their installed adapters. Preserve their
|
|
66
|
-
`task_registry`, `semantic_model`, `planning_depth`, lane,
|
|
67
|
-
`execution_tier`, typed topology, separate receipts, and feature closeout
|
|
68
|
-
contract. Do not migrate that machine authority or storage shape while syncing
|
|
69
|
-
an unrelated process-first packet.
|
|
@@ -6,16 +6,16 @@ Use the CafeKit loop: **Understand -> Plan -> Execute -> Verify -> Sync**.
|
|
|
6
6
|
|
|
7
7
|
- Read `./README.md` before feature planning or coding.
|
|
8
8
|
- Read the active spec/task file when one exists.
|
|
9
|
-
-
|
|
9
|
+
- Use a CafeKit skill when its description matches the request; how to choose is in `skill-workflow-routing.md`.
|
|
10
10
|
- Inspect only the code needed to understand the affected area.
|
|
11
|
-
- Use `
|
|
11
|
+
- Use `cf:scout` or focused search when structure is unclear.
|
|
12
12
|
|
|
13
13
|
## 2. Plan
|
|
14
14
|
|
|
15
15
|
- For non-trivial features, use `/cf:specs` to challenge the minimum scope,
|
|
16
|
-
open
|
|
16
|
+
open GATE-SCOPE, and create `specs/<feature>/plan.md` with flat
|
|
17
17
|
`task-NN-*.md` files beside it.
|
|
18
|
-
- After adversarial review, open
|
|
18
|
+
- After adversarial review, open GATE-REVIEW and apply only the findings the user accepts
|
|
19
19
|
or revises. Specs never starts implementation.
|
|
20
20
|
- Start implementation only after a new explicit `/cf:develop` invocation,
|
|
21
21
|
then select one unblocked task at a time.
|
|
@@ -24,16 +24,18 @@ Use the CafeKit loop: **Understand -> Plan -> Execute -> Verify -> Sync**.
|
|
|
24
24
|
- `Outcome`
|
|
25
25
|
- `Scope`
|
|
26
26
|
- `Ownership`
|
|
27
|
+
- `Steps`
|
|
27
28
|
- `Acceptance`
|
|
28
29
|
- `Dependencies`
|
|
29
30
|
- `Verification Plan`
|
|
31
|
+
- `Failure Protocol`
|
|
30
32
|
- If these are missing or too vague to verify, route back to spec correction.
|
|
31
33
|
|
|
32
34
|
## 3. Execute
|
|
33
35
|
|
|
34
36
|
- Implement only the active scope.
|
|
35
37
|
- Modify existing files directly; do not create duplicate "enhanced" variants.
|
|
36
|
-
- Keep named contracts from
|
|
38
|
+
- Keep named contracts from the plan and task intact.
|
|
37
39
|
- Do not use placeholder wiring, process-local stand-ins, or fake adapters as completion proof.
|
|
38
40
|
|
|
39
41
|
## 4. Verify
|
|
@@ -53,16 +55,15 @@ Use the CafeKit loop: **Understand -> Plan -> Execute -> Verify -> Sync**.
|
|
|
53
55
|
sole writer of task Status and proof.
|
|
54
56
|
- Keep exactly one `Status:` field. Write or replace the task's final inline
|
|
55
57
|
`## Receipt` before setting `Status: done`.
|
|
56
|
-
-
|
|
57
|
-
|
|
58
|
-
fenced current output. Never invent, copy, or infer missing proof.
|
|
58
|
+
- The canonical Receipt is defined in `state-sync.md`. Never invent, copy, or
|
|
59
|
+
infer missing proof.
|
|
59
60
|
- When the binding to Base and Head applies, and what the gate does and does not
|
|
60
61
|
detect, are stated once in `state-sync.md`.
|
|
61
62
|
- Re-read each edited task and reconcile its plan row, dependencies, acceptance
|
|
62
63
|
mapping, status, and receipt without rewriting unrelated bytes.
|
|
63
|
-
-
|
|
64
|
+
- Update affected existing docs when a completed task changes public or architecture docs.
|
|
64
65
|
- After all requested work has current proof, show evidence and limitations at
|
|
65
|
-
|
|
66
|
+
GATE-DONE. The user decides whether the feature is complete.
|
|
66
67
|
|
|
67
68
|
## Production Or CI Issues
|
|
68
69
|
|
|
@@ -73,12 +74,3 @@ Use the CafeKit loop: **Understand -> Plan -> Execute -> Verify -> Sync**.
|
|
|
73
74
|
5. Review before syncing or shipping.
|
|
74
75
|
|
|
75
76
|
Do not patch symptoms before diagnosis unless the issue is a trivial syntax/type/lint failure with an obvious local cause.
|
|
76
|
-
|
|
77
|
-
## Legacy compatibility
|
|
78
|
-
|
|
79
|
-
When an existing feature contains `spec.json`, nested `tasks/task-R*.md`, or
|
|
80
|
-
other legacy kernel artifacts, keep using the installed adapter. Preserve its
|
|
81
|
-
`task_registry`, `semantic_model`, `planning_depth`, lane,
|
|
82
|
-
`execution_tier`, typed boundaries, separate receipts, and final feature
|
|
83
|
-
receipt. Do not project that machine authority or storage shape into a new
|
|
84
|
-
process-first packet.
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
"scaffold_guard": {
|
|
59
59
|
"type": "boolean",
|
|
60
60
|
"default": true,
|
|
61
|
-
"
|
|
61
|
+
"deprecated": true,
|
|
62
|
+
"description": "Not honoured. The task-scaffold guard that read this key was removed; the key is kept so existing runtime.json files stay valid."
|
|
62
63
|
},
|
|
63
64
|
"tollgate": {
|
|
64
65
|
"type": "boolean",
|
|
@@ -75,12 +76,13 @@
|
|
|
75
76
|
"usage": {
|
|
76
77
|
"type": "object",
|
|
77
78
|
"additionalProperties": false,
|
|
78
|
-
"
|
|
79
|
+
"deprecated": true,
|
|
80
|
+
"description": "Not honoured. The usage hook that read this key was removed; the statusline takes quota from the rate_limits Claude Code sends. Kept so existing runtime.json files stay valid.",
|
|
79
81
|
"properties": {
|
|
80
82
|
"enabled": {
|
|
81
83
|
"type": "boolean",
|
|
82
84
|
"default": true,
|
|
83
|
-
"description": "
|
|
85
|
+
"description": "Not honoured. Nothing fetches quota data any more."
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
},
|
|
@@ -15,12 +15,13 @@ const RETIRED_DIRECTORIES = new Set([
|
|
|
15
15
|
'mobile-development',
|
|
16
16
|
'devops',
|
|
17
17
|
'react-best-practices',
|
|
18
|
+
'inspect',
|
|
19
|
+
'hotfix',
|
|
20
|
+
'question',
|
|
21
|
+
'loop',
|
|
22
|
+
'route',
|
|
23
|
+
'delegate',
|
|
18
24
|
]);
|
|
19
|
-
const PUBLIC_SUFFIX_BY_DIRECTORY = {
|
|
20
|
-
hotfix: 'fix',
|
|
21
|
-
inspect: 'scout',
|
|
22
|
-
question: 'ask',
|
|
23
|
-
};
|
|
24
25
|
|
|
25
26
|
function usage() {
|
|
26
27
|
console.log(`Usage:
|
|
@@ -124,7 +125,7 @@ function publicIdentity(name) {
|
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
function expectedIdentity(directory) {
|
|
127
|
-
return `cf:${
|
|
128
|
+
return `cf:${directory}`;
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
function diagnostic(code, directory, message) {
|
|
@@ -228,13 +228,30 @@ function identity(input) {
|
|
|
228
228
|
return { root, specsRoot, specFile, feature, session, mode };
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
+
// A Stop hook checks every done receipt against one checkout. Without a memo each
|
|
232
|
+
// receipt re-read the whole repository twice, so the gate grew with the number of
|
|
233
|
+
// done tasks (minutes on a large specs root). The memo is opt-in and lives only in
|
|
234
|
+
// the enabling process; every other caller still captures a fresh snapshot.
|
|
235
|
+
const snapshotMemo = new Map();
|
|
236
|
+
let snapshotMemoEnabled = false;
|
|
237
|
+
function enableSnapshotMemo() { snapshotMemoEnabled = true; }
|
|
238
|
+
|
|
239
|
+
function captureSnapshot(root, specsRoot) {
|
|
240
|
+
const key = `${root}\0${specsRoot}`;
|
|
241
|
+
if (snapshotMemoEnabled && snapshotMemo.has(key)) return snapshotMemo.get(key);
|
|
242
|
+
const base = gitBase(root, specsRoot);
|
|
243
|
+
const first = manifest(root, specsRoot);
|
|
244
|
+
const head = manifestDigest(first);
|
|
245
|
+
const second = manifest(root, specsRoot);
|
|
246
|
+
if (JSON.stringify(first) !== JSON.stringify(second) || gitBase(root, specsRoot) !== base) fail('worktree_race', 'checkout changed during provenance capture');
|
|
247
|
+
const snapshot = { base, head };
|
|
248
|
+
if (snapshotMemoEnabled) snapshotMemo.set(key, snapshot);
|
|
249
|
+
return snapshot;
|
|
250
|
+
}
|
|
251
|
+
|
|
231
252
|
function deriveRuntimeProvenance(input) {
|
|
232
253
|
const current = identity(input);
|
|
233
|
-
const base =
|
|
234
|
-
const first = manifest(current.root, current.specsRoot);
|
|
235
|
-
const head = manifestDigest(first);
|
|
236
|
-
const second = manifest(current.root, current.specsRoot);
|
|
237
|
-
if (JSON.stringify(first) !== JSON.stringify(second) || gitBase(current.root, current.specsRoot) !== base) fail('worktree_race', 'checkout changed during provenance capture');
|
|
254
|
+
const { base, head } = captureSnapshot(current.root, current.specsRoot);
|
|
238
255
|
const stable = {
|
|
239
256
|
schema_version: CONTEXT_SCHEMA_VERSION,
|
|
240
257
|
project_root: current.root,
|
|
@@ -328,6 +345,7 @@ module.exports = {
|
|
|
328
345
|
createReceiptBinding,
|
|
329
346
|
deriveRuntimeContext: deriveRuntimeProvenance,
|
|
330
347
|
deriveRuntimeProvenance,
|
|
348
|
+
enableSnapshotMemo,
|
|
331
349
|
isTrustedRuntimeContext,
|
|
332
350
|
recomputeRuntimeContext: recomputeRuntimeProvenance,
|
|
333
351
|
recomputeRuntimeProvenance,
|
|
@@ -6,12 +6,6 @@ const path = require('path');
|
|
|
6
6
|
const { spawnSync } = require('child_process');
|
|
7
7
|
const { annotatedMarkdownLines } = require('./spec-resolver.cjs');
|
|
8
8
|
|
|
9
|
-
const EVIDENCE_HEADINGS = new Set([
|
|
10
|
-
'Evidence',
|
|
11
|
-
'Task Test Plan & Verification Evidence',
|
|
12
|
-
'Verification & Evidence',
|
|
13
|
-
]);
|
|
14
|
-
|
|
15
9
|
function inside(root, target) {
|
|
16
10
|
const relative = path.relative(path.resolve(root), path.resolve(target));
|
|
17
11
|
return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
@@ -40,34 +34,6 @@ function safeRead(root, relativePath) {
|
|
|
40
34
|
}
|
|
41
35
|
}
|
|
42
36
|
|
|
43
|
-
function canonicalTaskReceiptPath(taskPath) {
|
|
44
|
-
if (typeof taskPath !== 'string') return null;
|
|
45
|
-
const match = taskPath.match(/^tasks\/(task-[A-Za-z0-9][A-Za-z0-9._-]*\.md)$/);
|
|
46
|
-
return match ? `receipts/${match[1]}` : null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function evidenceBody(text, policy = {}) {
|
|
50
|
-
const lines = String(text || '').split('\n');
|
|
51
|
-
let start = -1;
|
|
52
|
-
let level = 0;
|
|
53
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
54
|
-
const match = lines[index].match(/^(#{2,3})\s+(.+?)\s*$/);
|
|
55
|
-
if (match && EVIDENCE_HEADINGS.has(match[2])) {
|
|
56
|
-
start = index + 1;
|
|
57
|
-
level = match[1].length;
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (start < 0) return null;
|
|
62
|
-
let end = lines.length;
|
|
63
|
-
for (let index = start; index < lines.length; index += 1) {
|
|
64
|
-
const heading = lines[index].match(/^(#{1,6})\s+/);
|
|
65
|
-
const tap = typeof policy.isTapMetadataHeading === 'function' && policy.isTapMetadataHeading(lines[index]);
|
|
66
|
-
if (heading && heading[1].length <= level && !tap) { end = index; break; }
|
|
67
|
-
}
|
|
68
|
-
return lines.slice(start, end).join('\n');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
37
|
function workflowReceiptBody(text, policy = {}) {
|
|
72
38
|
const lines = annotatedMarkdownLines(text);
|
|
73
39
|
const starts = [];
|
|
@@ -149,11 +115,18 @@ function workflowVerificationCommand(taskText) {
|
|
|
149
115
|
if (current) current.push(line);
|
|
150
116
|
}
|
|
151
117
|
if (sections.length !== 1) return null;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
118
|
+
// Only a top-level `- Command:` counts: an indented one belongs to another field such as a
|
|
119
|
+
// Counterexample, and one under a `###` sub-heading is a side step that must not outrank
|
|
120
|
+
// the main block. When commands exist only under sub-headings, they are used as before.
|
|
121
|
+
const commandOf = (line) => line.match(/^-\s+(?:\*\*Command\*\*|Command)\s*:\s*(.*?)\s*$/i);
|
|
122
|
+
const firstSub = sections[0].findIndex((line) => /^###\s+/.test(line));
|
|
123
|
+
const main = (firstSub === -1 ? sections[0] : sections[0].slice(0, firstSub)).map(commandOf).filter(Boolean);
|
|
124
|
+
const commands = (main.length ? main : sections[0].map(commandOf).filter(Boolean))
|
|
155
125
|
.map((match) => normalizedCommand(match[1]));
|
|
156
|
-
|
|
126
|
+
// A plan may declare several commands when its proof takes more than one step. The last
|
|
127
|
+
// is canonical: earlier ones are prerequisites, and binding the Receipt to any of them
|
|
128
|
+
// would let a task close on its cheapest step. None declared still fails closed.
|
|
129
|
+
return commands.length >= 1 ? commands[commands.length - 1] : null;
|
|
157
130
|
}
|
|
158
131
|
|
|
159
132
|
function workflowCommandFailures(taskText, body) {
|
|
@@ -167,20 +140,6 @@ function workflowCommandFailures(taskText, body) {
|
|
|
167
140
|
: ['command_identity'];
|
|
168
141
|
}
|
|
169
142
|
|
|
170
|
-
function plannedField(taskText, name) {
|
|
171
|
-
const plan = String(taskText || '').split(/^##\s+Verification Plan\s*$/m)[1];
|
|
172
|
-
if (!plan) return null;
|
|
173
|
-
const value = fieldValues(plan.split(/^##\s+/m)[0], `- **${name}**`)[0];
|
|
174
|
-
return value ? value.replace(/^`|`$/g, '').trim() : null;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function identity(body) {
|
|
178
|
-
const names = ['Verification', 'Command', 'Commands', 'Exit', 'Result', 'Base', 'Head', 'base_sha', 'head_sha'];
|
|
179
|
-
const fields = names.flatMap((name) => fieldValues(body, name).map((value) => [name.toLowerCase(), value.trim()]));
|
|
180
|
-
const artifacts = String(body || '').split('\n').filter((line) => /^\s*Artifacts?\s*:|^\s*Artifact\s+produced\b/i.test(line)).map((line) => line.trim());
|
|
181
|
-
return crypto.createHash('sha256').update(JSON.stringify({ fields, artifacts })).digest('hex');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
143
|
function tryGitBytes(root, args) {
|
|
185
144
|
const result = spawnSync('git', ['-C', root, ...args], { encoding: 'buffer', maxBuffer: 64 * 1024 * 1024 });
|
|
186
145
|
if (result.error || result.status !== 0) return null;
|
|
@@ -240,57 +199,6 @@ function canonicalFailures(body, task, runtimeContext, policy, { bindProvenance
|
|
|
240
199
|
return policy.validateCanonicalReceipt(body, options);
|
|
241
200
|
}
|
|
242
201
|
|
|
243
|
-
function separateMetadataFailures(body, taskPath, taskText) {
|
|
244
|
-
const failures = [];
|
|
245
|
-
const basename = path.posix.basename(taskPath);
|
|
246
|
-
if (fieldValues(body, 'Task').length !== 1 || fieldValues(body, 'Task')[0] !== basename) failures.push('task_identity');
|
|
247
|
-
if (fieldValues(body, 'Task path').length !== 1 || fieldValues(body, 'Task path')[0] !== taskPath) failures.push('task_identity');
|
|
248
|
-
if (!concrete(fieldValues(body, 'Expected')[0]) || !concrete(fieldValues(body, 'Observed')[0])) failures.push('expected_observed');
|
|
249
|
-
const plannedCommand = plannedField(taskText, 'Command');
|
|
250
|
-
const actualCommand = fieldValues(body, 'Command')[0] || fieldValues(body, 'Commands')[0];
|
|
251
|
-
if (plannedCommand && !/^N\/A\b/i.test(plannedCommand) && actualCommand !== plannedCommand) failures.push('command_identity');
|
|
252
|
-
for (const name of ['Negative path', 'Reachability']) {
|
|
253
|
-
const planned = plannedField(taskText, name);
|
|
254
|
-
if (planned && !/^(?:N\/A|none|not relevant)\b/i.test(planned) && !concrete(fieldValues(body, name)[0])) failures.push(name.toLowerCase().replace(' ', '_'));
|
|
255
|
-
}
|
|
256
|
-
return [...new Set(failures)];
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function readTaskProof(featureDir, taskPath, policy = {}) {
|
|
260
|
-
const receiptPath = canonicalTaskReceiptPath(taskPath);
|
|
261
|
-
if (typeof taskPath !== 'string' || !/^tasks\/[^/]+\.md$/.test(taskPath)) return { status: 'unsafe', taskPath, receiptPath: null };
|
|
262
|
-
const taskFile = safeRead(featureDir, taskPath);
|
|
263
|
-
if (taskFile.status !== 'ok') return { status: taskFile.status, taskPath, receiptPath };
|
|
264
|
-
const taskText = taskFile.bytes.toString('utf8');
|
|
265
|
-
const separate = receiptPath ? safeRead(featureDir, receiptPath) : { status: 'missing' };
|
|
266
|
-
const legacyBody = evidenceBody(taskText, policy);
|
|
267
|
-
if (separate.status === 'unsafe') return { status: 'unsafe', taskPath, receiptPath, taskFile };
|
|
268
|
-
if (separate.status === 'ok') {
|
|
269
|
-
const body = separate.bytes.toString('utf8');
|
|
270
|
-
return { status: 'ok', source: 'separate', body, receiptBytes: separate.bytes, receiptFile: separate, taskFile, taskText, taskPath, receiptPath, legacyBody };
|
|
271
|
-
}
|
|
272
|
-
if (legacyBody !== null) return { status: 'ok', source: 'legacy', body: legacyBody, receiptBytes: Buffer.from(legacyBody), taskFile, taskText, taskPath, receiptPath, legacyBody };
|
|
273
|
-
return { status: 'missing', taskPath, receiptPath, taskFile, taskText };
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function checkTaskReceipt(featureDir, taskPath, task, runtimeContext, policy) {
|
|
277
|
-
const proof = readTaskProof(featureDir, taskPath, policy);
|
|
278
|
-
const failures = [];
|
|
279
|
-
if (proof.status !== 'ok') return { ...proof, failures: [proof.status === 'unsafe' ? 'unsafe_path' : 'missing_receipt'] };
|
|
280
|
-
const status = proof.taskText.split('\n').find((line) => /^\s*(?:\*\*)?Status(?:\*\*)?\s*:/i.test(line));
|
|
281
|
-
if (!status || !/\bdone\b/i.test(status)) failures.push('task_status');
|
|
282
|
-
failures.push(...canonicalFailures(proof.body, task, runtimeContext, policy));
|
|
283
|
-
if (proof.source === 'separate') {
|
|
284
|
-
failures.push(...separateMetadataFailures(proof.body, taskPath, proof.taskText));
|
|
285
|
-
if (proof.legacyBody !== null
|
|
286
|
-
&& /^\s*Verification:\s*PASS\s*$/m.test(proof.legacyBody)
|
|
287
|
-
&& identity(proof.legacyBody) !== identity(proof.body)) failures.push('receipt_conflict');
|
|
288
|
-
}
|
|
289
|
-
const completedAt = task && task.completed_at;
|
|
290
|
-
if (typeof completedAt !== 'string' || !/^\d{4}-\d{2}-\d{2}T/.test(completedAt) || Number.isNaN(Date.parse(completedAt))) failures.push('completed_at');
|
|
291
|
-
return { ...proof, failures: [...new Set(failures)] };
|
|
292
|
-
}
|
|
293
|
-
|
|
294
202
|
function checkWorkflowTaskReceipt(featureDir, taskPath, runtimeContext, policy) {
|
|
295
203
|
const safeTaskPath = typeof taskPath === 'string' && /^task-[A-Za-z0-9][A-Za-z0-9._-]*\.md$/.test(taskPath);
|
|
296
204
|
if (!safeTaskPath) return { status: 'unsafe', taskPath, failures: ['unsafe_path'] };
|
|
@@ -354,12 +262,22 @@ const RECEIPT_FIX_HINTS = Object.freeze([
|
|
|
354
262
|
]);
|
|
355
263
|
|
|
356
264
|
/**
|
|
357
|
-
* Turn receipt check codes into one instruction naming what to change.
|
|
265
|
+
* Turn receipt check codes into one instruction naming what to change. When the receipt
|
|
266
|
+
* body is available, a provenance failure is refined: telling someone to add lines they
|
|
267
|
+
* already wrote sends them looking in the wrong place, and the common cause is a pair
|
|
268
|
+
* typed by hand — git short SHAs — rather than the pair this runtime derives.
|
|
358
269
|
*/
|
|
359
|
-
function receiptFixHint(failures) {
|
|
270
|
+
function receiptFixHint(failures, body) {
|
|
360
271
|
const codes = new Set(Array.isArray(failures) ? failures : []);
|
|
361
272
|
const hints = RECEIPT_FIX_HINTS.filter(([code]) => codes.has(code)).map(([, hint]) => hint);
|
|
362
273
|
if (hints.length === 0) return null;
|
|
274
|
+
if (codes.has('provenance') && typeof body === 'string'
|
|
275
|
+
&& fieldValues(body, 'Base').length > 0 && fieldValues(body, 'Head').length > 0) {
|
|
276
|
+
const index = hints.length - 1;
|
|
277
|
+
hints[index] = 'the `Base:` and `Head:` lines are present but are not the pair this runtime derives'
|
|
278
|
+
+ ' — re-derive them (`Base` is a 40-character commit id, `Head` a 64-character tree digest);'
|
|
279
|
+
+ ' git short SHAs typed by hand never match';
|
|
280
|
+
}
|
|
363
281
|
return hints.length === 1 ? hints[0] : `${hints.slice(0, -1).join('; ')}; and ${hints[hints.length - 1]}`;
|
|
364
282
|
}
|
|
365
283
|
|
|
@@ -409,32 +327,17 @@ function checkWorkflowReceiptSet(candidates, projectRoot, runtimeSession, policy
|
|
|
409
327
|
for (const taskPath of doneTasks) {
|
|
410
328
|
const proof = checkWorkflowTaskReceipt(candidate.featureDir, taskPath, runtimeContext, policy);
|
|
411
329
|
if (proof.failures.length > 0) {
|
|
412
|
-
failures.push({ featureName: candidate.featureName, taskPath, failures: proof.failures });
|
|
330
|
+
failures.push({ featureName: candidate.featureName, taskPath, failures: proof.failures, body: proof.body });
|
|
413
331
|
}
|
|
414
332
|
}
|
|
415
333
|
}
|
|
416
334
|
return { failures };
|
|
417
335
|
}
|
|
418
336
|
|
|
419
|
-
function checkFeatureReceipt(featureDir, runtimeContext, policy) {
|
|
420
|
-
const receipt = safeRead(featureDir, 'feature-receipt.md');
|
|
421
|
-
if (receipt.status !== 'ok') return { status: receipt.status, failures: [receipt.status === 'unsafe' ? 'unsafe_path' : 'missing_receipt'] };
|
|
422
|
-
const body = receipt.bytes.toString('utf8');
|
|
423
|
-
const failures = canonicalFailures(body, {}, runtimeContext, policy);
|
|
424
|
-
if (fieldValues(body, 'Feature').length !== 1 || fieldValues(body, 'Feature')[0] !== path.basename(featureDir)) failures.push('feature_identity');
|
|
425
|
-
if (!concrete(fieldValues(body, 'Expected')[0]) || !concrete(fieldValues(body, 'Observed')[0])) failures.push('expected_observed');
|
|
426
|
-
return { status: 'ok', source: 'feature', body, receiptBytes: receipt.bytes, receiptFile: receipt, failures: [...new Set(failures)] };
|
|
427
|
-
}
|
|
428
|
-
|
|
429
337
|
module.exports = {
|
|
430
|
-
canonicalTaskReceiptPath,
|
|
431
|
-
checkFeatureReceipt,
|
|
432
|
-
checkTaskReceipt,
|
|
433
338
|
checkWorkflowReceiptSet,
|
|
434
339
|
checkWorkflowTaskReceipt,
|
|
435
340
|
receiptFixHint,
|
|
436
|
-
evidenceBody,
|
|
437
|
-
readTaskProof,
|
|
438
341
|
safeRead,
|
|
439
342
|
workflowDependencyProofState,
|
|
440
343
|
workflowReceiptBody,
|