@manhquy/flow-skill 0.7.1-next.0 → 0.7.3

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +5 -5
  3. package/README_VN.md +5 -5
  4. package/package.json +1 -1
  5. package/skills/flow/SKILL.md +109 -221
  6. package/skills/flow/_templates/01-research.md +3 -2
  7. package/skills/flow/_templates/02-scope.md +2 -0
  8. package/skills/flow/_templates/03-prd.md +2 -1
  9. package/skills/flow/_templates/05-contract.md +1 -0
  10. package/skills/flow/_templates/card.md +5 -1
  11. package/skills/flow/law/CLAUDE.md +6 -5
  12. package/skills/flow/law/CODING.md +37 -0
  13. package/skills/flow/references/agent-detection.md +11 -9
  14. package/skills/flow/references/agent-stage-mapping.md +47 -12
  15. package/skills/flow/references/auto-run.md +37 -7
  16. package/skills/flow/references/clarify.md +7 -7
  17. package/skills/flow/references/command-dispatch.md +51 -51
  18. package/skills/flow/references/concierge.md +7 -5
  19. package/skills/flow/references/debt-and-halts.md +20 -5
  20. package/skills/flow/references/gate-00.md +10 -0
  21. package/skills/flow/references/gate-01.md +18 -0
  22. package/skills/flow/references/gate-02.md +16 -0
  23. package/skills/flow/references/gate-03.md +10 -0
  24. package/skills/flow/references/gate-04.md +12 -0
  25. package/skills/flow/references/gate-05.md +33 -0
  26. package/skills/flow/references/gate-assess.md +13 -0
  27. package/skills/flow/references/gate-card.md +25 -0
  28. package/skills/flow/references/gate-consistency.md +24 -0
  29. package/skills/flow/references/gate-constitution.md +19 -0
  30. package/skills/flow/references/gate-debt.md +5 -0
  31. package/skills/flow/references/gate-eval.md +16 -13
  32. package/skills/flow/references/gate-examples.md +78 -0
  33. package/skills/flow/references/gate-rules.md +40 -241
  34. package/skills/flow/references/gate-shared.md +37 -0
  35. package/skills/flow/references/ground-truth-gates.md +1 -1
  36. package/skills/flow/references/mode-work.md +13 -10
  37. package/skills/flow/runner/flow.sh +300 -31
  38. package/skills-manifest.json +16 -2
