@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,108 @@
1
+ #!/usr/bin/env bash
2
+ # do-prove-selftest.sh — the red proof for do-prove.sh's argument contract.
3
+ # Invoked as `do-prove.sh --self-test`; $1 is the do-prove.sh under test.
4
+ #
5
+ # Asked for by .claude/improvements.queue.md:307 ("it has no --self-test, unlike
6
+ # its sibling gates ... or this regresses silently the way it arrived").
7
+ #
8
+ # Every GREEN check here is paired with a RED one that GUTS the guard it depends
9
+ # on and asserts the check then fails. A checker that stays green against gutted
10
+ # code proves nothing.
11
+ #
12
+ # Needs no live server: the parser half is pure argument handling, and the base
13
+ # half points at a closed port on purpose.
14
+ set -uo pipefail
15
+
16
+ TARGET="${1:-}"
17
+ [ -n "$TARGET" ] && [ -f "$TARGET" ] || { echo "selftest: need the path to do-prove.sh"; exit 2; }
18
+
19
+ pass=0; fail=0
20
+ ok() { echo " ok — $1"; pass=$((pass+1)); }
21
+ no() { echo " FAIL — $1"; fail=$((fail+1)); }
22
+
23
+ # A port nothing is listening on. Verified closed, not assumed.
24
+ DEAD=""
25
+ for p in 45871 45872 45873 45874; do
26
+ if ! curl -sf -o /dev/null --max-time 1 "http://127.0.0.1:$p/" 2>/dev/null; then DEAD="$p"; break; fi
27
+ done
28
+ [ -n "$DEAD" ] || { echo "selftest: could not find a closed port"; exit 2; }
29
+ DEADBASE="http://127.0.0.1:$DEAD"
30
+
31
+ # A gutted copy of do-prove.sh with one guard removed, laid out so its own
32
+ # ROOT-from-BASH_SOURCE derivation still works.
33
+ # A mutant must still PARSE. Deleting the body of an `if` leaves an orphaned
34
+ # `fi`, and bash exits 2 on a syntax error — which is indistinguishable from a
35
+ # usage error and would have let a broken mutant masquerade as a red proof.
36
+ # Measured while writing this file: both base-half mutants "failed" that way.
37
+ # So mutate by SUBSTITUTION (guard → `:`), and refuse a mutant that does not
38
+ # parse rather than scoring it.
39
+ gut() { # $1 = sed program → echoes the path of the mutant, or empty on failure
40
+ local d; d="$(mktemp -d)"
41
+ mkdir -p "$d/.claude/scripts"
42
+ sed "$1" "$TARGET" > "$d/.claude/scripts/do-prove.sh"
43
+ if ! cmp -s "$TARGET" "$d/.claude/scripts/do-prove.sh"; then :; else
44
+ echo "" ; return 0 # sed matched nothing — the guard moved
45
+ fi
46
+ bash -n "$d/.claude/scripts/do-prove.sh" 2>/dev/null || { echo ""; return 0; }
47
+ echo "$d/.claude/scripts/do-prove.sh"
48
+ }
49
+
50
+ echo "do-prove --self-test"
51
+ echo "[1/2] parser — an unrecognised FLAG is a hard error"
52
+
53
+ out="$(bash "$TARGET" --route / --bogus </dev/null 2>&1)"; rc=$?
54
+ [ "$rc" -eq 2 ] && ok "unknown flag --bogus exits 2 (got $rc)" \
55
+ || no "unknown flag --bogus should exit 2, got $rc"
56
+ case "$out" in *"unknown flag '--bogus'"*) ok "…and names the flag" ;;
57
+ *) no "output did not name the flag: $out" ;; esac
58
+
59
+ out="$(bash "$TARGET" --base </dev/null 2>&1)"; rc=$?
60
+ [ "$rc" -eq 2 ] && ok "--base with no value exits 2" || no "--base with no value: rc=$rc"
61
+ out="$(bash "$TARGET" --route </dev/null 2>&1)"; rc=$?
62
+ [ "$rc" -eq 2 ] && ok "--route with no value exits 2" || no "--route with no value: rc=$rc"
63
+
64
+ # The leniency that must SURVIVE: a bare token (cycle slug) is still ignored,
65
+ # not refused. do-w4-gates.sh:210 and do-smoke.sh:85-88 depend on this.
66
+ out="$(bash "$TARGET" some-cycle-slug --route / --base "$DEADBASE" </dev/null 2>&1)"; rc=$?
67
+ [ "$rc" -ne 2 ] && ok "a bare cycle slug is still tolerated (rc=$rc, not a usage error)" \
68
+ || no "a bare cycle slug was refused as a usage error — this breaks existing callers"
69
+ case "$out" in *"is not a path (cycle slug?)"*) ok "…and still says so" ;;
70
+ *) no "the lenient note is gone" ;; esac
71
+
72
+ # RED: remove the -*) branch and the flag guard must stop biting.
73
+ m="$(gut 's|^ exit 2 ;;$| shift ;;|')"
74
+ if [ -z "$m" ]; then no "RED PROOF FAILED: could not build the flag-guard mutant"; else
75
+ out="$(bash "$m" --route / --bogus --base "$DEADBASE" </dev/null 2>&1)"; rc=$?
76
+ [ "$rc" -ne 2 ] && ok "RED PROOF: gutted flag guard no longer exits 2 (got $rc) — check 1 is live" \
77
+ || no "RED PROOF FAILED: mutant still exits 2; check 1 proves nothing"
78
+ fi
79
+
80
+ echo "[2/2] base — --base pins the leg, and an unrun proof is not a pass"
81
+
82
+ out="$(bash "$TARGET" --route / --base "$DEADBASE" </dev/null 2>&1)"; rc=$?
83
+ [ "$rc" -ne 0 ] && ok "explicit --base that answers nothing exits non-zero (got $rc)" \
84
+ || no "explicit --base answered nothing and still exited 0 — the fail-open is back"
85
+ case "$out" in *"an unrun proof is not a pass"*) ok "…and says why" ;;
86
+ *) no "no unrun message: $out" ;; esac
87
+ case "$out" in *"https://one.ie"*) no "--base did not pin the fallback — it reached for https://one.ie" ;;
88
+ *) ok "--base pinned the fallback (production never named)" ;; esac
89
+
90
+ # RED: remove the fallback pin → the mutant must reach for https://one.ie.
91
+ m="$(gut 's|^ PROD_URL="\$BASE_URL"$| :|')"
92
+ if [ -z "$m" ]; then no "RED PROOF FAILED: could not build the pin mutant"; else
93
+ out="$(bash "$m" --route / --base "$DEADBASE" </dev/null 2>&1)"
94
+ case "$out" in *"https://one.ie"*) ok "RED PROOF: unpinned mutant reaches production — the pin is load-bearing" ;;
95
+ *) no "RED PROOF FAILED: mutant did not reach production; the pin check proves nothing" ;; esac
96
+ fi
97
+
98
+ # RED: remove the unrun-is-not-a-pass block → the mutant must go back to exit 0.
99
+ m="$(gut 's|^ exit 1$| :|')"
100
+ if [ -z "$m" ]; then no "RED PROOF FAILED: could not build the fail-open mutant"; else
101
+ out="$(bash "$m" --route / --base "$DEADBASE" </dev/null 2>&1)"; rc=$?
102
+ if [ "$rc" -eq 0 ]; then ok "RED PROOF: gutted mutant exits 0 on an unreachable base — the guard is live"
103
+ else no "RED PROOF FAILED: mutant still exits $rc; the fail-open check proves nothing"; fi
104
+ fi
105
+
106
+ echo
107
+ echo "do-prove --self-test: $pass passed, $fail failed"
108
+ [ "$fail" -eq 0 ] || exit 1
@@ -4,7 +4,8 @@
4
4
  # (chrome-headless-shell page check via chrome.mjs, curl fallback against prod).
