@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.
Files changed (263) hide show
  1. package/agents/abm-strategist.md +89 -0
  2. package/agents/ads-meta.md +97 -0
  3. package/agents/analyst.md +173 -0
  4. package/agents/architect.md +280 -0
  5. package/agents/brand-guardian.md +88 -0
  6. package/agents/brand-strategist.md +92 -0
  7. package/agents/campaign-content.md +90 -0
  8. package/agents/campaign-email.md +88 -0
  9. package/agents/campaign-sms.md +87 -0
  10. package/agents/campaign-social.md +87 -0
  11. package/agents/cco.md +99 -0
  12. package/agents/ceo.md +106 -0
  13. package/agents/chairman.md +120 -0
  14. package/agents/cmo.md +101 -0
  15. package/agents/community-greeter.md +89 -0
  16. package/agents/community-moderator.md +92 -0
  17. package/agents/compliance.md +92 -0
  18. package/agents/copywriter.md +100 -0
  19. package/agents/creative-strategist.md +95 -0
  20. package/agents/cro.md +98 -0
  21. package/agents/cto.md +267 -0
  22. package/agents/customer-interviewer.md +93 -0
  23. package/agents/customer-researcher.md +89 -0
  24. package/agents/customer-success-manager.md +90 -0
  25. package/agents/customer-trainer.md +90 -0
  26. package/agents/cxo.md +100 -0
  27. package/agents/demand-creator.md +89 -0
  28. package/agents/demo-mover.md +83 -0
  29. package/agents/demo-specialist.md +89 -0
  30. package/agents/demo-thai-family-law.md +87 -0
  31. package/agents/designer.md +112 -0
  32. package/agents/discovery-caller.md +98 -0
  33. package/agents/doctor.md +139 -0
  34. package/agents/educate-coach.md +100 -0
  35. package/agents/elevate-tutor.md +83 -0
  36. package/agents/email-lifecycle-marketer.md +91 -0
  37. package/agents/engage-specialist.md +99 -0
  38. package/agents/events-coordinator.md +90 -0
  39. package/agents/foundation-builder.md +94 -0
  40. package/agents/funnel-architect.md +89 -0
  41. package/agents/gift-creator.md +100 -0
  42. package/agents/google-ads.md +103 -0
  43. package/agents/guide.md +292 -0
  44. package/agents/helpdesk-dispatcher.md +88 -0
  45. package/agents/hook-specialist.md +91 -0
  46. package/agents/identify-optimizer.md +101 -0
  47. package/agents/implementer.md +305 -0
  48. package/agents/incident-commander.md +120 -0
  49. package/agents/insights-lead.md +93 -0
  50. package/agents/journey-runner.md +113 -0
  51. package/agents/linkedin-ads.md +123 -0
  52. package/agents/live-sales-chat.md +90 -0
  53. package/agents/market-researcher.md +88 -0
  54. package/agents/media-buyer.md +96 -0
  55. package/agents/movers-customer-researcher.md +95 -0
  56. package/agents/movers-foundation-builder.md +96 -0
  57. package/agents/movers-market-researcher.md +97 -0
  58. package/agents/movers-pricing-strategist.md +171 -0
  59. package/agents/nurture-architect.md +99 -0
  60. package/agents/offer-architect.md +92 -0
  61. package/agents/onboarder.md +222 -0
  62. package/agents/onboarding-specialist.md +92 -0
  63. package/agents/operations-dashboard.md +98 -0
  64. package/agents/perf-engineer.md +348 -0
  65. package/agents/playbook-writer.md +71 -0
  66. package/agents/plg-strategist.md +91 -0
  67. package/agents/positioning-architect.md +88 -0
  68. package/agents/press-officer.md +89 -0
  69. package/agents/pricing-strategist.md +94 -0
  70. package/agents/privacy-officer.md +92 -0
  71. package/agents/referral-manager.md +91 -0
  72. package/agents/refine-analyst.md +102 -0
  73. package/agents/release-manager.md +261 -0
  74. package/agents/renewals-upsell-rep.md +90 -0
  75. package/agents/review-engineer.md +327 -0
  76. package/agents/rewards-steward.md +82 -0
  77. package/agents/sales-call-coach.md +94 -0
  78. package/agents/sales-closer.md +98 -0
  79. package/agents/security-auditor.md +313 -0
  80. package/agents/sell-closer.md +101 -0
  81. package/agents/share-amplifier.md +99 -0
  82. package/agents/social-media-manager.md +92 -0
  83. package/agents/storyteller.md +250 -0
  84. package/agents/strategist.md +83 -0
  85. package/agents/strategy-aligner.md +94 -0
  86. package/agents/support-agent.md +90 -0
  87. package/agents/tagger.md +245 -0
  88. package/agents/tech-writer.md +216 -0
  89. package/agents/test-engineer.md +243 -0
  90. package/agents/tiktok-ads.md +105 -0
  91. package/agents/tracking-engineer.md +92 -0
  92. package/agents/upsell-strategist.md +101 -0
  93. package/agents/voice.md +85 -0
  94. package/agents/w4-tools.md +153 -0
  95. package/agents/w4-verify.md +20 -138
  96. package/agents/workflow-optimiser.md +84 -0
  97. package/commands/close.md +814 -160
  98. package/commands/create.md +2 -2
  99. package/commands/deploy.md +554 -175
  100. package/commands/do.md +119 -109
  101. package/commands/fast.md +67 -0
  102. package/commands/improve.md +2 -2
  103. package/commands/one.md +418 -0
  104. package/commands/see.md +1 -1
  105. package/commands/sync.md +1 -1
  106. package/commands/tasks.md +222 -0
  107. package/commands/vespio.md +110 -0
  108. package/commands/vespio.remote.md +227 -0
  109. package/hooks/hooks.json +25 -79
  110. package/hooks/lib/governor-escape-match.sh +88 -0
  111. package/hooks/lib/hook.sh +4 -13
  112. package/hooks/lib/signal.sh +9 -2
  113. package/hooks/scripts/branch-pin.sh +6 -0
  114. package/hooks/scripts/config-protect.sh +6 -0
  115. package/hooks/scripts/do-outcome-gate.sh +19 -2
  116. package/hooks/scripts/git-add-guard.sh +43 -2
  117. package/hooks/scripts/governor-escape.sh +96 -0
  118. package/hooks/scripts/load-guard.sh +127 -0
  119. package/hooks/scripts/post-edit-check.sh +36 -29
  120. package/hooks/scripts/session-start.sh +34 -80
  121. package/hooks/scripts/task-complete-verify.sh +46 -40
  122. package/package.json +1 -1
  123. package/rules/documentation.md +9 -0
  124. package/scripts/ad-copy-lint.sh +656 -0
  125. package/scripts/agentverse-audit.sh +256 -0
  126. package/scripts/asi-walk.sh +435 -0
  127. package/scripts/astro-build-cached.sh +143 -0
  128. package/scripts/blocks-manifest-cached.sh +100 -0
  129. package/scripts/blocks-manifest.mjs +497 -0
  130. package/scripts/blocks-ratchet.sh +116 -0
  131. package/scripts/blocks-render-probe.mjs +529 -0
  132. package/scripts/blocks-usage.mjs +334 -0
  133. package/scripts/chat-context-check.sh +89 -0
  134. package/scripts/close-metrics.sh +558 -0
  135. package/scripts/close-owner.sh +268 -0
  136. package/scripts/db-sync-lock-check.sh +116 -0
  137. package/scripts/deploy-dev.sh +35 -0
  138. package/scripts/deploy-emit.sh +298 -0
  139. package/scripts/deploy-gate-check.sh +128 -0
  140. package/scripts/deploy-ready.sh +78 -0
  141. package/scripts/deploy-record.sh +605 -0
  142. package/scripts/deploy.sh +1273 -0
  143. package/scripts/do-auto.sh +598 -48
  144. package/scripts/do-brief.sh +113 -0
  145. package/scripts/do-close.sh +1137 -0
  146. package/scripts/do-commit.sh +75 -0
  147. package/scripts/do-consumer-sweep.sh +18 -1
  148. package/scripts/do-cycle-shape-check.sh +160 -0
  149. package/scripts/do-decide.sh +476 -0
  150. package/scripts/do-derive-check.sh +436 -0
  151. package/scripts/do-fleet.sh +106 -28
  152. package/scripts/do-folder.sh +10 -1
  153. package/scripts/do-next.sh +106 -0
  154. package/scripts/do-orchestrate.sh +17 -5
  155. package/scripts/do-plan-json.mjs +201 -0
  156. package/scripts/do-plan-json.sh +8 -0
  157. package/scripts/do-preflight.sh +117 -0
  158. package/scripts/do-project.sh +157 -0
  159. package/scripts/do-prove-selftest.sh +108 -0
  160. package/scripts/do-prove.sh +295 -23
  161. package/scripts/do-rank.py +31 -1
  162. package/scripts/do-recon-cache.sh +7 -1
  163. package/scripts/do-recon-pack.sh +196 -0
  164. package/scripts/do-reconcile.sh +121 -4
  165. package/scripts/do-signal.sh +280 -23
  166. package/scripts/do-smoke.sh +18 -1
  167. package/scripts/do-test-gate.sh +80 -0
  168. package/scripts/do-tick.sh +102 -0
  169. package/scripts/do-tier.sh +6 -0
  170. package/scripts/do-triage.sh +182 -0
  171. package/scripts/do-ui-gate.sh +1 -1
  172. package/scripts/do-w4-gates.sh +451 -0
  173. package/scripts/do-walk.sh +12 -1
  174. package/scripts/env-sync.sh +173 -0
  175. package/scripts/factory-brief-check.sh +330 -0
  176. package/scripts/factory-check.sh +68 -14
  177. package/scripts/factory-close-check.sh +257 -0
  178. package/scripts/factory-emit.sh +211 -0
  179. package/scripts/factory-executor-check.mjs +353 -0
  180. package/scripts/factory-peak.sh +301 -0
  181. package/scripts/factory-repo.sh +118 -3
  182. package/scripts/factory-review-check.mjs +61 -0
  183. package/scripts/factory-ship.sh +61 -0
  184. package/scripts/factory-tasks-check.sh +18 -1
  185. package/scripts/factory-turn.sh +326 -0
  186. package/scripts/factory-walk.sh +396 -0
  187. package/scripts/factory-width.sh +57 -0
  188. package/scripts/fade-toxic.sh +4 -3
  189. package/scripts/fixtures/factory-brief-real.md +44 -0
  190. package/scripts/fixtures/triage-dupe.md +5 -0
  191. package/scripts/fleet-manifest.mjs +108 -0
  192. package/scripts/fleet-status.sh +110 -0
  193. package/scripts/full-suite-paths-check.sh +144 -0
  194. package/scripts/gate-reaper-check.sh +98 -0
  195. package/scripts/gate-reaper.sh +125 -0
  196. package/scripts/gate-run.sh +73 -0
  197. package/scripts/gc-content-check.sh +140 -0
  198. package/scripts/gen-dev-config.py +20 -0
  199. package/scripts/govern-bound-check.sh +60 -0
  200. package/scripts/govern-claims-check.sh +233 -0
  201. package/scripts/govern-mem-check.sh +290 -0
  202. package/scripts/governor-doors-check.sh +362 -0
  203. package/scripts/governor-escape-check.sh +171 -0
  204. package/scripts/health.sh +413 -0
  205. package/scripts/id-inventory.mjs +418 -0
  206. package/scripts/land.sh +551 -0
  207. package/scripts/lib/gc-finished.sh +77 -0
  208. package/scripts/lib/govern.sh +361 -0
  209. package/scripts/lib/govern.ts +756 -0
  210. package/scripts/lighthouse-run.sh +187 -0
  211. package/scripts/livekit-live-check.sh +61 -0
  212. package/scripts/livekit-ratchet.sh +46 -0
  213. package/scripts/load-guard-check.sh +49 -0
  214. package/scripts/machine-check.sh +102 -0
  215. package/scripts/machine-watch.sh +177 -0
  216. package/scripts/one-agents.mjs +415 -0
  217. package/scripts/one-resume.sh +78 -0
  218. package/scripts/orphan-baseline.json +182 -0
  219. package/scripts/orphan-modules.mjs +179 -0
  220. package/scripts/pr-body.sh +335 -0
  221. package/scripts/preview-fd-check.sh +289 -0
  222. package/scripts/promise-manifest.mjs +24 -1
  223. package/scripts/release.sh +322 -0
  224. package/scripts/roles-check.sh +946 -0
  225. package/scripts/sdk-build-cached.sh +64 -0
  226. package/scripts/signal-watch.sh +241 -0
  227. package/scripts/skills-publish.sh +94 -0
  228. package/scripts/speed-cache-check.sh +173 -0
  229. package/scripts/speed-check.mjs +907 -0
  230. package/scripts/speed-parity-check.sh +648 -0
  231. package/scripts/speed-waterfall-check.sh +355 -0
  232. package/scripts/substrate-env-parity.mjs +156 -0
  233. package/scripts/tasks-claim-race.mjs +108 -0
  234. package/scripts/tasks-loop.sh +185 -0
  235. package/scripts/test-cached.sh +255 -0
  236. package/scripts/test-full.sh +87 -0
  237. package/scripts/test-honesty.mjs +137 -0
  238. package/scripts/test-lanes.sh +166 -0
  239. package/scripts/test-speed.sh +94 -0
  240. package/scripts/triage-shape-check.sh +149 -0
  241. package/scripts/tsc-cached.sh +179 -0
  242. package/scripts/typedb-flake-check.sh +197 -0
  243. package/scripts/urls-lint.sh +15 -0
  244. package/scripts/verify-fast.sh +445 -0
  245. package/scripts/vespio-sync.sh +149 -0
  246. package/scripts/wf-check.mjs +104 -1
  247. package/scripts/worktree-preview.sh +879 -0
  248. package/scripts/worktree-up.sh +208 -0
  249. package/skills/livekit-agents/SKILL.md +285 -0
  250. package/skills/livekit-agents/references/freshness-rules.md +168 -0
  251. package/skills/shadcn/SKILL.md +1 -1
  252. package/skills/signal/SKILL.md +0 -1
  253. package/skills/voice/SKILL.md +94 -6
  254. package/skills/voice/corpus-check.sh +87 -0
  255. package/hooks/scripts/compact-hint.sh +0 -35
  256. package/hooks/scripts/gate-guard.sh +0 -83
  257. package/hooks/scripts/read-tracker.sh +0 -26
  258. package/hooks/scripts/session-end-verify.sh +0 -51
  259. package/hooks/scripts/stop-reflect.sh +0 -140
  260. package/hooks/scripts/sync-priority-todo.sh +0 -57
  261. package/hooks/scripts/sync-todo-docs.sh +0 -46
  262. package/hooks/scripts/tool-signal.sh +0 -48
  263. package/scripts/do-tasks-bridge.py +0 -366