@@ -171,6 +171,7 @@ current_stage_idx() { # highest CONTIGUOUS-from-00 stage index, or -1 if 00 miss
171
171
  # Prints violations; returns 0 = clean, 1 = violations found.
172
172
  scan_gate() {
173
173
  local file="$1" found=0 unchecked fills
174
+ _MIDTIER_FAILS=0
174
175
  if [ ! -f "$file" ]; then
175
176
  echo " missing file: $file"
176
177
  return 1
@@ -187,9 +188,132 @@ scan_gate() {
187
188
  printf '%s\n' "$fills" | sed 's/^/ L/'
188
189
  found=1
189
190
  fi
191
+ case "$(basename "$file")" in
192
+ 01-research.md|02-scope.md|03-prd.md|05-contract.md)
193
+ if ! _scan_midtier "$file"; then
194
+ found=1
195
+ fi
196
+ ;;
197
+ esac
190
198
  return $found
191
199
  }
192
200
 
201
+ # Materialize a ## heading body into stdout (one awk; caller assigns to a var).
202
+ # Copy of cmd_clarify's heading walk: start at heading, stop at the next ##.
203
+ _heading_slice() { # $1=file $2=heading prefix after "## "
204
+ local file="$1" heading="$2"
205
+ awk -v h="$heading" '
206
+ {
207
+ line = $0
208
+ sub(/\r$/, "", line)
209
+ }
210
+ line ~ ("^##[[:space:]]+" h) { f=1; next }
211
+ /^##[[:space:]]/ { f=0 }
212
+ f { print line }
213
+ ' "$file"
214
+ }
215
+
216
+ _midtier_hit() {
217
+ echo " [x] $1"
218
+ _MIDTIER_FAILS=$((_MIDTIER_FAILS + 1))
219
+ }
220
+
221
+ _midtier_entry_n() { # stdin = ## What exists already -> numbered or bullet entries
222
+ awk '
223
+ /^[[:space:]]*[0-9]+\./ { c++; next }
224
+ /^[[:space:]]*- / { c++ }
225
+ END { print c + 0 }
226
+ '
227
+ }
228
+
229
+ _midtier_l_above_a() { # stdin = ## Features in v1 body; exit 1 if a joined bullet is L + B/C
230
+ awk '
231
+ function flush() {
232
+ if (buf == "") return
233
+ low = tolower(buf)
234
+ hasL = (low ~ /impact[[:space:]]*:?[[:space:]]*l([^a-z]|$)/ || low ~ /(^|[^a-z])l-impact/)
235
+ hasBC = (low ~ /grade[[:space:]]*:?[[:space:]]*[bc]([^a-z]|$)/)
236
+ if (hasL && hasBC) bad++
237
+ buf = ""
238
+ }
239
+ /^[[:space:]]*- / { flush(); buf = $0; next }
240
+ /^[[:space:]]*$/ { flush(); next }
241
+ buf != "" { buf = buf " " $0; next }
242
+ { flush() }
243
+ END { flush(); exit (bad > 0) ? 1 : 0 }
244
+ '
245
+ }
246
+
247
+ _midtier_adjectives() { # stdin = Features or NFR body; print first listed adjective
248
+ awk '
249
+ /^[[:space:]]*- \[[ xX]\]/ { next }
250
+ {
251
+ line = tolower($0)
252
+ n = split("fast secure intuitive robust prominent", w, " ")
253
+ for (i = 1; i <= n; i++) {
254
+ if (line ~ "(^|[^a-z])" w[i] "([^a-z]|$)") { print w[i]; exit }
255
+ }
256
+ }
257
+ '
258
+ }
259
+
260
+ _midtier_od_n() { # stdin = ## Open decisions body -> markdown bullet count (checked or not)
261
+ awk '/^[[:space:]]*- / { c++ } END { print c + 0 }'
262
+ }
263
+
264
+ _midtier_open_decisions() { # $1=file
265
+ local body n
266
+ body="$(_heading_slice "$1" "Open decisions")"
267
+ n="$(printf '%s\n' "$body" | _midtier_od_n)"
268
+ if [ "$n" -gt 5 ]; then
269
+ _midtier_hit "more than 5 ## Open decisions bullets ($n)"
270
+ fi
271
+ }
272
+
273
+ _scan_midtier() { # $1=file; prints [x] lines; return 1 if any hit
274
+ local file="$1" base body n pt adj
275
+ base="$(basename "$file")"
276
+ case "$base" in
277
+ 01-research.md)
278
+ if [ -f "$PROJECT_TYPE_FILE" ]; then
279
+ pt="$(tr -d '\r' < "$PROJECT_TYPE_FILE" | awk 'NF{print; exit}')"
280
+ if [ "$pt" = "web" ]; then
281
+ body="$(_heading_slice "$file" "What exists already")"
282
+ n="$(printf '%s\n' "$body" | _midtier_entry_n)"
283
+ if [ "$n" -lt 3 ]; then
284
+ _midtier_hit "fewer than 3 entries under ## What exists already (typed web, got $n)"
285
+ fi
286
+ fi
287
+ fi
288
+ ;;
289
+ 02-scope.md)
290
+ body="$(_heading_slice "$file" "Features in v1")"
291
+ if ! printf '%s\n' "$body" | _midtier_l_above_a; then
292
+ _midtier_hit "L-impact feature above grade A in ## Features in v1"
293
+ fi
294
+ _midtier_open_decisions "$file"
295
+ ;;
296
+ 03-prd.md)
297
+ body="$(_heading_slice "$file" "Features")"
298
+ adj="$(printf '%s\n' "$body" | _midtier_adjectives)"
299
+ if [ -n "$adj" ]; then
300
+ _midtier_hit "unquantified adjective '$adj' in ## Features"
301
+ else
302
+ body="$(_heading_slice "$file" "Non-functional requirements")"
303
+ adj="$(printf '%s\n' "$body" | _midtier_adjectives)"
304
+ if [ -n "$adj" ]; then
305
+ _midtier_hit "unquantified adjective '$adj' in ## Non-functional requirements"
306
+ fi
307
+ fi
308
+ _midtier_open_decisions "$file"
309
+ ;;
310
+ 05-contract.md)
311
+ _midtier_open_decisions "$file"
312
+ ;;
313
+ esac
314
+ [ "$_MIDTIER_FAILS" -eq 0 ]
315
+ }
316
+
193
317
  _python() {
194
318
  # prefer python3, and only accept an interpreter that is actually Python 3.x (the harness +
195
319
  # repo_map.py are py3-only — a py2 `python` must NOT be selected or it fails silently).
@@ -437,6 +561,18 @@ planning_complete() { # 0 = yes: each stage is clean, OR debt-skipped (a skipped
437
561
  return 0
438
562
  }
439
563
 
564
+ # Files exist (or debt-skipped). Used by project-type lock so a midtier-dirty
565
+ # 01 cannot unlock a type flip after stages 00-05 are on disk.
566
+ planning_artifacts_present() {
567
+ local s
568
+ for s in $STAGES; do
569
+ if [ ! -f "$FLOW_DIR/$s.md" ]; then
570
+ stage_skipped "$s" || return 1
571
+ fi
572
+ done
573
+ return 0
574
+ }
575
+
440
576
  # project type (web|cli|library|skill); absent => web. Adapts done-evidence + guidance.
441
577
  get_project_type() {
442
578
  local t; t="$(cat "$PROJECT_TYPE_FILE" 2>/dev/null | tr -d '\r' | awk 'NF{print; exit}')"
@@ -750,6 +886,14 @@ _stage_dwell() { # $1 = current stage name -> "Xm/Xh/Xd" from genuine entry, or
750
886
  }
751
887
 
752
888
  cmd_status() {
889
+ if [ "${1:-}" = "--json" ]; then
890
+ if [ -n "${2:-}" ]; then usage; exit 2; fi
891
+ _emit_context_json
892
+ return 0
893
+ elif [ -n "${1:-}" ]; then
894
+ usage
895
+ exit 2
896
+ fi
753
897
  local idx; idx="$(current_stage_idx)"
754
898
  echo "flow status"
755
899
  echo " project: $ROOT"
@@ -759,7 +903,7 @@ cmd_status() {
759
903
  echo " auto: $_ast$([ "$_ast" = ACTIVE ] && echo " (receipts enforced)" || true)"
760
904
  lock_warn
761
905
  echo
762
- echo "NEXT -> $(_next_action)"
906
+ _emit_next
763
907
  echo
764
908
  if [ -f "$FLOW_DIR/00-inspect.md" ]; then
765
909
  if scan_gate "$FLOW_DIR/00-inspect.md" >/dev/null 2>&1; then
@@ -854,9 +998,19 @@ _resume_valid_lines() {
854
998
  }
855
999
 
856
1000
  cmd_resume() {
1001
+ if [ "${1:-}" = "--json" ]; then
1002
+ if [ -n "${2:-}" ]; then usage; exit 2; fi
1003
+ _emit_context_json
1004
+ return 0
1005
+ elif [ -n "${1:-}" ]; then
1006
+ usage
1007
+ exit 2
1008
+ fi
857
1009
  local idx; idx="$(current_stage_idx)"
858
1010
  if [ "$idx" -lt 0 ]; then
859
- echo "nothing to resume - $(_next_action)"
1011
+ _na="$(_next_action)"
1012
+ echo "nothing to resume - $_na"
1013
+ echo "NEXT_VERB=$(_next_verb_from "$_na")"
860
1014
  return 0
861
1015
  fi
862
1016
 
@@ -874,7 +1028,7 @@ cmd_resume() {
874
1028
  echo
875
1029
  _gate_state_brief "$idx"
876
1030
  echo
877
- echo "NEXT -> $(_next_action)"
1031
+ _emit_next
878
1032
  return 0
879
1033
  fi
880
1034
 
@@ -966,7 +1120,7 @@ cmd_resume() {
966
1120
  echo
967
1121
 
968
1122
  # ---- NEXT (shared with `status` via _next_action - the two verbs can never disagree) ----
969
- echo "NEXT -> $(_next_action)"
1123
+ _emit_next
970
1124
  }
971
1125
 
972
1126
  cmd_next() {
@@ -1005,7 +1159,7 @@ cmd_next() {
1005
1159
  # attribute the failing event to THIS stage (so usage top-fail-stage + propose can see it)
1006
1160
  # and record WHICH checks failed, so a chronically-failing stage is diagnosable.
1007
1161
  FLOW_LOG_STAGE_TO="$cur"
1008
- FLOW_LAST_GATE_FAIL="fill:$(grep -c '\[FILL' "$FLOW_DIR/$cur.md" 2>/dev/null || echo 0),unchecked:$(grep -cE '^[[:space:]]*- \[ \]' "$FLOW_DIR/$cur.md" 2>/dev/null || echo 0)"
1162
+ FLOW_LAST_GATE_FAIL="fill:$(grep -c '\[FILL' "$FLOW_DIR/$cur.md" 2>/dev/null || echo 0),unchecked:$(grep -cE '^[[:space:]]*- \[ \]' "$FLOW_DIR/$cur.md" 2>/dev/null || echo 0),midtier:${_MIDTIER_FAILS:-0}"
1009
1163
  echo
1010
1164
  echo "Fix the above, then run '/flow next' again. (Kill at a gate is also valid.)"
1011
1165
  return 1
@@ -1179,6 +1333,76 @@ _next_action() {
1179
1333
  echo "run '/flow check' then ship per stage 09"
1180
1334
  }
1181
1335
 
1336
+ # Closed advisory enum from _next_action prose only. Hosts must not auto-exec
1337
+ # next/card/card-start/check/fix-gate/auto/skip.
1338
+ _next_verb_from() { # $1 = _next_action text
1339
+ local a="$1"
1340
+ case "$a" in
1341
+ *"fix gate:"*) echo fix-gate ;;
1342
+ *"/flow card start"*) echo card-start ;;
1343
+ *"/flow card"*) echo card ;;
1344
+ *"/flow check"*) echo check ;;
1345
+ *"/flow next"*) echo next ;;
1346
+ *"in flight"*) echo card-start ;;
1347
+ *) echo none ;;
1348
+ esac
1349
+ }
1350
+
1351
+ _emit_next() {
1352
+ local a
1353
+ a="$(_next_action)"
1354
+ echo "NEXT -> $a"
1355
+ echo "NEXT_VERB=$(_next_verb_from "$a")"
1356
+ }
1357
+
1358
+ # PTC-aligned load hint for JSON status/resume. Display-only — never a capability token.
1359
+ # Matches SKILL.md load table: card/build -> law/CLAUDE.md; fix-gate/next/none stay empty
1360
+ # (mechanical FAIL is the blocked stage file; next PASS few-shots are after unlock).
1361
+ _json_load_for_verb() { # $1 = next_verb
1362
+ case "${1:-}" in
1363
+ card|card-start|check) echo "law/CLAUDE.md" ;;
1364
+ *) echo "" ;;
1365
+ esac
1366
+ }
1367
+
1368
+ # Closed flow_context/v1 printer. Scalars through _json_str. scan_gate never piped.
1369
+ # Hosts MUST NOT dispatch next_verb (next/card/card-start/check/fix-gate/auto/skip).
1370
+ _emit_context_json() {
1371
+ local idx cur f stage="" gate="none" next_verb card="" dwell="" load na infl id ts
1372
+ idx="$(current_stage_idx)"
1373
+ if [ "$idx" -ge 0 ]; then
1374
+ cur="$(stage_name_at "$idx")"
1375
+ stage="$cur"
1376
+ f="$FLOW_DIR/$cur.md"
1377
+ if [ -f "$f" ]; then
1378
+ if scan_gate "$f" >/dev/null 2>&1; then
1379
+ gate="PASS"
1380
+ else
1381
+ gate="BLOCKED"
1382
+ fi
1383
+ fi
1384
+ dwell="$(_stage_dwell "$cur")"
1385
+ fi
1386
+ na="$(_next_action)"
1387
+ next_verb="$(_next_verb_from "$na")"
1388
+ load="$(_json_load_for_verb "$next_verb")"
1389
+ infl="$(_inflight_file)"
1390
+ if [ -f "$infl" ]; then
1391
+ while read -r id ts; do
1392
+ [ -n "$id" ] || continue
1393
+ f="$(resolve_card_file "$id" 2>/dev/null)"
1394
+ [ -n "$f" ] && [ -f "$f" ] || continue
1395
+ [ "$(card_status "$f")" = "todo" ] || continue
1396
+ card="$id"
1397
+ break
1398
+ done < "$infl"
1399
+ fi
1400
+ printf '{"v":"%s","stage":"%s","gate":"%s","next_verb":"%s","card":"%s","dwell":"%s","load":"%s"}\n' \
1401
+ "$(_json_str "flow_context/v1")" "$(_json_str "$stage")" "$(_json_str "$gate")" \
1402
+ "$(_json_str "$next_verb")" "$(_json_str "$card")" "$(_json_str "$dwell")" "$(_json_str "$load")"
1403
+ }
1404
+
1405
+
1182
1406
  _set_card_status() { # $1=file $2=value -> rewrite the ^status: line (portable substitute; temp+mv)
1183
1407
  local f="$1" v="$2"
1184
1408
  sed "s/^status:.*/status: $v/" "$f" > "$f.tmp" 2>/dev/null || { rm -f "$f.tmp"; return 1; }
@@ -1590,12 +1814,13 @@ cmd_project_type() {
1590
1814
  fi
1591
1815
  case "$arg" in
1592
1816
  web|cli|library|skill)
1593
- # After planning is complete AND an explicit type file exists, refuse silent
1817
+ # After planning artifacts exist AND an explicit type file exists, refuse silent
1594
1818
  # type flips unless FLOW_FORCE=1 (D6). Default-web with no file is not "locked".
1595
- if planning_complete 2>/dev/null && [ -f "$PROJECT_TYPE_FILE" ]; then
1819
+ # Gate cleanliness is NOT required: a midtier-dirty 01 must not unlock a flip.
1820
+ if planning_artifacts_present 2>/dev/null && [ -f "$PROJECT_TYPE_FILE" ]; then
1596
1821
  local cur; cur="$(get_project_type)"
1597
1822
  if [ -n "$cur" ] && [ "$cur" != "$arg" ] && [ "${FLOW_FORCE:-0}" != "1" ]; then
1598
- echo "FAIL: project type is locked to '$cur' after planning completes (would change done-evidence lens)."
1823
+ echo "FAIL: project type is locked to '$cur' once planning artifacts exist (would change done-evidence lens)."
1599
1824
  echo " re-set with FLOW_FORCE=1 if intentional, and record a DEBT.md line for the change."
1600
1825
  return 1
1601
1826
  fi
@@ -3157,22 +3382,33 @@ _eval_heading_pattern() {
3157
3382
  case "$1" in
3158
3383
  01) echo '^## Stage 01' ;;
3159
3384
  02) echo '^## Stage 02' ;;