5
5
  #
6
6
  # Usage:
7
- # do-prove.sh [--route <path>]... [<changed-path>|<cycle-slug>]...
7
+ # do-prove.sh [--route <path>]... [--base <url>] [<changed-path>|<cycle-slug>]...
8
+ # do-prove.sh --self-test
8
9
  # git diff --name-only | do-prove.sh
9
10
  #
10
11
  # Arg contract:
@@ -12,16 +13,68 @@
12
13
  # - Positional args that do NOT exist on disk (e.g. a cycle slug) are ignored
13
14
  # for surface detection; changed paths then come from `git diff --name-only HEAD`.
14
15
  # - --route <path> (repeatable) forces frontend surface + names the route(s) to prove.
16
+ # - --base <url> proves against <url> instead of the default localhost base.
15
17
  # - stdin (non-tty) supplies changed paths when no positional paths are given.
18
+ # - AN UNRECOGNISED FLAG IS A HARD ERROR (exit 2). It was not, until 2026-09-07,
19
+ # and that is how the launch promise came to prove a laptop. `text/bring-one-to-life.md`
20
+ # deliverable 6 read `--route /keys --base https://one.ie`; `--base` did not
21
+ # exist, so BOTH tokens fell through the lenient `*)` branch as "not a path
22
+ # (cycle slug?)" and the run silently used the localhost default. Measured that
23
+ # morning with a dev server up on :4321:
24
+ # PROVE: note — '--base' is not a path (cycle slug?); …
25
+ # PROVE: ok / — http=200, 0 console/js errors (dev http://localhost:4321)
26
+ # PROVE: pass (1 route(s) proven) ← exit 0, prod never contacted
27
+ # The leniency is deliberate for BARE tokens (a cycle slug is passed
28
+ # legitimately — do-w4-gates.sh:210, do-smoke.sh:85-88). It is never correct
29
+ # for a token starting with `-`: nobody passes a flag they do not mean.
30
+ #
31
+ # --base semantics (matches land.sh:344-345, which pins both bases by hand and
32
+ # says why): --base PINS THE FALLBACK TOO, unless PROVE_PROD_URL is explicitly
33
+ # exported. Setting only the dev base would rebuild the same defect one layer
34
+ # down — an unreachable `--base https://staging.x` would fall through to
35
+ # PROD_URL and prove PRODUCTION while reporting it as staging.
36
+ # And an explicit --base that ANSWERS NOTHING exits non-zero rather than
37
+ # `PROVE: skipped` (exit 0): do-promise-settle.sh runs a promise's proof: string
38
+ # verbatim and reads the exit code, so a skipped prod probe would settle the
39
+ # promise KEPT with the site down. An unrun gate is not a pass. Bare invocations
40
+ # keep the old skip-is-0 semantics — land.sh:346-350 reads the ROUTE COUNT and
41
+ # factory-walk.sh:294 matches `PROVE: skipped` → unrun, and both rely on it.
16
42
  #
