qualia-framework 6.9.2 → 6.22.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 +8 -5
- package/CHANGELOG.md +208 -0
- package/CLAUDE.md +3 -1
- package/agents/roadmapper.md +16 -14
- package/agents/verifier.md +1 -1
- package/bin/agent-status.js +264 -0
- package/bin/analyze-gate.js +318 -0
- package/bin/branch-hygiene.js +135 -0
- package/bin/command-surface.js +2 -0
- package/bin/compile-instructions.js +82 -0
- package/bin/eval-runner.js +218 -0
- package/bin/host-adapters.js +72 -12
- package/bin/install.js +27 -17
- package/bin/last-report.js +207 -0
- package/bin/project-sync.js +315 -0
- package/bin/report-payload.js +7 -0
- package/bin/runtime-manifest.js +8 -0
- package/bin/state.js +257 -12
- package/bin/verify-panel.js +294 -0
- package/bin/wave-plan.js +211 -0
- package/docs/EMPLOYEE-QUICKSTART.md +3 -3
- package/docs/erp-contract.md +168 -0
- package/docs/qualia-manual.html +5 -5
- package/hooks/branch-guard.js +133 -63
- package/hooks/pre-deploy-gate.js +38 -0
- package/hooks/task-write-guard.js +165 -0
- package/package.json +3 -2
- package/rules/codex-goal.md +28 -26
- package/rules/infrastructure.md +1 -1
- package/skills/qualia/SKILL.md +6 -0
- package/skills/qualia-build/SKILL.md +39 -7
- package/skills/qualia-eval/SKILL.md +83 -0
- package/skills/qualia-feature/SKILL.md +20 -4
- package/skills/qualia-fix/SKILL.md +13 -1
- package/skills/qualia-milestone/SKILL.md +12 -6
- package/skills/qualia-new/REFERENCE.md +6 -4
- package/skills/qualia-new/SKILL.md +27 -15
- package/skills/qualia-plan/SKILL.md +2 -2
- package/skills/qualia-report/SKILL.md +10 -0
- package/skills/qualia-scope/SKILL.md +3 -3
- package/skills/qualia-ship/SKILL.md +37 -4
- package/skills/qualia-update/SKILL.md +100 -0
- package/skills/qualia-verify/SKILL.md +51 -24
- package/templates/instructions.md +32 -0
- package/templates/journey.md +2 -2
- package/templates/project-discovery.md +30 -23
- package/templates/requirements.md +7 -7
- package/tests/agent-status.test.sh +153 -0
- package/tests/analyze-gate.test.sh +170 -0
- package/tests/bin.test.sh +5 -4
- package/tests/branch-hygiene.test.sh +93 -0
- package/tests/eval-runner.test.sh +147 -0
- package/tests/hooks.test.sh +218 -17
- package/tests/install-smoke.test.sh +4 -3
- package/tests/instructions.test.sh +109 -0
- package/tests/last-report.test.sh +156 -0
- package/tests/lib.test.sh +2 -2
- package/tests/project-sync.test.sh +175 -0
- package/tests/run-all.sh +9 -0
- package/tests/runner.js +3 -2
- package/tests/state.test.sh +187 -0
- package/tests/verify-panel.test.sh +162 -0
- package/tests/wave-plan.test.sh +153 -0
- package/skills/qualia-discuss/SKILL.md +0 -222
package/tests/hooks.test.sh
CHANGED
|
@@ -96,29 +96,78 @@ TMP=$(setup_guard_repo main OWNER)
|
|
|
96
96
|
assert_exit "OWNER on main → allowed" 0 $?
|
|
97
97
|
rm -rf "$TMP"
|
|
98
98
|
|
|
99
|
-
# EMPLOYEE on main →
|
|
99
|
+
# EMPLOYEE on main → ALLOWED + counted + notice (v6.10 policy: accountability, not block)
|
|
100
100
|
TMP=$(setup_guard_repo main EMPLOYEE)
|
|
101
101
|
OUT=$(cd "$TMP/proj" && HOME="$TMP" $NODE "$HOOKS_DIR/branch-guard.js" 2>&1)
|
|
102
102
|
RC=$?
|
|
103
|
-
if [ "$RC" -eq
|
|
104
|
-
|
|
103
|
+
if [ "$RC" -eq 0 ] \
|
|
104
|
+
&& echo "$OUT" | grep -q "NOTICE" \
|
|
105
|
+
&& grep -q "employee_main_push" "$TMP/.claude/.main-push-events.json" 2>/dev/null \
|
|
106
|
+
&& grep -q '"total": 1' "$TMP/.claude/.main-push-events.json" 2>/dev/null; then
|
|
107
|
+
echo " ✓ EMPLOYEE on main → allowed + recorded + notice"
|
|
108
|
+
PASS=$((PASS + 1))
|
|
109
|
+
else
|
|
110
|
+
echo " ✗ EMPLOYEE on main → allowed+recorded (exit=$RC out=$OUT)"
|
|
111
|
+
FAIL=$((FAIL + 1))
|
|
112
|
+
fi
|
|
113
|
+
rm -rf "$TMP"
|
|
114
|
+
|
|
115
|
+
# EMPLOYEE repeated main push → per-employee count increments (framework counts)
|
|
116
|
+
TMP=$(setup_guard_repo main EMPLOYEE)
|
|
117
|
+
(cd "$TMP/proj" && HOME="$TMP" $NODE "$HOOKS_DIR/branch-guard.js" >/dev/null 2>&1)
|
|
118
|
+
(cd "$TMP/proj" && HOME="$TMP" $NODE "$HOOKS_DIR/branch-guard.js" >/dev/null 2>&1)
|
|
119
|
+
if grep -q '"total": 2' "$TMP/.claude/.main-push-events.json" 2>/dev/null; then
|
|
120
|
+
echo " ✓ EMPLOYEE repeated main push → count increments to 2"
|
|
121
|
+
PASS=$((PASS + 1))
|
|
122
|
+
else
|
|
123
|
+
echo " ✗ EMPLOYEE repeated main push → count increments"
|
|
124
|
+
FAIL=$((FAIL + 1))
|
|
125
|
+
fi
|
|
126
|
+
rm -rf "$TMP"
|
|
127
|
+
|
|
128
|
+
# EMPLOYEE refspec push to main from a feature branch → recorded
|
|
129
|
+
TMP=$(setup_guard_repo feature/xyz EMPLOYEE)
|
|
130
|
+
OUT=$(cd "$TMP/proj" && printf '%s' '{"tool_input":{"command":"git push origin feature/xyz:main"}}' | HOME="$TMP" $NODE "$HOOKS_DIR/branch-guard.js" 2>&1)
|
|
131
|
+
RC=$?
|
|
132
|
+
if [ "$RC" -eq 0 ] && grep -q "employee_main_push" "$TMP/.claude/.main-push-events.json" 2>/dev/null; then
|
|
133
|
+
echo " ✓ EMPLOYEE refspec push to :main → allowed + recorded"
|
|
105
134
|
PASS=$((PASS + 1))
|
|
106
135
|
else
|
|
107
|
-
echo " ✗ EMPLOYEE
|
|
136
|
+
echo " ✗ EMPLOYEE refspec push to :main → recorded (exit=$RC)"
|
|
108
137
|
FAIL=$((FAIL + 1))
|
|
109
138
|
fi
|
|
110
139
|
rm -rf "$TMP"
|
|
111
140
|
|
|
112
|
-
# EMPLOYEE on master →
|
|
141
|
+
# EMPLOYEE on master → allowed + recorded
|
|
113
142
|
TMP=$(setup_guard_repo master EMPLOYEE)
|
|
114
143
|
(cd "$TMP/proj" && HOME="$TMP" $NODE "$HOOKS_DIR/branch-guard.js" >/dev/null 2>&1)
|
|
115
|
-
assert_exit "EMPLOYEE on master →
|
|
144
|
+
assert_exit "EMPLOYEE on master → allowed (recorded)" 0 $?
|
|
116
145
|
rm -rf "$TMP"
|
|
117
146
|
|
|
118
|
-
# EMPLOYEE on feature branch → allowed
|
|
147
|
+
# EMPLOYEE on feature branch → allowed, NOT recorded
|
|
119
148
|
TMP=$(setup_guard_repo feature/xyz EMPLOYEE)
|
|
120
149
|
(cd "$TMP/proj" && HOME="$TMP" $NODE "$HOOKS_DIR/branch-guard.js" >/dev/null 2>&1)
|
|
121
|
-
|
|
150
|
+
RC=$?
|
|
151
|
+
if [ "$RC" -eq 0 ] && [ ! -f "$TMP/.claude/.main-push-events.json" ]; then
|
|
152
|
+
echo " ✓ EMPLOYEE on feature/xyz → allowed, not recorded"
|
|
153
|
+
PASS=$((PASS + 1))
|
|
154
|
+
else
|
|
155
|
+
echo " ✗ EMPLOYEE on feature/xyz → allowed/not-recorded (exit=$RC)"
|
|
156
|
+
FAIL=$((FAIL + 1))
|
|
157
|
+
fi
|
|
158
|
+
rm -rf "$TMP"
|
|
159
|
+
|
|
160
|
+
# OWNER on main → allowed, NOT recorded (OWNER pushes are unremarkable)
|
|
161
|
+
TMP=$(setup_guard_repo main OWNER)
|
|
162
|
+
(cd "$TMP/proj" && HOME="$TMP" $NODE "$HOOKS_DIR/branch-guard.js" >/dev/null 2>&1)
|
|
163
|
+
RC=$?
|
|
164
|
+
if [ "$RC" -eq 0 ] && [ ! -f "$TMP/.claude/.main-push-events.json" ]; then
|
|
165
|
+
echo " ✓ OWNER on main → allowed, not recorded"
|
|
166
|
+
PASS=$((PASS + 1))
|
|
167
|
+
else
|
|
168
|
+
echo " ✗ OWNER on main → allowed/not-recorded (exit=$RC)"
|
|
169
|
+
FAIL=$((FAIL + 1))
|
|
170
|
+
fi
|
|
122
171
|
rm -rf "$TMP"
|
|
123
172
|
|
|
124
173
|
# OWNER on feature branch → allowed
|
|
@@ -127,31 +176,38 @@ TMP=$(setup_guard_repo feature/xyz OWNER)
|
|
|
127
176
|
assert_exit "OWNER on feature/xyz → allowed" 0 $?
|
|
128
177
|
rm -rf "$TMP"
|
|
129
178
|
|
|
130
|
-
# Missing config →
|
|
179
|
+
# Missing config → allowed (the hook never blocks now)
|
|
131
180
|
TMP=$(mktemp -d)
|
|
132
181
|
mkdir -p "$TMP/proj"
|
|
133
|
-
(cd "$TMP/proj" && git init -q && git checkout -b
|
|
182
|
+
(cd "$TMP/proj" && git init -q && git checkout -b main -q 2>/dev/null)
|
|
134
183
|
# NO .claude/.qualia-config.json
|
|
135
184
|
(cd "$TMP/proj" && HOME="$TMP" $NODE "$HOOKS_DIR/branch-guard.js" >/dev/null 2>&1)
|
|
136
|
-
assert_exit "missing config →
|
|
185
|
+
assert_exit "missing config → allowed (never blocks)" 0 $?
|
|
137
186
|
rm -rf "$TMP"
|
|
138
187
|
|
|
139
|
-
# Malformed config JSON →
|
|
188
|
+
# Malformed config JSON → allowed
|
|
140
189
|
TMP=$(mktemp -d)
|
|
141
190
|
mkdir -p "$TMP/proj" "$TMP/.claude"
|
|
142
|
-
(cd "$TMP/proj" && git init -q && git checkout -b
|
|
191
|
+
(cd "$TMP/proj" && git init -q && git checkout -b main -q 2>/dev/null)
|
|
143
192
|
echo 'not json{' > "$TMP/.claude/.qualia-config.json"
|
|
144
193
|
(cd "$TMP/proj" && HOME="$TMP" $NODE "$HOOKS_DIR/branch-guard.js" >/dev/null 2>&1)
|
|
145
|
-
assert_exit "malformed config JSON →
|
|
194
|
+
assert_exit "malformed config JSON → allowed" 0 $?
|
|
146
195
|
rm -rf "$TMP"
|
|
147
196
|
|
|
148
|
-
# Empty role field →
|
|
197
|
+
# Empty role field → allowed, not recorded (not a known EMPLOYEE)
|
|
149
198
|
TMP=$(mktemp -d)
|
|
150
199
|
mkdir -p "$TMP/proj" "$TMP/.claude"
|
|
151
|
-
(cd "$TMP/proj" && git init -q && git checkout -b
|
|
200
|
+
(cd "$TMP/proj" && git init -q && git checkout -b main -q 2>/dev/null)
|
|
152
201
|
echo '{"role":""}' > "$TMP/.claude/.qualia-config.json"
|
|
153
202
|
(cd "$TMP/proj" && HOME="$TMP" $NODE "$HOOKS_DIR/branch-guard.js" >/dev/null 2>&1)
|
|
154
|
-
|
|
203
|
+
RC=$?
|
|
204
|
+
if [ "$RC" -eq 0 ] && [ ! -f "$TMP/.claude/.main-push-events.json" ]; then
|
|
205
|
+
echo " ✓ empty role → allowed, not recorded"
|
|
206
|
+
PASS=$((PASS + 1))
|
|
207
|
+
else
|
|
208
|
+
echo " ✗ empty role → allowed/not-recorded (exit=$RC)"
|
|
209
|
+
FAIL=$((FAIL + 1))
|
|
210
|
+
fi
|
|
155
211
|
rm -rf "$TMP"
|
|
156
212
|
|
|
157
213
|
# --- fawzi-approval-guard.js ---
|
|
@@ -382,6 +438,72 @@ RC=$?
|
|
|
382
438
|
assert_exit "regular page.tsx with service_role → blocked (exit 2)" 2 $RC
|
|
383
439
|
rm -rf "$TMP"
|
|
384
440
|
|
|
441
|
+
# --- pre-deploy-gate: anti-slop gate (slop-detect.mjs wired into ship) ---
|
|
442
|
+
# Build a tmp QUALIA_HOME carrying bin/slop-detect.mjs so the gate can find it.
|
|
443
|
+
SLOP_SRC="$(cd "$(dirname "$0")/../bin" && pwd)/slop-detect.mjs"
|
|
444
|
+
|
|
445
|
+
# Purple-blue gradient (CRITICAL design tell) → blocked with anti-slop diagnostic
|
|
446
|
+
TMP=$(mktemp -d)
|
|
447
|
+
QH=$(mktemp -d)
|
|
448
|
+
mkdir -p "$QH/bin" "$TMP/app"
|
|
449
|
+
cp "$SLOP_SRC" "$QH/bin/slop-detect.mjs"
|
|
450
|
+
echo 'export default function P(){return <div className="bg-gradient-to-r from-blue-500 to-purple-600">hi</div>;}' > "$TMP/app/page.tsx"
|
|
451
|
+
OUT=$( (cd "$TMP" && QUALIA_HOME="$QH" $NODE "$HOOKS_DIR/pre-deploy-gate.js") 2>&1 )
|
|
452
|
+
RC=$?
|
|
453
|
+
if [ "$RC" -eq 2 ] && echo "$OUT" | grep -qi "anti-slop"; then
|
|
454
|
+
echo " ✓ purple-blue gradient → blocked by anti-slop gate (exit 2)"
|
|
455
|
+
PASS=$((PASS + 1))
|
|
456
|
+
else
|
|
457
|
+
echo " ✗ purple-blue gradient → anti-slop block (exit=$RC out=$OUT)"
|
|
458
|
+
FAIL=$((FAIL + 1))
|
|
459
|
+
fi
|
|
460
|
+
rm -rf "$TMP" "$QH"
|
|
461
|
+
|
|
462
|
+
# Clean frontend file → anti-slop passes → exit 0 with "Anti-slop" check shown
|
|
463
|
+
TMP=$(mktemp -d)
|
|
464
|
+
QH=$(mktemp -d)
|
|
465
|
+
mkdir -p "$QH/bin" "$TMP/app"
|
|
466
|
+
cp "$SLOP_SRC" "$QH/bin/slop-detect.mjs"
|
|
467
|
+
echo 'export default function P(){return <div className="bg-stone-50 text-ink">hi</div>;}' > "$TMP/app/page.tsx"
|
|
468
|
+
OUT=$( (cd "$TMP" && QUALIA_HOME="$QH" $NODE "$HOOKS_DIR/pre-deploy-gate.js") 2>&1 )
|
|
469
|
+
RC=$?
|
|
470
|
+
if [ "$RC" -eq 0 ] && echo "$OUT" | grep -q "Anti-slop"; then
|
|
471
|
+
echo " ✓ clean frontend → anti-slop passes (exit 0)"
|
|
472
|
+
PASS=$((PASS + 1))
|
|
473
|
+
else
|
|
474
|
+
echo " ✗ clean frontend → anti-slop passes (exit=$RC out=$OUT)"
|
|
475
|
+
FAIL=$((FAIL + 1))
|
|
476
|
+
fi
|
|
477
|
+
rm -rf "$TMP" "$QH"
|
|
478
|
+
|
|
479
|
+
# QUALIA_SKIP_SLOP=1 by non-OWNER → blocked (OWNER-only escape)
|
|
480
|
+
TMP=$(mktemp -d)
|
|
481
|
+
QH=$(mktemp -d)
|
|
482
|
+
mkdir -p "$QH/bin" "$TMP/app"
|
|
483
|
+
cp "$SLOP_SRC" "$QH/bin/slop-detect.mjs"
|
|
484
|
+
echo '{"role":"EMPLOYEE"}' > "$QH/.qualia-config.json"
|
|
485
|
+
echo 'export default function P(){return <div className="bg-gradient-to-r from-blue-500 to-purple-600">x</div>;}' > "$TMP/app/page.tsx"
|
|
486
|
+
OUT=$( (cd "$TMP" && QUALIA_SKIP_SLOP=1 QUALIA_HOME="$QH" $NODE "$HOOKS_DIR/pre-deploy-gate.js") 2>&1 )
|
|
487
|
+
RC=$?
|
|
488
|
+
if [ "$RC" -eq 2 ] && echo "$OUT" | grep -q "OWNER-only"; then
|
|
489
|
+
echo " ✓ QUALIA_SKIP_SLOP by non-OWNER → blocked (OWNER-only)"
|
|
490
|
+
PASS=$((PASS + 1))
|
|
491
|
+
else
|
|
492
|
+
echo " ✗ QUALIA_SKIP_SLOP non-OWNER block (exit=$RC out=$OUT)"
|
|
493
|
+
FAIL=$((FAIL + 1))
|
|
494
|
+
fi
|
|
495
|
+
rm -rf "$TMP" "$QH"
|
|
496
|
+
|
|
497
|
+
# Scanner absent (older install) → gate skips silently → exit 0
|
|
498
|
+
TMP=$(mktemp -d)
|
|
499
|
+
QH=$(mktemp -d)
|
|
500
|
+
mkdir -p "$TMP/app"
|
|
501
|
+
echo 'export default function P(){return <div className="bg-gradient-to-r from-blue-500 to-purple-600">x</div>;}' > "$TMP/app/page.tsx"
|
|
502
|
+
OUT=$( (cd "$TMP" && QUALIA_HOME="$QH" $NODE "$HOOKS_DIR/pre-deploy-gate.js") 2>&1 )
|
|
503
|
+
RC=$?
|
|
504
|
+
assert_exit "anti-slop gate skips when scanner absent (exit 0)" 0 $RC
|
|
505
|
+
rm -rf "$TMP" "$QH"
|
|
506
|
+
|
|
385
507
|
# --- session-start.js — must exit 0 always ---
|
|
386
508
|
echo ""
|
|
387
509
|
echo "session-start:"
|
|
@@ -595,6 +717,85 @@ else
|
|
|
595
717
|
fi
|
|
596
718
|
rm -rf "$TMP"
|
|
597
719
|
|
|
720
|
+
# --- task-write-guard.js (R1 — runtime plan-contract file-scope guard) ---
|
|
721
|
+
echo ""
|
|
722
|
+
echo "task-write-guard:"
|
|
723
|
+
|
|
724
|
+
AS_BIN="$(cd "$(dirname "$0")/../bin" && pwd)/agent-status.js"
|
|
725
|
+
|
|
726
|
+
# setup_twg_project → tmp dir with a phase-1 contract (T1 declares src/a.ts +
|
|
727
|
+
# src/b.ts) and an active RUNNING status for T1. Prints the dir; caller rm -rf.
|
|
728
|
+
setup_twg_project() {
|
|
729
|
+
local tmp
|
|
730
|
+
tmp=$(mktemp -d)
|
|
731
|
+
mkdir -p "$tmp/.planning"
|
|
732
|
+
cat > "$tmp/.planning/phase-1-contract.json" <<'EOF'
|
|
733
|
+
{
|
|
734
|
+
"version": 1,
|
|
735
|
+
"phase": 1,
|
|
736
|
+
"tasks": [
|
|
737
|
+
{ "id": "T1", "wave": 1, "files_modify": ["src/a.ts"], "files_create": ["src/b.ts"] }
|
|
738
|
+
]
|
|
739
|
+
}
|
|
740
|
+
EOF
|
|
741
|
+
echo "$tmp"
|
|
742
|
+
}
|
|
743
|
+
twg() { echo "{\"tool_input\":{\"file_path\":\"$2\"}}" | (cd "$1" && $NODE "$HOOKS_DIR/task-write-guard.js" >/dev/null 2>&1); }
|
|
744
|
+
|
|
745
|
+
# No active build → guard is a no-op even on an undeclared file
|
|
746
|
+
TMP=$(setup_twg_project)
|
|
747
|
+
twg "$TMP" "src/evil.ts"
|
|
748
|
+
assert_exit "no active build → allowed (no-op)" 0 $?
|
|
749
|
+
rm -rf "$TMP"
|
|
750
|
+
|
|
751
|
+
# Active build (T1 RUNNING) + write to a DECLARED file → allowed
|
|
752
|
+
TMP=$(setup_twg_project)
|
|
753
|
+
$NODE "$AS_BIN" write T1 RUNNING --phase 1 --wave 1 --cwd "$TMP" >/dev/null 2>&1
|
|
754
|
+
twg "$TMP" "src/a.ts"
|
|
755
|
+
assert_exit "active build + declared file → allowed" 0 $?
|
|
756
|
+
|
|
757
|
+
# Active build + write to an UNDECLARED file → blocked (exit 2)
|
|
758
|
+
OUT=$(echo '{"tool_input":{"file_path":"src/evil.ts"}}' | (cd "$TMP" && $NODE "$HOOKS_DIR/task-write-guard.js" 2>&1))
|
|
759
|
+
RC=$?
|
|
760
|
+
if [ "$RC" -eq 2 ] && echo "$OUT" | grep -q "task-write-guard" && echo "$OUT" | grep -q "evil.ts"; then
|
|
761
|
+
echo " ✓ active build + undeclared file → blocked with diagnostic"
|
|
762
|
+
PASS=$((PASS + 1))
|
|
763
|
+
else
|
|
764
|
+
echo " ✗ active build + undeclared file → blocked (exit=$RC out=$OUT)"
|
|
765
|
+
FAIL=$((FAIL + 1))
|
|
766
|
+
fi
|
|
767
|
+
|
|
768
|
+
# files_create entries count as declared
|
|
769
|
+
twg "$TMP" "src/b.ts"
|
|
770
|
+
assert_exit "active build + declared files_create → allowed" 0 $?
|
|
771
|
+
|
|
772
|
+
# .planning/ and .agent-status/ paths are always writable during a build
|
|
773
|
+
twg "$TMP" ".planning/phase-1-verification.md"
|
|
774
|
+
assert_exit "active build + .planning path → allowed" 0 $?
|
|
775
|
+
twg "$TMP" ".agent-status/T1.json"
|
|
776
|
+
assert_exit "active build + .agent-status path → allowed" 0 $?
|
|
777
|
+
|
|
778
|
+
# OWNER escape hatch overrides the block
|
|
779
|
+
echo '{"tool_input":{"file_path":"src/evil.ts"}}' | (cd "$TMP" && QUALIA_ALLOW_OUTSIDE_CONTRACT=1 $NODE "$HOOKS_DIR/task-write-guard.js" >/dev/null 2>&1)
|
|
780
|
+
assert_exit "QUALIA_ALLOW_OUTSIDE_CONTRACT=1 → allowed despite undeclared" 0 $?
|
|
781
|
+
|
|
782
|
+
# Absolute path to an undeclared file still blocks
|
|
783
|
+
echo "{\"tool_input\":{\"file_path\":\"$TMP/src/evil.ts\"}}" | (cd "$TMP" && $NODE "$HOOKS_DIR/task-write-guard.js" >/dev/null 2>&1)
|
|
784
|
+
assert_exit "active build + absolute undeclared path → blocked" 2 $?
|
|
785
|
+
|
|
786
|
+
# Once T1 flips to DONE (no RUNNING), the guard goes quiet again
|
|
787
|
+
$NODE "$AS_BIN" write T1 DONE --commit abc --cwd "$TMP" >/dev/null 2>&1
|
|
788
|
+
twg "$TMP" "src/evil.ts"
|
|
789
|
+
assert_exit "build finished (no RUNNING) → allowed" 0 $?
|
|
790
|
+
rm -rf "$TMP"
|
|
791
|
+
|
|
792
|
+
# Active build but no contract present → fail open (allowed)
|
|
793
|
+
TMP=$(mktemp -d)
|
|
794
|
+
$NODE "$AS_BIN" write T1 RUNNING --phase 1 --cwd "$TMP" >/dev/null 2>&1
|
|
795
|
+
twg "$TMP" "src/anything.ts"
|
|
796
|
+
assert_exit "active build + no contract → allowed (fail open)" 0 $?
|
|
797
|
+
rm -rf "$TMP"
|
|
798
|
+
|
|
598
799
|
echo ""
|
|
599
800
|
echo "=== Results: $PASS passed, $FAIL failed ==="
|
|
600
801
|
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|
|
@@ -124,11 +124,12 @@ else
|
|
|
124
124
|
fi
|
|
125
125
|
|
|
126
126
|
if [ -d "$HOME_DIR/.claude/hooks" ] \
|
|
127
|
-
&& [ "$(find "$HOME_DIR/.claude/hooks" -maxdepth 1 -name '*.js' | wc -l | tr -d ' ')" = "
|
|
127
|
+
&& [ "$(find "$HOME_DIR/.claude/hooks" -maxdepth 1 -name '*.js' | wc -l | tr -d ' ')" = "15" ] \
|
|
128
128
|
&& [ -f "$HOME_DIR/.claude/hooks/fawzi-approval-guard.js" ] \
|
|
129
129
|
&& [ -f "$HOME_DIR/.claude/hooks/pre-compact.js" ] \
|
|
130
|
-
&& [ -f "$HOME_DIR/.claude/hooks/usage-capture.js" ]
|
|
131
|
-
|
|
130
|
+
&& [ -f "$HOME_DIR/.claude/hooks/usage-capture.js" ] \
|
|
131
|
+
&& [ -f "$HOME_DIR/.claude/hooks/task-write-guard.js" ]; then
|
|
132
|
+
pass "packaged install has 15 hooks including task-write-guard (v6.13)"
|
|
132
133
|
else
|
|
133
134
|
fail_case "packaged hook set mismatch"
|
|
134
135
|
fi
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# instructions.test.sh — single-source instruction compile (R4) + host adapter
|
|
3
|
+
# contract (R5). Run: bash tests/instructions.test.sh
|
|
4
|
+
|
|
5
|
+
PASS=0
|
|
6
|
+
FAIL=0
|
|
7
|
+
FRAMEWORK_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
8
|
+
NODE="${NODE:-node}"
|
|
9
|
+
HA="$FRAMEWORK_DIR/bin/host-adapters.js"
|
|
10
|
+
CI="$FRAMEWORK_DIR/bin/compile-instructions.js"
|
|
11
|
+
|
|
12
|
+
assert_exit() {
|
|
13
|
+
local name="$1" expected="$2" actual="$3"
|
|
14
|
+
if [ "$expected" = "$actual" ]; then echo " ✓ $name"; PASS=$((PASS+1));
|
|
15
|
+
else echo " ✗ $name (expected exit $expected, got $actual)"; FAIL=$((FAIL+1)); fi
|
|
16
|
+
}
|
|
17
|
+
assert_eq() {
|
|
18
|
+
local name="$1" expected="$2" actual="$3"
|
|
19
|
+
if [ "$expected" = "$actual" ]; then echo " ✓ $name"; PASS=$((PASS+1));
|
|
20
|
+
else echo " ✗ $name (expected '$expected', got '$actual')"; FAIL=$((FAIL+1)); fi
|
|
21
|
+
}
|
|
22
|
+
assert_contains() {
|
|
23
|
+
local name="$1" hay="$2" needle="$3"
|
|
24
|
+
if echo "$hay" | grep -qF "$needle"; then echo " ✓ $name"; PASS=$((PASS+1));
|
|
25
|
+
else echo " ✗ $name (missing '$needle')"; FAIL=$((FAIL+1)); fi
|
|
26
|
+
}
|
|
27
|
+
refute_contains() {
|
|
28
|
+
local name="$1" hay="$2" needle="$3"
|
|
29
|
+
if echo "$hay" | grep -qF "$needle"; then echo " ✗ $name (found '$needle')"; FAIL=$((FAIL+1));
|
|
30
|
+
else echo " ✓ $name"; PASS=$((PASS+1)); fi
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
echo "instructions.test.sh — single-source compile (R4) + host contract (R5)"
|
|
34
|
+
echo ""
|
|
35
|
+
|
|
36
|
+
# syntax
|
|
37
|
+
$NODE -c "$HA" 2>/dev/null && { echo " ✓ host-adapters syntax valid"; PASS=$((PASS+1)); } || { echo " ✗ host-adapters syntax"; FAIL=$((FAIL+1)); }
|
|
38
|
+
$NODE -c "$CI" 2>/dev/null && { echo " ✓ compile-instructions syntax valid"; PASS=$((PASS+1)); } || { echo " ✗ compile-instructions syntax"; FAIL=$((FAIL+1)); }
|
|
39
|
+
|
|
40
|
+
# --- R4: drift guard — committed files match the canonical ---
|
|
41
|
+
$NODE "$CI" --check >/dev/null 2>&1
|
|
42
|
+
assert_exit "compile-instructions --check passes (no drift)" 0 $?
|
|
43
|
+
|
|
44
|
+
# A canonical edit not reflected in the artifacts is caught by --check.
|
|
45
|
+
TMPCANON=$(mktemp)
|
|
46
|
+
cp "$FRAMEWORK_DIR/templates/instructions.md" "$TMPCANON"
|
|
47
|
+
printf '\nDRIFT-MARKER\n' >> "$FRAMEWORK_DIR/templates/instructions.md"
|
|
48
|
+
$NODE "$CI" --check >/dev/null 2>&1
|
|
49
|
+
RC=$?
|
|
50
|
+
cp "$TMPCANON" "$FRAMEWORK_DIR/templates/instructions.md" # restore immediately
|
|
51
|
+
rm -f "$TMPCANON"
|
|
52
|
+
assert_exit "drift guard fails (exit 1) when canonical edited but not recompiled" 1 $RC
|
|
53
|
+
# confirm restore worked
|
|
54
|
+
$NODE "$CI" --check >/dev/null 2>&1
|
|
55
|
+
assert_exit "canonical restored, --check green again" 0 $?
|
|
56
|
+
|
|
57
|
+
# --- R4: both artifacts share an identical body, differ only in footer ---
|
|
58
|
+
CLAUDE_BODY=$(sed -n '/# Qualia Framework/,/state router tells you the next command/p' "$FRAMEWORK_DIR/CLAUDE.md")
|
|
59
|
+
AGENTS_BODY=$(sed -n '/# Qualia Framework/,/state router tells you the next command/p' "$FRAMEWORK_DIR/AGENTS.md")
|
|
60
|
+
if [ "$CLAUDE_BODY" = "$AGENTS_BODY" ]; then
|
|
61
|
+
echo " ✓ CLAUDE.md and AGENTS.md bodies are identical (no drift)"; PASS=$((PASS+1));
|
|
62
|
+
else
|
|
63
|
+
echo " ✗ CLAUDE.md and AGENTS.md bodies diverge"; FAIL=$((FAIL+1));
|
|
64
|
+
fi
|
|
65
|
+
# host-specific footer survives
|
|
66
|
+
assert_contains "CLAUDE.md keeps Claude footer" "$(cat "$FRAMEWORK_DIR/CLAUDE.md")" "this file stays under 25 lines"
|
|
67
|
+
assert_contains "AGENTS.md keeps cross-vendor footer" "$(cat "$FRAMEWORK_DIR/AGENTS.md")" "cross-vendor compatibility"
|
|
68
|
+
refute_contains "AGENTS.md does NOT carry the Claude-only footer" "$(cat "$FRAMEWORK_DIR/AGENTS.md")" "this file stays under 25 lines"
|
|
69
|
+
# both carry the generated header + role placeholder for install to fill
|
|
70
|
+
assert_contains "CLAUDE.md has generated header" "$(head -1 "$FRAMEWORK_DIR/CLAUDE.md")" "GENERATED from templates/instructions.md"
|
|
71
|
+
assert_contains "AGENTS.md keeps {{ROLE}} for install" "$(cat "$FRAMEWORK_DIR/AGENTS.md")" "{{ROLE}}"
|
|
72
|
+
|
|
73
|
+
# --- R5: adapter contract is the single source of per-host facts ---
|
|
74
|
+
assert_eq "claude instructionFile" "CLAUDE.md" "$($NODE -e "console.log(require('$HA').adapter('claude').instructionFile)")"
|
|
75
|
+
assert_eq "codex instructionFile" "AGENTS.md" "$($NODE -e "console.log(require('$HA').adapter('codex').instructionFile)")"
|
|
76
|
+
assert_eq "claude configFile" "settings.json" "$($NODE -e "console.log(require('$HA').adapter('claude').configFile)")"
|
|
77
|
+
assert_eq "codex configFile" "config.toml" "$($NODE -e "console.log(require('$HA').adapter('codex').configFile)")"
|
|
78
|
+
assert_eq "codex agentExt" ".toml" "$($NODE -e "console.log(require('$HA').adapter('codex').agentExt)")"
|
|
79
|
+
assert_eq "unknown host throws" "throws" "$($NODE -e "try{require('$HA').adapter('cursor');console.log('no')}catch(e){console.log('throws')}")"
|
|
80
|
+
|
|
81
|
+
# --- R5: stripHostBlocks keeps only the matching host's block ---
|
|
82
|
+
SRC='before
|
|
83
|
+
<!--QUALIA-HOST claude-->
|
|
84
|
+
CLAUDE_ONLY
|
|
85
|
+
<!--/QUALIA-HOST-->
|
|
86
|
+
<!--QUALIA-HOST codex-->
|
|
87
|
+
CODEX_ONLY
|
|
88
|
+
<!--/QUALIA-HOST-->
|
|
89
|
+
after'
|
|
90
|
+
CLAUDE_OUT=$($NODE -e "console.log(require('$HA').stripHostBlocks(process.argv[1],'claude'))" "$SRC")
|
|
91
|
+
assert_contains "claude strip keeps CLAUDE_ONLY" "$CLAUDE_OUT" "CLAUDE_ONLY"
|
|
92
|
+
refute_contains "claude strip drops CODEX_ONLY" "$CLAUDE_OUT" "CODEX_ONLY"
|
|
93
|
+
CODEX_OUT=$($NODE -e "console.log(require('$HA').stripHostBlocks(process.argv[1],'codex'))" "$SRC")
|
|
94
|
+
assert_contains "codex strip keeps CODEX_ONLY" "$CODEX_OUT" "CODEX_ONLY"
|
|
95
|
+
refute_contains "codex strip drops CLAUDE_ONLY" "$CODEX_OUT" "CLAUDE_ONLY"
|
|
96
|
+
|
|
97
|
+
# --- R5: compileInstructions applies naming but leaves tokens/role for install ---
|
|
98
|
+
NAMED=$($NODE -e "console.log(require('$HA').compileInstructions('Use Claude Code now. Role: {{ROLE}} at \${QUALIA_BIN}','codex'))")
|
|
99
|
+
assert_contains "codex compile swaps the product name" "$NAMED" "Use Codex now"
|
|
100
|
+
assert_contains "codex compile leaves {{ROLE}} for install" "$NAMED" "{{ROLE}}"
|
|
101
|
+
assert_contains "codex compile leaves \${QUALIA_BIN} token for install" "$NAMED" '${QUALIA_BIN}'
|
|
102
|
+
|
|
103
|
+
# --- R5: renderText still resolves paths (regression) ---
|
|
104
|
+
RENDERED=$($NODE -e "console.log(require('$HA').renderText('bin at \${QUALIA_BIN}','claude'))")
|
|
105
|
+
assert_contains "renderText resolves QUALIA_BIN" "$RENDERED" "/.claude/bin"
|
|
106
|
+
|
|
107
|
+
echo ""
|
|
108
|
+
echo "=== Results: $PASS passed, $FAIL failed ==="
|
|
109
|
+
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# last-report.test.sh — bin/last-report.js (surface the latest session report)
|
|
3
|
+
# Run: bash tests/last-report.test.sh
|
|
4
|
+
|
|
5
|
+
PASS=0
|
|
6
|
+
FAIL=0
|
|
7
|
+
BIN_DIR="$(cd "$(dirname "$0")/../bin" && pwd)"
|
|
8
|
+
NODE="${NODE:-node}"
|
|
9
|
+
LR="$BIN_DIR/last-report.js"
|
|
10
|
+
|
|
11
|
+
assert_exit() {
|
|
12
|
+
local name="$1" expected="$2" actual="$3"
|
|
13
|
+
if [ "$expected" = "$actual" ]; then echo " ✓ $name"; PASS=$((PASS+1));
|
|
14
|
+
else echo " ✗ $name (expected exit $expected, got $actual)"; FAIL=$((FAIL+1)); fi
|
|
15
|
+
}
|
|
16
|
+
assert_contains() {
|
|
17
|
+
local name="$1" hay="$2" needle="$3"
|
|
18
|
+
if echo "$hay" | grep -qF "$needle"; then echo " ✓ $name"; PASS=$((PASS+1));
|
|
19
|
+
else echo " ✗ $name (missing '$needle' in: $hay)"; FAIL=$((FAIL+1)); fi
|
|
20
|
+
}
|
|
21
|
+
assert_not_contains() {
|
|
22
|
+
local name="$1" hay="$2" needle="$3"
|
|
23
|
+
if echo "$hay" | grep -qF "$needle"; then echo " ✗ $name (unexpected '$needle' in: $hay)"; FAIL=$((FAIL+1));
|
|
24
|
+
else echo " ✓ $name"; PASS=$((PASS+1)); fi
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Write a well-formed report at $1/.planning/reports/report-$2.md with summary $3, next $4
|
|
28
|
+
write_report() {
|
|
29
|
+
local root="$1" datepart="$2" summary="$3" next="$4" date="$5"
|
|
30
|
+
mkdir -p "$root/.planning/reports"
|
|
31
|
+
cat > "$root/.planning/reports/report-$datepart.md" <<EOF
|
|
32
|
+
# Session Report — $date
|
|
33
|
+
|
|
34
|
+
**Project:** demo
|
|
35
|
+
**Employee:** T
|
|
36
|
+
**Branch:** main
|
|
37
|
+
**Date:** $date
|
|
38
|
+
|
|
39
|
+
## What Was Done
|
|
40
|
+
- $summary
|
|
41
|
+
|
|
42
|
+
## Blockers
|
|
43
|
+
None.
|
|
44
|
+
|
|
45
|
+
## Next Steps
|
|
46
|
+
1. $next
|
|
47
|
+
|
|
48
|
+
## Commits (1)
|
|
49
|
+
- abc123 seed
|
|
50
|
+
EOF
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
echo "last-report.test.sh — bin/last-report.js"
|
|
54
|
+
echo ""
|
|
55
|
+
|
|
56
|
+
$NODE -c "$LR" 2>/dev/null && { echo " ✓ syntax valid"; PASS=$((PASS+1)); } || { echo " ✗ syntax invalid"; FAIL=$((FAIL+1)); }
|
|
57
|
+
|
|
58
|
+
# --- no reports dir → exit 1, found:false ---
|
|
59
|
+
TMP=$(mktemp -d)
|
|
60
|
+
(cd "$TMP" && $NODE "$LR" >/dev/null 2>&1)
|
|
61
|
+
assert_exit "no reports dir → exit 1" 1 $?
|
|
62
|
+
OUT=$(cd "$TMP" && $NODE "$LR" --json 2>&1)
|
|
63
|
+
assert_contains "json found:false when none" "$OUT" '"found": false'
|
|
64
|
+
OUT=$(cd "$TMP" && $NODE "$LR" 2>&1)
|
|
65
|
+
assert_contains "human: nothing to surface" "$OUT" "No session reports"
|
|
66
|
+
rm -rf "$TMP"
|
|
67
|
+
|
|
68
|
+
# --- single report → extracts date + summary + next, exit 0 ---
|
|
69
|
+
TMP=$(mktemp -d)
|
|
70
|
+
write_report "$TMP" "2026-05-28" "Built the login flow" "Wire the dashboard" "2026-05-28"
|
|
71
|
+
(cd "$TMP" && $NODE "$LR" >/dev/null 2>&1)
|
|
72
|
+
assert_exit "single report → exit 0" 0 $?
|
|
73
|
+
OUT=$(cd "$TMP" && $NODE "$LR" 2>&1)
|
|
74
|
+
assert_contains "human shows date" "$OUT" "2026-05-28"
|
|
75
|
+
assert_contains "human shows summary" "$OUT" "Built the login flow"
|
|
76
|
+
assert_contains "human shows next" "$OUT" "Wire the dashboard"
|
|
77
|
+
assert_contains "human has 'Last session' prefix" "$OUT" "Last session"
|
|
78
|
+
rm -rf "$TMP"
|
|
79
|
+
|
|
80
|
+
# --- json shape ---
|
|
81
|
+
TMP=$(mktemp -d)
|
|
82
|
+
write_report "$TMP" "2026-05-28" "Built the login flow" "Wire the dashboard" "2026-05-28"
|
|
83
|
+
OUT=$(cd "$TMP" && $NODE "$LR" --json 2>&1)
|
|
84
|
+
assert_contains "json found:true" "$OUT" '"found": true'
|
|
85
|
+
assert_contains "json file field" "$OUT" '"file": "report-2026-05-28.md"'
|
|
86
|
+
assert_contains "json date field" "$OUT" '"date": "2026-05-28"'
|
|
87
|
+
assert_contains "json summary field" "$OUT" '"summary": "Built the login flow"'
|
|
88
|
+
assert_contains "json next field" "$OUT" '"next": "Wire the dashboard"'
|
|
89
|
+
rm -rf "$TMP"
|
|
90
|
+
|
|
91
|
+
# --- multiple reports → picks the newest BY DATE (not file order) ---
|
|
92
|
+
TMP=$(mktemp -d)
|
|
93
|
+
write_report "$TMP" "2026-05-28" "Older work" "Old next" "2026-05-28"
|
|
94
|
+
write_report "$TMP" "2026-06-15" "Newest work" "New next" "2026-06-15"
|
|
95
|
+
write_report "$TMP" "2026-06-01" "Middle work" "Mid next" "2026-06-01"
|
|
96
|
+
OUT=$(cd "$TMP" && $NODE "$LR" --json 2>&1)
|
|
97
|
+
assert_contains "picks newest date file" "$OUT" '"file": "report-2026-06-15.md"'
|
|
98
|
+
assert_contains "picks newest summary" "$OUT" "Newest work"
|
|
99
|
+
assert_not_contains "does not pick older summary" "$OUT" "Older work"
|
|
100
|
+
rm -rf "$TMP"
|
|
101
|
+
|
|
102
|
+
# --- dated suffix filenames (report-2026-06-20-session2.md) parse correctly ---
|
|
103
|
+
TMP=$(mktemp -d)
|
|
104
|
+
write_report "$TMP" "2026-06-10" "Earlier" "e" "2026-06-10"
|
|
105
|
+
write_report "$TMP" "2026-06-20-session2" "Suffixed newest" "s" "2026-06-20"
|
|
106
|
+
OUT=$(cd "$TMP" && $NODE "$LR" --json 2>&1)
|
|
107
|
+
assert_contains "suffixed filename wins by date" "$OUT" "Suffixed newest"
|
|
108
|
+
assert_contains "suffixed date extracted" "$OUT" '"date": "2026-06-20"'
|
|
109
|
+
rm -rf "$TMP"
|
|
110
|
+
|
|
111
|
+
# --- --now makes age_days deterministic ---
|
|
112
|
+
TMP=$(mktemp -d)
|
|
113
|
+
write_report "$TMP" "2026-06-01" "Work" "Next" "2026-06-01"
|
|
114
|
+
OUT=$(cd "$TMP" && $NODE "$LR" --json --now 2026-06-11T00:00:00Z 2>&1)
|
|
115
|
+
assert_contains "age_days deterministic (10)" "$OUT" '"age_days": 10'
|
|
116
|
+
OUT=$(cd "$TMP" && $NODE "$LR" --now 2026-06-11T00:00:00Z 2>&1)
|
|
117
|
+
assert_contains "human age uses --now" "$OUT" "10d ago"
|
|
118
|
+
rm -rf "$TMP"
|
|
119
|
+
|
|
120
|
+
# --- library: latestReport() returns structured object ---
|
|
121
|
+
TMP=$(mktemp -d)
|
|
122
|
+
write_report "$TMP" "2026-06-01" "Lib work" "Lib next" "2026-06-01"
|
|
123
|
+
RES=$($NODE -e "console.log(require('$LR').latestReport('$TMP').summary)" 2>&1)
|
|
124
|
+
assert_contains "latestReport() returns summary" "$RES" "Lib work"
|
|
125
|
+
RES=$($NODE -e "console.log(require('$LR').latestReport('$TMP',{now:'2026-06-06T00:00:00Z'}).age_days)" 2>&1)
|
|
126
|
+
assert_contains "latestReport() honors now opt (5)" "$RES" "5"
|
|
127
|
+
rm -rf "$TMP"
|
|
128
|
+
|
|
129
|
+
# --- malformed / empty report handled gracefully (no crash, still exit 0) ---
|
|
130
|
+
TMP=$(mktemp -d)
|
|
131
|
+
mkdir -p "$TMP/.planning/reports"
|
|
132
|
+
: > "$TMP/.planning/reports/report-2026-06-02.md" # completely empty
|
|
133
|
+
(cd "$TMP" && $NODE "$LR" >/dev/null 2>&1)
|
|
134
|
+
assert_exit "empty report → exit 0 (found by filename)" 0 $?
|
|
135
|
+
OUT=$(cd "$TMP" && $NODE "$LR" --json 2>&1)
|
|
136
|
+
assert_contains "empty report found:true" "$OUT" '"found": true'
|
|
137
|
+
assert_contains "empty report date from filename" "$OUT" '"date": "2026-06-02"'
|
|
138
|
+
rm -rf "$TMP"
|
|
139
|
+
|
|
140
|
+
# --- malformed report (no headings, just prose) still yields a summary ---
|
|
141
|
+
TMP=$(mktemp -d)
|
|
142
|
+
mkdir -p "$TMP/.planning/reports"
|
|
143
|
+
printf 'just some freeform notes about the day\n' > "$TMP/.planning/reports/report-2026-06-03.md"
|
|
144
|
+
OUT=$(cd "$TMP" && $NODE "$LR" --json 2>&1)
|
|
145
|
+
assert_contains "malformed report falls back to first line" "$OUT" "just some freeform notes"
|
|
146
|
+
rm -rf "$TMP"
|
|
147
|
+
|
|
148
|
+
# --- bad argument → exit 2 ---
|
|
149
|
+
TMP=$(mktemp -d)
|
|
150
|
+
(cd "$TMP" && $NODE "$LR" --bogus >/dev/null 2>&1)
|
|
151
|
+
assert_exit "unknown flag → exit 2" 2 $?
|
|
152
|
+
rm -rf "$TMP"
|
|
153
|
+
|
|
154
|
+
echo ""
|
|
155
|
+
echo "=== Results: $PASS passed, $FAIL failed ==="
|
|
156
|
+
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|
package/tests/lib.test.sh
CHANGED
|
@@ -507,7 +507,7 @@ TMP=$(mktmp)
|
|
|
507
507
|
mkdir -p "$TMP/home/.claude/bin" "$TMP/home/.claude/hooks" "$TMP/home/.claude/knowledge/daily-log" "$TMP/home/.claude/qualia-design" "$TMP/home/.claude/agents" "$TMP/home/.claude/qualia-templates" "$TMP/project"
|
|
508
508
|
echo '{"installed_by":"Test","role":"OWNER","version":"6.3.0","erp":{"enabled":false}}' > "$TMP/home/.claude/.qualia-config.json"
|
|
509
509
|
touch "$TMP/home/.claude/CLAUDE.md" "$TMP/home/.claude/settings.json"
|
|
510
|
-
for f in runtime-manifest.js command-surface.js host-adapters.js state.js qualia-ui.js statusline.js knowledge.js knowledge-flush.js state-ledger.js plan-contract.js contract-runner.js harness-eval.js trust-score.js agent-runs.js slop-detect.mjs erp-retry.js work-packet.js report-payload.js project-snapshot.js codex-goal.js planning-hygiene.js prune-deprecated.js learning-candidates.js status-snapshot.js security-scan.js auto-report.js; do
|
|
510
|
+
for f in runtime-manifest.js command-surface.js host-adapters.js state.js qualia-ui.js statusline.js knowledge.js knowledge-flush.js state-ledger.js plan-contract.js contract-runner.js agent-status.js analyze-gate.js verify-panel.js wave-plan.js eval-runner.js branch-hygiene.js last-report.js harness-eval.js trust-score.js agent-runs.js slop-detect.mjs erp-retry.js work-packet.js report-payload.js project-snapshot.js project-sync.js codex-goal.js planning-hygiene.js prune-deprecated.js learning-candidates.js status-snapshot.js security-scan.js auto-report.js; do
|
|
511
511
|
touch "$TMP/home/.claude/bin/$f"
|
|
512
512
|
done
|
|
513
513
|
for h in session-start.js auto-update.js branch-guard.js pre-push.js pre-deploy-gate.js migration-guard.js git-guardrails.js stop-session-log.js fawzi-approval-guard.js vercel-account-guard.js env-empty-guard.js supabase-destructive-guard.js; do
|
|
@@ -622,7 +622,7 @@ TMP=$(mktmp)
|
|
|
622
622
|
mkdir -p "$TMP/.claude/bin" "$TMP/.claude/hooks" "$TMP/.claude/knowledge/daily-log" "$TMP/.claude/qualia-design" "$TMP/.claude/agents" "$TMP/.claude/qualia-templates" "$TMP/project/.planning"
|
|
623
623
|
echo '{"installed_by":"Test","role":"OWNER","erp":{"enabled":false}}' > "$TMP/.claude/.qualia-config.json"
|
|
624
624
|
touch "$TMP/.claude/CLAUDE.md" "$TMP/.claude/settings.json"
|
|
625
|
-
for f in runtime-manifest.js command-surface.js host-adapters.js state.js qualia-ui.js statusline.js knowledge.js knowledge-flush.js state-ledger.js plan-contract.js contract-runner.js harness-eval.js trust-score.js agent-runs.js slop-detect.mjs erp-retry.js work-packet.js report-payload.js project-snapshot.js codex-goal.js planning-hygiene.js prune-deprecated.js learning-candidates.js status-snapshot.js security-scan.js auto-report.js; do
|
|
625
|
+
for f in runtime-manifest.js command-surface.js host-adapters.js state.js qualia-ui.js statusline.js knowledge.js knowledge-flush.js state-ledger.js plan-contract.js contract-runner.js agent-status.js analyze-gate.js verify-panel.js wave-plan.js eval-runner.js branch-hygiene.js last-report.js harness-eval.js trust-score.js agent-runs.js slop-detect.mjs erp-retry.js work-packet.js report-payload.js project-snapshot.js project-sync.js codex-goal.js planning-hygiene.js prune-deprecated.js learning-candidates.js status-snapshot.js security-scan.js auto-report.js; do
|
|
626
626
|
touch "$TMP/.claude/bin/$f"
|
|
627
627
|
done
|
|
628
628
|
for h in session-start.js auto-update.js branch-guard.js pre-push.js pre-deploy-gate.js migration-guard.js git-guardrails.js stop-session-log.js fawzi-approval-guard.js vercel-account-guard.js env-empty-guard.js supabase-destructive-guard.js; do
|