3385
+ 05) echo '^## Stage 05' ;;
3160
3386
  card) echo '^## Card gate' ;;
3161
3387
  *) return 1 ;;
3162
3388
  esac
3163
3389
  }
3164
3390
 
3165
- # Extract one stage's ritual text: from its heading line up to (not including) the next '## '
3166
- # heading, or EOF. Prints nothing on a bad stage/missing file - caller MUST assert non-empty
3167
- # before any billable call (a silent empty extraction would judge against no rule at all).
3168
- _eval_extract_section() { # $1 = stage (01|02|card)
3169
- local pat; pat="$(_eval_heading_pattern "$1")" || return 1
3170
- [ -f "$GATE_RULES_FILE" ] || return 1
3391
+ # Extract one stage's ritual text from the mapped file (01→gate-01.md, 02→gate-02.md,
3392
+ # 05→gate-05.md, card→gate-card.md): from its heading line up to (not including) the next
3393
+ # '## ' heading, or EOF. Prints nothing on a bad stage/missing file - caller MUST assert
3394
+ # non-empty before any billable call (a silent empty extraction would judge against
3395
+ # no rule at all).
3396
+ _eval_extract_section() { # $1 = stage (01|02|05|card)
3397
+ local pat src
3398
+ pat="$(_eval_heading_pattern "$1")" || return 1
3399
+ case "$1" in
3400
+ 01) src="$SCRIPT_DIR/../references/gate-01.md" ;;
3401
+ 02) src="$SCRIPT_DIR/../references/gate-02.md" ;;
3402
+ 05) src="$SCRIPT_DIR/../references/gate-05.md" ;;
3403
+ card) src="$SCRIPT_DIR/../references/gate-card.md" ;;
3404
+ *) return 1 ;;
3405
+ esac
3406
+ [ -f "$src" ] || return 1
3171
3407
  awk -v pat="$pat" '
