@haposoft/cafekit 0.15.3 → 0.16.0-rc.2
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 +4 -2
- package/bin/__tests__/codex-hooks.test.js +140 -0
- package/bin/__tests__/codex-native.test.js +2 -2
- package/bin/__tests__/develop-contract.test.js +1120 -0
- package/bin/__tests__/installer-safety.test.js +510 -2
- package/bin/__tests__/package-inventory.test.js +258 -0
- package/bin/__tests__/secret-scanner.test.js +157 -0
- package/bin/__tests__/validator-grounding.test.js +713 -0
- package/bin/install.js +5 -1
- package/bin/lib/codex-install.js +21 -6
- package/bin/lib/context.js +17 -6
- package/bin/lib/copy-utils.js +31 -0
- package/bin/lib/instruction-blocks.js +85 -0
- package/bin/lib/managed-writer.js +4 -1
- package/bin/lib/opencode-install.js +75 -30
- package/bin/phases/claude-runtime.js +103 -4
- package/bin/phases/codex-runtime.js +9 -5
- package/bin/phases/post-install.js +29 -22
- package/bin/phases/root-config.js +89 -10
- package/package.json +14 -4
- package/src/claude/CLAUDE.md +6 -122
- package/src/claude/agents/code-auditor.md +28 -22
- package/src/claude/agents/debugger.md +3 -3
- package/src/claude/agents/deployer.md +1 -1
- package/src/claude/agents/{god-developer.md → implementer.md} +2 -2
- package/src/claude/agents/project-manager.md +1 -1
- package/src/claude/agents/spec-maker.md +2 -2
- package/src/claude/hooks/__tests__/spec-gate.test.js +356 -5
- package/src/claude/hooks/agent.cjs +7 -14
- package/src/claude/hooks/rules.cjs +33 -53
- package/src/claude/hooks/spec-gate.cjs +66 -21
- package/src/claude/hooks/spec-state.cjs +5 -0
- package/src/claude/migration-manifest.json +9 -2
- package/src/claude/runtime.json +0 -3
- package/src/claude/scripts/generate-skill-catalog.cjs +1 -1
- package/src/claude/scripts/scan-staged-secrets.cjs +170 -0
- package/src/claude/scripts/spec-ground.cjs +189 -83
- package/src/claude/scripts/validate-spec-output.cjs +294 -30
- package/src/claude/scripts/workflow-policy.cjs +609 -0
- package/src/claude/skills/ai-multimodal/SKILL.md +2 -2
- package/src/claude/skills/ai-multimodal/references/audio-processing.md +6 -6
- package/src/claude/skills/ai-multimodal/references/video-analysis.md +1 -1
- package/src/claude/skills/backend-development/SKILL.md +4 -4
- package/src/claude/skills/backend-development/references/backend-architecture.md +1 -1
- package/src/claude/skills/backend-development/references/backend-devops.md +2 -2
- package/src/claude/skills/backend-development/references/backend-mindset.md +3 -3
- package/src/claude/skills/backend-development/references/backend-performance.md +5 -5
- package/src/claude/skills/backend-development/references/backend-security.md +55 -3
- package/src/claude/skills/backend-development/references/backend-technologies.md +5 -5
- package/src/claude/skills/backend-development/references/backend-testing.md +2 -2
- package/src/claude/skills/brainstorm/SKILL.md +2 -3
- package/src/claude/skills/code-review/SKILL.md +12 -8
- package/src/claude/skills/code-review/references/verification-gate.md +4 -4
- package/src/claude/skills/debug/SKILL.md +2 -0
- package/src/claude/skills/develop/SKILL.md +100 -52
- package/src/claude/skills/develop/references/parallel-waves.md +105 -26
- package/src/claude/skills/develop/references/quality-gate.md +78 -38
- package/src/claude/skills/develop/references/subagent-patterns.md +2 -2
- package/src/claude/skills/git/SKILL.md +44 -50
- package/src/claude/skills/hotfix/SKILL.md +10 -8
- package/src/claude/skills/hotfix/references/parallel-patterns.md +2 -2
- package/src/claude/skills/hotfix/references/review-cycle.md +26 -21
- package/src/claude/skills/inspect/SKILL.md +12 -30
- package/src/claude/skills/mobile-development/SKILL.md +12 -12
- package/src/claude/skills/mobile-development/references/mobile-android.md +7 -7
- package/src/claude/skills/mobile-development/references/mobile-best-practices.md +22 -23
- package/src/claude/skills/mobile-development/references/mobile-frameworks.md +13 -13
- package/src/claude/skills/mobile-development/references/mobile-ios.md +1 -1
- package/src/claude/skills/mobile-development/references/mobile-mindset.md +17 -19
- package/src/claude/skills/question/SKILL.md +13 -13
- package/src/claude/skills/research/SKILL.md +2 -2
- package/src/claude/skills/specs/SKILL.md +32 -15
- package/src/claude/skills/specs/references/review.md +3 -3
- package/src/claude/skills/specs/rules/tasks-generation.md +1 -1
- package/src/claude/skills/specs/templates/spec-state.json +7 -3
- package/src/claude/skills/sync/SKILL.md +26 -1
- package/src/claude/skills/sync/references/sync-protocols.md +3 -3
- package/src/claude/skills/test/SKILL.md +12 -4
- package/src/claude/skills/test/references/failure-triage.md +17 -17
- package/src/claude/skills/test/references/test-memory.md +2 -2
- package/src/codex/AGENTS.md +16 -24
- package/src/codex/hooks/lib/spec-receipt.cjs +57 -4
- package/src/codex/hooks/rules.cjs +27 -26
- package/src/codex/hooks/spec-gate.cjs +19 -8
- package/src/codex/hooks/spec-state.cjs +10 -1
- package/src/common/AGENTS.md +43 -0
- package/src/opencode/AGENTS.md +21 -121
- package/src/opencode/plugins/rules.ts +26 -34
- package/src/opencode/plugins/spec-gate.ts +283 -0
- package/src/claude/skills/inspect/references/external-gemini-inspection.md +0 -169
|
@@ -32,7 +32,7 @@ Detail bullets must include:
|
|
|
32
32
|
- API payloads, routes, and JSON contracts.
|
|
33
33
|
- Edge cases, error handling, and exact validation thresholds (e.g., `Return 403 if invalid`).
|
|
34
34
|
|
|
35
|
-
**Rationale**: Humans review tasks to verify business requirements are met; AI Coders (like
|
|
35
|
+
**Rationale**: Humans review tasks to verify business requirements are met; AI Coders (like implementer or ck) read tasks to write explicit code. If you only write business jargon, the AI hallucinates. If you only write code names, the human reviewer cannot verify the business value. You MUST provide both.
|
|
36
36
|
|
|
37
37
|
### 2. Task Integration & Progression
|
|
38
38
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
+
"schema_version": "2.0",
|
|
2
3
|
"feature_name": "{{FEATURE_NAME}}",
|
|
3
4
|
"created_at": "{{TIMESTAMP}}",
|
|
4
5
|
"updated_at": "{{TIMESTAMP}}",
|
|
@@ -12,15 +13,18 @@
|
|
|
12
13
|
"approvals": {
|
|
13
14
|
"requirements": {
|
|
14
15
|
"generated": false,
|
|
15
|
-
"
|
|
16
|
+
"agent_validated": false,
|
|
17
|
+
"user_approved": false
|
|
16
18
|
},
|
|
17
19
|
"design": {
|
|
18
20
|
"generated": false,
|
|
19
|
-
"
|
|
21
|
+
"agent_validated": false,
|
|
22
|
+
"user_approved": false
|
|
20
23
|
},
|
|
21
24
|
"tasks": {
|
|
22
25
|
"generated": false,
|
|
23
|
-
"
|
|
26
|
+
"agent_validated": false,
|
|
27
|
+
"user_approved": false
|
|
24
28
|
}
|
|
25
29
|
},
|
|
26
30
|
"scope_lock": {
|
|
@@ -14,8 +14,21 @@ metadata:
|
|
|
14
14
|
|
|
15
15
|
This skill safely bridges the gap between active development state and physical documentation files (`spec.json` + `task_registry` + `tasks/task-R*.md`). Instead of relying on risky raw AI edits, this skill executes precise contextual replacements.
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## Lane-aware synchronization
|
|
18
|
+
|
|
19
|
+
Read lane policy before mutating state:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
node .claude/scripts/workflow-policy.cjs --classify-lane --task-json '<task JSON>' --json
|
|
23
|
+
```
|
|
18
24
|
|
|
25
|
+
- Direct may have no spec/state/registry; sync only concrete targeted evidence and never invents approval state.
|
|
26
|
+
- Standard stores one bounded spec and one canonical feature receipt; one combined `code-auditor` review gates feature closeout.
|
|
27
|
+
- Critical requires strict durable evidence and `inspector → implementer → test-runner → code-auditor` delegation by default.
|
|
28
|
+
- `generated`, `agent_validated`, and `user_approved` are independent. Missing `user_approved` stays false; sync never auto-approves user-owned state.
|
|
29
|
+
- Explicit lane overrides must preserve automatic classification and warning in receipt/state output, especially Critical downgrades.
|
|
30
|
+
|
|
31
|
+
## Supported Commands
|
|
19
32
|
### 1. Task Synchronization
|
|
20
33
|
Update a specific task's status and automatically check its relevant sub-checkboxes.
|
|
21
34
|
|
|
@@ -44,5 +57,17 @@ Scans the `spec.json` against all physical `task-R*.md` files to detect mismatch
|
|
|
44
57
|
5. **Task Docs Hook:** Every time `hapo:sync` marks a task as `done`, it must flag that a task-level docs checkpoint is now due for that verified task.
|
|
45
58
|
6. **Phase Prompt Rule:** When `hapo:sync` marks the final pending task in the whole feature as `done`, it should automatically prompt the user if they'd like to advance the phase, but only after the docs checkpoint for that last completed task has been considered.
|
|
46
59
|
|
|
60
|
+
### 4. Explicit Flash Finalization
|
|
61
|
+
|
|
62
|
+
**Usage:** `/hapo:sync <feature_name> <task_id|task-file> sync-finalize`
|
|
63
|
+
|
|
64
|
+
This is the only operation allowed to turn a flash-promoted task (`Verification: PASS`, `status: in_progress`, `readyForSync: true`) into `done` and unblock dependencies. A normal `done` request cannot bypass flash promotion or stale `FLASH_UNVERIFIED` state.
|
|
65
|
+
|
|
66
|
+
### Flash implementation state
|
|
67
|
+
|
|
68
|
+
Executable policy source: `.claude/scripts/workflow-policy.cjs` (source: `src/claude/scripts/workflow-policy.cjs`). Use `promoteFlashTask` only after exact task Evidence and reachability return PASS; FAIL, BLOCKED, and NO_TESTS remain blocked in progress.
|
|
69
|
+
|
|
70
|
+
`FLASH_UNVERIFIED` is storage for implemented-but-unverified work, not a completion status. Store it as `status: "in_progress"` with blocker `awaiting /hapo:test <feature>`; do not unblock dependencies. `/hapo:test` may promote one task only after its exact Evidence and reachability PASS, replacing the receipt with proof, clearing blocker, and setting `readyForSync: true` while dependencies remain blocked. Only explicit `/hapo:sync ... sync-finalize` may set `done` and unblock. FAIL, BLOCKED, and NO_TESTS remain `in_progress`.
|
|
71
|
+
|
|
47
72
|
## References
|
|
48
73
|
Read `references/sync-protocols.md` for exact Search/Replace regex patterns and JSON schema expectations before acting on the files.
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
The following guidelines dictate exactly how `hapo:sync` should interact with files to prevent data corruption.
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Flash implementation state:** `FLASH_UNVERIFIED` MUST remain `status: "in_progress"` with blocker `awaiting /hapo:test <feature>`. It never unblocks dependencies. A task-scoped `/hapo:test` PASS replaces receipt with real `Verification: PASS`, clears blocker, and sets `readyForSync: true` while dependency blocking remains. Only explicit `/hapo:sync ... sync-finalize` may set `done` and unblock; FAIL, BLOCKED, and NO_TESTS stay `in_progress`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Lane state rule:** classify Direct/Standard/Critical before sync. Direct may omit spec/state/registry for clear, reversible low-risk work; Standard uses one bounded spec and one feature receipt; Critical requires strict durable evidence. Explicit overrides must retain automatic lane, risk signals, and warning. `generated`, `agent_validated`, and `user_approved` are independent booleans; never infer or write `user_approved: true` from agent fields.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## 1. Updating `spec.json`
|
|
10
10
|
|
|
11
11
|
* **JSON Modification Rule:** Do not output whole files. Instead, load the JSON structure, apply the update to `status`, `current_phase`, `blocker` (if any), `task_files`, and the relevant `task_registry` entry, then overwrite the file cleanly.
|
|
12
12
|
* **Task Registry Rule:** Resolve the incoming task reference to a single relative path in `task_registry`. Accept either:
|
|
@@ -43,9 +43,17 @@ When a feature name or `specs/<feature>` path is supplied, testing is spec-aware
|
|
|
43
43
|
Load `spec.json`, `requirements.md`, `design.md`, active/recent task files, and task `Evidence` / test-plan proof.
|
|
44
44
|
The verdict MUST compare executed/reachable behavior against `scope_lock`, requirements, design contracts, task Completion Criteria, and runtime reachability obligations.
|
|
45
45
|
Build/typecheck success without scoped runtime proof is not PASS.
|
|
46
|
-
|
|
46
|
+
## Flash promotion contract
|
|
47
|
+
|
|
48
|
+
Executable policy source: `.claude/scripts/workflow-policy.cjs` (source: `src/claude/scripts/workflow-policy.cjs`). Its `consumeReviewVerdict` and `promoteFlashTask` functions are sole source for PASS/FAIL/BLOCKED handling and task-scoped promotion.
|
|
49
|
+
|
|
50
|
+
When `/hapo:test <feature>` finds a task with `status: "in_progress"` and receipt `FLASH_UNVERIFIED`, evaluate that task's exact Evidence and runtime reachability only:
|
|
51
|
+
|
|
52
|
+
- PASS → replace receipt with concrete proof while keeping `status: "in_progress"`, `dependencyBlocked: true`, `unblocks: false`, and `readyForSync: true`; only explicit `/hapo:sync ... sync-finalize` may set `done` and unblock dependents.
|
|
53
|
+
- FAIL, BLOCKED, or NO_TESTS → keep `status: "in_progress"`, keep or update blocker, and do not promote any other flash task.
|
|
54
|
+
- Never blanket-promote every flash task in a feature because one task passed.
|
|
55
|
+
|
|
47
56
|
|
|
48
|
-
## 4-Phase Execution
|
|
49
57
|
|
|
50
58
|
```mermaid
|
|
51
59
|
flowchart TD
|
|
@@ -153,7 +161,7 @@ Return a **structured verdict** (required format — not free-form prose):
|
|
|
153
161
|
|
|
154
162
|
### Action
|
|
155
163
|
- PASS → Proceed. Hand off to hapo:code-review.
|
|
156
|
-
- FAIL → [list specific fixes needed] → Return to
|
|
164
|
+
- FAIL → [list specific fixes needed] → Return to implementer. (If REGRESSION: label "Test Regression — tests were deleted or weakened to produce green result")
|
|
157
165
|
- PARTIAL → [list uncovered areas] → Consider adding tests.
|
|
158
166
|
- NO_TESTS → No test runner detected. User must configure tests first.
|
|
159
167
|
|
|
@@ -176,7 +184,7 @@ It merges the JSON data into `.hapo/test-memory.json` per `references/test-memor
|
|
|
176
184
|
| `hapo:code-review` | runs in parallel | Both run at Quality Gate Step 4 |
|
|
177
185
|
| `hapo:develop` | orchestrates | Spawns hapo:test at Step 4 |
|
|
178
186
|
| `inspector` agent | hapo:test → | Scout test file locations when structure is unfamiliar |
|
|
179
|
-
| `
|
|
187
|
+
| `implementer` agent | hapo:test → | FAIL verdicts route back here for fixing |
|
|
180
188
|
| `test-runner` agent | hapo:test → | Primary executor, spawned via the `Agent` tool |
|
|
181
189
|
| chrome-devtools scripts | test-runner → | UI verification (navigate, screenshot, console, network, performance, aria-snapshot, inject-auth) |
|
|
182
190
|
|
|
@@ -35,21 +35,21 @@ Failure detected
|
|
|
35
35
|
├─ Compile Error (#1)
|
|
36
36
|
│ → STOP immediately. Do NOT run further tests.
|
|
37
37
|
│ → Report: file, line, error message.
|
|
38
|
-
│ → Action: return to
|
|
38
|
+
│ → Action: return to implementer. Fix compile errors first.
|
|
39
39
|
│
|
|
40
40
|
├─ Logic Error (#2)
|
|
41
41
|
│ → Report: test name, file:line, expected value, actual value.
|
|
42
|
-
│ → Action: return to
|
|
42
|
+
│ → Action: return to implementer with precise location.
|
|
43
43
|
│ → Do NOT modify test to make it pass.
|
|
44
44
|
│
|
|
45
45
|
├─ Environment Error (#3)
|
|
46
46
|
│ → Report: which service/env var is missing.
|
|
47
|
-
│ → Action: escalate to USER (not
|
|
47
|
+
│ → Action: escalate to USER (not implementer).
|
|
48
48
|
│ "Environment issue: [DB/service] not reachable. Please start it."
|
|
49
49
|
│
|
|
50
50
|
├─ Flaky Test (#4)
|
|
51
51
|
│ → Report: test name + "intermittent failure observed".
|
|
52
|
-
│ → Action: flag for
|
|
52
|
+
│ → Action: flag for implementer review.
|
|
53
53
|
│ Likely cause: shared state, async timing, or test ordering.
|
|
54
54
|
│
|
|
55
55
|
├─ Coverage Gap (#5)
|
|
@@ -59,17 +59,17 @@ Failure detected
|
|
|
59
59
|
│
|
|
60
60
|
├─ Build Error (#6)
|
|
61
61
|
│ → Report: build command, error output (last 20 lines).
|
|
62
|
-
│ → Action: return to
|
|
62
|
+
│ → Action: return to implementer.
|
|
63
63
|
│
|
|
64
64
|
├─ UI Console Error (#7)
|
|
65
65
|
│ → Report: error message, source file, line number.
|
|
66
|
-
│ → Action: return to
|
|
66
|
+
│ → Action: return to implementer if JS error.
|
|
67
67
|
│ If external script error: flag as warning only.
|
|
68
68
|
│
|
|
69
69
|
├─ UI Network Error (#8)
|
|
70
70
|
│ → Report: endpoint URL, status code, response body excerpt.
|
|
71
|
-
│ → Action: 404 →
|
|
72
|
-
│ 500 →
|
|
71
|
+
│ → Action: 404 → implementer (missing route/resource).
|
|
72
|
+
│ 500 → implementer (server error). 401/403 → auth issue (user).
|
|
73
73
|
│
|
|
74
74
|
├─ UI Performance (#9)
|
|
75
75
|
│ → Report: specific metric name + actual value + threshold.
|
|
@@ -77,27 +77,27 @@ Failure detected
|
|
|
77
77
|
│
|
|
78
78
|
├─ Accessibility Error (#10)
|
|
79
79
|
│ → Report: element role + missing attribute.
|
|
80
|
-
│ → Action: return to
|
|
80
|
+
│ → Action: return to implementer. Add aria-label / alt / for attributes.
|
|
81
81
|
│
|
|
82
82
|
├─ User Flow Error (#11)
|
|
83
83
|
│ → Report: step failed (e.g., login submit), expected vs actual URL/state.
|
|
84
|
-
│ → Action: return to
|
|
84
|
+
│ → Action: return to implementer to fix E2E UI flow.
|
|
85
85
|
│
|
|
86
86
|
├─ SEO Error (#12)
|
|
87
87
|
│ → Report: missing tag or invalid setup (e.g., no H1, bad canonical).
|
|
88
|
-
│ → Action: return to
|
|
88
|
+
│ → Action: return to implementer. Add missing HTML meta tags.
|
|
89
89
|
│
|
|
90
90
|
├─ Security Warning (#13)
|
|
91
91
|
│ → Report: missing header names or exposed secret pattern.
|
|
92
|
-
│ → Action: return to
|
|
92
|
+
│ → Action: return to implementer. Add helmet/headers or remove secrets.
|
|
93
93
|
│
|
|
94
94
|
├─ Broken Links (#14)
|
|
95
95
|
│ → Report: crawler source URL → broken target URL (404).
|
|
96
|
-
│ → Action: return to
|
|
96
|
+
│ → Action: return to implementer to fix href paths.
|
|
97
97
|
│
|
|
98
98
|
└─ UI Visual Error (#15)
|
|
99
99
|
→ Report: AI parsed visual issue (e.g., button overlapping text).
|
|
100
|
-
→ Action: return to
|
|
100
|
+
→ Action: return to implementer to fix CSS/layout issues.
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
---
|
|
@@ -107,11 +107,11 @@ Failure detected
|
|
|
107
107
|
| Failure count | Verdict | Action |
|
|
108
108
|
|---|---|---|
|
|
109
109
|
| 0 failures | **PASS** | Proceed to hapo:code-review |
|
|
110
|
-
| 1–2 failures | **FAIL** | Return to
|
|
111
|
-
| 3–10 failures | **FAIL** | Return to
|
|
110
|
+
| 1–2 failures | **FAIL** | Return to implementer with targeted fix list |
|
|
111
|
+
| 3–10 failures | **FAIL** | Return to implementer, flag as "significant failures" |
|
|
112
112
|
| > 10 failures OR 0 tests pass | **COLLAPSE** | Call `AskUserQuestion`: "Test suite critically broken. User intervention required." |
|
|
113
113
|
| Only coverage gaps | **PARTIAL** | Proceed to hapo:code-review, attach coverage gap list |
|
|
114
|
-
| Environment errors only | **BLOCKED** | Escalate to user (not
|
|
114
|
+
| Environment errors only | **BLOCKED** | Escalate to user (not implementer) |
|
|
115
115
|
|
|
116
116
|
---
|
|
117
117
|
|
|
@@ -19,7 +19,7 @@ If the file does not exist, the `hapo:test` skill (or the orchestrator) MUST cre
|
|
|
19
19
|
The `test-runner` agent is Read-Only. At Phase 1, it must read `.hapo/test-memory.json` (if it exists) to factor into its testing process:
|
|
20
20
|
- **`env_setup_commands`**: Commands that must be run before tests (e.g. `docker compose up -d redis`). If listed, flag missing dependencies instead of immediate failure.
|
|
21
21
|
- **`flaky_tests`**: Array of test file paths. If a failure occurs in one of these files, the verdict should explicitly note `(Known Flaky Test)`.
|
|
22
|
-
- **`known_issues`**: Issues that
|
|
22
|
+
- **`known_issues`**: Issues that implementer or user marked as ignored.
|
|
23
23
|
|
|
24
24
|
## Writing to Memory
|
|
25
25
|
|
|
@@ -28,7 +28,7 @@ Because `test-runner` is restricted from editing files, it cannot update the mem
|
|
|
28
28
|
Example:
|
|
29
29
|
```markdown
|
|
30
30
|
### Action
|
|
31
|
-
→ [FAIL] Return to
|
|
31
|
+
→ [FAIL] Return to implementer: Fix CSS layout offset.
|
|
32
32
|
|
|
33
33
|
<lessons_learned>
|
|
34
34
|
{
|
package/src/codex/AGENTS.md
CHANGED
|
@@ -1,30 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
## Codex runtime
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
- Repository instructions live in `AGENTS.md`.
|
|
4
|
+
- CafeKit skills live in `.agents/skills/`; invoke them as `$hapo-<name>` or browse with `/skills`. Edit skills in the project, not in a global skills directory.
|
|
5
|
+
- Run Python skill scripts with the project venv:
|
|
6
|
+
- macOS/Linux: `.agents/skills/.venv/bin/python3 scripts/<script>.py`
|
|
7
|
+
- Windows: `.agents\\skills\\.venv\\Scripts\\python.exe scripts\\<script>.py`
|
|
8
|
+
- CafeKit agents live in `.codex/agents/*.toml` and are auto-discovered after the repository is trusted.
|
|
9
|
+
- Runtime support lives in `.codex/rules/`, `.codex/scripts/`, `.codex/references/`, and `.codex/runtime.json`.
|
|
10
|
+
- Project hooks live in `.codex/hooks.json`; review trusted hooks with `/hooks`.
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
- CafeKit skills: `.agents/skills/`; invoke explicitly as `$hapo-<name>` or browse with `/skills`.
|
|
7
|
-
- CafeKit agents: native auto-discovered definitions in `.codex/agents/*.toml`.
|
|
8
|
-
- Runtime support: `.codex/rules/`, `.codex/scripts/`, `.codex/references/`, and `.codex/runtime.json`.
|
|
9
|
-
- Lifecycle enforcement: `.codex/hooks.json`. Project hooks run only after the repository and hook definitions are trusted; review them with `/hooks`.
|
|
10
|
-
- Do not create deprecated custom prompts or assume Claude/OpenCode command wrappers exist.
|
|
12
|
+
## Codex caveats
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
- Custom agents use snake_case names and `fork_turns: "none"` for explicit delegation.
|
|
15
|
+
- Use Codex-native subagent delegation and task-state tools; do not rely on Claude-only tool labels.
|
|
16
|
+
- Do not edit global trust configuration. Hooks are not a complete security boundary; hosted tools and untrusted project hooks can bypass the local hook path.
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
- Choose the matching CafeKit skill before improvising a workflow.
|
|
16
|
-
- Read `.codex/rules/workflow.md`, `.codex/rules/ai-dev-rules.md`, and
|
|
17
|
-
`.codex/rules/skill-workflow-routing.md` for non-trivial work. Load the other
|
|
18
|
-
`.codex/rules/*.md` files when their topic applies.
|
|
19
|
-
- Keep scope surgical; prefer YAGNI, KISS, then DRY.
|
|
20
|
-
- Do not claim completion without fresh build, test, runtime, or artifact evidence.
|
|
21
|
-
- For spec tasks, keep `spec.json`, task status, `Completion Criteria`, and `Evidence` synchronized only after verification.
|
|
22
|
-
- Privacy approvals are one request, one session, one retry. Only the user may submit the exact approval phrase emitted by the privacy hook.
|
|
18
|
+
## Combined-install boundary
|
|
23
19
|
|
|
24
|
-
|
|
20
|
+
Codex's native project instruction surface is root `AGENTS.md`. Combined installs keep this Codex block there because no separate Codex project entrypoint is configured by CafeKit. Other runtimes may read the same root file; this shared-root trade-off is intentional and must not be treated as filesystem isolation.
|
|
25
21
|
|
|
26
|
-
- Codex
|
|
27
|
-
- When selecting a CafeKit custom role explicitly, call `spawn_agent` with `agent_type`, a unique snake_case `task_name`, `message`, and `fork_turns: "none"`. A full-history fork cannot change agent type.
|
|
28
|
-
- Use Codex-native subagent delegation and task-state tools; do not rely on Claude-only tool labels.
|
|
29
|
-
- The project `.codex/` layer—agents, config, rules, and hooks—loads only after the repository is trusted. Never edit the user's global trust configuration on their behalf.
|
|
30
|
-
- Hooks are guardrails, not a complete security boundary. Hosted tools and untrusted project hooks may not enter the local hook path.
|
|
22
|
+
**Ownership / ignore contract (fail-safe):** This Codex block is owned by Codex CLI only. If you are Claude Code, OpenCode, or any other runtime, ignore this entire Codex block and consume only CORE plus your native block. If you cannot determine which block is yours, treat the file as CORE-only. Do not treat another runtime's block as instructions.
|
|
@@ -39,9 +39,32 @@ function evidenceBody(text) {
|
|
|
39
39
|
function safeTaskFile(featureDir, taskPath) {
|
|
40
40
|
const target = path.resolve(featureDir, taskPath);
|
|
41
41
|
const relative = path.relative(featureDir, target);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) return null;
|
|
43
|
+
return target;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function validateCanonicalReceipt(body) {
|
|
47
|
+
const failures = [];
|
|
48
|
+
if (!/^\s*Verification:\s*PASS\s*$/m.test(body)) {
|
|
49
|
+
failures.push('verification_state');
|
|
50
|
+
}
|
|
51
|
+
if (!/^\s*Command(?:\(s\))?\s*:/m.test(body)) {
|
|
52
|
+
failures.push('command');
|
|
53
|
+
}
|
|
54
|
+
if (!/^\s*Exit\s*:|exit\s+code\s*[:=]|\bResult\s*:\s*PASS\b/im.test(body)) {
|
|
55
|
+
failures.push('exit_result');
|
|
56
|
+
}
|
|
57
|
+
const hasBase = /^\s*Base[ \t]*:[ \t]*\S/im.test(body);
|
|
58
|
+
const hasHead = /^\s*Head[ \t]*:[ \t]*\S/im.test(body);
|
|
59
|
+
const hasBaseSha = /\bbase_sha[ \t]*:[ \t]*\S/im.test(body);
|
|
60
|
+
const hasHeadSha = /\bhead_sha[ \t]*:[ \t]*\S/im.test(body);
|
|
61
|
+
if (!((hasBase && hasHead) || (hasBaseSha && hasHeadSha))) {
|
|
62
|
+
failures.push('provenance');
|
|
63
|
+
}
|
|
64
|
+
if (/\bartifact\b/i.test(body) && !/sha256:/i.test(body)) {
|
|
65
|
+
failures.push('artifact_hash');
|
|
66
|
+
}
|
|
67
|
+
return failures;
|
|
45
68
|
}
|
|
46
69
|
|
|
47
70
|
function checkReceipt(featureDir, taskPath, task) {
|
|
@@ -64,6 +87,36 @@ function checkReceipt(featureDir, taskPath, task) {
|
|
|
64
87
|
|| !(PASS_MARKER.test(body) || LEGACY_SUCCESS.test(body))
|
|
65
88
|
) {
|
|
66
89
|
failures.push('c');
|
|
90
|
+
} else {
|
|
91
|
+
// Canonical receipt requirements: command, exit/result, provenance, unambiguous PASS
|
|
92
|
+
// For done tasks, require canonical fields. If missing, add e/f/g.
|
|
93
|
+
const canonical = validateCanonicalReceipt(body);
|
|
94
|
+
// Map canonical failures to letters e,f,g,h for backward compat checks
|
|
95
|
+
// Use detailed names but also add letters for gate reporting
|
|
96
|
+
const map = {
|
|
97
|
+
verification_state: 'c', // already covers but keep distinct
|
|
98
|
+
command: 'e',
|
|
99
|
+
exit_result: 'f',
|
|
100
|
+
provenance: 'g',
|
|
101
|
+
artifact_hash: 'h',
|
|
102
|
+
};
|
|
103
|
+
for (const fail of canonical) {
|
|
104
|
+
const letter = map[fail];
|
|
105
|
+
// Avoid duplicating c if already failed, but add specific
|
|
106
|
+
if (fail === 'verification_state' && failures.includes('c')) continue;
|
|
107
|
+
if (fail === 'verification_state') {
|
|
108
|
+
// This should have been caught by c, but if PASS present via legacy, still need strict PASS
|
|
109
|
+
if (!PASS_MARKER.test(body)) failures.push('c');
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (letter && !failures.includes(letter)) failures.push(letter);
|
|
113
|
+
}
|
|
114
|
+
// Additional strict check: legacy success alone is not sufficient for canonical
|
|
115
|
+
// If body contains only legacy PASS but not canonical Verification: PASS, c already would have caught if no PASS_MARKER?
|
|
116
|
+
// But we allow legacy for backward compat in c, but canonical requires PASS_MARKER, so we must enforce.
|
|
117
|
+
if (!PASS_MARKER.test(body)) {
|
|
118
|
+
if (!failures.includes('c')) failures.push('c');
|
|
119
|
+
}
|
|
67
120
|
}
|
|
68
121
|
|
|
69
122
|
const completedAt = task?.completed_at;
|
|
@@ -75,4 +128,4 @@ function checkReceipt(featureDir, taskPath, task) {
|
|
|
75
128
|
return failures;
|
|
76
129
|
}
|
|
77
130
|
|
|
78
|
-
module.exports = { checkReceipt };
|
|
131
|
+
module.exports = { checkReceipt, evidenceBody, safeTaskFile, validateCanonicalReceipt };
|
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
'use strict';
|
|
3
2
|
|
|
4
3
|
const crypto = require('crypto');
|
|
5
4
|
const fs = require('fs');
|
|
6
5
|
const path = require('path');
|
|
7
6
|
const {
|
|
8
|
-
atomicWrite,
|
|
9
7
|
getHookContext,
|
|
10
8
|
logCrash,
|
|
11
9
|
readPayload,
|
|
12
10
|
resolveProjectPath
|
|
13
11
|
} = require('./lib/hook-context.cjs');
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
function cooldownFile(projectRoot, sessionId) {
|
|
13
|
+
function reservationFile(projectRoot, sessionId) {
|
|
18
14
|
const key = crypto.createHash('sha256')
|
|
19
15
|
.update(String(sessionId || 'unknown'))
|
|
20
16
|
.digest('hex')
|
|
@@ -22,21 +18,39 @@ function cooldownFile(projectRoot, sessionId) {
|
|
|
22
18
|
return path.join(projectRoot, '.codex', 'hooks', '.logs', `rules-${key}.json`);
|
|
23
19
|
}
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
/** Reserve one injection slot per session. Returns null on reservation errors. */
|
|
22
|
+
function reserveSession(projectRoot, sessionId) {
|
|
23
|
+
if (!sessionId) return null;
|
|
24
|
+
try {
|
|
25
|
+
const file = reservationFile(projectRoot, sessionId);
|
|
26
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
27
|
+
fs.writeFileSync(file, JSON.stringify({ sessionId: String(sessionId) }), {
|
|
28
|
+
encoding: 'utf8',
|
|
29
|
+
flag: 'wx',
|
|
30
|
+
mode: 0o600
|
|
31
|
+
});
|
|
32
|
+
return true;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return error.code === 'EEXIST' ? false : null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function readRuntime(projectRoot) {
|
|
26
39
|
try {
|
|
27
|
-
const
|
|
28
|
-
return
|
|
40
|
+
const file = path.join(projectRoot, '.codex', 'runtime.json');
|
|
41
|
+
return fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : null;
|
|
29
42
|
} catch {
|
|
30
|
-
return
|
|
43
|
+
return null;
|
|
31
44
|
}
|
|
32
45
|
}
|
|
33
46
|
|
|
34
47
|
try {
|
|
35
48
|
const payload = readPayload();
|
|
36
49
|
if (!payload) process.exit(0);
|
|
37
|
-
const { projectRoot
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
const { projectRoot } = getHookContext(payload);
|
|
51
|
+
if (reserveSession(projectRoot, payload.session_id) !== true) process.exit(0);
|
|
52
|
+
const runtime = readRuntime(projectRoot);
|
|
53
|
+
if (runtime === null) process.exit(0);
|
|
40
54
|
|
|
41
55
|
const respondLang = runtime.locale?.responseLanguage || '';
|
|
42
56
|
const thinkLang = runtime.locale?.thinkingLanguage || (respondLang ? 'en' : '');
|
|
@@ -57,23 +71,10 @@ try {
|
|
|
57
71
|
'## Rules',
|
|
58
72
|
`- Markdown: Plans → "${plansPath}/" | Docs → "${docsPath}/"`,
|
|
59
73
|
'- Do not create markdown outside those directories unless explicitly asked.',
|
|
60
|
-
`- docs.maxLoc: ${runtime.docs?.maxLoc || 800} lines per doc file
|
|
61
|
-
'- Follow YAGNI · KISS · DRY.',
|
|
62
|
-
'- Reports: concise; unresolved questions last.',
|
|
63
|
-
'',
|
|
64
|
-
'## Skill Routing',
|
|
65
|
-
'- Choose skills using `.codex/rules/skill-workflow-routing.md` and `.codex/rules/skill-domain-routing.md`.',
|
|
66
|
-
'- Inspect installed skills with `node .codex/scripts/generate-skill-catalog.cjs --skills` when needed.',
|
|
67
|
-
'- Explicit user requests override routing suggestions.',
|
|
68
|
-
'',
|
|
69
|
-
'## Modularization',
|
|
70
|
-
'- Consider splitting code files over 200 lines at real concern boundaries.',
|
|
71
|
-
'- Check existing modules first; use descriptive names and comments.',
|
|
72
|
-
'- Skip modularization for markdown, plain text, shell, config, and env files.'
|
|
74
|
+
`- docs.maxLoc: ${runtime.docs?.maxLoc || 800} lines per doc file`
|
|
73
75
|
);
|
|
74
76
|
|
|
75
77
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
76
|
-
atomicWrite(cache, `${JSON.stringify({ ts: Date.now() })}\n`);
|
|
77
78
|
} catch (error) {
|
|
78
79
|
logCrash('rules', error);
|
|
79
80
|
}
|
|
@@ -11,6 +11,10 @@ const {
|
|
|
11
11
|
} = require('./lib/hook-context.cjs');
|
|
12
12
|
const { checkReceipt } = require('./lib/spec-receipt.cjs');
|
|
13
13
|
const { findActiveSpec, taskStatusMap } = require('./lib/spec-utils.cjs');
|
|
14
|
+
const policyPath = path.join(__dirname, '..', 'scripts', 'workflow-policy.cjs');
|
|
15
|
+
const POLICY = require(fs.existsSync(policyPath)
|
|
16
|
+
? policyPath
|
|
17
|
+
: path.join(__dirname, '../../claude/scripts/workflow-policy.cjs'));
|
|
14
18
|
|
|
15
19
|
try {
|
|
16
20
|
const payload = readPayload();
|
|
@@ -35,18 +39,25 @@ try {
|
|
|
35
39
|
try { cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8')); } catch { cache = {}; }
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
// Cache hardening: revalidate every done task on every Stop so a
|
|
43
|
+
// cached PASS cannot hide later receipt/provenance mutations.
|
|
44
|
+
// Cache is optimization only - validate every done task even on first run.
|
|
45
|
+
const previous = cacheExists ? (cache[active.featureName] || {}) : {};
|
|
46
|
+
const staleFlashTasks = Object.entries(registry)
|
|
47
|
+
.filter(([, task]) => POLICY.isStaleFlashDone(task))
|
|
48
|
+
.map(([taskPath]) => taskPath);
|
|
49
|
+
if (staleFlashTasks.length > 0) {
|
|
50
|
+
process.stdout.write(`${JSON.stringify({
|
|
51
|
+
decision: 'block',
|
|
52
|
+
reason: `Completion gate: ${staleFlashTasks.length} task(s) marked done with FLASH_UNVERIFIED (${staleFlashTasks.join(', ')}). Run /hapo:test, then use explicit sync-finalize.`
|
|
53
|
+
})}\n`);
|
|
41
54
|
process.exit(0);
|
|
42
55
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const newlyDone = Object.keys(registry).filter((taskPath) => (
|
|
46
|
-
currentStatuses[taskPath] === 'done' && previous[taskPath] !== 'done'
|
|
56
|
+
const allDoneTasks = Object.keys(registry).filter((taskPath) => (
|
|
57
|
+
currentStatuses[taskPath] === 'done'
|
|
47
58
|
));
|
|
48
59
|
const featureDir = path.join(active.specsDir, active.featureName);
|
|
49
|
-
const failures =
|
|
60
|
+
const failures = allDoneTasks
|
|
50
61
|
.map((taskPath) => ({
|
|
51
62
|
taskPath,
|
|
52
63
|
failures: checkReceipt(featureDir, taskPath, registry[taskPath])
|
|
@@ -11,6 +11,10 @@ const {
|
|
|
11
11
|
readPayload
|
|
12
12
|
} = require('./lib/hook-context.cjs');
|
|
13
13
|
const { findActiveSpec } = require('./lib/spec-utils.cjs');
|
|
14
|
+
const policyPath = path.join(__dirname, '..', 'scripts', 'workflow-policy.cjs');
|
|
15
|
+
const POLICY = require(fs.existsSync(policyPath)
|
|
16
|
+
? policyPath
|
|
17
|
+
: path.join(__dirname, '../../claude/scripts/workflow-policy.cjs'));
|
|
14
18
|
|
|
15
19
|
function cacheFile(projectRoot, sessionId) {
|
|
16
20
|
const key = crypto.createHash('sha256')
|
|
@@ -29,7 +33,9 @@ try {
|
|
|
29
33
|
if (!active) process.exit(0);
|
|
30
34
|
|
|
31
35
|
const phase = active.spec.current_phase || active.spec.phase || 'unknown';
|
|
32
|
-
const
|
|
36
|
+
const taskRegistry = active.spec.task_registry || {};
|
|
37
|
+
const flashTasks = POLICY.flashState(taskRegistry);
|
|
38
|
+
const tasks = Object.entries(taskRegistry);
|
|
33
39
|
const counts = tasks.reduce((result, [, task]) => {
|
|
34
40
|
const status = task?.status || 'pending';
|
|
35
41
|
result[status] = (result[status] || 0) + 1;
|
|
@@ -62,6 +68,9 @@ try {
|
|
|
62
68
|
`- Phase: \`${phase}\` | Tasks: ${counts.done || 0} done / ${tasks.length} total`
|
|
63
69
|
];
|
|
64
70
|
if (next) lines.push(`- Next unblocked: \`${next[0]}\``);
|
|
71
|
+
if (flashTasks.length > 0) {
|
|
72
|
+
lines.push(`- Flash verification pending: ${flashTasks.map((taskPath) => `\`${taskPath}\``).join(', ')}. PASS promotion keeps task in_progress until explicit sync-finalize.`);
|
|
73
|
+
}
|
|
65
74
|
lines.push(
|
|
66
75
|
'- Sync `spec.json` and the task file only after verified work.',
|
|
67
76
|
`- Validate with \`node .codex/scripts/validate-spec-output.cjs specs/${active.featureName}\`.`,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## Shared CafeKit instructions
|
|
4
|
+
|
|
5
|
+
- Deliver exactly what was asked. Do not expand, polish, or add optional work beyond the request. Match existing code style and structure.
|
|
6
|
+
- For spec work, `Completion Criteria` and `## Evidence` in `specs/<feature>/tasks/*.md` are the source of truth for task state.
|
|
7
|
+
- `NO_TESTS` and `0 tests + exit 0` do not pass when the task requires automated tests.
|
|
8
|
+
- When a hook blocks an action, that is an instruction boundary — do not work around it.
|
|
9
|
+
- Use conventional commits. Do not add AI attribution unless requested.
|
|
10
|
+
|
|
11
|
+
## Response style
|
|
12
|
+
|
|
13
|
+
Keep replies focused and brief. Match written-file length to task needs. Do not add filler sections or redundant summaries. Lead with the outcome.
|
|
14
|
+
|
|
15
|
+
## Uncertainty
|
|
16
|
+
|
|
17
|
+
Say when you are unsure and what would settle it. Label inferences instead of presenting them as verified facts.
|
|
18
|
+
|
|
19
|
+
## Delegation
|
|
20
|
+
|
|
21
|
+
Do the work yourself when it takes a handful of tool calls. Delegate genuinely independent parallel tracks. Verification comes from the project's hooks and validators, not from spawning more agents.
|
|
22
|
+
|
|
23
|
+
## Runtime ownership
|
|
24
|
+
|
|
25
|
+
- This `CORE` block is runtime-neutral and safe for every runtime.
|
|
26
|
+
- Runtime-specific instructions live in that runtime's own managed block, not in `CORE`.
|
|
27
|
+
- In a combined install, consume `CORE` plus your native block only. Ignore managed blocks not owned by your runtime. If ownership is unclear, treat the file as `CORE`-only (fail-safe).
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
<!-- Add project-specific install, test, lint, and build commands here. Keep commands executable. -->
|
|
32
|
+
|
|
33
|
+
## Do not touch
|
|
34
|
+
|
|
35
|
+
<!-- List files, directories, generated artifacts, or secrets that tasks must leave unchanged. -->
|
|
36
|
+
|
|
37
|
+
## Slow or expensive
|
|
38
|
+
|
|
39
|
+
<!-- Note commands, environments, or operations that need explicit planning before running. -->
|
|
40
|
+
|
|
41
|
+
## Language Consistency <!-- cafekit:lang -->
|
|
42
|
+
|
|
43
|
+
Match the language the user writes in. Technical terms, code identifiers, and file paths may remain in English.
|