@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,64 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# sdk-build-cached.sh — build @oneie/sdk, or skip when its sources have not moved.
|
|
3
|
+
#
|
|
4
|
+
# manifest: needs-env
|
|
5
|
+
#
|
|
6
|
+
# WHY. `cd packages/sdk && bun run build` ran on EVERY verify-fast pass, and it is
|
|
7
|
+
# 2.0s of the 2.6s total deterministic gate cost of a /do cycle -- 78%. Measured
|
|
8
|
+
# 2026-09-01: hashing dist before and after a rebuild on an unchanged tree gives
|
|
9
|
+
# the SAME digest. The build is deterministic, so on an unchanged src it is two
|
|
10
|
+
# seconds spent reproducing bytes that were already there.
|
|
11
|
+
#
|
|
12
|
+
# dist is a pure function of src (the same reasoning tsc-cached.sh uses to key on
|
|
13
|
+
# src rather than on dist mtimes, which every rebuild churns). So: fingerprint
|
|
14
|
+
# src, skip when it matches the fingerprint the current dist was built from.
|
|
15
|
+
#
|
|
16
|
+
# SAFETY. The stamp lives INSIDE dist. If dist is deleted, cleaned, or was never
|
|
17
|
+
# built, the stamp goes with it and the build runs -- there is no path where a
|
|
18
|
+
# missing dist reads as cached. A stale-but-present dist cannot survive an src
|
|
19
|
+
# edit either, because the fingerprint covers every tracked file under src.
|
|
20
|
+
#
|
|
21
|
+
# SDK_BUILD_FORCE=1 rebuilds unconditionally. --self-test runs the fixtures.
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
24
|
+
SDK="$ROOT/packages/sdk"
|
|
25
|
+
|
|
26
|
+
_fp() {
|
|
27
|
+
{ git -C "$ROOT" ls-files -s packages/sdk/src packages/sdk/package.json 2>/dev/null
|
|
28
|
+
# uncommitted edits must move the key too -- ls-files -s shows the INDEX,
|
|
29
|
+
# which does not change when a tracked file is edited in the working tree.
|
|
30
|
+
git -C "$ROOT" diff -- packages/sdk/src packages/sdk/package.json 2>/dev/null
|
|
31
|
+
git -C "$ROOT" ls-files --others --exclude-standard packages/sdk/src 2>/dev/null \
|
|
32
|
+
| while IFS= read -r u; do shasum -a 256 "$ROOT/$u" 2>/dev/null; done
|
|
33
|
+
} | shasum -a 256 | cut -d' ' -f1
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if [ "${1:-}" = "--self-test" ]; then
|
|
37
|
+
fails=0
|
|
38
|
+
b1=$(bash "$0"); b2=$(bash "$0")
|
|
39
|
+
case "$b2" in *"cached"*) echo "ok: second build on an unchanged tree is CACHED";;
|
|
40
|
+
*) echo "FAIL: unchanged tree rebuilt: $b2"; fails=$((fails+1));; esac
|
|
41
|
+
# RED PROOF 1 -- an src edit must force a rebuild
|
|
42
|
+
probe="$SDK/src/.__cache_probe.ts"; echo 'export const __p = 1' > "$probe"
|
|
43
|
+
b3=$(bash "$0")
|
|
44
|
+
case "$b3" in *"cached"*) echo "FAIL: an src edit did NOT invalidate"; fails=$((fails+1));;
|
|
45
|
+
*) echo "ok: an src edit forces a rebuild";; esac
|
|
46
|
+
rm -f "$probe"
|
|
47
|
+
# RED PROOF 2 -- a missing dist must force a rebuild, never read as cached
|
|
48
|
+
rm -f "$SDK/dist/.build-fingerprint"
|
|
49
|
+
b4=$(bash "$0")
|
|
50
|
+
case "$b4" in *"cached"*) echo "FAIL: missing stamp read as cached"; fails=$((fails+1));;
|
|
51
|
+
*) echo "ok: a missing stamp forces a rebuild";; esac
|
|
52
|
+
[ "$fails" -eq 0 ] && { echo "sdk-build-cached: self-test PASS"; exit 0; }
|
|
53
|
+
echo "sdk-build-cached: self-test FAILED ($fails)"; exit 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
STAMP="$SDK/dist/.build-fingerprint"
|
|
57
|
+
want="$(_fp)"
|
|
58
|
+
if [ "${SDK_BUILD_FORCE:-0}" != "1" ] && [ -f "$STAMP" ] && [ "$(cat "$STAMP")" = "$want" ]; then
|
|
59
|
+
echo "[sdk] cached — src unchanged (${want:0:12})"
|
|
60
|
+
exit 0
|
|
61
|
+
fi
|
|
62
|
+
( cd "$SDK" && bun run build >/dev/null ) || { echo "[sdk] BUILD FAILED" >&2; exit 1; }
|
|
63
|
+
printf '%s' "$want" > "$STAMP"
|
|
64
|
+
echo "[sdk] built (${want:0:12})"
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# signal-watch.sh — watch signals travel through the factory's gates, live,
|
|
3
|
+
# with a budget per door and a ledger that tracks speed over time.
|
|
4
|
+
#
|
|
5
|
+
# manifest: needs-env (reads GATEWAY_API_KEY from the shell or one.ie/web/.env;
|
|
6
|
+
# the local lane reads one.ie/web/.dev.vars)
|
|
7
|
+
#
|
|
8
|
+
# USAGE
|
|
9
|
+
# bash .claude/scripts/signal-watch.sh both lanes, once
|
|
10
|
+
# bash .claude/scripts/signal-watch.sh --prod|--local one lane
|
|
11
|
+
# bash .claude/scripts/signal-watch.sh --watch 30 redraw every 30s
|
|
12
|
+
# bash .claude/scripts/signal-watch.sh --json one JSON line per door
|
|
13
|
+
# bash .claude/scripts/signal-watch.sh --self-test the red proofs
|
|
14
|
+
#
|
|
15
|
+
# THE DOORS are the five HTTP hops the factory's own path makes (text/factory-do.md
|
|
16
|
+
# § THE ONE PATH: classify · size · board · claim · close) plus the world door the
|
|
17
|
+
# hooks and do-signal.sh post to, plus the stream the listeners sit on:
|
|
18
|
+
#
|
|
19
|
+
# health GET /api/health the worker answers
|
|
20
|
+
# world:announce POST a canary tagged [signal-watch-wire] on the
|
|
21
|
+
# MARKETPLACE board — intake's door.
|
|
22
|
+
# The wire, and only the wire: zero subscribers matched, so
|
|
23
|
+
# the number is the door's own cost with no fan-out in it.
|
|
24
|
+
# It used to be sent on the caller's own board and the header
|
|
25
|
+
# claimed it "matches nobody by design" — it matched `ceo`,
|
|
26
|
+
# which subscribes to EVERY tag by canon (root CLAUDE.md: the
|
|
27
|
+
# CEO is the default receiver, always listening). Measured
|
|
28
|
+
# 2026-09-08 on prod server-timing, same door, one variable:
|
|
29
|
+
# matched=0 -> data 259/332/401 ms; matched=1 -> data
|
|
30
|
+
# 1535..2066 ms. So ~1.3s per matched subscriber was being
|
|
31
|
+
# read as "the wire". The probe now ASSERTS matched=0 and
|
|
32
|
+
# goes UNRUN if it ever matches — a wire probe that fanned
|
|
33
|
+
# out did not measure the wire, and an unrun door is not a
|
|
34
|
+
# pass. The fan-out's own cost is a separate finding
|
|
35
|
+
# (text/announce-fanout-plan.md), not a budget to widen.
|
|
36
|
+
# BOTH halves are load-bearing and both were measured
|
|
37
|
+
# wrong before: the BOARD (own board -> `ceo`, who
|
|
38
|
+
# follows every tag) and the TAG (`cc` on the marketplace
|
|
39
|
+
# board -> the live `cc-proof-listener`, found 2026-09-08
|
|
40
|
+
# by this very assertion on its first run). Hence a
|
|
41
|
+
# RESERVED tag nobody stakes, on the shared board. If
|
|
42
|
+
# someone ever stakes `signal-watch-wire`, the row goes
|
|
43
|
+
# unrun and says so, rather than quietly reporting a
|
|
44
|
+
# fan-out as the wire.
|
|
45
|
+
# tasks:everywhere POST {workspace:factory-do} the board read
|
|
46
|
+
# factory:size POST {paths:[this file]} the size hop
|
|
47
|
+
# stream GET channels.one.ie/stream/space:vespio time to first byte
|
|
48
|
+
#
|
|
49
|
+
# EVERY ROW CARRIES the server-timing phases the worker itself reports
|
|
50
|
+
# (auth · data · det) and cf-placement, so "slow" is never a single number:
|
|
51
|
+
# you can read WHICH leg paid. Measured 2026-09-05: D1 `one-owners` runs in
|
|
52
|
+
# APAC, TypeDB Cloud in Virginia, and Smart Placement runs one-prod at ORD about
|
|
53
|
+
# one call in three — so every substrate call pays one Pacific leg somewhere.
|
|
54
|
+
#
|
|
55
|
+
# VERDICTS, four states, the same vocabulary as do-w4-gates.sh:
|
|
56
|
+
# pass 200, the receiver answered `outcome:result`, wall <= budget
|
|
57
|
+
# fail dropped (no response inside 15s) · non-200 · a refusal · over budget
|
|
58
|
+
# n/a the door is not on this box (no local server, no listener config)
|
|
59
|
+
# unrun the door could not be TRIED (no key) — never read as a pass
|
|
60
|
+
# Exit 0 = every door pass or n/a · 1 = any fail · 3 = any unrun and no fail.
|
|
61
|
+
#
|
|
62
|
+
# LEDGER: one JSON line per door per run appended to .claude/.signal-watch.jsonl
|
|
63
|
+
# (gitignored). `--no-ledger` skips it. Speed is tracked, never remembered.
|
|
64
|
+
set -uo pipefail
|
|
65
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
66
|
+
PROD_URL="${SIGNAL_WATCH_PROD:-https://one.ie}"
|
|
67
|
+
LOCAL_URL="${SIGNAL_WATCH_LOCAL:-http://localhost:4321}"
|
|
68
|
+
STREAM_URL="${SIGNAL_WATCH_STREAM:-https://channels.one.ie/stream/space:vespio}"
|
|
69
|
+
LEDGER="${SIGNAL_WATCH_LEDGER:-$ROOT/.claude/.signal-watch.jsonl}"
|
|
70
|
+
CURL_MAX="${SIGNAL_WATCH_MAX:-15}"
|
|
71
|
+
BUDGET_SCALE="${SIGNAL_WATCH_BUDGET_SCALE:-1}" # self-test shrinks budgets with this
|
|
72
|
+
|
|
73
|
+
LANES="prod local"; WATCH=0; JSON=0; LEDGER_ON=1
|
|
74
|
+
while [ $# -gt 0 ]; do
|
|
75
|
+
case "$1" in
|
|
76
|
+
--prod) LANES="prod" ;; --local) LANES="local" ;;
|
|
77
|
+
--watch) WATCH="${2:-30}"; shift ;;
|
|
78
|
+
--json) JSON=1 ;; --no-ledger) LEDGER_ON=0 ;;
|
|
79
|
+
--self-test) SELF_TEST=1 ;;
|
|
80
|
+
-h|--help) sed -n 2,45p "$0"; exit 0 ;;
|
|
81
|
+
*) echo "signal-watch: unknown arg $1" >&2; exit 2 ;;
|
|
82
|
+
esac; shift
|
|
83
|
+
done
|
|
84
|
+
|
|
85
|
+
PASS=0; FAIL=0; NA=0; UNRUN=0
|
|
86
|
+
_key() { # $1 = env file; the shell wins; the value is never printed
|
|
87
|
+
local f="$1" v="${GATEWAY_API_KEY:-}"
|
|
88
|
+
[ -n "$v" ] && { printf '%s' "$v"; return; }
|
|
89
|
+
[ -f "$f" ] && grep -E '^GATEWAY_API_KEY=' "$f" | head -1 | cut -d= -f2- | tr -d '"'
|
|
90
|
+
}
|
|
91
|
+
_budget() { awk -v b="$1" -v s="$BUDGET_SCALE" 'BEGIN{printf "%d", b*s}'; }
|
|
92
|
+
_row() { # lane door verdict wall budget placement phases note
|
|
93
|
+
if [ "$JSON" = 1 ]; then
|
|
94
|
+
printf '{"ts":"%s","lane":"%s","door":"%s","verdict":"%s","wall_ms":%s,"budget_ms":%s,"placement":"%s","phases":"%s","note":"%s"}\n' \
|
|
95
|
+
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$1" "$2" "$3" "${4:-null}" "$5" "$6" "$7" "$8"
|
|
96
|
+
else
|
|
97
|
+
printf ' %-6s %-18s %-5s %7s ms budget %5s %-11s %-34s %s\n' "$1" "$2" "$3" "${4:--}" "$5" "${6:--}" "${7:--}" "$8"
|
|
98
|
+
fi
|
|
99
|
+
if [ "$LEDGER_ON" = 1 ]; then
|
|
100
|
+
printf '{"ts":"%s","lane":"%s","door":"%s","verdict":"%s","wall_ms":%s,"budget_ms":%s,"placement":"%s","phases":"%s"}\n' \
|
|
101
|
+
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$1" "$2" "$3" "${4:-null}" "$5" "$6" "$7" >> "$LEDGER" 2>/dev/null || true
|
|
102
|
+
fi
|
|
103
|
+
case "$3" in pass) PASS=$((PASS+1));; fail) FAIL=$((FAIL+1));; n/a) NA=$((NA+1));; unrun) UNRUN=$((UNRUN+1));; esac
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# door <lane> <name> <method> <url> <payload|-> <budget_ms> <key|-> [expect|-]
|
|
107
|
+
# `expect` is a fixed string the RESPONSE BODY must contain for the number to
|
|
108
|
+
# mean what the door claims to measure. Absent it, a door can keep answering 200
|
|
109
|
+
# fast while measuring something else entirely — which is exactly how the
|
|
110
|
+
# announce canary reported "the wire" while paying a full fan-out. A miss is
|
|
111
|
+
# UNRUN, never fail and never pass: the door ran, but not the door we named.
|
|
112
|
+
door() {
|
|
113
|
+
local lane="$1" name="$2" method="$3" url="$4" payload="$5" budget key="$7" expect="${8:--}" hdr body out code wall placement phases verdict note=""
|
|
114
|
+
budget=$(_budget "$6")
|
|
115
|
+
hdr=$(mktemp); body=$(mktemp)
|
|
116
|
+
if [ "$method" = POST ] && [ "$key" = "-" ]; then
|
|
117
|
+
_row "$lane" "$name" unrun "" "$budget" "" "" "no GATEWAY_API_KEY — not tried"; rm -f "$hdr" "$body"; return
|
|
118
|
+
fi
|
|
119
|
+
# bash 3.2 + set -u: an EMPTY array expands as unbound. The ${a[@]+"${a[@]}"} idiom is the only portable form.
|
|
120
|
+
local cfg=""; [ "$key" != "-" ] && { cfg=$(mktemp); chmod 600 "$cfg"; printf 'header = "Authorization: Bearer %s"\n' "$key" > "$cfg"; }
|
|
121
|
+
local -a auth=(); [ -n "$cfg" ] && auth=(--config "$cfg")
|
|
122
|
+
if [ "$method" = POST ]; then
|
|
123
|
+
out=$(curl -s -D "$hdr" -o "$body" --max-time "$CURL_MAX" -X POST "$url" -H 'content-type: application/json' ${auth[@]+"${auth[@]}"} -d "$payload" -w '%{http_code} %{time_total}' 2>/dev/null)
|
|
124
|
+
else
|
|
125
|
+
out=$(curl -s -D "$hdr" -o "$body" --max-time "$CURL_MAX" "$url" ${auth[@]+"${auth[@]}"} -w '%{http_code} %{time_total}' 2>/dev/null)
|
|
126
|
+
fi
|
|
127
|
+
[ -n "$cfg" ] && rm -f "$cfg"
|
|
128
|
+
code="${out%% *}"; wall=$(awk -v t="${out##* }" 'BEGIN{printf "%d", t*1000}')
|
|
129
|
+
placement=$(grep -i '^cf-placement:' "$hdr" | head -1 | cut -d' ' -f2- | tr -d '\r')
|
|
130
|
+
phases=$(grep -i '^server-timing:' "$hdr" | head -1 | grep -oE '(auth|data|det);dur=[0-9]+' | sed -E 's/;dur=/=/' | paste -sd' ' -)
|
|
131
|
+
if [ "$code" = "000" ] || [ -z "$code" ]; then verdict=fail; note="DROPPED — no response inside ${CURL_MAX}s"
|
|
132
|
+
elif [ "$code" != "200" ]; then verdict=fail; note="http $code $(head -c 80 "$body" | tr -d '\n')"
|
|
133
|
+
elif [ "$method" = POST ] && ! grep -q '"outcome":"result"' "$body"; then verdict=fail; note="refused: $(head -c 100 "$body" | tr -d '\n')"
|
|
134
|
+
elif [ "$method" = POST ] && grep -q '"ok":false' "$body"; then verdict=unrun; note="refused before the work: $(grep -oE '"error":"[^"]{0,80}' "$body" | head -1 | cut -d'"' -f4)"
|
|
135
|
+
elif [ "$expect" != "-" ] && ! grep -qF "$expect" "$body"; then verdict=unrun; note="not the door we named: body lacks $expect — $(head -c 60 "$body" | tr -d '\n')"
|
|
136
|
+
elif [ "$wall" -gt "$budget" ]; then verdict=fail; note="over budget by $((wall-budget)) ms"
|
|
137
|
+
else verdict=pass; fi
|
|
138
|
+
_row "$lane" "$name" "$verdict" "$wall" "$budget" "${placement:-}" "${phases:-}" "$note"
|
|
139
|
+
rm -f "$hdr" "$body"
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
stream_door() {
|
|
143
|
+
local budget cfg="$HOME/.cc-connect/.auth.conf" out code ttfb wall verdict note=""
|
|
144
|
+
budget=$(_budget 600)
|
|
145
|
+
[ -f "$cfg" ] || { _row prod stream n/a "" "$budget" "" "" "no ~/.cc-connect/.auth.conf on this box"; return; }
|
|
146
|
+
out=$(curl -s -N -o /dev/null --max-time 4 --config "$cfg" "$STREAM_URL?since=$(date +%s)000" -w '%{http_code} %{time_starttransfer}' 2>/dev/null)
|
|
147
|
+
code="${out%% *}"; ttfb=$(awk -v t="${out##* }" 'BEGIN{printf "%d", t*1000}')
|
|
148
|
+
if [ "$code" != "200" ]; then verdict=fail; note="http ${code:-000}"
|
|
149
|
+
elif [ "$ttfb" -gt "$budget" ]; then verdict=fail; note="first byte over budget"
|
|
150
|
+
else verdict=pass; note="first byte"; fi
|
|
151
|
+
_row prod stream "$verdict" "$ttfb" "$budget" "" "" "$note"
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
listeners() {
|
|
155
|
+
command -v launchctl >/dev/null 2>&1 || { _row prod listeners n/a "" 0 "" "" "no launchctl"; return; }
|
|
156
|
+
local alive; alive=$(launchctl list 2>/dev/null | awk '/ie\.one\.(cc-vespio|cc-oo|tg-listen)$/ && $1 ~ /^[0-9]+$/ {n++} END{print n+0}')
|
|
157
|
+
if [ "$alive" -gt 0 ]; then _row prod listeners pass "" 0 "" "" "$alive listener(s) alive (cc-vespio · cc-oo · tg-listen)"
|
|
158
|
+
else _row prod listeners fail "" 0 "" "" "no ie.one.* listener has a pid"; fi
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
lane() { # prod|local
|
|
162
|
+
local l="$1" base key
|
|
163
|
+
# The prod credential file honours ONE_ENV_FILE / DO_ENV_FILE, like every other
|
|
164
|
+
# credential read in the harness (factory-repo.sh --check-env-indirection).
|
|
165
|
+
# A relative path is taken from $ROOT; the shell's GATEWAY_API_KEY still wins.
|
|
166
|
+
local env_file="${ONE_ENV_FILE:-${DO_ENV_FILE:-one.ie/web/.env}}"
|
|
167
|
+
case "$env_file" in /*) ;; *) env_file="$ROOT/$env_file" ;; esac
|
|
168
|
+
if [ "$l" = prod ]; then base="$PROD_URL"; key=$(_key "$env_file")
|
|
169
|
+
else
|
|
170
|
+
base="$LOCAL_URL"; key=$(_key "$ROOT/one.ie/web/.dev.vars")
|
|
171
|
+
if ! curl -s -o /dev/null --max-time 3 "$base/api/health"; then
|
|
172
|
+
_row local health n/a "" 0 "" "" "no dev server at $base"; return
|
|
173
|
+
fi
|
|
174
|
+
fi
|
|
175
|
+
[ -z "$key" ] && key="-"
|
|
176
|
+
# Budgets are a RATCHET: set at the measured p90 the day the door was first
|
|
177
|
+
# watched (2026-09-05 — announce 1.3-1.8s, board/size 0.8-1.5s), lowered only
|
|
178
|
+
# after a measured improvement ships. Never raised to make a red row green.
|
|
179
|
+
# announce: 2000 → 800 after the parallel-read ship (2026-09-05, measured 149-379 ms).
|
|
180
|
+
# UNCHANGED 2026-09-08 on purpose. The door it describes is now the WIRE door
|
|
181
|
+
# (board=marketplace, matched=0), whose data phase measures 259-401 ms — the
|
|
182
|
+
# same range 800 was set against. The wall it is compared to also carries the
|
|
183
|
+
# CLIENT's egress, which is not the worker's: `health` does det=0 ms of server
|
|
184
|
+
# work and has walked 134 → 72 → 437 → 463 ms across the ledger purely on where
|
|
185
|
+
# the laptop was sitting. So a marginal announce/health miss can be this box's
|
|
186
|
+
# network, not the door. Whether these budgets should be worker-phase budgets
|
|
187
|
+
# rather than wall budgets is a RECALIBRATION — a human decision, deliberately
|
|
188
|
+
# not taken here. A budget moved to fit an observation stops meaning anything.
|
|
189
|
+
local b1 b2 b3; if [ "$l" = prod ]; then b1=400; b2=1500; b3=800; else b1=150; b2=300; b3=300; fi
|
|
190
|
+
door "$l" health GET "$base/api/health" - "$b1" -
|
|
191
|
+
# SIGNAL_WATCH_WIRE_OWN_BOARD=1 exists for --self-test case 4 ONLY: DROPPING
|
|
192
|
+
# the board key sends the canary to the caller's own board, where `ceo` matches
|
|
193
|
+
# every tag by canon — the durable red half of the assertion below. (Setting
|
|
194
|
+
# `board` to an arbitrary slug does NOT work: the receiver's Zod schema rejects
|
|
195
|
+
# it with a 400, which is a different failure and proves nothing.) Never set
|
|
196
|
+
# this in a real run.
|
|
197
|
+
local wire_board='"board":"marketplace",'
|
|
198
|
+
if [ "${SIGNAL_WATCH_WIRE_OWN_BOARD:-0}" = 1 ]; then wire_board=''; fi
|
|
199
|
+
door "$l" world:announce POST "$base/api/ask/world:announce" \
|
|
200
|
+
"{\"data\":{\"tags\":[\"signal-watch-wire\"],${wire_board}\"type\":\"signal:canary\",\"text\":\"signal-watch $(date -u +%H:%M:%SZ)\",\"priority\":\"fyi\",\"slug\":\"one\"}}" "$b3" "$key" '"matched":0'
|
|
201
|
+
door "$l" tasks:everywhere POST "$base/api/ask/tasks:everywhere" '{"data":{"workspace":"factory-do","limit":200}}' "$b2" "$key"
|
|
202
|
+
door "$l" factory:size POST "$base/api/ask/factory:size" '{"data":{"paths":[".claude/scripts/signal-watch.sh"]}}' "$b2" "$key"
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
run_once() {
|
|
206
|
+
PASS=0; FAIL=0; NA=0; UNRUN=0
|
|
207
|
+
[ "$JSON" = 1 ] || { echo "signal-watch · $(date '+%Y-%m-%d %H:%M:%S') · prod=$PROD_URL local=$LOCAL_URL"; echo; }
|
|
208
|
+
for l in $LANES; do lane "$l"; done
|
|
209
|
+
case " $LANES " in *" prod "*) stream_door; listeners ;; esac
|
|
210
|
+
[ "$JSON" = 1 ] || { echo; echo "SIGNAL-WATCH: pass=$PASS fail=$FAIL n/a=$NA unrun=$UNRUN$([ "$LEDGER_ON" = 1 ] && printf ' · ledger %s' "$LEDGER")"; }
|
|
211
|
+
[ "$FAIL" -gt 0 ] && return 1; [ "$UNRUN" -gt 0 ] && return 3; return 0
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if [ "${SELF_TEST:-0}" = 1 ]; then
|
|
215
|
+
fails=0
|
|
216
|
+
echo "== 1. a dropped door is a fail, and the exit says so"
|
|
217
|
+
out=$(SIGNAL_WATCH_PROD=http://127.0.0.1:1 SIGNAL_WATCH_MAX=2 bash "$0" --prod --no-ledger 2>&1); rc=$?
|
|
218
|
+
[ "$rc" -eq 1 ] && grep -q "DROPPED" <<<"$out" && echo " ok exit 1, DROPPED named" || { echo " FAIL rc=$rc"; echo "$out" | tail -4; fails=$((fails+1)); }
|
|
219
|
+
echo "== 2. a door over budget is a fail, never a slow pass"
|
|
220
|
+
out=$(SIGNAL_WATCH_BUDGET_SCALE=0.0001 bash "$0" --prod --no-ledger 2>&1); rc=$?
|
|
221
|
+
[ "$rc" -eq 1 ] && grep -q "over budget" <<<"$out" && echo " ok exit 1, over budget named" || { echo " FAIL rc=$rc"; echo "$out" | tail -4; fails=$((fails+1)); }
|
|
222
|
+
echo "== 3. no key is UNRUN (exit 3), not a pass — the door was never tried"
|
|
223
|
+
# the .env key resolves on a configured box, so prove the branch from a stub root with no .env
|
|
224
|
+
tmp=$(mktemp -d); mkdir -p "$tmp/.claude/scripts" "$tmp/one.ie/web"; cp "$0" "$tmp/.claude/scripts/"
|
|
225
|
+
out=$(GATEWAY_API_KEY= SIGNAL_WATCH_BUDGET_SCALE=100 bash "$tmp/.claude/scripts/signal-watch.sh" --prod --no-ledger 2>&1); rc=$?
|
|
226
|
+
[ "$rc" -eq 3 ] && grep -q "unrun" <<<"$out" && echo " ok exit 3, unrun rows" || { echo " FAIL rc=$rc"; echo "$out" | tail -4; fails=$((fails+1)); }
|
|
227
|
+
rm -rf "$tmp"
|
|
228
|
+
echo "== 4. a wire probe that MATCHED somebody is unrun (exit 3), not a fast pass"
|
|
229
|
+
# Red half: point the canary back at the caller's own board, where `ceo`
|
|
230
|
+
# subscribes to every tag. The door still answers 200 with outcome=result —
|
|
231
|
+
# only the assertion catches it.
|
|
232
|
+
out=$(SIGNAL_WATCH_WIRE_OWN_BOARD=1 SIGNAL_WATCH_BUDGET_SCALE=100 bash "$0" --prod --no-ledger 2>&1); rc=$?
|
|
233
|
+
[ "$rc" -eq 3 ] && grep -q 'not the door we named' <<<"$out" && echo " ok exit 3, fan-out named as not-the-wire" || { echo " FAIL rc=$rc"; echo "$out" | tail -4; fails=$((fails+1)); }
|
|
234
|
+
echo "signal-watch --self-test: $fails failed"
|
|
235
|
+
exit "$fails"
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
if [ "$WATCH" -gt 0 ] 2>/dev/null; then
|
|
239
|
+
while :; do clear; run_once; sleep "$WATCH"; done
|
|
240
|
+
fi
|
|
241
|
+
run_once
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Publish the harness skills in .claude/skills/ into the substrate catalog, so the
|
|
3
|
+
# skills /do actually uses are reachable through `skill:run` and `skills:list`
|
|
4
|
+
# instead of only as local files an agent must already be on this disk to read.
|
|
5
|
+
#
|
|
6
|
+
# The two on-disk shapes map 1:1 onto the two shapes db/skills.ts already reads:
|
|
7
|
+
# .claude/skills/<name>/SKILL.md -> R2 <slug>/skills/<name>/SKILL.md
|
|
8
|
+
# .claude/skills/<name>.md -> R2 <slug>/skills/<name>.md
|
|
9
|
+
# So this is a copy, not a conversion — nothing here reformats a skill body.
|
|
10
|
+
#
|
|
11
|
+
# The SECOND source is the shared skill library in one.ie/ai/skills/, which is what
|
|
12
|
+
# an executor actually runs — `foundation:deep` loads each elevate-foundation-*
|
|
13
|
+
# SKILL.md through getSkillOrRef, so a skill that is on disk but not in R2 makes the
|
|
14
|
+
# run fail closed (`skill_not_found:<name>`). Same copy, same key shape:
|
|
15
|
+
# one.ie/ai/skills/<name>/SKILL.md -> R2 <slug>/skills/<name>/SKILL.md
|
|
16
|
+
#
|
|
17
|
+
# That dir holds 140+ skills, so it is filtered by --ai-pattern rather than published
|
|
18
|
+
# wholesale: a routine run of this script must not silently push the whole library to
|
|
19
|
+
# prod R2. The default covers exactly the 10 skills in deep-sequence.ts SKILL_ORDER.
|
|
20
|
+
#
|
|
21
|
+
# Usage:
|
|
22
|
+
# skills-publish.sh [--slug one] [--dry-run] [--bucket one-content]
|
|
23
|
+
# [--ai-pattern 'elevate-foundation-*'] [--no-ai]
|
|
24
|
+
#
|
|
25
|
+
# Re-running is safe: an R2 put is a full overwrite of one key, and the catalog is
|
|
26
|
+
# keyed by name, so a republished skill replaces itself rather than accumulating.
|
|
27
|
+
# Deletions are NOT mirrored — a skill removed from disk stays in the catalog until
|
|
28
|
+
# it is deleted deliberately, because unpublishing is a decision, not a side effect.
|
|
29
|
+
set -euo pipefail
|
|
30
|
+
|
|
31
|
+
SLUG="one"
|
|
32
|
+
BUCKET="one-content"
|
|
33
|
+
DRY=false
|
|
34
|
+
AI_PATTERN='elevate-foundation-*'
|
|
35
|
+
AI=true
|
|
36
|
+
while [ $# -gt 0 ]; do
|
|
37
|
+
case "$1" in
|
|
38
|
+
--slug) SLUG="$2"; shift 2 ;;
|
|
39
|
+
--bucket) BUCKET="$2"; shift 2 ;;
|
|
40
|
+
--ai-pattern) AI_PATTERN="$2"; shift 2 ;;
|
|
41
|
+
--no-ai) AI=false; shift ;;
|
|
42
|
+
--dry-run) DRY=true; shift ;;
|
|
43
|
+
*) echo "unknown arg: $1" >&2; exit 2 ;;
|
|
44
|
+
esac
|
|
45
|
+
done
|
|
46
|
+
|
|
47
|
+
_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
48
|
+
SKILLS="$_ROOT/.claude/skills"
|
|
49
|
+
AI_SKILLS="$_ROOT/one.ie/ai/skills"
|
|
50
|
+
[ -d "$SKILLS" ] || { echo "no skills dir at $SKILLS" >&2; exit 1; }
|
|
51
|
+
|
|
52
|
+
# wrangler resolves its account from one.ie/web's config; run from there.
|
|
53
|
+
cd "$_ROOT/one.ie/web"
|
|
54
|
+
|
|
55
|
+
ok=0; fail=0; failed=""
|
|
56
|
+
|
|
57
|
+
_put() { # <r2-key> <local-file>
|
|
58
|
+
if [ "$DRY" = true ]; then echo "DRY $1"; ok=$((ok + 1)); return 0; fi
|
|
59
|
+
# Output captured, then matched — never `| grep -q`. In a pipefail script a
|
|
60
|
+
# matching grep exits first and the producer takes SIGPIPE, which reads as a
|
|
61
|
+
# failure on success (see .claude/CLAUDE.md § TRAP).
|
|
62
|
+
local out
|
|
63
|
+
if out=$(npx wrangler r2 object put "$BUCKET/$1" --file="$2" --remote 2>&1); then
|
|
64
|
+
ok=$((ok + 1))
|
|
65
|
+
else
|
|
66
|
+
fail=$((fail + 1)); failed="$failed $1"
|
|
67
|
+
printf '%s\n' "$out" | tail -2 >&2
|
|
68
|
+
fi
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for d in "$SKILLS"/*/; do
|
|
72
|
+
[ -f "$d/SKILL.md" ] || continue
|
|
73
|
+
_put "$SLUG/skills/$(basename "$d")/SKILL.md" "$d/SKILL.md"
|
|
74
|
+
done
|
|
75
|
+
|
|
76
|
+
for f in "$SKILLS"/*.md; do
|
|
77
|
+
[ -f "$f" ] || continue
|
|
78
|
+
_put "$SLUG/skills/$(basename "$f")" "$f"
|
|
79
|
+
done
|
|
80
|
+
|
|
81
|
+
ai=0
|
|
82
|
+
if [ "$AI" = true ] && [ -d "$AI_SKILLS" ]; then
|
|
83
|
+
for d in "$AI_SKILLS"/$AI_PATTERN/; do
|
|
84
|
+
[ -f "$d/SKILL.md" ] || continue
|
|
85
|
+
_put "$SLUG/skills/$(basename "$d")/SKILL.md" "$d/SKILL.md"
|
|
86
|
+
ai=$((ai + 1))
|
|
87
|
+
done
|
|
88
|
+
if [ "$ai" -eq 0 ]; then
|
|
89
|
+
echo "skills-publish: WARNING no ai/skills matched --ai-pattern '$AI_PATTERN'" >&2
|
|
90
|
+
fi
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
echo "skills-publish: $ok published ($ai from ai/skills), $fail failed (slug=$SLUG bucket=$BUCKET)"
|
|
94
|
+
[ "$fail" -eq 0 ] || { echo "failed:$failed" >&2; exit 1; }
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# speed-cache-check.sh — proves domain-memo.ts's cross-isolate (L2) cache is a
|
|
3
|
+
# real behaviour, not a presence grep.
|
|
4
|
+
#
|
|
5
|
+
# WHY THIS EXISTS: an earlier draft of "prove the cache works" matched the
|
|
6
|
+
# word "CACHED" inside a docblock and passed on nothing — a presence grep
|
|
7
|
+
# proves a SHAPE, never a BEHAVIOUR (text/learnings.md, project_fail_open_aggregate_runners).
|
|
8
|
+
# This check instead runs the real vitest suite twice against two different
|
|
9
|
+
# trees of the SAME file (working tree vs `git show HEAD:<path>`, the shape
|
|
10
|
+
# before this cycle's L2 fix landed) and asserts the exit code differs in the
|
|
11
|
+
# expected direction:
|
|
12
|
+
#
|
|
13
|
+
# RED (pre-fix domain-memo.ts): "a cold isolate ... still skips D1 via L2"
|
|
14
|
+
# fails — a per-isolate Map has no cross-isolate reach, so a fresh
|
|
15
|
+
# `vi.resetModules()` import always re-pays D1.
|
|
16
|
+
# GREEN (this cycle's domain-memo.ts): the same test passes — a fresh
|
|
17
|
+
# module import still finds the row in the shared `caches.default`
|
|
18
|
+
# store the "warm" isolate wrote, and never touches D1.
|
|
19
|
+
#
|
|
20
|
+
# The GREEN half is what a normal CI run enforces (`bun run verify` already
|
|
21
|
+
# runs tests/unit/domain-memo.test.ts). This script exists for the RED half —
|
|
22
|
+
# proving the test can fail, and fails for the SPECIFIC reason claimed (a cold
|
|
23
|
+
# isolate re-pays D1), not some unrelated breakage.
|
|
24
|
+
#
|
|
25
|
+
# Usage: bash .claude/scripts/speed-cache-check.sh
|
|
26
|
+
# Exits 0 only if: RED run fails on the cold-isolate test AND GREEN run
|
|
27
|
+
# (the current working tree) passes the whole file.
|
|
28
|
+
|
|
29
|
+
set -euo pipefail
|
|
30
|
+
|
|
31
|
+
# _GATE — see governor-doors-check.sh "compute sites". A gate_lock dedupes
|
|
32
|
+
# identical work; only a SLOT bounds N worktrees running N computes at once.
|
|
33
|
+
# Empty inside an existing gate so the outer slot is inherited, never doubled.
|
|
34
|
+
_GATE=()
|
|
35
|
+
if [ "${GOVERN_IN_GATE:-0}" != "1" ]; then
|
|
36
|
+
_GR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/gate-run.sh"
|
|
37
|
+
[ -f "$_GR" ] && _GATE=( bash "$_GR" "compute:$(basename "${BASH_SOURCE[0]}")" -- )
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
42
|
+
WEB="$ROOT/one.ie/web"
|
|
43
|
+
REL_MEMO="one.ie/web/src/lib/domain-memo.ts"
|
|
44
|
+
REL_TEST="one.ie/web/tests/unit/domain-memo.test.ts"
|
|
45
|
+
TEST_FILE="tests/unit/domain-memo.test.ts"
|
|
46
|
+
COLD_TEST_NAME="a cold isolate"
|
|
47
|
+
|
|
48
|
+
cd "$ROOT"
|
|
49
|
+
|
|
50
|
+
if [ ! -f "$REL_MEMO" ]; then
|
|
51
|
+
echo "[speed-cache-check] missing $REL_MEMO — nothing to check" >&2
|
|
52
|
+
exit 2
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
56
|
+
echo "[speed-cache-check] not a git repo — cannot diff RED vs GREEN" >&2
|
|
57
|
+
exit 2
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# The RED half needs a tree where domain-memo.ts has NO L2 (Cache API) layer.
|
|
61
|
+
# If HEAD already has the fix (this cycle already committed), fall back to a
|
|
62
|
+
# hand-written broken copy so the check still proves something rather than
|
|
63
|
+
# silently degrading to "n/a".
|
|
64
|
+
HEAD_HAS_L2=0
|
|
65
|
+
if git show HEAD:"$REL_MEMO" 2>/dev/null | grep -q "caches\.default"; then
|
|
66
|
+
HEAD_HAS_L2=1
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
TMP_BROKEN=""
|
|
70
|
+
cleanup() {
|
|
71
|
+
if [ -n "$TMP_BROKEN" ] && [ -f "$TMP_BROKEN" ]; then
|
|
72
|
+
rm -f "$TMP_BROKEN"
|
|
73
|
+
fi
|
|
74
|
+
if [ "${STASHED:-0}" = "1" ]; then
|
|
75
|
+
git stash pop --quiet
|
|
76
|
+
fi
|
|
77
|
+
}
|
|
78
|
+
trap cleanup EXIT
|
|
79
|
+
|
|
80
|
+
STASHED=0
|
|
81
|
+
RED_SOURCE_DESC=""
|
|
82
|
+
|
|
83
|
+
if [ "$HEAD_HAS_L2" = "0" ]; then
|
|
84
|
+
RED_SOURCE_DESC="git HEAD:$REL_MEMO (pre-fix shape)"
|
|
85
|
+
# Stash ONLY this file's working-tree changes (never a blanket stash —
|
|
86
|
+
# .claude/CLAUDE.md's git-add-guard rule, and just good hygiene: this repo
|
|
87
|
+
# has other unrelated modified files in play from sibling /do cycles).
|
|
88
|
+
if ! git diff --quiet -- "$REL_MEMO"; then
|
|
89
|
+
git stash push --quiet -- "$REL_MEMO"
|
|
90
|
+
STASHED=1
|
|
91
|
+
fi
|
|
92
|
+
else
|
|
93
|
+
# HEAD already carries the L2 fix (e.g. re-run after commit) — synthesize
|
|
94
|
+
# the pre-fix shape by stripping the L2 read/write calls out of a scratch
|
|
95
|
+
# copy, then swap it in the same way (stash, write broken, test, restore).
|
|
96
|
+
RED_SOURCE_DESC="synthesized pre-L2 copy (HEAD already has the fix)"
|
|
97
|
+
TMP_BROKEN="$(mktemp)"
|
|
98
|
+
python3 - "$REL_MEMO" "$TMP_BROKEN" <<'PY'
|
|
99
|
+
import re, sys
|
|
100
|
+
src_path, out_path = sys.argv[1], sys.argv[2]
|
|
101
|
+
with open(src_path) as f:
|
|
102
|
+
src = f.read()
|
|
103
|
+
# Remove the L2 read/write calls from readDomainRowMemo so it behaves exactly
|
|
104
|
+
# like the original per-isolate-only implementation.
|
|
105
|
+
src = src.replace(
|
|
106
|
+
"""\tconst edgeRow = await readEdgeCache(host);
|
|
107
|
+
\tif (edgeRow !== undefined) {
|
|
108
|
+
\t\tif (_cache.size >= MAX_ENTRIES) _cache.clear();
|
|
109
|
+
\t\t_cache.set(host, { row: edgeRow, at: Date.now() });
|
|
110
|
+
\t\treturn edgeRow;
|
|
111
|
+
\t}
|
|
112
|
+
|
|
113
|
+
""",
|
|
114
|
+
"",
|
|
115
|
+
)
|
|
116
|
+
src = src.replace("\twriteEdgeCache(host, row);\n", "")
|
|
117
|
+
with open(out_path, "w") as f:
|
|
118
|
+
f.write(src)
|
|
119
|
+
PY
|
|
120
|
+
cp "$REL_MEMO" "${REL_MEMO}.speed-cache-check.bak"
|
|
121
|
+
cp "$TMP_BROKEN" "$REL_MEMO"
|
|
122
|
+
STASHED=2
|
|
123
|
+
cleanup() {
|
|
124
|
+
mv "${REL_MEMO}.speed-cache-check.bak" "$REL_MEMO"
|
|
125
|
+
rm -f "$TMP_BROKEN"
|
|
126
|
+
}
|
|
127
|
+
trap cleanup EXIT
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
echo "[speed-cache-check] RED run — $RED_SOURCE_DESC"
|
|
131
|
+
cd "$WEB"
|
|
132
|
+
set +e
|
|
133
|
+
RED_OUT="$("${_GATE[@]}" bunx vitest run "$TEST_FILE" -t "$COLD_TEST_NAME" 2>&1)"
|
|
134
|
+
RED_STATUS=$?
|
|
135
|
+
set -e
|
|
136
|
+
cd "$ROOT"
|
|
137
|
+
|
|
138
|
+
if [ $RED_STATUS -eq 0 ]; then
|
|
139
|
+
echo "[speed-cache-check] FAIL: the cold-isolate test PASSED against the pre-fix shape." >&2
|
|
140
|
+
echo " That means the test isn't proving cross-isolate behaviour — it would pass on nothing." >&2
|
|
141
|
+
echo "$RED_OUT" | tail -40 >&2
|
|
142
|
+
exit 1
|
|
143
|
+
fi
|
|
144
|
+
if ! printf '%s' "$RED_OUT" | grep -q "$COLD_TEST_NAME"; then
|
|
145
|
+
echo "[speed-cache-check] FAIL: RED run failed but not on the named test — some other breakage." >&2
|
|
146
|
+
echo "$RED_OUT" | tail -60 >&2
|
|
147
|
+
exit 1
|
|
148
|
+
fi
|
|
149
|
+
echo "[speed-cache-check] RED confirmed — cold-isolate test fails without L2 (as expected)."
|
|
150
|
+
|
|
151
|
+
# Restore the fixed file before the GREEN run.
|
|
152
|
+
trap - EXIT
|
|
153
|
+
cleanup
|
|
154
|
+
STASHED=0
|
|
155
|
+
TMP_BROKEN=""
|
|
156
|
+
|
|
157
|
+
echo "[speed-cache-check] GREEN run — working tree $REL_MEMO"
|
|
158
|
+
cd "$WEB"
|
|
159
|
+
set +e
|
|
160
|
+
GREEN_OUT="$("${_GATE[@]}" bunx vitest run "$TEST_FILE" 2>&1)"
|
|
161
|
+
GREEN_STATUS=$?
|
|
162
|
+
set -e
|
|
163
|
+
cd "$ROOT"
|
|
164
|
+
|
|
165
|
+
if [ $GREEN_STATUS -ne 0 ]; then
|
|
166
|
+
echo "[speed-cache-check] FAIL: the full domain-memo suite does not pass on the fixed file." >&2
|
|
167
|
+
echo "$GREEN_OUT" | tail -60 >&2
|
|
168
|
+
exit 1
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
echo "[speed-cache-check] GREEN confirmed — full domain-memo.test.ts passes with L2 wired."
|
|
172
|
+
echo "[speed-cache-check] OK — the cross-isolate cache is proven by behaviour, not by grep."
|
|
173
|
+
exit 0
|