3172
3408
  $0 ~ pat { f=1; print; next }
3173
3409
  f && /^## / { f=0 }
3174
3410
  f { print }
3175
- ' "$GATE_RULES_FILE"
3411
+ ' "$src"
3176
3412
  }
3177
3413
 
3178
3414
  # Usability probe: zero cost if `claude` is absent; exactly one minimal billable call if present.
@@ -3227,12 +3463,20 @@ _eval_isolated_run() {
3227
3463
  }
3228
3464
 
3229
3465
 
3230
- # Build the judge prompt for one fixture. Returns 1 (writes nothing usable) if the gate-rules.md
3231
- # section extraction came back empty.
3466
+ # Build the judge prompt for one fixture. The challenge block is the extract output
3467
+ # (already the judge bytes; `_eval_gate_rules_sha` is not stdin). Returns 1 if empty.
3232
3468
  _eval_build_prompt() { # $1=outfile $2=stage $3=artifact-file $4=nonce
3233
- local outfile="$1" stage="$2" artifact="$3" nonce="$4" section
3469
+ local outfile="$1" stage="$2" artifact="$3" nonce="$4" section shared
3234
3470
  section="$(_eval_extract_section "$stage")" || return 1
3235
3471
  [ -n "$section" ] || return 1
3472
+ # Shared OD/authority lives in 02/03/05 challenges. Extract maps 01/02/05/card;
3473
+ # append shared onto 02 only. 05 extract may mention gate-shared.md; do not cat it
3474
+ # (shared OD on 05 false-FLAGs f05a — PASS fixture states bearer auth without an ADR).
3475
+ if [ "$stage" = "02" ]; then
3476
+ shared="$SCRIPT_DIR/../references/gate-shared.md"
3477
+ [ -f "$shared" ] && section="$section
3478
+ $(cat "$shared")"
3479
+ fi
3236
3480
  {
3237
3481
  printf 'You are reviewing a build-process artifact against the quality-gate challenge below.\n'
3238
3482
  printf 'Read the challenge, then the artifact, then decide honestly: does the artifact\n'
@@ -3311,11 +3555,27 @@ _eval_nonce_epoch() { # $1=run_id -> epoch seconds (or empty)
3311
3555
  printf '%s' "$1" | awk -F- 'NF>=2 { print $(NF-1) }'
3312
3556
  }
3313
3557
 
3314
- # CRLF-normalized gate-rules.md hash: a raw-byte hash would differ per checkout line-endings
3315
- # (git autocrlf) and produce a false "prose changed" drift alarm across identical content.
3558
+ # CRLF-normalized hash of the explicit gate-rules corpus (index + shared + 11
3559
+ # section files). Never glob `gate-*.md` (would pull gate-eval.md + gate-examples.md).
3560
+ # A raw-byte hash would differ per checkout line-endings (git autocrlf).
3316
3561
  _eval_gate_rules_sha() {
3317
- [ -f "$GATE_RULES_FILE" ] || { printf 'unknown'; return; }
3318
- tr -d '\r' < "$GATE_RULES_FILE" | cksum | awk '{print $1}'
3562
+ local ref="$SCRIPT_DIR/../references"
3563
+ [ -f "$ref/gate-rules.md" ] || { printf 'unknown'; return; }
3564
+ {
3565
+ cat "$ref/gate-rules.md"
3566
+ [ -f "$ref/gate-shared.md" ] && cat "$ref/gate-shared.md"
3567
+ [ -f "$ref/gate-assess.md" ] && cat "$ref/gate-assess.md"
3568
+ [ -f "$ref/gate-00.md" ] && cat "$ref/gate-00.md"
3569
+ [ -f "$ref/gate-01.md" ] && cat "$ref/gate-01.md"
3570
+ [ -f "$ref/gate-02.md" ] && cat "$ref/gate-02.md"
3571
+ [ -f "$ref/gate-03.md" ] && cat "$ref/gate-03.md"
3572
+ [ -f "$ref/gate-04.md" ] && cat "$ref/gate-04.md"
3573
+ [ -f "$ref/gate-05.md" ] && cat "$ref/gate-05.md"
3574
+ [ -f "$ref/gate-card.md" ] && cat "$ref/gate-card.md"
3575
+ [ -f "$ref/gate-consistency.md" ] && cat "$ref/gate-consistency.md"
3576
+ [ -f "$ref/gate-constitution.md" ] && cat "$ref/gate-constitution.md"
3577
+ [ -f "$ref/gate-debt.md" ] && cat "$ref/gate-debt.md"
3578
+ } | tr -d '\r' | cksum | awk '{print $1}'
3319
3579
  }
3320
3580
 
3321
3581
  # CRLF-normalized assembler-output hash (same recipe as _eval_gate_rules_sha). Covers
@@ -3781,6 +4041,9 @@ _eval_routing_print_scorecard() { # $1=run_id
3781
4041
  '
3782
4042
  }
3783
4043
 
4044
+ _eval_skip_unmeasured() { # $1 = kind (eval / routing eval / converge eval)
4045
+ echo "SKIP: 'claude' CLI not found on PATH - $1 needs it to run the LLM judge. Zero calls made."
4046
+ }
3784
4047
  # NOTE: defined with the `function name { }` form (no parens) rather than this file's usual
3785
4048
  # `name() { }` style, solely so the literal 4-byte substring the shipped verb name is built
3786
4049
  # from never appears immediately followed by '(' in the source - a blind text-pattern security
@@ -3812,7 +4075,8 @@ function cmd_eval_routing { # same arg shape as cmd_eval, called only when --sta
3812
4075
  local probe; probe="$(_eval_isolated_run _eval_probe)"
3813
4076
  case "$probe" in
3814
4077
  absent)
3815
- echo "SKIP: 'claude' CLI not found on PATH - routing eval needs it to run the LLM judge. Zero calls made."
4078
+ _eval_skip_unmeasured "routing eval"
4079
+ echo "semantic layer unmeasured on this host"
3816
4080
  return 0 ;;
3817
4081
  fail)
3818
4082
  echo "SKIP: 'claude' CLI is present but the sentinel probe did not come back clean (one minimal"
@@ -4123,7 +4387,7 @@ function cmd_eval_converge { # same arg shape as cmd_eval, called only when --st
4123
4387
 
4124
4388
  local probe; probe="$(_eval_isolated_run _eval_probe)"
4125
4389
  case "$probe" in
4126
- absent) echo "SKIP: 'claude' CLI not found on PATH - converge eval needs it to run the LLM judge. Zero calls made."; return 0 ;;
4390
+ absent) _eval_skip_unmeasured "converge eval"; echo "semantic layer unmeasured on this host"; return 0 ;;
4127
4391
  fail) echo "SKIP: 'claude' CLI present but the sentinel probe did not come back clean (one minimal probe call was made). Retry once 'claude -p' runs headless."; return 0 ;;