17
43
  # Env:
18
- # PROVE_BASE_URL dev base for browser check (default http://localhost:4321)
44
+ # PROVE_BASE_URL dev base for browser check (default http://localhost:4321).
45
+ # `--base <url>` is the flag form of exactly this, and wins.
19
46
  # PROVE_PROD_URL prod fallback base (default https://one.ie)
20
47
  # PROVE_CHECK_CMD optional check command for non-frontend surfaces; if set and
21
48
  # it fails, do-prove.sh exits nonzero.
49
+ # PROVE_SESSION_COOKIE
50
+ # `name=value` session cookie, sent on BOTH legs — chrome.mjs
51
+ # already reads this env var itself; the curl leg now sends it
52
+ # as a Cookie header. Needed to prove any authed route
53
+ # (anything under /u/<slug>/…). Against a LOCAL dev base it is
54
+ # acquired automatically via one.ie/web/scripts/dev-sign-in.ts
55
+ # so a promise's proof: still runs in a bare shell; set it by
56
+ # hand for a remote base. Sent to a localhost base only — the
57
+ # prod fallback leg never carries it.
58
+ # PROVE_NO_SESSION=1
59
+ # skip session acquisition — the documented way to drive the
60
+ # landing rule red on demand (`--route /u/one/tasks` then fails
61
+ # with "landed on /signin"). Without it the red state is only
62
+ # reachable when sign-in itself fails.
63
+ #
64
+ # LANDING RULE (2026-08-04): a route is proven only if the browser/curl ENDED UP
65
+ # on the path that was asked for. Measured: `--route /u/one/tasks` signed-out
66
+ # 302s to /signin, which renders 200 with zero console errors — so do-prove
67
+ # reported "ok" and a promise's proof clause went green on the sign-in page. The
68
+ # tasks page was never loaded. Every authed `/u/one/*` clause in the corpus had
69
+ # the same hole. Path is compared without query or trailing slash, so
70
+ # `/u/one/in?dest=tasks` still proves itself.
22
71
  #
23
72
  # Exit codes: 0 = pass or skipped (no reachable environment) · 1 = a reachable
24
- # page failed (HTTP >= 500 / status 0 / console+JS errors) or declared check failed.
73
+ # page failed (HTTP >= 500 / 404 / status 0 / console+JS errors / landed on a
74
+ # different path than requested), a declared check failed, or an explicit --base
75
+ # answered nothing · 2 = usage error (unknown flag, or a flag missing its value).
76
+ #
77
+ # --self-test drives both halves red and green with no live server required.
25
78
  set -euo pipefail
26
79
 
27
80
  ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
@@ -31,11 +84,34 @@ BROWSER_CHECK="$ROOT/.claude/scripts/chrome.mjs"
31
84
 
32
85
  routes=()
33
86
  paths=()
87
+ base_explicit=0
88
+ self_test=0
89
+
90
+ prove_usage() {
91
+ echo "PROVE: usage: do-prove.sh [--route <path>]... [--base <url>] [--self-test] [<changed-path>|<cycle-slug>]..."
92
+ }
93
+
34
94
  while [ "$#" -gt 0 ]; do
35
95
  case "$1" in
36
96
  --route)
37
- [ "$#" -ge 2 ] || { echo "PROVE: --route requires a value"; exit 1; }
97
+ [ "$#" -ge 2 ] || { echo "PROVE: --route requires a value"; exit 2; }
38
98
  routes+=("$2"); shift 2 ;;
99
+ --base)
100
+ # The flag form of PROVE_BASE_URL. Trailing slash stripped so
101
+ # "$BASE_URL$route" never produces a double slash.
102
+ [ "$#" -ge 2 ] || { echo "PROVE: --base requires a value"; exit 2; }
103
+ BASE_URL="${2%/}"; base_explicit=1; shift 2 ;;
104
+ --self-test)
105
+ self_test=1; shift ;;
106
+ -h|--help)
107
+ prove_usage; exit 0 ;;
108
+ -*)
109
+ # THE ROOT CAUSE, closed. A token that looks like a flag and is not one is
110
+ # a caller asking for something this script cannot do — never a cycle slug.
111
+ # Silently continuing is how `--base https://one.ie` became a no-op.
112
+ echo "PROVE: unknown flag '$1'" >&2
113
+ prove_usage >&2
114
+ exit 2 ;;
39
115
  *)
40
116
  # A real path (exists on disk relative to root or cwd) counts as a changed
