@dzhechkov/keysarium 1.5.35 → 1.5.37
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/.dz-manifest.json +59 -19
- package/README.md +41 -4
- package/package.json +1 -1
- package/sbom.json +118 -18
- package/templates/.claude/skills/feature-adr/SKILL.md +48 -7
- package/templates/.claude/skills/feature-adr/modules/01-requirements.md +26 -0
- package/templates/.claude/skills/feature-adr/modules/02-research.md +26 -0
- package/templates/.claude/skills/feature-adr/modules/03-adr.md +26 -0
- package/templates/.claude/skills/feature-adr/modules/03.5-ideation-swarm.md +25 -0
- package/templates/.claude/skills/feature-adr/modules/04-ddd.md +25 -0
- package/templates/.claude/skills/feature-adr/modules/05-architecture.md +26 -0
- package/templates/.claude/skills/feature-adr/modules/06-implementation-plan.md +74 -4
- package/templates/.claude/skills/feature-adr/modules/08-qe.md +14 -1
- package/templates/.claude/skills/feature-adr/scripts/check-plan-completeness.mjs +166 -0
- package/templates/.claude/skills/goap-research-ed25519/SKILL.md +340 -47
- package/templates/.claude/skills/goap-research-ed25519/scripts/check_report_evidence.py +359 -3
- package/templates/.claude/skills/goap-research-ed25519/scripts/ed25519_verifier.py +386 -13
- package/templates/.claude/skills/goap-research-ed25519/scripts/fixture_legacy_v2_fact.json +23 -0
- package/templates/.claude/skills/goap-research-ed25519/scripts/fixtures_field_cases.json +133 -0
- package/templates/.claude/skills/goap-research-ed25519/scripts/goap_planner.py +314 -44
- package/templates/.claude/skills/goap-research-ed25519/scripts/learning_bridge.py +891 -280
- package/templates/.claude/skills/goap-research-ed25519/scripts/population_match.py +591 -0
- package/templates/.claude/skills/goap-research-ed25519/scripts/risk_statement.py +289 -0
- package/templates/.claude/skills/goap-research-ed25519/scripts/test_ed25519_verifier.py +57 -2
- package/templates/.claude/skills/goap-research-ed25519/scripts/test_evidence_provenance.py +970 -306
- package/templates/.claude/skills/goap-research-ed25519/scripts/test_goap_planner.py +420 -0
- package/templates/.claude/skills/goap-research-ed25519/scripts/test_population_match.py +544 -0
- package/templates/.claude/skills/goap-research-ed25519/scripts/test_risk_absolute.py +239 -0
- package/templates/.claude/skills/goap-research-ed25519/scripts/test_signature_v3.py +554 -0
- package/templates/.claude/skills/goap-research-ed25519/scripts/test_suite_completeness.py +90 -0
|
@@ -77,6 +77,32 @@ Explicitly state:
|
|
|
77
77
|
- **Dependencies:** What must exist before this feature works
|
|
78
78
|
- **Dependents:** What will break if this feature changes
|
|
79
79
|
|
|
80
|
+
## Write discipline (the 180-second rule)
|
|
81
|
+
|
|
82
|
+
An executor that returns from a tool call and then thinks in silence past **180 seconds** is killed by the
|
|
83
|
+
runtime. Thinking time grows with the history already accumulated, so on a large repo "read everything,
|
|
84
|
+
then write the document" is not a risk — it is a deterministic death, and nothing survives it, because
|
|
85
|
+
nothing was ever on disk.
|
|
86
|
+
|
|
87
|
+
MEASURED on this harness: the writing steps died **18 times out of 18** in the reading phase without ever
|
|
88
|
+
writing a file. The control — same slice, same model, one added instruction to write a skeleton early —
|
|
89
|
+
landed the skeleton 8 minutes in, on the first attempt, after six consecutive deaths.
|
|
90
|
+
|
|
91
|
+
So, in this step:
|
|
92
|
+
|
|
93
|
+
1. **Skeleton first — inside your first ~12 tool calls.** Write `features/<slug>/01_requirements.md` containing only the headings this step
|
|
94
|
+
requires (Stakeholders · Functional requirements · Non-functional requirements · Constraints ·
|
|
95
|
+
Scope boundaries · Open questions), one line of intent under each. (S-tier is inline and has no
|
|
96
|
+
file — this section does not apply to it.)
|
|
97
|
+
2. **Then fill it one section per edit.** No single edit longer than ~120 lines. Every edit leaves the
|
|
98
|
+
file readable; none of them is allowed to wait for the section after it.
|
|
99
|
+
3. **Never go more than 2 minutes without a tool call.** A thought that is getting long is the signal to
|
|
100
|
+
stop and write what you have — an edit is a checkpoint, not an interruption.
|
|
101
|
+
4. **When you are unsure whether to read more or to write, WRITE.** A thin section refined later survives;
|
|
102
|
+
a perfect section you never reached does not.
|
|
103
|
+
|
|
104
|
+
The skeleton is not a draft to apologise for. It is the artifact, opened early.
|
|
105
|
+
|
|
80
106
|
## Output
|
|
81
107
|
|
|
82
108
|
### For S-tier (inline)
|
|
@@ -73,6 +73,32 @@ Alternative considered: [Y], rejected because [reasons].
|
|
|
73
73
|
|
|
74
74
|
After both complete, synthesize results sequentially.
|
|
75
75
|
|
|
76
|
+
## Write discipline (the 180-second rule)
|
|
77
|
+
|
|
78
|
+
An executor that returns from a tool call and then thinks in silence past **180 seconds** is killed by the
|
|
79
|
+
runtime. Thinking time grows with the history already accumulated, so on a large repo "read everything,
|
|
80
|
+
then write the document" is not a risk — it is a deterministic death, and nothing survives it, because
|
|
81
|
+
nothing was ever on disk.
|
|
82
|
+
|
|
83
|
+
MEASURED on this harness: the writing steps died **18 times out of 18** in the reading phase without ever
|
|
84
|
+
writing a file. The control — same slice, same model, one added instruction to write a skeleton early —
|
|
85
|
+
landed the skeleton 8 minutes in, on the first attempt, after six consecutive deaths.
|
|
86
|
+
|
|
87
|
+
So, in this step:
|
|
88
|
+
|
|
89
|
+
1. **Skeleton first — inside your first ~12 tool calls.** Write `features/<slug>/02_research.md` containing only the headings this step
|
|
90
|
+
requires (Codebase patterns · External patterns · Library evaluation · Anti-patterns ·
|
|
91
|
+
Recommended approach), one line of intent under each — before the research agents report back,
|
|
92
|
+
not after.
|
|
93
|
+
2. **Then fill it one section per edit.** No single edit longer than ~120 lines. Every edit leaves the
|
|
94
|
+
file readable; none of them is allowed to wait for the section after it.
|
|
95
|
+
3. **Never go more than 2 minutes without a tool call.** A thought that is getting long is the signal to
|
|
96
|
+
stop and write what you have — an edit is a checkpoint, not an interruption.
|
|
97
|
+
4. **When you are unsure whether to read more or to write, WRITE.** A thin section refined later survives;
|
|
98
|
+
a perfect section you never reached does not.
|
|
99
|
+
|
|
100
|
+
The skeleton is not a draft to apologise for. It is the artifact, opened early.
|
|
101
|
+
|
|
76
102
|
## Output
|
|
77
103
|
|
|
78
104
|
Create `features/<slug>/02_research.md` with:
|
|
@@ -167,6 +167,32 @@ Each ADR must trace back to specific requirements:
|
|
|
167
167
|
| Placeholder text | Template hints or TODOs remain | Remove or fill before checkpoint |
|
|
168
168
|
| Supersession in-place | Existing ADR rewritten | Mint a new ADR and link it |
|
|
169
169
|
|
|
170
|
+
## Write discipline (the 180-second rule)
|
|
171
|
+
|
|
172
|
+
An executor that returns from a tool call and then thinks in silence past **180 seconds** is killed by the
|
|
173
|
+
runtime. Thinking time grows with the history already accumulated, so on a large repo "read everything,
|
|
174
|
+
then write the document" is not a risk — it is a deterministic death, and nothing survives it, because
|
|
175
|
+
nothing was ever on disk.
|
|
176
|
+
|
|
177
|
+
MEASURED on this harness: the writing steps died **18 times out of 18** in the reading phase without ever
|
|
178
|
+
writing a file. The control — same slice, same model, one added instruction to write a skeleton early —
|
|
179
|
+
landed the skeleton 8 minutes in, on the first attempt, after six consecutive deaths.
|
|
180
|
+
|
|
181
|
+
So, in this step:
|
|
182
|
+
|
|
183
|
+
1. **Skeleton first — inside your first ~12 tool calls.** For each decision you have identified, write
|
|
184
|
+
`features/<slug>/03_adr/NNN-{decision-slug}.md` containing only the invariant-core headings
|
|
185
|
+
(Status · Context · Decision Drivers · Considered Options · Decision · Rationale · Consequences ·
|
|
186
|
+
Confirmation), one line of intent under each. A named-but-empty option beats an unwritten ADR.
|
|
187
|
+
2. **Then fill it one section per edit.** No single edit longer than ~120 lines. Every edit leaves the
|
|
188
|
+
file readable; none of them is allowed to wait for the section after it.
|
|
189
|
+
3. **Never go more than 2 minutes without a tool call.** A thought that is getting long is the signal to
|
|
190
|
+
stop and write what you have — an edit is a checkpoint, not an interruption.
|
|
191
|
+
4. **When you are unsure whether to read more or to write, WRITE.** A thin section refined later survives;
|
|
192
|
+
a perfect section you never reached does not.
|
|
193
|
+
|
|
194
|
+
The skeleton is not a draft to apologise for. It is the artifact, opened early.
|
|
195
|
+
|
|
170
196
|
## Output
|
|
171
197
|
|
|
172
198
|
Create `features/<slug>/03_adr/` directory with:
|
|
@@ -154,6 +154,31 @@ AM-N (source): <change>. Confirmation: <property> → test `test_name` (fails if
|
|
|
154
154
|
an existence test and never fires in production.
|
|
155
155
|
- A cheap lint holds the shape: every `AM-N` line must contain a `→ test ` token naming a test that exists.
|
|
156
156
|
|
|
157
|
+
## Write discipline (the 180-second rule)
|
|
158
|
+
|
|
159
|
+
An executor that returns from a tool call and then thinks in silence past **180 seconds** is killed by the
|
|
160
|
+
runtime. Thinking time grows with the history already accumulated, so on a large repo "read everything,
|
|
161
|
+
then write the document" is not a risk — it is a deterministic death, and nothing survives it, because
|
|
162
|
+
nothing was ever on disk.
|
|
163
|
+
|
|
164
|
+
MEASURED on this harness: the writing steps died **18 times out of 18** in the reading phase without ever
|
|
165
|
+
writing a file. The control — same slice, same model, one added instruction to write a skeleton early —
|
|
166
|
+
landed the skeleton 8 minutes in, on the first attempt, after six consecutive deaths.
|
|
167
|
+
|
|
168
|
+
So, in this step:
|
|
169
|
+
|
|
170
|
+
1. **Skeleton first — inside your first ~12 tool calls.** Write `features/<slug>/03.5_ideation_report.md` containing only the eight
|
|
171
|
+
section headings of §5 (Executive summary … Amendments), one line of intent under each — while
|
|
172
|
+
the swarm is still running, not after every agent has returned.
|
|
173
|
+
2. **Then fill it one section per edit.** No single edit longer than ~120 lines. Every edit leaves the
|
|
174
|
+
file readable; none of them is allowed to wait for the section after it.
|
|
175
|
+
3. **Never go more than 2 minutes without a tool call.** A thought that is getting long is the signal to
|
|
176
|
+
stop and write what you have — an edit is a checkpoint, not an interruption.
|
|
177
|
+
4. **When you are unsure whether to read more or to write, WRITE.** A thin section refined later survives;
|
|
178
|
+
a perfect section you never reached does not.
|
|
179
|
+
|
|
180
|
+
The skeleton is not a draft to apologise for. It is the artifact, opened early.
|
|
181
|
+
|
|
157
182
|
## Output
|
|
158
183
|
|
|
159
184
|
Create `features/<slug>/03.5_ideation_report.md` with full QCSD report.
|
|
@@ -92,6 +92,31 @@ Check that proposed domain model is compatible with existing code:
|
|
|
92
92
|
- Can aggregates be implemented with existing ORM/framework?
|
|
93
93
|
- Do relationships align with existing data access patterns?
|
|
94
94
|
|
|
95
|
+
## Write discipline (the 180-second rule)
|
|
96
|
+
|
|
97
|
+
An executor that returns from a tool call and then thinks in silence past **180 seconds** is killed by the
|
|
98
|
+
runtime. Thinking time grows with the history already accumulated, so on a large repo "read everything,
|
|
99
|
+
then write the document" is not a risk — it is a deterministic death, and nothing survives it, because
|
|
100
|
+
nothing was ever on disk.
|
|
101
|
+
|
|
102
|
+
MEASURED on this harness: the writing steps died **18 times out of 18** in the reading phase without ever
|
|
103
|
+
writing a file. The control — same slice, same model, one added instruction to write a skeleton early —
|
|
104
|
+
landed the skeleton 8 minutes in, on the first attempt, after six consecutive deaths.
|
|
105
|
+
|
|
106
|
+
So, in this step:
|
|
107
|
+
|
|
108
|
+
1. **Skeleton first — inside your first ~12 tool calls.** Write `features/<slug>/04_domain_model.md` containing only the headings this step
|
|
109
|
+
requires (Bounded context map · Ubiquitous language · Aggregates/Entities/VOs · Relationships ·
|
|
110
|
+
Codebase mapping), one line of intent under each.
|
|
111
|
+
2. **Then fill it one section per edit.** No single edit longer than ~120 lines. Every edit leaves the
|
|
112
|
+
file readable; none of them is allowed to wait for the section after it.
|
|
113
|
+
3. **Never go more than 2 minutes without a tool call.** A thought that is getting long is the signal to
|
|
114
|
+
stop and write what you have — an edit is a checkpoint, not an interruption.
|
|
115
|
+
4. **When you are unsure whether to read more or to write, WRITE.** A thin section refined later survives;
|
|
116
|
+
a perfect section you never reached does not.
|
|
117
|
+
|
|
118
|
+
The skeleton is not a draft to apologise for. It is the artifact, opened early.
|
|
119
|
+
|
|
95
120
|
## Output
|
|
96
121
|
|
|
97
122
|
Create `features/<slug>/04_domain_model.md` with:
|
|
@@ -118,6 +118,32 @@ GET /api/v1/{resource}/:id
|
|
|
118
118
|
Errors: 401, 404
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
+
## Write discipline (the 180-second rule)
|
|
122
|
+
|
|
123
|
+
An executor that returns from a tool call and then thinks in silence past **180 seconds** is killed by the
|
|
124
|
+
runtime. Thinking time grows with the history already accumulated, so on a large repo "read everything,
|
|
125
|
+
then write the document" is not a risk — it is a deterministic death, and nothing survives it, because
|
|
126
|
+
nothing was ever on disk.
|
|
127
|
+
|
|
128
|
+
MEASURED on this harness: the writing steps died **18 times out of 18** in the reading phase without ever
|
|
129
|
+
writing a file. The control — same slice, same model, one added instruction to write a skeleton early —
|
|
130
|
+
landed the skeleton 8 minutes in, on the first attempt, after six consecutive deaths.
|
|
131
|
+
|
|
132
|
+
So, in this step:
|
|
133
|
+
|
|
134
|
+
1. **Skeleton first — inside your first ~12 tool calls.** Write `features/<slug>/05_architecture.md` containing only the headings this step
|
|
135
|
+
requires at your tier (C4 Context · C4 Container · C4 Component · Sequence flows · Data & storage ·
|
|
136
|
+
API design), one line of intent under each. An empty `mermaid` fence with a caption is a
|
|
137
|
+
heading; fill it later.
|
|
138
|
+
2. **Then fill it one section per edit.** No single edit longer than ~120 lines. Every edit leaves the
|
|
139
|
+
file readable; none of them is allowed to wait for the section after it.
|
|
140
|
+
3. **Never go more than 2 minutes without a tool call.** A thought that is getting long is the signal to
|
|
141
|
+
stop and write what you have — an edit is a checkpoint, not an interruption.
|
|
142
|
+
4. **When you are unsure whether to read more or to write, WRITE.** A thin section refined later survives;
|
|
143
|
+
a perfect section you never reached does not.
|
|
144
|
+
|
|
145
|
+
The skeleton is not a draft to apologise for. It is the artifact, opened early.
|
|
146
|
+
|
|
121
147
|
## Output
|
|
122
148
|
|
|
123
149
|
### M-tier
|
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
## When
|
|
6
6
|
|
|
7
|
-
Always runs.
|
|
8
|
-
- **S:**
|
|
7
|
+
Always runs, and ALWAYS writes `features/<slug>/06_implementation_plan.md` — every tier. Depth adapts:
|
|
8
|
+
- **S:** minimal plan FILE (3-5 tasks + an `EXPECTED_CODE_TARGETS:` block). An inline-only checklist is
|
|
9
|
+
no longer permitted: the K2 gate below is mandatory at every tier and it reads the file, so a plan
|
|
10
|
+
that exists only in the conversation makes the gate answer NOT-ESTABLISHED and the run stops. The
|
|
11
|
+
file must clear the gate's own floor: >200 characters and a non-empty targets block.
|
|
9
12
|
- **M/L/XL:** Full structured plan document
|
|
10
13
|
|
|
11
14
|
## Model
|
|
@@ -132,15 +135,45 @@ Before finalizing the plan, validate completeness:
|
|
|
132
135
|
|
|
133
136
|
This gap-check loop ensures the implementation plan is complete before coding begins.
|
|
134
137
|
|
|
138
|
+
## Write discipline (the 180-second rule)
|
|
139
|
+
|
|
140
|
+
An executor that returns from a tool call and then thinks in silence past **180 seconds** is killed by the
|
|
141
|
+
runtime. Thinking time grows with the history already accumulated, so on a large repo "read everything,
|
|
142
|
+
then write the document" is not a risk — it is a deterministic death, and nothing survives it, because
|
|
143
|
+
nothing was ever on disk.
|
|
144
|
+
|
|
145
|
+
MEASURED on this harness: the writing steps died **18 times out of 18** in the reading phase without ever
|
|
146
|
+
writing a file. The control — same slice, same model, one added instruction to write a skeleton early —
|
|
147
|
+
landed the skeleton 8 minutes in, on the first attempt, after six consecutive deaths.
|
|
148
|
+
|
|
149
|
+
So, in this step:
|
|
150
|
+
|
|
151
|
+
1. **Skeleton first — inside your first ~12 tool calls.** Write `features/<slug>/06_implementation_plan.md` containing only the headings
|
|
152
|
+
this step requires (Goal state · Tasks · Dependency order · Parallel groups · Checkpoints · Risk
|
|
153
|
+
assessment · Amendments · `EXPECTED_CODE_TARGETS:`), one line of intent under each. The K2 gate
|
|
154
|
+
reads this file — a plan that lives only in the conversation cannot pass it.
|
|
155
|
+
2. **Then fill it one section per edit.** No single edit longer than ~120 lines. Every edit leaves the
|
|
156
|
+
file readable; none of them is allowed to wait for the section after it.
|
|
157
|
+
3. **Never go more than 2 minutes without a tool call.** A thought that is getting long is the signal to
|
|
158
|
+
stop and write what you have — an edit is a checkpoint, not an interruption.
|
|
159
|
+
4. **When you are unsure whether to read more or to write, WRITE.** A thin section refined later survives;
|
|
160
|
+
a perfect section you never reached does not.
|
|
161
|
+
|
|
162
|
+
The skeleton is not a draft to apologise for. It is the artifact, opened early.
|
|
163
|
+
|
|
135
164
|
## Output
|
|
136
165
|
|
|
137
|
-
### S-tier (inline)
|
|
138
|
-
|
|
166
|
+
### S-tier (minimal file — NOT inline-only)
|
|
167
|
+
Write `features/<slug>/06_implementation_plan.md` with the checklist plus the machine-read block, and
|
|
168
|
+
show the same checklist inline at the checkpoint:
|
|
139
169
|
```
|
|
140
170
|
Implementation plan:
|
|
141
171
|
- [ ] {task 1}
|
|
142
172
|
- [ ] {task 2}
|
|
143
173
|
- [ ] {task 3}
|
|
174
|
+
|
|
175
|
+
EXPECTED_CODE_TARGETS:
|
|
176
|
+
- {repo-relative path Step 7 will create or modify}
|
|
144
177
|
```
|
|
145
178
|
|
|
146
179
|
### M/L/XL
|
|
@@ -157,6 +190,41 @@ Create `features/<slug>/06_implementation_plan.md` with:
|
|
|
157
190
|
|
|
158
191
|
Set `{IMPL_PLAN}` variable.
|
|
159
192
|
|
|
193
|
+
## K2 plan-completeness gate (MANDATORY — Step 7 does not start until it exits 0)
|
|
194
|
+
|
|
195
|
+
After the plan is written, run the gate. It is a script, not a judgement call:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
node .claude/skills/feature-adr/scripts/check-plan-completeness.mjs features/<slug>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
| Exit | Verdict | What you do |
|
|
202
|
+
|------|---------|-------------|
|
|
203
|
+
| `0` | PASS | proceed to Step 7 |
|
|
204
|
+
| `1` | FAIL | **return to Step 6** — fix the plan for every `FAIL C*` line, rerun the gate |
|
|
205
|
+
| `3` | NOT-ESTABLISHED | **INCONCLUSIVE** — the gate could not read its inputs (no plan, plan under the size floor, ADR claims it cannot see). Fix the inputs and rerun. Never proceed. |
|
|
206
|
+
|
|
207
|
+
Never proceed on a non-zero exit, and never treat empty output as a pass — the last line
|
|
208
|
+
(`K2 plan-completeness: PASS|FAIL|NOT-ESTABLISHED`) is the verdict, and its absence is not one.
|
|
209
|
+
What it checks: C1 every ADR has a plan task citing it · C2 every ADR Confirmation test path is named
|
|
210
|
+
in the plan · C3 the `EXPECTED_CODE_TARGETS:` block parses line by line · C4 the feature's declared
|
|
211
|
+
acid corpus is named · C5 (WARN) the `Inputs read:` line. An S-tier run with no `03_adr/` skips C1/C2
|
|
212
|
+
with a note (it cannot be failed for ADRs it never had) — unless the plan itself cites `ADR-<n>`,
|
|
213
|
+
which is NOT-ESTABLISHED. C1 is a grep: it catches "forgot entirely", not "mentioned but not tasked".
|
|
214
|
+
|
|
215
|
+
Pass the run's tier so the check cannot be dodged: `--tier=S|M|L|XL`. An M/L/XL feature with no
|
|
216
|
+
`03_adr/` FAILS C1/C2 (an M+ feature owes ADRs); only `--tier=S` — or no tier at all, and then the
|
|
217
|
+
skip note says so — takes the skip. A declared-but-malformed acid table (lowercase `| a1 |`, a header
|
|
218
|
+
with no parsable rows) FAILS as `C4-malformed`; it is not a skip.
|
|
219
|
+
|
|
220
|
+
**If the gate fails in the ultracode workflow:** the run returns `phase: 'plan-gate-failed'` and the
|
|
221
|
+
plan stage is CHECKPOINTED, so a bare re-invoke resumes the same failing plan. Repair it from outside:
|
|
222
|
+
edit `features/<slug>/06_implementation_plan.md` to fix each `FAIL` line and re-invoke (the checkpoint
|
|
223
|
+
is keyed on run INPUTS, not on the file, so your edit survives), or re-invoke with `args.resume:
|
|
224
|
+
'never'` (or delete `features/<slug>/.fa-state/`) to force a fresh plan.
|
|
225
|
+
|
|
226
|
+
The checkpoint banner's Gates line carries the verdict: `K2 plan-completeness ✓ | ✗ | inconclusive`.
|
|
227
|
+
|
|
160
228
|
## Checkpoint Format
|
|
161
229
|
|
|
162
230
|
```
|
|
@@ -167,6 +235,7 @@ Tier: {COMPLEXITY_TIER}
|
|
|
167
235
|
|
|
168
236
|
{N} tasks in {M} parallel groups
|
|
169
237
|
Estimated {K} files to create/modify
|
|
238
|
+
🚦 Gates: K2 plan-completeness ✓ (exit 0) ← ✗/inconclusive ⇒ do NOT start Step 7
|
|
170
239
|
|
|
171
240
|
• "ок" — start coding
|
|
172
241
|
• "разбей [TASK-N]" — split task further
|
|
@@ -196,3 +265,4 @@ re-flagging it. In the ultracode workflow this врезка runs automatically a
|
|
|
196
265
|
- [ ] Parallel groups correctly identified
|
|
197
266
|
- [ ] Each task touches identifiable files
|
|
198
267
|
- [ ] Total file count matches complexity tier expectation
|
|
268
|
+
- [ ] `check-plan-completeness.mjs features/<slug>` exited 0 (its output quoted at the checkpoint)
|
|
@@ -152,7 +152,7 @@ Fail the ADR gate for any miss:
|
|
|
152
152
|
| Follow-up discipline | Consequences link follow-up ADRs or state none; after-action review is scheduled |
|
|
153
153
|
| Confirmation stanza | Names verification method, monitoring, success metric, and owner |
|
|
154
154
|
| Confirmation-to-test link | The named load-bearing property has a real automated test/fitness check by file/name |
|
|
155
|
-
| No placeholders | No TODO
|
|
155
|
+
| No placeholders | No `TODO`, template hint text, raw prompt/generation scaffold, or empty tail sections |
|
|
156
156
|
|
|
157
157
|
Reject explainer-masquerading-as-ADR: if the document describes a problem space or technology landscape but has no concrete `Decision`, record a BLOCKER.
|
|
158
158
|
|
|
@@ -182,6 +182,19 @@ paths — especially startup/lifespan/health: require a negative resource-down t
|
|
|
182
182
|
path degrades per its declared contract: fail-open for advisory, explicit fail-fast for load-bearing).
|
|
183
183
|
Missing → HIGH gap.
|
|
184
184
|
|
|
185
|
+
### 3d. No-stubs gate (backlog 0b403a0106103901 — layer 1 of the cost-of-detection ladder)
|
|
186
|
+
|
|
187
|
+
Over the files THIS RUN touched (the Step-7 change list; for a Codex coder, the landed-barrier file
|
|
188
|
+
list), grep for unfinished-stub markers: `TODO` / `FIXME` / `HACK` / `XXX` / `PLACEHOLDER`
|
|
189
|
+
(case-SENSITIVE, word-bounded — never add `-i`; `hackathon`/`todos` must not fire) plus the phrase
|
|
190
|
+
`implement later` (case-insensitive). **Any match = the task shipped incomplete** → HIGH gap naming
|
|
191
|
+
file:line, UNLESS the line carries an inline `no-stubs: <reason>` waiver WITH a non-empty reason, or
|
|
192
|
+
`.dz/guard.json` `stubWaivers` lists the path WITH a reason. A REASONLESS waiver is itself a HIGH gap,
|
|
193
|
+
never an exemption. Cross-check mechanically: `dz guard check --op publish --json` runs the same scan
|
|
194
|
+
as the SOFT `no-stubs` rule over the working-tree diff. When you QUOTE a marker in `08_qe_report.md`,
|
|
195
|
+
backtick it so the report itself scans clean (the claim-check forbidden-phrase convention). Record the
|
|
196
|
+
verdict in the ADR Fitness section.
|
|
197
|
+
|
|
185
198
|
### 4. Multi-Agent Review Panel (L/XL)
|
|
186
199
|
|
|
187
200
|
For L/XL features, spawn 3 parallel review agents using brutal-honesty modes:
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// K2 — machine plan-completeness gate for /feature-adr, run BETWEEN Step 6 (plan) and Step 7 (code).
|
|
3
|
+
// Generalized from features/wave1-instrument-repair/check-plan-completeness.mjs (that copy is the
|
|
4
|
+
// historical artifact of its run and stays untouched); this one is parameterized by feature dir.
|
|
5
|
+
//
|
|
6
|
+
// USAGE: node .claude/skills/feature-adr/scripts/check-plan-completeness.mjs [<feature-dir>] [--tier=M] [--acid=A1,A2]
|
|
7
|
+
// <feature-dir> defaults to the current working directory.
|
|
8
|
+
// --tier=S|M|L|XL closes the ADR-less dodge (see S-TIER HONESTY); omitting it keeps the
|
|
9
|
+
// heuristic, and the skip note then names the dodge out loud.
|
|
10
|
+
//
|
|
11
|
+
// VERDICT CONTRACT (unchanged from the proven copy — never a silent pass):
|
|
12
|
+
// PASS exit 0 last line: `K2 plan-completeness: PASS (...)`
|
|
13
|
+
// FAIL exit 1 last line: `K2 plan-completeness: FAIL (...)`
|
|
14
|
+
// NOT-ESTABLISHED exit 3 last line: `K2 plan-completeness: NOT-ESTABLISHED — <reason>`
|
|
15
|
+
// (the wave1 copy printed the exit-3 line as `K2: NOT-ESTABLISHED — …`; the prefix is unified here so
|
|
16
|
+
// ONE regex parses all three verdicts — the exit codes and their meanings are identical.)
|
|
17
|
+
// Set difference over IDENTIFIERS, not text similarity.
|
|
18
|
+
//
|
|
19
|
+
// KNOWN LIMITATION (measured on the discrimination twin, 2026-08-19): C1 is a grep — a PROSE
|
|
20
|
+
// mention of "ADR-002" satisfies it exactly like a task reference. The real N14 (backlog
|
|
21
|
+
// 3dbd2851-adjacent) must parse task structure. Kept honest here: C1 catches "forgot entirely",
|
|
22
|
+
// not "mentioned but not tasked".
|
|
23
|
+
//
|
|
24
|
+
// Checks:
|
|
25
|
+
// C1 every ADR file in 03_adr/ has >=1 task line in 06_implementation_plan.md citing it (ADR-00N)
|
|
26
|
+
// C2 every Confirmation-numbered check in each ADR is named in the plan (by its test-file path)
|
|
27
|
+
// C3 the plan carries an EXPECTED_CODE_TARGETS: block, non-empty, and EVERY line parses to a
|
|
28
|
+
// plausible repo-relative path (no spaces unless quoted, no traversal, no markdown residue)
|
|
29
|
+
// — SFDIPOT condition: line-level validation, reject-with-reason, not just block presence
|
|
30
|
+
// C4 the plan names the feature's OWN acid corpus (see "acid corpus" below)
|
|
31
|
+
// C5 the plan has an 'Inputs read:' line naming 03_adr, 05_architecture (wave-2 seam, cheap here)
|
|
32
|
+
//
|
|
33
|
+
// S-TIER HONESTY (no 03_adr/): an S-tier run legitimately has no ADR files, and forcing it to fail a
|
|
34
|
+
// plan gate it can never satisfy would make the gate a nuisance to route around. So:
|
|
35
|
+
// - no ADR files BUT the plan cites `ADR-<digits>` → NOT-ESTABLISHED (exit 3): the plan claims
|
|
36
|
+
// decisions the gate cannot see, so its completeness is unknown — that is never a pass.
|
|
37
|
+
// - no ADR files AND --tier is M/L/XL → FAIL: an M+ feature owes ADRs, and skipping C1/C2 for it
|
|
38
|
+
// would let a whole tier dodge the check by simply not writing 03_adr/ (G-F3).
|
|
39
|
+
// - no ADR files AND (--tier=S or no --tier) AND the plan claims no ADR work
|
|
40
|
+
// → C1 + C2 are SKIPPED-with-note (printed as SKIP lines; the run can still PASS on C3-C5).
|
|
41
|
+
// Without --tier the skip note NAMES the dodge, because the gate cannot tell an honest S-run
|
|
42
|
+
// from an M+ run that deleted its ADR directory.
|
|
43
|
+
//
|
|
44
|
+
// ECHO SAFETY (G-F1, reproduced by execution): this script echoes plan-controlled content (rejected
|
|
45
|
+
// target lines, acid tokens, the feature path). A plan line carrying the literal verdict marker was
|
|
46
|
+
// echoed verbatim and a first-match parser upstream read the PLANT instead of the real verdict. Every
|
|
47
|
+
// echoed value now goes through `safe()`, which defangs the marker and the exit trailer and truncates,
|
|
48
|
+
// so the only spellable verdict line in this stream is the one this script writes last.
|
|
49
|
+
//
|
|
50
|
+
// ACID CORPUS (C4): the wave1 copy hard-coded ITS run's tokens. Here the corpus is DISCOVERED from
|
|
51
|
+
// the feature's own `00_complexity_assessment.md` acid-case table (rows shaped `| A<N> | … |`), or
|
|
52
|
+
// supplied explicitly with `--acid=T1,T2,…`. If neither establishes a corpus, C4 is SKIPPED-with-note
|
|
53
|
+
// (a feature that declared no acid cases cannot be failed for not naming them).
|
|
54
|
+
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
55
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
56
|
+
|
|
57
|
+
const argv = process.argv.slice(2);
|
|
58
|
+
const acidArg = argv.find((a) => a.startsWith('--acid='));
|
|
59
|
+
const tierArg = argv.find((a) => a.startsWith('--tier='));
|
|
60
|
+
const TIER = tierArg ? tierArg.slice('--tier='.length).trim().toUpperCase() : null;
|
|
61
|
+
const TIER_REQUIRES_ADR = TIER === 'M' || TIER === 'L' || TIER === 'XL';
|
|
62
|
+
const dirArg = argv.find((a) => !a.startsWith('--'));
|
|
63
|
+
const FDIR = resolve(dirArg && dirArg !== '' ? (isAbsolute(dirArg) ? dirArg : join(process.cwd(), dirArg)) : process.cwd());
|
|
64
|
+
const planPath = join(FDIR, '06_implementation_plan.md');
|
|
65
|
+
const adrDir = join(FDIR, '03_adr');
|
|
66
|
+
const complexityPath = join(FDIR, '00_complexity_assessment.md');
|
|
67
|
+
|
|
68
|
+
// Defang any value this script echoes back: a plan (or a path, or an ADR) must never be able to spell
|
|
69
|
+
// the verdict marker or the exit trailer into this stream. Truncated so one hostile line cannot bury
|
|
70
|
+
// the verdict either. The offender stays READABLE — defanged, not dropped.
|
|
71
|
+
const safe = (v) => String(v)
|
|
72
|
+
.replace(/K2 plan-completeness:/g, 'K2 plan-completeness[echoed]:')
|
|
73
|
+
.replace(/K2_EXIT=/g, 'K2_EXIT[echoed]=')
|
|
74
|
+
.replace(/[\r\n]+/g, ' ')
|
|
75
|
+
.slice(0, 300);
|
|
76
|
+
const out = (s) => console.log(s);
|
|
77
|
+
const notEstablished = (why) => { out(`K2 plan-completeness: NOT-ESTABLISHED — ${safe(why)}`); process.exit(3); };
|
|
78
|
+
let failures = [], warnings = [], skips = [];
|
|
79
|
+
|
|
80
|
+
if (!existsSync(FDIR)) notEstablished(`feature dir absent: ${FDIR}`);
|
|
81
|
+
if (!existsSync(planPath)) notEstablished('06_implementation_plan.md absent');
|
|
82
|
+
const plan = readFileSync(planPath, 'utf-8');
|
|
83
|
+
if (plan.trim().length < 200) notEstablished('plan suspiciously small (<200 chars)');
|
|
84
|
+
const adrFiles = existsSync(adrDir) ? readdirSync(adrDir).filter(f => f.endsWith('.md')).sort() : [];
|
|
85
|
+
const planClaimsAdrWork = /\bADR-\d+/.test(plan);
|
|
86
|
+
if (adrFiles.length === 0 && planClaimsAdrWork) notEstablished('no ADR files under 03_adr/, yet the plan cites ADR-<n> — completeness cannot be established');
|
|
87
|
+
|
|
88
|
+
if (adrFiles.length === 0 && TIER_REQUIRES_ADR) {
|
|
89
|
+
failures.push(`C1: tier ${TIER} has NO ADR files under 03_adr/ — an M/L/XL feature owes at least one ADR; the plan cannot be complete against decisions that were never written`);
|
|
90
|
+
failures.push(`C2: tier ${TIER} has no 03_adr/ — no Confirmation test can be checked`);
|
|
91
|
+
} else if (adrFiles.length === 0) {
|
|
92
|
+
skips.push(`C1: no 03_adr/ and the plan claims no ADR work — ADR-coverage check SKIPPED${TIER === 'S' ? ' (--tier=S, the legitimate S-tier shape)' : ' (NO --tier supplied: an M/L/XL run that simply never wrote 03_adr/ would dodge C1/C2 here — pass --tier to close it)'}`);
|
|
93
|
+
skips.push('C2: no 03_adr/ — Confirmation-test coverage check SKIPPED');
|
|
94
|
+
} else {
|
|
95
|
+
// C1 — ADR ids referenced by plan tasks
|
|
96
|
+
for (const f of adrFiles) {
|
|
97
|
+
const m = f.match(/^(\d{3})-/); if (!m) { warnings.push(`C1: unparseable ADR filename ${safe(f)}`); continue; }
|
|
98
|
+
const id = `ADR-${m[1]}`;
|
|
99
|
+
const re = new RegExp(`ADR-0*${Number(m[1])}\\b`);
|
|
100
|
+
if (!re.test(plan)) failures.push(`C1: ${id} (${safe(f)}) has NO task in the plan referencing it`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// C2 — every Confirmation-listed test file path appears in the plan
|
|
104
|
+
const testPathRe = /(?:packages\/[\w@/.-]+\/test\/[\w./-]+\.test\.(?:ts|mjs|js)|tests?\/[\w./-]+\.test\.(?:ts|mjs|js))/g;
|
|
105
|
+
for (const f of adrFiles) {
|
|
106
|
+
const adr = readFileSync(join(adrDir, f), 'utf-8');
|
|
107
|
+
const confIdx = adr.search(/^##+\s*Confirmation/mi);
|
|
108
|
+
if (confIdx < 0) { failures.push(`C2: ${safe(f)} has no Confirmation section`); continue; }
|
|
109
|
+
const conf = adr.slice(confIdx);
|
|
110
|
+
const paths = [...new Set((conf.match(testPathRe) ?? []))];
|
|
111
|
+
if (paths.length === 0) { failures.push(`C2: ${safe(f)} Confirmation names no test file paths`); continue; }
|
|
112
|
+
for (const p of paths) if (!plan.includes(p)) failures.push(`C2: ${safe(f)} Confirmation test ${safe(p)} NOT named in the plan`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// C3 — EXPECTED_CODE_TARGETS block, line-level validation
|
|
117
|
+
const blockM = plan.match(/EXPECTED_CODE_TARGETS:\s*\n((?:\s*[-*]\s*.+\n?)+)/);
|
|
118
|
+
if (!blockM) failures.push('C3: no EXPECTED_CODE_TARGETS: block in the plan');
|
|
119
|
+
else {
|
|
120
|
+
const lines = blockM[1].split('\n').map(s => s.trim()).filter(Boolean);
|
|
121
|
+
if (lines.length === 0) failures.push('C3: EXPECTED_CODE_TARGETS block is empty');
|
|
122
|
+
for (const ln of lines) {
|
|
123
|
+
const path = ln.replace(/^[-*]\s*/, '').replace(/`/g, '').trim();
|
|
124
|
+
const reasons = [];
|
|
125
|
+
if (/\s/.test(path)) reasons.push('contains whitespace');
|
|
126
|
+
if (/\.\./.test(path)) reasons.push('path traversal');
|
|
127
|
+
if (/[*#\[\]()]/.test(path)) reasons.push('markdown residue');
|
|
128
|
+
if (!/^[\w@][\w@/.-]*$/.test(path)) reasons.push('not a plain repo-relative path');
|
|
129
|
+
if (reasons.length) failures.push(`C3: target line rejected: "${safe(ln)}" — ${reasons.join(', ')}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// C4 — the feature's OWN acid corpus is named in the plan (discovered, or --acid=…)
|
|
134
|
+
// G-F5: a corpus that was DECLARED but declared BADLY (lowercase `| a1 |`, a row with trailing junk,
|
|
135
|
+
// or an acid-case table header with no parsable rows) is a FAILURE, not a skip. Silently skipping it
|
|
136
|
+
// let a malformed declaration buy the same green as an honest "no acid cases here". The strict row
|
|
137
|
+
// shape is `| A<digits> |`; anything that LOOKS like an acid row under the loose shape but is not
|
|
138
|
+
// strict is reported by name. (Honest scope: rows that look nothing like an acid row — a defect table
|
|
139
|
+
// `| D1 |`, say — are not acid candidates and are left alone.)
|
|
140
|
+
let acidTokens = [];
|
|
141
|
+
let acidSource = null;
|
|
142
|
+
if (acidArg) {
|
|
143
|
+
acidTokens = acidArg.slice('--acid='.length).split(',').map(s => s.trim()).filter(Boolean);
|
|
144
|
+
acidSource = '--acid';
|
|
145
|
+
if (acidTokens.length === 0) failures.push('C4-malformed: --acid was passed but declares no tokens');
|
|
146
|
+
} else if (existsSync(complexityPath)) {
|
|
147
|
+
const complexity = readFileSync(complexityPath, 'utf-8');
|
|
148
|
+
acidTokens = [...new Set((complexity.match(/^\|\s*(A\d+)\s*\|/gm) ?? []).map(r => r.replace(/[|\s]/g, '')))];
|
|
149
|
+
if (acidTokens.length) acidSource = '00_complexity_assessment.md acid-case table';
|
|
150
|
+
const loose = [...new Set((complexity.match(/^\|\s*([Aa]\d+[A-Za-z]?)\s*\|/gm) ?? []).map(r => r.replace(/[|\s]/g, '')))];
|
|
151
|
+
for (const cand of loose) if (!acidTokens.includes(cand)) failures.push(`C4-malformed: acid row "${safe(cand)}" does not match the required \`| A<n> |\` shape (case-sensitive, digits only) — it is declared but uncheckable`);
|
|
152
|
+
const declaresTable = /\|\s*Acid case\s*\|/i.test(complexity);
|
|
153
|
+
if (declaresTable && acidTokens.length === 0) failures.push('C4-malformed: 00_complexity_assessment.md declares an acid-case table but NO `| A<n> |` row parsed from it');
|
|
154
|
+
}
|
|
155
|
+
if (acidTokens.length === 0) skips.push('C4: no acid corpus declared (no --acid, no `| A<n> |` table in 00_complexity_assessment.md) — acid-naming check SKIPPED');
|
|
156
|
+
else for (const t of acidTokens) if (!new RegExp(`\\b${t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i').test(plan)) failures.push(`C4: acid token "${safe(t)}" (from ${acidSource}) not named in the plan`);
|
|
157
|
+
|
|
158
|
+
// C5 — Inputs read line
|
|
159
|
+
if (!/Inputs read:/i.test(plan)) warnings.push('C5: no "Inputs read:" line (wave-2 seam, WARN only)');
|
|
160
|
+
else for (const need of ['03_adr','05_architecture']) if (!plan.includes(need)) warnings.push(`C5: Inputs read line missing ${need}`);
|
|
161
|
+
|
|
162
|
+
for (const s of skips) out('SKIP ' + s);
|
|
163
|
+
for (const w of warnings) out('WARN ' + w);
|
|
164
|
+
for (const f of failures) out('FAIL ' + f);
|
|
165
|
+
out(`K2 plan-completeness: ${failures.length === 0 ? 'PASS' : 'FAIL'} (${failures.length} failure(s), ${warnings.length} warning(s), ${skips.length} skip(s)) over ${adrFiles.length} ADRs, tier ${TIER === null ? '(unspecified)' : safe(TIER)}, in ${safe(FDIR)}`);
|
|
166
|
+
process.exit(failures.length === 0 ? 0 : 1);
|