4128
4392
  esac
4129
4393
 
@@ -4238,6 +4502,8 @@ _eval_guard_unbounded_darwin() { # $1=replay_mode (0/1); return 1 = refused
4238
4502
  local replay_mode="${1:-0}"
4239
4503
  [ "$replay_mode" -eq 0 ] || return 0
4240
4504
  [ "${FLOW_EVAL_UNBOUNDED:-}" = "1" ] && return 0
4505
+ # No judge binary => no stuck call to bound. Probe SKIP (unmeasured) owns this case.
4506
+ command -v claude >/dev/null 2>&1 || return 0
4241
4507
  local darwin=0
4242
4508
  if [ "${FLOW_EVAL_FORCE_DARWIN:-}" = "1" ]; then
4243
4509
  darwin=1
@@ -4378,7 +4644,8 @@ function cmd_eval {
4378
4644
  local probe; probe="$(_eval_isolated_run _eval_probe)"
4379
4645
  case "$probe" in
4380
4646
  absent)
4381
- echo "SKIP: 'claude' CLI not found on PATH - eval needs it to run the LLM judge. Zero calls made."
4647
+ _eval_skip_unmeasured "eval"
4648
+ echo "semantic layer unmeasured on this host"
4382
4649
  return 0 ;;
4383
4650
  fail)
