qualia-framework 6.5.0 → 6.7.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/README.md +3 -3
- package/agents/roadmapper.md +1 -1
- package/bin/auto-report.js +156 -0
- package/bin/command-surface.js +3 -1
- package/bin/erp-retry.js +4 -2
- package/bin/report-payload.js +5 -0
- package/bin/state.js +727 -1
- package/guide.md +1 -1
- package/hooks/stop-session-log.js +15 -0
- package/package.json +1 -1
- package/rules/codex-goal.md +1 -1
- package/rules/one-opinion.md +1 -1
- package/skills/qualia/SKILL.md +2 -0
- package/skills/qualia-discuss/SKILL.md +2 -0
- package/skills/qualia-idk/SKILL.md +2 -2
- package/skills/qualia-milestone/SKILL.md +1 -1
- package/skills/qualia-new/SKILL.md +16 -12
- package/skills/qualia-plan/SKILL.md +2 -2
- package/skills/qualia-research/SKILL.md +1 -1
- package/skills/qualia-road/SKILL.md +2 -2
- package/skills/qualia-scope/SKILL.md +86 -4
- package/skills/qualia-ship/SKILL.md +8 -1
- package/templates/CONTEXT.md +1 -1
- package/templates/phase-context.md +1 -1
- package/templates/planning.gitignore +10 -0
- package/templates/project-discovery.md +1 -1
- package/templates/projects/ai-agent.md +2 -2
- package/templates/projects/mobile-app.md +2 -2
- package/templates/projects/voice-agent.md +1 -1
- package/templates/projects/website.md +1 -1
- package/tests/auto-report.test.sh +158 -0
- package/tests/bin.test.sh +8 -8
- package/tests/run-all.sh +1 -0
- package/tests/state.test.sh +176 -0
package/tests/bin.test.sh
CHANGED
|
@@ -1076,18 +1076,18 @@ for SKILL in qualia-zoom qualia-issues qualia-triage; do
|
|
|
1076
1076
|
fi
|
|
1077
1077
|
done
|
|
1078
1078
|
|
|
1079
|
-
# 85. qualia-discuss
|
|
1080
|
-
if grep -qi "
|
|
1081
|
-
pass "qualia-discuss
|
|
1079
|
+
# 85. A4 fold: qualia-discuss is RETIRED, its grill absorbed by qualia-scope (PHASE MODE).
|
|
1080
|
+
if [ ! -f "$TMP/.claude/skills/qualia-discuss/SKILL.md" ] && grep -qi "grill" "$TMP/.claude/skills/qualia-scope/SKILL.md"; then
|
|
1081
|
+
pass "qualia-discuss retired; qualia-scope carries the grill (PHASE/INCREMENT MODE)"
|
|
1082
1082
|
else
|
|
1083
|
-
fail_case "qualia-discuss
|
|
1083
|
+
fail_case "qualia-discuss should be retired and its grill absorbed by qualia-scope"
|
|
1084
1084
|
fi
|
|
1085
1085
|
|
|
1086
|
-
# 86. qualia-
|
|
1087
|
-
if grep -q "CONTEXT.md" "$TMP/.claude/skills/qualia-
|
|
1088
|
-
pass "qualia-
|
|
1086
|
+
# 86. qualia-scope carries the CONTEXT.md substrate + PROJECT MODE kickoff discuss used to own.
|
|
1087
|
+
if grep -q "CONTEXT.md" "$TMP/.claude/skills/qualia-scope/SKILL.md" && grep -q "PROJECT MODE" "$TMP/.claude/skills/qualia-scope/SKILL.md"; then
|
|
1088
|
+
pass "qualia-scope references CONTEXT.md + has PROJECT MODE (absorbed from qualia-discuss)"
|
|
1089
1089
|
else
|
|
1090
|
-
fail_case "qualia-
|
|
1090
|
+
fail_case "qualia-scope missing CONTEXT.md reference or PROJECT MODE"
|
|
1091
1091
|
fi
|
|
1092
1092
|
|
|
1093
1093
|
# 87. qualia-road has disable-model-invocation: true (pure reference skill, user-only)
|
package/tests/run-all.sh
CHANGED
package/tests/state.test.sh
CHANGED
|
@@ -1158,6 +1158,182 @@ else
|
|
|
1158
1158
|
fail_case "next-report-id missing peeked: true"
|
|
1159
1159
|
fi
|
|
1160
1160
|
|
|
1161
|
+
# ─── A5: per-increment, concurrency-aware layout ─────────
|
|
1162
|
+
echo ""
|
|
1163
|
+
echo "A5 increment layout:"
|
|
1164
|
+
|
|
1165
|
+
# 58. migrate converts a legacy project to increments + releases + gitignore + manifest
|
|
1166
|
+
TMP=$(make_project)
|
|
1167
|
+
MIG=$(cd "$TMP" && $NODE "$STATE_JS" migrate 2>&1)
|
|
1168
|
+
if echo "$MIG" | grep -q '"ok": true' \
|
|
1169
|
+
&& [ -f "$TMP/.planning/increments/inc-0001-foundation.md" ] \
|
|
1170
|
+
&& [ -f "$TMP/.planning/increments/inc-0002-core.md" ] \
|
|
1171
|
+
&& [ -f "$TMP/.planning/releases/r1.md" ] \
|
|
1172
|
+
&& [ -f "$TMP/.planning/.gitignore" ] \
|
|
1173
|
+
&& [ -f "$TMP/.planning/migration-manifest.json" ] \
|
|
1174
|
+
&& grep -q '^STATE.md$' "$TMP/.planning/.gitignore" \
|
|
1175
|
+
&& grep -q '^tracking.json$' "$TMP/.planning/.gitignore"; then
|
|
1176
|
+
pass "migrate creates increments/ + releases/ + .gitignore + manifest"
|
|
1177
|
+
else
|
|
1178
|
+
fail_case "migrate layout" "out=$MIG"
|
|
1179
|
+
fi
|
|
1180
|
+
|
|
1181
|
+
# 59. migrate is idempotent (re-run is a no-op)
|
|
1182
|
+
MIG2=$(cd "$TMP" && $NODE "$STATE_JS" migrate 2>&1)
|
|
1183
|
+
if echo "$MIG2" | grep -q '"already_migrated": true'; then
|
|
1184
|
+
pass "migrate is idempotent (already_migrated on re-run)"
|
|
1185
|
+
else
|
|
1186
|
+
fail_case "migrate idempotent" "out=$MIG2"
|
|
1187
|
+
fi
|
|
1188
|
+
|
|
1189
|
+
# 60. migrate --revert restores STATE.md + tracking.json and removes the layout
|
|
1190
|
+
REV=$(cd "$TMP" && $NODE "$STATE_JS" migrate --revert 2>&1)
|
|
1191
|
+
if echo "$REV" | grep -q '"restored": true' \
|
|
1192
|
+
&& [ ! -d "$TMP/.planning/increments" ] \
|
|
1193
|
+
&& [ ! -d "$TMP/.planning/releases" ] \
|
|
1194
|
+
&& [ ! -f "$TMP/.planning/.cursor.json" ] \
|
|
1195
|
+
&& [ -f "$TMP/.planning/STATE.md" ] \
|
|
1196
|
+
&& [ -f "$TMP/.planning/tracking.json" ]; then
|
|
1197
|
+
pass "migrate --revert restores legacy files + removes layout"
|
|
1198
|
+
else
|
|
1199
|
+
fail_case "migrate revert" "out=$REV"
|
|
1200
|
+
fi
|
|
1201
|
+
|
|
1202
|
+
# 61. claim writes claimed_by + branch onto the increment file
|
|
1203
|
+
TMP=$(make_project)
|
|
1204
|
+
(cd "$TMP" && $NODE "$STATE_JS" migrate >/dev/null 2>&1)
|
|
1205
|
+
CL=$(cd "$TMP" && QUALIA_ACTOR=alice $NODE "$STATE_JS" claim --id inc-0001-foundation --branch feat-a 2>&1)
|
|
1206
|
+
if echo "$CL" | grep -q '"claimed_by": "alice"' \
|
|
1207
|
+
&& echo "$CL" | grep -q '"branch": "feat-a"' \
|
|
1208
|
+
&& grep -q '^claimed_by: alice$' "$TMP/.planning/increments/inc-0001-foundation.md" \
|
|
1209
|
+
&& grep -q '^branch: feat-a$' "$TMP/.planning/increments/inc-0001-foundation.md"; then
|
|
1210
|
+
pass "claim writes claimed_by + branch to increment file"
|
|
1211
|
+
else
|
|
1212
|
+
fail_case "claim writes fields" "out=$CL"
|
|
1213
|
+
fi
|
|
1214
|
+
|
|
1215
|
+
# 62. a second actor claiming a held increment is refused
|
|
1216
|
+
DC=$(cd "$TMP" && QUALIA_ACTOR=bob $NODE "$STATE_JS" claim --id inc-0001-foundation --branch feat-b 2>&1)
|
|
1217
|
+
if echo "$DC" | grep -q '"error": "ALREADY_CLAIMED"'; then
|
|
1218
|
+
pass "double-claim by another actor is refused (ALREADY_CLAIMED)"
|
|
1219
|
+
else
|
|
1220
|
+
fail_case "double-claim refused" "out=$DC"
|
|
1221
|
+
fi
|
|
1222
|
+
|
|
1223
|
+
# 63. check (increment layout) skips the increment claimed by another actor
|
|
1224
|
+
CK=$(cd "$TMP" && QUALIA_ACTOR=bob $NODE "$STATE_JS" check 2>&1)
|
|
1225
|
+
if echo "$CK" | grep -q '"layout": "increments"' \
|
|
1226
|
+
&& echo "$CK" | grep -q '"next_increment": "inc-0002-core"' \
|
|
1227
|
+
&& echo "$CK" | grep -q '"claimed_by": "alice"'; then
|
|
1228
|
+
pass "check routes around another actor's claim (next_increment skips it)"
|
|
1229
|
+
else
|
|
1230
|
+
fail_case "check skip-claimed" "out=$CK"
|
|
1231
|
+
fi
|
|
1232
|
+
|
|
1233
|
+
# 64. release ships the increment + clears the claim (migrated DoD is satisfied)
|
|
1234
|
+
RL=$(cd "$TMP" && QUALIA_ACTOR=alice $NODE "$STATE_JS" release --id inc-0001-foundation --deployed-url https://x.example 2>&1)
|
|
1235
|
+
if echo "$RL" | grep -q '"status": "shipped"' \
|
|
1236
|
+
&& echo "$RL" | grep -q '"claim_cleared_from": "alice"' \
|
|
1237
|
+
&& grep -q '^status: shipped$' "$TMP/.planning/increments/inc-0001-foundation.md" \
|
|
1238
|
+
&& grep -q '^claimed_by: $' "$TMP/.planning/increments/inc-0001-foundation.md" \
|
|
1239
|
+
&& grep -q '^deployed_url: https://x.example$' "$TMP/.planning/increments/inc-0001-foundation.md"; then
|
|
1240
|
+
pass "release ships increment + clears claim + records deployed_url"
|
|
1241
|
+
else
|
|
1242
|
+
fail_case "release ships" "out=$RL"
|
|
1243
|
+
fi
|
|
1244
|
+
|
|
1245
|
+
# 65. release requires --deployed-url
|
|
1246
|
+
RLN=$(cd "$TMP" && QUALIA_ACTOR=alice $NODE "$STATE_JS" release --id inc-0002-core 2>&1)
|
|
1247
|
+
if echo "$RLN" | grep -q '"error": "MISSING_ARG"'; then
|
|
1248
|
+
pass "release without --deployed-url is refused (MISSING_ARG)"
|
|
1249
|
+
else
|
|
1250
|
+
fail_case "release requires url" "out=$RLN"
|
|
1251
|
+
fi
|
|
1252
|
+
|
|
1253
|
+
# 66. DoD gate (strict): an open '- [ ]' line blocks release
|
|
1254
|
+
TMP=$(make_project)
|
|
1255
|
+
(cd "$TMP" && $NODE "$STATE_JS" migrate >/dev/null 2>&1)
|
|
1256
|
+
printf '\n- [ ] unfinished acceptance item\n' >> "$TMP/.planning/increments/inc-0001-foundation.md"
|
|
1257
|
+
DOD=$(cd "$TMP" && $NODE "$STATE_JS" release --id inc-0001-foundation --deployed-url https://x.example 2>&1)
|
|
1258
|
+
if echo "$DOD" | grep -q '"error": "DOD_INCOMPLETE"'; then
|
|
1259
|
+
pass "DoD gate (strict): open checklist item blocks release"
|
|
1260
|
+
else
|
|
1261
|
+
fail_case "DoD strict block" "out=$DOD"
|
|
1262
|
+
fi
|
|
1263
|
+
|
|
1264
|
+
# 67. DoD gate: --force (OWNER override) ships despite open items
|
|
1265
|
+
DODF=$(cd "$TMP" && $NODE "$STATE_JS" release --id inc-0001-foundation --deployed-url https://x.example --force 2>&1)
|
|
1266
|
+
if echo "$DODF" | grep -q '"status": "shipped"'; then
|
|
1267
|
+
pass "DoD gate: --force ships despite open checklist items"
|
|
1268
|
+
else
|
|
1269
|
+
fail_case "DoD force" "out=$DODF"
|
|
1270
|
+
fi
|
|
1271
|
+
|
|
1272
|
+
# 68. DoD gate (standard): open item allowed only with a WAIVED reason
|
|
1273
|
+
TMP=$(make_project)
|
|
1274
|
+
(cd "$TMP" && $NODE "$STATE_JS" migrate >/dev/null 2>&1)
|
|
1275
|
+
printf '\n- [ ] perf budget WAIVED: out of scope for v1 (ADR-014)\n' >> "$TMP/.planning/increments/inc-0001-foundation.md"
|
|
1276
|
+
DODS=$(cd "$TMP" && QUALIA_PROFILE=standard $NODE "$STATE_JS" release --id inc-0001-foundation --deployed-url https://x.example 2>&1)
|
|
1277
|
+
if echo "$DODS" | grep -q '"status": "shipped"'; then
|
|
1278
|
+
pass "DoD gate (standard): WAIVED open item permits release"
|
|
1279
|
+
else
|
|
1280
|
+
fail_case "DoD standard waive" "out=$DODS"
|
|
1281
|
+
fi
|
|
1282
|
+
|
|
1283
|
+
# 69. legacy projects (no increments/) are untouched by the new path
|
|
1284
|
+
TMP=$(make_project)
|
|
1285
|
+
LEG=$(cd "$TMP" && $NODE "$STATE_JS" check 2>&1)
|
|
1286
|
+
if echo "$LEG" | grep -q '"next_command": "/qualia-plan 1"' \
|
|
1287
|
+
&& ! echo "$LEG" | grep -q '"layout": "increments"'; then
|
|
1288
|
+
pass "legacy project check is unchanged (no increment layout)"
|
|
1289
|
+
else
|
|
1290
|
+
fail_case "legacy untouched" "out=$LEG"
|
|
1291
|
+
fi
|
|
1292
|
+
|
|
1293
|
+
# 70. TWO-ACTOR KEYSTONE: two clones ship two increments → merge with zero conflict
|
|
1294
|
+
if command -v git >/dev/null 2>&1; then
|
|
1295
|
+
ORIGIN=$(mktemp -d); CA=$(mktemp -d); CB=$(mktemp -d)
|
|
1296
|
+
(cd "$ORIGIN" && git init -q && git config user.email o@o.com && git config user.name origin && git config commit.gpgsign false \
|
|
1297
|
+
&& $NODE "$STATE_JS" init --project "Conc" --phases '[{"name":"Alpha","goal":"A"},{"name":"Beta","goal":"B"}]' >/dev/null 2>&1 \
|
|
1298
|
+
&& $NODE "$STATE_JS" migrate >/dev/null 2>&1 \
|
|
1299
|
+
&& git add -A && git commit -q -m "seed")
|
|
1300
|
+
DEFBR=$(cd "$ORIGIN" && git branch --show-current)
|
|
1301
|
+
git clone -q "$ORIGIN" "$CA" 2>/dev/null; git clone -q "$ORIGIN" "$CB" 2>/dev/null
|
|
1302
|
+
(cd "$CA" && git config user.email a@a.com && git config user.name alice && git config commit.gpgsign false && git checkout -b feat-a -q \
|
|
1303
|
+
&& QUALIA_ACTOR=alice $NODE "$STATE_JS" claim --id inc-0001-alpha --branch feat-a >/dev/null 2>&1 \
|
|
1304
|
+
&& QUALIA_ACTOR=alice $NODE "$STATE_JS" release --id inc-0001-alpha --deployed-url https://a.example >/dev/null 2>&1 \
|
|
1305
|
+
&& git add .planning/increments/inc-0001-alpha.md && git commit -q -m "ship alpha" && git push -q origin feat-a)
|
|
1306
|
+
(cd "$CB" && git config user.email b@b.com && git config user.name bob && git config commit.gpgsign false && git checkout -b feat-b -q \
|
|
1307
|
+
&& QUALIA_ACTOR=bob $NODE "$STATE_JS" claim --id inc-0002-beta --branch feat-b >/dev/null 2>&1 \
|
|
1308
|
+
&& QUALIA_ACTOR=bob $NODE "$STATE_JS" release --id inc-0002-beta --deployed-url https://b.example >/dev/null 2>&1 \
|
|
1309
|
+
&& git add .planning/increments/inc-0002-beta.md && git commit -q -m "ship beta" && git push -q origin feat-b)
|
|
1310
|
+
MRG=$(cd "$ORIGIN" && git checkout -q "$DEFBR" && git merge feat-a -m ma >/dev/null 2>&1; git merge feat-b -m mb 2>&1; echo "exit=$?")
|
|
1311
|
+
CONFLICTS=$(cd "$ORIGIN" && git grep -l '<<<<<<<' HEAD -- .planning 2>/dev/null | wc -l | tr -d ' ')
|
|
1312
|
+
STATE_TRACKED=$(cd "$ORIGIN" && git ls-files .planning/STATE.md | wc -l | tr -d ' ')
|
|
1313
|
+
if echo "$MRG" | grep -q 'exit=0' \
|
|
1314
|
+
&& [ "$CONFLICTS" = "0" ] \
|
|
1315
|
+
&& grep -q '^status: shipped$' "$ORIGIN/.planning/increments/inc-0001-alpha.md" \
|
|
1316
|
+
&& grep -q '^status: shipped$' "$ORIGIN/.planning/increments/inc-0002-beta.md" \
|
|
1317
|
+
&& [ "$STATE_TRACKED" = "0" ]; then
|
|
1318
|
+
pass "two-actor: ship A on feat-a + B on feat-b → merge to main with ZERO conflict, STATE.md never committed"
|
|
1319
|
+
else
|
|
1320
|
+
fail_case "two-actor zero-conflict" "merge=$MRG conflicts=$CONFLICTS state_tracked=$STATE_TRACKED"
|
|
1321
|
+
fi
|
|
1322
|
+
rm -rf "$ORIGIN" "$CA" "$CB"
|
|
1323
|
+
else
|
|
1324
|
+
pass "two-actor keystone skipped (git unavailable)"
|
|
1325
|
+
fi
|
|
1326
|
+
|
|
1327
|
+
# 71. id guard: a path-traversal --id is rejected before reaching the filesystem
|
|
1328
|
+
TMP=$(make_project)
|
|
1329
|
+
(cd "$TMP" && $NODE "$STATE_JS" migrate >/dev/null 2>&1)
|
|
1330
|
+
TRAV=$(cd "$TMP" && $NODE "$STATE_JS" claim --id "../../etc/passwd" --branch x 2>&1)
|
|
1331
|
+
if echo "$TRAV" | grep -q '"error": "INVALID_ID"'; then
|
|
1332
|
+
pass "claim rejects a path-traversal --id (INVALID_ID)"
|
|
1333
|
+
else
|
|
1334
|
+
fail_case "id traversal guard" "out=$TRAV"
|
|
1335
|
+
fi
|
|
1336
|
+
|
|
1161
1337
|
# ─── Summary ─────────────────────────────────────────────
|
|
1162
1338
|
echo ""
|
|
1163
1339
|
echo "=== Results: $PASS passed, $FAIL failed ==="
|