qualia-framework 6.2.9 → 6.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/AGENTS.md +1 -0
- package/CLAUDE.md +1 -0
- package/README.md +26 -30
- package/agents/builder.md +7 -7
- package/agents/planner.md +39 -3
- package/agents/research-synthesizer.md +1 -1
- package/agents/researcher.md +3 -3
- package/agents/roadmapper.md +7 -7
- package/agents/verifier.md +18 -6
- package/agents/visual-evaluator.md +8 -7
- package/bin/cli.js +160 -16
- package/bin/command-surface.js +71 -0
- package/bin/contract-runner.js +219 -0
- package/bin/harness-eval.js +296 -0
- package/bin/host-adapters.js +66 -0
- package/bin/install.js +116 -172
- package/bin/knowledge-flush.js +21 -10
- package/bin/knowledge.js +1 -1
- package/bin/plan-contract.js +99 -2
- package/bin/planning-hygiene.js +262 -0
- package/bin/project-snapshot.js +20 -0
- package/bin/report-payload.js +18 -0
- package/bin/runtime-manifest.js +35 -0
- package/bin/state-ledger.js +184 -0
- package/bin/state.js +330 -20
- package/bin/trust-score.js +268 -0
- package/bin/work-packet.js +228 -0
- package/docs/erp-contract.md +81 -1
- package/docs/onboarding.html +4 -14
- package/guide.md +16 -16
- package/hooks/fawzi-approval-guard.js +143 -0
- package/hooks/pre-deploy-gate.js +74 -1
- package/hooks/session-start.js +29 -1
- package/package.json +1 -1
- package/qualia-design/design-rubric.md +17 -5
- package/qualia-design/frontend.md +6 -2
- package/qualia-design/graphics.md +47 -0
- package/rules/codex-goal.md +1 -1
- package/rules/command-output.md +35 -0
- package/rules/one-opinion.md +2 -2
- package/rules/speed.md +0 -1
- package/skills/qualia/SKILL.md +12 -12
- package/skills/qualia-build/SKILL.md +20 -14
- package/skills/qualia-discuss/SKILL.md +10 -10
- package/skills/qualia-doctor/SKILL.md +140 -0
- package/skills/qualia-feature/SKILL.md +24 -22
- package/skills/qualia-fix/SKILL.md +216 -0
- package/skills/qualia-handoff/SKILL.md +9 -9
- package/skills/qualia-learn/SKILL.md +11 -11
- package/skills/qualia-map/SKILL.md +2 -2
- package/skills/qualia-milestone/SKILL.md +15 -15
- package/skills/qualia-new/REFERENCE.md +9 -9
- package/skills/qualia-new/SKILL.md +14 -14
- package/skills/qualia-optimize/REFERENCE.md +1 -1
- package/skills/qualia-optimize/SKILL.md +23 -16
- package/skills/qualia-plan/SKILL.md +23 -13
- package/skills/qualia-polish/REFERENCE.md +15 -15
- package/skills/qualia-polish/SKILL.md +81 -21
- package/skills/qualia-polish/scripts/loop.mjs +3 -3
- package/skills/qualia-polish/scripts/score.mjs +9 -3
- package/skills/{qualia-vibe/scripts/extract.mjs → qualia-polish/scripts/vibe-extract.mjs} +5 -5
- package/skills/{qualia-vibe/scripts/tokens.mjs → qualia-polish/scripts/vibe-tokens.mjs} +6 -6
- package/skills/qualia-postmortem/SKILL.md +9 -9
- package/skills/qualia-report/SKILL.md +23 -23
- package/skills/qualia-research/SKILL.md +5 -5
- package/skills/qualia-review/SKILL.md +28 -12
- package/skills/qualia-road/SKILL.md +30 -22
- package/skills/qualia-ship/SKILL.md +31 -24
- package/skills/qualia-test/SKILL.md +5 -5
- package/skills/qualia-verify/SKILL.md +45 -23
- package/skills/zoho-workflow/SKILL.md +1 -1
- package/templates/help.html +11 -20
- package/tests/bin.test.sh +178 -76
- package/tests/hooks.test.sh +81 -1
- package/tests/install-smoke.test.sh +35 -5
- package/tests/lib.test.sh +432 -0
- package/tests/published-install-smoke.test.sh +4 -3
- package/tests/refs.test.sh +9 -4
- package/tests/runner.js +32 -28
- package/tests/skills.test.sh +4 -4
- package/tests/state.test.sh +133 -3
- package/skills/qualia-debug/SKILL.md +0 -185
- package/skills/qualia-flush/SKILL.md +0 -198
- package/skills/qualia-help/SKILL.md +0 -74
- package/skills/qualia-hook-gen/SKILL.md +0 -206
- package/skills/qualia-idk/SKILL.md +0 -166
- package/skills/qualia-issues/SKILL.md +0 -151
- package/skills/qualia-pause/SKILL.md +0 -68
- package/skills/qualia-resume/SKILL.md +0 -52
- package/skills/qualia-skill-new/SKILL.md +0 -173
- package/skills/qualia-triage/SKILL.md +0 -152
- package/skills/qualia-vibe/SKILL.md +0 -226
- package/skills/qualia-zoom/SKILL.md +0 -51
package/tests/refs.test.sh
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# Qualia Framework — surface-drift guard
|
|
3
3
|
# Greps every active surface for backtick-quoted /qualia-{name} command references.
|
|
4
|
-
# Asserts each name has a matching
|
|
4
|
+
# Asserts each name is in bin/command-surface.js and has a matching SKILL.md.
|
|
5
5
|
#
|
|
6
6
|
# Why: v5.7 + v5.8 removed /qualia-quick, /qualia-task, /qualia-prd, /qualia-design,
|
|
7
7
|
# /qualia-polish-loop. Three user-facing files (rules/speed.md, templates/help.html,
|
|
@@ -81,6 +81,11 @@ SCAN_FILES=$(
|
|
|
81
81
|
# We capture name + file:line so we can show context per failure.
|
|
82
82
|
declare -A SEEN_REFS
|
|
83
83
|
declare -A REF_LOCATIONS
|
|
84
|
+
declare -A ACTIVE_SKILL_MAP
|
|
85
|
+
|
|
86
|
+
while IFS= read -r skill; do
|
|
87
|
+
[ -n "$skill" ] && ACTIVE_SKILL_MAP["/$skill"]=1
|
|
88
|
+
done < <("$NODE" -e 'const {ACTIVE_SKILLS}=require(process.argv[1]); for (const s of ACTIVE_SKILLS) console.log(s)' "$FRAMEWORK_ROOT/bin/command-surface.js")
|
|
84
89
|
|
|
85
90
|
while IFS= read -r file; do
|
|
86
91
|
# Pattern A: backtick-quoted commands. Allow trailing flag/word but only capture base name.
|
|
@@ -124,11 +129,11 @@ for ref in $(printf '%s\n' "${!SEEN_REFS[@]}" | sort); do
|
|
|
124
129
|
name="${ref#/}"
|
|
125
130
|
skill_dir="$SKILLS_DIR/$name"
|
|
126
131
|
locations="${REF_LOCATIONS[$ref]}"
|
|
127
|
-
if [ -d "$skill_dir" ] && [ -f "$skill_dir/SKILL.md" ]; then
|
|
128
|
-
pass "$ref → skills/$name/SKILL.md"
|
|
132
|
+
if [ "${ACTIVE_SKILL_MAP[$ref]:-}" = "1" ] && [ -d "$skill_dir" ] && [ -f "$skill_dir/SKILL.md" ]; then
|
|
133
|
+
pass "$ref → active skills/$name/SKILL.md"
|
|
129
134
|
continue
|
|
130
135
|
fi
|
|
131
|
-
fail_case "$ref" "
|
|
136
|
+
fail_case "$ref" "not an active shipped command or missing skills/$name/SKILL.md — referenced by: $locations"
|
|
132
137
|
done
|
|
133
138
|
|
|
134
139
|
PACKAGE_VERSION="$("$NODE" -e 'console.log(require(process.argv[1]).version)' "$FRAMEWORK_ROOT/package.json" 2>/dev/null || echo "")"
|
package/tests/runner.js
CHANGED
|
@@ -212,7 +212,7 @@ describe("CLI", () => {
|
|
|
212
212
|
it("team list works", () => {
|
|
213
213
|
const r = run("cli.js", ["team", "list"]);
|
|
214
214
|
assert.equal(r.status, 0);
|
|
215
|
-
assert.match(stripAnsi(r.stdout), /QS-FAWZI-
|
|
215
|
+
assert.match(stripAnsi(r.stdout), /QS-FAWZI-11/);
|
|
216
216
|
});
|
|
217
217
|
|
|
218
218
|
it("traces handles missing traces dir", () => {
|
|
@@ -230,7 +230,7 @@ describe("CLI", () => {
|
|
|
230
230
|
try {
|
|
231
231
|
fs.mkdirSync(path.join(tmpHome, ".claude"), { recursive: true });
|
|
232
232
|
fs.writeFileSync(path.join(tmpHome, ".claude", ".qualia-config.json"), JSON.stringify({
|
|
233
|
-
code: "QS-FAWZI-
|
|
233
|
+
code: "QS-FAWZI-11",
|
|
234
234
|
installed_by: "Fawzi Goussous",
|
|
235
235
|
role: "OWNER",
|
|
236
236
|
version: "2.8.1",
|
|
@@ -1607,7 +1607,7 @@ describe("Hooks", () => {
|
|
|
1607
1607
|
try {
|
|
1608
1608
|
fs.mkdirSync(path.join(tmpHome, ".claude"), { recursive: true });
|
|
1609
1609
|
fs.writeFileSync(path.join(tmpHome, ".claude", ".qualia-config.json"), JSON.stringify({
|
|
1610
|
-
code: "QS-FAWZI-
|
|
1610
|
+
code: "QS-FAWZI-11", version: "99.99.99",
|
|
1611
1611
|
}));
|
|
1612
1612
|
const r = spawnSync(NODE, [path.join(HOOKS, "auto-update.js")], {
|
|
1613
1613
|
encoding: "utf8", timeout: 5000,
|
|
@@ -2441,7 +2441,7 @@ describe("qualia-ui.js", () => {
|
|
|
2441
2441
|
try {
|
|
2442
2442
|
fs.mkdirSync(path.join(tmpHome, ".claude"), { recursive: true });
|
|
2443
2443
|
fs.writeFileSync(path.join(tmpHome, ".claude", ".qualia-config.json"), JSON.stringify({
|
|
2444
|
-
code: "QS-FAWZI-
|
|
2444
|
+
code: "QS-FAWZI-11",
|
|
2445
2445
|
installed_by: "Fawzi Goussous",
|
|
2446
2446
|
role: "OWNER",
|
|
2447
2447
|
version: "2.8.1",
|
|
@@ -2535,7 +2535,7 @@ describe("install.js", () => {
|
|
|
2535
2535
|
it("valid code installs everything", () => {
|
|
2536
2536
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2537
2537
|
try {
|
|
2538
|
-
const r = runInstall("QS-FAWZI-
|
|
2538
|
+
const r = runInstall("QS-FAWZI-11", tmpHome);
|
|
2539
2539
|
assert.equal(r.status, 0);
|
|
2540
2540
|
assert.ok(fs.existsSync(path.join(tmpHome, ".claude", "skills", "qualia", "SKILL.md")));
|
|
2541
2541
|
assert.ok(fs.existsSync(path.join(tmpHome, ".claude", "hooks", "session-start.js")));
|
|
@@ -2556,9 +2556,9 @@ describe("install.js", () => {
|
|
|
2556
2556
|
it("config JSON has correct fields", () => {
|
|
2557
2557
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2558
2558
|
try {
|
|
2559
|
-
runInstall("QS-FAWZI-
|
|
2559
|
+
runInstall("QS-FAWZI-11", tmpHome);
|
|
2560
2560
|
const config = JSON.parse(fs.readFileSync(path.join(tmpHome, ".claude", ".qualia-config.json"), "utf8"));
|
|
2561
|
-
assert.equal(config.code, "QS-FAWZI-
|
|
2561
|
+
assert.equal(config.code, "QS-FAWZI-11");
|
|
2562
2562
|
assert.equal(config.installed_by, "Fawzi Goussous");
|
|
2563
2563
|
assert.equal(config.role, "OWNER");
|
|
2564
2564
|
} finally {
|
|
@@ -2569,7 +2569,7 @@ describe("install.js", () => {
|
|
|
2569
2569
|
it("CLAUDE.md role placeholder replaced", () => {
|
|
2570
2570
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2571
2571
|
try {
|
|
2572
|
-
runInstall("QS-FAWZI-
|
|
2572
|
+
runInstall("QS-FAWZI-11", tmpHome);
|
|
2573
2573
|
const claude = fs.readFileSync(path.join(tmpHome, ".claude", "CLAUDE.md"), "utf8");
|
|
2574
2574
|
assert.match(claude, /Role: OWNER/);
|
|
2575
2575
|
assert.doesNotMatch(claude, /\{\{ROLE\}\}/);
|
|
@@ -2578,12 +2578,13 @@ describe("install.js", () => {
|
|
|
2578
2578
|
}
|
|
2579
2579
|
});
|
|
2580
2580
|
|
|
2581
|
-
it("
|
|
2581
|
+
it("12 hooks installed (v6.2.11: proxy approval guard added; v6.2.0: pre-compact removed)", () => {
|
|
2582
2582
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2583
2583
|
try {
|
|
2584
|
-
runInstall("QS-FAWZI-
|
|
2584
|
+
runInstall("QS-FAWZI-11", tmpHome);
|
|
2585
2585
|
const hooks = fs.readdirSync(path.join(tmpHome, ".claude", "hooks")).filter(f => f.endsWith(".js"));
|
|
2586
|
-
assert.equal(hooks.length,
|
|
2586
|
+
assert.equal(hooks.length, 12, `expected 12 hooks, got ${hooks.length}: ${hooks.join(", ")}`);
|
|
2587
|
+
assert.ok(hooks.includes("fawzi-approval-guard.js"), "fawzi-approval-guard.js must be installed");
|
|
2587
2588
|
assert.ok(!hooks.includes("pre-compact.js"), "pre-compact.js must NOT be installed (removed in v6.2.0)");
|
|
2588
2589
|
} finally {
|
|
2589
2590
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
@@ -2593,7 +2594,7 @@ describe("install.js", () => {
|
|
|
2593
2594
|
it("settings.json has hooks and statusLine", () => {
|
|
2594
2595
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2595
2596
|
try {
|
|
2596
|
-
runInstall("QS-FAWZI-
|
|
2597
|
+
runInstall("QS-FAWZI-11", tmpHome);
|
|
2597
2598
|
const settings = fs.readFileSync(path.join(tmpHome, ".claude", "settings.json"), "utf8");
|
|
2598
2599
|
assert.match(settings, /SessionStart/);
|
|
2599
2600
|
assert.match(settings, /PreToolUse/);
|
|
@@ -2606,10 +2607,10 @@ describe("install.js", () => {
|
|
|
2606
2607
|
it("lowercase code is normalized", () => {
|
|
2607
2608
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2608
2609
|
try {
|
|
2609
|
-
const r = runInstall("qs-fawzi-
|
|
2610
|
+
const r = runInstall("qs-fawzi-11", tmpHome);
|
|
2610
2611
|
assert.equal(r.status, 0);
|
|
2611
2612
|
const config = JSON.parse(fs.readFileSync(path.join(tmpHome, ".claude", ".qualia-config.json"), "utf8"));
|
|
2612
|
-
assert.equal(config.code, "QS-FAWZI-
|
|
2613
|
+
assert.equal(config.code, "QS-FAWZI-11");
|
|
2613
2614
|
} finally {
|
|
2614
2615
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
2615
2616
|
}
|
|
@@ -2618,10 +2619,10 @@ describe("install.js", () => {
|
|
|
2618
2619
|
it("O/0 typo tolerance in code suffix", () => {
|
|
2619
2620
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2620
2621
|
try {
|
|
2621
|
-
const r = runInstall("QS-
|
|
2622
|
+
const r = runInstall("QS-HASAN-O2", tmpHome);
|
|
2622
2623
|
assert.equal(r.status, 0);
|
|
2623
2624
|
const config = JSON.parse(fs.readFileSync(path.join(tmpHome, ".claude", ".qualia-config.json"), "utf8"));
|
|
2624
|
-
assert.equal(config.code, "QS-
|
|
2625
|
+
assert.equal(config.code, "QS-HASAN-02");
|
|
2625
2626
|
} finally {
|
|
2626
2627
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
2627
2628
|
}
|
|
@@ -2672,10 +2673,10 @@ describe("install.js", () => {
|
|
|
2672
2673
|
it("whitespace-padded code is accepted", () => {
|
|
2673
2674
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2674
2675
|
try {
|
|
2675
|
-
const r = runInstall(" QS-FAWZI-
|
|
2676
|
+
const r = runInstall(" QS-FAWZI-11 ", tmpHome);
|
|
2676
2677
|
assert.equal(r.status, 0);
|
|
2677
2678
|
const config = JSON.parse(fs.readFileSync(path.join(tmpHome, ".claude", ".qualia-config.json"), "utf8"));
|
|
2678
|
-
assert.equal(config.code, "QS-FAWZI-
|
|
2679
|
+
assert.equal(config.code, "QS-FAWZI-11");
|
|
2679
2680
|
} finally {
|
|
2680
2681
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
2681
2682
|
}
|
|
@@ -2689,7 +2690,7 @@ describe("install.js", () => {
|
|
|
2689
2690
|
customKey: "preserved",
|
|
2690
2691
|
env: { MY_CUSTOM_VAR: "hello" },
|
|
2691
2692
|
}));
|
|
2692
|
-
const r = runInstall("QS-FAWZI-
|
|
2693
|
+
const r = runInstall("QS-FAWZI-11", tmpHome);
|
|
2693
2694
|
assert.equal(r.status, 0);
|
|
2694
2695
|
const settings = JSON.parse(fs.readFileSync(path.join(tmpHome, ".claude", "settings.json"), "utf8"));
|
|
2695
2696
|
assert.equal(settings.customKey, "preserved");
|
|
@@ -2704,7 +2705,7 @@ describe("install.js", () => {
|
|
|
2704
2705
|
it("knowledge files created on first install", () => {
|
|
2705
2706
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2706
2707
|
try {
|
|
2707
|
-
runInstall("QS-FAWZI-
|
|
2708
|
+
runInstall("QS-FAWZI-11", tmpHome);
|
|
2708
2709
|
assert.ok(fs.existsSync(path.join(tmpHome, ".claude", "knowledge", "learned-patterns.md")));
|
|
2709
2710
|
assert.ok(fs.existsSync(path.join(tmpHome, ".claude", "knowledge", "common-fixes.md")));
|
|
2710
2711
|
assert.ok(fs.existsSync(path.join(tmpHome, ".claude", "knowledge", "client-prefs.md")));
|
|
@@ -2716,10 +2717,10 @@ describe("install.js", () => {
|
|
|
2716
2717
|
it("re-install preserves user edits in knowledge files", () => {
|
|
2717
2718
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2718
2719
|
try {
|
|
2719
|
-
runInstall("QS-FAWZI-
|
|
2720
|
+
runInstall("QS-FAWZI-11", tmpHome);
|
|
2720
2721
|
fs.appendFileSync(path.join(tmpHome, ".claude", "knowledge", "learned-patterns.md"),
|
|
2721
2722
|
"\n## CUSTOM LEARNING — DO NOT OVERWRITE\n");
|
|
2722
|
-
runInstall("QS-FAWZI-
|
|
2723
|
+
runInstall("QS-FAWZI-11", tmpHome);
|
|
2723
2724
|
const content = fs.readFileSync(path.join(tmpHome, ".claude", "knowledge", "learned-patterns.md"), "utf8");
|
|
2724
2725
|
assert.match(content, /CUSTOM LEARNING/);
|
|
2725
2726
|
} finally {
|
|
@@ -2732,7 +2733,7 @@ describe("install.js", () => {
|
|
|
2732
2733
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2733
2734
|
try {
|
|
2734
2735
|
// Fresh install first, then inject a user-owned hook, then reinstall.
|
|
2735
|
-
runInstall("QS-FAWZI-
|
|
2736
|
+
runInstall("QS-FAWZI-11", tmpHome);
|
|
2736
2737
|
const settingsPath = path.join(tmpHome, ".claude", "settings.json");
|
|
2737
2738
|
const before = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
2738
2739
|
|
|
@@ -2746,7 +2747,7 @@ describe("install.js", () => {
|
|
|
2746
2747
|
before.hooks.PreToolUse = [userHook, ...(before.hooks.PreToolUse || [])];
|
|
2747
2748
|
fs.writeFileSync(settingsPath, JSON.stringify(before, null, 2));
|
|
2748
2749
|
|
|
2749
|
-
const r = runInstall("QS-FAWZI-
|
|
2750
|
+
const r = runInstall("QS-FAWZI-11", tmpHome);
|
|
2750
2751
|
assert.equal(r.status, 0);
|
|
2751
2752
|
const after = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
2752
2753
|
const allCmds = [];
|
|
@@ -2770,7 +2771,7 @@ describe("install.js", () => {
|
|
|
2770
2771
|
it("templates copied to qualia-templates/", () => {
|
|
2771
2772
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2772
2773
|
try {
|
|
2773
|
-
runInstall("QS-FAWZI-
|
|
2774
|
+
runInstall("QS-FAWZI-11", tmpHome);
|
|
2774
2775
|
const tmplDir = path.join(tmpHome, ".claude", "qualia-templates");
|
|
2775
2776
|
assert.ok(fs.existsSync(tmplDir));
|
|
2776
2777
|
const files = fs.readdirSync(tmplDir);
|
|
@@ -2783,7 +2784,7 @@ describe("install.js", () => {
|
|
|
2783
2784
|
it("agents copied", () => {
|
|
2784
2785
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2785
2786
|
try {
|
|
2786
|
-
runInstall("QS-FAWZI-
|
|
2787
|
+
runInstall("QS-FAWZI-11", tmpHome);
|
|
2787
2788
|
const agentDir = path.join(tmpHome, ".claude", "agents");
|
|
2788
2789
|
assert.ok(fs.existsSync(agentDir));
|
|
2789
2790
|
const files = fs.readdirSync(agentDir);
|
|
@@ -2796,11 +2797,14 @@ describe("install.js", () => {
|
|
|
2796
2797
|
it("rules copied", () => {
|
|
2797
2798
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2798
2799
|
try {
|
|
2799
|
-
runInstall("QS-FAWZI-
|
|
2800
|
+
runInstall("QS-FAWZI-11", tmpHome);
|
|
2800
2801
|
const rulesDir = path.join(tmpHome, ".claude", "rules");
|
|
2801
2802
|
assert.ok(fs.existsSync(rulesDir));
|
|
2802
2803
|
const files = fs.readdirSync(rulesDir);
|
|
2803
2804
|
assert.ok(files.length > 0);
|
|
2805
|
+
assert.ok(fs.existsSync(path.join(rulesDir, "command-output.md")));
|
|
2806
|
+
assert.match(fs.readFileSync(path.join(rulesDir, "command-output.md"), "utf8"), /alwaysApply: true/);
|
|
2807
|
+
assert.ok(fs.existsSync(path.join(tmpHome, ".claude", "qualia-design", "graphics.md")));
|
|
2804
2808
|
} finally {
|
|
2805
2809
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
2806
2810
|
}
|
|
@@ -2809,7 +2813,7 @@ describe("install.js", () => {
|
|
|
2809
2813
|
it("config version matches package.json", () => {
|
|
2810
2814
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2811
2815
|
try {
|
|
2812
|
-
runInstall("QS-FAWZI-
|
|
2816
|
+
runInstall("QS-FAWZI-11", tmpHome);
|
|
2813
2817
|
const config = JSON.parse(fs.readFileSync(path.join(tmpHome, ".claude", ".qualia-config.json"), "utf8"));
|
|
2814
2818
|
assert.equal(config.version, PKG_VERSION);
|
|
2815
2819
|
} finally {
|
package/tests/skills.test.sh
CHANGED
|
@@ -106,7 +106,7 @@ for skill_dir in "$SKILLS_DIR"/*/; do
|
|
|
106
106
|
|
|
107
107
|
# Cache-aware spawn audit (per rules/grounding.md):
|
|
108
108
|
# Every spawn to a CUSTOM (qualia-*) agent must anchor the prompt with
|
|
109
|
-
#
|
|
109
|
+
# `@${QUALIA_AGENTS}/{name}.md` (either `Role: @...` or `Read your role:
|
|
110
110
|
# @...` — both forms accepted). The role file is session-stable; placing
|
|
111
111
|
# it first lets Anthropic's prompt cache reuse the prefix across spawns
|
|
112
112
|
# (documented 81-90% cost reduction). If task-specific content lands
|
|
@@ -125,14 +125,14 @@ for skill_dir in "$SKILLS_DIR"/*/; do
|
|
|
125
125
|
custom_spawn_count=$((custom_spawn_count + $(grep -c 'subagent_type="qualia-' "$ref_md")))
|
|
126
126
|
fi
|
|
127
127
|
if [ "${custom_spawn_count:-0}" -gt 0 ]; then
|
|
128
|
-
role_count=$(grep -
|
|
128
|
+
role_count=$(grep -cF '@${QUALIA_AGENTS}/' "$skill_md")
|
|
129
129
|
if [ -f "$ref_md" ]; then
|
|
130
|
-
role_count=$((role_count + $(grep -
|
|
130
|
+
role_count=$((role_count + $(grep -cF '@${QUALIA_AGENTS}/' "$ref_md")))
|
|
131
131
|
fi
|
|
132
132
|
if [ "${role_count:-0}" -ge "$custom_spawn_count" ]; then
|
|
133
133
|
pass "$name: spawn audit ($custom_spawn_count custom spawn(s), all role-anchored for cache)"
|
|
134
134
|
else
|
|
135
|
-
fail_case "$name: spawn audit" "$custom_spawn_count custom spawn(s) but only ${role_count:-0} '
|
|
135
|
+
fail_case "$name: spawn audit" "$custom_spawn_count custom spawn(s) but only ${role_count:-0} '@\${QUALIA_AGENTS}/' anchors — prompt cache will miss"
|
|
136
136
|
fi
|
|
137
137
|
fi
|
|
138
138
|
done
|
package/tests/state.test.sh
CHANGED
|
@@ -6,6 +6,8 @@ PASS=0
|
|
|
6
6
|
FAIL=0
|
|
7
7
|
# Resolve STATE_JS to an ABSOLUTE path so `cd` inside subshells doesn't break it.
|
|
8
8
|
STATE_JS="$(cd "$(dirname "$0")/../bin" && pwd)/state.js"
|
|
9
|
+
FRAMEWORK_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
10
|
+
STATE_LEDGER_JS="$FRAMEWORK_DIR/bin/state-ledger.js"
|
|
9
11
|
NODE="${NODE:-node}"
|
|
10
12
|
|
|
11
13
|
# Track tmp dirs we create so we can clean them up on exit
|
|
@@ -73,6 +75,47 @@ Goal: Test goal
|
|
|
73
75
|
PLAN
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
make_valid_contract() {
|
|
79
|
+
local dir="$1"
|
|
80
|
+
local phase="${2:-1}"
|
|
81
|
+
cat > "$dir/.planning/phase-${phase}-contract.json" <<'JSON'
|
|
82
|
+
{
|
|
83
|
+
"version": 1,
|
|
84
|
+
"phase": 1,
|
|
85
|
+
"goal": "Test goal",
|
|
86
|
+
"why": "Exercise machine evidence",
|
|
87
|
+
"generated_at": "2026-05-23T00:00:00.000Z",
|
|
88
|
+
"generated_by": "manual",
|
|
89
|
+
"source_plan_hash": "",
|
|
90
|
+
"success_criteria": ["Machine check passes"],
|
|
91
|
+
"tasks": [
|
|
92
|
+
{
|
|
93
|
+
"id": "T1",
|
|
94
|
+
"title": "Machine check",
|
|
95
|
+
"wave": 1,
|
|
96
|
+
"depends_on": [],
|
|
97
|
+
"persona": "none",
|
|
98
|
+
"files_modify": [],
|
|
99
|
+
"files_create": [],
|
|
100
|
+
"files_delete": [],
|
|
101
|
+
"acceptance_criteria": ["Node command exits 0"],
|
|
102
|
+
"action": "Run deterministic evidence check",
|
|
103
|
+
"context_files": [],
|
|
104
|
+
"verification": [
|
|
105
|
+
{
|
|
106
|
+
"type": "command-exit",
|
|
107
|
+
"command": "node",
|
|
108
|
+
"args": ["-e", "process.exit(0)"],
|
|
109
|
+
"expected_exit": 0,
|
|
110
|
+
"timeout_ms": 5000
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
JSON
|
|
117
|
+
}
|
|
118
|
+
|
|
76
119
|
echo "=== state.js Behavioral Tests ==="
|
|
77
120
|
echo ""
|
|
78
121
|
|
|
@@ -98,11 +141,18 @@ INIT_EXIT=$?
|
|
|
98
141
|
if [ "$INIT_EXIT" -eq 0 ] \
|
|
99
142
|
&& [ -f "$TMP/.planning/tracking.json" ] \
|
|
100
143
|
&& [ -f "$TMP/.planning/STATE.md" ] \
|
|
144
|
+
&& [ -f "$TMP/.planning/qualia/state.jsonl" ] \
|
|
101
145
|
&& grep -q '"ok": true' /tmp/qualia-state-test.out \
|
|
102
146
|
&& grep -q '"action": "init"' /tmp/qualia-state-test.out; then
|
|
103
|
-
pass "cmdInit creates tracking.json + STATE.md"
|
|
147
|
+
pass "cmdInit creates tracking.json + STATE.md + ledger"
|
|
104
148
|
else
|
|
105
|
-
fail_case "cmdInit creates tracking.json + STATE.md" "exit=$INIT_EXIT"
|
|
149
|
+
fail_case "cmdInit creates tracking.json + STATE.md + ledger" "exit=$INIT_EXIT"
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
if (cd "$TMP" && $NODE "$STATE_LEDGER_JS" validate 2>&1 | grep -q '"ok": true'); then
|
|
153
|
+
pass "cmdInit writes valid state ledger"
|
|
154
|
+
else
|
|
155
|
+
fail_case "cmdInit state ledger validates"
|
|
106
156
|
fi
|
|
107
157
|
|
|
108
158
|
# tracking.json content sanity
|
|
@@ -175,13 +225,22 @@ EXIT=$?
|
|
|
175
225
|
if [ "$EXIT" -eq 0 ] \
|
|
176
226
|
&& echo "$OUT" | grep -q '"ok": true' \
|
|
177
227
|
&& echo "$OUT" | grep -q '"status": "built"' \
|
|
228
|
+
&& echo "$OUT" | grep -q '"ledger_event_id":' \
|
|
178
229
|
&& grep -q '"tasks_done": 5' "$TMP/.planning/tracking.json" \
|
|
179
230
|
&& grep -q '"tasks_total": 5' "$TMP/.planning/tracking.json"; then
|
|
180
|
-
pass "planned → built records tasks_done/tasks_total"
|
|
231
|
+
pass "planned → built records tasks_done/tasks_total + ledger id"
|
|
181
232
|
else
|
|
182
233
|
fail_case "planned → built" "exit=$EXIT"
|
|
183
234
|
fi
|
|
184
235
|
|
|
236
|
+
LEDGER_COUNT=$(wc -l < "$TMP/.planning/qualia/state.jsonl" | tr -d ' ')
|
|
237
|
+
if [ "$LEDGER_COUNT" -ge 3 ] \
|
|
238
|
+
&& (cd "$TMP" && $NODE "$STATE_LEDGER_JS" validate 2>&1 | grep -q '"ok": true'); then
|
|
239
|
+
pass "state ledger records init + transitions as valid chain"
|
|
240
|
+
else
|
|
241
|
+
fail_case "state ledger transition chain" "count=$LEDGER_COUNT"
|
|
242
|
+
fi
|
|
243
|
+
|
|
185
244
|
# 6. built → verified(pass) auto-advances to phase 2, resets status to setup
|
|
186
245
|
touch "$TMP/.planning/phase-1-verification.md"
|
|
187
246
|
OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification pass 2>&1)
|
|
@@ -195,6 +254,33 @@ else
|
|
|
195
254
|
fail_case "built → verified(pass) auto-advance" "exit=$EXIT out=$OUT"
|
|
196
255
|
fi
|
|
197
256
|
|
|
257
|
+
# 6b. verified(pass) refuses when a contract exists but machine evidence is missing
|
|
258
|
+
TMP=$(make_project)
|
|
259
|
+
make_valid_plan "$TMP" 1
|
|
260
|
+
make_valid_contract "$TMP" 1
|
|
261
|
+
(cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
|
|
262
|
+
(cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
|
|
263
|
+
echo "result: PASS" > "$TMP/.planning/phase-1-verification.md"
|
|
264
|
+
OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification pass 2>&1)
|
|
265
|
+
EXIT=$?
|
|
266
|
+
if [ "$EXIT" -ne 0 ] && echo "$OUT" | grep -q '"error": "MISSING_EVIDENCE"'; then
|
|
267
|
+
pass "verified(pass) refuses missing machine evidence when contract exists"
|
|
268
|
+
else
|
|
269
|
+
fail_case "verified(pass) missing evidence guard" "exit=$EXIT out=$OUT"
|
|
270
|
+
fi
|
|
271
|
+
|
|
272
|
+
# 6c. verified(pass) succeeds after contract-runner writes passing evidence
|
|
273
|
+
(cd "$TMP" && $NODE "$FRAMEWORK_DIR/bin/contract-runner.js" .planning/phase-1-contract.json >/dev/null 2>&1)
|
|
274
|
+
OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification pass 2>&1)
|
|
275
|
+
EXIT=$?
|
|
276
|
+
if [ "$EXIT" -eq 0 ] \
|
|
277
|
+
&& echo "$OUT" | grep -q '"ok": true' \
|
|
278
|
+
&& echo "$OUT" | grep -q '"phase": 2'; then
|
|
279
|
+
pass "verified(pass) accepts passing machine evidence"
|
|
280
|
+
else
|
|
281
|
+
fail_case "verified(pass) with machine evidence" "exit=$EXIT out=$OUT"
|
|
282
|
+
fi
|
|
283
|
+
|
|
198
284
|
# 7. built → verified(fail) stays on phase 1, records verification=fail
|
|
199
285
|
TMP=$(make_project)
|
|
200
286
|
make_valid_plan "$TMP" 1
|
|
@@ -618,6 +704,50 @@ else
|
|
|
618
704
|
fail_case "validate well-formed plan" "exit=$EXIT out=$OUT"
|
|
619
705
|
fi
|
|
620
706
|
|
|
707
|
+
OUT=$(cd "$TMP" && $NODE "$STATE_JS" validate-plan --phase 1 --require-contract 2>&1)
|
|
708
|
+
EXIT=$?
|
|
709
|
+
if [ "$EXIT" -eq 1 ] \
|
|
710
|
+
&& echo "$OUT" | grep -q 'JSON contract missing'; then
|
|
711
|
+
pass "validate-plan --require-contract fails when JSON contract is missing"
|
|
712
|
+
else
|
|
713
|
+
fail_case "validate-plan --require-contract missing contract" "exit=$EXIT out=$OUT"
|
|
714
|
+
fi
|
|
715
|
+
|
|
716
|
+
HASH=$(cd "$TMP" && $NODE -e "const pc=require('$FRAMEWORK_DIR/bin/plan-contract.js'); const fs=require('fs'); process.stdout.write(pc.hashPlan(fs.readFileSync('.planning/phase-1-plan.md','utf8')))")
|
|
717
|
+
cat > "$TMP/.planning/phase-1-contract.json" <<JSON
|
|
718
|
+
{
|
|
719
|
+
"version": 1,
|
|
720
|
+
"phase": 1,
|
|
721
|
+
"goal": "test",
|
|
722
|
+
"why": "validate require-contract",
|
|
723
|
+
"generated_at": "2026-05-23T00:00:00Z",
|
|
724
|
+
"generated_by": "manual",
|
|
725
|
+
"source_plan_hash": "$HASH",
|
|
726
|
+
"success_criteria": ["ok"],
|
|
727
|
+
"tasks": [{
|
|
728
|
+
"id": "T1",
|
|
729
|
+
"title": "Task",
|
|
730
|
+
"wave": 1,
|
|
731
|
+
"depends_on": [],
|
|
732
|
+
"files_modify": [],
|
|
733
|
+
"files_create": [],
|
|
734
|
+
"files_delete": [],
|
|
735
|
+
"acceptance_criteria": ["ok"],
|
|
736
|
+
"action": "Do task",
|
|
737
|
+
"context_files": [],
|
|
738
|
+
"verification": [{ "type": "file-exists", "path": ".planning/phase-1-plan.md" }]
|
|
739
|
+
}]
|
|
740
|
+
}
|
|
741
|
+
JSON
|
|
742
|
+
OUT=$(cd "$TMP" && $NODE "$STATE_JS" validate-plan --phase 1 --require-contract 2>&1)
|
|
743
|
+
EXIT=$?
|
|
744
|
+
if [ "$EXIT" -eq 0 ] \
|
|
745
|
+
&& echo "$OUT" | grep -q '"contract_status": "valid"'; then
|
|
746
|
+
pass "validate-plan --require-contract accepts valid JSON contract"
|
|
747
|
+
else
|
|
748
|
+
fail_case "validate-plan --require-contract valid contract" "exit=$EXIT out=$OUT"
|
|
749
|
+
fi
|
|
750
|
+
|
|
621
751
|
# 33. validate-plan rejects empty plan
|
|
622
752
|
TMP=$(make_project)
|
|
623
753
|
echo "" > "$TMP/.planning/phase-1-plan.md"
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: qualia-debug
|
|
3
|
-
description: "Investigative debugging — parses symptom from arguments, runs diagnostic scans, identifies root cause, applies minimal fix, writes DEBUG report. Trigger on 'debug', 'find bug', 'fix error', 'something is broken', 'not working', 'weird behavior', 'layout broken', 'CSS issue', 'slow page', 'performance'."
|
|
4
|
-
allowed-tools:
|
|
5
|
-
- Bash
|
|
6
|
-
- Read
|
|
7
|
-
- Edit
|
|
8
|
-
- Write
|
|
9
|
-
- Grep
|
|
10
|
-
- Glob
|
|
11
|
-
- Agent
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
# /qualia-debug — Investigative Debugging (one-shot)
|
|
15
|
-
|
|
16
|
-
Parse the symptom. Run diagnostics. Find root cause. Apply minimal fix. Write report. **One-shot — no mandatory user questions.**
|
|
17
|
-
|
|
18
|
-
## Usage
|
|
19
|
-
|
|
20
|
-
- `/qualia-debug {symptom}` — investigate a specific symptom
|
|
21
|
-
- `/qualia-debug` — no symptom given: investigate recently-changed files for obvious bugs
|
|
22
|
-
- `/qualia-debug --frontend {symptom}` — layout/z-index/overflow bias
|
|
23
|
-
- `/qualia-debug --perf {symptom}` — performance bias
|
|
24
|
-
|
|
25
|
-
## Tool Budget
|
|
26
|
-
|
|
27
|
-
Max 10 Read/Grep/Bash calls for investigation. If you haven't narrowed to root cause in 10, return `INSUFFICIENT EVIDENCE after 10 steps. Narrowed to: {files}. Recommend: {next diagnostic}.` Do not keep guessing.
|
|
28
|
-
|
|
29
|
-
## Process
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
node ~/.claude/bin/qualia-ui.js banner debug
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### 1. Parse Symptom from $ARGUMENTS
|
|
36
|
-
|
|
37
|
-
- If arguments provided → that's the symptom. Extract: what's broken, where (file/page/feature), when (on click? on load? after change?).
|
|
38
|
-
- If arguments empty → run `git diff HEAD~3 --stat` to find recently-touched files. Treat those as the suspect set. Symptom = "something in recent changes".
|
|
39
|
-
|
|
40
|
-
### 2. Check Known Fixes First (cheap)
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
node ~/.claude/bin/knowledge.js search "{symptom_keywords}"
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
If a known fix matches, apply it and jump to step 5 (verify). Known fixes are pre-verified patterns — no need to re-investigate.
|
|
47
|
-
|
|
48
|
-
### 3. Diagnostic Scan
|
|
49
|
-
|
|
50
|
-
Run the scan matching the symptom type. All commands in a scan block run as parallel Bash calls in a single response turn.
|
|
51
|
-
|
|
52
|
-
**General mode (default):**
|
|
53
|
-
```bash
|
|
54
|
-
# Compile errors
|
|
55
|
-
npx tsc --noEmit 2>&1 | grep "error TS" | head -20
|
|
56
|
-
|
|
57
|
-
# Empty catch / swallowed errors
|
|
58
|
-
grep -rn "catch\s*{}\|catch\s*(.*)\s*{\s*}" --include="*.ts" --include="*.tsx" app/ components/ src/ lib/ 2>/dev/null | head -10
|
|
59
|
-
|
|
60
|
-
# Recent console.error or thrown errors
|
|
61
|
-
grep -rn "console\.error\|throw new" --include="*.ts" --include="*.tsx" app/ components/ src/ lib/ 2>/dev/null | head -10
|
|
62
|
-
|
|
63
|
-
# Broken imports
|
|
64
|
-
npx tsc --noEmit 2>&1 | grep -i "Cannot find module\|has no exported"
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
**Frontend mode (`--frontend`):**
|
|
68
|
-
```bash
|
|
69
|
-
# Stacking context audit (z-index issues)
|
|
70
|
-
grep -rn "z-index\|z-\[" --include="*.tsx" --include="*.css" app/ components/ src/ 2>/dev/null | head -20
|
|
71
|
-
|
|
72
|
-
# Overflow candidates (horizontal scroll, clipping)
|
|
73
|
-
grep -rn "100vw\|overflow.*hidden\|overflow-x\|position.*fixed" --include="*.tsx" --include="*.css" app/ components/ src/ 2>/dev/null | head -15
|
|
74
|
-
|
|
75
|
-
# Fixed dimensions breaking mobile
|
|
76
|
-
grep -rn "width:.*[0-9]\+px\|height:.*[0-9]\+px\|w-\[[0-9]\+px\|h-\[[0-9]\+px" --include="*.tsx" --include="*.css" app/ components/ src/ 2>/dev/null | grep -v "min-\|max-" | head -10
|
|
77
|
-
|
|
78
|
-
# Flex/grid blowout candidates
|
|
79
|
-
grep -rn "flex\|grid" --include="*.tsx" app/ components/ src/ 2>/dev/null | grep -v "min-w-0\|minmax(0" | wc -l
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
**Perf mode (`--perf`):**
|
|
83
|
-
```bash
|
|
84
|
-
# Sequential awaits that should be Promise.all
|
|
85
|
-
grep -rn "const.*=.*await" --include="*.tsx" --include="*.ts" app/ src/ 2>/dev/null | grep -v "Promise.all\|Promise.allSettled" | head -15
|
|
86
|
-
|
|
87
|
-
# Large files
|
|
88
|
-
find app/ components/ src/ -name "*.tsx" -o -name "*.ts" 2>/dev/null | xargs wc -l 2>/dev/null | sort -rn | head -10
|
|
89
|
-
|
|
90
|
-
# Missing next/image
|
|
91
|
-
grep -rn "<img " --include="*.tsx" --include="*.jsx" app/ components/ src/ 2>/dev/null | grep -v "next/image" | wc -l
|
|
92
|
-
|
|
93
|
-
# No dynamic imports (possible big bundles)
|
|
94
|
-
grep -rn "import(\|next/dynamic" --include="*.tsx" --include="*.ts" app/ src/ 2>/dev/null | wc -l
|
|
95
|
-
|
|
96
|
-
# Client-boundary usage
|
|
97
|
-
grep -rln "'use client'" --include="*.tsx" app/ components/ src/ 2>/dev/null | wc -l
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### 4. Form Hypothesis + Apply Minimal Fix
|
|
101
|
-
|
|
102
|
-
From the diagnostic output, state the root cause in one sentence with `file:line` citation. No hedging — either you have evidence or you write `INSUFFICIENT EVIDENCE` and return (step 6).
|
|
103
|
-
|
|
104
|
-
Apply the minimal fix:
|
|
105
|
-
- Only edit files whose contents caused the symptom
|
|
106
|
-
- One concept per commit — don't fold in cleanup
|
|
107
|
-
- Don't refactor adjacent code
|
|
108
|
-
- If the fix touches > 3 files, stop and ask the user first (major refactor disguised as a debug)
|
|
109
|
-
|
|
110
|
-
### 5. Verify Fix
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
# TypeScript still compiles?
|
|
114
|
-
npx tsc --noEmit 2>&1 | grep -c "error TS" # Expect 0
|
|
115
|
-
|
|
116
|
-
# Symptom reproduction — ideally a grep that would have matched the bug
|
|
117
|
-
# and now returns empty:
|
|
118
|
-
grep -rn "{pattern that represented the bug}" {scope} 2>/dev/null
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
If the verification fails, revert and return to step 3 with narrower hypothesis.
|
|
122
|
-
|
|
123
|
-
### 6. Write DEBUG Report
|
|
124
|
-
|
|
125
|
-
Write to `.planning/DEBUG-{YYYY-MM-DD-HHMM}.md`:
|
|
126
|
-
|
|
127
|
-
```markdown
|
|
128
|
-
# Debug Report — {YYYY-MM-DD HH:MM}
|
|
129
|
-
|
|
130
|
-
**Symptom:** {user description or "recent changes" if no args}
|
|
131
|
-
**Mode:** general | frontend | perf
|
|
132
|
-
**Tool calls used:** {N}/10
|
|
133
|
-
|
|
134
|
-
## Investigation
|
|
135
|
-
- Diagnostic scans run: {list}
|
|
136
|
-
- Files examined: {list}
|
|
137
|
-
- Patterns searched: {list}
|
|
138
|
-
|
|
139
|
-
## Root Cause
|
|
140
|
-
{file:line} — "{quoted problematic code}" — {explanation of why it caused the symptom}
|
|
141
|
-
|
|
142
|
-
## Fix Applied
|
|
143
|
-
- Files: {list}
|
|
144
|
-
- Diff summary: {one paragraph}
|
|
145
|
-
- Verification: {commands run + results}
|
|
146
|
-
|
|
147
|
-
## Related Observations
|
|
148
|
-
- {any adjacent issues noticed but NOT fixed in this debug pass}
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### 7. Commit
|
|
152
|
-
|
|
153
|
-
```bash
|
|
154
|
-
git add {specific files you changed}
|
|
155
|
-
git commit -m "fix: {what was broken and why}"
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
## INSUFFICIENT EVIDENCE Return
|
|
159
|
-
|
|
160
|
-
If you exhaust the 10-call budget without a confident root cause:
|
|
161
|
-
|
|
162
|
-
```markdown
|
|
163
|
-
# Debug Report — {YYYY-MM-DD HH:MM}
|
|
164
|
-
|
|
165
|
-
**Symptom:** {description}
|
|
166
|
-
**Outcome:** INSUFFICIENT EVIDENCE after 10 inspection steps
|
|
167
|
-
|
|
168
|
-
## Narrowed To
|
|
169
|
-
- Files examined: {list}
|
|
170
|
-
- Ruled out: {list}
|
|
171
|
-
- Remaining suspects: {list}
|
|
172
|
-
|
|
173
|
-
## Recommended Next Diagnostic
|
|
174
|
-
- {specific next step for the user — e.g., "run `npm run dev` and watch browser console for the specific error", or "add console.log at file:line and reproduce"}
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
Do NOT apply a speculative fix. Return the report and stop.
|
|
178
|
-
|
|
179
|
-
## Rules
|
|
180
|
-
|
|
181
|
-
- **No mandatory questions.** This is one-shot. If symptom args are missing, investigate recent changes.
|
|
182
|
-
- **Root cause or INSUFFICIENT EVIDENCE** — no "probably" fixes.
|
|
183
|
-
- **Minimal fix only.** One concept, one commit. No refactors dressed as debug.
|
|
184
|
-
- **Tool budget is hard.** 10 calls, then stop.
|
|
185
|
-
- **Every fix gets a DEBUG report in .planning/** — creates a searchable record.
|