4384
4651
  echo "SKIP: 'claude' CLI is present but the sentinel probe did not come back clean (one minimal"
@@ -4442,7 +4709,7 @@ function cmd_eval {
4442
4709
  [ -z "$fid" ] && continue
4443
4710
  # manifest's stage column uses flow.sh's own full STAGES names ("01-research", "02-scope",
4444
4711
  # "card") for consistency with the rest of the codebase; --stage/heading-map use the terse
4445
- # 01|02|card form the plan's CLI spec documents - normalize by stripping to the leading
4712
+ # 01|02|05|card form the plan's CLI spec documents - normalize by stripping to the leading
4446
4713
  # token before the first '-' (a bare "card" has none, so it passes through unchanged).
4447
4714
  local fstage_short="${fstage%%-*}"
4448
4715
  [ -n "$stage_filter" ] && [ "$fstage_short" != "$stage_filter" ] && continue
@@ -4468,7 +4735,7 @@ function cmd_eval {
4468
4735
  local promptfile="$rundir/prompt.txt"
4469
4736
  if ! _eval_build_prompt "$promptfile" "$fstage_short" "$artifact_path" "$nonce"; then
4470
4737
  if [ "$replay_mode" -eq 1 ]; then
4471
- echo " $fid: SKIP - no heading-mapped section (artifact-replay scope is 01|02|card)"
4738
+ echo " $fid: SKIP - no heading-mapped section (artifact-replay scope is 01|02|05|card)"
4472
4739
  rm -rf "$rundir" 2>/dev/null
4473
4740
  continue
4474
4741
  fi
@@ -4699,10 +4966,12 @@ flow.sh - buildflow gate runner (mechanical layer)
4699
4966
 
4700
4967
  usage: bash flow.sh <command> [args]
4701
4968
 
4702
- status Where am I? What's blocking? (also: no command)
4703
- resume Session-story brief for a fresh agent entering mid-cycle: last session
4969
+ status [--json] Where am I? What's blocking? (also: no command). --json prints
4970
+ one flow_context/v1 object (display-only; do not auto-exec next_verb).
4971
+ resume [--json] Session-story brief for a fresh agent entering mid-cycle: last session
4704
4972
  (command names only, never raw args), in-flight card + dwell, gate state,
4705
4973
  one NEXT-> line. Read-only, no lock. Run this FIRST when resuming a project.
4974
+ --json: same closed object as status --json.
4706
4975
  next Check current gate; unlock next stage (or start at 00)
4707
4976
  assess Brownfield: scaffold + gate a current-state assessment (flow/00-inspect.md) before planning
4708
4977
  card Create the next build card (after planning complete)
@@ -4911,8 +5180,8 @@ fi
4911
5180
  FLOW_LOG_START="$(_now)"
4912
5181
  trap '_log_on_exit' EXIT
4913
5182
  case "$cmd" in
4914
- status|"") cmd_status ;;
4915
- resume) cmd_resume ;;
5183
+ status|"") cmd_status "$@" ;;
5184
+ resume) cmd_resume "$@" ;;
4916
5185
  next) cmd_next ;;
