@llman-sdd/core 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/archive/freeze.ts +86 -18
- package/src/archive/frozenCard.ts +105 -0
- package/src/archive/sevenzip.ts +15 -13
- package/src/change/closeOutHarness.ts +29 -0
- package/src/change/collect.ts +140 -0
- package/src/change/frontmatter.ts +48 -6
- package/src/change/id.ts +2 -6
- package/src/change/lifecycle.ts +285 -86
- package/src/change/nextId.ts +63 -2
- package/src/change/resolve.ts +2 -2
- package/src/change/tasks.ts +59 -0
- package/src/config/changeId.ts +14 -12
- package/src/config/load.ts +14 -0
- package/src/config/schema.ts +4 -41
- package/src/config/surface.ts +6 -36
- package/src/context/indexStore.ts +7 -3
- package/src/context/retrieve.ts +8 -10
- package/src/context/tree.ts +28 -24
- package/src/git/spawnGit.ts +90 -2
- package/src/index.ts +67 -59
- package/src/init/defaultConfig.ts +1 -5
- package/src/init/init.ts +19 -4
- package/src/ports.ts +1 -7
- package/src/project/migrateNotes.ts +104 -0
- package/src/render/machine.ts +30 -0
- package/src/report/collect.ts +11 -127
- package/src/report/graph/analysis.ts +152 -0
- package/src/report/graph/deps.ts +30 -0
- package/src/report/graph/graphData.ts +53 -0
- package/src/report/graph/nodes.ts +130 -0
- package/src/report/graph/render.ts +83 -0
- package/src/report/graph/types.ts +47 -0
- package/src/report/graph.ts +9 -381
- package/src/report/show.ts +20 -22
- package/src/report/specHelpers.ts +42 -21
- package/src/report/specs.ts +23 -25
- package/src/review/review.ts +45 -30
- package/src/spec/authoring.ts +91 -63
- package/src/spec/ir.ts +43 -15
- package/src/spec/migrateNative.ts +167 -0
- package/src/spec/parser.ts +73 -77
- package/src/spec/reqRegistry.ts +8 -9
- package/src/templates/embedded.ts +10 -16
- package/src/templates/engine.ts +10 -5
- package/src/templates/locale.ts +1 -1
- package/src/templates/skills.ts +4 -5
- package/src/validation/changeCheck.ts +128 -105
- package/src/validation/harness.ts +161 -0
- package/src/validation/staleness.ts +9 -5
- package/src/validation/validate.ts +60 -88
- package/templates/en/skills/llman-sdd-apply-cycle.md +20 -28
- package/templates/en/skills/llman-sdd-apply.md +58 -76
- package/templates/en/skills/llman-sdd-arch-review.md +12 -19
- package/templates/en/skills/llman-sdd-archive.md +27 -42
- package/templates/en/skills/llman-sdd-continue.md +17 -24
- package/templates/en/skills/llman-sdd-draft.md +17 -28
- package/templates/en/skills/llman-sdd-explore.md +29 -43
- package/templates/en/skills/llman-sdd-ff.md +12 -17
- package/templates/en/skills/llman-sdd-graph.md +14 -32
- package/templates/en/skills/llman-sdd-propose.md +48 -63
- package/templates/en/skills/llman-sdd-quick.md +12 -27
- package/templates/en/skills/llman-sdd-research.md +13 -24
- package/templates/en/skills/llman-sdd-specs-compact.md +14 -39
- package/templates/en/skills/llman-sdd-validate.md +11 -15
- package/templates/en/skills/llman-sdd-verify.md +23 -44
- package/templates/en/skills/llman-sdd-wayfinder.md +18 -22
- package/templates/en/units/skills/cli-footer.md +2 -0
- package/templates/en/units/skills/git-native-flow-brief.md +7 -6
- package/templates/en/units/skills/git-native-flow.md +21 -11
- package/templates/en/units/skills/human-readable-summary.md +2 -3
- package/templates/en/units/skills/stage-guard.md +7 -7
- package/templates/en/units/skills/structured-protocol.md +5 -8
- package/templates/en/units/skills/validation-hints.md +10 -14
- package/templates/en/units/spec/feature-contract.md +27 -16
- package/templates/en/units/workflow/archive-freeze-guidance.md +6 -3
- package/templates/zh-Hans/skills/llman-sdd-apply-cycle.md +23 -31
- package/templates/zh-Hans/skills/llman-sdd-apply.md +63 -81
- package/templates/zh-Hans/skills/llman-sdd-arch-review.md +21 -28
- package/templates/zh-Hans/skills/llman-sdd-archive.md +29 -44
- package/templates/zh-Hans/skills/llman-sdd-continue.md +17 -24
- package/templates/zh-Hans/skills/llman-sdd-draft.md +18 -29
- package/templates/zh-Hans/skills/llman-sdd-explore.md +34 -48
- package/templates/zh-Hans/skills/llman-sdd-ff.md +13 -18
- package/templates/zh-Hans/skills/llman-sdd-graph.md +16 -34
- package/templates/zh-Hans/skills/llman-sdd-propose.md +51 -65
- package/templates/zh-Hans/skills/llman-sdd-quick.md +15 -30
- package/templates/zh-Hans/skills/llman-sdd-research.md +17 -28
- package/templates/zh-Hans/skills/llman-sdd-specs-compact.md +15 -40
- package/templates/zh-Hans/skills/llman-sdd-validate.md +11 -15
- package/templates/zh-Hans/skills/llman-sdd-verify.md +26 -47
- package/templates/zh-Hans/skills/llman-sdd-wayfinder.md +25 -29
- package/templates/zh-Hans/units/skills/cli-footer.md +2 -0
- package/templates/zh-Hans/units/skills/git-native-flow-brief.md +7 -6
- package/templates/zh-Hans/units/skills/git-native-flow.md +22 -12
- package/templates/zh-Hans/units/skills/human-readable-summary.md +4 -5
- package/templates/zh-Hans/units/skills/stage-guard.md +9 -9
- package/templates/zh-Hans/units/skills/structured-protocol.md +5 -8
- package/templates/zh-Hans/units/skills/validation-hints.md +10 -14
- package/templates/zh-Hans/units/spec/feature-contract.md +25 -16
- package/templates/zh-Hans/units/workflow/archive-freeze-guidance.md +6 -2
- package/templates/en/skills/llman-sdd-onboard.md +0 -34
- package/templates/en/skills/llman-sdd-show.md +0 -24
- package/templates/en/units/migrate-prompt.md +0 -28
- package/templates/zh-Hans/skills/llman-sdd-onboard.md +0 -34
- package/templates/zh-Hans/skills/llman-sdd-show.md +0 -24
- package/templates/zh-Hans/units/migrate-prompt.md +0 -28
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Validation engine (validation capability): aggregates Phase-2 parse errors
|
|
3
|
-
* plus the verdict-equivalent gates (r11/r12, ordered to match
|
|
3
|
+
* plus the verdict-equivalent gates (r11/r12, ordered to match predecessor
|
|
4
4
|
* `spec/validation.rs` observable issue order). Pure — filesystem access is
|
|
5
5
|
* injected via SpecIo.
|
|
6
6
|
*/
|
|
7
7
|
import type { CapabilityDoc } from '../spec/ir.ts';
|
|
8
|
-
import {
|
|
8
|
+
import { specIdOf } from '../spec/ir.ts';
|
|
9
9
|
import { buildReqRegistry } from '../spec/reqRegistry.ts';
|
|
10
10
|
|
|
11
11
|
export type ValidationLevel = 'ERROR' | 'WARNING' | 'INFO';
|
|
12
12
|
|
|
13
13
|
export interface ValidationItem {
|
|
14
14
|
level: ValidationLevel;
|
|
15
|
-
/** Gate anchor, e.g. `t/rule/ok` or `t/valid_scope` (
|
|
15
|
+
/** Gate anchor, e.g. `t/rule/ok` or `t/valid_scope` (predecessor-style `path`). */
|
|
16
16
|
id: string;
|
|
17
17
|
message: string;
|
|
18
18
|
}
|
|
@@ -41,7 +41,6 @@ export interface ValidationReport {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const rulesPath = (cap: string): string => `${cap}/rules`;
|
|
44
|
-
const acceptanceReqPath = (cap: string, name: string): string => `${cap}/acceptance/${name}/@req`;
|
|
45
44
|
const coveragePath = (cap: string): string => `${cap}/coverage`;
|
|
46
45
|
|
|
47
46
|
export function validateCapability(
|
|
@@ -51,7 +50,7 @@ export function validateCapability(
|
|
|
51
50
|
opts: { strict?: boolean } = {},
|
|
52
51
|
): SpecVerdict {
|
|
53
52
|
const { doc } = entry;
|
|
54
|
-
const cap =
|
|
53
|
+
const cap = specIdOf(entry);
|
|
55
54
|
const strict = opts.strict === true;
|
|
56
55
|
const items: ValidationItem[] = [];
|
|
57
56
|
|
|
@@ -59,27 +58,23 @@ export function validateCapability(
|
|
|
59
58
|
items.push({ level, id, message });
|
|
60
59
|
};
|
|
61
60
|
|
|
62
|
-
// Header gates (r12 /
|
|
61
|
+
// Header gates (r12 / predecessor spec_meta). predecessor treats a missing `# capability:`
|
|
63
62
|
// header as a parse-level failure: only `file` + registry-scan issues are
|
|
64
63
|
// emitted and all single-track gates are skipped.
|
|
65
64
|
if (doc.header.capability === null) {
|
|
66
|
-
const msg = `spec \`${cap}\`: missing \`# capability:\` header comment
|
|
65
|
+
const msg = `spec \`${cap}\`: missing \`# capability:\` header comment`;
|
|
67
66
|
push('ERROR', 'file', msg);
|
|
68
67
|
push('ERROR', 'llmanspec/specs', `Failed to scan req_id index: ${msg}`);
|
|
69
68
|
return { fileName: entry.fileName, capability: cap, ok: false, items };
|
|
70
69
|
}
|
|
71
70
|
if (doc.header.purpose === null || doc.header.purpose.trim() === '') {
|
|
72
|
-
push(
|
|
73
|
-
'ERROR',
|
|
74
|
-
`${cap}/purpose`,
|
|
75
|
-
'`# purpose:` header comment must not be empty (spec-format r133)',
|
|
76
|
-
);
|
|
71
|
+
push('ERROR', `${cap}/purpose`, '`# purpose:` header comment must not be empty');
|
|
77
72
|
}
|
|
78
73
|
if (doc.header.scope === null) {
|
|
79
74
|
push(
|
|
80
75
|
'ERROR',
|
|
81
76
|
`${cap}/valid_scope`,
|
|
82
|
-
'Spec valid_scope must not be empty (
|
|
77
|
+
'Spec valid_scope must not be empty (declare it in the "# scope:" header comment).',
|
|
83
78
|
);
|
|
84
79
|
} else {
|
|
85
80
|
// r42: missing valid_scope paths are independent failures —
|
|
@@ -112,86 +107,50 @@ export function validateCapability(
|
|
|
112
107
|
push('ERROR', `${cap}/feature`, 'Feature line must carry a title');
|
|
113
108
|
}
|
|
114
109
|
|
|
115
|
-
// Parser-level structural errors
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
const OWNED_BY_THIS_LAYER = ['missing-header:', 'rule:missing-must-word'];
|
|
110
|
+
// Parser-level structural errors. Header gates are owned here (mapped below);
|
|
111
|
+
// the parser's duplicate header findings are skipped.
|
|
112
|
+
const OWNED_BY_THIS_LAYER = ['missing-header:'];
|
|
119
113
|
for (const err of doc.errors) {
|
|
120
114
|
if (OWNED_BY_THIS_LAYER.some((prefix) => err.code.startsWith(prefix))) continue;
|
|
121
115
|
push('ERROR', err.code.startsWith('file') ? 'file' : `${cap}/${err.code}`, err.message);
|
|
122
116
|
}
|
|
123
117
|
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
const
|
|
118
|
+
// Native single-track gates: `规则:` blocks with `@req` handles; nested
|
|
119
|
+
// `场景:` are their executable examples; top-level scenarios are orphans.
|
|
120
|
+
const rules = doc.rules;
|
|
127
121
|
|
|
128
|
-
if (
|
|
129
|
-
push('ERROR', rulesPath(cap), 'spec must define at least one
|
|
122
|
+
if (rules.length === 0) {
|
|
123
|
+
push('ERROR', rulesPath(cap), 'spec must define at least one rule');
|
|
130
124
|
}
|
|
131
125
|
|
|
132
|
-
for (const
|
|
133
|
-
const anchor = `${cap}/rule/${
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
if (!MUST_WORD_RE.test(scenario.statement)) {
|
|
139
|
-
push('ERROR', anchor, 'constraint statement must contain MUST/SHALL (or 必须/不得/禁止)');
|
|
140
|
-
}
|
|
141
|
-
} else if (scenario.classification === 'executable') {
|
|
142
|
-
// (pairing handled below, after all rule req ids are known)
|
|
143
|
-
} else {
|
|
126
|
+
for (const rule of rules) {
|
|
127
|
+
const anchor = `${cap}/rule/${rule.title}`;
|
|
128
|
+
if (rule.reqId === '') {
|
|
129
|
+
push('ERROR', anchor, 'rule must carry an @req:<req_id> tag on the rule header');
|
|
130
|
+
} else if (duplicatesFor(rule.reqId)) {
|
|
144
131
|
push(
|
|
145
|
-
'
|
|
146
|
-
`${cap}/
|
|
147
|
-
`
|
|
132
|
+
'ERROR',
|
|
133
|
+
`${cap}/registry/${rule.reqId}`,
|
|
134
|
+
`global duplicate req_id \`${rule.reqId}\` used by multiple capabilities`,
|
|
148
135
|
);
|
|
149
136
|
}
|
|
150
|
-
for (const reqId of scenario.reqIds) {
|
|
151
|
-
if (duplicatesFor(reqId)) {
|
|
152
|
-
push(
|
|
153
|
-
'ERROR',
|
|
154
|
-
`${cap}/registry/${reqId}`,
|
|
155
|
-
`global duplicate req_id \`${reqId}\` used by multiple capabilities`,
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
137
|
}
|
|
160
138
|
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// r65: orphan acceptance scenario — no @req link at all (v1 r132 WARNING).
|
|
165
|
-
if (sc.reqIds.length === 0) {
|
|
166
|
-
push(
|
|
167
|
-
'WARNING',
|
|
168
|
-
`${cap}/acceptance/${sc.name}`,
|
|
169
|
-
`orphan acceptance scenario \`${sc.name}\` has no @req:<req_id> link`,
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
for (const rid of sc.reqIds) {
|
|
173
|
-
if (!ruleReqIds.has(rid)) {
|
|
174
|
-
push(
|
|
175
|
-
'ERROR',
|
|
176
|
-
acceptanceReqPath(cap, sc.name),
|
|
177
|
-
`@req:${rid} on acceptance scenario \`${sc.name}\` has no matching @human constraint`,
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
139
|
+
// Top-level `场景:` outside any rule are plain feature-level examples
|
|
140
|
+
// (native Gherkin); they carry no rule handle, so no warning or signal —
|
|
141
|
+
// they simply aren't part of rule accounting.
|
|
182
142
|
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
143
|
+
// Bare-rule aggregate (r134 migrated): rules with no nested executable
|
|
144
|
+
// scenario, aggregated per capability (never one issue per rule), INFO so it
|
|
145
|
+
// never blocks anything. The real accountability lives in the review
|
|
146
|
+
// `pending` signal and the specs-compact workflow.
|
|
147
|
+
const bare = rules.filter((r) => r.scenarios.length === 0).length;
|
|
148
|
+
if (bare > 0) {
|
|
149
|
+
push(
|
|
150
|
+
'INFO',
|
|
151
|
+
coveragePath(cap),
|
|
152
|
+
`${bare} bare rule(s) without any executable scenario — convert to 场景: or compact`,
|
|
153
|
+
);
|
|
195
154
|
}
|
|
196
155
|
|
|
197
156
|
return {
|
|
@@ -202,13 +161,28 @@ export function validateCapability(
|
|
|
202
161
|
};
|
|
203
162
|
}
|
|
204
163
|
|
|
205
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Shared req_id duplicate gate — single source of truth for the CLI
|
|
166
|
+
* (`validate <spec>` path) and the full sweep. The registry is built from the
|
|
167
|
+
* already-parsed docs, so a duplicate is reported for every involved
|
|
168
|
+
* capability regardless of unrelated parse errors elsewhere (r12 acceptance:
|
|
169
|
+
* 重复 req_id MUST 对每个涉事 capability 判 ERROR — the 前代 "structural error
|
|
170
|
+
* aborts the index scan" guard is intentionally dropped; a parse-failed spec
|
|
171
|
+
* merely omits the scenarios it could not decode, never invents ids).
|
|
172
|
+
*/
|
|
173
|
+
export function buildDuplicatesFor(entries: readonly SpecEntry[]): (reqId: string) => boolean {
|
|
206
174
|
const registry = buildReqRegistry(entries);
|
|
207
175
|
const duplicateIds = new Set(registry.duplicates.flatMap((d) => d.reqId));
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
176
|
+
return (reqId: string): boolean => duplicateIds.has(reqId);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** `Totals:` report line — single wording source for the engine and the CLI. */
|
|
180
|
+
export function formatTotals(passed: number, failed: number, total: number): string {
|
|
181
|
+
return `Totals: ${passed} passed, ${failed} failed (${total} items)`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function validateAllSpecs(entries: readonly SpecEntry[], io: SpecIo): ValidationReport {
|
|
185
|
+
const duplicatesFor = buildDuplicatesFor(entries);
|
|
212
186
|
|
|
213
187
|
const verdicts = entries.map((e) => validateCapability(e, duplicatesFor, io));
|
|
214
188
|
const failed = verdicts.some((v) => !v.ok);
|
|
@@ -221,14 +195,12 @@ export function validateAllSpecs(entries: readonly SpecEntry[], io: SpecIo): Val
|
|
|
221
195
|
lines.push(` [${item.level}] ${item.id}: ${item.message}`);
|
|
222
196
|
}
|
|
223
197
|
}
|
|
224
|
-
lines.push(
|
|
225
|
-
`Totals: ${passed} passed, ${verdicts.length - passed} failed (${verdicts.length} items)`,
|
|
226
|
-
);
|
|
198
|
+
lines.push(formatTotals(passed, verdicts.length - passed, verdicts.length));
|
|
227
199
|
|
|
228
200
|
return { verdicts, lines, failed };
|
|
229
201
|
}
|
|
230
202
|
|
|
231
|
-
/**
|
|
203
|
+
/** predecessor `apply_strict`: WARNING issues escalate to ERROR when --strict. */
|
|
232
204
|
export function applyStrict<T extends { level: ValidationLevel }>(items: readonly T[]): T[] {
|
|
233
205
|
return items.map((i) => (i.level === 'WARNING' ? { ...i, level: 'ERROR' as const } : i));
|
|
234
206
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "llman-sdd-apply-cycle"
|
|
3
|
-
description: "
|
|
3
|
+
description: "End-to-end closed loop for one change: implement→test→validate→verify→archive. Manual trigger only; agent must not auto-invoke."
|
|
4
4
|
metadata:
|
|
5
5
|
version: "{{ llman_version }}"
|
|
6
6
|
disable-model-invocation: true
|
|
@@ -8,7 +8,7 @@ disable-model-invocation: true
|
|
|
8
8
|
|
|
9
9
|
# LLMAN SDD Apply Cycle
|
|
10
10
|
|
|
11
|
-
End-to-end closed loop for one change (manual). Requires
|
|
11
|
+
End-to-end closed loop for one change (manual). Requires a bound branch and a green specs-landed gate (`specsLanded ∨ needsSpecsChange=false`); `readyToImplement=true` (the completion signal) closes the cycle.
|
|
12
12
|
|
|
13
13
|
**Manual trigger only**: `/skill:llman-sdd-apply-cycle <change-id>`
|
|
14
14
|
|
|
@@ -16,60 +16,52 @@ End-to-end closed loop for one change (manual). Requires Branch binding and `rea
|
|
|
16
16
|
|
|
17
17
|
### 0) Gate + status
|
|
18
18
|
```bash
|
|
19
|
-
llman-sdd show <change-id> --json --type change
|
|
19
|
+
llman-sdd show <change-id> --output json --type change
|
|
20
20
|
```
|
|
21
|
-
> Stage
|
|
21
|
+
> Stage decisions use the `stage` / `readyToImplement` fields; the full decision table lives in llman-sdd-apply.
|
|
22
22
|
|
|
23
23
|
- Must be on the bound non-default branch.
|
|
24
|
-
-
|
|
25
|
-
- Track progress via `tasks.md` checkboxes (or `llman-sdd list` task counts); still read `tasks.md`, proposal/design, and
|
|
24
|
+
- specs-landed gate failing → STOP (land specs or `needs_specs_change: false`). Green but `readyToImplement=false` → normal: tasks pending, keep implementing; **finalize only when `readyToImplement=true`**.
|
|
25
|
+
- Track progress via `tasks.md` checkboxes (or `llman-sdd list` task counts); still read `tasks.md`, proposal/design, and `llmanspec/specs/**` on the bound branch (the single source of truth).
|
|
26
26
|
|
|
27
27
|
### 1) Loop: implement → test
|
|
28
28
|
For each incomplete task:
|
|
29
|
-
1. Implement per task +
|
|
30
|
-
2. Run
|
|
29
|
+
1. Implement per task + specs (minimal diff)
|
|
30
|
+
2. Run the task's stated verification command when the task text names one
|
|
31
31
|
3. On failure, fix and retry (same self-repair budget as `llman-sdd-apply`: cap 8 rounds)
|
|
32
32
|
4. Check off `tasks.md` as `[x]`
|
|
33
33
|
|
|
34
34
|
### 2) Validate
|
|
35
35
|
```bash
|
|
36
|
-
llman-sdd validate <change-id> --strict
|
|
36
|
+
llman-sdd validate <change-id> --strict
|
|
37
37
|
```
|
|
38
|
-
On failure, fix and retry (
|
|
38
|
+
On failure, fix and retry (cap 8 rounds).
|
|
39
39
|
|
|
40
40
|
### 3) Verify (recommended)
|
|
41
|
-
Prefer `llman-sdd-verify` (or equivalent dual-axis self-check). CRITICAL → STOP; do not archive.
|
|
41
|
+
Prefer `llman-sdd-verify` (or an equivalent dual-axis self-check). CRITICAL → STOP; do not archive.
|
|
42
42
|
|
|
43
|
-
### 4) Archive
|
|
43
|
+
### 4) Archive + commit
|
|
44
44
|
```bash
|
|
45
45
|
llman-sdd change finalize <change-id>
|
|
46
46
|
```
|
|
47
|
-
|
|
47
|
+
Dirty tree OK; auto merge (squash default) + rename + **auto commit** `archive(sdd): <change-id>` in one process. `--no-commit` skips the auto commit (manual/CI histories) — then commit with `git add -A && git commit -m "archive(sdd): <change-id>"`. Plain `change archive` stays as a fallback.
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
### 5) Commit (see step 4)
|
|
52
|
-
Finalize already auto-committed unless `--no-commit` was passed.
|
|
53
|
-
|
|
54
|
-
### 6) Optional cleanup
|
|
49
|
+
### 5) Optional cleanup
|
|
55
50
|
```bash
|
|
56
51
|
git branch -D <feature-branch> # after squash the branch is no longer an ancestor of main; -d gets refused
|
|
57
52
|
```
|
|
58
|
-
|
|
53
|
+
Push / PR only when the user explicitly asks.
|
|
59
54
|
|
|
60
55
|
## Hard constraints
|
|
61
56
|
- **Never ask** "should I continue" unless blocked.
|
|
62
57
|
- **Never switch** changes until this one is archived and committed.
|
|
63
|
-
- **
|
|
64
|
-
- **Do not** author `changes/<id>/specs/` or use `change delta`.
|
|
65
|
-
- **No default push/PR**.
|
|
58
|
+
- **Do not** author `changes/<id>/specs/`; **no default push/PR**.
|
|
66
59
|
|
|
67
60
|
## Ethics Governance
|
|
68
61
|
- `ethics.risk_level`: medium
|
|
69
|
-
- `ethics.prohibited_actions`:
|
|
62
|
+
- `ethics.prohibited_actions`: implementing without a bound branch / a green specs-landed gate, archiving without `readyToImplement=true`, switching changes early, writing `changes/<id>/specs/`, committing without validation, default push/PR
|
|
70
63
|
- `ethics.required_evidence`: `readyToImplement=true`, validate --strict pass, all tasks checked, finalize/archive success
|
|
71
|
-
- `ethics.refusal_contract`: after
|
|
72
|
-
- `ethics.escalation_policy`: if changing SDD workflow specs/templates, pause for user
|
|
64
|
+
- `ethics.refusal_contract`: after 8 self-repair rounds still failing, report a blocker; never force-archive
|
|
65
|
+
- `ethics.escalation_policy`: if changing SDD workflow specs/templates, pause for user confirmation before archive
|
|
73
66
|
|
|
74
|
-
|
|
75
|
-
> "Spec" here = a `.feature` file under this project's `llmanspec/specs/`; run `llman-sdd list --specs` or `llman-sdd show <capability>`.
|
|
67
|
+
{{ unit("skills/cli-footer") }}
|
|
@@ -1,125 +1,107 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "llman-sdd-apply"
|
|
3
|
-
description: "Implement
|
|
3
|
+
description: "Implement a proposed change's tasks in a closed loop: code → test → self-heal → all gates green. Enter after propose, once specs are landed."
|
|
4
4
|
metadata:
|
|
5
5
|
version: "{{ llman_version }}"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# LLMAN SDD Apply
|
|
9
9
|
|
|
10
|
-
Implement all tasks in `llmanspec/changes/<id>/tasks.md` **in one closed loop**:
|
|
11
|
-
Implement code → Add tests/acceptance → Run gates → Self-heal on failures → Report results when all pass.
|
|
12
|
-
Unless there is a clear blocker, **DO NOT stop halfway to ask "should I continue?"**
|
|
10
|
+
Implement all tasks in `llmanspec/changes/<id>/tasks.md` **in one closed loop**: implement → add tests/acceptance → run gates → self-heal and re-run on failure → report when all pass. Unless there is a clear blocker, **do not stop halfway to ask "should I continue?"**
|
|
13
11
|
|
|
14
12
|
## Pipeline Position
|
|
15
13
|
|
|
16
14
|
{{ unit("skills/git-native-flow-brief") }}
|
|
17
15
|
|
|
18
|
-
### Skill navigation (not the lifecycle; shows current skill only)
|
|
19
|
-
|
|
20
16
|
```mermaid
|
|
21
17
|
flowchart LR
|
|
22
|
-
propose["llman-sdd-propose
|
|
23
|
-
apply["
|
|
24
|
-
|
|
25
|
-
verify --> archive["llman-sdd-archive<br/>Archive"]
|
|
18
|
+
propose["llman-sdd-propose"] --> apply["★ llman-sdd-apply"]
|
|
19
|
+
apply --> verify["llman-sdd-verify"]
|
|
20
|
+
verify --> archive["llman-sdd-archive"]
|
|
26
21
|
|
|
27
22
|
style apply fill:#fff3cd,stroke:#ffc107,stroke-width:3px
|
|
28
23
|
```
|
|
29
24
|
|
|
30
|
-
> 📍
|
|
25
|
+
> 📍 Entry requires the specs-landed gate green (or `needs_specs_change: false`); `readyToImplement=true` (all gates green) is the completion signal that closes this loop → next `llman-sdd-verify`.
|
|
31
26
|
|
|
32
27
|
## Hard Constraints
|
|
33
28
|
|
|
34
|
-
- **
|
|
35
|
-
- **Scope-locked**:
|
|
36
|
-
- **
|
|
37
|
-
- **No
|
|
38
|
-
- **
|
|
39
|
-
- **Don't ask "should I continue?"**: Execute to loop closure unless you hit an unresolvable blocker.
|
|
40
|
-
- **Close-out**: this skill's closed loop ends by suggesting `llman-sdd-verify`; finalize/archive is handled by `llman-sdd-archive` (do not finalize inside the self-healing loop).
|
|
29
|
+
- **Single-source-of-truth driven**: `proposal.md` / `design.md` / `tasks.md` and `llmanspec/specs/**` on the branch; every MUST/SHALL in specs must be fulfilled.
|
|
30
|
+
- **Scope-locked**: only implement the current change's scope; never fix "unrelated issues" on the side; keep changes minimal.
|
|
31
|
+
- **No guessing**: unclear requirements or specs contradicting reality → STOP and report; don't assume.
|
|
32
|
+
- **No legacy compatibility layers**: if the change requires new behavior, upgrade all call sites directly, unless tasks/proposal explicitly require compatibility.
|
|
33
|
+
- **Close-out**: this loop ends by suggesting `llman-sdd-verify`; finalize/archive belongs to `llman-sdd-archive` (do not finalize inside the self-healing loop).
|
|
41
34
|
|
|
42
35
|
## Commit Policy
|
|
43
36
|
|
|
44
|
-
- **Commits on the change branch are free** (
|
|
45
|
-
- **Default close-out**: after all tasks pass gates and verify is green, `llman-sdd change finalize <id>` auto-commits `archive(sdd): <change-id>` (uncommitted
|
|
46
|
-
- **Blocker interrupt**: when you must STOP on a blocker, make ONE
|
|
37
|
+
- **Commits on the change branch are free** (`change finalize` needs no clean tree): segment by task/milestone, or keep the tree dirty and let finalize make one close commit — both are first-class.
|
|
38
|
+
- **Default close-out**: after all tasks pass gates and verify is green, `llman-sdd change finalize <id>` auto-commits `archive(sdd): <change-id>` (uncommitted diff + frontmatter + rename in one commit). Do not run finalize inside the apply loop. `--no-commit` skips the auto commit (manual/CI histories; pre-commit-hook conflicts).
|
|
39
|
+
- **Blocker interrupt**: when you must STOP on a blocker, make ONE WIP commit (e.g. `wip(sdd): <change-id> <summary>`) to preserve the state, then report.
|
|
47
40
|
|
|
48
41
|
## Steps
|
|
49
42
|
|
|
50
43
|
### 0) Preflight (required)
|
|
51
|
-
- Read and obey
|
|
52
|
-
- `git status --porcelain`:
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
- If a change id is provided, use it directly.
|
|
60
|
-
- Otherwise infer from context; if ambiguous, run `llman-sdd list --json` and let user pick.
|
|
61
|
-
- Always announce: "Using change: <id>" and how to override.
|
|
62
|
-
- Confirm you are on the non-default feature branch bound via `llman-sdd change start <id>` or `change attach <id>` (`--force` only to rebind). Specs/features on the branch are SSOT — do not author under `changes/<id>/specs/`.
|
|
44
|
+
- Read and obey `llmanspec/config.yaml`, `AGENTS.md` (if present).
|
|
45
|
+
- `git status --porcelain`: if the tree is dirty with changes not belonging to this change → `git stash push -u -m "llman-sdd-apply autopilot backup"` first.
|
|
46
|
+
- `llman-sdd validate --all --strict`: if it fails for reasons unrelated to this change → stop and report (inconsistent artifacts prevent source-of-truth-driven implementation).
|
|
47
|
+
- **Check spec valid_scope integrity**: `llman-sdd list --specs --json` lists all specs; for each, verify every `valid_scope` path exists on disk. On missing paths → stop and suggest updating the spec (remove the deleted path).
|
|
48
|
+
|
|
49
|
+
### 1) Select the change id and check prerequisites
|
|
50
|
+
- If provided, use it; otherwise infer from context, and if ambiguous run `llman-sdd list --json` and let the user pick. Always announce "Using change: <id>" and how to override.
|
|
51
|
+
- Confirm you are on the non-default branch bound via `llman-sdd change start <id>` or `change attach <id>` (`--force` only to rebind). Specs on the branch are the single source of truth — do not author under `changes/<id>/specs/`.
|
|
63
52
|
{{ unit("skills/stage-guard") }}
|
|
64
53
|
- Use `llman-sdd context --task "<goal from proposal>" --paths "<scope from specs>"` to get relevant specs.
|
|
65
|
-
-
|
|
54
|
+
- Context unavailable → run `llman-sdd index check` first: stale/missing → `llman-sdd index rebuild` and retry; still unavailable on a fresh index (`LLMAN_SDD_INDEX_CHAT_MODEL` unset) → fall back to `llman-sdd list --specs` + reading `.feature` files directly — do not loop on rebuild.
|
|
66
55
|
|
|
67
|
-
### 2) Read
|
|
68
|
-
|
|
69
|
-
- `llmanspec/
|
|
70
|
-
- `llmanspec/changes/<id>/design.md` (if present)
|
|
71
|
-
- `llmanspec/changes/<id>/tasks.md`
|
|
72
|
-
- Live specs on the feature branch: `llmanspec/specs/**` (`<capability>.feature`) — this is SSOT
|
|
56
|
+
### 2) Read the source-of-truth artifacts
|
|
57
|
+
- `llmanspec/changes/<id>/proposal.md`, `design.md` (if present), `tasks.md`
|
|
58
|
+
- `llmanspec/specs/**` (`<capability>.feature`) on the branch
|
|
73
59
|
|
|
74
|
-
|
|
60
|
+
Distill proposal/design decisions into a list of inviolable hard constraints; convert tasks.md into a minimal executable step sequence (preserving original order).
|
|
75
61
|
|
|
76
62
|
### 3) Show status
|
|
77
|
-
- Progress
|
|
78
|
-
- Next 1–3 unchecked tasks (brief overview)
|
|
63
|
+
- Progress "N/M tasks complete" + a brief look at the next 1–3 unchecked tasks.
|
|
79
64
|
|
|
80
|
-
### 4) Implement tasks one by one (closed
|
|
65
|
+
### 4) Implement tasks one by one (closed loop)
|
|
81
66
|
For each unchecked task:
|
|
82
|
-
1. **Implement**: strictly per task description + specs
|
|
83
|
-
2. **
|
|
84
|
-
3.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
67
|
+
1. **Implement**: strictly per task description + specs, minimal changes.
|
|
68
|
+
2. **Check the box immediately** after completion: `- [ ]` → `- [x]`. **Close-out is not a task**: `change finalize` / `change archive` are pipeline steps and MUST NOT appear in tasks.md — if one is listed (e.g. "close-out — finalize"), remove it (the close-out task gate requires every task checked).
|
|
69
|
+
3. **Edit, then verify — serially**: verification MUST run after edits land on disk; MUST NOT put edits and tests/validation in the same parallel tool-call batch (the check may read stale files and report a false failure or a false pass).
|
|
70
|
+
4. Task unclear, blocker hit, or specs/design contradict reality → STOP and report; don't assume.
|
|
71
|
+
|
|
72
|
+
### 5) Verification and self-healing loop (after each task or batch)
|
|
73
|
+
Run the project gates as appropriate:
|
|
74
|
+
- Test suite: `just test` or `cargo test --all`; format/lint: `just check` or `just lint` + `just fmt`
|
|
75
|
+
- Edit `llmanspec/specs/<capability>.feature` on the branch as needed (flat or directory main file; canonical native layout: `@req:<id>` on the `规则:` block header, nested `场景:` as executable examples); run `llman-sdd validate --specs` after spec edits; commit on the branch freely.
|
|
76
|
+
- SDD validation: `llman-sdd validate <id> --strict`
|
|
77
|
+
|
|
78
|
+
**Gate evidence**:
|
|
79
|
+
- Close-out runs the configured `bdd.run_command`, so do not run that command again just before close-out; the skip line printed by `--no-check` is not a pass.
|
|
80
|
+
- Gate verdicts MUST come from the real harness: MUST NOT obtain a "pass" via `--no-check`; on harness failure, find the root cause first (leaked env vars, nested-invocation guards, wrong cwd …) — MUST NOT label it an "inherent/self-referential property" and bypass it.
|
|
81
|
+
- Before/after criteria (counts, baselines) MUST be measured on the change branch (against the freshly computed merge-base); a value measured on the default branch is usually trivially the baseline and proves nothing.
|
|
82
|
+
- Refactors and bulk replacements: MUST compare the test count before and after; all-green gates with fewer tests is a failure.
|
|
83
|
+
|
|
84
|
+
**On failure → self-heal (don't ask "should I continue?"):**
|
|
85
|
+
1. Parse the failure cause (test / lint / format / validation).
|
|
86
|
+
2. Decide if it's a hard-to-locate bug (cause unclear / intermittent flake / regression not obvious at a glance):
|
|
87
|
+
- **Not hard-to-locate** (clear lint/format/compile/validation error): apply a minimal fix (don't expand scope); re-run the minimum failure-repro command first, then all gates.
|
|
88
|
+
- **Hard-to-locate → escalate to the diagnose sub-flow**:
|
|
100
89
|
1. **First build a command that reproduces the failure** (fast, deterministic, agent-runnable, and goes red on *this* bug) — one that drives the real bug path and asserts the user's exact symptom. **MUST NOT start hypothesizing before such a command exists** (staring at code and guessing is the failure this prevents).
|
|
101
90
|
2. Run it, confirm red → minimize the repro (cut inputs/calls/config/data one at a time, keep only what's load-bearing).
|
|
102
91
|
3. Generate **3–5 ranked hypotheses**, each falsifiable ("if X is the cause, changing Y makes the bug disappear").
|
|
103
92
|
4. Verify one variable at a time; fix once the root cause is found.
|
|
104
|
-
5. If there's no correct seam for a regression test, note the architectural gap (hand off to `llman-sdd-arch-review`; when
|
|
105
|
-
3. Re-run the
|
|
106
|
-
4. Log
|
|
107
|
-
|
|
108
|
-
**Self-healing cap: 8 rounds**; exceeding this is a blocker: stop and output a blocker report (last failing command + output summary + what you tried).
|
|
93
|
+
5. If there's no correct seam for a regression test, note the architectural gap (hand off to `llman-sdd-arch-review`; when not enabled, write the gap into this change's `proposal.md` Further Notes section or `design.md`, and MUST NOT break the loop over it).
|
|
94
|
+
3. Re-run the minimum failure-repro command first, then all gates.
|
|
95
|
+
4. Log one self-healing round: `Round N: failure → fix → re-run → pass/fail`.
|
|
109
96
|
|
|
110
|
-
**
|
|
97
|
+
**Self-healing cap: 8 rounds**; exceeding it is a blocker: stop and output a blocker report (last failing command + output summary + what you tried).
|
|
111
98
|
|
|
112
|
-
-
|
|
113
|
-
- Non-zero exit = CRITICAL findings: STOP, fix, re-run review; MUST NOT enter the next batch or emit the completion report with CRITICAL findings open.
|
|
99
|
+
**Human review gate (after each task batch passes the gates)**: before starting the next batch or emitting the completion report, run `llman-sdd review`: exit code zero → continue; non-zero = CRITICAL findings → STOP, fix, re-run review; MUST NOT enter the next batch or emit the completion report with CRITICAL findings open.
|
|
114
100
|
|
|
115
101
|
### 6) Completion report
|
|
116
|
-
After all tasks complete + all gates green, output a structured report (see Output Contract
|
|
117
|
-
Then suggest running `llman-sdd-verify` for the verification phase.
|
|
118
|
-
|
|
119
|
-
> 💡 Implementation done → next: `llman-sdd-verify` (verify)
|
|
102
|
+
After all tasks complete + all gates green, output a structured report (see Output Contract), then suggest `llman-sdd-verify`.
|
|
120
103
|
|
|
121
|
-
|
|
122
|
-
> "Spec" here = a `.feature` file under this project's `llmanspec/specs/`; run `llman-sdd list --specs` or `llman-sdd show <capability>`.
|
|
104
|
+
{{ unit("skills/cli-footer") }}
|
|
123
105
|
|
|
124
106
|
{{ unit("skills/validation-hints") }}
|
|
125
107
|
|
|
@@ -1,27 +1,21 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "llman-sdd-arch-review"
|
|
3
|
-
description: "
|
|
3
|
+
description: "Architecture review: scan for shallow modules (interface ≈ implementation) and surface deepening candidates to improve testability and AI-navigability."
|
|
4
4
|
metadata:
|
|
5
5
|
version: "{{ llman_version }}"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# LLMAN SDD Architecture Review
|
|
9
9
|
|
|
10
|
-
Scan the codebase for architectural friction and surface **deepening opportunities** —
|
|
11
|
-
|
|
12
|
-
## Pipeline position
|
|
13
|
-
|
|
14
|
-
Auxiliary tool, not part of the main pipeline (explore→propose→apply→verify→archive). Usable at any stage; commonly triggered during explore to surface improvement candidates.
|
|
15
|
-
|
|
16
|
-
> 📍 Standalone optional skill; does not replace any pipeline stage.
|
|
10
|
+
Scan the codebase for architectural friction and surface **deepening opportunities** — turning shallow modules (interface ≈ implementation) into deep ones (lots of behavior behind a small interface). The aim is testability and AI-navigability. Auxiliary tool, not part of the main pipeline; usable at any stage, commonly triggered during explore.
|
|
17
11
|
|
|
18
12
|
## Design vocabulary
|
|
19
13
|
|
|
20
|
-
|
|
14
|
+
Words about module shape; MUST NOT substitute "component" / "service" / "API" / "boundary" (broader, less precise):
|
|
21
15
|
|
|
22
16
|
- **Module** — anything with an interface and an implementation (function/class/package/cross-layer slice).
|
|
23
17
|
- **Interface** — everything a caller must know to use it correctly: type signature, plus invariants, ordering constraints, error modes, performance characteristics.
|
|
24
|
-
- **Depth** — the amount of
|
|
18
|
+
- **Depth** — the amount of behavior behind the interface. **Deep** = lots of behavior behind a small interface; **shallow** = interface nearly as complex as the implementation (the caller saves nothing). This skill turns shallow into deep.
|
|
25
19
|
- **Seam** — a place where you can swap the implementation without editing call sites (where the interface lives). In llman, seam = the public boundary driven by `*.feature` GWT steps.
|
|
26
20
|
- **Leverage** — what callers get from depth: more capability per unit of interface learned.
|
|
27
21
|
- **Locality** — what maintainers get from depth: changes/bugs/knowledge/verification concentrate in one place.
|
|
@@ -31,10 +25,10 @@ A set of words about module shape, used to articulate "where it's worth changing
|
|
|
31
25
|
### 1. Explore (scope first, YAGNI)
|
|
32
26
|
- If the user named a direction (module/subsystem/pain point), accept it; skip inference.
|
|
33
27
|
- Otherwise walk `git log --oneline` for hot spots (files/areas that keep coming up).
|
|
34
|
-
- Prefer reading
|
|
28
|
+
- Prefer reading `<capability>.feature` (the single source of truth) and `design.md` (existing ADRs); MUST NOT create a `CONTEXT.md`.
|
|
35
29
|
- Use the Agent tool (`subagent_type=Explore`) to walk the codebase, noting friction:
|
|
36
30
|
- Does understanding one concept require bouncing between many small modules?
|
|
37
|
-
- Where are modules **shallow** (interface
|
|
31
|
+
- Where are modules **shallow** (interface ≈ implementation complexity, callers save nothing)?
|
|
38
32
|
- Where are pure functions extracted only for testability, but real bugs hide in how they're called (no locality)?
|
|
39
33
|
- Which parts are untested or hard to test through their current interface?
|
|
40
34
|
|
|
@@ -46,20 +40,19 @@ For each candidate:
|
|
|
46
40
|
- **Benefits** — locality and leverage improvements; how tests get better.
|
|
47
41
|
- **Recommendation strength** — `Strong` / `Worth exploring` / `Speculative`.
|
|
48
42
|
|
|
49
|
-
**Deletion test**: for any suspected-shallow module, imagine deleting it — does complexity vanish (it's just a pass-through
|
|
43
|
+
**Deletion test**: for any suspected-shallow module, imagine deleting it — does complexity vanish (it's just a pass-through) or reappear across N call sites (it's actually earning its keep)? "Reappears" is the signal you want.
|
|
50
44
|
|
|
51
45
|
**ADR conflicts**: if a candidate contradicts an existing `design.md` decision, surface it only when the friction is real enough to warrant reopening, and mark it in the candidate.
|
|
52
46
|
|
|
53
|
-
### 3.
|
|
54
|
-
Run `llman-sdd-explore`'s **
|
|
47
|
+
### 3. Deep-dive Q&A (after the user picks a candidate)
|
|
48
|
+
Run `llman-sdd-explore`'s **deep-dive Q&A branch** (trigger "deep-dig") to walk the decision tree — constraints, dependencies, the deepened module's shape, what sits behind the seam, which tests survive.
|
|
55
49
|
|
|
56
|
-
-
|
|
50
|
+
- The deepened module uses a concept not in the `.feature`? → update the `.feature` **only** if the change is branch-bound and you are on the bound branch; otherwise STOP and route to `llman-sdd-propose` / `change start` — **never** edit specs on the default branch.
|
|
57
51
|
- User rejects the candidate with a load-bearing reason? → offer an ADR only when "hard to reverse + surprising without context + real trade-off" all hold; record in `design.md`.
|
|
58
52
|
|
|
59
53
|
## Output
|
|
60
|
-
Candidate list (text; optional HTML report written to OS temp dir, not the repo) + the
|
|
54
|
+
Candidate list (text; optional HTML report written to the OS temp dir, not the repo) + the deep-dive decision record after the user picks one (write back to the proposal; contract edits only after landing on the bound branch into the `.feature`).
|
|
61
55
|
|
|
62
|
-
|
|
63
|
-
> "Spec" here = a `.feature` file under this project's `llmanspec/specs/`; run `llman-sdd list --specs` or `llman-sdd show <capability>`.
|
|
56
|
+
{{ unit("skills/cli-footer") }}
|
|
64
57
|
|
|
65
58
|
{{ unit("skills/structured-protocol") }}
|