41
117
  # path; anything else (a cycle slug) is noted and ignored for detection.
@@ -48,6 +124,17 @@ while [ "$#" -gt 0 ]; do
48
124
  esac
49
125
  done
50
126
 
127
+ # An explicit --base means "prove HERE, nowhere else". Only an explicitly
128
+ # exported PROVE_PROD_URL may name a different fallback (land.sh does exactly
129
+ # that, pinning both to the dev URL).
130
+ if [ "$base_explicit" -eq 1 ] && [ -z "${PROVE_PROD_URL:-}" ]; then
131
+ PROD_URL="$BASE_URL"
132
+ fi
133
+
134
+ if [ "$self_test" -eq 1 ]; then
135
+ exec bash "$ROOT/.claude/scripts/do-prove-selftest.sh" "$ROOT/.claude/scripts/do-prove.sh"
136
+ fi
137
+
51
138
  # stdin (piped) paths
52
139
  if [ "${#paths[@]}" -eq 0 ] && [ ! -t 0 ]; then
53
140
  while IFS= read -r l; do [ -n "$l" ] && paths+=("$l"); done || true
@@ -130,9 +217,122 @@ routes=("${deduped[@]}")
130
217
  dev_up=0
131
218
  if curl -sf -o /dev/null --max-time 4 "$BASE_URL/" 2>/dev/null; then dev_up=1; fi
132
219
 
133
- check_route_curl() { # $1 = base, $2 = route echoes http code
134
- curl -s -o /dev/null --max-time 20 -w '%{http_code}' "$1$2" 2>/dev/null || echo "000"
220
+ # A session turns "the sign-in page rendered" into "the page under test rendered".
221
+ # chrome.mjs reads PROVE_SESSION_COOKIE from the environment on its own (it is
222
+ # inherited by the `node` call below); the curl leg needs it passed explicitly.
223
+ #
224
+ # Auto-acquire against a LOCAL dev server only. A promise's proof: has to run in
225
+ # a bare shell — that is the whole point of the kill-switch — so requiring a
226
+ # human to export a cookie first would just move the fail-open one level up.
227
+ # Strictly gated: localhost/127.0.0.1 base, dev-sign-in.ts present, no cookie
228
+ # already set. Never against a remote host, and the value is never echoed.
229
+ _prove_local_base() {
230
+ case "$BASE_URL" in
231
+ http://localhost:*|http://localhost|http://127.0.0.1:*|http://127.0.0.1) return 0 ;;
232
+ *) return 1 ;;
233
+ esac
135
234
  }
