qualia-framework 6.6.0 → 6.7.1
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/command-surface.js +3 -1
- package/bin/state.js +727 -1
- package/docs/onboarding.html +1 -1
- package/guide.md +1 -1
- 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/help.html +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/bin.test.sh +8 -8
- package/tests/state.test.sh +176 -0
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 ==="
|