4917
5186
  assess) cmd_assess ;;
4918
5187
  card) cmd_card "$@" ;;
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "source": "../skills/flow",
3
3
  "destRelative": "skills/flow",
4
- "fileCount": 103,
5
- "syncedAt": "2026-08-20T13:35:30.768Z",
4
+ "fileCount": 117,
5
+ "syncedAt": "2026-09-17T15:38:14.807Z",
6
6
  "fileList": [
7
7
  "SKILL.md",
8
8
  "_templates/00-idea.md",
@@ -69,6 +69,7 @@
69
69
  "harness/schema/012-usage-ephemeral.sql",
70
70
  "harness/schema/014-graph-executor.sql",
71
71
  "law/CLAUDE.md",
72
+ "law/CODING.md",
72
73
  "law/DESIGN.md",
73
74
  "law/RETRO.md",
74
75
  "playbooks/README.md",
@@ -94,8 +95,21 @@
94
95
  "references/flow-catalog.tsv",
95
96
  "references/flow-topology.json",
96
97
  "references/forge-idea.md",
98
+ "references/gate-00.md",
99
+ "references/gate-01.md",
100
+ "references/gate-02.md",
101
+ "references/gate-03.md",
102
+ "references/gate-04.md",
103
+ "references/gate-05.md",
104
+ "references/gate-assess.md",
105
+ "references/gate-card.md",
106
+ "references/gate-consistency.md",
107
+ "references/gate-constitution.md",
108
+ "references/gate-debt.md",
97
109
  "references/gate-eval.md",
110
+ "references/gate-examples.md",
98
111
  "references/gate-rules.md",
112
+ "references/gate-shared.md",
99
113
  "references/ground-truth-gates.md",
100
114
  "references/host-agnostic-parallel.md",
101
115
  "references/loop-harness-2026-principles.md",