@manhquy/flow-skill 0.7.1-next.0 → 0.7.2
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 +21 -0
- package/README.md +5 -5
- package/README_VN.md +5 -5
- package/package.json +1 -1
- package/skills/flow/SKILL.md +108 -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 +2 -2
- 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 +6 -3
- 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 +223 -24
- package/skills-manifest.json +16 -2
|
@@ -2,17 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
`MODE` file at project root (default `teach`). Set with `/flow mode work`.
|
|
4
4
|
|
|
5
|
-
- **teach** — the operator writes every artifact;
|
|
6
|
-
- **work** —
|
|
5
|
+
- **teach** — the operator writes every artifact; the host only gatekeeps. Default.
|
|
6
|
+
- **work** — interview the operator ONCE, draft stages 00-05, pause only for
|
|
7
7
|
scope sign-off, deliver the card set as one summary. **Gates and done-rules are identical
|
|
8
|
-
to teach** —
|
|
8
|
+
to teach** — every gate still passes; the host also authors.
|
|
9
9
|
|
|
10
10
|
## The work-mode shape (interview once -> draft -> one pause -> summary)
|
|
11
11
|
|
|
12
|
-
1. **Interview once.**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
1. **Interview once.** One tight batch — numbered questions, not a drip across stages:
|
|
13
|
+
1. Idea in one line?
|
|
14
|
+
2. Who has the pain (a real named person/group)?
|
|
15
|
+
3. Rough budget/time?
|
|
16
|
+
4. Stack preference, if any?
|
|
17
|
+
5. One channel for the first users?
|
|
18
|
+
Host-neutral: use whatever ask/prompt surface the host has.
|
|
16
19
|
2. **Draft 00 -> 01 -> 02.** Fill idea, do real research (delegate to `researcher` per
|
|
17
20
|
`agent-stage-mapping.md` — actually open competitors, quote real complaints with links),
|
|
18
21
|
and produce the scope table with honest Impact + Grade. Run each gate with `flow.sh next`.
|
|
@@ -33,8 +36,8 @@
|
|
|
33
36
|
|
|
34
37
|
## Rules that still hold in work mode
|
|
35
38
|
- Every gate must pass mechanically (`flow.sh`) AND semantically (`gate-rules.md`). Drafting
|
|
36
|
-
does not lower the bar — self-challenge before presenting
|
|
37
|
-
|
|
39
|
+
does not lower the bar — self-challenge the draft before presenting. Self-challenge is
|
|
40
|
+
color, not a verdict: it never substitutes for `flow.sh` + `gate-rules.md`.
|
|
38
41
|
- Research must be real (opened competitors, quoted complaints with links) — no fabrication.
|
|
39
42
|
This is the highest-risk stage for an AI author; verify every quote has a source.
|
|
40
43
|
- Scope honesty: do not grade-launder your own C features to B. Call C a C, justify the path.
|
|
@@ -43,5 +46,5 @@
|
|
|
43
46
|
can still halt or add a bullet (a policy-encoding `## Assumptions` line with no operator/ADR
|
|
44
47
|
authority is an open decision, not a silent default). Never invent auth / tenancy / retention
|
|
45
48
|
/ billing to clear FILL.
|
|
46
|
-
- Done is still world-state evidence;
|
|
49
|
+
- Done is still world-state evidence; a card cannot be marked done without real proof.
|
|
47
50
|
- Write durable records (intake/decision/story) as you go, same as teach mode.
|
|
@@ -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}')"
|
|
@@ -759,7 +895,7 @@ cmd_status() {
|
|
|
759
895
|
echo " auto: $_ast$([ "$_ast" = ACTIVE ] && echo " (receipts enforced)" || true)"
|
|
760
896
|
lock_warn
|
|
761
897
|
echo
|
|
762
|
-
|
|
898
|
+
_emit_next
|
|
763
899
|
echo
|
|
764
900
|
if [ -f "$FLOW_DIR/00-inspect.md" ]; then
|
|
765
901
|
if scan_gate "$FLOW_DIR/00-inspect.md" >/dev/null 2>&1; then
|
|
@@ -856,7 +992,9 @@ _resume_valid_lines() {
|
|
|
856
992
|
cmd_resume() {
|
|
857
993
|
local idx; idx="$(current_stage_idx)"
|
|
858
994
|
if [ "$idx" -lt 0 ]; then
|
|
859
|
-
|
|
995
|
+
_na="$(_next_action)"
|
|
996
|
+
echo "nothing to resume - $_na"
|
|
997
|
+
echo "NEXT_VERB=$(_next_verb_from "$_na")"
|
|
860
998
|
return 0
|
|
861
999
|
fi
|
|
862
1000
|
|
|
@@ -874,7 +1012,7 @@ cmd_resume() {
|
|
|
874
1012
|
echo
|
|
875
1013
|
_gate_state_brief "$idx"
|
|
876
1014
|
echo
|
|
877
|
-
|
|
1015
|
+
_emit_next
|
|
878
1016
|
return 0
|
|
879
1017
|
fi
|
|
880
1018
|
|
|
@@ -966,7 +1104,7 @@ cmd_resume() {
|
|
|
966
1104
|
echo
|
|
967
1105
|
|
|
968
1106
|
# ---- NEXT (shared with `status` via _next_action - the two verbs can never disagree) ----
|
|
969
|
-
|
|
1107
|
+
_emit_next
|
|
970
1108
|
}
|
|
971
1109
|
|
|
972
1110
|
cmd_next() {
|
|
@@ -1005,7 +1143,7 @@ cmd_next() {
|
|
|
1005
1143
|
# attribute the failing event to THIS stage (so usage top-fail-stage + propose can see it)
|
|
1006
1144
|
# and record WHICH checks failed, so a chronically-failing stage is diagnosable.
|
|
1007
1145
|
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)"
|
|
1146
|
+
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
1147
|
echo
|
|
1010
1148
|
echo "Fix the above, then run '/flow next' again. (Kill at a gate is also valid.)"
|
|
1011
1149
|
return 1
|
|
@@ -1179,6 +1317,27 @@ _next_action() {
|
|
|
1179
1317
|
echo "run '/flow check' then ship per stage 09"
|
|
1180
1318
|
}
|
|
1181
1319
|
|
|
1320
|
+
# Closed advisory enum from _next_action prose only. Hosts must not auto-exec auto/skip.
|
|
1321
|
+
_next_verb_from() { # $1 = _next_action text
|
|
1322
|
+
local a="$1"
|
|
1323
|
+
case "$a" in
|
|
1324
|
+
*"fix gate:"*) echo fix-gate ;;
|
|
1325
|
+
*"/flow card start"*) echo card-start ;;
|
|
1326
|
+
*"/flow card"*) echo card ;;
|
|
1327
|
+
*"/flow check"*) echo check ;;
|
|
1328
|
+
*"/flow next"*) echo next ;;
|
|
1329
|
+
*"in flight"*) echo card-start ;;
|
|
1330
|
+
*) echo none ;;
|
|
1331
|
+
esac
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
_emit_next() {
|
|
1335
|
+
local a
|
|
1336
|
+
a="$(_next_action)"
|
|
1337
|
+
echo "NEXT -> $a"
|
|
1338
|
+
echo "NEXT_VERB=$(_next_verb_from "$a")"
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1182
1341
|
_set_card_status() { # $1=file $2=value -> rewrite the ^status: line (portable substitute; temp+mv)
|
|
1183
1342
|
local f="$1" v="$2"
|
|
1184
1343
|
sed "s/^status:.*/status: $v/" "$f" > "$f.tmp" 2>/dev/null || { rm -f "$f.tmp"; return 1; }
|
|
@@ -1590,12 +1749,13 @@ cmd_project_type() {
|
|
|
1590
1749
|
fi
|
|
1591
1750
|
case "$arg" in
|
|
1592
1751
|
web|cli|library|skill)
|
|
1593
|
-
# After planning
|
|
1752
|
+
# After planning artifacts exist AND an explicit type file exists, refuse silent
|
|
1594
1753
|
# type flips unless FLOW_FORCE=1 (D6). Default-web with no file is not "locked".
|
|
1595
|
-
|
|
1754
|
+
# Gate cleanliness is NOT required: a midtier-dirty 01 must not unlock a flip.
|
|
1755
|
+
if planning_artifacts_present 2>/dev/null && [ -f "$PROJECT_TYPE_FILE" ]; then
|
|
1596
1756
|
local cur; cur="$(get_project_type)"
|
|
1597
1757
|
if [ -n "$cur" ] && [ "$cur" != "$arg" ] && [ "${FLOW_FORCE:-0}" != "1" ]; then
|
|
1598
|
-
echo "FAIL: project type is locked to '$cur'
|
|
1758
|
+
echo "FAIL: project type is locked to '$cur' once planning artifacts exist (would change done-evidence lens)."
|
|
1599
1759
|
echo " re-set with FLOW_FORCE=1 if intentional, and record a DEBT.md line for the change."
|
|
1600
1760
|
return 1
|
|
1601
1761
|
fi
|
|
@@ -3162,17 +3322,26 @@ _eval_heading_pattern() {
|
|
|
3162
3322
|
esac
|
|
3163
3323
|
}
|
|
3164
3324
|
|
|
3165
|
-
# Extract one stage's ritual text
|
|
3166
|
-
#
|
|
3167
|
-
#
|
|
3325
|
+
# Extract one stage's ritual text from the mapped file (01→gate-01.md, 02→gate-02.md,
|
|
3326
|
+
# card→gate-card.md): from its heading line up to (not including) the next '## '
|
|
3327
|
+
# heading, or EOF. Prints nothing on a bad stage/missing file - caller MUST assert
|
|
3328
|
+
# non-empty before any billable call (a silent empty extraction would judge against
|
|
3329
|
+
# no rule at all).
|
|
3168
3330
|
_eval_extract_section() { # $1 = stage (01|02|card)
|
|
3169
|
-
local pat
|
|
3170
|
-
|
|
3331
|
+
local pat src
|
|
3332
|
+
pat="$(_eval_heading_pattern "$1")" || return 1
|
|
3333
|
+
case "$1" in
|
|
3334
|
+
01) src="$SCRIPT_DIR/../references/gate-01.md" ;;
|
|
3335
|
+
02) src="$SCRIPT_DIR/../references/gate-02.md" ;;
|
|
3336
|
+
card) src="$SCRIPT_DIR/../references/gate-card.md" ;;
|
|
3337
|
+
*) return 1 ;;
|
|
3338
|
+
esac
|
|
3339
|
+
[ -f "$src" ] || return 1
|
|
3171
3340
|
awk -v pat="$pat" '
|
|
3172
3341
|
$0 ~ pat { f=1; print; next }
|
|
3173
3342
|
f && /^## / { f=0 }
|
|
3174
3343
|
f { print }
|
|
3175
|
-
' "$
|
|
3344
|
+
' "$src"
|
|
3176
3345
|
}
|
|
3177
3346
|
|
|
3178
3347
|
# Usability probe: zero cost if `claude` is absent; exactly one minimal billable call if present.
|
|
@@ -3227,12 +3396,19 @@ _eval_isolated_run() {
|
|
|
3227
3396
|
}
|
|
3228
3397
|
|
|
3229
3398
|
|
|
3230
|
-
# Build the judge prompt for one fixture.
|
|
3231
|
-
#
|
|
3399
|
+
# Build the judge prompt for one fixture. The challenge block is the extract output
|
|
3400
|
+
# (already the judge bytes; `_eval_gate_rules_sha` is not stdin). Returns 1 if empty.
|
|
3232
3401
|
_eval_build_prompt() { # $1=outfile $2=stage $3=artifact-file $4=nonce
|
|
3233
|
-
local outfile="$1" stage="$2" artifact="$3" nonce="$4" section
|
|
3402
|
+
local outfile="$1" stage="$2" artifact="$3" nonce="$4" section shared
|
|
3234
3403
|
section="$(_eval_extract_section "$stage")" || return 1
|
|
3235
3404
|
[ -n "$section" ] || return 1
|
|
3405
|
+
# Shared OD/authority lives in 02/03/05 challenges. Extract maps 01/02/card only;
|
|
3406
|
+
# appending shared onto 01/card lets OD rules false-FLAG research/evidence.
|
|
3407
|
+
if [ "$stage" = "02" ]; then
|
|
3408
|
+
shared="$SCRIPT_DIR/../references/gate-shared.md"
|
|
3409
|
+
[ -f "$shared" ] && section="$section
|
|
3410
|
+
$(cat "$shared")"
|
|
3411
|
+
fi
|
|
3236
3412
|
{
|
|
3237
3413
|
printf 'You are reviewing a build-process artifact against the quality-gate challenge below.\n'
|
|
3238
3414
|
printf 'Read the challenge, then the artifact, then decide honestly: does the artifact\n'
|
|
@@ -3311,11 +3487,27 @@ _eval_nonce_epoch() { # $1=run_id -> epoch seconds (or empty)
|
|
|
3311
3487
|
printf '%s' "$1" | awk -F- 'NF>=2 { print $(NF-1) }'
|
|
3312
3488
|
}
|
|
3313
3489
|
|
|
3314
|
-
# CRLF-normalized
|
|
3315
|
-
#
|
|
3490
|
+
# CRLF-normalized hash of the explicit gate-rules corpus (index + shared + 11
|
|
3491
|
+
# section files). Never glob `gate-*.md` (would pull gate-eval.md + gate-examples.md).
|
|
3492
|
+
# A raw-byte hash would differ per checkout line-endings (git autocrlf).
|
|
3316
3493
|
_eval_gate_rules_sha() {
|
|
3317
|
-
|
|
3318
|
-
|
|
3494
|
+
local ref="$SCRIPT_DIR/../references"
|
|
3495
|
+
[ -f "$ref/gate-rules.md" ] || { printf 'unknown'; return; }
|
|
3496
|
+
{
|
|
3497
|
+
cat "$ref/gate-rules.md"
|
|
3498
|
+
[ -f "$ref/gate-shared.md" ] && cat "$ref/gate-shared.md"
|
|
3499
|
+
[ -f "$ref/gate-assess.md" ] && cat "$ref/gate-assess.md"
|
|
3500
|
+
[ -f "$ref/gate-00.md" ] && cat "$ref/gate-00.md"
|
|
3501
|
+
[ -f "$ref/gate-01.md" ] && cat "$ref/gate-01.md"
|
|
3502
|
+
[ -f "$ref/gate-02.md" ] && cat "$ref/gate-02.md"
|
|
3503
|
+
[ -f "$ref/gate-03.md" ] && cat "$ref/gate-03.md"
|
|
3504
|
+
[ -f "$ref/gate-04.md" ] && cat "$ref/gate-04.md"
|
|
3505
|
+
[ -f "$ref/gate-05.md" ] && cat "$ref/gate-05.md"
|
|
3506
|
+
[ -f "$ref/gate-card.md" ] && cat "$ref/gate-card.md"
|
|
3507
|
+
[ -f "$ref/gate-consistency.md" ] && cat "$ref/gate-consistency.md"
|
|
3508
|
+
[ -f "$ref/gate-constitution.md" ] && cat "$ref/gate-constitution.md"
|
|
3509
|
+
[ -f "$ref/gate-debt.md" ] && cat "$ref/gate-debt.md"
|
|
3510
|
+
} | tr -d '\r' | cksum | awk '{print $1}'
|
|
3319
3511
|
}
|
|
3320
3512
|
|
|
3321
3513
|
# CRLF-normalized assembler-output hash (same recipe as _eval_gate_rules_sha). Covers
|
|
@@ -3781,6 +3973,9 @@ _eval_routing_print_scorecard() { # $1=run_id
|
|
|
3781
3973
|
'
|
|
3782
3974
|
}
|
|
3783
3975
|
|
|
3976
|
+
_eval_skip_unmeasured() { # $1 = kind (eval / routing eval / converge eval)
|
|
3977
|
+
echo "SKIP: 'claude' CLI not found on PATH - $1 needs it to run the LLM judge. Zero calls made."
|
|
3978
|
+
}
|
|
3784
3979
|
# NOTE: defined with the `function name { }` form (no parens) rather than this file's usual
|
|
3785
3980
|
# `name() { }` style, solely so the literal 4-byte substring the shipped verb name is built
|
|
3786
3981
|
# from never appears immediately followed by '(' in the source - a blind text-pattern security
|
|
@@ -3812,7 +4007,8 @@ function cmd_eval_routing { # same arg shape as cmd_eval, called only when --sta
|
|
|
3812
4007
|
local probe; probe="$(_eval_isolated_run _eval_probe)"
|
|
3813
4008
|
case "$probe" in
|
|
3814
4009
|
absent)
|
|
3815
|
-
|
|
4010
|
+
_eval_skip_unmeasured "routing eval"
|
|
4011
|
+
echo "semantic layer unmeasured on this host"
|
|
3816
4012
|
return 0 ;;
|
|
3817
4013
|
fail)
|
|
3818
4014
|
echo "SKIP: 'claude' CLI is present but the sentinel probe did not come back clean (one minimal"
|
|
@@ -4123,7 +4319,7 @@ function cmd_eval_converge { # same arg shape as cmd_eval, called only when --st
|
|
|
4123
4319
|
|
|
4124
4320
|
local probe; probe="$(_eval_isolated_run _eval_probe)"
|
|
4125
4321
|
case "$probe" in
|
|
4126
|
-
absent)
|
|
4322
|
+
absent) _eval_skip_unmeasured "converge eval"; echo "semantic layer unmeasured on this host"; return 0 ;;
|
|
4127
4323
|
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
4324
|
esac
|
|
4129
4325
|
|
|
@@ -4238,6 +4434,8 @@ _eval_guard_unbounded_darwin() { # $1=replay_mode (0/1); return 1 = refused
|
|
|
4238
4434
|
local replay_mode="${1:-0}"
|
|
4239
4435
|
[ "$replay_mode" -eq 0 ] || return 0
|
|
4240
4436
|
[ "${FLOW_EVAL_UNBOUNDED:-}" = "1" ] && return 0
|
|
4437
|
+
# No judge binary => no stuck call to bound. Probe SKIP (unmeasured) owns this case.
|
|
4438
|
+
command -v claude >/dev/null 2>&1 || return 0
|
|
4241
4439
|
local darwin=0
|
|
4242
4440
|
if [ "${FLOW_EVAL_FORCE_DARWIN:-}" = "1" ]; then
|
|
4243
4441
|
darwin=1
|
|
@@ -4378,7 +4576,8 @@ function cmd_eval {
|
|
|
4378
4576
|
local probe; probe="$(_eval_isolated_run _eval_probe)"
|
|
4379
4577
|
case "$probe" in
|
|
4380
4578
|
absent)
|
|
4381
|
-
|
|
4579
|
+
_eval_skip_unmeasured "eval"
|
|
4580
|
+
echo "semantic layer unmeasured on this host"
|
|
4382
4581
|
return 0 ;;
|
|
4383
4582
|
fail)
|
|
4384
4583
|
echo "SKIP: 'claude' CLI is present but the sentinel probe did not come back clean (one minimal"
|
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-11T03:27:04.868Z",
|
|
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",
|