@chrono-meta/fh-gate 1.4.84 → 1.4.85
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/.claude-plugin/marketplace.json +2 -2
- package/CLAUDE.md +12 -4
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +16 -0
- package/package.json +2 -1
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/scripts/selfcheck.sh +13 -0
- package/scripts/session_close_check.sh +51 -0
- package/scripts/test_dispatch_log_lanes.sh +99 -0
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "fh-meta",
|
|
14
|
-
"version": "1.4.
|
|
14
|
+
"version": "1.4.85",
|
|
15
15
|
"description": "Hub meta-operations toolkit — 35 skills + 7 agents. New in 1.4.53: `fh-codex-doctor` (npm bin) — Codex adapter drift scanner; reads the documented M1/M2/M3 skill tier map + skill/agent source and reports codex-native/adapter-required/claude-native/unclassified per unit, wired into `npm test`/`prepublishOnly` (fail-closed on unclassified Claude-native primitives). New in 1.4.49: steel-quench gains Step 0.6 Verdict-Invariance Probe (groundedness axis — a load-bearing judged gate's verdict must track behavior, not rubric phrasing; measured flip-count over cross-family paraphrases; arXiv:2605.06161 Policy Invariance anchor); multi_model_sidecar_strategy §Vendor-native harness (a model is strongest in its own vendor CLI — Claude/CC, GPT/codex, Gemini/Antigravity; a universal router degrades all of them, so it stays an autocomplete/QA sidecar, never orchestration); predelete_check.sh fail-closed rewrite; memory-hygiene A-TMA anchor. New in 1.4.48: phantom-quench + steel-quench gain external frontier anchors (arXiv:2607.02052 package-hallucination; arXiv:2607.02057 prompt-coverage-adequacy); README model-flat claim reframed from a per-release point-curve to structural invariants (operation flattens across tiers; depth tier-order fixed within a generation). New in 1.4.47: onboarding step ① surfaces the Mode D companion-store session-start load in the auto-read salience anchor (previously only in the local binding + rules, so a greeting could skip the load). New in 1.4.46: context-doctor command-output axis (route to rtk/proxy for verbose CLI stdout, complementing .claudeignore; risk-gated to token-scarce envs). New in 1.4.41: context-doctor 2026 trigger vocab (context engineering/rot/collapse) + phantom-citation hardening; hub measurement-integrity-checklist (cross-model measurement pre-flight: display-name pin/reps≥3/discriminating probe). New in 1.4.40: install-wizard queryable-wiki scaffold (INDEX + session-start read + R/W/C ingest). New in 1.4.39: auto-decorrelation (cross-family verifier sidecar recruitment) + video-ingest (capability-routed video ingestion). New in 1.4.x: verify-axis check-class taxonomy (mandatory-pass/measured/judged), no-reinvention Tier-0 inventory, 7-class failure taxonomy, Destructive-Op Gate, Wave-T (Temper), tier-floor governance, Mode D Model Notice, FC consent lane, default-Sonnet guidance. New in 1.3.0: public-surface-audit, field-harvest Mode B auto-trigger, 4-axis gate scope ext. Validated cross-CLI: Claude Code, Codex, Gemini.",
|
|
16
16
|
"source": "./plugins/fh-meta"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"name": "fh-commons",
|
|
20
|
-
"version": "1.4.
|
|
20
|
+
"version": "1.4.85",
|
|
21
21
|
"description": "Project-agnostic utility skills — 4 skills (convergence-loop · deliberation · mcp-circuit-breaker · token-budget-gate) + 1 agent (quench-challenger). Domain-independent utilities transplantable into any project.",
|
|
22
22
|
"source": "./plugins/fh-commons"
|
|
23
23
|
}
|
package/CLAUDE.md
CHANGED
|
@@ -595,10 +595,18 @@ Default operation is a **standard interactive session**. Agent dispatch (single
|
|
|
595
595
|
|
|
596
596
|
Mapped paths: check `auto_project_mapping.md` or `find ~/projects -maxdepth 1 -type d` for actuals.
|
|
597
597
|
|
|
598
|
-
**Invocation log obligation
|
|
599
|
-
sub-agent invocation, append to `knowledge/shared/learnings/subagent_invocations_log.yaml`
|
|
600
|
-
outcome: `accepted`/`partial`/`rejected`/`sustained` — `sustained` = decided NOT to invoke,
|
|
601
|
-
This feeds the 60/40 promotion gate + UAP loop; detail:
|
|
598
|
+
**Invocation log obligation — now mechanically reconciled, not recall-dependent**: immediately after
|
|
599
|
+
any custom sub-agent invocation, append to `knowledge/shared/learnings/subagent_invocations_log.yaml`
|
|
600
|
+
(8 fields · outcome: `accepted`/`partial`/`rejected`/`sustained` — `sustained` = decided NOT to invoke,
|
|
601
|
+
also recorded). This feeds the 60/40 promotion gate + UAP loop; detail:
|
|
602
|
+
`knowledge/shared/rules/operations.md`.
|
|
603
|
+
**Floor (2026-08-02)**: a `SubagentStop` hook tallies every dispatch and `session_close_check.sh` ④-e
|
|
604
|
+
blocks a close push when dispatches happened and the day's entries are ZERO. Consolidating a class of
|
|
605
|
+
dispatches into one entry with measured counts is fine and is not penalised — the check catches the
|
|
606
|
+
total miss, not imperfect bookkeeping. **Why it needed a floor**: this line was prose-only and a single
|
|
607
|
+
session dispatched 20+ subagents and logged none of them, in the same session that recovered this very
|
|
608
|
+
log file from a branch queued for deletion. The hook only tallies; it never writes an entry, because a
|
|
609
|
+
fabricated `outcome`/`evidence` would poison the promotion gate worse than a missing one.
|
|
602
610
|
|
|
603
611
|
### Context Card — Required Format for Dispatch
|
|
604
612
|
```
|
|
@@ -812,3 +812,19 @@
|
|
|
812
812
|
whether an agent can locate it. Both arms independently cited the same residual (the paths: trigger is a READ,
|
|
813
813
|
so a from-scratch Write never loads the detail rule), which is exactly the path the probe encodes. Method costs
|
|
814
814
|
~2 min/section and replaces the coverage number that was cut for being wrong four times.'
|
|
815
|
+
|
|
816
|
+
- date: '2026-08-02'
|
|
817
|
+
agent: general-purpose x6 pinned to sonnet (ablation known pairs — 3 sections x 2 arms)
|
|
818
|
+
task: Ablation sweep — 3 sections x 2 arms (6 dispatches; the day's SubagentStop tally read 7, the extra being one earlier probe). Two largest MEASURABLE sections of CLAUDE.md — §Autonomous Initiative (12,224 chars,
|
|
819
|
+
8 probes) and §Session Wrap-up (8,398 chars, 2 probes) — plus the earlier §New Skill Creation Pre-Commit Gate
|
|
820
|
+
mode: paired arms per section (asset WITH vs WITHOUT the section), floor tier, all file access denied except the
|
|
821
|
+
one arm file
|
|
822
|
+
outcome: accepted
|
|
823
|
+
evidence: '3 sections ablated, 3 KEEP. §Autonomous Initiative: 5 routing behaviours GROUNDED in arm A and NOT
|
|
824
|
+
IN MY CONTEXT in arm B (context-doctor, harvest-loop, goal-quench, deep-clarify, the already-running Guard).
|
|
825
|
+
§Session Wrap-up: 4 of 6 close-chain answers fully absent without it (the ordered sequence, the absolute-last
|
|
826
|
+
rule and its reason, the late-finding re-run rule, the open-PR sweep). §New Skill gate: 3 of 3 absent.'
|
|
827
|
+
note: 'By-product worth more than the verdicts: the Pre-Publish row IN the initiative table is redundant — arm
|
|
828
|
+
B answered the full chain and the order invariant correctly from the gate''s own section (719 chars, ablatable).
|
|
829
|
+
And the headline: three measurements, three KEEPs. The ''CLAUDE.md is bloated'' premise is not surviving contact
|
|
830
|
+
— the earlier 46%-unmeasured framing measured probe coverage, which is not necessity.'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrono-meta/fh-gate",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.85",
|
|
4
4
|
"description": "FH runtime adapters — run FH governance, skills, and agents via Claude or Codex with machine-parseable gates.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -104,6 +104,7 @@
|
|
|
104
104
|
"scripts/prepush_guard_check.sh",
|
|
105
105
|
"scripts/psa_scan_lib.sh",
|
|
106
106
|
"scripts/session_close_check.sh",
|
|
107
|
+
"scripts/test_dispatch_log_lanes.sh",
|
|
107
108
|
"scripts/memory_link_check.py",
|
|
108
109
|
"scripts/test_memory_link_check.sh",
|
|
109
110
|
"scripts/memory_nearcheck.py",
|
package/scripts/selfcheck.sh
CHANGED
|
@@ -330,6 +330,19 @@ else
|
|
|
330
330
|
fail=1
|
|
331
331
|
fi
|
|
332
332
|
|
|
333
|
+
# ④-e dispatch-log reconciliation + its tally hook. Wired in the same commit that ships them: the
|
|
334
|
+
# obligation they mechanize lost 20/20 in a single session, so leaving the checker itself unrun
|
|
335
|
+
# would be the same defect one layer up.
|
|
336
|
+
if [ -f scripts/test_dispatch_log_lanes.sh ]; then
|
|
337
|
+
if ! bash scripts/test_dispatch_log_lanes.sh >/dev/null 2>&1; then
|
|
338
|
+
echo "FAIL test_dispatch_log_lanes.sh: the dispatch-log reconciliation would mis-report"
|
|
339
|
+
bash scripts/test_dispatch_log_lanes.sh 2>&1 | tail -14
|
|
340
|
+
fail=1
|
|
341
|
+
else
|
|
342
|
+
echo "PASS test_dispatch_log_lanes.sh (date-spelling + verdict + tally-hook lanes)"
|
|
343
|
+
fi
|
|
344
|
+
fi
|
|
345
|
+
|
|
333
346
|
# selfcheck's own subject-presence discriminators. Every other guard under scripts/ has a lane suite;
|
|
334
347
|
# this decision had none, and it shipped two mis-routings in one session — a two-arm form that fell
|
|
335
348
|
# through in silence, then a package discriminator keyed on a file that actually ships. Both are
|
|
@@ -117,6 +117,57 @@ if [ -n "$LAST_TAG" ] && [ -f "$FH/package.json" ]; then
|
|
|
117
117
|
fi
|
|
118
118
|
fi
|
|
119
119
|
|
|
120
|
+
# ④-e DISPATCH-LOG RECONCILIATION — mechanical, because prose failed completely.
|
|
121
|
+
# CLAUDE.md makes an invocation-log entry MANDATORY immediately after any custom sub-agent
|
|
122
|
+
# invocation (it feeds the 60/40 promotion gate and the UAP loop). Measured 2026-08-02: a single
|
|
123
|
+
# session dispatched 20+ subagents and logged ZERO — not a marginal lapse, a total one, in the same
|
|
124
|
+
# session that RECOVERED that very log file from a branch about to be deleted. An obligation that
|
|
125
|
+
# loses 20 times out of 20 is not under-emphasised; it is unmechanized. Per this repo's own rule
|
|
126
|
+
# (1-2 occurrences -> prose; N>=3 or a repeat on another surface -> mechanize), this is well past it.
|
|
127
|
+
#
|
|
128
|
+
# The check is a RECONCILIATION, not an auto-writer. A hook cannot fill `outcome` or `evidence`
|
|
129
|
+
# without fabricating judgment, and a fabricated log entry is worse than a missing one — it would
|
|
130
|
+
# poison the promotion gate with invented outcomes. So the hook only TALLIES (SubagentStop appends a
|
|
131
|
+
# date line) and this step compares the tally against today's entries.
|
|
132
|
+
#
|
|
133
|
+
# It deliberately does NOT demand 1:1. Consolidating twenty challenger rounds into one entry with
|
|
134
|
+
# measured counts is better bookkeeping than twenty stubs, and punishing it would train stub-spam.
|
|
135
|
+
# What it catches is the failure that actually happened: dispatches occurred and NOTHING was written.
|
|
136
|
+
# ABSENCE IS NOT ZERO. The tally comes from a SubagentStop hook configured in `.claude/settings.json`,
|
|
137
|
+
# which is GITIGNORED by design (it also carries local permissions). So a fresh clone, another
|
|
138
|
+
# machine, or a wiped settings file has no hook — and without this branch the check would read an
|
|
139
|
+
# empty tally as "no dispatches today" and pass in silence. That is the exact fail-open this whole
|
|
140
|
+
# step exists to close, re-created inside it; caught before commit by asking where the tally comes
|
|
141
|
+
# from. Installable snippet: templates/subagent-tally-hook.json.
|
|
142
|
+
TALLY="$FH/tracks/_meta/.subagent_dispatch_tally"
|
|
143
|
+
LOG="$FH/knowledge/shared/learnings/subagent_invocations_log.yaml"
|
|
144
|
+
HOOK_OK=0
|
|
145
|
+
if [ -f "$FH/.claude/settings.json" ]; then
|
|
146
|
+
grep -q '"SubagentStop"' "$FH/.claude/settings.json" 2>/dev/null && HOOK_OK=1
|
|
147
|
+
fi
|
|
148
|
+
if [ "$HOOK_OK" -eq 0 ]; then
|
|
149
|
+
echo "⚠️ ④-e dispatch log NOT MEASURED — no SubagentStop tally hook in .claude/settings.json"
|
|
150
|
+
echo " (that file is gitignored, so a fresh clone has none). An unmeasured dispatch count is"
|
|
151
|
+
echo " NOT a count of zero. Install: templates/subagent-tally-hook.json → .claude/settings.json"
|
|
152
|
+
fi
|
|
153
|
+
DISPATCHED=$(grep -c "^$TODAY$" "$TALLY" 2>/dev/null | tr -d ' ' || echo 0)
|
|
154
|
+
# Both quotings, because the file carries both: hand-written entries use `- date: 2026-08-02`
|
|
155
|
+
# while anything appended via yaml.dump renders `- date: '"'"'2026-08-02'"'"'`. Matching one form counted
|
|
156
|
+
# half the entries as absent — a divergent-normalizer miss inside the check that exists to catch
|
|
157
|
+
# missing records. Known-pair calibrated below in test_dispatch_log_lanes.sh.
|
|
158
|
+
LOGGED=$(grep -cE "^- date: *'?$TODAY'?" "$LOG" 2>/dev/null | tr -d ' ' || echo 0)
|
|
159
|
+
if [ "${DISPATCHED:-0}" -gt 0 ] && [ "${LOGGED:-0}" -eq 0 ]; then
|
|
160
|
+
echo "❌ ④-e $DISPATCHED sub-agent dispatch(es) today and ZERO invocation-log entries — the 60/40"
|
|
161
|
+
echo " promotion gate and the UAP loop both read that file; an unlogged session is invisible to"
|
|
162
|
+
echo " them. Append to knowledge/shared/learnings/subagent_invocations_log.yaml (consolidated"
|
|
163
|
+
echo " per class is fine — record counts and outcomes, not one stub per dispatch)."
|
|
164
|
+
FAIL=1
|
|
165
|
+
elif [ "${DISPATCHED:-0}" -gt 0 ]; then
|
|
166
|
+
echo "✅ ④-e dispatch log: $DISPATCHED dispatch(es) today, $LOGGED log entr(ies) recorded"
|
|
167
|
+
elif [ "$HOOK_OK" -eq 1 ]; then
|
|
168
|
+
echo "✅ ④-e dispatch log: no sub-agent dispatches tallied today"
|
|
169
|
+
fi
|
|
170
|
+
|
|
120
171
|
# ⑤ CARD-LAST invariant — the card must be the NEWEST close artifact. A card older than
|
|
121
172
|
# fh_completed / signal files written this session = ⑤ ran before ①–④ finished (the bug class).
|
|
122
173
|
if [ -f "$CARD" ]; then
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_dispatch_log_lanes.sh — known-pair anchor for session_close_check.sh ④-e (dispatch-log
|
|
3
|
+
# reconciliation) and for the SubagentStop tally hook that feeds it.
|
|
4
|
+
#
|
|
5
|
+
# WHY (2026-08-02): CLAUDE.md makes an invocation-log entry mandatory immediately after any custom
|
|
6
|
+
# sub-agent invocation — it feeds the 60/40 promotion gate and the UAP loop. Measured: one session
|
|
7
|
+
# dispatched 20+ subagents and logged ZERO, in the same session that recovered that very log file
|
|
8
|
+
# from a branch queued for deletion. An obligation that loses 20/20 is not under-emphasised, it is
|
|
9
|
+
# unmechanized, so ④-e now reconciles a hook-written tally against the day's entries.
|
|
10
|
+
#
|
|
11
|
+
# The reconciliation itself then shipped a defect this suite exists to prevent from returning: the
|
|
12
|
+
# log carries TWO date spellings — `- date: 2026-08-02` for hand-written entries and
|
|
13
|
+
# `- date: '2026-08-02'` for anything appended through yaml.dump — and the first matcher saw only
|
|
14
|
+
# one, counting half the entries as absent. A missing-records check that itself miscounts records is
|
|
15
|
+
# the worst possible shape, so both spellings are lanes below.
|
|
16
|
+
#
|
|
17
|
+
# Exit 0 = the reconciliation discriminates · 1 = it would mis-report.
|
|
18
|
+
|
|
19
|
+
set -uo pipefail
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
|
+
CHECK="$SCRIPT_DIR/session_close_check.sh"
|
|
22
|
+
SETTINGS="$SCRIPT_DIR/../.claude/settings.json"
|
|
23
|
+
FAILED=0; PASS=0
|
|
24
|
+
chk() { if [ "$1" -eq 0 ]; then PASS=$((PASS+1)); echo " ✅ $2"; else FAILED=1; echo " ❌ $2"; fi; }
|
|
25
|
+
|
|
26
|
+
[ -f "$CHECK" ] || { echo "FAIL dispatch-log lanes: subject $CHECK missing"; exit 1; }
|
|
27
|
+
|
|
28
|
+
# The matcher under test, lifted from the subject rather than re-spelled (a hand-copied predicate is
|
|
29
|
+
# a divergent normalizer — which is exactly the bug being anchored). If it is gone, fail loudly.
|
|
30
|
+
MATCHER=$(grep -oE "grep -cE \"\^- date: \*'\?\\\$TODAY'\?\"" "$CHECK" | head -1)
|
|
31
|
+
if [ -z "$MATCHER" ]; then
|
|
32
|
+
echo "FAIL the ④-e date matcher is no longer in session_close_check.sh in the expected form —"
|
|
33
|
+
echo " this lane cannot verify what it claims to. Update the lane WITH the subject."
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
echo "── matcher located in the subject"
|
|
37
|
+
|
|
38
|
+
T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
|
|
39
|
+
D=2026-08-02
|
|
40
|
+
|
|
41
|
+
echo "── date-spelling lanes (the shipped miscount) ──"
|
|
42
|
+
printf -- "- date: %s\n" "$D" > "$T/plain.yaml"
|
|
43
|
+
printf -- "- date: '%s'\n" "$D" > "$T/quoted.yaml"
|
|
44
|
+
printf -- "- date: %s\n- date: '%s'\n- date: 2020-01-01\n" "$D" "$D" > "$T/both.yaml"
|
|
45
|
+
count() { grep -cE "^- date: *'?$D'?" "$1" 2>/dev/null | tr -d ' '; }
|
|
46
|
+
[ "$(count "$T/plain.yaml")" = 1 ] ; chk $? "unquoted date counted"
|
|
47
|
+
[ "$(count "$T/quoted.yaml")" = 1 ] ; chk $? "yaml.dump-quoted date counted (the half the first matcher missed)"
|
|
48
|
+
[ "$(count "$T/both.yaml")" = 2 ] ; chk $? "mixed file counts both, and ignores another date"
|
|
49
|
+
# known-POSITIVE for the bug itself: the reverted single-form matcher DOES undercount
|
|
50
|
+
[ "$(grep -c "^- date: *$D" "$T/both.yaml")" = 1 ] ; chk $? "known-POSITIVE: the reverted matcher undercounts (1 of 2) — the defect is real"
|
|
51
|
+
|
|
52
|
+
echo "── reconciliation verdict lanes ──"
|
|
53
|
+
verdict() { # $1=dispatched $2=logged -> BLOCK | OK | NONE
|
|
54
|
+
if [ "$1" -gt 0 ] && [ "$2" -eq 0 ]; then echo BLOCK
|
|
55
|
+
elif [ "$1" -gt 0 ]; then echo OK
|
|
56
|
+
else echo NONE; fi
|
|
57
|
+
}
|
|
58
|
+
[ "$(verdict 20 0)" = BLOCK ] ; chk $? "dispatches with ZERO entries → BLOCK (this is the 2026-08-02 failure)"
|
|
59
|
+
[ "$(verdict 20 4)" = OK ] ; chk $? "consolidated logging (20 dispatches, 4 entries) → OK, not punished"
|
|
60
|
+
[ "$(verdict 1 1)" = OK ] ; chk $? "1:1 logging → OK"
|
|
61
|
+
[ "$(verdict 0 0)" = NONE ] ; chk $? "no dispatches → silent, not a failure"
|
|
62
|
+
|
|
63
|
+
echo "── the tally hook that feeds it ──"
|
|
64
|
+
if [ -f "$SETTINGS" ]; then
|
|
65
|
+
python3 - "$SETTINGS" <<'PY' >"$T/hookcmd" 2>/dev/null || true
|
|
66
|
+
import json,sys
|
|
67
|
+
d=json.load(open(sys.argv[1]))
|
|
68
|
+
g=d.get("hooks",{}).get("SubagentStop",[])
|
|
69
|
+
print(g[0]["hooks"][0]["command"] if g and g[0].get("hooks") else "")
|
|
70
|
+
PY
|
|
71
|
+
cmd=$(cat "$T/hookcmd")
|
|
72
|
+
[ -n "$cmd" ] ; chk $? "SubagentStop hook is configured (an untallied dispatch is invisible to ④-e)"
|
|
73
|
+
if [ -n "$cmd" ]; then
|
|
74
|
+
case "$cmd" in *"exit 0"*) ok=0 ;; *) ok=1 ;; esac
|
|
75
|
+
[ "$ok" -eq 0 ] ; chk $? "hook ends in exit 0 — a non-zero hook exit discards its stdout SILENTLY"
|
|
76
|
+
# run it against a scratch HUB and confirm it appends today's date
|
|
77
|
+
CLAUDE_PROJECT_DIR="$T/hub" bash -c "$cmd" >/dev/null 2>&1
|
|
78
|
+
[ "$(grep -c "$(date +%Y-%m-%d)" "$T/hub/tracks/_meta/.subagent_dispatch_tally" 2>/dev/null || echo 0)" -ge 1 ]
|
|
79
|
+
chk $? "hook actually appends a dated line when run (not merely present)"
|
|
80
|
+
fi
|
|
81
|
+
else
|
|
82
|
+
echo " ⏭️ .claude/settings.json absent — hook lanes unchecked (not a pass)"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
echo "── absence-is-not-zero (the fail-open this fix nearly shipped) ──"
|
|
86
|
+
# .claude/settings.json is GITIGNORED, so a fresh clone has no hook and an empty tally. Without an
|
|
87
|
+
# explicit branch the reconciliation would read that as "no dispatches" and pass in silence.
|
|
88
|
+
hookless_verdict() { # $1=hook_configured -> MEASURED | NOT-MEASURED
|
|
89
|
+
[ "$1" -eq 1 ] && echo MEASURED || echo NOT-MEASURED
|
|
90
|
+
}
|
|
91
|
+
[ "$(hookless_verdict 0)" = NOT-MEASURED ] ; chk $? "no hook → NOT MEASURED (an unmeasured count is not zero)"
|
|
92
|
+
[ "$(hookless_verdict 1)" = MEASURED ] ; chk $? "hook present → measured"
|
|
93
|
+
grep -q "NOT MEASURED" "$CHECK" ; chk $? "the subject actually carries that branch (not just this lane)"
|
|
94
|
+
[ -f "$SCRIPT_DIR/../templates/subagent-tally-hook.json" ] ; chk $? "installable snippet exists for a clone that has no settings.json"
|
|
95
|
+
|
|
96
|
+
echo ""
|
|
97
|
+
if [ "$FAILED" -ne 0 ]; then echo "DISPATCH-LOG LANES: FAIL"; exit 1; fi
|
|
98
|
+
echo "DISPATCH-LOG LANES: PASS ($PASS/$PASS)"
|
|
99
|
+
exit 0
|