235
+ # LAZY, and only on a /signin bounce. Signing in up front changes the answer for
236
+ # routes that were already fine: locally, DEV_SLUG lets a signed-out request
237
+ # render /u/one/* as workspace `one`, while a real session is tony@one.ie — so
238
+ # an eager session turned `--route /u/one/workflows` from a genuine pass into
239
+ # "landed on /u/tony/workflows". Measured 2026-08-04. Try signed-out first; only
240
+ # a bounce to the login wall is worth a second attempt.
241
+ #
242
+ # PROVE_NO_SESSION=1 disables the retry entirely. Without it the landing rule's
243
+ # red half is only reachable when sign-in itself fails, i.e. by accident — and a
244
+ # gate nobody can drive red on demand is what this whole change exists to
245
+ # prevent. The documented two-line matrix:
246
+ # PROVE_NO_SESSION=1 do-prove.sh --route /u/one/tasks → FAIL, landed on /signin
247
+ # do-prove.sh --route /u/one/tasks → ok (retried with a session)
248
+ _session_tried=0
249
+ prove_acquire_session() { # → 0 if a cookie is now available, 1 otherwise
250
+ [ -n "${PROVE_SESSION_COOKIE:-}" ] && return 0
251
+ [ -n "${PROVE_NO_SESSION:-}" ] && return 1
252
+ [ "$_session_tried" -eq 1 ] && return 1
253
+ _session_tried=1
254
+ _prove_local_base || return 1
255
+ local c=""
256
+ # Preferred: the repo's own script. It seeds the credential in local D1 first,
257
+ # so it only works from a tree that has .wrangler/state — i.e. the main tree,
258
+ # which is where promise proofs actually run.
259
+ if [ -f "$ROOT/one.ie/web/scripts/dev-sign-in.ts" ] && command -v bun >/dev/null 2>&1; then
260
+ c="$( (cd "$ROOT/one.ie/web" && bun scripts/dev-sign-in.ts 2>/dev/null) \
261
+ | sed -n 's/^Set-Cookie: //p' | head -1 || true)"
262
+ fi
263
+ # Fallback for a worktree (no local D1 state): plain HTTP sign-in, but only
264
+ # with credentials the caller supplied. No password is hardcoded here —
265
+ # one.ie/web/scripts/dev-sign-in.ts stays the single source of the dev creds.
266
+ if [ -z "$c" ] && [ -n "${OWNER_EMAIL:-}" ] && [ -n "${DEV_PASSWORD:-}" ]; then
267
+ c="$(curl -s -D - -o /dev/null --max-time 20 -X POST \
268
+ -H 'Content-Type: application/json' -H "Origin: $BASE_URL" \
269
+ --data "{\"email\":\"${OWNER_EMAIL}\",\"password\":\"${DEV_PASSWORD}\"}" \
270
+ "$BASE_URL/api/auth/sign-in/email" 2>/dev/null \
271
+ | sed -n 's/^[Ss]et-[Cc]ookie: \(better-auth\.session_token=[^;]*\).*/\1/p' | head -1 || true)"
272
+ fi
273
+ [ -z "$c" ] && return 1
274
+ export PROVE_SESSION_COOKIE="$c"
275
+ curl_auth=(-H "Cookie: $c")
276
+ echo "PROVE: signed-out run hit the login wall — retrying with a dev session"
277
+ return 0
278
+ }
279
+
280
+ # A login wall is the one landing worth a retry; anything else is a real answer.
281
+ is_signin_path() {
282
+ case "$1" in /signin|/login|/sign-in) return 0 ;; *) return 1 ;; esac
283
+ }
284
+
285
+ curl_auth=()
286
+ [ -n "${PROVE_SESSION_COOKIE:-}" ] && curl_auth=(-H "Cookie: ${PROVE_SESSION_COOKIE}")
287
+
288
+ check_route_curl() { # $1 = base, $2 = route → echoes "<http_code> <final_url>"
289
+ # -L on purpose: a redirect is followed so the LANDING RULE can see where it
290
+ # ended up. Without it every 302 read as a bare 3xx and the old code scored
291
+ # that as "ok — not >=500, not 404", which is how a signed-out authed route
292
+ # proved itself green.
293
+ # The session cookie goes to a LOCAL base only. Auto-acquire is already
294
+ # localhost-gated, but a hand-exported PROVE_SESSION_COOKIE is not — and this
295
+ # same function serves the prod fallback below, so without this check a dev
296
+ # server being down would send a live session cookie to $PROD_URL.
297
+ local auth=()
298
+ case "$1" in
299
+ http://localhost|http://localhost:*|http://127.0.0.1|http://127.0.0.1:*)
300
+ auth=("${curl_auth[@]+"${curl_auth[@]}"}") ;;
301
+ esac
302
+ curl -sL -o /dev/null --max-time 20 "${auth[@]+"${auth[@]}"}" \
303
+ -w '%{http_code} %{url_effective}' "$1$2" 2>/dev/null || echo "000 "
304
+ }
305
+
306
+ # Pathname only — no scheme/host, no query, no fragment, no trailing slash.
307
+ # The landing rule compares these, so `/u/one/in?dest=tasks` matches its own
308
+ # redirect-free landing and a bounce to /signin does not.
309
+ path_of() {
310
+ local u="$1" rest
311
+ case "$u" in
312
+ *://*) rest="${u#*://}"
313
+ case "$rest" in
314
+ */*) u="/${rest#*/}" ;;
315
+ *) u="/" ;;
316
+ esac ;;
317
+ esac
318
+ u="${u%%\?*}"; u="${u%%#*}"
319
+ case "$u" in ""|"/") printf '/' ;; *) printf '%s' "${u%/}" ;; esac
320
+ }
321
+
322
+ # One place decides what a wrong landing means, so both legs say the same thing.
323
+ report_redirect() { # $1 = route, $2 = landed path, $3 = leg label
324
+ echo "PROVE: FAIL $1 — landed on $2, not $(path_of "$1") ($3) — the page under test never rendered"
325
+ case "$2" in
326
+ /signin|/login|/sign-in)
327
+ echo "PROVE: hint: authed route — export PROVE_SESSION_COOKIE='better-auth.session_token=…'"
328
+ echo "PROVE: (cd one.ie/web && bun scripts/dev-sign-in.ts prints one)" ;;
329
+ esac
330
+ }
331
+
332
+ # The result line used to hardcode the word "dev" — so a production run
333
+ # printed "(dev https://one.ie)" on the very receipt someone reads to decide
334
+ # whether the site is live. Name the leg for what it is.
335
+ if _prove_local_base; then LEG="dev $BASE_URL"; else LEG="base $BASE_URL"; fi
136
336
 
137
337
  fail=0
138
338
  checked=0
@@ -154,47 +354,112 @@ for route in "${routes[@]}"; do
154
354
  # "Failed to load resource: … 401", which curl had always passed. Everything
155
355
  # else still counts — uncaught JS, real console.error, and subresource
156
356
  # 404/5xx, which are genuine breakage.
