@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,396 @@
1
+ #!/usr/bin/env bash
2
+ # factory-walk.sh — the factory's deterministic check of ONE piece of work.
3
+ #
4
+ # manifest: needs-env
5
+ # Names one.ie/web (the previewed app) and reads that app's gitignored .env
6
+ # through the scripts it composes. Every stage degrades to `unrun`, never to a
7
+ # pass, when the environment cannot answer.
8
+ #
9
+ # WHAT IT IS. One command whose exit code is the verdict on a task: it walks the
10
+ # work across the lifecycle stages text/factory-do.md § THE WHOLE PATH names —
11
+ # size · isolate · gate · prove · human · substrate — and writes one receipt. It
12
+ # builds NO new gate. Every stage is an existing script, chosen by the tier and
13
+ # the surface, run through the governor and the memo:
14
+ #
15
+ # size do-tier.sh how big is the change; UNSIZED is a FAIL,
16
+ # because absence of recon must never read
17
+ # as simplicity
18
+ # isolate worktree-preview.sh a UI task's tree is a real worktree with
19
+ # local .vite/.astro caches (n/a on main)
20
+ # gate verify-fast.sh the tier's lane, per folder do-folder.sh
21
+ # maps — fast for PATCH/FIX/FEATURE, FULL
22
+ # for SCHEMA or schema/sdk/auth paths; via
23
+ # gate-run.sh (slot), tsc-cached.sh and
24
+ # test-cached.sh (memo). Exit 144 = UNRUN.
25
+ # prove do-prove.sh UI: every route, landing rule, against the
26
+ # do-walk.sh --agents task's preview URL. Otherwise the slug's
27
+ # agents walk (text/<slug>-agents.md).
28
+ # human the link block UI only: the preview URL, one stop per
29
+ # route or per text/<slug>-humans.md stop,
30
+ # and the instruction that records the
31
+ # verdict. `--humans` on a tty asks y/n.
32
+ # Until a person answers, PENDING — which
33
+ # is unrun, which is not a pass.
34
+ # substrate factory-check.sh SCHEMA tier only: the ladder is live.
35
+ #
36
+ # THE HONESTY LAW, inherited from do-w4-gates.sh: every stage has FOUR states
37
+ # pass : ran, green fail : ran, red
38
+ # unrun : could not run n/a : does not apply, by rule
39
+ # and the verdict is `pass` only with ZERO fails AND ZERO unruns. A missing row
40
+ # is treated as unrun and named — an absent row is the fail-open trap in data
41
+ # form. Exit 0 = pass · 1 = a stage failed · 3 = nothing failed but something
42
+ # was not proven (unrun / pending human).
43
+ #
44
+ # THE LANE IS PART OF THE RECEIPT. A fast pass is never reported as a full pass:
45
+ # the receipt and the last line carry `lane:<fast|full|test|none>` beside
46
+ # `verdict:<ok|red|unrun>`, which is exactly the pair the executor writes onto
47
+ # the task with tasks:tag (§ Tests, T4).
48
+ #
49
+ # Usage:
50
+ # factory-walk.sh <slug> [--files <path>...] [--route </path>]...
51
+ # [--preview <url>] [--no-gate] [--humans] [--json <file>]
52
+ # factory-walk.sh --self-test
53
+ #
54
+ # <slug> names text/<slug>-agents.md / -humans.md when they exist, and
55
+ # the receipt. Any word; a task id is fine.
56
+ # --files the changed paths (default: git diff HEAD + untracked, from
57
+ # the repo this script lives in — a worktree checks itself)
58
+ # --route a route to prove; repeatable. Derived from changed
59
+ # src/pages/**.astro when omitted (dynamic [param] routes are
60
+ # skipped and named — pass them explicitly)
61
+ # --preview the task's private server, from worktree-preview.sh up
62
+ # (default http://localhost:4321 — the human's server)
63
+ # --no-gate record the gate as UNRUN instead of running it (a read of the
64
+ # other stages; never a pass)
65
+ # --humans on a tty, ask y/n per stop and record the human verdict
66
+ # --json where the receipt goes (default $TMPDIR/one-factory-walk/<slug>.json)
67
+ #
68
+ # Env, self-test only (the TSC_CACHE_CMD idiom): FACTORY_WALK_TIER_CMD,
69
+ # FACTORY_WALK_GATE_CMD, FACTORY_WALK_PROVE_CMD, FACTORY_WALK_SUBSTRATE_CMD
70
+ # substitute the stage's command so every state can be driven deterministically,
71
+ # and FACTORY_WALK_DROP_ROW=<stage> drops a row to prove the missing-row trap
72
+ # bites. None of them is read outside --self-test.
73
+ set -uo pipefail
74
+
75
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
76
+ S="$ROOT/.claude/scripts"
77
+ cd "$ROOT" || exit 2
78
+
79
+ # ---------------------------------------------------------------------------
80
+ # self-test — every state driven, and the two red proofs that matter
81
+ # ---------------------------------------------------------------------------
82
+ if [ "${1:-}" = "--self-test" ]; then
83
+ fails=0; T="$(mktemp -d)"
84
+ ok() { printf ' ok %s\n' "$*"; }
85
+ bad() { printf ' FAIL %s\n' "$*"; fails=$((fails+1)); }
86
+ # stubs: a tier, a gate, a prove, a substrate check — each a tiny script
87
+ printf '#!/bin/bash\necho "{\\"tier\\":\\"%s\\"}"; exit %s\n' FIX 0 > "$T/tier-fix"; chmod +x "$T/tier-fix"
88
+ printf '#!/bin/bash\necho "{\\"tier\\":\\"%s\\"}"; exit %s\n' SCHEMA 0 > "$T/tier-schema"; chmod +x "$T/tier-schema"
89
+ printf '#!/bin/bash\necho "{\\"tier\\":\\"%s\\"}"; exit %s\n' UNSIZED 3 > "$T/tier-unsized"; chmod +x "$T/tier-unsized"
90
+ for rc in 0 1 3 144; do printf '#!/bin/bash\nexit %s\n' "$rc" > "$T/rc$rc"; chmod +x "$T/rc$rc"; done
91
+ printf '#!/bin/bash\necho "PROVE: skipped (no reachable environment)"; exit 0\n' > "$T/prove-skipped"; chmod +x "$T/prove-skipped"
92
+ printf '#!/bin/bash\necho "PROVE: pass (1 route(s) proven)"; exit 0\n' > "$T/prove-pass"; chmod +x "$T/prove-pass"
93
+ run() { # run <expected-exit> <label> <env...> -- <args...>
94
+ local want="$1" label="$2"; shift 2
95
+ local envs=(); while [ "$1" != "--" ]; do envs+=("$1"); shift; done; shift
96
+ local out rc
97
+ out="$(env "${envs[@]+"${envs[@]}"}" FACTORY_WALK_SELFTEST=1 bash "$0" "$@" --json "$T/r.json" 2>&1)"; rc=$?
98
+ if [ "$rc" = "$want" ]; then ok "$label (exit $rc)"; else bad "$label — expected exit $want, got $rc"; printf '%s\n' "$out" | tail -8 | sed 's/^/ /'; fi
99
+ LAST_OUT="$out"
100
+ }
101
+ COMMON=(FACTORY_WALK_SUBSTRATE_CMD="$T/rc0" FACTORY_WALK_PROVE_CMD="$T/rc0")
102
+
103
+ echo "== green: a FIX with every stage answering"
104
+ run 0 "non-UI FIX, gate green, walk green" FACTORY_WALK_TIER_CMD="$T/tier-fix" FACTORY_WALK_GATE_CMD="$T/rc0" "${COMMON[@]}" -- selftest --files one.ie/web/src/lib/x.ts
105
+ jq -e '.verdict=="ok" and .lane=="fast" and .tier=="FIX"' "$T/r.json" >/dev/null && ok "receipt carries verdict:ok lane:fast tier:FIX" || bad "receipt wrong: $(cat "$T/r.json")"
106
+
107
+ echo "== RED PROOFS: the walk must be able to lose"
108
+ run 1 "gate exits 1 → verdict red, exit 1" FACTORY_WALK_TIER_CMD="$T/tier-fix" FACTORY_WALK_GATE_CMD="$T/rc1" "${COMMON[@]}" -- selftest --files one.ie/web/src/lib/x.ts
109
+ jq -e '.verdict=="red"' "$T/r.json" >/dev/null && ok "receipt says red" || bad "receipt did not say red"
110
+ run 3 "gate exits 144 (unrun) → NOT a pass, exit 3" FACTORY_WALK_TIER_CMD="$T/tier-fix" FACTORY_WALK_GATE_CMD="$T/rc144" "${COMMON[@]}" -- selftest --files one.ie/web/src/lib/x.ts
111
+ jq -e '.stages[]|select(.stage=="gate")|.status=="unrun"' "$T/r.json" >/dev/null && ok "144 is recorded as unrun" || bad "144 not recorded as unrun"
112
+ run 1 "no paths → UNSIZED → FAIL (absence of recon is not simplicity)" FACTORY_WALK_TIER_CMD="$T/tier-unsized" FACTORY_WALK_GATE_CMD="$T/rc0" "${COMMON[@]}" -- selftest --files one.ie/web/src/lib/x.ts
113
+ run 3 "--no-gate → gate unrun → exit 3, never 0" FACTORY_WALK_TIER_CMD="$T/tier-fix" FACTORY_WALK_GATE_CMD="$T/rc0" "${COMMON[@]}" -- selftest --files one.ie/web/src/lib/x.ts --no-gate
114
+ run 3 "a dropped stage row reads as unrun, never as pass" FACTORY_WALK_TIER_CMD="$T/tier-fix" FACTORY_WALK_GATE_CMD="$T/rc0" FACTORY_WALK_DROP_ROW=gate "${COMMON[@]}" -- selftest --files one.ie/web/src/lib/x.ts
115
+ case "$LAST_OUT" in *"no row for gate"*) ok "the missing row is NAMED" ;; *) bad "missing row not named" ;; esac
116
+
117
+ echo "== UI: the human gets a link and the walk is PENDING until they answer"
118
+ run 3 "UI change, prove skipped, no human yet → exit 3" FACTORY_WALK_TIER_CMD="$T/tier-fix" FACTORY_WALK_GATE_CMD="$T/rc0" FACTORY_WALK_PROVE_CMD="$T/prove-skipped" FACTORY_WALK_SUBSTRATE_CMD="$T/rc0" -- selftest --files one.ie/web/src/pages/factory.astro --preview http://localhost:4399
119
+ case "$LAST_OUT" in *"HUMAN WALK"*"http://localhost:4399/factory"*"factory-walk.sh selftest --humans"*) ok "link block: URL, route, and the record-the-verdict instruction" ;; *) bad "link block missing pieces"; printf '%s\n' "$LAST_OUT" | grep -n 'HUMAN\|http' | head -5 ;; esac
120
+ jq -e '.stages[]|select(.stage=="prove")|.status=="unrun"' "$T/r.json" >/dev/null && ok "a skipped prove is unrun (do-prove exits 0 on skip — the walk reads the line, not the code)" || bad "skipped prove misread"
121
+ run 3 "UI, prove PASS, human pending → still exit 3 (a person has not looked)" FACTORY_WALK_TIER_CMD="$T/tier-fix" FACTORY_WALK_GATE_CMD="$T/rc0" FACTORY_WALK_PROVE_CMD="$T/prove-pass" FACTORY_WALK_SUBSTRATE_CMD="$T/rc0" -- selftest --files one.ie/web/src/pages/factory.astro --preview http://localhost:4399
122
+ jq -e '.stages[]|select(.stage=="human")|.status=="unrun" and (.detail|test("PENDING"))' "$T/r.json" >/dev/null && ok "human stage is PENDING, counted as unrun" || bad "human stage not pending"
123
+ run 3 "a dynamic [param] page with no --route is unrun and named" FACTORY_WALK_TIER_CMD="$T/tier-fix" FACTORY_WALK_GATE_CMD="$T/rc0" FACTORY_WALK_PROVE_CMD="$T/prove-pass" FACTORY_WALK_SUBSTRATE_CMD="$T/rc0" -- selftest --files 'one.ie/web/src/pages/u/[slug]/tasks.astro'
124
+ case "$LAST_OUT" in *"pass --route"*) ok "the fix is named in the output" ;; *) bad "dynamic route not named" ;; esac
125
+
126
+ echo "== SCHEMA: the substrate check joins, and a 3 is cannot-run"
127
+ run 3 "schema tier, substrate exits 3 → unrun" FACTORY_WALK_TIER_CMD="$T/tier-schema" FACTORY_WALK_GATE_CMD="$T/rc0" FACTORY_WALK_PROVE_CMD="$T/rc0" FACTORY_WALK_SUBSTRATE_CMD="$T/rc3" -- selftest --files schema/one.tql
128
+ jq -e '.lane=="full"' "$T/r.json" >/dev/null && ok "SCHEMA takes the FULL lane" || bad "SCHEMA did not take the full lane: $(jq -r .lane "$T/r.json")"
129
+ run 1 "schema tier, substrate exits 1 → red" FACTORY_WALK_TIER_CMD="$T/tier-schema" FACTORY_WALK_GATE_CMD="$T/rc0" FACTORY_WALK_PROVE_CMD="$T/rc0" FACTORY_WALK_SUBSTRATE_CMD="$T/rc1" -- selftest --files schema/one.tql
130
+
131
+ echo "== doc-only: no gate by rule (n/a), and n/a is not unrun"
132
+ run 0 "a text/ change: gate n/a, prove via walk stub, pass" FACTORY_WALK_TIER_CMD="$T/tier-fix" FACTORY_WALK_GATE_CMD="$T/rc1" "${COMMON[@]}" -- selftest --files text/factory-do.md
133
+ jq -e '.stages[]|select(.stage=="gate")|.status=="n/a"' "$T/r.json" >/dev/null && ok "doc-only gate is n/a (the red stub was never run)" || bad "doc-only gate not n/a"
134
+
135
+ rm -rf "$T"
136
+ [ "$fails" -eq 0 ] && { echo "factory-walk: self-test PASS"; exit 0; }
137
+ echo "factory-walk: self-test FAILED ($fails)"; exit 1
138
+ fi
139
+
140
+ # ---------------------------------------------------------------------------
141
+ # args
142
+ # ---------------------------------------------------------------------------
143
+ slug=""; files=(); routes=(); preview="${FACTORY_WALK_PREVIEW:-http://localhost:4321}"
144
+ no_gate=0; humans=0; json_out=""
145
+ while [ "$#" -gt 0 ]; do
146
+ case "$1" in
147
+ --files) shift; while [ "$#" -gt 0 ] && [ "${1#--}" = "$1" ]; do files+=("$1"); shift; done ;;
148
+ --route) routes+=("$2"); shift 2 ;;
149
+ --preview) preview="${2%/}"; shift 2 ;;
150
+ --no-gate) no_gate=1; shift ;;
151
+ --humans) humans=1; shift ;;
152
+ --json) json_out="$2"; shift 2 ;;
153
+ --*) echo "factory-walk: unknown flag $1" >&2; exit 2 ;;
154
+ *) [ -z "$slug" ] && slug="$1" || { echo "factory-walk: one slug only" >&2; exit 2; }; shift ;;
155
+ esac
156
+ done
157
+ [ -n "$slug" ] || { sed -n '2,3p;42,60p' "$0" >&2; exit 2; }
158
+ [ -n "$json_out" ] || { mkdir -p "${TMPDIR:-/tmp}/one-factory-walk"; json_out="${TMPDIR:-/tmp}/one-factory-walk/$slug.json"; }
159
+ selftest="${FACTORY_WALK_SELFTEST:-0}"
160
+
161
+ if [ "${#files[@]}" -eq 0 ]; then
162
+ while IFS= read -r f; do [ -n "$f" ] && files+=("$f"); done < <(
163
+ { git diff --name-only HEAD 2>/dev/null; git ls-files --others --exclude-standard 2>/dev/null; } | sort -u)
164
+ fi
165
+
166
+ # ---------------------------------------------------------------------------
167
+ # receipt rows — jq lines, exactly the do-w4-gates.sh shape
168
+ # ---------------------------------------------------------------------------
169
+ ROWS="$(mktemp)"; trap 'rm -f "$ROWS"' EXIT
170
+ _add() { # stage status detail
171
+ [ "$selftest" = "1" ] && [ "${FACTORY_WALK_DROP_ROW:-}" = "$1" ] && return 0
172
+ jq -n --arg s "$1" --arg st "$2" --arg d "$3" '{stage:$s,status:$st,detail:$d}' >> "$ROWS"
173
+ printf ' %-9s %-6s %s\n' "$1" "$2" "$3"
174
+ }
175
+ _cmd() { # _cmd <ENV_NAME> <default...> — self-test substitution only
176
+ local v="$1"; shift
177
+ if [ "$selftest" = "1" ] && [ -n "${!v:-}" ]; then printf '%s' "${!v}"; else printf '%s' "$*"; fi
178
+ }
179
+
180
+ echo "FACTORY-WALK: $slug · ${#files[@]} file(s) · preview $preview"
181
+
182
+ # ---------------------------------------------------------------------------
183
+ # 1. size — the fork everything else hangs off
184
+ # ---------------------------------------------------------------------------
185
+ tier="UNKNOWN"
186
+ _tier_cmd="$(_cmd FACTORY_WALK_TIER_CMD bash "$S/do-tier.sh")"
187
+ _tier_out="$(printf '%s\n' "${files[@]}" | $_tier_cmd 2>/dev/null)"; _tier_rc=$?
188
+ tier="$(printf '%s' "$_tier_out" | jq -r '.tier // "UNKNOWN"' 2>/dev/null || echo UNKNOWN)"
189
+ if [ "$_tier_rc" -eq 3 ] || [ "$tier" = "UNSIZED" ]; then
190
+ _add size fail "UNSIZED — no paths to size; absence of recon must never read as simplicity (do-tier.sh exit 3)"
191
+ elif [ "$_tier_rc" -ne 0 ] || [ "$tier" = "UNKNOWN" ]; then
192
+ _add size unrun "do-tier.sh could not answer (exit $_tier_rc)"
193
+ else
194
+ _add size pass "tier=$tier"
195
+ fi
196
+
197
+ # surface — from the paths, by rule
198
+ ui=0; substrate=0; full=0; doc_only=1; pages=()
199
+ for f in "${files[@]}"; do
200
+ case "$f" in
201
+ one.ie/web/src/pages/*|one.ie/web/src/components/*|one.ie/web/src/layouts/*|*.astro) ui=1 ;;
202
+ esac
203
+ case "$f" in schema/*|*.tql) substrate=1 ;; esac
204
+ case "$f" in schema/*|packages/sdk/*|*auth*|*authority*|*roles*) full=1 ;; esac
205
+ case "$f" in *.md|.claude/*|text/*|docs/*) ;; *) doc_only=0 ;; esac
206
+ case "$f" in one.ie/web/src/pages/*.astro) pages+=("$f") ;; esac
207
+ done
208
+ [ "$tier" = "SCHEMA" ] && full=1
209
+ # derive routes from the changed pages
210
+ for pg in "${pages[@]+"${pages[@]}"}"; do
211
+ r="${pg#one.ie/web/src/pages}"; r="${r%.astro}"; r="${r%/index}"; [ -z "$r" ] && r="/"
212
+ case "$r" in *\[*) echo " route note $pg is dynamic — pass --route <concrete path> to prove it"; dyn=1; continue ;; esac
213
+ routes+=("$r")
214
+ done
215
+ dyn="${dyn:-0}"
216
+
217
+ # ---------------------------------------------------------------------------
218
+ # 2. isolate — a UI task lives in a worktree with local caches
219
+ # ---------------------------------------------------------------------------
220
+ if [ "$ui" -eq 1 ] && [ "$selftest" != "1" ]; then
221
+ _common="$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null || true)"
222
+ if [ -n "$_common" ] && [ "$(dirname "$_common")" != "$ROOT" ]; then
223
+ if bash "$S/worktree-preview.sh" check --dir "$ROOT/one.ie/web" >/dev/null 2>&1; then
224
+ _add isolate pass "linked worktree, per-entry node_modules, local .vite/.astro"
225
+ else
226
+ _add isolate fail "worktree-preview.sh check RED — see \`worktree-preview.sh check --dir $ROOT/one.ie/web\`"
227
+ fi
228
+ else
229
+ _add isolate n/a "main checkout — the human's tree; a task's build goes in a worktree (§ Concurrency)"
230
+ fi
231
+ elif [ "$ui" -eq 1 ]; then
232
+ _add isolate n/a "self-test"
233
+ else
234
+ _add isolate n/a "not a UI change"
235
+ fi
236
+
237
+ # ---------------------------------------------------------------------------
238
+ # 3. gate — the tier's lane, per folder, through the governor and the memo
239
+ # ---------------------------------------------------------------------------
240
+ lane="none"
241
+ if [ "$doc_only" -eq 1 ]; then
242
+ _add gate n/a "doc-only change — no gate by rule (do-folder.sh)"
243
+ elif [ "$no_gate" -eq 1 ]; then
244
+ _add gate unrun "--no-gate: the lane was not run (a read, never a pass)"
245
+ lane="$([ "$full" -eq 1 ] && echo full || echo fast)"
246
+ else
247
+ lane="$([ "$full" -eq 1 ] && echo full || echo fast)"
248
+ _gate_cmd="$(_cmd FACTORY_WALK_GATE_CMD "")"
249
+ if [ -n "$_gate_cmd" ]; then
250
+ $_gate_cmd "$lane" >/dev/null 2>&1; _g=$?
251
+ case "$_g" in
252
+ 0) _add gate pass "lane=$lane (stub)" ;;
253
+ 144) _add gate unrun "lane=$lane exit 144 — the gate could not run (queued out / killed); not red, not green" ;;
254
+ *) _add gate fail "lane=$lane exit $_g" ;;
255
+ esac
256
+ else
257
+ _gfail=0; _gunrun=0; _gran=0
258
+ while IFS= read -r row; do
259
+ folder="$(printf '%s' "$row" | jq -r '.folder // empty')"; [ -n "$folder" ] || continue
260
+ [ -d "$ROOT/$folder" ] || { _add gate unrun "$folder: not on disk"; _gunrun=1; continue; }
261
+ _log="$(mktemp)"
262
+ if jq -e '.scripts["verify:fast"]' "$ROOT/$folder/package.json" >/dev/null 2>&1; then
263
+ ( cd "$ROOT/$folder" && FULL_VERIFY="$( [ "$lane" = full ] && echo 1 || echo 0 )" bun run verify:fast ) > "$_log" 2>&1; _g=$?
264
+ _lane_ran="$lane"
265
+ elif jq -e '.scripts.test' "$ROOT/$folder/package.json" >/dev/null 2>&1; then
266
+ ( cd "$ROOT/$folder" && bun run test ) > "$_log" 2>&1; _g=$?
267
+ _lane_ran="test"; lane="test"
268
+ else
269
+ _add gate unrun "$folder: no verify:fast or test script"; _gunrun=1; rm -f "$_log"; continue
270
+ fi
271
+ _gran=1
272
+ case "$_g" in
273
+ 0) _add gate pass "$folder lane=$_lane_ran — $(grep -o 'PASS ([^)]*)\|cache HIT[^—]*\|typecheck: 0 errors' "$_log" | tail -1)" ;;
274
+ 144) _add gate unrun "$folder lane=$_lane_ran exit 144 — could not run (queued out or killed); log $_log"; _gunrun=1 ;;
275
+ *) _add gate fail "$folder lane=$_lane_ran exit $_g — $(grep -m1 'FAILED\|FAIL\|error' "$_log" | cut -c1-120); log $_log" ; _gfail=1 ;;
276
+ esac
277
+ [ "$_g" -eq 0 ] && rm -f "$_log"
278
+ done < <(printf '%s\n' "${files[@]}" | bash "$S/do-folder.sh" 2>/dev/null | jq -c 'select(.folder != null)')
279
+ [ "$_gran" -eq 0 ] && [ "$_gfail" -eq 0 ] && [ "$_gunrun" -eq 0 ] && _add gate unrun "no folder mapped — do-folder.sh answered nothing for these paths"
280
+ fi
281
+ fi
282
+
283
+ # ---------------------------------------------------------------------------
284
+ # 4. prove — routes with the landing rule, or the slug's agents walk
285
+ # ---------------------------------------------------------------------------
286
+ if [ "$ui" -eq 1 ]; then
287
+ if [ "${#routes[@]}" -eq 0 ]; then
288
+ _add prove unrun "UI change with no provable route$([ "$dyn" = 1 ] && echo ' (dynamic page)') — pass --route </path>"
289
+ else
290
+ _prove_cmd="$(_cmd FACTORY_WALK_PROVE_CMD bash "$S/do-prove.sh")"
291
+ _rargs=(); for r in "${routes[@]}"; do _rargs+=(--route "$r"); done
292
+ _pout="$(PROVE_BASE_URL="$preview" $_prove_cmd "${_rargs[@]}" 2>&1)"; _p=$?
293
+ case "$_pout" in
294
+ *"PROVE: skipped"*|*"PROVE: no changes"*) _add prove unrun "do-prove.sh skipped — no reachable environment at $preview (exit 0 there is NOT a pass)" ;;
295
+ *) if [ "$_p" -eq 0 ]; then _add prove pass "${#routes[@]} route(s) at $preview, landing rule held"
296
+ else _add prove fail "$(printf '%s\n' "$_pout" | grep -m1 'PROVE: FAIL' | cut -c1-140)"; fi ;;
297
+ esac
298
+ fi
299
+ else
300
+ _prove_cmd="$(_cmd FACTORY_WALK_PROVE_CMD "")"
301
+ if [ -n "$_prove_cmd" ]; then
302
+ $_prove_cmd >/dev/null 2>&1; _p=$?
303
+ case "$_p" in 0) _add prove pass "agents walk (stub)";; 3) _add prove unrun "walk could not run (stub 3)";; *) _add prove fail "walk failed (stub $_p)";; esac
304
+ elif [ -f "$ROOT/text/$slug-agents.md" ]; then
305
+ _wout="$(WALK_BASE_URL="$preview" bash "$S/do-walk.sh" "$slug" --agents --strict 2>&1)"; _p=$?
306
+ case "$_p" in
307
+ 0) _add prove pass "$(printf '%s\n' "$_wout" | grep -m1 '^WALK: pass')" ;;
308
+ 3) _add prove unrun "$(printf '%s\n' "$_wout" | grep -m1 'could not be run' | cut -c1-140)" ;;
309
+ *) _add prove fail "$(printf '%s\n' "$_wout" | grep -m1 '^WALK: FAIL' | cut -c1-140)" ;;
310
+ esac
311
+ else
312
+ _add prove unrun "no text/$slug-agents.md — write the walk (one stop per deliverable, expect_cmd = the accept:)"
313
+ fi
314
+ fi
315
+
316
+ # ---------------------------------------------------------------------------
317
+ # 5. human — UI only: the link, the stops, and how the verdict lands
318
+ # ---------------------------------------------------------------------------
319
+ if [ "$ui" -eq 1 ]; then
320
+ hdoc="$ROOT/text/$slug-humans.md"
321
+ echo
322
+ echo "── HUMAN WALK — $slug ──────────────────────────────────────────────"
323
+ echo " open: $preview${routes[0]:-/}"
324
+ if [ -f "$hdoc" ]; then
325
+ WALK_BASE_URL="$preview" bash "$S/do-walk.sh" "$slug" --humans --list 2>/dev/null | sed -n '2,$p' | sed 's/^/ /'
326
+ else
327
+ i=0
328
+ for r in "${routes[@]+"${routes[@]}"}"; do
329
+ i=$((i+1)); echo " $i. $preview$r"
330
+ echo " · the page you land on IS $r (a bounce to /signin proves nothing — sign in first)"
331
+ echo " · the change this task describes is visible and behaves; no console errors (⌥⌘I)"
332
+ done
333
+ [ "$i" -eq 0 ] && echo " (no route derived — open the preview and the page this task changed)"
334
+ echo " No text/$slug-humans.md yet — one \`\`\`walk block per stop makes this list yours."
335
+ fi
336
+ echo " record: bash .claude/scripts/factory-walk.sh $slug --humans # asks y/n per stop on a tty"
337
+ echo " or: signal(\"tasks:tag\", { tid, tags: [\"verdict:ok\"] }) # or verdict:red — lands on the task"
338
+ echo "────────────────────────────────────────────────────────────────────"
339
+ echo
340
+ if [ "$humans" -eq 1 ] && [ -t 0 ] && [ -f "$hdoc" ]; then
341
+ if WALK_BASE_URL="$preview" bash "$S/do-walk.sh" "$slug" --humans; then _add human pass "a person walked every stop and said ok"
342
+ else _add human fail "a person rejected a stop"; fi
343
+ elif [ "$humans" -eq 1 ] && [ -t 0 ]; then
344
+ printf ' verified in the browser? [y/n] '; read -r v || v=n
345
+ case "$v" in y|Y|yes) _add human pass "a person looked and said ok" ;; *) _add human fail "a person looked and said red" ;; esac
346
+ else
347
+ _add human unrun "PENDING — a person has not looked; the link block above is the instruction"
348
+ fi
349
+ else
350
+ _add human n/a "not a UI change"
351
+ fi
352
+
353
+ # ---------------------------------------------------------------------------
354
+ # 6. substrate — SCHEMA tier: the ladder is live, and a 3 is cannot-run
355
+ # ---------------------------------------------------------------------------
356
+ if [ "$substrate" -eq 1 ] || [ "$tier" = "SCHEMA" ]; then
357
+ _sub_cmd="$(_cmd FACTORY_WALK_SUBSTRATE_CMD bash "$S/factory-check.sh" schema-live)"
358
+ $_sub_cmd >/dev/null 2>&1; _s=$?
359
+ case "$_s" in
360
+ 0) _add substrate pass "factory-check.sh schema-live" ;;
361
+ 3) _add substrate unrun "factory-check.sh could not reach the substrate (3) — nothing proven either way" ;;
362
+ *) _add substrate fail "factory-check.sh schema-live RED (exit $_s)" ;;
363
+ esac
364
+ else
365
+ _add substrate n/a "not a schema change"
366
+ fi
367
+
368
+ # ---------------------------------------------------------------------------
369
+ # verdict — zero fails AND zero unruns, over exactly the six rows
370
+ # ---------------------------------------------------------------------------
371
+ fails=$(jq -s '[.[]|select(.status=="fail")]|length' "$ROWS")
372
+ unruns=$(jq -s '[.[]|select(.status=="unrun")]|length' "$ROWS")
373
+ missing=0
374
+ for st in size isolate gate prove human substrate; do
375
+ if [ "$(jq -r --arg s "$st" 'select(.stage==$s)|.stage' "$ROWS" 2>/dev/null | head -1)" != "$st" ]; then
376
+ echo " $st unrun no row for $st — an absent row is the fail-open trap in data form"
377
+ missing=$((missing+1))
378
+ fi
379
+ done
380
+ unruns=$((unruns + missing))
381
+ if [ "$fails" -gt 0 ]; then verdict=red; rc=1
382
+ elif [ "$unruns" -gt 0 ]; then verdict=unrun; rc=3
383
+ else verdict=ok; rc=0; fi
384
+
385
+ jq -n --arg slug "$slug" --arg tier "$tier" --arg lane "$lane" --arg verdict "$verdict" \
386
+ --arg preview "$preview" --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
387
+ --argjson ui "$ui" --argjson fails "$fails" --argjson unruns "$unruns" \
388
+ --slurpfile rows "$ROWS" \
389
+ '{slug:$slug, ts:$ts, tier:$tier, lane:$lane, verdict:$verdict, ui:($ui==1), preview:$preview,
390
+ fails:$fails, unruns:$unruns, stages:$rows}' > "$json_out" 2>/dev/null \
391
+ || jq -n --arg slug "$slug" --arg verdict "$verdict" --arg lane "$lane" --arg tier "$tier" '{slug:$slug,verdict:$verdict,lane:$lane,tier:$tier,stages:[]}' > "$json_out"
392
+
393
+ echo
394
+ echo "FACTORY-WALK: $verdict — $fails failed · $unruns not proven · tier $tier · receipt $json_out"
395
+ echo "tasks:tag lane:$lane verdict:$verdict # what the executor writes on the task (T4)"
396
+ exit "$rc"
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ # factory-width.sh — how many factory tasks this box may build AT ONCE, measured.
3
+ #
4
+ # manifest: portable
5
+ #
6
+ # The Workflow runtime caps a script's concurrency at min(16, cpus-2) — CORES,
7
+ # which were never the binding constraint here (10 cores authorised 8 worktrees
8
+ # on a 24 GB box; 8 cycles is ~16 GB of gates). Two things bind first, and this
9
+ # prints the smaller so the launcher passes it as args.width:
10
+ #
11
+ # memory gate_headroom — MB actually free (vm_stat: free + purgeable + cold
12
+ # cache, never memory_pressure's inflated figure) ÷ the measured price
13
+ # of one cycle. 99 means "the sensor is unreadable — do not constrain".
14
+ # ports the preview range minus what a neighbour already holds. One
15
+ # server per UI task; 4321 is the human's and is never counted.
16
+ #
17
+ # Prints ONE JSON line: {"width":N,"memory":N,"ports":N,"price_mb":N}. Never 0 —
18
+ # work slows, it never stops (the governor's own floor). Exit 0 always; a reader
19
+ # that cannot get a number gets 1, printed, not a crash.
20
+ #
21
+ # bash .claude/scripts/factory-width.sh # {"width":1,...}
22
+ # bash .claude/scripts/factory-width.sh --explain # the same, with the why
23
+ set -uo pipefail
24
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
25
+ LIB="$ROOT/.claude/scripts/lib/govern.sh"
26
+ PORT_MIN="${PREVIEW_PORT_MIN:-4322}"; PORT_MAX="${PREVIEW_PORT_MAX:-4329}"
27
+
28
+ mem=99; price=0
29
+ if [ -f "$LIB" ]; then
30
+ # shellcheck source=lib/govern.sh
31
+ . "$LIB"
32
+ mem="$(gate_headroom 2>/dev/null || echo 99)"
33
+ price="$(gate_price_mb 2>/dev/null || echo 0)"
34
+ fi
35
+ case "$mem" in ''|*[!0-9]*) mem=99 ;; esac
36
+
37
+ ports=0
38
+ if command -v lsof >/dev/null 2>&1; then
39
+ for ((p = PORT_MIN; p <= PORT_MAX; p++)); do
40
+ lsof -nP -iTCP:"$p" -sTCP:LISTEN >/dev/null 2>&1 || ports=$((ports + 1))
41
+ done
42
+ else
43
+ ports=$((PORT_MAX - PORT_MIN + 1)) # no lsof: cannot see neighbours; the range is the cap
44
+ fi
45
+ [ "$ports" -lt 1 ] && ports=1
46
+
47
+ width="$mem"; [ "$ports" -lt "$width" ] && width="$ports"
48
+ [ "$width" -lt 1 ] && width=1
49
+ # 99 is "unconstrained by memory"; the ports then bind, and they always exist.
50
+ [ "$width" -gt 16 ] && width=16
51
+
52
+ printf '{"width":%s,"memory":%s,"ports":%s,"price_mb":%s}\n' "$width" "$mem" "$ports" "$price"
53
+ if [ "${1:-}" = "--explain" ]; then
54
+ echo "width $width = min(memory funds $mem cycle(s) at ${price} MB, $ports free preview port(s) in $PORT_MIN-$PORT_MAX)" >&2
55
+ [ "$mem" = 99 ] && echo "memory sensor unreadable (99) — not constraining; ports bind" >&2
56
+ fi
57
+ exit 0
@@ -4,7 +4,8 @@
4
4
  # Runs every hour via ie.one.fade-toxic.plist:
5
5
  # 1. --sync-closes reconcile close-derived weight from learnings.md (idempotent)
6
6
  # 2. --fade --rate decay LIVE weight (strength cools, resistance heals 2× faster)
7
- # 3. regenerate todo.md (same shape as the sync-priority-todo hook one source of truth)
7
+ # 3. regenerate todo.md (this loop is its ONLY writer since 2026-09-05the
8
+ # per-edit/Stop/SessionStart hook that also wrote it cost 5.7s a fire)
8
9
  # 4. append a one-line toxic report to the log
9
10
  #
10
11
  # NON-DESTRUCTIVE by contract: fades + reports only. It NEVER deletes a todo, edits a plan,
@@ -31,7 +32,7 @@ TS=$(date '+%Y-%m-%d %H:%M')
31
32
  python3 "$RANK" --sync-closes >/dev/null 2>&1 || true
32
33
  FADED_LINE=$(python3 "$RANK" --fade --rate "$FADE_RATE" 2>/dev/null | head -1 || true)
33
34
 
34
- # 3 — regenerate todo.md (identical wrapper to .claude/hooks/sync-priority-todo.sh).
35
+ # 3 — regenerate todo.md. `do-rank.py` is live for anything that needs it fresher than hourly.
35
36
  QUEUE_OUT=$(python3 "$RANK" 2>/dev/null || true)
36
37
  if [ -n "$QUEUE_OUT" ]; then
37
38
  {
@@ -41,7 +42,7 @@ if [ -n "$QUEUE_OUT" ]; then
41
42
  echo "$QUEUE_OUT"
42
43
  echo '```'
43
44
  echo
44
- echo "> Auto-generated by do-rank.py (hourly fade-toxic loop + session hooks)."
45
+ echo "> Auto-generated by do-rank.py (hourly fade-toxic loop). Fresher: python3 .claude/scripts/do-rank.py"
45
46
  echo "> Run \`do-rank.py --toxic\` for the demoted list, \`--frontier\` for what's next."
46
47
  } > "$ROOT/todo.md"
47
48
  fi
@@ -0,0 +1,44 @@
1
+ You are the hand. Build task task:01a07591e5c350a306c93933: "In pages/components add a button > add this component to a page — it shows a list of pages in the workspace, you select the page to add the component, then edit the page or continue adding components".
2
+ Notes from the board:
3
+ SPEC (the title is the whole spec; the row's notes were empty — this brief was assembled by the launching session from a real read of the tree on 2026-09-06, every file:line verified).
4
+
5
+ WHAT TO BUILD
6
+ On /components (the Component Library gallery, ComponentBrowser.tsx), give every block an 'Add to page' affordance. Clicking it opens a picker listing the workspace's pages; choosing one appends that block to that page; after the write, offer two exits — 'Edit page' (link to the page editor) and 'Continue adding components' (dismiss the picker, stay in the gallery).
7
+
8
+ BOTH RECEIVERS ALREADY EXIST — DO NOT WRITE A NEW ONE, DO NOT ADD AN API ROUTE.
9
+ - pages:list — one.ie/web/src/lib/resolvers/pages.ts:670. Request { slug }. Returns { pages: [{ slug, title, status, updated_at }] } ordered by updated_at DESC. This is the picker's list. Returns { pages: [] } on any auth denial.
10
+ - pages:add-block — one.ie/web/src/lib/resolvers/pages.ts:630. Request { slug, page, section: { component, props }, atIndex? }. atIndex omitted = append to end, which is what this feature wants. It already runs normalizeBlockProps(BLOCK_SCHEMA, ...) server-side, so send the block's defaultProps verbatim — do NOT normalize on the client.
11
+
12
+ EXACT CALL-SITE PRECEDENT — copy this shape:
13
+ one.ie/web/src/components/chat/BlockFrame.tsx:112-125. It is the same feature one surface over ('Add to page' from chat). Note its gate: `const canAddToPage = addToPage && !!slug && !!page`, its pending/added state machine, and `emitClick('ui:chat:add-to-page', { component })` before the call. Use `ask` from '@/lib/in/ask'. Emit a sibling signal name, e.g. 'ui:component-browser:add-to-page'.
14
+
15
+ THE ONE DESIGN DECISION, ALREADY SETTLED — DO NOT RE-OPEN IT.
16
+ /components is a genuinely PUBLIC route. pages/components.astro mounts <ComponentBrowser client:only="react" /> with a plain Layout and passes NO props, and ComponentBrowser takes none. It is NOT one of the slugless console pages: those (see pages/tasks.astro) call resolveRoom() and rewrite to /u/[slug]/..., and components.astro does neither. There is also no workspace-scoped mount — ComponentBrowser appears only in pages/components.astro and is referenced by components/puck/BlockFinder.tsx.
17
+
18
+ So: derive the workspace slug SERVER-SIDE in pages/components.astro (from Astro.locals.workspaceContext?.workspace ?? Astro.locals.slug — verify which is right against how another workspace-aware page reads it) and pass it down as an optional `slug` prop to ComponentBrowser. When there is no session there is no slug, and the 'Add to page' button MUST NOT RENDER — same gate shape as BlockFrame's canAddToPage. An anonymous visitor keeps exactly today's public gallery, unchanged. Do NOT invent a client-side workspace read, do NOT add a new route, do NOT make /components members-only.
19
+
20
+ WHERE THE BUTTON GOES
21
+ ComponentBrowser.tsx has two surfaces for a block: the grid card (article, ~line 250) and PreviewModal (~line 143). Put the affordance in the PreviewModal footer at minimum — the modal is the block's detail view and already has a footer row. A card-level quick-add is welcome but must not swallow the card's existing onClick (which opens the preview).
22
+
23
+ DONE LOOKS LIKE
24
+ 1. Signed in, /components → open any block → 'Add to page' → picker lists this workspace's real pages → pick one → success state → 'Edit page' and 'Continue adding components' both present and both work.
25
+ 2. The block actually lands: re-open that page in the editor and the block is there, rendered, at the end.
26
+ 3. Signed out, /components renders exactly as before with no 'Add to page' button anywhere.
27
+ 4. `cd one.ie/web && bun run verify:fast` green. Add a unit test for the picker gate (no slug ⇒ no button).
28
+
29
+ CONSTRAINTS
30
+ - 6-token palette only (hook:design-check blocks otherwise) — follow the existing var(--color-border) / text-font / bg-foreground idiom in this very file.
31
+ - Do not regress the IntersectionObserver lazy-mount or the PreviewErrorBoundary; the gallery renders ~376 blocks.
32
+ - Edit in a worktree, never on shared main.
33
+
34
+ RULES, each one a recorded outage (text/factory-do.md § Concurrency):
35
+ 1. Work in YOUR OWN worktree, never the shared main tree. Slug: task-task:01a07591e5c350a306c93933.
36
+ UI task — run: bash .claude/scripts/worktree-preview.sh up task-task:01a07591e5c350a306c93933 --route /components
37
+ It cuts the worktree from main, links node_modules CONTENTS (never the dir), copies .env, allocates a port (never sweeps), and hands you a URL only after 3 consecutive 200s. Use the path and URL it prints.
38
+ 2. NEVER `bun install` inside the worktree. NEVER symlink node_modules itself.
39
+ 3. Make the change the task describes — minimal, in the worktree. Write or extend the test that names the concrete failure this change prevents (input → wrong result). No test that guards a shape.
40
+ 4. Run only YOUR test file(s) in the worktree: cd <wt>/one.ie/web && bunx vitest related --run <files> — through bash ../../.claude/scripts/gate-run.sh doer -- … . Do NOT run the suite; factory-walk.sh runs the tier's lane after you.
41
+ 5. Commit in the worktree by EXPLICIT PATH (git add <file>...), message: "task(task:01a07591e5c350a306c93933): <what was measured>". Never git add -A.
42
+ 6. Report files from `git status --porcelain` / `git show --stat` in the worktree, never from memory.
43
+ 7. THE BRAIN AND THE EDGE (root CLAUDE.md, LOCKED): if the change adds or touches a receiver, it reads KV / BrainDO JSON — never typedbQuery — and writes to the brain fire-and-forget or via sync/. resolvers/factory.ts is the one exception. Prove the door: bash .claude/scripts/signal-watch.sh --local (with your preview as SIGNAL_WATCH_LOCAL) — a door over budget or reaching for the brain is red, and red closes nothing.
44
+ If the task cannot be done as written, do the part that can, say exactly what you left out and why, and return committed:false rather than inventing scope.
@@ -0,0 +1,5 @@
1
+ ---
2
+ deliverables:
3
+ - item: "xyzzy-triage-dupe unique deliverable phrase"
4
+ accept: "true"
5
+ ---