@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.
- package/CHANGELOG.md +33 -0
- package/README.md +5 -5
- package/README_VN.md +5 -5
- package/package.json +1 -1
- package/skills/flow/SKILL.md +109 -221
- package/skills/flow/_templates/01-research.md +3 -2
- package/skills/flow/_templates/02-scope.md +2 -0
- package/skills/flow/_templates/03-prd.md +2 -1
- package/skills/flow/_templates/05-contract.md +1 -0
- package/skills/flow/_templates/card.md +5 -1
- package/skills/flow/law/CLAUDE.md +6 -5
- package/skills/flow/law/CODING.md +37 -0
- package/skills/flow/references/agent-detection.md +11 -9
- package/skills/flow/references/agent-stage-mapping.md +47 -12
- package/skills/flow/references/auto-run.md +37 -7
- package/skills/flow/references/clarify.md +7 -7
- package/skills/flow/references/command-dispatch.md +51 -51
- package/skills/flow/references/concierge.md +7 -5
- package/skills/flow/references/debt-and-halts.md +20 -5
- package/skills/flow/references/gate-00.md +10 -0
- package/skills/flow/references/gate-01.md +18 -0
- package/skills/flow/references/gate-02.md +16 -0
- package/skills/flow/references/gate-03.md +10 -0
- package/skills/flow/references/gate-04.md +12 -0
- package/skills/flow/references/gate-05.md +33 -0
- package/skills/flow/references/gate-assess.md +13 -0
- package/skills/flow/references/gate-card.md +25 -0
- package/skills/flow/references/gate-consistency.md +24 -0
- package/skills/flow/references/gate-constitution.md +19 -0
- package/skills/flow/references/gate-debt.md +5 -0
- package/skills/flow/references/gate-eval.md +16 -13
- package/skills/flow/references/gate-examples.md +78 -0
- package/skills/flow/references/gate-rules.md +40 -241
- package/skills/flow/references/gate-shared.md +37 -0
- package/skills/flow/references/ground-truth-gates.md +1 -1
- package/skills/flow/references/mode-work.md +13 -10
- package/skills/flow/runner/flow.sh +300 -31
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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'
|
|
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
|
|
3166
|
-
#
|
|
3167
|
-
#
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
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
|
-
' "$
|
|
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.
|
|
3231
|
-
#
|
|
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
|
|
3315
|
-
#
|
|
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
|
-
|
|
3318
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
|
4703
|
-
|
|
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 "$@" ;;
|
package/skills-manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "../skills/flow",
|
|
3
3
|
"destRelative": "skills/flow",
|
|
4
|
-
"fileCount":
|
|
5
|
-
"syncedAt": "2026-
|
|
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",
|