157
- counted='((.jsErrors // []) + ((.consoleErrors // []) | map(select(test("status of (401|403)") | not))))'
357
+ # 400 joins the same carve-out for the same reason: Layout.astro fires a
358
+ # fire-and-forget tracking beacon (/api/follow) on every page, and it
359
+ # answers 400 missing_slug on any page outside a tenant workspace — measured
360
+ # on /, /components and prod one.ie/components identically (2026-08-07,
361
+ # livekit C5). The beacon's failure has no bearing on the page; it is
362
+ # gated-by-context the same way 401/403 is gated-by-session.
363
+ counted='((.jsErrors // []) + ((.consoleErrors // []) | map(select(test("status of (400|401|403)") | not))))'
364
+ # Against a LOCAL base only, a subresource 503 joins that list. Plain
365
+ # `astro dev` binds no Durable Objects, so /api/analytics answers 503
366
+ # intermittently — measured 2026-08-04 at roughly 1 load in 4, which made the
367
+ # types-views proof flip GREEN/GREEN/RED across three identical sweeps. Same
368
+ # reasoning as the 401/403 policy above: the environment is limited, the page
369
+ # is not broken. Scoped to localhost so a 503 in prod still fails, and every
370
+ # ignored one is PRINTED below rather than silently dropped. `dev:wrangler`
371
+ # binds the DOs and makes this moot.
372
+ if _prove_local_base; then
373
+ counted='((.jsErrors // []) + ((.consoleErrors // []) | map(select(test("status of (400|401|403|503)") | not))))'
374
+ ignored='((.consoleErrors // []) | map(select(test("status of 503"))) | length)'
375
+ else
376
+ ignored='0'
377
+ fi
158
378
  errs="$(printf '%s' "$out" | jq -r "$counted | length" 2>/dev/null || echo "")"
379
+ final="$(printf '%s' "$out" | jq -r '.finalUrl // ""' 2>/dev/null || echo "")"
380
+ # Hit the login wall signed-out? That is the one case a session can change,
381
+ # so retry once. Any other landing is the real answer and is left alone.
382
+ if is_signin_path "$(path_of "${final:-$route}")" && prove_acquire_session; then
383
+ out="$(node "$BROWSER_CHECK" "$BASE_URL$route" 2>/dev/null || true)"
384
+ status="$(printf '%s' "$out" | jq -r '.httpStatus // 0' 2>/dev/null || echo 0)"
385
+ errs="$(printf '%s' "$out" | jq -r "$counted | length" 2>/dev/null || echo "")"
386
+ final="$(printf '%s' "$out" | jq -r '.finalUrl // ""' 2>/dev/null || echo "")"
387
+ fi
388
+ # One retry when the ONLY complaint is console errors on an otherwise correct
389
+ # landing. Vite's first load after it pulls a new dep throws `useState of
390
+ # null` / "more than one copy of React" and they vanish once the optimizer
391
+ # settles (learnings 2026-08-02). Measured 2026-08-04: the same /u/one/tasks
392
+ # proof came back exit=1 then exit=0 with no edit in between — a gate that
393
+ # flips on a re-run cannot settle a promise. Status and landing failures are
394
+ # NOT retried; a genuinely broken page fails both runs, so this kills the
395
+ # flake without buying a flaky pass.
396
+ landed="$(path_of "${final:-$route}")"
397
+ if [ -n "$status" ] && [ "$status" != "0" ] && [ -n "$errs" ] && [ "$errs" -gt 0 ] \
398
+ && [ "$status" -lt 500 ] && [ "$status" -ne 404 ] && [ "$landed" = "$(path_of "$route")" ]; then
399
+ out="$(node "$BROWSER_CHECK" "$BASE_URL$route" 2>/dev/null || true)"
400
+ status="$(printf '%s' "$out" | jq -r '.httpStatus // 0' 2>/dev/null || echo 0)"
401
+ errs="$(printf '%s' "$out" | jq -r "$counted | length" 2>/dev/null || echo "")"
402
+ final="$(printf '%s' "$out" | jq -r '.finalUrl // ""' 2>/dev/null || echo "")"
403
+ fi
159
404
  if [ -n "$status" ] && [ "$status" != "0" ] && [ -n "$errs" ]; then
160
405
  proved=1; checked=$((checked+1))
406
+ landed="$(path_of "${final:-$route}")"
407
+ # LANDING RULE first — a bounce to /signin renders 200 with zero errors, so
408
+ # every check below it would pass on a page that is not the one requested.
161
409
  # 404 IS A FAIL here too. The curl leg below learned this on 2026-07-29;
162
410
  # the browser leg never did, so a nonexistent route proved itself green
163
411
  # whenever the page also logged no errors. 401/403 stay ok — gated, not gone.
164
- if [ "$status" -ge 500 ] || [ "$status" -eq 404 ] || [ "$errs" -gt 0 ]; then
165
- echo "PROVE: FAIL $route http=$status console/js errors=$errs (dev $BASE_URL)"
412
+ if [ -n "$final" ] && [ "$landed" != "$(path_of "$route")" ]; then
413
+ report_redirect "$route" "$landed" "$LEG"
414
+ fail=1
415
+ elif [ "$status" -ge 500 ] || [ "$status" -eq 404 ] || [ "$errs" -gt 0 ]; then
416
+ echo "PROVE: FAIL $route — http=$status console/js errors=$errs ($LEG)"
166
417
  printf '%s' "$out" | jq -r "$counted[]" 2>/dev/null | head -5 | sed 's/^/PROVE: error: /'
167
418
  fail=1
168
419
  else
169
- echo "PROVE: ok $route — http=$status, 0 console/js errors (dev $BASE_URL)"
420
+ echo "PROVE: ok $route — http=$status, 0 console/js errors ($LEG)"
170
421
  fi
