pi-aia-asf 0.2.1 → 0.3.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/CHANGELOG.md +20 -0
- package/index.ts +144 -8
- package/package.json +1 -1
- package/skills/aia-asf/SKILL.md +38 -8
- package/skills/aia-asf/references/01-intake.md +18 -0
- package/skills/aia-asf/references/04-adversarial.md +12 -0
- package/skills/aia-asf/references/05-plan.md +12 -1
- package/skills/aia-asf/references/06-implementation.md +29 -0
- package/skills/aia-asf/references/06b-testing-qa.md +50 -0
- package/skills/aia-asf/references/06c-code-quality.md +170 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] - 2026-08-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- (describe changes for 0.3.0)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [0.2.2] - 2026-08-14
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Mandatory modularity & maintainability standard** (`references/06c-code-quality.md`) — 8 rules distilled from Bruno's Tunnel-project philosophy (small well-readable modules plugged in where needed; one authoritative implementation for shared functionality with a single escalation path; no hardcoding — config-driven; fully testable outside the host then integrated verbatim, same modules in tests and production; refactor what is too complex to understand; layered with clear one-way boundaries and an architecture writeup; full I/O debug logging with replay of stored data; nothing may break existing functionality) and reinforced by external research (SSOT, testability as design property, ports-and-adapters seams).
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **Phase 6** now requires reading `06c-code-quality.md` before structuring code; **Phase 7** verification includes the modularity DoD (no duplicated shared logic, no hardcoded config values, standalone-tested modules, architecture writeup, existing functionality green).
|
|
26
|
+
- **Phase 4** adversarial analysis gains a code-quality lens (duplication, single escalation path, standalone testability, hardcoded values); **Phase 5** PLAN.md architecture section now requires the SSOT map, layering rules, and standalone-testability notes.
|
|
27
|
+
- Anti-patterns extended: god-objects, copy-pasted shared logic, test copies of modules, hardcoding, per-caller escalation logic, untestable-standalone modules, behavior-breaking refactors.
|
|
28
|
+
|
|
29
|
+
|
|
10
30
|
## [0.2.1] - 2026-08-14
|
|
11
31
|
|
|
12
32
|
### Added
|
package/index.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
15
15
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
16
|
-
import { existsSync } from "node:fs";
|
|
16
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
17
17
|
import { join } from "node:path";
|
|
18
18
|
import { homedir } from "node:os";
|
|
19
19
|
|
|
@@ -51,9 +51,29 @@ const QA_CHECKLIST: Array<{ key: string; label: string }> = [
|
|
|
51
51
|
{ key: "observable", label: "Observable end state verified as a user would experience it" },
|
|
52
52
|
{ key: "browser", label: "Web surfaces exercised through a real browser (n/a if none)" },
|
|
53
53
|
{ key: "specs", label: "Every MUST spec 'met' with concrete evidence" },
|
|
54
|
+
{ key: "trace", label: "Spec-to-code traceability: every met spec has outcome → codePath → test" },
|
|
55
|
+
{ key: "surface", label: "Every delivered feature is consumed by a surface (UI or API) — nothing dead" },
|
|
56
|
+
{ key: "e2e", label: "Feature specs have an end-to-end behavioral test through the real entry point" },
|
|
54
57
|
{ key: "honest", label: "Skipped/inconclusive checks reported explicitly" },
|
|
55
58
|
];
|
|
56
59
|
|
|
60
|
+
/** Spec-to-code traceability (M1) — mirrors pi-vigilant's Trace. */
|
|
61
|
+
interface Trace {
|
|
62
|
+
outcome: string;
|
|
63
|
+
codePath: string;
|
|
64
|
+
testFile?: string;
|
|
65
|
+
assertion?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface SpecItem {
|
|
69
|
+
id: string;
|
|
70
|
+
requirement: string;
|
|
71
|
+
area: string;
|
|
72
|
+
priority: string;
|
|
73
|
+
status: string;
|
|
74
|
+
trace?: Trace;
|
|
75
|
+
}
|
|
76
|
+
|
|
57
77
|
interface ProjectStateFile {
|
|
58
78
|
current: AsfState | null;
|
|
59
79
|
history: Array<{ workType: string; phase: AsfPhase; startedAt: string; endedAt: string }>;
|
|
@@ -94,6 +114,61 @@ async function saveState(project: string, data: ProjectStateFile): Promise<void>
|
|
|
94
114
|
await writeFile(file, JSON.stringify(data, null, 2), "utf-8");
|
|
95
115
|
}
|
|
96
116
|
|
|
117
|
+
// ─── Spec-memory read (shared with pi-vigilant) ────────────────────────────
|
|
118
|
+
|
|
119
|
+
const SPEC_DIR = join(SKILLS_DIR, "spec-memory", "projects");
|
|
120
|
+
|
|
121
|
+
function specTaskFileFor(project: string): string {
|
|
122
|
+
return join(SPEC_DIR, project, "current-task.json");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function loadSpecs(project: string): Promise<SpecItem[] | null> {
|
|
126
|
+
const file = specTaskFileFor(project);
|
|
127
|
+
if (!existsSync(file)) return null;
|
|
128
|
+
try {
|
|
129
|
+
const task = JSON.parse(await readFile(file, "utf-8")) as {
|
|
130
|
+
areas?: Record<string, SpecItem[]>;
|
|
131
|
+
};
|
|
132
|
+
return Object.values(task.areas || {}).flat();
|
|
133
|
+
} catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Mechanical M1 validation of a met spec's trace. */
|
|
139
|
+
function validateTrace(spec: SpecItem): { ok: boolean; reason: string } {
|
|
140
|
+
const t = spec.trace;
|
|
141
|
+
if (!t || !t.outcome || !t.codePath) {
|
|
142
|
+
return {
|
|
143
|
+
ok: false,
|
|
144
|
+
reason: "met but trace incomplete — outcome + codePath required (add via update_spec_status trace)",
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (t.testFile) {
|
|
148
|
+
const abs = join(process.cwd(), t.testFile);
|
|
149
|
+
if (!existsSync(abs)) {
|
|
150
|
+
return { ok: false, reason: `testFile not found: ${t.testFile}` };
|
|
151
|
+
}
|
|
152
|
+
if (t.assertion) {
|
|
153
|
+
let content = "";
|
|
154
|
+
try {
|
|
155
|
+
content = readFileSync(abs, "utf-8");
|
|
156
|
+
} catch {
|
|
157
|
+
/* unreadable → treat as missing */
|
|
158
|
+
}
|
|
159
|
+
if (!content.includes(t.assertion)) {
|
|
160
|
+
return { ok: false, reason: `assertion not found in ${t.testFile}: "${t.assertion}"` };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
ok: true,
|
|
166
|
+
reason: !t.testFile
|
|
167
|
+
? "trace complete (no testFile — ensure this is verifiable by inspection, or add an E2E test per 06b Rule 14)"
|
|
168
|
+
: "trace complete",
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
97
172
|
// ─── Dependency check ──────────────────────────────────────────────────────
|
|
98
173
|
|
|
99
174
|
interface DependencyCheck {
|
|
@@ -225,18 +300,79 @@ export default function register(pi: ExtensionAPI): void {
|
|
|
225
300
|
}
|
|
226
301
|
case "verify": {
|
|
227
302
|
// Gate 7: force an explicit, itemised QA pass before delivery.
|
|
303
|
+
// Large work: mechanical spec-to-code traceability gate (M1).
|
|
228
304
|
const project = projectName();
|
|
229
305
|
const state = await loadState(project);
|
|
230
306
|
if (!state.current) return "No active ASF session — nothing to verify.";
|
|
231
307
|
await setPhase(ctx, "verification");
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
`
|
|
235
|
-
`
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
308
|
+
const scale = state.current.scale;
|
|
309
|
+
const lines: string[] = [
|
|
310
|
+
`ASF verification gate (${project}) — Definition of Done.`,
|
|
311
|
+
`Scale: ${scale || "unset"}${
|
|
312
|
+
scale === "large"
|
|
313
|
+
? " — mechanical traceability gate ACTIVE"
|
|
314
|
+
: scale === "small"
|
|
315
|
+
? " (automatic — no mechanical gate)"
|
|
316
|
+
: ""
|
|
317
|
+
}`,
|
|
318
|
+
];
|
|
319
|
+
|
|
320
|
+
if (scale === "large") {
|
|
321
|
+
const specs = await loadSpecs(project);
|
|
322
|
+
lines.push("\nSPEC-TO-CODE TRACEABILITY (M1 — large work):");
|
|
323
|
+
if (!specs || specs.length === 0) {
|
|
324
|
+
lines.push(
|
|
325
|
+
" (no spec-memory task found — capture specs with capture_spec, incl. items from external planning docs)",
|
|
326
|
+
);
|
|
327
|
+
} else {
|
|
328
|
+
const metSpecs = specs.filter((s) => s.status === "met");
|
|
329
|
+
if (metSpecs.length === 0) {
|
|
330
|
+
lines.push(
|
|
331
|
+
" (no specs marked met yet — traceability applies when closing specs)",
|
|
332
|
+
);
|
|
333
|
+
} else {
|
|
334
|
+
let allOk = true;
|
|
335
|
+
for (const spec of metSpecs) {
|
|
336
|
+
const check = validateTrace(spec);
|
|
337
|
+
if (!check.ok) allOk = false;
|
|
338
|
+
lines.push(` [${check.ok ? "PASS" : "FAIL"}] ${spec.id}: ${check.reason}`);
|
|
339
|
+
if (check.ok && spec.trace) {
|
|
340
|
+
lines.push(` outcome: ${spec.trace.outcome}`);
|
|
341
|
+
lines.push(` code path: ${spec.trace.codePath}`);
|
|
342
|
+
if (spec.trace.testFile) {
|
|
343
|
+
lines.push(
|
|
344
|
+
` test: ${spec.trace.testFile}${spec.trace.assertion ? ` (asserts "${spec.trace.assertion}")` : ""}`,
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
lines.push(
|
|
350
|
+
allOk
|
|
351
|
+
? "\n ✓ All met specs have complete traces."
|
|
352
|
+
: "\n ✗ GATE NOT PASSED — resolve FAIL rows before delivery (attach trace via update_spec_status).",
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
lines.push(
|
|
359
|
+
"\nDefinition of Done checklist (references/06b-testing-qa.md Rule 10):",
|
|
360
|
+
);
|
|
361
|
+
lines.push(
|
|
362
|
+
"Read references/06b-testing-qa.md. Confirm EACH item with concrete evidence",
|
|
363
|
+
);
|
|
364
|
+
lines.push(
|
|
365
|
+
"(command output, file list, screenshot). Do not tick anything you did not run.",
|
|
366
|
+
);
|
|
367
|
+
lines.push("");
|
|
368
|
+
QA_CHECKLIST.forEach((c, i) => lines.push(` ${i + 1}. [ ] ${c.label}`));
|
|
369
|
+
lines.push(
|
|
370
|
+
"\nThen run get_task_specs and close every spec with update_spec_status.",
|
|
371
|
+
);
|
|
372
|
+
lines.push(
|
|
373
|
+
"Unverifiable → 'partial' + ask the user. Never self-certify.",
|
|
239
374
|
);
|
|
375
|
+
return lines.join("\n");
|
|
240
376
|
}
|
|
241
377
|
case "abort":
|
|
242
378
|
return await setPhase(ctx, "none");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-aia-asf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Ai Applied Agentic Software Factory — codifies the full software development flow: intake, research, spec capture, adversarial analysis, planning with approval gates, test-first implementation, and release. Requires pi-vigilant, pi-smart-web-search, pi-smart-fetch, and pi-aia-browser.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
package/skills/aia-asf/SKILL.md
CHANGED
|
@@ -70,6 +70,7 @@ Minimum intake checklist (ask anything not yet known, one question at a time or
|
|
|
70
70
|
- **Constraints** — musts, must-nots, boundaries, budget, timeline
|
|
71
71
|
- **Preferences** — stack, language, platform, style (only if the user has them)
|
|
72
72
|
- **Definition of done** — tests? deploy? release? docs?
|
|
73
|
+
- **External planning docs** — are there IMPROVEMENT-PLAN.md / PLAN.md / requirements docs / delivery logs / ticket lists? Locate them (repo root, `docs/`, referenced by the user); they are inputs to spec capture, not ground truth.
|
|
73
74
|
|
|
74
75
|
For each answer, **capture hard requirements immediately with `capture_spec`** (requirement, area, priority). Specs are the shared contract with pi-vigilant — it will re-verify them at the end.
|
|
75
76
|
|
|
@@ -105,9 +106,10 @@ Turn the intake answers + research into the authoritative spec set.
|
|
|
105
106
|
|
|
106
107
|
1. Run `get_task_specs` to see what's already captured.
|
|
107
108
|
2. Fill gaps: for every requirement the user stated or approved, ensure a spec exists (`capture_spec`).
|
|
108
|
-
3.
|
|
109
|
-
4.
|
|
110
|
-
5.
|
|
109
|
+
3. **Ingest external planning docs (M6):** every actionable item in an external planning doc (IMPROVEMENT-PLAN.md, PLAN.md, requirements docs, delivery logs, ticket lists) becomes a captured spec with `sourceQuote` pointing at the doc + item id. The doc's own ✅/delivered markers are **claims, not evidence** — each item gets traced and verified like any other spec.
|
|
110
|
+
4. Decompose broad specs with `parentId` (e.g. "must be secure" → auth + encryption sub-specs).
|
|
111
|
+
5. Default priority is `must`; use `should` only when the user says "nice to have".
|
|
112
|
+
6. Areas: functionality, ui-ux, performance, security, error-handling, testing, documentation, compatibility, constraints, format, data, deployment, other.
|
|
111
113
|
|
|
112
114
|
**Gate 3** (large only): show the full spec tree (`get_task_specs`) and get user sign-off: "specs correct — proceed to adversarial analysis?" Small work: capture specs silently, no sign-off needed.
|
|
113
115
|
|
|
@@ -172,6 +174,14 @@ Keep the plan **implementation-ready**: any competent engineer (or agent) can ex
|
|
|
172
174
|
> **read the actual error before editing**; **verify clean-room** (caches serve stale
|
|
173
175
|
> builds); **add a regression test for every bug fixed**.
|
|
174
176
|
|
|
177
|
+
> **Read `references/06c-code-quality.md` before structuring code.** It is the mandatory
|
|
178
|
+
> modularity & maintainability standard: small well-readable modules plugged in where
|
|
179
|
+
> needed; **one implementation for shared functionality** (single escalation path,
|
|
180
|
+
> SSOT); no hardcoding (config-driven); **testable outside the host then integrated
|
|
181
|
+
> verbatim** (same modules in tests and production); refactor what is too complex to
|
|
182
|
+
> understand; layered with clear boundaries and an architecture writeup; full I/O debug
|
|
183
|
+
> logging with replay; nothing breaks existing functionality.
|
|
184
|
+
|
|
175
185
|
Execute the task list milestone by milestone. Discipline rules:
|
|
176
186
|
|
|
177
187
|
1. **Test-first**: write/update tests before or with implementation; run them; only commit green.
|
|
@@ -180,9 +190,11 @@ Execute the task list milestone by milestone. Discipline rules:
|
|
|
180
190
|
2. **Codebase isolation**: work strictly inside the project's own codebase. Do NOT edit files in other repos, global config, or unrelated directories — **unless the user explicitly instructs otherwise**. If a change would touch another codebase, stop and ask.
|
|
181
191
|
3. **No scope creep**: if something new is discovered that changes specs, capture it, ask the user, and update the plan before implementing.
|
|
182
192
|
4. **Descriptive commits**: `git commit -m "type: specific description of what and why"` (e.g. `fix: verify specs before rotation`). No vague messages, no placeholders.
|
|
183
|
-
5. **Browser testing — MANDATORY for any web interface**: if the deliverable has a UI/website/web app, test it through `pi-aia-browser` (`browser_init`, `browser_navigate`, `browser_click`, `browser_type`, `browser_screenshot`, `browser_dom`, …) to replicate the user's real experience — not just curl/API checks. Verify: loads, key user journeys, responsive behavior, console errors.
|
|
193
|
+
5. **Browser testing — MANDATORY for any web interface**: if the deliverable has a UI/website/web app, test it through `pi-aia-browser` (`browser_init`, `browser_navigate`, `browser_click`, `browser_type`, `browser_screenshot`, `browser_dom`, …) to replicate the user's real experience — not just curl/API checks. Verify: loads, key user journeys, responsive behavior, console errors. **Silent async paths included**: ingest through the real flow and wait for the enrichment to land (06b Rule 9).
|
|
184
194
|
6. **Let pi-vigilant do its job**: it will auto-continue after premature stops and verify specs at settle. When it asks for `update_spec_status` with evidence, do it.
|
|
185
195
|
7. **CHANGELOG discipline**: every user-visible change gets a CHANGELOG entry describing exactly what changed (no placeholder text).
|
|
196
|
+
8. **Challenge approved designs (M4)**: if a spec's literal reading creates product tension (e.g. feedback clusters under "Plan" when "Plan = plans"), stop and resolve it with the user before implementing — never implement blindly and call it delivered.
|
|
197
|
+
9. **Trace before claiming delivered (M5)**: the delivery log is a claim; the code is the evidence. Before marking anything ✅, trace the actual code path, confirm the output is consumed by a surface, and confirm the operator-facing outcome test passes.
|
|
186
198
|
|
|
187
199
|
---
|
|
188
200
|
|
|
@@ -190,6 +202,10 @@ Execute the task list milestone by milestone. Discipline rules:
|
|
|
190
202
|
|
|
191
203
|
Run the **Definition of Done checklist** in `references/06b-testing-qa.md` (Rule 10). Every box must hold.
|
|
192
204
|
|
|
205
|
+
Also check the **modularity DoD** from `references/06c-code-quality.md` (Phase 7 section): no duplicated shared logic, no hardcoded config values, every module tested standalone with the same calls it gets in the host, architecture writeup exists, existing functionality still green.
|
|
206
|
+
|
|
207
|
+
**Large work:** run `/asf verify` — it mechanically validates the **spec-to-code traceability matrix** (M1): every `met` spec must carry `trace` (outcome → codePath → testFile + assertion), testFile must exist, assertion must appear in it. FAIL rows block delivery. **Verify ingested specs from external planning docs too** — the doc's ✅ markers are claims, not evidence.
|
|
208
|
+
|
|
193
209
|
1. Run the full test suite (all of it, not a subset); fix failures; re-run until green.
|
|
194
210
|
2. **Verify the artifact a user would actually get**: inspect the packaged file list
|
|
195
211
|
(`npm pack` → `tar tzf`), install/load it clean-room in a fresh dir with caches
|
|
@@ -223,13 +239,27 @@ Run the **Definition of Done checklist** in `references/06b-testing-qa.md` (Rule
|
|
|
223
239
|
- ❌ Asking a barrage of intake questions for small work — small runs automatically
|
|
224
240
|
- ❌ Writing PLAN.md / demanding approval for small work — that's the large-work gate only
|
|
225
241
|
- ❌ Waiting for sign-off when the work is small; when in doubt, default to small and start
|
|
242
|
+
- ❌ One giant file / god-object that "does everything" — small well-readable modules, plugged in
|
|
243
|
+
- ❌ Copy-pasting shared logic instead of importing the one authoritative module (SSOT)
|
|
244
|
+
- ❌ A "test copy" of a module that differs from the production version — same modules everywhere
|
|
245
|
+
- ❌ Hardcoding values (model names, thresholds, URLs) that config should drive
|
|
246
|
+
- ❌ Escalation/fallback logic re-implemented per caller instead of one shared escalation path
|
|
247
|
+
- ❌ Shipping a module that cannot run/test standalone outside the host
|
|
248
|
+
- ❌ Refactoring without the architecture writeup (see `references/06c-code-quality.md`)
|
|
249
|
+
- ❌ Breaking existing functionality during a refactor — refactoring preserves behavior
|
|
250
|
+
- ❌ Marking a spec delivered from the delivery log instead of tracing the code — the log is a claim
|
|
251
|
+
- ❌ Shipping machinery no surface consumes — delivered = visible in the product (UI or API)
|
|
252
|
+
- ❌ Trusting unit tests as proof of wiring — assert the operator-facing outcome end-to-end
|
|
253
|
+
- ❌ Trusting an external plan's ✅ (IMPROVEMENT-PLAN / delivery log) — ingest its items as specs and verify them
|
|
254
|
+
- ❌ Implementing a spec literally when it creates product tension — challenge it and resolve with the user
|
|
226
255
|
|
|
227
256
|
## References
|
|
228
257
|
|
|
229
|
-
- `references/01-intake.md` — question bank and probing techniques
|
|
258
|
+
- `references/01-intake.md` — question bank and probing techniques (incl. external planning docs, M6)
|
|
230
259
|
- `references/02-research.md` — research playbook with search templates
|
|
231
260
|
- `references/04-adversarial.md` — adversarial checklist per area
|
|
232
|
-
- `references/05-plan.md` — PLAN.md template with examples
|
|
233
|
-
- `references/06-implementation.md` — coding discipline details
|
|
234
|
-
- `references/06b-testing-qa.md` — **mandatory testing & QA standard** (
|
|
261
|
+
- `references/05-plan.md` — PLAN.md template with examples (incl. spec-to-code traceability matrix)
|
|
262
|
+
- `references/06-implementation.md` — coding discipline details (incl. M4 challenge designs, M5 trace before claiming)
|
|
263
|
+
- `references/06b-testing-qa.md` — **mandatory testing & QA standard** (14 rules + definition of done)
|
|
264
|
+
- `references/06c-code-quality.md` — **mandatory modularity & maintainability standard** (8 rules, SSOT, testable-standalone, single escalation path)
|
|
235
265
|
- `references/07-release.md` — release workflow (versioning, CHANGELOG, tags, npm, CI/CD)
|
|
@@ -13,6 +13,24 @@ Keep asking until the user confirms. One question at a time is fine; short batch
|
|
|
13
13
|
- "What exists today?" (blank slate / existing repo / replaces something)
|
|
14
14
|
- "How will we know it's done and correct?" (concrete success criteria)
|
|
15
15
|
- "Any hard constraints?" (stack, platform, budget, timeline, must-nots)
|
|
16
|
+
- "**Are there external planning docs?**" (IMPROVEMENT-PLAN.md, PLAN.md, requirements docs, delivery logs, ticket lists — in the repo, `docs/`, or referenced by the user)
|
|
17
|
+
|
|
18
|
+
## External planning docs (M6) — locate and ingest
|
|
19
|
+
|
|
20
|
+
When a task references or contains external planning documents, **realize they
|
|
21
|
+
exist**: look in the repo root, `docs/`, and anything the user points at. Then
|
|
22
|
+
**treat them like own captured specs**:
|
|
23
|
+
|
|
24
|
+
1. Read the doc(s) and list every actionable item (e.g. `IMP-002/006 — service agent titles`).
|
|
25
|
+
2. `capture_spec` each item with `sourceQuote` pointing at the doc + item id.
|
|
26
|
+
3. **The doc's own ✅ / "delivered" markers are claims, not evidence** — each
|
|
27
|
+
item gets traced (outcome → codePath → test) and verified like any other spec.
|
|
28
|
+
4. If the doc is large, ingest by section and decompose with `parentId`.
|
|
29
|
+
|
|
30
|
+
> **Why this matters (betamaxx audit):** the IMPROVEMENT-PLAN's delivery log said
|
|
31
|
+
> "IMP-006+P-A delivered ✅" and that was trusted as ground truth — the code was
|
|
32
|
+
> never traced. `intelligenceBudget.analyze()` shipped as dead code. Ingesting
|
|
33
|
+
> the plan's items as specs forces each one to be traced and verified.
|
|
16
34
|
|
|
17
35
|
## Probing techniques
|
|
18
36
|
|
|
@@ -8,6 +8,18 @@ Challenge every spec and design decision like a hostile reviewer. For each item,
|
|
|
8
8
|
|
|
9
9
|
## Per-spec questions
|
|
10
10
|
|
|
11
|
+
> **Code-quality lens (always applied):** for every module/design under review,
|
|
12
|
+
> also ask the `references/06c-code-quality.md` questions — is shared logic
|
|
13
|
+
> duplicated anywhere? Is the single escalation path identifiable? Is the module
|
|
14
|
+
> testable standalone with the same calls? Any hardcoded values that belong in
|
|
15
|
+
> config?
|
|
16
|
+
|
|
17
|
+
> **Traceability lens (always applied):** for every spec, ask — *what is the
|
|
18
|
+
> operator-facing outcome, and what code path delivers it?* Trace the path in
|
|
19
|
+
> your head: where could **dead wiring** hide (a function defined but never
|
|
20
|
+
> called, an async enrichment that never lands, a surface that never renders)?
|
|
21
|
+
> If the outcome has no concrete path, the spec is not yet real.
|
|
22
|
+
|
|
11
23
|
- **Edge cases**: empty input, zero data, max load, missing fields, concurrent access, duplicate input, unicode, huge payloads
|
|
12
24
|
- **Failure modes**: what breaks first? Is failure loud or silent? Can we recover automatically?
|
|
13
25
|
- **Security**: authentication, authorization, injection (SQL/XSS), data exposure, secrets, abuse/rate-limiting, supply chain
|
|
@@ -21,10 +21,21 @@ Existing system, repo layout, relevant prior work. Links to research sources.
|
|
|
21
21
|
Decided approach with rationale. Cite the research (package names, URLs).
|
|
22
22
|
|
|
23
23
|
## Architecture / Design
|
|
24
|
-
-
|
|
24
|
+
- Modules and their responsibilities (small, single-purpose — see `references/06c-code-quality.md`)
|
|
25
25
|
- Data model / schema (if any)
|
|
26
26
|
- Key flows (request lifecycle, event flow)
|
|
27
27
|
- Interfaces / contracts between components
|
|
28
|
+
- **Where shared truth lives** (single source of truth map: which module owns each shared capability)
|
|
29
|
+
- **Layering**: one-way dependency rules between layers; what each layer may/may not import
|
|
30
|
+
- **Standalone testability**: how each module is exercised outside the host with the same calls
|
|
31
|
+
|
|
32
|
+
## Spec-to-code traceability matrix
|
|
33
|
+
For every spec, the operator-facing outcome, the code path that delivers it, and the test that asserts it. This is what `/asf verify` checks mechanically at delivery (M1).
|
|
34
|
+
|
|
35
|
+
| Spec | Operator-facing outcome | Code path | Test (asserts outcome) |
|
|
36
|
+
|------|------------------------|-----------|------------------------|
|
|
37
|
+
| spc-… | e.g. "ingested report has an agent_title on the card" | ingest → analyze() → persist → render | tests/e2e-ingest.test.ts (`agent_title`) |
|
|
38
|
+
| … | | | |
|
|
28
39
|
|
|
29
40
|
## Milestones
|
|
30
41
|
| # | Milestone | Exit criteria |
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Phase 6 — Implementation Discipline
|
|
2
2
|
|
|
3
|
+
> **Also read `references/06c-code-quality.md`** — the modularity & maintainability
|
|
4
|
+
> standard. Structure code as small modules with one responsibility, keep shared
|
|
5
|
+
> functionality in exactly one implementation (SSOT, single escalation path),
|
|
6
|
+
> never hardcode what config should drive, and keep every module testable
|
|
7
|
+
> standalone outside the host.
|
|
8
|
+
|
|
3
9
|
## Test-first
|
|
4
10
|
|
|
5
11
|
1. Write the failing test for the next behavior
|
|
@@ -54,3 +60,26 @@ Every user-visible change gets an entry under `## [Unreleased]` or the released
|
|
|
54
60
|
- New hard requirement discovered → `capture_spec` immediately
|
|
55
61
|
- Requirement changed → capture superseding spec (old → obsolete)
|
|
56
62
|
- When pi-vigilant injects its verification checklist → respond with `update_spec_status` + evidence
|
|
63
|
+
|
|
64
|
+
## Challenge approved designs during implementation (M4)
|
|
65
|
+
|
|
66
|
+
A plan was approved, but that does not make it correct. When a spec's literal
|
|
67
|
+
reading creates **product tension** (e.g. feedback clusters rendered under the
|
|
68
|
+
"Plan" tab when "Plan = plans"), **stop and resolve it with the user before
|
|
69
|
+
implementing** — do not implement blindly and call it delivered.
|
|
70
|
+
|
|
71
|
+
- Tension detected → state it plainly, propose the fix, get a decision.
|
|
72
|
+
- Implement the *sensible* version, not the literal-but-wrong one.
|
|
73
|
+
- The user's words "implemented in a SENSIBLE way" are the acceptance bar.
|
|
74
|
+
|
|
75
|
+
## The delivery log is a claim; the code is the evidence (M5)
|
|
76
|
+
|
|
77
|
+
Before marking anything ✅ (a spec, a milestone, a delivery-log item):
|
|
78
|
+
|
|
79
|
+
1. **Trace the actual code path** — the function is called, not just defined.
|
|
80
|
+
2. Confirm the output is **consumed by a surface** (Rule 13).
|
|
81
|
+
3. Confirm the **operator-facing outcome test** passes (Rule 12).
|
|
82
|
+
|
|
83
|
+
A delivery log saying "IMP-006 delivered ✅" proves nothing. The code is the
|
|
84
|
+
evidence. External planning docs (IMPROVEMENT-PLAN.md, PLAN.md, delivery logs)
|
|
85
|
+
are inputs to spec capture — their ✅ markers are claims, never ground truth.
|
|
@@ -125,6 +125,13 @@ API/curl checks do not replicate what a human sees. Via `pi-aia-browser`:
|
|
|
125
125
|
|
|
126
126
|
A 200 response with a blank or broken page is a **failure**.
|
|
127
127
|
|
|
128
|
+
**Silent async paths are the priority.** Fire-and-forget enrichment (an async
|
|
129
|
+
budgeted analysis that fills in a card title after render) fails silently — the
|
|
130
|
+
page renders, nobody notices the enrichment never landed. For any async/silent
|
|
131
|
+
surface: ingest through the real flow, then **wait for the enrichment to appear**
|
|
132
|
+
and assert it (e.g. ingest a report, wait for `agent_title` on the card). If it
|
|
133
|
+
never arrives, the wiring is dead — that is a failed test.
|
|
134
|
+
|
|
128
135
|
## Rule 10 — Definition of done (all must hold)
|
|
129
136
|
|
|
130
137
|
- [ ] Typecheck/build passes
|
|
@@ -135,6 +142,9 @@ A 200 response with a blank or broken page is a **failure**.
|
|
|
135
142
|
- [ ] Observable end state verified as a user would experience it
|
|
136
143
|
- [ ] Web surfaces exercised through a real browser
|
|
137
144
|
- [ ] Every MUST spec `met` with concrete evidence (`update_spec_status`)
|
|
145
|
+
- [ ] **Spec-to-code traceability: every `met` spec carries `trace` (outcome → codePath → testFile + assertion); `/asf verify` mechanically validates it (large work)**
|
|
146
|
+
- [ ] **Consumed by a surface: every delivered feature's output is visible in the product (UI or API) — nothing ships as dead machinery**
|
|
147
|
+
- [ ] **E2E behavioral test: every feature spec has a test through the real entry point asserting the operator-facing outcome**
|
|
138
148
|
- [ ] Unverifiable specs → `partial` + asked the user (never self-certified)
|
|
139
149
|
|
|
140
150
|
## Rule 11 — Report honestly
|
|
@@ -144,3 +154,43 @@ A 200 response with a blank or broken page is a **failure**.
|
|
|
144
154
|
- If a check was skipped or inconclusive, **say so explicitly** and say why.
|
|
145
155
|
- Distinguish "tests pass" from "feature works for the user" — Rule 2.
|
|
146
156
|
- If you discover you shipped something broken, say it plainly and fix it first.
|
|
157
|
+
|
|
158
|
+
## Rule 12 — Test the OPERATOR-FACING OUTCOME, not the machinery
|
|
159
|
+
|
|
160
|
+
> **Real failure (betamaxx audit):** `intelligence-budget.test.ts` and
|
|
161
|
+
> `service-session.test.ts` passed — they proved `analyze()` *works in
|
|
162
|
+
> isolation*. Nobody tested "does ingest actually call `analyze()`?". The tests
|
|
163
|
+
> proved the machinery, not the wiring. `intelligenceBudget.analyze()` shipped
|
|
164
|
+
> as **dead code** while every unit test was green.
|
|
165
|
+
|
|
166
|
+
Unit tests prove a component works in isolation. They do **not** prove the
|
|
167
|
+
feature is wired. For every feature spec, the deciding test is the one that
|
|
168
|
+
asserts the **operator-facing outcome** end-to-end:
|
|
169
|
+
|
|
170
|
+
- ❌ "`analyze()` returns a budget verdict" (machinery)
|
|
171
|
+
- ✅ "an ingested report eventually has an `agent_title` rendered on the card" (outcome)
|
|
172
|
+
|
|
173
|
+
If a unit test is green but the outcome test is missing, the feature is **not**
|
|
174
|
+
done — the wiring may be dead.
|
|
175
|
+
|
|
176
|
+
## Rule 13 — Nothing is delivered until CONSUMED BY A SURFACE
|
|
177
|
+
|
|
178
|
+
> **Real failure (betamaxx audit):** code commented *"Not yet consumed by a
|
|
179
|
+
> surface"* shipped as "delivered". Machinery (budget, runner, verdict
|
|
180
|
+
> contract) was built; the product behavior (cards titled by the agent) never
|
|
181
|
+
> happened.
|
|
182
|
+
|
|
183
|
+
- **Delivered = the output is visible in the product** (UI or API).
|
|
184
|
+
- Code commented "not yet consumed by a surface" is, by definition, **not
|
|
185
|
+
delivered**.
|
|
186
|
+
- Before marking a spec `met`, answer: *where does a user/operator see this?*
|
|
187
|
+
If nowhere — it is not done.
|
|
188
|
+
|
|
189
|
+
## Rule 14 — Every feature spec ships an E2E behavioral test through the REAL entry point
|
|
190
|
+
|
|
191
|
+
- The E2E test drives the **real entry point** (HTTP endpoint, CLI, UI) with a
|
|
192
|
+
mocked boundary (agent, DB), and asserts the operator-facing outcome.
|
|
193
|
+
- This catches dead wiring in one test: ingest → `analyze()` → persist → render.
|
|
194
|
+
- **Scale note:** mandatory for feature specs in **large/gated work**. For small
|
|
195
|
+
work, required only when the change touches a surface/wiring; otherwise the
|
|
196
|
+
standard test-first rules above suffice.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Modularity & Maintainability Standard (MANDATORY)
|
|
2
|
+
|
|
3
|
+
Distilled from Bruno's own development philosophy — honed on the Tunnel project
|
|
4
|
+
and its modules (smart-router, autoroute, feature-extractor, heuristic-engine) —
|
|
5
|
+
and reinforced by established software-engineering research: **Single Source of
|
|
6
|
+
Truth (SSOT)**, **testability as a design property**, and **ports-and-adapters
|
|
7
|
+
(hexagonal) seams**.
|
|
8
|
+
|
|
9
|
+
These rules exist because violating them caused real pain in those sessions:
|
|
10
|
+
code "thrown on a heap", duplicated logic, hardcoded model attributes, modules
|
|
11
|
+
that could not be tested outside the server, and regressions that broke
|
|
12
|
+
existing functionality. Each rule below carries the lesson.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Rule 1 — Small, well-readable modules, plugged in where needed
|
|
17
|
+
|
|
18
|
+
> *"You throw a bunch of code and functionality on a heap, instead of making
|
|
19
|
+
> smaller, well-readable modules and plugging them in where needed."* — Tunnel sessions
|
|
20
|
+
|
|
21
|
+
- One module = one responsibility. If a file does two unrelated things, split it.
|
|
22
|
+
- A module must be **readable top to bottom** by someone new: clear names,
|
|
23
|
+
short functions, no 500-line god-files.
|
|
24
|
+
- **Plug in, don't embed**: the host system imports and wires modules; it does
|
|
25
|
+
not copy their logic inline.
|
|
26
|
+
- Extraction triggers (any of these → extract a module):
|
|
27
|
+
- a function/concern exceeds ~100 lines or 3 levels of nesting
|
|
28
|
+
- the same logic is needed by two call sites
|
|
29
|
+
- you cannot explain what the file does in one sentence
|
|
30
|
+
- you are about to debug the same area a second time
|
|
31
|
+
|
|
32
|
+
## Rule 2 — One implementation for shared functionality (single escalation path)
|
|
33
|
+
|
|
34
|
+
> *"Make a separate module for ... communication and use IT instead of having
|
|
35
|
+
> that function integrated in there."* — Tunnel sessions
|
|
36
|
+
|
|
37
|
+
- **Every shared capability lives in exactly one authoritative module.**
|
|
38
|
+
Everything else imports it. No copies, no re-implementations, no
|
|
39
|
+
"parallel systems working side by side" (*"I want an integrated system, not a
|
|
40
|
+
dual system working side by side"*).
|
|
41
|
+
- This is the **Single Source of Truth** (SSOT) principle: every piece of
|
|
42
|
+
knowledge exists in exactly one place; every other reference *points* to it.
|
|
43
|
+
- SSOT checklist before shipping (from research on SSOT):
|
|
44
|
+
- Is any value/logic defined in more than one place?
|
|
45
|
+
- If this changes tomorrow, how many files do I touch? (Answer: **one**.)
|
|
46
|
+
- Would a new developer know where the authoritative version lives?
|
|
47
|
+
- Escalation paths included: if a request can escalate (to a stronger model,
|
|
48
|
+
a fallback, a retry, an admin), there is **one** escalation path module that
|
|
49
|
+
every caller routes through — not ad-hoc escalation logic scattered per
|
|
50
|
+
caller.
|
|
51
|
+
|
|
52
|
+
## Rule 3 — No hardcoding; config drives behavior
|
|
53
|
+
|
|
54
|
+
> *"DO NOT hard-code attributes of models ... stuff in the brackets is specific
|
|
55
|
+
> to opus, but not necessarily to any model we will add."* — Tunnel sessions
|
|
56
|
+
|
|
57
|
+
- Model names, thresholds, limits, timeouts, URLs, feature flags: all load from
|
|
58
|
+
config, never literal in logic.
|
|
59
|
+
- If you hardcode a value that another system (or a future system) might share,
|
|
60
|
+
you have created a second source of truth — see Rule 2.
|
|
61
|
+
- The module must behave the same for any config input; its logic is
|
|
62
|
+
config-agnostic.
|
|
63
|
+
|
|
64
|
+
## Rule 4 — Testable outside the host, then integrated verbatim
|
|
65
|
+
|
|
66
|
+
> *"MAKE IT TIGHT AND MODULAR AND FULLY TESTABLE OUTSIDE OF THE SERVER. TEST
|
|
67
|
+
> EVERYTHING 100% ANALOGOUS TO HOW IT WORKS ON THE SERVER ... THEN INTEGRATE
|
|
68
|
+
> BACK INTO THE SERVER."* — Tunnel sessions
|
|
69
|
+
|
|
70
|
+
- A module must run standalone: a test script imports and primes it **with the
|
|
71
|
+
exact same calls** the host would make.
|
|
72
|
+
- **Same modules in tests and production** — never a "test copy" of logic.
|
|
73
|
+
Tests import the production module; the host imports the same module.
|
|
74
|
+
(*"It should be imported in [the host] and used there with no changes — a
|
|
75
|
+
separate test script importing and priming the module can be created for
|
|
76
|
+
testing."*)
|
|
77
|
+
- **Testability is a design property, not a testing task** (research): if a
|
|
78
|
+
business rule needs a running server + real DB to validate, the architecture
|
|
79
|
+
is wrong. Core logic should run as plain functions with injected fakes.
|
|
80
|
+
- Build **seams** (ports-and-adapters): core logic depends on interfaces
|
|
81
|
+
(clock, IDs, persistence, external calls, message publishing), not on
|
|
82
|
+
concrete infrastructure. Adapters live at the edge.
|
|
83
|
+
- Verification flow: develop → test standalone (100% analogous) → **integrate
|
|
84
|
+
into the host verbatim** → re-test in place.
|
|
85
|
+
|
|
86
|
+
## Rule 5 — Refactor what is too complex to understand
|
|
87
|
+
|
|
88
|
+
> *"REFACTOR WHAT IS TOO COMPLEX FOR YOU TO UNDERSTAND!"* — Tunnel sessions
|
|
89
|
+
|
|
90
|
+
- Complexity you cannot explain is a defect, not a badge of honor.
|
|
91
|
+
- When you find yourself running "rounds like a moron" debugging something,
|
|
92
|
+
**stop and refactor the module** instead of patching blind.
|
|
93
|
+
- Refactor gate (when to invest): only refactor when it makes debugging and
|
|
94
|
+
individual component testing easier and faster, with the data you already
|
|
95
|
+
have. Not refactoring for its own sake; refactoring for **testability and
|
|
96
|
+
clarity**.
|
|
97
|
+
- Refactoring techniques that directly serve this (research-validated):
|
|
98
|
+
**Extract Method** (break long functions into named steps), **Extract Class/
|
|
99
|
+
Module** (group related logic), **Replace Conditional with Polymorphism**
|
|
100
|
+
(turn nested conditionals into modular dispatch), **Remove Duplication**
|
|
101
|
+
(collapse copies into one implementation).
|
|
102
|
+
|
|
103
|
+
## Rule 6 — Layer with clear boundaries, and document them
|
|
104
|
+
|
|
105
|
+
> *"Refactor the layering for readability completely, test everything in
|
|
106
|
+
> detail, and do a writeup for the layered system."* — Tunnel sessions
|
|
107
|
+
|
|
108
|
+
- Define layers (e.g., handler → router → modules → backends) with one-way
|
|
109
|
+
dependency rules. No layer reaches across another.
|
|
110
|
+
- **Do a writeup**: a short architecture doc (in the repo) stating what each
|
|
111
|
+
module is, what it uses, how and why, and where truth lives (the SSOT map).
|
|
112
|
+
(*"Properly document the modules and the main scaffolding to always have a
|
|
113
|
+
clear idea what is happening where."*)
|
|
114
|
+
- Research on architecture fitness: keep the "core" boring and deterministic
|
|
115
|
+
(no framework/IO inside); edges translate to and from infrastructure.
|
|
116
|
+
|
|
117
|
+
## Rule 7 — Debug observability: full I/O logging, replayable
|
|
118
|
+
|
|
119
|
+
> *"Shouldn't you also log somewhere the FULL input and outputs of the whole
|
|
120
|
+
> chain (during debug only) including headers and bodies ... and then
|
|
121
|
+
> comparing, since the lateral test just works?"* — Tunnel sessions
|
|
122
|
+
|
|
123
|
+
- During debug: log the **full input and output of the whole chain** (headers,
|
|
124
|
+
bodies, intermediate steps) to disk — not just errors.
|
|
125
|
+
- Side tests must log identically, so you can **compare** a passing lateral
|
|
126
|
+
test against a failing production call and see the divergence.
|
|
127
|
+
- Keep captured real traffic and **replay it through the structure**
|
|
128
|
+
(*"using stored message data and replaying it through the structure"*) to
|
|
129
|
+
reproduce and fix without the live host.
|
|
130
|
+
- Logging must be enough that long-term debugging works from log files and the
|
|
131
|
+
stored corpus alone.
|
|
132
|
+
|
|
133
|
+
## Rule 8 — Nothing may break existing functionality
|
|
134
|
+
|
|
135
|
+
> *"Nothing may break already existing functionality in the server."* — Tunnel sessions
|
|
136
|
+
|
|
137
|
+
- Modularity changes and refactors are **behavior-preserving**: same inputs,
|
|
138
|
+
same outputs, same side effects.
|
|
139
|
+
- After any restructuring, run the existing tests + a regression pass over the
|
|
140
|
+
previous behavior before declaring done.
|
|
141
|
+
- This is the SSOT/refactor safety net: refactoring restructures *structure*,
|
|
142
|
+
never *behavior*.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Where this applies in ASF
|
|
147
|
+
|
|
148
|
+
- **Phase 4 (adversarial)**: challenge the design — is there duplication?
|
|
149
|
+
Where is the single source of truth? Is the module testable outside the
|
|
150
|
+
host? What breaks if a config value changes?
|
|
151
|
+
- **Phase 5 (PLAN.md)**: the Architecture/Design section must name the modules,
|
|
152
|
+
their boundaries, the one-way dependencies, where shared truth lives, and
|
|
153
|
+
how each module is tested standalone.
|
|
154
|
+
- **Phase 6 (implementation)**: apply Rules 1–8 as you build; extract modules
|
|
155
|
+
when triggers fire; write the architecture doc alongside the code.
|
|
156
|
+
- **Phase 7 (verification)**: the DoD checklist includes: no duplicated shared
|
|
157
|
+
logic (Rule 2), no hardcoded config values (Rule 3), every module tested
|
|
158
|
+
standalone with the same calls (Rule 4), architecture doc written (Rule 6),
|
|
159
|
+
existing functionality still green (Rule 8).
|
|
160
|
+
|
|
161
|
+
## Anti-patterns
|
|
162
|
+
|
|
163
|
+
- ❌ One giant file / god-object that "does everything"
|
|
164
|
+
- ❌ Copy-pasting shared logic instead of importing the one module
|
|
165
|
+
- ❌ A "test version" of a module that differs from the production version
|
|
166
|
+
- ❌ Hardcoded model names, thresholds, URLs in logic instead of config
|
|
167
|
+
- ❌ Escalation/fallback logic re-implemented per caller instead of one path
|
|
168
|
+
- ❌ Refactoring "for fun" without the testability/debugging payoff
|
|
169
|
+
- ❌ Shipping a module that cannot run outside the host
|
|
170
|
+
- ❌ Skipping the architecture writeup ("the code is self-documenting")
|