@leing2021/super-pi 0.23.10 → 0.23.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/extensions/ce-core/index.ts +29 -11
- package/extensions/ce-core/tools/parallel-subagent.ts +3 -1
- package/extensions/ce-core/tools/subagent-renderer.ts +71 -46
- package/extensions/ce-core/tools/subagent.ts +1 -1
- package/package.json +1 -1
- package/skills/01-brainstorm/SKILL.md +20 -9
- package/skills/01-brainstorm/references/context-glossary.md +46 -0
- package/skills/01-brainstorm/references/requirements-template.md +5 -0
- package/skills/02-plan/references/adr-template.md +43 -0
- package/skills/02-plan/references/plan-template.md +5 -0
- package/skills/03-work/SKILL.md +1 -1
- package/skills/03-work/references/debug-discipline.md +53 -0
package/README.md
CHANGED
|
@@ -71,7 +71,7 @@ You: /skill:03-work docs/plans/plan.md
|
|
|
71
71
|
|
|
72
72
|
| Skill | What it does | Core tool |
|
|
73
73
|
|-------|-------------|-----------|
|
|
74
|
-
| **01-brainstorm** | Structured multi-round discovery | `brainstorm_dialog` |
|
|
74
|
+
| **01-brainstorm** | Structured multi-round discovery, domain vocabulary persistence | `brainstorm_dialog` |
|
|
75
75
|
| **02-plan** | TDD-gated implementation units, optional CEO Review | `plan_diff` |
|
|
76
76
|
| **03-work** | Inline-first execution, bounded subagents, checkpoint resume, strict TDD, stop-the-line | `ce_subagent`, `ce_parallel_subagent` |
|
|
77
77
|
| **04-review** | Auto-assigned reviewers, five-axis findings, autofix loop | `review_router` |
|
|
@@ -122,6 +122,7 @@ Super Pi is not a fork or wrapper. It extracts useful methods from the projects
|
|
|
122
122
|
| [superpowers](https://github.com/obra/superpowers) | Strict TDD gates, design checklists, review discipline, and the idea that agents need hard gates instead of gentle suggestions. |
|
|
123
123
|
| [compound-engineering-plugin](https://github.com/EveryInc/compound-engineering-plugin) | The five-step think → plan → build → review → learn loop and the knowledge-compounding backbone. |
|
|
124
124
|
| [gstack](https://github.com/garrytan/gstack) | YC-style forcing questions, CEO Review cognitive frameworks, browser QA patterns, failure maps, and evidence-first validation. |
|
|
125
|
+
| [mattpocock/skills](https://github.com/mattpocock/skills) | Context glossary (`CONTEXT.md`) for cross-session term persistence, lightweight ADR with three-condition threshold, and feedback-loop-first debug discipline. Adopted as reference templates embedded into existing skills — no new skills or tools. |
|
|
125
126
|
|
|
126
127
|
---
|
|
127
128
|
|
|
@@ -133,8 +134,8 @@ When an unexpected failure occurs during `03-work`:
|
|
|
133
134
|
|
|
134
135
|
1. **STOP** adding features
|
|
135
136
|
2. **PRESERVE** evidence
|
|
136
|
-
3. **DIAGNOSE** root cause
|
|
137
|
-
4. **FIX** the root cause
|
|
137
|
+
3. **DIAGNOSE** root cause — build a feedback loop first, then reproduce → hypothesise → instrument → fix
|
|
138
|
+
4. **FIX** the root cause, not the symptom
|
|
138
139
|
5. **GUARD** with a regression test
|
|
139
140
|
6. **RESUME** only after verification passes
|
|
140
141
|
|
|
@@ -173,6 +174,7 @@ your-project/
|
|
|
173
174
|
├── docs/
|
|
174
175
|
│ ├── brainstorms/ # Requirements
|
|
175
176
|
│ ├── plans/ # Execution plans
|
|
177
|
+
│ ├── adr/ # Architecture decisions (lazy)
|
|
176
178
|
│ └── solutions/ # Knowledge cards
|
|
177
179
|
└── .context/
|
|
178
180
|
└── compound-engineering/
|
|
@@ -130,7 +130,7 @@ function createSubagentRunner(
|
|
|
130
130
|
prompt,
|
|
131
131
|
agent: "",
|
|
132
132
|
task: "",
|
|
133
|
-
cwd: pi.cwd ?? process.cwd(),
|
|
133
|
+
cwd: (pi as any).cwd ?? process.cwd(),
|
|
134
134
|
extraFlags: options?.extraFlags,
|
|
135
135
|
extraEnv: options?.extraEnv,
|
|
136
136
|
signal,
|
|
@@ -503,7 +503,7 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
503
503
|
inheritSkills: params.inheritSkills,
|
|
504
504
|
},
|
|
505
505
|
createSubagentRunner(pi, signal),
|
|
506
|
-
{ onUpdate },
|
|
506
|
+
{ onUpdate: onUpdate ? (details) => onUpdate({ content: [], details }) : undefined },
|
|
507
507
|
)
|
|
508
508
|
|
|
509
509
|
// Build final content from results
|
|
@@ -521,7 +521,10 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
521
521
|
return renderSubagentCall(args, theme)
|
|
522
522
|
},
|
|
523
523
|
renderResult(result, renderContext, theme, _context) {
|
|
524
|
-
|
|
524
|
+
// Handle partial updates where data is at top level (from onUpdate)
|
|
525
|
+
// rather than nested in .details (from final result return value)
|
|
526
|
+
const data = (result.details || result) as SubagentLiveDetails
|
|
527
|
+
return renderSubagentResult(data, renderContext, theme)
|
|
525
528
|
},
|
|
526
529
|
})
|
|
527
530
|
|
|
@@ -620,31 +623,46 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
620
623
|
inheritSkills: params.inheritSkills,
|
|
621
624
|
},
|
|
622
625
|
createSubagentRunner(pi, signal),
|
|
623
|
-
{ onUpdate },
|
|
626
|
+
{ onUpdate: onUpdate ? (details) => onUpdate({ content: [], details }) : undefined },
|
|
624
627
|
)
|
|
625
628
|
|
|
626
|
-
// Build summary content
|
|
629
|
+
// Build compact summary content for LLM consumption
|
|
627
630
|
const successCount = result.results.filter(r => r.exitCode === 0).length
|
|
628
631
|
const failCount = result.results.filter(r => isFailedResult(r)).length
|
|
629
|
-
const summaries = result.results.map(r => {
|
|
632
|
+
const summaries = result.results.map((r, i) => {
|
|
633
|
+
const icon = isFailedResult(r) ? "✗" : "✓"
|
|
630
634
|
const output = getFinalOutput(r.messages) || r.errorMessage || r.stderr || "(no output)"
|
|
631
|
-
|
|
632
|
-
|
|
635
|
+
// Compact: first non-empty line, stripped of markdown formatting
|
|
636
|
+
const summaryLine = output.split("\n").find((l: string) => l.trim().length > 0) || ""
|
|
637
|
+
const cleaned = summaryLine.replace(/^#+\s*/, "").replace(/\*\*/g, "").trim()
|
|
638
|
+
const oneLine = cleaned.length > 200 ? cleaned.slice(0, 199) + "…" : cleaned
|
|
639
|
+
return `${i + 1}. ${icon} ${r.agent} — ${oneLine}`
|
|
640
|
+
})
|
|
641
|
+
|
|
642
|
+
// Full outputs available in details for expanded view
|
|
643
|
+
const fullOutputs = result.results.map(r => {
|
|
644
|
+
const output = getFinalOutput(r.messages) || ""
|
|
645
|
+
return output
|
|
633
646
|
})
|
|
634
647
|
|
|
635
648
|
return {
|
|
636
649
|
content: [{
|
|
637
650
|
type: "text",
|
|
638
|
-
text: `Parallel: ${successCount}/${result.results.length} succeeded${failCount > 0 ? `, ${failCount} failed` : ""}\n
|
|
651
|
+
text: `Parallel: ${successCount}/${result.results.length} succeeded${failCount > 0 ? `, ${failCount} failed` : ""}\n${summaries.join("\n")}`,
|
|
639
652
|
}],
|
|
640
653
|
details: result,
|
|
641
654
|
}
|
|
642
655
|
},
|
|
643
656
|
renderCall(args, theme, _context) {
|
|
644
|
-
return renderSubagentCall(
|
|
657
|
+
return renderSubagentCall({
|
|
658
|
+
tasks: typeof args.tasks === "string" ? [] : args.tasks,
|
|
659
|
+
}, theme)
|
|
645
660
|
},
|
|
646
661
|
renderResult(result, renderContext, theme, _context) {
|
|
647
|
-
|
|
662
|
+
// Handle partial updates where data is at top level (from onUpdate)
|
|
663
|
+
// rather than nested in .details (from final result return value)
|
|
664
|
+
const data = (result.details || result) as ParallelSubagentLiveDetails
|
|
665
|
+
return renderSubagentResult(data, renderContext, theme)
|
|
648
666
|
},
|
|
649
667
|
})
|
|
650
668
|
|
|
@@ -20,11 +20,13 @@ import {
|
|
|
20
20
|
getFinalOutput,
|
|
21
21
|
makeFailedResult,
|
|
22
22
|
invokeRunner,
|
|
23
|
+
type AnyRunner,
|
|
23
24
|
} from "./subagent-events"
|
|
24
25
|
import {
|
|
25
26
|
assertNonPipelineStageSkill,
|
|
26
27
|
type SubagentLiveRunner,
|
|
27
28
|
type SubagentLiveExecOptions,
|
|
29
|
+
type SubagentRunner,
|
|
28
30
|
} from "./subagent"
|
|
29
31
|
|
|
30
32
|
// ---------------------------------------------------------------------------
|
|
@@ -88,7 +90,7 @@ export function createParallelSubagentTool() {
|
|
|
88
90
|
|
|
89
91
|
async execute(
|
|
90
92
|
input: ParallelSubagentInput,
|
|
91
|
-
runner: SubagentLiveRunner,
|
|
93
|
+
runner: SubagentLiveRunner | SubagentRunner,
|
|
92
94
|
toolCtx?: { onUpdate?: (details: ParallelSubagentLiveDetails) => void },
|
|
93
95
|
): Promise<ParallelSubagentLiveDetails> {
|
|
94
96
|
// Recursion depth guard
|
|
@@ -142,13 +142,14 @@ export function renderSubagentCall(
|
|
|
142
142
|
|
|
143
143
|
if (args.tasks && args.tasks.length > 0) {
|
|
144
144
|
let text =
|
|
145
|
-
theme.fg("toolTitle", theme.bold("
|
|
146
|
-
theme.fg("accent",
|
|
147
|
-
for (
|
|
148
|
-
const
|
|
149
|
-
|
|
145
|
+
theme.fg("toolTitle", theme.bold("⬇ parallel ")) +
|
|
146
|
+
theme.fg("accent", `${args.tasks.length} agents launching...`)
|
|
147
|
+
for (let i = 0; i < args.tasks.length; i++) {
|
|
148
|
+
const t = args.tasks[i]
|
|
149
|
+
const num = theme.fg("muted", `${i + 1}.`)
|
|
150
|
+
const preview = t.task.length > 50 ? `${t.task.slice(0, 50)}...` : t.task
|
|
151
|
+
text += `\n ${num} ${theme.fg("accent", t.agent)} ${theme.fg("dim", preview)}`
|
|
150
152
|
}
|
|
151
|
-
if (args.tasks.length > 3) text += `\n ${theme.fg("muted", `... +${args.tasks.length - 3} more`)}`
|
|
152
153
|
return new Text(text, 0, 0)
|
|
153
154
|
}
|
|
154
155
|
|
|
@@ -373,70 +374,94 @@ function renderParallelResult(
|
|
|
373
374
|
const successCount = results.filter(r => r.exitCode !== -1 && !isFailedResult(r)).length
|
|
374
375
|
const failCount = results.filter(r => r.exitCode !== -1 && isFailedResult(r)).length
|
|
375
376
|
const isRunning = running > 0
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
:
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
377
|
+
|
|
378
|
+
if (isRunning) {
|
|
379
|
+
// --- Live progress: compact status line ---
|
|
380
|
+
const doneCount = successCount + failCount
|
|
381
|
+
const icon = theme.fg("warning", "⏳")
|
|
382
|
+
const bar = renderProgressBar(doneCount, results.length, theme)
|
|
383
|
+
const doneLabel = failCount > 0
|
|
384
|
+
? theme.fg("success", `${doneCount}/${results.length}✓`) + " " + theme.fg("error", `${failCount}✗`)
|
|
385
|
+
: theme.fg("success", `${doneCount}/${results.length}✓`)
|
|
386
|
+
const runningLabel = theme.fg("dim", `, ${running} running...`)
|
|
387
|
+
return new Text(`${icon} ${bar} ${doneLabel}${runningLabel}`, 0, 0)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// --- Completed: summary card layout ---
|
|
391
|
+
const allSuccess = failCount === 0
|
|
392
|
+
const headerIcon = allSuccess ? theme.fg("success", "✓") : theme.fg("warning", "◐")
|
|
393
|
+
const headerText = failCount > 0
|
|
394
|
+
? `${successCount}/${results.length} succeeded, ${failCount} failed`
|
|
395
|
+
: `${successCount}/${results.length} succeeded`
|
|
396
|
+
|
|
397
|
+
if (context.expanded) {
|
|
398
|
+
// Expanded: header + per-task details with tool calls and output
|
|
386
399
|
const container = new Container()
|
|
387
|
-
container.addChild(new Text(`${
|
|
400
|
+
container.addChild(new Text(`${headerIcon} ${theme.fg("toolTitle", theme.bold("parallel "))}${theme.fg("accent", headerText)}`, 0, 0))
|
|
401
|
+
container.addChild(new Spacer(1))
|
|
388
402
|
|
|
389
403
|
for (const r of results) {
|
|
390
404
|
const rIcon = isFailedResult(r) ? theme.fg("error", "✗") : theme.fg("success", "✓")
|
|
391
|
-
const displayItems = getDisplayItems(r.messages)
|
|
392
405
|
const finalOutput = getFinalOutput(r.messages)
|
|
406
|
+
const summaryText = isFailedResult(r)
|
|
407
|
+
? (r.errorMessage || r.stderr || "unknown error")
|
|
408
|
+
: (finalOutput ? summarizeText(finalOutput, 200) : "(no output)")
|
|
393
409
|
|
|
394
|
-
container.addChild(new
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
for (const item of displayItems) {
|
|
398
|
-
if (item.type === "toolCall")
|
|
399
|
-
container.addChild(new Text(theme.fg("muted", "→ ") + formatToolCall(item.name, item.args, theme.fg.bind(theme)), 0, 0))
|
|
400
|
-
}
|
|
401
|
-
if (finalOutput) {
|
|
402
|
-
container.addChild(new Spacer(1))
|
|
410
|
+
container.addChild(new Text(`${rIcon} ${theme.fg("accent", r.agent)} — ${summaryText}`, 0, 0))
|
|
411
|
+
|
|
412
|
+
if (!isFailedResult(r) && finalOutput) {
|
|
403
413
|
container.addChild(new Markdown(finalOutput.trim(), 0, 0, mdTheme))
|
|
404
414
|
}
|
|
415
|
+
|
|
405
416
|
const taskUsage = formatUsageStats(r.usage, r.model)
|
|
406
417
|
if (taskUsage) container.addChild(new Text(theme.fg("dim", taskUsage), 0, 0))
|
|
418
|
+
container.addChild(new Spacer(1))
|
|
407
419
|
}
|
|
408
420
|
|
|
409
421
|
const usageStr = formatUsageStats(aggregateUsage(results))
|
|
410
422
|
if (usageStr) {
|
|
411
|
-
container.addChild(new Spacer(1))
|
|
412
423
|
container.addChild(new Text(theme.fg("dim", `Total: ${usageStr}`), 0, 0))
|
|
413
424
|
}
|
|
414
425
|
return container
|
|
415
426
|
}
|
|
416
427
|
|
|
417
|
-
// Collapsed
|
|
418
|
-
let text = `${
|
|
428
|
+
// Collapsed: one-line summary per task
|
|
429
|
+
let text = `${headerIcon} ${theme.fg("toolTitle", theme.bold("parallel "))}${theme.fg("accent", headerText)}`
|
|
419
430
|
for (const r of results) {
|
|
420
|
-
const rIcon =
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const displayItems = getDisplayItems(r.messages)
|
|
427
|
-
text += `\n\n${theme.fg("muted", "─── ")}${theme.fg("accent", r.agent)} ${rIcon}`
|
|
428
|
-
if (displayItems.length === 0)
|
|
429
|
-
text += `\n${theme.fg("muted", r.exitCode === -1 ? "(running...)" : "(no output)")}`
|
|
430
|
-
else text += `\n${renderDisplayItems(displayItems, 5)}`
|
|
431
|
+
const rIcon = isFailedResult(r) ? theme.fg("error", "✗") : theme.fg("success", "✓")
|
|
432
|
+
const finalOutput = getFinalOutput(r.messages)
|
|
433
|
+
const summaryText = isFailedResult(r)
|
|
434
|
+
? (r.errorMessage || r.stderr || "unknown error")
|
|
435
|
+
: (finalOutput ? summarizeText(finalOutput, 120) : "(no output)")
|
|
436
|
+
text += `\n ${rIcon} ${theme.fg("accent", r.agent)} — ${summaryText}`
|
|
431
437
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
if (!context.expanded) text += `\n${theme.fg("muted", "(Ctrl+O to expand)")}`
|
|
438
|
+
const usageStr = formatUsageStats(aggregateUsage(results))
|
|
439
|
+
if (usageStr) text += `\n${theme.fg("dim", usageStr)}`
|
|
440
|
+
text += `\n${theme.fg("muted", "(Ctrl+O to expand)")}`
|
|
437
441
|
return new Text(text, 0, 0)
|
|
438
442
|
}
|
|
439
443
|
|
|
444
|
+
// ---------------------------------------------------------------------------
|
|
445
|
+
// Progress bar & text helpers
|
|
446
|
+
// ---------------------------------------------------------------------------
|
|
447
|
+
|
|
448
|
+
function renderProgressBar(done: number, total: number, theme: Theme): string {
|
|
449
|
+
const width = Math.min(total, 20)
|
|
450
|
+
const filled = Math.round((done / total) * width)
|
|
451
|
+
const empty = width - filled
|
|
452
|
+
const bar = theme.fg("success", "█".repeat(filled)) + theme.fg("dim", "░".repeat(empty))
|
|
453
|
+
return bar
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function summarizeText(text: string, maxLen: number): string {
|
|
457
|
+
// Take first meaningful paragraph or line
|
|
458
|
+
const firstLine = text.split("\n").find(l => l.trim().length > 0) || ""
|
|
459
|
+
// Strip markdown headers and bold for summary
|
|
460
|
+
const cleaned = firstLine.replace(/^#+\s*/, "").replace(/\*\*/g, "").replace(/\[.*?\]\(.*?\)/g, "").trim()
|
|
461
|
+
if (cleaned.length <= maxLen) return cleaned
|
|
462
|
+
return cleaned.slice(0, maxLen - 1) + "…"
|
|
463
|
+
}
|
|
464
|
+
|
|
440
465
|
// ---------------------------------------------------------------------------
|
|
441
466
|
// Markdown theme helper
|
|
442
467
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -59,6 +59,15 @@ Before summarizing, ensure the design answers:
|
|
|
59
59
|
- What can fail, and how?
|
|
60
60
|
- How will we verify success?
|
|
61
61
|
|
|
62
|
+
## Domain vocabulary (optional)
|
|
63
|
+
|
|
64
|
+
After mode-specific questions, check if the project has a `CONTEXT.md` at root.
|
|
65
|
+
If not, and the brainstorm reveals 3+ domain-specific terms with ambiguous meanings,
|
|
66
|
+
offer to create one using `references/context-glossary.md`. Update it inline during
|
|
67
|
+
the session — don't batch. If it exists, cross-reference and flag conflicts:
|
|
68
|
+
|
|
69
|
+
> "Your CONTEXT.md defines 'cancellation' as X, but you seem to mean Y — which is it?"
|
|
70
|
+
|
|
62
71
|
## Stop conditions
|
|
63
72
|
|
|
64
73
|
Stop and ask instead of guessing when: requirements conflict, success criteria unclear, task spans multiple systems, or user hasn't approved design.
|
|
@@ -70,15 +79,17 @@ Stop and ask instead of guessing when: requirements conflict, success criteria u
|
|
|
70
79
|
## Workflow
|
|
71
80
|
|
|
72
81
|
1. Scan repository for nearby context
|
|
73
|
-
2.
|
|
74
|
-
3.
|
|
75
|
-
4. Run
|
|
76
|
-
5.
|
|
77
|
-
6.
|
|
78
|
-
7.
|
|
79
|
-
8.
|
|
80
|
-
9.
|
|
81
|
-
10.
|
|
82
|
+
2. Check for existing `CONTEXT.md` at repo root
|
|
83
|
+
3. Determine mode (Startup / Builder / CE)
|
|
84
|
+
4. Run mode-specific questions (use reference files)
|
|
85
|
+
5. Run Premise Challenge
|
|
86
|
+
6. Generate 2-3 alternatives (minimal viable + ideal architecture)
|
|
87
|
+
7. Validate against design checklist
|
|
88
|
+
8. Offer to create/update `CONTEXT.md` if domain terms emerged
|
|
89
|
+
9. Use `brainstorm_dialog` `summarize` to finalize
|
|
90
|
+
10. Capture requirements in `docs/brainstorms/`
|
|
91
|
+
11. Get explicit user approval
|
|
92
|
+
12. Handoff to `02-plan` using `references/handoff.md`
|
|
82
93
|
|
|
83
94
|
## Artifact contract
|
|
84
95
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Context Glossary
|
|
2
|
+
|
|
3
|
+
An optional project vocabulary file that persists domain terms across sessions.
|
|
4
|
+
|
|
5
|
+
## When to create
|
|
6
|
+
|
|
7
|
+
Create `CONTEXT.md` at repo root **only when** the brainstorm reveals 3+ domain-specific terms
|
|
8
|
+
that have ambiguous or overloaded meanings. Skip for trivial features.
|
|
9
|
+
|
|
10
|
+
## Format
|
|
11
|
+
|
|
12
|
+
```md
|
|
13
|
+
# {Project Name}
|
|
14
|
+
|
|
15
|
+
## Language
|
|
16
|
+
|
|
17
|
+
**Term**: One-sentence definition.
|
|
18
|
+
_Avoid_: aliases to discourage
|
|
19
|
+
|
|
20
|
+
**Term2**: One-sentence definition.
|
|
21
|
+
_Avoid_: aliases to discourage
|
|
22
|
+
|
|
23
|
+
## Relationships
|
|
24
|
+
|
|
25
|
+
- **Term** contains many **Term2**
|
|
26
|
+
|
|
27
|
+
## Example
|
|
28
|
+
|
|
29
|
+
> Dev: "The materialization cascade is failing."
|
|
30
|
+
> Domain expert: "You mean when a lesson gets a filesystem path?"
|
|
31
|
+
> Dev: "Yes — 'materialization cascade' is our term for that flow."
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Rules
|
|
35
|
+
|
|
36
|
+
- One definition per term, 1-2 sentences max. Define what it IS, not what it does.
|
|
37
|
+
- Pick one canonical term, list alternatives as _Avoid_.
|
|
38
|
+
- Only project-specific concepts — general programming terms don't belong.
|
|
39
|
+
- Flag ambiguities explicitly.
|
|
40
|
+
- No implementation details. This is a glossary, not a spec.
|
|
41
|
+
|
|
42
|
+
## Multiple bounded contexts
|
|
43
|
+
|
|
44
|
+
If the project has clearly separated domains (e.g. ordering vs billing), create a
|
|
45
|
+
`CONTEXT-MAP.md` at root that lists each context and its `CONTEXT.md` location.
|
|
46
|
+
Most projects need only a single root `CONTEXT.md`.
|
|
@@ -23,3 +23,8 @@ Describe the chosen direction and why it is the best fit.
|
|
|
23
23
|
## Success criteria
|
|
24
24
|
|
|
25
25
|
List the concrete outcomes that would mean this work succeeded.
|
|
26
|
+
|
|
27
|
+
## Domain vocabulary
|
|
28
|
+
|
|
29
|
+
List any canonical terms established during this session.
|
|
30
|
+
If a `CONTEXT.md` was created or updated, link it here.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# ADR Template
|
|
2
|
+
|
|
3
|
+
Lightweight Architecture Decision Records. Only create when ALL THREE are true:
|
|
4
|
+
|
|
5
|
+
1. **Hard to reverse** — changing your mind later costs meaningfully
|
|
6
|
+
2. **Surprising without context** — a future reader will wonder "why this way?"
|
|
7
|
+
3. **Real trade-off** — genuine alternatives existed and you picked one for a reason
|
|
8
|
+
|
|
9
|
+
If any is missing, skip the ADR.
|
|
10
|
+
|
|
11
|
+
## File location
|
|
12
|
+
|
|
13
|
+
`docs/adr/0001-slug.md`, sequential numbering. Create `docs/adr/` lazily.
|
|
14
|
+
|
|
15
|
+
## Template
|
|
16
|
+
|
|
17
|
+
```md
|
|
18
|
+
# {Short title}
|
|
19
|
+
|
|
20
|
+
{1-3 sentences: context, decision, and why.}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
That's it. A single paragraph is fine. The value is recording THAT a decision was made and WHY.
|
|
24
|
+
|
|
25
|
+
## Optional sections
|
|
26
|
+
|
|
27
|
+
Only when they add genuine value:
|
|
28
|
+
- **Considered Options** — when rejected alternatives are worth remembering
|
|
29
|
+
- **Consequences** — when downstream effects are non-obvious
|
|
30
|
+
|
|
31
|
+
## What qualifies
|
|
32
|
+
|
|
33
|
+
- Architectural shape (monorepo, event-sourced, etc.)
|
|
34
|
+
- Technology choices with lock-in (database, message bus, auth provider)
|
|
35
|
+
- Boundary/scope decisions
|
|
36
|
+
- Deliberate deviations from the obvious path
|
|
37
|
+
- Constraints not visible in code (compliance, latency SLA)
|
|
38
|
+
|
|
39
|
+
## What doesn't qualify
|
|
40
|
+
|
|
41
|
+
- Easy-to-reverse decisions — just reverse them
|
|
42
|
+
- Obvious choices — nobody will wonder why
|
|
43
|
+
- No real alternative — nothing to record
|
|
@@ -12,6 +12,11 @@ List the relevant prior artifacts from `docs/solutions/`.
|
|
|
12
12
|
|
|
13
13
|
Define what is in and out of scope.
|
|
14
14
|
|
|
15
|
+
## Architecture decisions
|
|
16
|
+
|
|
17
|
+
List any decisions that meet the ADR threshold (see `references/adr-template.md`).
|
|
18
|
+
Link to `docs/adr/XXXX-slug.md` if created. If none qualify, state "No ADR-worthy decisions."
|
|
19
|
+
|
|
15
20
|
## Implementation units
|
|
16
21
|
|
|
17
22
|
Break the work into execution-ready units.
|
package/skills/03-work/SKILL.md
CHANGED
|
@@ -46,7 +46,7 @@ When any unexpected failure occurs during execution:
|
|
|
46
46
|
|
|
47
47
|
1. **STOP** adding features or making changes
|
|
48
48
|
2. **PRESERVE** evidence (error output, repro steps)
|
|
49
|
-
3. **DIAGNOSE** root cause —
|
|
49
|
+
3. **DIAGNOSE** root cause — follow debug discipline (`references/debug-discipline.md`): build feedback loop first, then reproduce → hypothesise → instrument → fix
|
|
50
50
|
4. **FIX** the root cause, not the symptom
|
|
51
51
|
5. **GUARD** with a regression test
|
|
52
52
|
6. **RESUME** only after verification passes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Debug Discipline
|
|
2
|
+
|
|
3
|
+
When stop-the-line triggers, follow this order. Skip phases only when explicitly justified.
|
|
4
|
+
|
|
5
|
+
## Phase 1 — Build a feedback loop
|
|
6
|
+
|
|
7
|
+
**This is the skill. Everything else is mechanical.**
|
|
8
|
+
|
|
9
|
+
If you have a fast, deterministic, agent-runnable pass/fail signal, you will find the cause.
|
|
10
|
+
If you don't, no amount of staring at code will save you. Spend disproportionate effort here.
|
|
11
|
+
|
|
12
|
+
### Strategies (try in order)
|
|
13
|
+
|
|
14
|
+
1. **Failing test** at the seam closest to the bug
|
|
15
|
+
2. **CLI invocation** with fixture input, diff against known-good output
|
|
16
|
+
3. **Curl/HTTP script** against running dev server
|
|
17
|
+
4. **Throwaway harness** — minimal subset exercising the bug path
|
|
18
|
+
5. **Bisection** — `git bisect run` between known-good and known-bad states
|
|
19
|
+
|
|
20
|
+
### Iterate on the loop itself
|
|
21
|
+
|
|
22
|
+
- Faster? (Skip unrelated setup, narrow scope)
|
|
23
|
+
- Sharper signal? (Assert the specific symptom, not "didn't crash")
|
|
24
|
+
- More deterministic? (Pin time, seed RNG, isolate filesystem)
|
|
25
|
+
|
|
26
|
+
A 2-second deterministic loop is a debugging superpower.
|
|
27
|
+
A 30-second flaky loop is barely better than no loop.
|
|
28
|
+
|
|
29
|
+
## Phase 2 — Reproduce
|
|
30
|
+
|
|
31
|
+
Confirm the loop produces the **user-described** failure, not a different nearby failure.
|
|
32
|
+
|
|
33
|
+
## Phase 3 — Hypothesise
|
|
34
|
+
|
|
35
|
+
Generate 3-5 ranked hypotheses. Each must be falsifiable:
|
|
36
|
+
|
|
37
|
+
> "If <X> is the cause, then <Y> will make the bug disappear."
|
|
38
|
+
|
|
39
|
+
Show the ranked list to the user before testing. They often have domain knowledge
|
|
40
|
+
that re-ranks instantly.
|
|
41
|
+
|
|
42
|
+
## Phase 4 — Instrument
|
|
43
|
+
|
|
44
|
+
One probe per hypothesis. Change one variable at a time.
|
|
45
|
+
|
|
46
|
+
Tag debug logs with unique prefixes (e.g. `[DEBUG-a4f2]`) so cleanup is a single grep.
|
|
47
|
+
|
|
48
|
+
## Phase 5 — Fix + regression test
|
|
49
|
+
|
|
50
|
+
Write regression test BEFORE the fix — but only if a correct seam exists.
|
|
51
|
+
If no correct seam exists, that itself is the finding. Note it.
|
|
52
|
+
|
|
53
|
+
After fix: remove all debug instrumentation (grep for tags).
|