422
+ # Never silent: an ignored error is still an error someone should see.
423
+ ign="$(printf '%s' "$out" | jq -r "$ignored" 2>/dev/null || echo 0)"
424
+ [ "${ign:-0}" -gt 0 ] 2>/dev/null && \
425
+ echo "PROVE: note — $ign subresource 503(s) ignored (no DO bindings in vite dev; dev:wrangler for DO parity)"
171
426
  fi
172
427
  # status 0 / unparsable = environmental (browser-check couldn't run) → fall through
173
428
  fi
174
429
  if [ "$proved" -eq 0 ] && [ "$dev_up" -eq 1 ]; then
175
430
  # dev reachable but playwright unavailable → curl the dev route
176
- code="$(check_route_curl "$BASE_URL" "$route")"
431
+ read -r code effective <<< "$(check_route_curl "$BASE_URL" "$route")"
432
+ if is_signin_path "$(path_of "${effective:-$route}")" && prove_acquire_session; then
433
+ read -r code effective <<< "$(check_route_curl "$BASE_URL" "$route")"
434
+ fi
177
435
  if [ "$code" != "000" ]; then
178
436
  proved=1; checked=$((checked+1))
179
- # 404 IS A FAIL. It used to report `ok` — only >=500 failed — so a route
180
- # that does not exist proved itself. Measured 2026-07-29: `--route /factory`
181
- # returned "ok /factory http=404" against a dev server running a tree
182
- # without the page, and that line was the 17th leg of a promise's proof.
183
- # 401/403 stay ok: the route exists and answered, it is merely gated.
184
- if [ "$code" -ge 500 ] || [ "$code" -eq 404 ]; then
185
- echo "PROVE: FAIL $route http=$code (dev $BASE_URL, curl)"; fail=1
437
+ landed="$(path_of "${effective:-$route}")"
438
+ # LANDING RULE, then 404. 404 IS A FAIL: it used to report `ok` — only >=500
439
+ # failed so a route that does not exist proved itself. Measured 2026-07-29:
440
+ # `--route /factory` returned "ok /factory http=404" against a dev server
441
+ # running a tree without the page, and that line was the 17th leg of a
442
+ # promise's proof. 401/403 stay ok: the route answered, it is merely gated.
443
+ if [ -n "$effective" ] && [ "$landed" != "$(path_of "$route")" ]; then
444
+ report_redirect "$route" "$landed" "$LEG, curl"; fail=1
445
+ elif [ "$code" -ge 500 ] || [ "$code" -eq 404 ]; then
446
+ echo "PROVE: FAIL $route — http=$code ($LEG, curl)"; fail=1
186
447
  else
187
- echo "PROVE: ok $route — http=$code (dev $BASE_URL, curl)"
448
+ echo "PROVE: ok $route — http=$code ($LEG, curl)"
188
449
  fi
189
450
  fi
190
451
  fi
191
452
  if [ "$proved" -eq 0 ]; then
192
- code="$(check_route_curl "$PROD_URL" "$route")"
453
+ read -r code effective <<< "$(check_route_curl "$PROD_URL" "$route")"
193
454
  if [ "$code" != "000" ]; then
194
455
  checked=$((checked+1))
195
- # Same rule as the dev branch above — a 404 in production is the clearest
196
- # possible "not shipped", and it must not read as proven.
197
- if [ "$code" -ge 500 ] || [ "$code" -eq 404 ]; then
456
+ # Same rules as the dev branch above — a redirect off the requested path
457
+ # proves the wrong page, and a 404 in production is the clearest possible
458
+ # "not shipped". Neither may read as proven.
459
+ landed="$(path_of "${effective:-$route}")"
460
+ if [ -n "$effective" ] && [ "$landed" != "$(path_of "$route")" ]; then
461
+ report_redirect "$route" "$landed" "prod $PROD_URL"; fail=1
462
+ elif [ "$code" -ge 500 ] || [ "$code" -eq 404 ]; then
198
463
  echo "PROVE: FAIL $route — http=$code (prod $PROD_URL)"; fail=1
199
464
  else
200
465
  echo "PROVE: ok $route — http=$code (prod $PROD_URL)"
@@ -209,6 +474,13 @@ if [ "$fail" -eq 1 ]; then
209
474
  echo "PROVE: fail"; exit 1
210
475
  fi
211
476
  if [ "$checked" -eq 0 ]; then
477
+ # An explicitly named base that answered nothing is an UNRUN PROOF, and
478
+ # do-promise-settle.sh reads this exit code verbatim. Exit 0 here would settle
479
+ # a promise KEPT because the site was unreachable.
480
+ if [ "$base_explicit" -eq 1 ]; then
481
+ echo "PROVE: FAIL — --base $BASE_URL answered nothing; an unrun proof is not a pass"
482
+ exit 1
483
+ fi
212
484
  echo "PROVE: skipped (no reachable environment)"; exit 0
213
485
  fi
214
486
  echo "PROVE: pass ($checked route(s) proven)"; exit 0
@@ -96,6 +96,19 @@ def deliverable_dirs(text):
96
96
  dirs = set()