@@ -0,0 +1,648 @@
1
+ #!/usr/bin/env bash
2
+ # speed-parity-check.sh — C10's checker. Three flags:
3
+ #
4
+ # --parity static, exit 0/1 — P1-P6, the six structural facts the
5
+ # Interface Contract (text/speed-todo.md, C10) pins: the
6
+ # altitude of the read probe (P1), the sequence order of the
7
+ # store middleware (P2), the frozen export surface of
8
+ # lib/page-cache.ts (P3), pageCacheVerdict as the single
9
+ # decision point in both tenant routes (P4), hasLiveBindings
10
+ # staying uncapped (P5), and no per-visitor value reaching
11
+ # LeadCaptureScript un-gated (P6).
12
+ # --freshness static, exit 0/1 — F1/F2. touchPage is the ONE choke point
13
+ # for "this page changed" (IC row 8): every UPDATE pages SET /
14
+ # INSERT INTO pages write site across the four writer files
15
+ # must be followed by a touchPage( call within 6 lines (F1),
16
+ # and the old name (broadcastPageUpdated) must have zero
17
+ # occurrences repo-wide once the rename lands (F2) — a
18
+ # survivor is a second, silent choke point.
19
+ # --check-gate exit 0 iff EVERY one of six planted violations goes RED
20
+ # against the real checks above. G1/G2/G4/G5 exercise the pure
21
+ # pageCacheVerdict() function directly with a bad input — no
22
+ # file mutation needed. G3 and G6 mutate a COPY of the source
23
+ # under $TMP (never a tracked file) and re-run the matching
24
+ # structural check against that copy.
25
+ #
26
+ # Exit codes: 0 pass · 1 fail · 2 missing prerequisite (a source file or `bun`
27
+ # is absent — never reported as pass).
28
+ #
29
+ # THE TWO TRAPS THIS SCRIPT IS WRITTEN AROUND (both already paid for in this
30
+ # repo — see .claude/CLAUDE.md and text/learnings.md):
31
+ # 1. Plant against a FIXTURE COPY in $TMP, never a tracked file. An
32
+ # interrupted run must never leave middleware.ts / inject-rows.ts /
33
+ # page-cache.ts corrupted, and mutate-then-restore is unsafe under a live
34
+ # parallel batch (roles-check.sh --check-decide is the shape copied here).
35
+ # 2. NO `producer | grep -q` under `set -o pipefail` anywhere — a MATCH
36
+ # SIGPIPEs the producer and the pipeline reports 141 (false RED, or false
37
+ # GREEN under a `!`). Every check below captures a command's full output
38
+ # to a variable or a scratch file FIRST (`out=$(...)`), then matches it
39
+ # with `grep -c` (which reads to EOF, never exits early) or an `awk`
40
+ # pass with no `head`/`grep -q` downstream of a live process.
41
+ #
42
+ # Usage:
43
+ # speed-parity-check.sh --parity
44
+ # speed-parity-check.sh --freshness
45
+ # speed-parity-check.sh --check-gate
46
+
47
+ set -euo pipefail
48
+
49
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
50
+ cd "$ROOT"
51
+
52
+ PAGE_CACHE_TS="one.ie/web/src/lib/page-cache.ts"
53
+ PAGE_CACHE_MW="one.ie/web/src/lib/page-cache-middleware.ts"
54
+ MIDDLEWARE_TS="one.ie/web/src/middleware.ts"
55
+ INJECT_ROWS="one.ie/web/src/lib/puck/inject-rows.ts"
56
+ PAGESLUG_ASTRO="one.ie/web/src/pages/u/[slug]/p/[pageSlug].astro"
57
+ INDEX_ASTRO="one.ie/web/src/pages/u/[slug]/index.astro"
58
+ PAGES_TS="one.ie/web/src/lib/resolvers/pages.ts"
59
+ PAGES_CHAT_TS="one.ie/web/src/lib/resolvers/pages-chat.ts"
60
+ API_SLUG_TS="one.ie/web/src/pages/api/pages/[slug].ts"
61
+ API_INDEX_TS="one.ie/web/src/pages/api/pages/index.ts"
62
+
63
+ WRITER_FILES=("$PAGES_TS" "$PAGES_CHAT_TS" "$API_SLUG_TS" "$API_INDEX_TS")
64
+ ROUTE_FILES=("$PAGESLUG_ASTRO" "$INDEX_ASTRO")
65
+
66
+ TMP="$(mktemp -d)"
67
+ trap 'rm -rf "$TMP"' EXIT
68
+
69
+ need_file() {
70
+ if [ ! -f "$1" ]; then
71
+ echo "[speed-parity-check] missing $1" >&2
72
+ return 2
73
+ fi
74
+ return 0
75
+ }
76
+
77
+ need_bun() {
78
+ if ! command -v bun >/dev/null 2>&1; then
79
+ echo "[speed-parity-check] bun not on PATH — cannot run pure-function checks" >&2
80
+ return 2
81
+ fi
82
+ return 0
83
+ }
84
+
85
+ # first_field_of_first_line PATTERN VAR_CONTAINING_GREP_N_OUTPUT
86
+ # Never `grep -n ... | head -1` — that pipes a live grep into an early-exiting
87
+ # consumer. Every caller here first captures grep's FULL output to a shell
88
+ # variable (`matches=$(grep -n ... file || true)`), which is a command
89
+ # substitution (reads to EOF, no early-exit risk), then this just does string
90
+ # processing on the already-captured text.
91
+ first_line_number() {
92
+ awk -F: 'NR==1 && $1 != "" {n=$1} END{print n+0}' <<<"$1"
93
+ }
94
+
95
+ # ---------------------------------------------------------------------------
96
+ # --parity : P1-P6
97
+ # ---------------------------------------------------------------------------
98
+
99
+ p1_altitude() {
100
+ local file="${1:-$MIDDLEWARE_TS}"
101
+ need_file "$file" || return 2
102
+ local isprimary_return domainmemo_matches pagecache_matches
103
+ isprimary_return=$(awk '/const isPrimary =/{f=1} f && /return next\(\);/{print NR; exit}' "$file")
104
+ domainmemo_matches=$(grep -n 'readDomainRowMemo(' "$file" || true)
105
+ pagecache_matches=$(grep -n 'readPageCache(' "$file" || true)
106
+ local domainmemo_line pagecache_line
107
+ domainmemo_line=$(first_line_number "$domainmemo_matches")
108
+ pagecache_line=$(first_line_number "$pagecache_matches")
109
+ if [ -z "${isprimary_return:-}" ] || [ "$domainmemo_line" -eq 0 ] || [ "$pagecache_line" -eq 0 ]; then
110
+ echo " P1 FAIL — could not locate anchors (isPrimary-return='${isprimary_return:-}' readDomainRowMemo=$domainmemo_line readPageCache=$pagecache_line) in $file" >&2
111
+ return 1
112
+ fi
113
+ if [ "$pagecache_line" -gt "$isprimary_return" ] && [ "$pagecache_line" -lt "$domainmemo_line" ]; then
114
+ return 0
115
+ fi
116
+ echo " P1 FAIL — altitude violated in $file: isPrimary-return=$isprimary_return readPageCache=$pagecache_line readDomainRowMemo=$domainmemo_line (want isPrimary-return < readPageCache < readDomainRowMemo)" >&2
117
+ return 1
118
+ }
119
+
120
+ p2_sequence() {
121
+ local file="${1:-$MIDDLEWARE_TS}"
122
+ need_file "$file" || return 2
123
+ local block
124
+ block=$(sed -n '/^export const onRequest = sequence(/,/^);/p' "$file")
125
+ if [ -z "$block" ]; then
126
+ echo " P2 FAIL — sequence(...) block not found in $file" >&2
127
+ return 1
128
+ fi
129
+ local perfstart_matches store_matches mainmw_matches
130
+ perfstart_matches=$(grep -n 'perfStart' <<<"$block" || true)
131
+ store_matches=$(grep -n 'pageCacheStore' <<<"$block" || true)
132
+ mainmw_matches=$(grep -nE '^\s*mainMiddleware,\s*$' <<<"$block" || true)
133
+ local perfstart_line store_line mainmw_line
134
+ perfstart_line=$(first_line_number "$perfstart_matches")
135
+ store_line=$(first_line_number "$store_matches")
136
+ mainmw_line=$(first_line_number "$mainmw_matches")
137
+ if [ "$perfstart_line" -eq 0 ] || [ "$store_line" -eq 0 ] || [ "$mainmw_line" -eq 0 ]; then
138
+ echo " P2 FAIL — sequence missing an anchor (perfStart=$perfstart_line pageCacheStore=$store_line mainMiddleware=$mainmw_line)" >&2
139
+ return 1
140
+ fi
141
+ if [ "$store_line" -gt "$perfstart_line" ] && [ "$store_line" -lt "$mainmw_line" ]; then
142
+ return 0
143
+ fi
144
+ echo " P2 FAIL — sequence order wrong: perfStart=$perfstart_line pageCacheStore=$store_line mainMiddleware=$mainmw_line (want perfStart < pageCacheStore < mainMiddleware)" >&2
145
+ return 1
146
+ }
147
+
148
+ p3_exports() {
149
+ local file="${1:-$PAGE_CACHE_TS}"
150
+ need_file "$file" || return 2
151
+ local pinned=(PAGE_CACHE_TTL_SECONDS PAGE_CACHE_HEADER PAGE_CACHE_STATUS_HEADER pageCacheKey PageCacheInput pageCacheVerdict readPageCache storePageCache purgePageCache)
152
+ # PageCacheVerdict is the return-type companion to PageCacheInput and is
153
+ # the one allowed export beyond the nine pinned names.
154
+ local allowed_extra="PageCacheVerdict"
155
+ local exports
156
+ exports=$(grep -nE '^export ' "$file" || true)
157
+ printf '%s\n' "$exports" >"$TMP/p3-exports.txt"
158
+ local missing=0 name
159
+ for name in "${pinned[@]}"; do
160
+ local hit
161
+ hit=$(grep -c "[[:space:]]$name\b" "$TMP/p3-exports.txt" || true)
162
+ if [ "$hit" -eq 0 ]; then
163
+ echo " P3 FAIL — $file does not export $name" >&2
164
+ missing=1
165
+ fi
166
+ done
167
+ # Any export line not naming a pinned or allowed-extra identifier is drift.
168
+ local extra=0
169
+ while IFS= read -r line; do
170
+ [ -z "$line" ] && continue
171
+ local ok=0 n hit
172
+ for n in "${pinned[@]}" "$allowed_extra"; do
173
+ hit=$(grep -cE "[[:space:]]$n\b" <<<"$line" || true)
174
+ if [ "$hit" -ge 1 ]; then
175
+ ok=1
176
+ fi
177
+ done
178
+ if [ "$ok" -eq 0 ]; then
179
+ echo " P3 FAIL — unexpected export: $line" >&2
180
+ extra=1
181
+ fi
182
+ done <"$TMP/p3-exports.txt"
183
+ if [ "$missing" -eq 0 ] && [ "$extra" -eq 0 ]; then
184
+ return 0
185
+ fi
186
+ return 1
187
+ }
188
+
189
+ p4_decision_point() {
190
+ local files=("${@:-${ROUTE_FILES[@]}}")
191
+ local bad=0
192
+ local f
193
+ for f in "${files[@]}"; do
194
+ need_file "$f" || return 2
195
+ done
196
+ # Exactly the two tenant routes call pageCacheVerdict( in the astro tree.
197
+ local call_files
198
+ call_files=$(grep -rl 'pageCacheVerdict(' --include='*.astro' one.ie/web/src/pages 2>/dev/null || true)
199
+ local n
200
+ n=$(grep -c '.' <<<"$call_files" || true)
201
+ if [ "$n" -ne "${#files[@]}" ]; then
202
+ echo " P4 FAIL — pageCacheVerdict( is called in $n .astro file(s), want ${#files[@]}: $call_files" >&2
203
+ bad=1
204
+ fi
205
+ for f in "${files[@]}"; do
206
+ local vmatches hmatches vline hline gated
207
+ vmatches=$(grep -n 'pageCacheVerdict(' "$f" || true)
208
+ hmatches=$(grep -n 'Astro.response.headers.set(PAGE_CACHE_HEADER' "$f" || true)
209
+ gated=$(grep -c 'if (cacheVerdict.cacheable) Astro.response.headers.set(PAGE_CACHE_HEADER' "$f" || true)
210
+ vline=$(first_line_number "$vmatches")
211
+ hline=$(first_line_number "$hmatches")
212
+ if [ "$vline" -eq 0 ] || [ "$hline" -eq 0 ] || [ "$gated" -lt 1 ]; then
213
+ echo " P4 FAIL — $f: verdict-call=$vline header-set=$hline gated-form=$gated (want both present and the header set inside \`if (cacheVerdict.cacheable)\`)" >&2
214
+ bad=1
215
+ continue
216
+ fi
217
+ if [ "$hline" -le "$vline" ]; then
218
+ echo " P4 FAIL — $f: PAGE_CACHE_HEADER set at line $hline is not after the verdict call at line $vline" >&2
219
+ bad=1
220
+ fi
221
+ done
222
+ [ "$bad" -eq 0 ]
223
+ }
224
+
225
+ p5_uncapped() {
226
+ local file="${1:-$INJECT_ROWS}"
227
+ need_file "$file" || return 2
228
+ local body
229
+ body=$(awk '/^export function hasLiveBindings/{f=1} f{print} f && /^}/{exit}' "$file")
230
+ if [ -z "$body" ]; then
231
+ echo " P5 FAIL — hasLiveBindings not found in $file" >&2
232
+ return 1
233
+ fi
234
+ printf '%s\n' "$body" >"$TMP/p5-body.txt"
235
+ local hits
236
+ hits=$(grep -cE '\bcap\b' "$TMP/p5-body.txt" || true)
237
+ if [ "$hits" -eq 0 ]; then
238
+ return 0
239
+ fi
240
+ echo " P5 FAIL — hasLiveBindings in $file references \`cap\` — it must stay uncapped (existence, not job-limited collection)" >&2
241
+ return 1
242
+ }
243
+
244
+ p6_visitor_value() {
245
+ local files=("${@:-${ROUTE_FILES[@]}}")
246
+ local bad=0 f
247
+ for f in "${files[@]}"; do
248
+ need_file "$f" || return 2
249
+ local raw leadv
250
+ raw=$(grep -c 'visitorHash={Astro.locals.visitorHash' "$f" || true)
251
+ leadv=$(grep -c 'visitorHash={leadVisitorHash}' "$f" || true)
252
+ # A route with no LeadCaptureScript at all (leadv==0 AND no
253
+ # LeadCaptureScript tag) is not a violation — only a route that both
254
+ # renders the script AND still passes the raw session value is.
255
+ local has_script
256
+ has_script=$(grep -c '<LeadCaptureScript' "$f" || true)
257
+ if [ "$raw" -gt 0 ]; then
258
+ echo " P6 FAIL — $f passes Astro.locals.visitorHash directly into a component; must go through the verdict-gated variable" >&2
259
+ bad=1
260
+ elif [ "$has_script" -gt 0 ] && [ "$leadv" -eq 0 ]; then
261
+ echo " P6 FAIL — $f renders LeadCaptureScript without the gated leadVisitorHash variable" >&2
262
+ bad=1
263
+ fi
264
+ done
265
+ [ "$bad" -eq 0 ]
266
+ }
267
+
268
+ run_parity() {
269
+ local bad=0
270
+ echo "speed-parity-check --parity"
271
+ if p1_altitude; then echo " ok P1 altitude — readPageCache sits strictly between the isPrimary exit and readDomainRowMemo"; else bad=1; fi
272
+ if p2_sequence; then echo " ok P2 sequence — pageCacheStore wired after perfStart, before mainMiddleware"; else bad=1; fi
273
+ if p3_exports; then echo " ok P3 exports — lib/page-cache.ts exports exactly the frozen surface"; else bad=1; fi
274
+ if p4_decision_point; then echo " ok P4 single decision point — pageCacheVerdict is the one gate on both tenant routes"; else bad=1; fi
275
+ if p5_uncapped; then echo " ok P5 uncapped — hasLiveBindings carries no job cap"; else bad=1; fi
276
+ if p6_visitor_value; then echo " ok P6 visitor value — no route hands a raw per-visitor hash to a cacheable component"; else bad=1; fi
277
+ if [ "$bad" -eq 0 ]; then
278
+ echo "[speed-parity-check --parity] ok — all six structural facts hold"
279
+ return 0
280
+ fi
281
+ echo "[speed-parity-check --parity] FAIL" >&2
282
+ return 1
283
+ }
284
+
285
+ # ---------------------------------------------------------------------------
286
+ # --freshness : F1/F2
287
+ # ---------------------------------------------------------------------------
288
+
289
+ # A write SITE is one DB.prepare(...).bind(...).run() call (or an if/else
290
+ # group of them, per spec 12's "ONE call after the if/else, never one per
291
+ # branch") — never a raw string-literal count. A ternary of two SQL literals
292
+ # for the same statement, or a 3-way if/elseif/else each with its own
293
+ # UPDATE/.run(), is ONE site: matches within 5 lines of each other cluster
294
+ # together, anchored at the cluster's LAST `.run()`. From there the rule is
295
+ # 6 SIGNIFICANT lines (non-blank, non-comment) — not 6 raw file lines, or a
296
+ # W2-authored explanatory comment between the `.run()` and the `touchPage(`
297
+ # call (exactly the shape at api/pages/[slug].ts:130-138) would itself burn
298
+ # the budget and make the pinned rule self-defeating. Comment-only lines
299
+ # (`//`, `*`, `/*`) never count as a write-site match either — pages.ts:32's
300
+ # own docblock quotes `UPDATE pages SET` / `INSERT INTO pages` in backticks
301
+ # and must not be read as a 19th writer.
302
+ f1_scan_file() {
303
+ local f="$1"
304
+ awk '
305
+ function is_comment(s, t) {
306
+ t = s
307
+ sub(/^[ \t]*/, "", t)
308
+ return (t ~ /^\/\// || t ~ /^\*/ || t ~ /^\/\*/)
309
+ }
310
+ {
311
+ lines[NR] = $0
312
+ if (!is_comment($0) && ($0 ~ /UPDATE pages SET/ || $0 ~ /INSERT INTO pages/)) {
313
+ if (NR - last_match > 10) {
314
+ nclusters++
315
+ cstart[nclusters] = NR
316
+ }
317
+ cend[nclusters] = NR
318
+ last_match = NR
319
+ }
320
+ }
321
+ END {
322
+ total_lines = NR
323
+ for (c = 1; c <= nclusters; c++) {
324
+ anchor = 0
325
+ for (i = cend[c]; i <= cend[c] + 10 && i <= total_lines; i++) {
326
+ if (lines[i] ~ /\.run\(\)/) { anchor = i; break }
327
+ }
328
+ if (anchor == 0) anchor = cend[c]
329
+ found = 0
330
+ sig = 0
331
+ for (i = anchor + 1; i <= total_lines && sig < 6; i++) {
332
+ t2 = lines[i]
333
+ trimmed = t2
334
+ sub(/^[ \t]*/, "", trimmed)
335
+ if (trimmed == "") continue
336
+ if (is_comment(t2)) continue
337
+ sig++
338
+ if (t2 ~ /touchPage\(/) { found = 1; break }
339
+ }
340
+ total_sites++
341
+ if (!found) {
342
+ print "ORPHAN\t" anchor
343
+ orphan_count++
344
+ }
345
+ }
346
+ print "TOTAL\t" total_sites + 0
347
+ print "ORPHANS\t" orphan_count + 0
348
+ }
349
+ ' "$f"
350
+ }
351
+
352
+ f1_write_sites() {
353
+ local files=("${@:-${WRITER_FILES[@]}}")
354
+ local total=0 orphans=0 f
355
+ for f in "${files[@]}"; do
356
+ need_file "$f" || return 2
357
+ done
358
+ for f in "${files[@]}"; do
359
+ local out
360
+ out=$(f1_scan_file "$f")
361
+ printf '%s\n' "$out" >"$TMP/f1-scan.txt"
362
+ local file_total file_orphans
363
+ file_total=$(awk -F'\t' '$1=="TOTAL"{print $2}' "$TMP/f1-scan.txt")
364
+ file_orphans=$(awk -F'\t' '$1=="ORPHANS"{print $2}' "$TMP/f1-scan.txt")
365
+ file_total=${file_total:-0}
366
+ file_orphans=${file_orphans:-0}
367
+ total=$((total + file_total))
368
+ orphans=$((orphans + file_orphans))
369
+ while IFS=$'\t' read -r tag lineno; do
370
+ [ "$tag" = "ORPHAN" ] || continue
371
+ echo " F1 orphan — $f: no touchPage( within 6 significant lines after the .run() at line $lineno" >&2
372
+ done <"$TMP/f1-scan.txt"
373
+ done
374
+ echo " F1 write-sites=$total touchPage-covered=$((total - orphans)) orphans=$orphans"
375
+ [ "$orphans" -eq 0 ]
376
+ }
377
+
378
+ f2_no_broadcast() {
379
+ local hits
380
+ hits=$(grep -rn --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist --exclude-dir=.astro --exclude-dir=.wrangler --include='*.ts' --include='*.astro' 'broadcastPageUpdated' one.ie 2>/dev/null || true)
381
+ printf '%s\n' "$hits" >"$TMP/f2-hits.txt"
382
+ local n
383
+ n=$(grep -c '.' "$TMP/f2-hits.txt" || true)
384
+ if [ "$n" -eq 0 ]; then
385
+ return 0
386
+ fi
387
+ echo " F2 FAIL — broadcastPageUpdated survives at $n site(s):" >&2
388
+ cat "$TMP/f2-hits.txt" >&2
389
+ return 1
390
+ }
391
+
392
+ run_freshness() {
393
+ local bad=0
394
+ echo "speed-parity-check --freshness"
395
+ if f1_write_sites; then echo " ok F1 — every UPDATE pages SET / INSERT INTO pages site is followed by touchPage( within 6 lines"; else bad=1; fi
396
+ if f2_no_broadcast; then echo " ok F2 — broadcastPageUpdated has zero occurrences repo-wide (touchPage is the one choke point)"; else bad=1; fi
397
+ if [ "$bad" -eq 0 ]; then
398
+ echo "[speed-parity-check --freshness] ok"
399
+ return 0
400
+ fi
401
+ echo "[speed-parity-check --freshness] FAIL" >&2
402
+ return 1
403
+ }
404
+
405
+ # ---------------------------------------------------------------------------
406
+ # --check-gate : G1-G6
407
+ # ---------------------------------------------------------------------------
408
+
409
+ PAGE_CACHE_ABS="$ROOT/$PAGE_CACHE_TS"
410
+
411
+ write_probe() {
412
+ cat >"$TMP/probe.mjs" <<PROBEEOF
413
+ import { pageCacheVerdict } from '${PAGE_CACHE_ABS}'
414
+ const input = JSON.parse(process.argv[2])
415
+ process.stdout.write(JSON.stringify(pageCacheVerdict(input)))
416
+ PROBEEOF
417
+ }
418
+
419
+ assert_reason() {
420
+ local name="$1" json="$2" want="$3"
421
+ local out
422
+ if ! out=$(bun "$TMP/probe.mjs" "$json" 2>&1); then
423
+ echo " FAIL $name — probe threw: $out" >&2
424
+ return 1
425
+ fi
426
+ printf '%s' "$out" >"$TMP/last-probe.json"
427
+ local got
428
+ got=$(grep -c "\"reason\":\"$want\"" "$TMP/last-probe.json" || true)
429
+ if [ "$got" -ge 1 ]; then
430
+ echo " ok $name -> reason=$want"
431
+ return 0
432
+ fi
433
+ echo " FAIL $name — want reason=$want, got: $out" >&2
434
+ return 1
435
+ }
436
+
437
+ write_key_probe() {
438
+ local module="$1" out="$2"
439
+ cat >"$out" <<KEYEOF
440
+ import { pageCacheKey } from '${module}'
441
+ console.log(pageCacheKey(process.argv[2], process.argv[3]))
442
+ KEYEOF
443
+ }
444
+
445
+ plant_key_confusion() {
446
+ # G3 — a $TMP key builder that drops the host. Never touches the tracked
447
+ # file; writes only into $TMP.
448
+ cat >"$TMP/plant-key.mjs" <<'JSEOF'
449
+ import { readFileSync, writeFileSync } from 'node:fs'
450
+ const src = readFileSync(process.argv[2], 'utf8')
451
+ const needle = 'return `${PAGE_CACHE_ORIGIN}/${encodeURIComponent(host)}${pathname}`;'
452
+ const replacement = 'return `${PAGE_CACHE_ORIGIN}${pathname}`;'
453
+ if (!src.includes(needle)) {
454
+ console.error('PLANT_FAILED_NO_MATCH')
455
+ process.exit(1)
456
+ }
457
+ writeFileSync(process.argv[3], src.replace(needle, replacement))
458
+ JSEOF
459
+ bun "$TMP/plant-key.mjs" "$PAGE_CACHE_ABS" "$TMP/page-cache-red.ts"
460
+ }
461
+
462
+ plant_capped_bindings() {
463
+ # G4b — a $TMP copy of inject-rows.ts whose hasLiveBindings caps at 5.
464
+ cat >"$TMP/plant-cap.mjs" <<'JSEOF'
465
+ import { readFileSync, writeFileSync } from 'node:fs'
466
+ const src = readFileSync(process.argv[2], 'utf8')
467
+ const needle = 'export function hasLiveBindings(data: Data): boolean {'
468
+ if (!src.includes(needle)) {
469
+ console.error('PLANT_FAILED_NO_MATCH')
470
+ process.exit(1)
471
+ }
472
+ const replacement = needle + '\n\tconst cap = 5 // PLANTED — proves P5 catches a job cap creeping in\n'
473
+ writeFileSync(process.argv[3], src.replace(needle, replacement))
474
+ JSEOF
475
+ bun "$TMP/plant-cap.mjs" "$ROOT/$INJECT_ROWS" "$TMP/inject-rows-red.ts"
476
+ }
477
+
478
+ plant_altitude() {
479
+ # G6 — a $TMP copy of middleware.ts with the read probe moved BELOW
480
+ # readDomainRowMemo. Text-only mutation; the plant never needs to compile,
481
+ # it only needs to reproduce the byte order p1_altitude scans for.
482
+ cat >"$TMP/plant-altitude.mjs" <<'JSEOF'
483
+ import { readFileSync, writeFileSync } from 'node:fs'
484
+ const src = readFileSync(process.argv[2], 'utf8')
485
+ const lines = src.split('\n')
486
+ const startIdx = lines.findIndex((l) => l.includes('C10 PARITY PROBE'))
487
+ if (startIdx === -1) {
488
+ console.error('PLANT_FAILED_NO_START')
489
+ process.exit(1)
490
+ }
491
+ const anchorIdx = lines.findIndex((l) => l.includes('An explicit verified'))
492
+ if (anchorIdx === -1) {
493
+ console.error('PLANT_FAILED_NO_ANCHOR')
494
+ process.exit(1)
495
+ }
496
+ let cut = anchorIdx - 1
497
+ while (cut > startIdx && lines[cut].trim() === '') cut--
498
+ const endIdx = cut
499
+ const block = lines.slice(startIdx, endIdx + 1)
500
+ const rest = lines.slice(0, startIdx).concat(lines.slice(endIdx + 1))
501
+ const domainIdx = rest.findIndex((l) => l.includes('readDomainRowMemo('))
502
+ if (domainIdx === -1) {
503
+ console.error('PLANT_FAILED_NO_DOMAIN')
504
+ process.exit(1)
505
+ }
506
+ const out = rest.slice(0, domainIdx + 1).concat(['']).concat(block).concat(rest.slice(domainIdx + 1))
507
+ writeFileSync(process.argv[3], out.join('\n'))
508
+ JSEOF
509
+ bun "$TMP/plant-altitude.mjs" "$ROOT/$MIDDLEWARE_TS" "$TMP/middleware-red.ts"
510
+ }
511
+
512
+ run_check_gate() {
513
+ need_bun || return 2
514
+ need_file "$PAGE_CACHE_TS" || return 2
515
+ need_file "$INJECT_ROWS" || return 2
516
+ need_file "$MIDDLEWARE_TS" || return 2
517
+
518
+ echo "speed-parity-check --check-gate — proving each of the six safety-rule plants goes RED"
519
+ echo
520
+ local bad=0
521
+
522
+ write_probe
523
+
524
+ # G1 draft
525
+ if assert_reason "G1 draft" '{"status":"draft","hasSession":false,"hasLiveBindings":false,"hasExperiment":false,"canEdit":false,"visitorHash":""}' "draft"; then :; else bad=1; fi
526
+
527
+ # G2 session
528
+ if assert_reason "G2 session" '{"status":"published","hasSession":true,"hasLiveBindings":false,"hasExperiment":false,"canEdit":false,"visitorHash":""}' "session"; then :; else bad=1; fi
529
+
530
+ # G4a live bindings
531
+ if assert_reason "G4a bindings" '{"status":"published","hasSession":false,"hasLiveBindings":true,"hasExperiment":false,"canEdit":false,"visitorHash":""}' "bindings"; then :; else bad=1; fi
532
+
533
+ # G5 visitor value
534
+ if assert_reason "G5 visitor-value" '{"status":"published","hasSession":false,"hasLiveBindings":false,"hasExperiment":false,"canEdit":false,"visitorHash":"abc123"}' "visitor-value"; then :; else bad=1; fi
535
+
536
+ # G — experiment (sixth named reason; IC row 3 lists all six in order)
537
+ if assert_reason "G experiment" '{"status":"published","hasSession":false,"hasLiveBindings":false,"hasExperiment":true,"canEdit":false,"visitorHash":""}' "experiment"; then :; else bad=1; fi
538
+
539
+ # G — can-edit
540
+ if assert_reason "G can-edit" '{"status":"published","hasSession":false,"hasLiveBindings":false,"hasExperiment":false,"canEdit":true,"visitorHash":""}' "can-edit"; then :; else bad=1; fi
541
+
542
+ # Sanity: the all-clear input is cacheable — proves the five guards above
543
+ # are not vacuously true (e.g. always returning cacheable:false).
544
+ if assert_reason "sanity all-clear" '{"status":"published","hasSession":false,"hasLiveBindings":false,"hasExperiment":false,"canEdit":false,"visitorHash":""}' "ok"; then :; else bad=1; fi
545
+
546
+ # G3 — host confusion. Real module: two hosts, same pathname, different keys.
547
+ write_key_probe "$PAGE_CACHE_ABS" "$TMP/key-real.mjs"
548
+ local ka kb
549
+ ka=$(bun "$TMP/key-real.mjs" 'a.com' '/p/x' 2>&1) || { echo " FAIL G3 real probe threw: $ka" >&2; bad=1; }
550
+ kb=$(bun "$TMP/key-real.mjs" 'b.com' '/p/x' 2>&1) || { echo " FAIL G3 real probe threw: $kb" >&2; bad=1; }
551
+ if [ -n "$ka" ] && [ -n "$kb" ] && [ "$ka" != "$kb" ]; then
552
+ echo " ok G3a real pageCacheKey — a.com and b.com produce different keys"
553
+ else
554
+ echo " FAIL G3a real pageCacheKey did not distinguish hosts: '$ka' vs '$kb'" >&2
555
+ bad=1
556
+ fi
557
+ # ...then plant a key builder that drops the host and prove the SAME
558
+ # assertion goes red against the plant.
559
+ if plant_key_confusion; then
560
+ write_key_probe "$TMP/page-cache-red.ts" "$TMP/key-red.mjs"
561
+ local rka rkb
562
+ rka=$(bun "$TMP/key-red.mjs" 'a.com' '/p/x' 2>&1) || rka=""
563
+ rkb=$(bun "$TMP/key-red.mjs" 'b.com' '/p/x' 2>&1) || rkb=""
564
+ if [ -n "$rka" ] && [ "$rka" = "$rkb" ]; then
565
+ echo " ok G3b planted host-dropping key builder is flagged RED (a.com and b.com collide)"
566
+ else
567
+ echo " FAIL G3b planted key builder was NOT flagged red — got '$rka' vs '$rkb' (want them equal, proving the bug is caught)" >&2
568
+ bad=1
569
+ fi
570
+ else
571
+ echo " FAIL G3b could not plant the key-confusion fixture" >&2
572
+ bad=1
573
+ fi
574
+
575
+ # G4b — capped hasLiveBindings must flag P5 red.
576
+ if p5_uncapped "$INJECT_ROWS" >/dev/null 2>&1; then
577
+ echo " ok G4b(real) working tree's hasLiveBindings is GREEN (uncapped)"
578
+ else
579
+ echo " FAIL G4b(real) working tree's hasLiveBindings did NOT pass P5" >&2
580
+ bad=1
581
+ fi
582
+ if plant_capped_bindings; then
583
+ if p5_uncapped "$TMP/inject-rows-red.ts" >/dev/null 2>&1; then
584
+ echo " FAIL G4b planted capped hasLiveBindings was NOT flagged (got pass, want fail)" >&2
585
+ bad=1
586
+ else
587
+ echo " ok G4b planted capped hasLiveBindings is flagged RED by P5"
588
+ fi
589
+ else
590
+ echo " FAIL G4b could not plant the capped-bindings fixture" >&2
591
+ bad=1
592
+ fi
593
+
594
+ # G6 — altitude. Working tree must be GREEN; the plant (probe moved below
595
+ # readDomainRowMemo) must be RED.
596
+ if p1_altitude "$MIDDLEWARE_TS" >/dev/null 2>&1; then
597
+ echo " ok G6(real) working tree's middleware.ts passes P1 altitude"
598
+ else
599
+ echo " FAIL G6(real) working tree's middleware.ts did NOT pass P1 altitude" >&2
600
+ bad=1
601
+ fi
602
+ if plant_altitude; then
603
+ if p1_altitude "$TMP/middleware-red.ts" >/dev/null 2>&1; then
604
+ echo " FAIL G6 planted altitude violation was NOT flagged (got pass, want fail)" >&2
605
+ bad=1
606
+ else
607
+ echo " ok G6 planted altitude violation (probe moved below readDomainRowMemo) is flagged RED by P1 — this is the plant that guards the deliverable itself"
608
+ fi
609
+ else
610
+ echo " FAIL G6 could not plant the altitude fixture" >&2
611
+ bad=1
612
+ fi
613
+
614
+ echo
615
+ if [ "$bad" -eq 0 ]; then
616
+ echo "check-gate proven: all six planted violations are flagged RED, and the working tree stays GREEN where it should"
617
+ return 0
618
+ fi
619
+ echo "$bad self-test group(s) FAILED — the parity gate is not trustworthy" >&2
620
+ return 1
621
+ }
622
+
623
+ # ---------------------------------------------------------------------------
624
+ # dispatch
625
+ # ---------------------------------------------------------------------------
626
+
627
+ MODE=""
628
+ for arg in "$@"; do
629
+ case "$arg" in
630
+ --parity) MODE="parity" ;;
631
+ --freshness) MODE="freshness" ;;
632
+ --check-gate) MODE="check-gate" ;;
633
+ *)
634
+ echo "[speed-parity-check] unknown flag: $arg" >&2
635
+ exit 2
636
+ ;;
637
+ esac
638
+ done
639
+
640
+ case "$MODE" in
641
+ parity) run_parity; exit $? ;;
642
+ freshness) run_freshness; exit $? ;;
643
+ check-gate) run_check_gate; exit $? ;;
644
+ *)
645
+ echo "usage: speed-parity-check.sh --parity|--freshness|--check-gate" >&2
646
+ exit 2
647
+ ;;
648
+ esac