@oneie/claude 0.7.0 → 0.9.0
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/agents/abm-strategist.md +89 -0
- package/agents/ads-meta.md +97 -0
- package/agents/analyst.md +173 -0
- package/agents/architect.md +280 -0
- package/agents/brand-guardian.md +88 -0
- package/agents/brand-strategist.md +92 -0
- package/agents/campaign-content.md +90 -0
- package/agents/campaign-email.md +88 -0
- package/agents/campaign-sms.md +87 -0
- package/agents/campaign-social.md +87 -0
- package/agents/cco.md +99 -0
- package/agents/ceo.md +106 -0
- package/agents/chairman.md +120 -0
- package/agents/cmo.md +101 -0
- package/agents/community-greeter.md +89 -0
- package/agents/community-moderator.md +92 -0
- package/agents/compliance.md +92 -0
- package/agents/copywriter.md +100 -0
- package/agents/creative-strategist.md +95 -0
- package/agents/cro.md +98 -0
- package/agents/cto.md +267 -0
- package/agents/customer-interviewer.md +93 -0
- package/agents/customer-researcher.md +89 -0
- package/agents/customer-success-manager.md +90 -0
- package/agents/customer-trainer.md +90 -0
- package/agents/cxo.md +100 -0
- package/agents/demand-creator.md +89 -0
- package/agents/demo-mover.md +83 -0
- package/agents/demo-specialist.md +89 -0
- package/agents/demo-thai-family-law.md +87 -0
- package/agents/designer.md +112 -0
- package/agents/discovery-caller.md +98 -0
- package/agents/doctor.md +139 -0
- package/agents/educate-coach.md +100 -0
- package/agents/elevate-tutor.md +83 -0
- package/agents/email-lifecycle-marketer.md +91 -0
- package/agents/engage-specialist.md +99 -0
- package/agents/events-coordinator.md +90 -0
- package/agents/foundation-builder.md +94 -0
- package/agents/funnel-architect.md +89 -0
- package/agents/gift-creator.md +100 -0
- package/agents/google-ads.md +103 -0
- package/agents/guide.md +292 -0
- package/agents/helpdesk-dispatcher.md +88 -0
- package/agents/hook-specialist.md +91 -0
- package/agents/identify-optimizer.md +101 -0
- package/agents/implementer.md +305 -0
- package/agents/incident-commander.md +120 -0
- package/agents/insights-lead.md +93 -0
- package/agents/journey-runner.md +113 -0
- package/agents/linkedin-ads.md +123 -0
- package/agents/live-sales-chat.md +90 -0
- package/agents/market-researcher.md +88 -0
- package/agents/media-buyer.md +96 -0
- package/agents/movers-customer-researcher.md +95 -0
- package/agents/movers-foundation-builder.md +96 -0
- package/agents/movers-market-researcher.md +97 -0
- package/agents/movers-pricing-strategist.md +171 -0
- package/agents/nurture-architect.md +99 -0
- package/agents/offer-architect.md +92 -0
- package/agents/onboarder.md +222 -0
- package/agents/onboarding-specialist.md +92 -0
- package/agents/operations-dashboard.md +98 -0
- package/agents/perf-engineer.md +348 -0
- package/agents/playbook-writer.md +71 -0
- package/agents/plg-strategist.md +91 -0
- package/agents/positioning-architect.md +88 -0
- package/agents/press-officer.md +89 -0
- package/agents/pricing-strategist.md +94 -0
- package/agents/privacy-officer.md +92 -0
- package/agents/referral-manager.md +91 -0
- package/agents/refine-analyst.md +102 -0
- package/agents/release-manager.md +261 -0
- package/agents/renewals-upsell-rep.md +90 -0
- package/agents/review-engineer.md +327 -0
- package/agents/rewards-steward.md +82 -0
- package/agents/sales-call-coach.md +94 -0
- package/agents/sales-closer.md +98 -0
- package/agents/security-auditor.md +313 -0
- package/agents/sell-closer.md +101 -0
- package/agents/share-amplifier.md +99 -0
- package/agents/social-media-manager.md +92 -0
- package/agents/storyteller.md +250 -0
- package/agents/strategist.md +83 -0
- package/agents/strategy-aligner.md +94 -0
- package/agents/support-agent.md +90 -0
- package/agents/tagger.md +245 -0
- package/agents/tech-writer.md +216 -0
- package/agents/test-engineer.md +243 -0
- package/agents/tiktok-ads.md +105 -0
- package/agents/tracking-engineer.md +92 -0
- package/agents/upsell-strategist.md +101 -0
- package/agents/voice.md +85 -0
- package/agents/w4-tools.md +153 -0
- package/agents/w4-verify.md +20 -138
- package/agents/workflow-optimiser.md +84 -0
- package/commands/close.md +814 -160
- package/commands/create.md +2 -2
- package/commands/deploy.md +554 -175
- package/commands/do.md +119 -109
- package/commands/fast.md +67 -0
- package/commands/improve.md +2 -2
- package/commands/one.md +418 -0
- package/commands/see.md +1 -1
- package/commands/sync.md +1 -1
- package/commands/tasks.md +222 -0
- package/commands/vespio.md +110 -0
- package/commands/vespio.remote.md +227 -0
- package/hooks/hooks.json +25 -79
- package/hooks/lib/governor-escape-match.sh +88 -0
- package/hooks/lib/hook.sh +4 -13
- package/hooks/lib/signal.sh +9 -2
- package/hooks/scripts/branch-pin.sh +6 -0
- package/hooks/scripts/config-protect.sh +6 -0
- package/hooks/scripts/do-outcome-gate.sh +19 -2
- package/hooks/scripts/git-add-guard.sh +43 -2
- package/hooks/scripts/governor-escape.sh +96 -0
- package/hooks/scripts/load-guard.sh +127 -0
- package/hooks/scripts/post-edit-check.sh +36 -29
- package/hooks/scripts/session-start.sh +34 -80
- package/hooks/scripts/task-complete-verify.sh +46 -40
- package/package.json +1 -1
- package/rules/documentation.md +9 -0
- package/scripts/ad-copy-lint.sh +656 -0
- package/scripts/agentverse-audit.sh +256 -0
- package/scripts/asi-walk.sh +435 -0
- package/scripts/astro-build-cached.sh +143 -0
- package/scripts/blocks-manifest-cached.sh +100 -0
- package/scripts/blocks-manifest.mjs +497 -0
- package/scripts/blocks-ratchet.sh +116 -0
- package/scripts/blocks-render-probe.mjs +529 -0
- package/scripts/blocks-usage.mjs +334 -0
- package/scripts/chat-context-check.sh +89 -0
- package/scripts/close-metrics.sh +558 -0
- package/scripts/close-owner.sh +268 -0
- package/scripts/db-sync-lock-check.sh +116 -0
- package/scripts/deploy-dev.sh +35 -0
- package/scripts/deploy-emit.sh +298 -0
- package/scripts/deploy-gate-check.sh +128 -0
- package/scripts/deploy-ready.sh +78 -0
- package/scripts/deploy-record.sh +605 -0
- package/scripts/deploy.sh +1273 -0
- package/scripts/do-auto.sh +598 -48
- package/scripts/do-brief.sh +113 -0
- package/scripts/do-close.sh +1137 -0
- package/scripts/do-commit.sh +75 -0
- package/scripts/do-consumer-sweep.sh +18 -1
- package/scripts/do-cycle-shape-check.sh +160 -0
- package/scripts/do-decide.sh +476 -0
- package/scripts/do-derive-check.sh +436 -0
- package/scripts/do-fleet.sh +106 -28
- package/scripts/do-folder.sh +10 -1
- package/scripts/do-next.sh +106 -0
- package/scripts/do-orchestrate.sh +17 -5
- package/scripts/do-plan-json.mjs +201 -0
- package/scripts/do-plan-json.sh +8 -0
- package/scripts/do-preflight.sh +117 -0
- package/scripts/do-project.sh +157 -0
- package/scripts/do-prove-selftest.sh +108 -0
- package/scripts/do-prove.sh +295 -23
- package/scripts/do-rank.py +31 -1
- package/scripts/do-recon-cache.sh +7 -1
- package/scripts/do-recon-pack.sh +196 -0
- package/scripts/do-reconcile.sh +121 -4
- package/scripts/do-signal.sh +280 -23
- package/scripts/do-smoke.sh +18 -1
- package/scripts/do-test-gate.sh +80 -0
- package/scripts/do-tick.sh +102 -0
- package/scripts/do-tier.sh +6 -0
- package/scripts/do-triage.sh +182 -0
- package/scripts/do-ui-gate.sh +1 -1
- package/scripts/do-w4-gates.sh +451 -0
- package/scripts/do-walk.sh +12 -1
- package/scripts/env-sync.sh +173 -0
- package/scripts/factory-brief-check.sh +330 -0
- package/scripts/factory-check.sh +68 -14
- package/scripts/factory-close-check.sh +257 -0
- package/scripts/factory-emit.sh +211 -0
- package/scripts/factory-executor-check.mjs +353 -0
- package/scripts/factory-peak.sh +301 -0
- package/scripts/factory-repo.sh +118 -3
- package/scripts/factory-review-check.mjs +61 -0
- package/scripts/factory-ship.sh +61 -0
- package/scripts/factory-tasks-check.sh +18 -1
- package/scripts/factory-turn.sh +326 -0
- package/scripts/factory-walk.sh +396 -0
- package/scripts/factory-width.sh +57 -0
- package/scripts/fade-toxic.sh +4 -3
- package/scripts/fixtures/factory-brief-real.md +44 -0
- package/scripts/fixtures/triage-dupe.md +5 -0
- package/scripts/fleet-manifest.mjs +108 -0
- package/scripts/fleet-status.sh +110 -0
- package/scripts/full-suite-paths-check.sh +144 -0
- package/scripts/gate-reaper-check.sh +98 -0
- package/scripts/gate-reaper.sh +125 -0
- package/scripts/gate-run.sh +73 -0
- package/scripts/gc-content-check.sh +140 -0
- package/scripts/gen-dev-config.py +20 -0
- package/scripts/govern-bound-check.sh +60 -0
- package/scripts/govern-claims-check.sh +233 -0
- package/scripts/govern-mem-check.sh +290 -0
- package/scripts/governor-doors-check.sh +362 -0
- package/scripts/governor-escape-check.sh +171 -0
- package/scripts/health.sh +413 -0
- package/scripts/id-inventory.mjs +418 -0
- package/scripts/land.sh +551 -0
- package/scripts/lib/gc-finished.sh +77 -0
- package/scripts/lib/govern.sh +361 -0
- package/scripts/lib/govern.ts +756 -0
- package/scripts/lighthouse-run.sh +187 -0
- package/scripts/livekit-live-check.sh +61 -0
- package/scripts/livekit-ratchet.sh +46 -0
- package/scripts/load-guard-check.sh +49 -0
- package/scripts/machine-check.sh +102 -0
- package/scripts/machine-watch.sh +177 -0
- package/scripts/one-agents.mjs +415 -0
- package/scripts/one-resume.sh +78 -0
- package/scripts/orphan-baseline.json +182 -0
- package/scripts/orphan-modules.mjs +179 -0
- package/scripts/pr-body.sh +335 -0
- package/scripts/preview-fd-check.sh +289 -0
- package/scripts/promise-manifest.mjs +24 -1
- package/scripts/release.sh +322 -0
- package/scripts/roles-check.sh +946 -0
- package/scripts/sdk-build-cached.sh +64 -0
- package/scripts/signal-watch.sh +241 -0
- package/scripts/skills-publish.sh +94 -0
- package/scripts/speed-cache-check.sh +173 -0
- package/scripts/speed-check.mjs +907 -0
- package/scripts/speed-parity-check.sh +648 -0
- package/scripts/speed-waterfall-check.sh +355 -0
- package/scripts/substrate-env-parity.mjs +156 -0
- package/scripts/tasks-claim-race.mjs +108 -0
- package/scripts/tasks-loop.sh +185 -0
- package/scripts/test-cached.sh +255 -0
- package/scripts/test-full.sh +87 -0
- package/scripts/test-honesty.mjs +137 -0
- package/scripts/test-lanes.sh +166 -0
- package/scripts/test-speed.sh +94 -0
- package/scripts/triage-shape-check.sh +149 -0
- package/scripts/tsc-cached.sh +179 -0
- package/scripts/typedb-flake-check.sh +197 -0
- package/scripts/urls-lint.sh +15 -0
- package/scripts/verify-fast.sh +445 -0
- package/scripts/vespio-sync.sh +149 -0
- package/scripts/wf-check.mjs +104 -1
- package/scripts/worktree-preview.sh +879 -0
- package/scripts/worktree-up.sh +208 -0
- package/skills/livekit-agents/SKILL.md +285 -0
- package/skills/livekit-agents/references/freshness-rules.md +168 -0
- package/skills/shadcn/SKILL.md +1 -1
- package/skills/signal/SKILL.md +0 -1
- package/skills/voice/SKILL.md +94 -6
- package/skills/voice/corpus-check.sh +87 -0
- package/hooks/scripts/compact-hint.sh +0 -35
- package/hooks/scripts/gate-guard.sh +0 -83
- package/hooks/scripts/read-tracker.sh +0 -26
- package/hooks/scripts/session-end-verify.sh +0 -51
- package/hooks/scripts/stop-reflect.sh +0 -140
- package/hooks/scripts/sync-priority-todo.sh +0 -57
- package/hooks/scripts/sync-todo-docs.sh +0 -46
- package/hooks/scripts/tool-signal.sh +0 -48
- package/scripts/do-tasks-bridge.py +0 -366
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# do-triage.sh — zero-LLM pre-pass over a captured task.
|
|
3
|
+
# Emits {verdict, paths, route, dupe}. Verdicts: sizeable | idea | dupe | split.
|
|
4
|
+
# Discriminator for idea: can you state in one line the check that proves this done?
|
|
5
|
+
# Usage: do-triage.sh "captured text" | do-triage.sh --self-test
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
9
|
+
TEXT_DIR="${TRIAGE_TEXT_DIR:-$ROOT/text}"
|
|
10
|
+
PAGES="$ROOT/one.ie/web/src/pages"
|
|
11
|
+
|
|
12
|
+
# --self-test — one fixture per verdict (same idiom as do-tier.sh).
|
|
13
|
+
if [ "${1:-}" = "--self-test" ]; then
|
|
14
|
+
S="$(cd "$(dirname "$0")" && pwd)"; f=0
|
|
15
|
+
export TRIAGE_TEXT_DIR="$S/fixtures"
|
|
16
|
+
chk() {
|
|
17
|
+
want="$1"; shift
|
|
18
|
+
got="$("$S/do-triage.sh" "$@" 2>/dev/null)" || true
|
|
19
|
+
case "$got" in *"\"verdict\":\"$want\""*) printf ' ok %s → %s\n' "$*" "$want" ;;
|
|
20
|
+
*) printf ' FAIL %s → expected %s, got %s\n' "$*" "$want" "$got"; f=1 ;; esac
|
|
21
|
+
}
|
|
22
|
+
chk sizeable "fix UNSIZED exit in .claude/scripts/do-tier.sh"
|
|
23
|
+
chk idea "make the board nicer"
|
|
24
|
+
chk split "fix .claude/scripts/do-tier.sh and .claude/scripts/do-survey.sh"
|
|
25
|
+
chk dupe "xyzzy-triage-dupe unique deliverable phrase"
|
|
26
|
+
# sizeable names a real file — paths must include it (feeds do-tier, never a sentence)
|
|
27
|
+
got="$("$S/do-triage.sh" "fix UNSIZED exit in .claude/scripts/do-tier.sh" 2>/dev/null)" || true
|
|
28
|
+
case "$got" in *".claude/scripts/do-tier.sh"*) printf ' ok sizeable paths include the named file\n' ;;
|
|
29
|
+
*) printf ' FAIL sizeable paths missing named file: %s\n' "$got"; f=1 ;; esac
|
|
30
|
+
got="$("$S/do-triage.sh" "the banner on /chat shows twice" 2>/dev/null)" || true
|
|
31
|
+
case "$got" in *"\"verdict\":\"sizeable\""*"\"route\":\"/chat\""*|*"\"route\":\"/chat\""*"\"verdict\":\"sizeable\""*) printf ' ok named route /chat → sizeable\n' ;;
|
|
32
|
+
*) printf ' FAIL named route /chat: %s\n' "$got"; f=1 ;; esac
|
|
33
|
+
[ "$f" -eq 0 ] && echo "do-triage: self-test green" || echo "do-triage: self-test FAILED"
|
|
34
|
+
exit "$f"
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
text="${*:-}"
|
|
38
|
+
if [ -z "$text" ]; then
|
|
39
|
+
if [ -t 0 ]; then echo "usage: do-triage.sh <captured text> | --self-test" >&2; exit 4; fi
|
|
40
|
+
text="$(cat)"
|
|
41
|
+
fi
|
|
42
|
+
[ -z "$text" ] && { echo "usage: do-triage.sh <captured text> | --self-test" >&2; exit 4; }
|
|
43
|
+
|
|
44
|
+
# Existing repo-relative paths the title names.
|
|
45
|
+
paths=""
|
|
46
|
+
while IFS= read -r p; do
|
|
47
|
+
[ -z "$p" ] && continue
|
|
48
|
+
case "$p" in http*|*:*) continue ;; esac
|
|
49
|
+
if [ -e "$ROOT/$p" ]; then
|
|
50
|
+
case "
|
|
51
|
+
$paths
|
|
52
|
+
" in *"
|
|
53
|
+
$p
|
|
54
|
+
"*) ;; *) paths="${paths}${p}
|
|
55
|
+
" ;; esac
|
|
56
|
+
fi
|
|
57
|
+
done <<EOF
|
|
58
|
+
$(printf '%s' "$text" | grep -oE '[A-Za-z0-9_.-]+(/[A-Za-z0-9_.-]+)+' || true)
|
|
59
|
+
EOF
|
|
60
|
+
|
|
61
|
+
# A whitespace-bounded /route that maps onto an existing page.
|
|
62
|
+
route=""
|
|
63
|
+
while IFS= read -r tok; do
|
|
64
|
+
[ -z "$tok" ] && continue
|
|
65
|
+
r="$(printf '%s' "$tok" | sed 's/^[[:space:]]*//')"
|
|
66
|
+
rel="${r#/}"
|
|
67
|
+
page=""
|
|
68
|
+
if [ -f "$PAGES/${rel}.astro" ]; then page="one.ie/web/src/pages/${rel}.astro"
|
|
69
|
+
elif [ -f "$PAGES/${rel}/index.astro" ]; then page="one.ie/web/src/pages/${rel}/index.astro"
|
|
70
|
+
else
|
|
71
|
+
slugrel="$(printf '%s' "$rel" | sed -E 's|^u/[^/]+/|u/[slug]/|')"
|
|
72
|
+
if [ "$slugrel" != "$rel" ]; then
|
|
73
|
+
[ -f "$PAGES/${slugrel}.astro" ] && page="one.ie/web/src/pages/${slugrel}.astro"
|
|
74
|
+
[ -z "$page" ] && [ -f "$PAGES/${slugrel}/index.astro" ] && page="one.ie/web/src/pages/${slugrel}/index.astro"
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
if [ -n "$page" ]; then
|
|
78
|
+
route="$r"
|
|
79
|
+
case "
|
|
80
|
+
$paths
|
|
81
|
+
" in *"
|
|
82
|
+
$page
|
|
83
|
+
"*) ;; *) paths="${paths}${page}
|
|
84
|
+
" ;; esac
|
|
85
|
+
break
|
|
86
|
+
fi
|
|
87
|
+
done <<EOF
|
|
88
|
+
$(printf '%s' "$text" | grep -oE '(^|[[:space:]])/[a-z][][a-z0-9_/-]*' || true)
|
|
89
|
+
EOF
|
|
90
|
+
|
|
91
|
+
# Near-dupe of an existing promise deliverable (or optional open-task list).
|
|
92
|
+
# One Python walk — a bash for-loop over text/*.md is ~8s (1470 files) and find hits ARG_MAX.
|
|
93
|
+
dupe_json="null"
|
|
94
|
+
if [ -d "$TEXT_DIR" ]; then
|
|
95
|
+
dupe_json="$(python3 - "$TEXT_DIR" "$ROOT" "$text" "${TRIAGE_OPEN_TASKS:-}" <<'PY'
|
|
96
|
+
import json, os, re, sys
|
|
97
|
+
text_dir, root, needle, open_tasks = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]
|
|
98
|
+
def norm(s):
|
|
99
|
+
return re.sub(r"\s+", " ", s.lower()).strip()
|
|
100
|
+
def unquote(s):
|
|
101
|
+
s = s.strip()
|
|
102
|
+
if len(s) >= 2 and s[0] == '"':
|
|
103
|
+
s = re.sub(r'^"(.*)"\s*(#.*)?$', r"\1", s).replace('\\"', '"')
|
|
104
|
+
elif len(s) >= 2 and s[0] == "'":
|
|
105
|
+
s = re.sub(r"^'(.*)'\s*(#.*)?$", r"\1", s)
|
|
106
|
+
else:
|
|
107
|
+
s = re.sub(r"\s+#.*$", "", s)
|
|
108
|
+
return s
|
|
109
|
+
nt = norm(needle)
|
|
110
|
+
item_re = re.compile(r"^\s*-\s*item:\s*(.*)$")
|
|
111
|
+
def hit(item, rel):
|
|
112
|
+
nitem = norm(item)
|
|
113
|
+
if len(nitem) < 16:
|
|
114
|
+
return False
|
|
115
|
+
if nitem in nt or (len(nt) >= 16 and nt in nitem):
|
|
116
|
+
print(json.dumps({"item": item, "file": rel}, separators=(",", ":")))
|
|
117
|
+
return True
|
|
118
|
+
return False
|
|
119
|
+
for dirpath, dirnames, files in os.walk(text_dir):
|
|
120
|
+
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
|
|
121
|
+
rel_dir = os.path.relpath(dirpath, text_dir)
|
|
122
|
+
depth = 0 if rel_dir == "." else rel_dir.count(os.sep) + 1
|
|
123
|
+
if depth > 2:
|
|
124
|
+
dirnames[:] = []
|
|
125
|
+
continue
|
|
126
|
+
for fn in files:
|
|
127
|
+
if not fn.endswith(".md"):
|
|
128
|
+
continue
|
|
129
|
+
path = os.path.join(dirpath, fn)
|
|
130
|
+
try:
|
|
131
|
+
with open(path, encoding="utf-8", errors="ignore") as fh:
|
|
132
|
+
in_fm = 0
|
|
133
|
+
for line in fh:
|
|
134
|
+
if line.startswith("---"):
|
|
135
|
+
in_fm += 1
|
|
136
|
+
if in_fm >= 2:
|
|
137
|
+
break
|
|
138
|
+
continue
|
|
139
|
+
if in_fm != 1:
|
|
140
|
+
continue
|
|
141
|
+
m = item_re.match(line)
|
|
142
|
+
if not m:
|
|
143
|
+
continue
|
|
144
|
+
item = unquote(m.group(1))
|
|
145
|
+
rel = path[len(root)+1:] if path.startswith(root) else path
|
|
146
|
+
if hit(item, rel):
|
|
147
|
+
sys.exit(0)
|
|
148
|
+
except OSError:
|
|
149
|
+
continue
|
|
150
|
+
if open_tasks and os.path.isfile(open_tasks):
|
|
151
|
+
for line in open(open_tasks, encoding="utf-8", errors="ignore"):
|
|
152
|
+
ot = line.strip()
|
|
153
|
+
if ot and hit(ot, "open-task"):
|
|
154
|
+
sys.exit(0)
|
|
155
|
+
print("null")
|
|
156
|
+
PY
|
|
157
|
+
)"
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
nfiles=0
|
|
161
|
+
if [ -n "$paths" ]; then nfiles="$(printf '%s' "$paths" | grep -c . || true)"; fi
|
|
162
|
+
nnum="$(printf '%s' "$text" | grep -oE '(^|[[:space:]])[0-9]+\.' | grep -c . || true)"
|
|
163
|
+
has_cmd=0
|
|
164
|
+
printf '%s' "$text" | grep -qE 'test -[efd]|grep -[qF]|bunx?[[:space:]]+vitest|bash[[:space:]]|\.sh[[:space:]]|accept:' && has_cmd=1 || true
|
|
165
|
+
|
|
166
|
+
verdict=idea
|
|
167
|
+
if [ "$dupe_json" != "null" ]; then
|
|
168
|
+
verdict=dupe
|
|
169
|
+
elif [ "$nfiles" -ge 2 ] || [ "$nnum" -ge 2 ]; then
|
|
170
|
+
verdict=split
|
|
171
|
+
elif [ "$nfiles" -ge 1 ] || [ -n "$route" ] || [ "$has_cmd" -eq 1 ]; then
|
|
172
|
+
verdict=sizeable
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
python3 -c '
|
|
176
|
+
import json,sys
|
|
177
|
+
verdict, route, dupe = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
178
|
+
paths = [ln for ln in sys.stdin.read().splitlines() if ln]
|
|
179
|
+
print(json.dumps({"verdict":verdict,"paths":paths,"route":route or None,"dupe":None if dupe=="null" else json.loads(dupe)}, separators=(",",":")))
|
|
180
|
+
' "$verdict" "$route" "$dupe_json" <<EOF
|
|
181
|
+
$paths
|
|
182
|
+
EOF
|
package/scripts/do-ui-gate.sh
CHANGED
|
@@ -311,7 +311,7 @@ for n in $active_ids; do
|
|
|
311
311
|
if [ -n "$r" ] && [ -n "$base" ]; then
|
|
312
312
|
body="$(curl -s --max-time 20 "$base$r" 2>/dev/null || true)"
|
|
313
313
|
if [ -z "$body" ]; then ok=0; why="no response from $base$r"; fi
|
|
314
|
-
if [ "$ok" -eq 1 ] && [ -n "$et" ] && !
|
|
314
|
+
if [ "$ok" -eq 1 ] && [ -n "$et" ] && ! grep -qiF "$et" <<<"$body"; then
|
|
315
315
|
ok=0; why="text '$et' not in served HTML"
|
|
316
316
|
fi
|
|
317
317
|
fi
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# do-w4-gates.sh — run W4's DETERMINISTIC gate battery, emit one JSON evidence pack.
|
|
3
|
+
#
|
|
4
|
+
# manifest: needs-env
|
|
5
|
+
#
|
|
6
|
+
# needs-env: it runs do-reconcile.sh, do-folder.sh and the folder verify lane,
|
|
7
|
+
# all of which are needs-env.
|
|
8
|
+
#
|
|
9
|
+
# WHY. Every gate in `.claude/commands/do.md` § W4 is already bash. The `w4-verify`
|
|
10
|
+
# agent was being spawned (28.9KB of prose + 5.4KB of w4-tools.md) mostly to READ
|
|
11
|
+
# those gate lines out of the markdown and then type them into Bash. That is a
|
|
12
|
+
# model call to operate a shell. This script IS the gate list, executed, with a
|
|
13
|
+
# machine-readable receipt — so the only thing left that needs judgment is the
|
|
14
|
+
# rubric VERDICT (goal-fit especially, which no script can score).
|
|
15
|
+
#
|
|
16
|
+
# GATE PARITY IS THE WHOLE POINT. This script must run exactly the gates do.md
|
|
17
|
+
# lists, no more and no fewer. It adds nothing and removes nothing; it only
|
|
18
|
+
# stops a model from re-typing them.
|
|
19
|
+
#
|
|
20
|
+
# THE HONESTY LAW (this repo has shipped green gates over red trees three times
|
|
21
|
+
# on record): a gate has FOUR states, never two —
|
|
22
|
+
# pass : ran, exit 0
|
|
23
|
+
# fail : ran, non-zero
|
|
24
|
+
# unrun : could not run (tool absent, verify skipped, no baseline)
|
|
25
|
+
# n/a : does not apply to this diff (by rule, not by accident)
|
|
26
|
+
# `deterministic_pass` is true only when there are ZERO fails and ZERO unruns.
|
|
27
|
+
# An unrun gate is NOT a passed gate. An absent tool is an unrun check, never a
|
|
28
|
+
# green one.
|
|
29
|
+
#
|
|
30
|
+
# TWO GATES THIS SCRIPT CANNOT INVENT. do.md's W4 block also runs the cycle's
|
|
31
|
+
# `$(cycle.demo.command)` — the goal gate — and `do-prove.sh <slug>` when the
|
|
32
|
+
# promise exists. Neither is derivable from a diff: one lives in the cycle, the
|
|
33
|
+
# other needs the plan slug. So the CALLER supplies them (`--demo`, `--slug`)
|
|
34
|
+
# and, when it does not, they are reported `unrun` — which under the law above
|
|
35
|
+
# makes `deterministic_pass` false. Running this script bare therefore CANNOT
|
|
36
|
+
# produce a green over an unrun goal gate. That is deliberate, not an oversight.
|
|
37
|
+
#
|
|
38
|
+
# Usage:
|
|
39
|
+
# do-w4-gates.sh [--files <changed-path>...] # default: git diff HEAD --name-only
|
|
40
|
+
# [--demo '<command>'] # the cycle's goal gate
|
|
41
|
+
# [--slug <slug>] # enables do-prove.sh <slug>
|
|
42
|
+
# do-w4-gates.sh --self-test # fixtures + RED PROOF
|
|
43
|
+
# Env:
|
|
44
|
+
# W4_SKIP_VERIFY=1 skip the folder verify lane -> reported `unrun` (never pass)
|
|
45
|
+
# W4_SKIP_RECONCILE=1 SELF-TEST ONLY — skip the 7 canons + consumer sweep, all
|
|
46
|
+
# reported `unrun`. Exists because `do-reconcile.sh types`
|
|
47
|
+
# is a tsc that can queue for minutes behind the governor
|
|
48
|
+
# and the self-test calls this script six times. Safe only
|
|
49
|
+
# because a skip is unrun, not pass; never set it for real.
|
|
50
|
+
set -euo pipefail
|
|
51
|
+
|
|
52
|
+
# _GATE — see governor-doors-check.sh "compute sites". A gate_lock dedupes
|
|
53
|
+
# identical work; only a SLOT bounds N worktrees running N computes at once.
|
|
54
|
+
# Empty inside an existing gate so the outer slot is inherited, never doubled.
|
|
55
|
+
_GATE=()
|
|
56
|
+
if [ "${GOVERN_IN_GATE:-0}" != "1" ]; then
|
|
57
|
+
_GR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/gate-run.sh"
|
|
58
|
+
[ -f "$_GR" ] && _GATE=( bash "$_GR" "compute:$(basename "${BASH_SOURCE[0]}")" -- )
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
63
|
+
S="$ROOT/.claude/scripts"
|
|
64
|
+
RESULTS_FILE=""
|
|
65
|
+
DEMO_CMD=""
|
|
66
|
+
SLUG=""
|
|
67
|
+
|
|
68
|
+
_add() { # name status detail
|
|
69
|
+
jq -n --arg n "$1" --arg s "$2" --arg d "$3" '{gate:$n,status:$s,detail:$d}' >> "$RESULTS_FILE"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Run a command; classify by exit code. Never let a missing tool invert the exit.
|
|
73
|
+
_run() { # name cmd...
|
|
74
|
+
local name="$1"; shift
|
|
75
|
+
local out rc
|
|
76
|
+
set +e
|
|
77
|
+
out="$( "$@" </dev/null 2>&1 )"; rc=$?
|
|
78
|
+
set -e
|
|
79
|
+
if [ $rc -eq 0 ]; then _add "$name" pass "$(printf '%s' "$out" | tail -3 | tr '\n' ' ')"
|
|
80
|
+
else _add "$name" fail "exit=$rc $(printf '%s' "$out" | tail -5 | tr '\n' ' ')"; fi
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_gates() {
|
|
84
|
+
# macOS ships bash 3.2: no `mapfile`, and "${arr[@]}" on an EMPTY array is an
|
|
85
|
+
# unbound-variable error under `set -u`. So the changed set is carried as a
|
|
86
|
+
# newline-delimited STRING and re-split only where a command needs argv.
|
|
87
|
+
local files_txt nfiles
|
|
88
|
+
if [ "$#" -gt 0 ]; then files_txt="$(printf '%s\n' "$@")"
|
|
89
|
+
else files_txt="$(git -C "$ROOT" diff HEAD --name-only 2>/dev/null || true)"; fi
|
|
90
|
+
nfiles="$(printf '%s\n' "$files_txt" | grep -c . || true)"
|
|
91
|
+
RESULTS_FILE="$(mktemp)"
|
|
92
|
+
|
|
93
|
+
# ---- 1. folder verify lane (bun run verify / verify:fast; doc-only -> n/a) ----
|
|
94
|
+
local folders
|
|
95
|
+
folders="$(printf '%s\n' "$files_txt" | bash "$S/do-folder.sh" 2>/dev/null || echo '')"
|
|
96
|
+
if [ -z "$folders" ]; then
|
|
97
|
+
_add verify unrun "do-folder.sh resolved no folder for the changed set"
|
|
98
|
+
elif printf '%s\n' "$folders" | jq -se 'all(.doc_only)' >/dev/null 2>&1; then
|
|
99
|
+
_add verify n/a "doc-only diff — do.md W0/W4 skip bun verify by rule"
|
|
100
|
+
elif [ "${W4_SKIP_VERIFY:-0}" = "1" ]; then
|
|
101
|
+
_add verify unrun "W4_SKIP_VERIFY=1 — an unrun gate is not a pass"
|
|
102
|
+
else
|
|
103
|
+
local f v
|
|
104
|
+
while IFS=$'\t' read -r f v; do
|
|
105
|
+
[ -n "$f" ] || continue
|
|
106
|
+
_run "verify:$f" bash -c "cd '$ROOT/$f' && $v"
|
|
107
|
+
done < <(printf '%s\n' "$folders" | jq -r 'select(.verify) | "\(.folder)\t\(.verify)"')
|
|
108
|
+
# A folder with NO verify script must say so. `select(.verify)` silently
|
|
109
|
+
# drops it, so before 2026-09-03 a changed set that resolved only to
|
|
110
|
+
# verify-less folders ran the loop zero times and added NO ROW AT ALL --
|
|
111
|
+
# not pass, not unrun, not n/a. That is outside this script's own four-state
|
|
112
|
+
# vocabulary, and an absent row is the fail-open trap in data form: every
|
|
113
|
+
# reader that asks "did verify pass" gets nothing back and treats it as
|
|
114
|
+
# nothing to worry about. Measured on channels/ and pay/, two of the five
|
|
115
|
+
# deployed services, in BOTH lanes.
|
|
116
|
+
local _unverified
|
|
117
|
+
_unverified="$(printf '%s\n' "$folders" | jq -r 'select(.verify | not) | .folder // .reason // "unmapped"' | paste -sd, - 2>/dev/null || true)"
|
|
118
|
+
if [ -n "$_unverified" ]; then
|
|
119
|
+
_add verify unrun "no verify script for: $_unverified — an unrun gate is not a pass"
|
|
120
|
+
fi
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# ---- 2. objective security gate (zero-LLM backstop) ----
|
|
124
|
+
if command -v gitleaks >/dev/null 2>&1; then
|
|
125
|
+
_run secrets gitleaks detect --no-git --source "$ROOT" --redact -q
|
|
126
|
+
else
|
|
127
|
+
_add secrets unrun "gitleaks not installed — absent tool is an unrun check, never green"
|
|
128
|
+
fi
|
|
129
|
+
if printf '%s\n' "$files_txt" | grep -qE '(bun\.lock|package\.json)$'; then
|
|
130
|
+
_run dep-audit bash -c "cd '$ROOT' && bun audit --audit-level moderate"
|
|
131
|
+
else
|
|
132
|
+
_add dep-audit n/a "no dependency file in the diff"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# security greps over the diff (w4-tools.md § Security grep) — hits are a FAIL
|
|
136
|
+
local sec
|
|
137
|
+
sec="$(git -C "$ROOT" diff HEAD 2>/dev/null | grep -E '^\+' \
|
|
138
|
+
| grep -vE '^\+\+\+' \
|
|
139
|
+
| grep -E "eval\(|dangerouslySetInnerHTML|Access-Control-Allow-Origin.*\*" \
|
|
140
|
+
| grep -vE '// allow|sanitize|DOMPurify' | head -5 || true)"
|
|
141
|
+
if [ -n "$sec" ]; then _add security-grep fail "$(printf '%s' "$sec" | tr '\n' ' ')"
|
|
142
|
+
else _add security-grep pass "0 hits over the diff"; fi
|
|
143
|
+
|
|
144
|
+
# ---- 3. surface checklist (missing file = W4 FAIL, by do.md rule) ----
|
|
145
|
+
if [ ! -f "$ROOT/.w2-surface-checklist.json" ]; then
|
|
146
|
+
_add surface-checklist fail ".w2-surface-checklist.json missing — W2 must declare surfaces or surface:none+reason"
|
|
147
|
+
elif [ "$(jq -r '.surface // empty' "$ROOT/.w2-surface-checklist.json")" = "none" ]; then
|
|
148
|
+
if [ -n "$(jq -r '.reason // empty' "$ROOT/.w2-surface-checklist.json")" ]; then
|
|
149
|
+
_add surface-checklist pass "surface:none with a reason"
|
|
150
|
+
else
|
|
151
|
+
_add surface-checklist fail "surface:none without a reason"
|
|
152
|
+
fi
|
|
153
|
+
else
|
|
154
|
+
_add surface-checklist pass "surfaces declared"
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
# ---- 3b. per-surface WIRING greps (do.md W4: nav_entry / inbound_links /
|
|
158
|
+
# sdk_export / mcp_tool / cli_verb). These are NOT the same check as
|
|
159
|
+
# `do-reconcile.sh navigation|sdk`; each declared surface must be found
|
|
160
|
+
# wired at its named site. Needs a folder to resolve src/ paths against.
|
|
161
|
+
local cl="$ROOT/.w2-surface-checklist.json" wroot wfails=0 wchecked=0 route entry page txt ex tool verb
|
|
162
|
+
if [ -f "$cl" ] && [ "$(jq -r '.surface // empty' "$cl")" != "none" ]; then
|
|
163
|
+
wroot="$(printf '%s\n' "$folders" | jq -r 'select(.verify) | .folder' | head -1)"
|
|
164
|
+
if [ -z "$wroot" ]; then
|
|
165
|
+
_add surface-wiring unrun "no code folder resolved — cannot locate src/ to grep the declared surfaces"
|
|
166
|
+
else
|
|
167
|
+
# KNOWN, MEASURED 2026-09-01: this is do.md's grep VERBATIM, and
|
|
168
|
+
# one.ie/web/src/lib/navigation.ts contains exactly two `path:` lines, both
|
|
169
|
+
# of them TypeScript parameter declarations — no route table. So a cycle
|
|
170
|
+
# declaring nav_entry fails here. That is not a bug introduced by moving
|
|
171
|
+
# the check into a script: do.md's own inline `grep -q … || exit 1` fails
|
|
172
|
+
# identically. Transcribed faithfully on purpose; fixing the check is a
|
|
173
|
+
# separate change to the contract, not something a runner may do quietly.
|
|
174
|
+
while IFS= read -r route; do
|
|
175
|
+
[ -n "$route" ] || continue; wchecked=$((wchecked+1))
|
|
176
|
+
grep -q "path:.*$route" "$ROOT/$wroot/src/lib/navigation.ts" 2>/dev/null \
|
|
177
|
+
|| { wfails=$((wfails+1)); }
|
|
178
|
+
done < <(jq -r '.surfaces[]? | select(.nav_entry) | .nav_entry' "$cl" 2>/dev/null)
|
|
179
|
+
while IFS= read -r entry; do
|
|
180
|
+
[ -n "$entry" ] || continue; wchecked=$((wchecked+1))
|
|
181
|
+
page="${entry%%:*}"; txt="${entry#*:}"
|
|
182
|
+
grep -q "$txt" "$ROOT/$wroot/src/pages/$page.astro" 2>/dev/null || wfails=$((wfails+1))
|
|
183
|
+
done < <(jq -r '.surfaces[]? | select(.inbound_links) | .inbound_links[]' "$cl" 2>/dev/null)
|
|
184
|
+
while IFS= read -r ex; do
|
|
185
|
+
[ -n "$ex" ] || continue; wchecked=$((wchecked+1))
|
|
186
|
+
grep -q "export.*from.*$ex" "$ROOT/packages/sdk/src/index.ts" 2>/dev/null || wfails=$((wfails+1))
|
|
187
|
+
done < <(jq -r '.surfaces[]? | select(.sdk_export) | .sdk_export' "$cl" 2>/dev/null)
|
|
188
|
+
while IFS= read -r tool; do
|
|
189
|
+
[ -n "$tool" ] || continue; wchecked=$((wchecked+1))
|
|
190
|
+
grep -rq "$tool" "$ROOT/packages/mcp/src/index.ts" 2>/dev/null || wfails=$((wfails+1))
|
|
191
|
+
done < <(jq -r '.surfaces[]? | select(.mcp_tool) | .mcp_tool' "$cl" 2>/dev/null)
|
|
192
|
+
while IFS= read -r verb; do
|
|
193
|
+
[ -n "$verb" ] || continue; wchecked=$((wchecked+1))
|
|
194
|
+
grep -rq "$verb" "$ROOT/packages/cli/src/index.ts" 2>/dev/null || wfails=$((wfails+1))
|
|
195
|
+
done < <(jq -r '.surfaces[]? | select(.cli_verb) | .cli_verb' "$cl" 2>/dev/null)
|
|
196
|
+
if [ "$wfails" -gt 0 ]; then
|
|
197
|
+
_add surface-wiring fail "$wfails of $wchecked declared surfaces are NOT wired at their named site"
|
|
198
|
+
else
|
|
199
|
+
_add surface-wiring pass "$wchecked declared surfaces wired"
|
|
200
|
+
fi
|
|
201
|
+
fi
|
|
202
|
+
else
|
|
203
|
+
_add surface-wiring n/a "no surfaces declared (surface:none, or no checklist — see surface-checklist)"
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
# ---- 3c. the GOAL GATE and the PROMISE ORACLE.
|
|
207
|
+
# do.md's W4 block runs `$(cycle.demo.command)` and, when text/<slug>.md
|
|
208
|
+
# exists, `do-prove.sh <slug>`. This script cannot invent either: the demo
|
|
209
|
+
# command lives in the cycle, and the slug in the plan. They are therefore
|
|
210
|
+
# reported UNRUN unless supplied — which, by the four-state law, keeps
|
|
211
|
+
# deterministic_pass RED. A caller who runs the script bare CANNOT get a
|
|
212
|
+
# green without having run the goal gate. That is the point.
|
|
213
|
+
if [ -n "$DEMO_CMD" ]; then
|
|
214
|
+
_run demo-gate bash -c "cd '$ROOT' && $DEMO_CMD"
|
|
215
|
+
else
|
|
216
|
+
_add demo-gate unrun "no --demo <cmd> supplied — the cycle's goal gate has NOT run"
|
|
217
|
+
fi
|
|
218
|
+
if [ -n "$SLUG" ]; then
|
|
219
|
+
if [ -f "$ROOT/text/$SLUG.md" ]; then
|
|
220
|
+
_run promise-prove bash "$S/do-prove.sh" "$SLUG"
|
|
221
|
+
else
|
|
222
|
+
_add promise-prove n/a "no text/$SLUG.md — no promise to prove"
|
|
223
|
+
fi
|
|
224
|
+
else
|
|
225
|
+
_add promise-prove unrun "no --slug supplied — do-prove.sh has NOT run"
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
# ---- 4. the RECONCILES gate: the 7 canons ----
|
|
229
|
+
# $files_txt is deliberately UNQUOTED here so newline-separated paths become
|
|
230
|
+
# argv. Repo paths carry no spaces; a path that did would need bash 4 arrays.
|
|
231
|
+
local canon
|
|
232
|
+
if [ "${W4_SKIP_RECONCILE:-0}" = "1" ]; then
|
|
233
|
+
# ESCAPE HATCH FOR THE SELF-TEST ONLY. `do-reconcile.sh types` is a tsc that
|
|
234
|
+
# can queue for minutes behind the governor, and the self-test calls this
|
|
235
|
+
# script six times to prove the cheap gates flip. Skipping is safe ONLY
|
|
236
|
+
# because a skip is `unrun`, not `pass` — the verdict still goes red, and
|
|
237
|
+
# test 6 below asserts exactly that. Never set this in a real cycle.
|
|
238
|
+
for canon in types dictionary substrate sdk authority design navigation; do
|
|
239
|
+
_add "reconcile:$canon" unrun "W4_SKIP_RECONCILE=1 — an unrun canon is not a passed canon"
|
|
240
|
+
done
|
|
241
|
+
_add consumer-sweep unrun "W4_SKIP_RECONCILE=1 — an unrun sweep is not a passed sweep"
|
|
242
|
+
else
|
|
243
|
+
_run reconcile:types bash "$S/do-reconcile.sh" types
|
|
244
|
+
_run reconcile:dictionary bash "$S/do-reconcile.sh" dictionary $files_txt
|
|
245
|
+
# do.md scopes these: `do-reconcile.sh navigation <surface>` / `design <file>`
|
|
246
|
+
# are SURFACE-task canons. A markdown doc is not a UI surface, so feeding
|
|
247
|
+
# text/*.md to `navigation` produces a guaranteed false RED ("text/do.md not
|
|
248
|
+
# registered in menu.ts" — measured 2026-09-01 on this very change). Excluded
|
|
249
|
+
# here to restore do.md's own scoping; everything that IS a surface still runs.
|
|
250
|
+
local surface_txt
|
|
251
|
+
surface_txt="$(printf '%s\n' "$files_txt" | grep -vE '\.md$' || true)"
|
|
252
|
+
for canon in substrate sdk authority; do
|
|
253
|
+
_run "reconcile:$canon" bash "$S/do-reconcile.sh" "$canon" $files_txt
|
|
254
|
+
done
|
|
255
|
+
for canon in design navigation; do
|
|
256
|
+
if [ -n "$surface_txt" ]; then
|
|
257
|
+
_run "reconcile:$canon" bash "$S/do-reconcile.sh" "$canon" $surface_txt
|
|
258
|
+
else
|
|
259
|
+
_add "reconcile:$canon" n/a "no non-markdown surface in the diff"
|
|
260
|
+
fi
|
|
261
|
+
done
|
|
262
|
+
|
|
263
|
+
# ---- 5. consumer sweep ----
|
|
264
|
+
if [ "$nfiles" -gt 0 ]; then
|
|
265
|
+
_run consumer-sweep bash "$S/do-consumer-sweep.sh" --exec $files_txt
|
|
266
|
+
else
|
|
267
|
+
_add consumer-sweep n/a "empty diff"
|
|
268
|
+
fi
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
# ---- 6. tsc delta vs the W0 baseline (hard gate: <= 0) ----
|
|
272
|
+
if [ -f "$ROOT/.w0-baseline.json" ]; then
|
|
273
|
+
local base now
|
|
274
|
+
base="$(jq -r '.tscErrors // empty' "$ROOT/.w0-baseline.json" 2>/dev/null || echo '')"
|
|
275
|
+
if [ -z "$base" ]; then
|
|
276
|
+
_add tsc-delta unrun ".w0-baseline.json has no tscErrors"
|
|
277
|
+
else
|
|
278
|
+
set +e; now="$(cd "$ROOT" && "${_GATE[@]}" bunx tsc --noEmit 2>&1 | grep -c 'error TS')"; set -e
|
|
279
|
+
if [ "$now" -le "$base" ]; then _add tsc-delta pass "baseline=$base now=$now"
|
|
280
|
+
else _add tsc-delta fail "baseline=$base now=$now — new type errors"; fi
|
|
281
|
+
fi
|
|
282
|
+
else
|
|
283
|
+
_add tsc-delta unrun "no .w0-baseline.json — W0 did not run, so the delta is unknown"
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
# ---- verdict ----
|
|
287
|
+
local gates fails unruns
|
|
288
|
+
gates="$(jq -s '.' "$RESULTS_FILE")"
|
|
289
|
+
fails="$(printf '%s' "$gates" | jq '[.[] | select(.status=="fail")] | length')"
|
|
290
|
+
unruns="$(printf '%s' "$gates" | jq '[.[] | select(.status=="unrun")] | length')"
|
|
291
|
+
rm -f "$RESULTS_FILE"
|
|
292
|
+
|
|
293
|
+
jq -n --argjson gates "$gates" --argjson fails "$fails" --argjson unruns "$unruns" \
|
|
294
|
+
--argjson files "$(printf '%s\n' "$files_txt" | jq -R -s 'split("\n") | map(select(length>0))')" \
|
|
295
|
+
'{files:$files, gates:$gates, fails:$fails, unrun:$unruns,
|
|
296
|
+
deterministic_pass: ($fails == 0 and $unruns == 0),
|
|
297
|
+
note: "deterministic_pass requires ZERO fails AND ZERO unruns. An unrun gate is not a passed gate. The rubric verdict (goal-fit, taste) is scored on top of this pack; it may only LOWER the outcome, never raise a fail to a pass."}'
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
_self_test() {
|
|
301
|
+
local fails=0 out t
|
|
302
|
+
t="$(mktemp -d)"; trap 'rm -rf "$t"' RETURN
|
|
303
|
+
|
|
304
|
+
# 1. shape: a run emits valid JSON with a gate list and a verdict.
|
|
305
|
+
# Use a CODE file, not a doc: a doc-only diff makes the verify lane `n/a`
|
|
306
|
+
# by rule, which would hide the unrun branch test 2 exists to prove.
|
|
307
|
+
out="$(W4_SKIP_VERIFY=1 W4_SKIP_RECONCILE=1 bash "$S/do-w4-gates.sh" --files one.ie/web/src/lib/authority.ts 2>/dev/null)" || true
|
|
308
|
+
if printf '%s' "$out" | jq -e '(.gates|length) > 5 and has("deterministic_pass")' >/dev/null 2>&1; then
|
|
309
|
+
echo "ok: evidence pack is valid JSON with a gate list and a verdict"
|
|
310
|
+
else
|
|
311
|
+
echo "FAIL: evidence pack malformed"; fails=$((fails+1))
|
|
312
|
+
fi
|
|
313
|
+
|
|
314
|
+
# 2. RED PROOF (a) — an UNRUN gate must not read as a pass.
|
|
315
|
+
# W4_SKIP_VERIFY=1 forces the verify lane unrun; deterministic_pass MUST be false.
|
|
316
|
+
if printf '%s' "$out" | jq -e '[.gates[]|select(.gate=="verify" and .status=="unrun")]|length==1' >/dev/null 2>&1; then
|
|
317
|
+
if printf '%s' "$out" | jq -e '.deterministic_pass == false' >/dev/null 2>&1; then
|
|
318
|
+
echo "ok: RED PROOF — a skipped verify is reported unrun and the verdict goes RED"
|
|
319
|
+
else
|
|
320
|
+
echo "FAIL: RED PROOF — unrun verify still produced deterministic_pass:true"; fails=$((fails+1))
|
|
321
|
+
fi
|
|
322
|
+
else
|
|
323
|
+
echo "FAIL: RED PROOF — W4_SKIP_VERIFY did not surface as an unrun verify gate"; fails=$((fails+1))
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
# 3/4. RED PROOF (b) — the surface-checklist gate must move BOTH ways.
|
|
327
|
+
# A gate that is always red proves as little as one that is always green,
|
|
328
|
+
# so drive it green -> red -> green over a real file and assert each flip.
|
|
329
|
+
local cl="$ROOT/.w2-surface-checklist.json" had_cl=false
|
|
330
|
+
if [ -f "$cl" ]; then had_cl=true; cp "$cl" "$t/cl.bak"; fi
|
|
331
|
+
_cl_status() {
|
|
332
|
+
W4_SKIP_VERIFY=1 W4_SKIP_RECONCILE=1 bash "$S/do-w4-gates.sh" --files text/do.md 2>/dev/null \
|
|
333
|
+
| jq -r '[.gates[]|select(.gate=="surface-checklist")][0].status'
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
rm -f "$cl"
|
|
337
|
+
if [ "$(_cl_status)" = "fail" ]; then
|
|
338
|
+
echo "ok: RED PROOF — an ABSENT .w2-surface-checklist.json fails the cycle"
|
|
339
|
+
else
|
|
340
|
+
echo "FAIL: RED PROOF — absent surface checklist did not fail"; fails=$((fails+1))
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
printf '{"surface":"none"}\n' > "$cl"
|
|
344
|
+
if [ "$(_cl_status)" = "fail" ]; then
|
|
345
|
+
echo "ok: RED PROOF — surface:none WITHOUT a reason fails"
|
|
346
|
+
else
|
|
347
|
+
echo "FAIL: RED PROOF — surface:none without a reason passed"; fails=$((fails+1))
|
|
348
|
+
fi
|
|
349
|
+
|
|
350
|
+
# surface-WIRING, both directions. This is the gate that was MISSING from the
|
|
351
|
+
# first cut, so it gets the same treatment as the rest: prove it red, prove it
|
|
352
|
+
# green. `sdk_export` is used because its target (packages/sdk/src/index.ts) is
|
|
353
|
+
# a fixed path, unlike nav/inbound which resolve against the cycle's folder.
|
|
354
|
+
# A CODE file is passed so do-folder resolves $wroot (a doc-only diff cannot).
|
|
355
|
+
_wire_status() {
|
|
356
|
+
W4_SKIP_VERIFY=1 W4_SKIP_RECONCILE=1 bash "$S/do-w4-gates.sh" \
|
|
357
|
+
--files one.ie/web/src/lib/authority.ts 2>/dev/null \
|
|
358
|
+
| jq -r '[.gates[]|select(.gate=="surface-wiring")][0].status'
|
|
359
|
+
}
|
|
360
|
+
printf '{"surfaces":[{"sdk_export":"zzz-not-a-real-export"}]}\n' > "$cl"
|
|
361
|
+
if [ "$(_wire_status)" = "fail" ]; then
|
|
362
|
+
echo "ok: RED PROOF — a declared surface that is NOT wired fails surface-wiring"
|
|
363
|
+
else
|
|
364
|
+
echo "FAIL: RED PROOF — an unwired declared surface did not fail"; fails=$((fails+1))
|
|
365
|
+
fi
|
|
366
|
+
printf '{"surfaces":[{"sdk_export":"types"}]}\n' > "$cl"
|
|
367
|
+
if [ "$(_wire_status)" = "pass" ]; then
|
|
368
|
+
echo "ok: GREEN PROOF — a genuinely wired surface passes (surface-wiring is not stuck red)"
|
|
369
|
+
else
|
|
370
|
+
echo "FAIL: GREEN PROOF — a wired surface did not pass surface-wiring"; fails=$((fails+1))
|
|
371
|
+
fi
|
|
372
|
+
|
|
373
|
+
printf '{"surface":"none","reason":"self-test fixture"}\n' > "$cl"
|
|
374
|
+
if [ "$(_cl_status)" = "pass" ]; then
|
|
375
|
+
echo "ok: GREEN PROOF — surface:none WITH a reason passes (the gate is not stuck red)"
|
|
376
|
+
else
|
|
377
|
+
echo "FAIL: GREEN PROOF — a well-formed checklist did not pass"; fails=$((fails+1))
|
|
378
|
+
fi
|
|
379
|
+
|
|
380
|
+
rm -f "$cl"
|
|
381
|
+
if [ "$had_cl" = true ]; then cp "$t/cl.bak" "$cl"; fi
|
|
382
|
+
|
|
383
|
+
# 5. RED PROOF — the GOAL GATE. Bare, it must be `unrun`; supplied and passing,
|
|
384
|
+
# it must flip to `pass`. A caller can never get a green without running it.
|
|
385
|
+
local dg
|
|
386
|
+
dg="$(W4_SKIP_VERIFY=1 W4_SKIP_RECONCILE=1 bash "$S/do-w4-gates.sh" --files text/do.md 2>/dev/null \
|
|
387
|
+
| jq -r '[.gates[]|select(.gate=="demo-gate")][0].status')"
|
|
388
|
+
if [ "$dg" = "unrun" ]; then
|
|
389
|
+
echo "ok: RED PROOF — no --demo means the goal gate is UNRUN (verdict cannot be green)"
|
|
390
|
+
else
|
|
391
|
+
echo "FAIL: RED PROOF — bare run reported demo-gate as '$dg', not unrun"; fails=$((fails+1))
|
|
392
|
+
fi
|
|
393
|
+
dg="$(W4_SKIP_VERIFY=1 W4_SKIP_RECONCILE=1 bash "$S/do-w4-gates.sh" --demo true --files text/do.md 2>/dev/null \
|
|
394
|
+
| jq -r '[.gates[]|select(.gate=="demo-gate")][0].status')"
|
|
395
|
+
if [ "$dg" = "pass" ]; then
|
|
396
|
+
echo "ok: GREEN PROOF — a supplied, passing --demo flips the goal gate to pass"
|
|
397
|
+
else
|
|
398
|
+
echo "FAIL: GREEN PROOF — --demo true reported '$dg'"; fails=$((fails+1))
|
|
399
|
+
fi
|
|
400
|
+
dg="$(W4_SKIP_VERIFY=1 W4_SKIP_RECONCILE=1 bash "$S/do-w4-gates.sh" --demo false --files text/do.md 2>/dev/null \
|
|
401
|
+
| jq -r '[.gates[]|select(.gate=="demo-gate")][0].status')"
|
|
402
|
+
if [ "$dg" = "fail" ]; then
|
|
403
|
+
echo "ok: RED PROOF — a FAILING --demo fails the goal gate"
|
|
404
|
+
else
|
|
405
|
+
echo "FAIL: RED PROOF — --demo false reported '$dg'"; fails=$((fails+1))
|
|
406
|
+
fi
|
|
407
|
+
|
|
408
|
+
# 6. The PROMISE ORACLE's branch selection — proven without running do-prove.sh
|
|
409
|
+
# (a real prove is a browser run; what needs proving here is the routing).
|
|
410
|
+
local pp
|
|
411
|
+
pp="$(W4_SKIP_VERIFY=1 W4_SKIP_RECONCILE=1 bash "$S/do-w4-gates.sh" --files text/do.md 2>/dev/null \
|
|
412
|
+
| jq -r '[.gates[]|select(.gate=="promise-prove")][0].status')"
|
|
413
|
+
[ "$pp" = "unrun" ] \
|
|
414
|
+
&& echo "ok: RED PROOF — no --slug means the promise oracle is UNRUN" \
|
|
415
|
+
|| { echo "FAIL: no --slug reported promise-prove as '$pp', not unrun"; fails=$((fails+1)); }
|
|
416
|
+
pp="$(W4_SKIP_VERIFY=1 W4_SKIP_RECONCILE=1 bash "$S/do-w4-gates.sh" --slug __nosuchpromise__ --files text/do.md 2>/dev/null \
|
|
417
|
+
| jq -r '[.gates[]|select(.gate=="promise-prove")][0].status')"
|
|
418
|
+
[ "$pp" = "n/a" ] \
|
|
419
|
+
&& echo "ok: a slug with no text/<slug>.md is n/a — there is no promise to prove" \
|
|
420
|
+
|| { echo "FAIL: absent promise reported '$pp', not n/a"; fails=$((fails+1)); }
|
|
421
|
+
|
|
422
|
+
# 7. RED PROOF — the self-test's OWN escape hatch must not read as a pass.
|
|
423
|
+
# Tests 1-5 run with W4_SKIP_RECONCILE=1. If that skip scored `pass`, every
|
|
424
|
+
# assertion above would be measured against a gutted battery.
|
|
425
|
+
if printf '%s' "$out" | jq -e '[.gates[]|select(.gate|startswith("reconcile:"))|select(.status!="unrun")]|length==0' >/dev/null 2>&1; then
|
|
426
|
+
echo "ok: RED PROOF — the self-test's own W4_SKIP_RECONCILE reads as unrun, not pass"
|
|
427
|
+
else
|
|
428
|
+
echo "FAIL: RED PROOF — a skipped canon scored something other than unrun"; fails=$((fails+1))
|
|
429
|
+
fi
|
|
430
|
+
|
|
431
|
+
if [ "$fails" -eq 0 ]; then echo "do-w4-gates: self-test PASS"; return 0; fi
|
|
432
|
+
echo "do-w4-gates: self-test FAIL ($fails)"; return 1
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
[ "${1:-}" = "--self-test" ] && { _self_test; exit $?; }
|
|
436
|
+
|
|
437
|
+
PATHS=""
|
|
438
|
+
while [ "$#" -gt 0 ]; do
|
|
439
|
+
case "$1" in
|
|
440
|
+
--demo) DEMO_CMD="${2:-}"; shift 2 ;;
|
|
441
|
+
--slug) SLUG="${2:-}"; shift 2 ;;
|
|
442
|
+
--files) shift ;;
|
|
443
|
+
-h|--help)
|
|
444
|
+
echo "usage: do-w4-gates.sh [--files <path>...] [--demo '<cmd>'] [--slug <slug>] | --self-test" >&2
|
|
445
|
+
exit 2 ;;
|
|
446
|
+
*) PATHS="$PATHS $1"; shift ;;
|
|
447
|
+
esac
|
|
448
|
+
done
|
|
449
|
+
|
|
450
|
+
# unquoted on purpose — see the RECONCILES note in _gates
|
|
451
|
+
_gates $PATHS
|
package/scripts/do-walk.sh
CHANGED
|
@@ -286,9 +286,20 @@ for n in $ids; do
|
|
|
286
286
|
[ "$code" != "$es" ] && { ok=0; why="http=$code (want $es)"; }
|
|
287
287
|
fi
|
|
288
288
|
if [ "$ok" -eq 1 ] && [ -n "$et" ]; then
|
|
289
|
-
|
|
289
|
+
# Body FIRST, then grep it — never `curl | grep -q` here. `grep -q` exits
|
|
290
|
+
# the moment it matches, curl is still writing, curl dies of SIGPIPE (141),
|
|
291
|
+
# and `set -o pipefail` (line 73) hands that 141 to the `if`. The stop then
|
|
292
|
+
# fails *because the text was found*, on any page big enough that curl has
|
|
293
|
+
# not finished writing. Measured 2026-08-04: `/u/one/shop` serves "Shop"
|
|
294
|
+
# twice and the stop still reported "text 'Shop' not in served HTML",
|
|
295
|
+
# reproducibly. A false RED — the mirror of the false GREEN do-prove had.
|
|
296
|
+
# A here-string, not a pipe: `printf … | grep -q` reintroduces the exact
|
|
297
|
+
# same SIGPIPE race one process further left.
|
|
298
|
+
body="$(curl -s --max-time 20 "${cookie_args[@]+"${cookie_args[@]}"}" "$url" 2>/dev/null || true)"
|
|
299
|
+
if ! grep -qiF -- "$et" <<<"$body"; then
|
|
290
300
|
ok=0; why="text '$et' not in served HTML"
|
|
291
301
|
fi
|
|
302
|
+
unset body
|
|
292
303
|
fi
|
|
293
304
|
fi
|
|
294
305
|
if [ "$ok" -eq 1 ] && [ -n "$ec" ]; then
|