97
97
  for tok in deliverable_paths(text):
98
98
  parts = tok.split("/")
99
+ # `text/` is the ONE family where the containing-dir rule is wrong. The
100
+ # coupling argument above holds for code (two plans in one component family
101
+ # clobber each other); it does not hold here, because text/ is one flat dir
102
+ # of per-slug documents — `text/<slug>-docs.md` is named after the plan that
103
+ # owns it, so two plans there edit two different files BY CONSTRUCTION and
104
+ # cannot collide. Keying them to the shared `text` dir made every plan with a
105
+ # `doc:` deliverable mutually exclusive with every other: measured 2026-08-31,
106
+ # it serialized all four lifecycle children (and voice-agents, identity,
107
+ # playbook-run, agent-wallet-guardian) behind one another for no real conflict.
108
+ # File granularity here, containing-dir everywhere else.
109
+ if parts[0] == "text" and len(parts) == 2 and "." in parts[-1]:
110
+ dirs.add(tok)
111
+ continue
99
112
  # Drop the leaf if it names a file (has an extension); else the path IS a dir.
100
113
  sub = "/".join(parts[:-1]) if "." in parts[-1] else tok
101
114
  if sub:
@@ -121,7 +134,12 @@ def deliverable_paths(text):
121
134
  """Every repo-root path token in the `deliverables:` block (full path, not the
122
135
  top/second dir). The granularity-independent source — deliverable_dirs() and the
123
136
  linter both derive from it. Path column only (before the em-dash), REPO_DIRS-rooted."""
124
- m = re.search(r"^deliverables:\s*$(.*?)(?=^[a-z_]+:|^---\s*$|\Z)",
137
+ # The key line may carry a trailing YAML comment — `deliverables: # note`.
138
+ # It legally does in 12 tracked plans INCLUDING text/template-todo.md, the
139
+ # file every new plan is copied from, so a `\s*$` anchor here made plans
140
+ # born unscoped: the block never matched, zero paths came back, and the
141
+ # fleet read that as "declares no dirs" and refused to pack them.
142
+ m = re.search(r"^deliverables:[ \t]*(?:#[^\n]*)?$(.*?)(?=^[a-z_]+:|^---\s*$|\Z)",
125
143
  text, re.MULTILINE | re.DOTALL)
126
144
  if not m:
127
145
  return []
@@ -1122,6 +1140,18 @@ def selftest():
1122
1140
  check("dirs: filename dropped",
1123
1141
  "one.ie/web/src/lib/billing/expiry.ts" not in deliverable_dirs(fm_sub))
1124
1142
  check("dirs: bare dir kept", "channels/src" in deliverable_dirs(fm_sub))
1143
+ # text/ file granularity — two plans' docs must NOT collapse to one `text` lock.
1144
+ fm_t1 = "deliverables:\n - doc: text/alpha-docs.md — x\n"
1145
+ fm_t2 = "deliverables:\n - doc: text/beta-docs.md — x\n"
1146
+ check("dirs: text/ keeps the filename",
1147
+ "text/alpha-docs.md" in deliverable_dirs(fm_t1))
1148
+ check("dirs: text/ does not collapse to the bare dir",
1149
+ "text" not in deliverable_dirs(fm_t1))
1150
+ check("dirs: two text/ docs are disjoint",
1151
+ not (deliverable_dirs(fm_t1) & deliverable_dirs(fm_t2)))
1152
+ # RED half — the old behaviour must be genuinely gone, not merely untested.
1153
+ check("dirs: text/ nested path still uses its containing dir",
1154
+ "text/world" in deliverable_dirs("deliverables:\n - doc: text/world/homepage-text.md — x\n"))
1125
1155
 
1126
1156
  # 6. transitive leverage: a→b→c chain ⇒ a has 2 dependents (b and c), not 1.
1127
1157
  direct = {"a": {"b"}, "b": {"c"}, "c": set()}
@@ -11,7 +11,13 @@
11
11
  # prune → delete entries older than MAX_AGE_DAYS
12
12
  set -euo pipefail
13
13
 
14
- CACHE_DIR="${CACHE_DIR:-.w1-cache}"
14
+ # CACHE_DIR must NOT be CWD-relative. Every /do cycle runs in a fresh
15
+ # .do-worktrees/<slug> checkout, so a relative `.w1-cache` was born empty on every
16
+ # single cycle -- the "0 tokens on hit, ~12k saved/recon" lever this script exists
17
+ # to provide had never once fired in a worktree run (measured 2026-09-01: no
18
+ # .w1-cache directory existed anywhere in the tree). Key it to the MACHINE, the
19
+ # same way the gate governor keys its locks, so worktrees share one warm store.
20
+ CACHE_DIR="${CACHE_DIR:-${TMPDIR:-/tmp}/one-w1-cache}"
15
21
  MAX_AGE_DAYS=14
16
22
  BASE_URL="${ONEIE_BASE_URL:-${ONE_BASE_URL:-}}"
17
23
  API_KEY="${WAVE_CACHE_SECRET:-${ONEIE_API_KEY:-${ONE_API_KEY:-}}}"