@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,445 @@
1
+ #!/usr/bin/env bash
2
+ # verify-fast.sh — the DEV lane. Run from a package folder (one.ie/web, channels).
3
+ #
4
+ # Full `bun run verify` is the REVIEW/DEPLOY gate (deploy.sh, /close, --full).
5
+ # During a /do cycle we only need: does the code still typecheck, and do the
6
+ # tests that actually cover what I touched still pass?
7
+ #
8
+ # fast = sdk build + tsc --noEmit + vitest related(<changed>) + PINNED always-run
9
+ #
10
+ # Three rules learned the hard way:
11
+ # 1. EMPTY DIFF ⇒ RUN THE FULL SUITE. Never ⇒ pass. A fast lane that goes
12
+ # green because it selected zero tests is worse than the slow one.
13
+ # 2. PINS. `vitest related` follows imports; config/parity/boundary suites
14
+ # import nothing from what they guard (cron-wrangler-parity reads a .toml).
15
+ # Those are always run — ~seconds, and they are the class of gate that
16
+ # historically went dark.
17
+ # 3. Everything heavy goes through gate-run.sh (machine governor + load-guard).
18
+ # This was ASSERTED here and false until 2026-09-07: tsc-cached.sh was called
19
+ # outside the governor on purpose (see below), so a typecheck escaped the
20
+ # slot cap entirely. It now takes its own slot internally.
21
+ #
22
+ # FULL_VERIFY=1 makes this an alias for `bun run verify`.
23
+ set -euo pipefail
24
+
25
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
26
+ GATE="$ROOT/.claude/scripts/gate-run.sh"
27
+ FOLDER="$(pwd)"
28
+ BASE_REF="${DO_BASE_REF:-main}"
29
+
30
+ # The deferred-pin ledger. It must NOT live under $ROOT: inside a /do cycle $ROOT
31
+ # is .do-worktrees/<slug>, and do-auto.sh --gc deletes that checkout -- so the
32
+ # debt would evaporate with the worktree it was owed from, and the
33
+ # outstanding-debt-on-main backstop would never see it. A deferral that can
34
+ # vanish is a deleted gate wearing a different word, which is the exact thing the
35
+ # ledger exists to prevent. Machine-keyed, like the gate locks, the recon cache
36
+ # and the test memo, so every worktree and session reads ONE ledger.
37
+ DEBT_FILE="${VERIFY_FAST_DEBT_FILE:-${TMPDIR:-/tmp}/one-verify-fast-debt}"
38
+
39
+ # --- 0. generated-artifact drift ---------------------------------------------
40
+ # `blocks-manifest.mjs` builds THREE COMMITTED artifacts from one registry:
41
+ # one.ie/web/src/lib/puck/block-manifest.json what tooling reads
42
+ # channels/src/generated/block-enum.ts what the model is told
43
+ # one.ie/web/src/lib/puck/block-schema.generated.ts what the browser
44
+ # normalises against
45
+ # Until 2026-09-09 NOTHING ran it: `grep -c blocks-manifest verify-fast.sh
46
+ # deploy.sh` was 0 and 0, and no test shells out to it. Three committed files
47
+ # behind an ungated generator are three files that rot silently -- the registry
48
+ # moves, the artifacts do not, and the first symptom is a model being told about
49
+ # a block the browser cannot render.
50
+ #
51
+ # IT IS A STEP, NOT A PIN, and that distinction is the entire gate. `PINS` below
52
+ # are vitest POSITIONAL SUBSTRING filters. Adding `blocks-manifest` there would
53
+ # select zero test files, the other pins would still match, the pinned gate would
54
+ # exit 0 -- and `grep -q blocks-manifest .claude/scripts/verify-fast.sh` would be
55
+ # TRUE. A green grep over a check that never ran is worse than no gate at all.
56
+ #
57
+ # It runs BEFORE the FULL_VERIFY branch on purpose: every lane below has an exit
58
+ # of its own (FULL_VERIFY exits at the top; an empty selection `exec`s the full
59
+ # suite; a do/* branch exits 0 after deferring the pins), so anywhere lower is a
60
+ # lane that can skip it. Repo-scoped, not folder-scoped -- one of the three
61
+ # artifacts lives in channels/ and one in one.ie/web/, so both folders check both.
62
+ #
63
+ # NOT wrapped in gate-run.sh: one node process, ~2.5s of CPU. Most of its ~16s
64
+ # wall is `bunx tsx` resolving a dependency this repo does not vendor
65
+ # (`one.ie/web/node_modules/.bin/tsx` does not exist -- measured 2026-09-09), and
66
+ # holding a machine-wide slot through a network round-trip is the anti-pattern
67
+ # the tsc-cached note further down records.
68
+ #
69
+ # Capture-then-branch, per rule 3 at the top of this file: `set -e` would kill the
70
+ # script with no message, and `node ... | grep` is the PIPESTATUS scar recorded at
71
+ # the vitest call below. `--check` writes `ok (no drift)` to stdout and `drift
72
+ # detected` to stderr; both are shown. A rc it cannot explain is still RED --
73
+ # an unrun check is not a passing check.
74
+ # MEMOISED since 2026-09-09. The raw --check is ~7s WARM (measured 8.19s then
75
+ # 6.98s back-to-back on this box), and this lane runs on every edit, so a
76
+ # doc-only or test-only cycle was paying 7s for a registry nobody touched.
77
+ # blocks-manifest-cached.sh keys on the whole of one.ie/web/src -- a superset
78
+ # of the check's declared inputs AND of the three artifacts it compares --
79
+ # so any src edit still runs it. Only a PASS is stamped; drift is never
80
+ # memoised. deploy.sh Step 0.5 deliberately keeps calling the RAW check: the
81
+ # ship gate runs once and should not depend on a stamp. BLOCKS_MANIFEST_FORCE=1
82
+ # bypasses the memo; --self-test proves the miss.
83
+ _bm_out="$(mktemp)"
84
+ set +e
85
+ bash "$ROOT/.claude/scripts/blocks-manifest-cached.sh" > "$_bm_out" 2>&1
86
+ _bm_rc=$?
87
+ set -e
88
+ cat "$_bm_out"; rm -f "$_bm_out"
89
+ if [ "$_bm_rc" -ne 0 ]; then
90
+ echo "[verify-fast] block manifest DRIFT (or --check could not run) - rc=$_bm_rc" >&2
91
+ echo "[verify-fast] regenerate and commit all three artifacts:" >&2
92
+ echo "[verify-fast] node .claude/scripts/blocks-manifest.mjs" >&2
93
+ exit 1
94
+ fi
95
+
96
+ if [ "${FULL_VERIFY:-0}" = "1" ]; then
97
+ echo "[verify-fast] FULL_VERIFY=1 — running the full gate"
98
+ # The close gate is where deferred pins come due. The full suite is a superset
99
+ # of every pin, so reaching here GREEN is the payment.
100
+ #
101
+ # Order is load-bearing, and the first version got it wrong: it cleared the
102
+ # ledger and THEN ran the suite, so a RED close wiped the debt on its way to
103
+ # failing and the deferred pins would never run again. Proven wrong by probe,
104
+ # not by reading: a scripted `verify` of `exit 1` cleared the ledger.
105
+ # The debt is settled by a PASS, never by an attempt. `exec` is gone for the
106
+ # same reason -- it replaces the process, leaving no line after the run to
107
+ # settle from. `set -e` is suspended around the run so a red suite reaches the
108
+ # settle logic instead of killing the script first.
109
+ _full_st=0
110
+ set +e
111
+ if jq -e '.scripts.verify' package.json >/dev/null 2>&1; then
112
+ bun run verify; _full_st=$?
113
+ else
114
+ # channels has no `verify` script; its full gate is typecheck + the whole suite.
115
+ bash "$ROOT/.claude/scripts/sdk-build-cached.sh" >/dev/null
116
+ bun run typecheck && bun run test; _full_st=$?
117
+ fi
118
+ set -e
119
+ if [ -f "$DEBT_FILE" ]; then
120
+ if [ "$_full_st" -eq 0 ]; then
121
+ echo "[verify-fast] full gate GREEN — clearing $(wc -l < "$DEBT_FILE" | tr -d ' ') deferred pin run(s)"
122
+ rm -f "$DEBT_FILE"
123
+ else
124
+ echo "[verify-fast] full gate RED — deferred pins stay owed" >&2
125
+ fi
126
+ fi
127
+ exit "$_full_st"
128
+ fi
129
+
130
+ # Pinned suites: run on every fast pass regardless of the diff.
131
+ #
132
+ # `portable` and `design-dock` were added 2026-09-05, after both went red at the
133
+ # ship gate on a change whose fast pass was green. They belong here for the
134
+ # reason the whole list exists — they import NOTHING from what they guard:
135
+ #
136
+ # astro-portable reads the pages DIRECTORY. Adding a root `*.astro` puts it in
137
+ # neither ASTRO_PORTABLE nor ASTRO_NON_PORTABLE, and the new
138
+ # page then 404s on every tenant host. No import graph reaches
139
+ # a file that was just created, so `vitest related` cannot ever
140
+ # select this — the one edit that trips it is invisible to it.
141
+ # design-dock reads `Layout.astro` and every page as STRINGS, to hold "one
142
+ # BrandingSection per document". An edit to Layout.astro selects
143
+ # nothing that imports Layout.astro.
144
+ # render-sites reads MessageList/ShowcaseMessageList as STRINGS to hold that
145
+ # both approval states route to the approval card. Added
146
+ # 2026-09-06, after both sites gated on `approval-requested`
147
+ # alone: the answered card — the whole celebration — was
148
+ # unreachable through the wire, and the component suite covering
149
+ # that state was green the entire time, because it handed the
150
+ # card a state no render site ever passes it.
151
+ # panel-tabs reads `PanelPanes.tsx` as a STRING to hold that every tab a
152
+ # person-panel strip can emit resolves to a rendered pane. Both import only
153
+ # `panels/tabs`, so editing ONLY PanelPanes -- which is where a
154
+ # pane is DELETED -- selects neither. Measured 2026-09-09:
155
+ # with `pane('task', …)` removed, all 79 tests passed.
156
+ # ad-flow-panel reads `pages/ad/index.astro` and `AdFunnel.tsx` as STRINGS.
157
+ # Same class as design-dock: nothing imports an .astro page.
158
+ #
159
+ # Deliberately NOT pinned: `page-chat-meta`, which imports everything it guards,
160
+ # so `vitest related` already reaches it. A pin for a suite the import graph
161
+ # covers is cost with no coverage.
162
+ #
163
+ # These three cost SECONDS, not the milliseconds the two above cost: measured
164
+ # 2026-09-09, 2.34s for 49 tests, most of it `ad-flow-panel.test.tsx` building a
165
+ # jsdom to render AdFlowPanel. That is the price of the only check that can see
166
+ # a deleted pane, and it is paid on every fast lane.
167
+ #
168
+ # Cost measured on the run that motivated this: 7ms and 46ms. The failure they
169
+ # catch costs an 18-minute deploy gate and a red ship.
170
+ PINS_DEFAULT='parity|wrangler|boundary|authority|roles|signals-parity|receivers|portable|design-dock|render-sites|panel-tabs|person-panel|ad-flow-panel'
171
+ PINS="${VERIFY_FAST_PINS:-$PINS_DEFAULT}"
172
+
173
+ # --- changed files: committed-since-base UNION uncommitted -------------------
174
+ # The BASE the committed half of the diff is measured from.
175
+ #
176
+ # Default `main` is right for a one-off check and wrong for a /do plan. On branch
177
+ # do/<slug> the committed term is "everything this PLAN has landed", so cycle 5
178
+ # re-selects cycles 1-4's files, cycle 6 re-selects five cycles' worth, and the
179
+ # fast lane converges on the full suite exactly as the plan gets long. Measured:
180
+ # 23 changed files after 3 commits, growing monotonically.
181
+ #
182
+ # Those earlier cycles were each verified green when they landed, and the plan's
183
+ # LAST cycle takes the full lane anyway (do-engine escalates on final-batch
184
+ # membership), so integration is still gated -- once, at the end, instead of once
185
+ # per cycle.
186
+ #
187
+ # The cycle's own edits are UNCOMMITTED at W4 time (W4 verifies, then commits),
188
+ # so the uncommitted term below already captures them exactly. Moving the base to
189
+ # the previous cycle's commit just stops the committed term re-adding work that
190
+ # is already proven. Derived from the commit message do-engine writes -- no
191
+ # plumbing, no ref passed through an LLM. Override with VERIFY_FAST_SINCE.
192
+ cycle_base=""
193
+ if [ -n "${VERIFY_FAST_SINCE:-}" ]; then
194
+ cycle_base="$VERIFY_FAST_SINCE"
195
+ elif git rev-parse --git-dir >/dev/null 2>&1; then
196
+ _br="$(git -C "$ROOT" branch --show-current 2>/dev/null || true)"
197
+ case "$_br" in
198
+ do/*)
199
+ _slug="${_br#do/}"
200
+ # do-engine's W4 prompt asks for `do(<slug>): C<n>`, but the agent writing
201
+ # the commit does not always obey it -- the two live plans wrote
202
+ # `do(lifecycle): C1` and `lifecycle-money C2: ...` respectively. Match
203
+ # either shape, and NEVER fall back to main: falling back is what re-selects
204
+ # the whole plan, which is the thing being fixed. With no boundary commit
205
+ # found, HEAD is the correct base -- every commit on this branch is already
206
+ # verified work, and this cycle's own edits are uncommitted at W4 time.
207
+ cycle_base="$(git -C "$ROOT" log -1 --format=%H \
208
+ --grep="^do(${_slug}): C[0-9]" --grep="^${_slug} C[0-9]" 2>/dev/null || true)"
209
+ [ -n "$cycle_base" ] || cycle_base="$(git -C "$ROOT" rev-parse HEAD 2>/dev/null || true)"
210
+ ;;
211
+ esac
212
+ fi
213
+ if [ -n "$cycle_base" ]; then
214
+ BASE_REF="$cycle_base"
215
+ echo "[verify-fast] cycle base ${cycle_base:0:9} (previous cycle) - earlier cycles were verified when they landed; the plan's last cycle takes the full lane"
216
+ fi
217
+
218
+ changed=""
219
+ if git rev-parse --git-dir >/dev/null 2>&1; then
220
+ # -C "$ROOT" is load-bearing: run from a subdir, git prints paths relative to
221
+ # CWD, and the repo-relative prefix strip below then matches nothing — which
222
+ # silently drops every fast run into the full-suite fallback.
223
+ mb="$(git -C "$ROOT" merge-base HEAD "$BASE_REF" 2>/dev/null || true)"
224
+ { [ -n "$mb" ] && git -C "$ROOT" diff --name-only "$mb"...HEAD 2>/dev/null || true
225
+ git -C "$ROOT" diff --name-only HEAD 2>/dev/null || true
226
+ git -C "$ROOT" ls-files --others --exclude-standard 2>/dev/null || true
227
+ } > /tmp/.vf-changed.$$ || true
228
+ changed="$(sort -u /tmp/.vf-changed.$$ | grep -E '\.(ts|tsx|astro|js|mjs)$' || true)"
229
+ rm -f /tmp/.vf-changed.$$
230
+ fi
231
+
232
+ # Paths are repo-relative; vitest wants them relative to the package folder.
233
+ rel="${FOLDER#"$ROOT"/}"
234
+ scoped="$(printf '%s\n' "$changed" | sed -n "s#^${rel}/##p" || true)"
235
+
236
+ echo "[verify-fast] $rel — $(printf '%s\n' "$scoped" | grep -c . || true) changed file(s)"
237
+
238
+ # --- 1. SDK build (types for tsc) + typecheck --------------------------------
239
+ # Generated-but-gitignored inputs tsc resolves as modules. Missing => a wall of
240
+ # phantom TS2307s on any tree that has not run dev/build yet.
241
+ if [ -f package.json ] && grep -q '"gen:page-modified"' package.json; then
242
+ bun run gen:page-modified >/dev/null || true
243
+ fi
244
+ # Through the fingerprint cache. The raw build is 2.0s of the 2.6s total
245
+ # deterministic cost of a cycle -- 78% -- and on an unchanged src it reproduces
246
+ # byte-identical dist (measured: same digest before and after). Skip it.
247
+ bash "$ROOT/.claude/scripts/sdk-build-cached.sh" || { echo "[verify-fast] sdk build FAILED" >&2; exit 1; }
248
+ # Typecheck through the SHARED fingerprint cache, not a fresh tsc every time.
249
+ #
250
+ # tsc-cached.sh keys on (folder, tree-fingerprint) using do-reconcile's exact key
251
+ # format, so a reconcile and N cycle gates on one unchanged tree cost ONE tsc
252
+ # between them instead of N. Measured on a warm tree: 21.6s -> 0.1s.
253
+ #
254
+ # It was written and proven in isolation and then not wired here, so every /do
255
+ # cycle kept paying full price for a typecheck of a tree that had not changed
256
+ # since the last cycle typechecked it. Proving an artifact is not the same as
257
+ # proving it in place.
258
+ #
259
+ # It is NOT wrapped in gate-run here, and that is now safe rather than a hole:
260
+ # tsc-cached.sh takes its own SLOT internally, around the compute and after the
261
+ # cache double-check, so a HIT costs nothing and a MISS queues like every other
262
+ # gate. Wrapping it out here instead would hold a slot through the up-to-900s
263
+ # lock wait -- two sessions on one folder would occupy both slots while one only
264
+ # waits for the other's answer.
265
+ #
266
+ # The comment this replaces said it "must NOT be nested inside another gate-run
267
+ # slot -- that is a second lock on the same logical gate". That conflated the
268
+ # per-folder dedupe LOCK with the machine-wide SLOT, and it is the instruction
269
+ # that kept the typecheck outside the governor. It prints the error
270
+ # COUNT on stdout and exits non-zero only when the check could not RUN; an unrun
271
+ # check is not a passing check, so both a non-zero exit and a non-zero count fail.
272
+ if [ -x "$ROOT/.claude/scripts/tsc-cached.sh" ] && [ "${VERIFY_FAST_TSC_CACHE:-1}" = "1" ]; then
273
+ _tsc_n=$(bash "$ROOT/.claude/scripts/tsc-cached.sh" "$rel") || {
274
+ echo "[verify-fast] typecheck could NOT RUN (tsc-cached) — treating as RED" >&2; exit 1; }
275
+ _tsc_n=$(printf '%s' "$_tsc_n" | tr -d '[:space:]')
276
+ if [ "${_tsc_n:-1}" != "0" ]; then
277
+ echo "[verify-fast] typecheck FAILED — ${_tsc_n} error(s) in $rel" >&2
278
+ exit 1
279
+ fi
280
+ echo "[verify-fast] typecheck: 0 errors (shared fingerprint cache)"
281
+ else
282
+ bash "$GATE" typecheck -- bun run typecheck || exit 1
283
+ fi
284
+
285
+ # --- 2. tests ----------------------------------------------------------------
286
+ # Tightening the base (above) introduces one way to select nothing that is NOT
287
+ # the dangerous one rule 1 guards. On a do/* branch the cycle's edits are normally
288
+ # uncommitted at W4 time -- but a W3 that commits its own work leaves both terms
289
+ # empty, and rule 1 would then send every cycle to the FULL suite: slower than the
290
+ # lane we replaced. The last commit IS that cycle's work, so retry with it before
291
+ # concluding nothing changed. Rule 1 still has the final word: if even that is
292
+ # empty, nothing was selected and the full suite runs.
293
+ if [ -z "$scoped" ] && [ -n "$cycle_base" ]; then
294
+ last="$(git -C "$ROOT" diff --name-only HEAD~1 HEAD 2>/dev/null | grep -E '\.(ts|tsx|astro|js|mjs)$' || true)"
295
+ scoped="$(printf '%s\n' "$last" | sed -n "s#^${rel}/##p" || true)"
296
+ [ -n "$scoped" ] && echo "[verify-fast] cycle edits were already committed - selecting the last commit's $(printf '%s\n' "$scoped" | grep -c . || true) file(s)"
297
+ fi
298
+
299
+ if [ -z "$scoped" ]; then
300
+ echo "[verify-fast] no changed source files in $rel — falling back to the FULL suite"
301
+ exec bash "$GATE" test -- bun run test
302
+ fi
303
+
304
+ # vitest `related` resolves the importers of the changed files.
305
+ # Through test-cached.sh: identical inputs (same selection, same file contents,
306
+ # same working delta) reuse the recorded PASS instead of re-running. Only passes
307
+ # are remembered -- a red gate re-runs every time until it is green.
308
+ # shellcheck disable=SC2086
309
+ # THE THIRD CASE. Rule 1 covers an empty DIFF. This is an empty SELECTION: the
310
+ # diff is non-empty, but `vitest related` resolves no importers, and vitest then
311
+ # prints "No test files found" and exits 1. That is a FALSE RED -- it fails the
312
+ # cycle for a property of the import graph, not of the code. Measured on
313
+ # src/lib/authority.ts, a real file a real cycle edits.
314
+ #
315
+ # It must not become a pass either; selecting zero tests is exactly the shape
316
+ # rule 1 exists to refuse. So an empty selection falls back to the FULL suite,
317
+ # the same answer an empty diff gets. Conservative in the safe direction.
318
+ # `if cmd | tee` would test TEE's status, not the gate's -- the PIPESTATUS trap
319
+ # this repo has a scar for. Capture first, then branch on the real exit code.
320
+ # `set -e` is ON: a bare failing command exits the script BEFORE the next line,
321
+ # so `cmd; rc=$?` never assigns and the fallback below never runs -- the test gate
322
+ # then vanishes silently, which is strictly worse than the false RED being fixed.
323
+ # Measured: the run stopped after "typecheck: 0 errors" and printed no test line
324
+ # at all. Suspend it around the call, exactly as the FULL_VERIFY branch does.
325
+
326
+ # --- memo probe: never queue for a slot you do not need ----------------------
327
+ # Both test legs below go through test-cached.sh, which reuses a recorded PASS.
328
+ # But the SLOT was taken first and the memo consulted second, so a hit still
329
+ # waited in the governor queue. Measured 2026-09-09 on a saturated box: a single
330
+ # acquisition queued 855s and 1324s -- for work that, on a hit, is a stat() of a
331
+ # stamp file. A hit needs no memory, no forks and no slot.
332
+ #
333
+ # _memo_hit asks test-cached.sh itself (TEST_CACHE_PROBE=1, runs nothing, exits 0
334
+ # on a hit) so there is exactly ONE definition of what a hit is. On a MISS we
335
+ # fall through and take the slot exactly as before -- the gate is never skipped,
336
+ # only the WAIT for a gate that had nothing to do.
337
+ _memo_hit() {
338
+ TEST_CACHE_PROBE=1 bash "$ROOT/.claude/scripts/test-cached.sh" "$@" >/dev/null 2>&1
339
+ }
340
+
341
+ _rel_out="$(mktemp)"
342
+ set +e
343
+ if _memo_hit "$rel" -- related --run $(printf '%s ' $scoped); then
344
+ echo "[verify-fast] related: cache HIT — no slot taken"
345
+ _rel_rc=0
346
+ else
347
+ bash "$GATE" test -- bash "$ROOT/.claude/scripts/test-cached.sh" "$rel" -- related --run $(printf '%s ' $scoped) > "$_rel_out" 2>&1
348
+ _rel_rc=$?
349
+ fi
350
+ set -e
351
+ cat "$_rel_out"
352
+ if [ "$_rel_rc" -eq 0 ]; then
353
+ :
354
+ else
355
+ if grep -q 'No test files found' "$_rel_out"; then
356
+ # An empty SELECTION must never read as a pass, and must not silently exit 1
357
+ # either -- vitest prints "No test files found" and exits 1, which fails the
358
+ # cycle for a property of the import graph rather than of the code.
359
+ #
360
+ # HOW I MISREAD THIS, recorded so the next reader does not repeat it: I first
361
+ # hit the empty selection on src/lib/authority.ts and concluded the file was
362
+ # untested. It is not -- `vitest related` finds 338 test files for it. The
363
+ # empty selection was an ARGUMENT-PASSING bug in the test-cached wrapper,
364
+ # fixed separately (c983c45e2). So this branch is a genuine guard against a
365
+ # state that must not be a pass, NOT evidence that any particular file lacks
366
+ # coverage. Do not infer a coverage gap from reaching here without checking
367
+ # `bunx vitest related --run <file>` directly first.
368
+ #
369
+ # The pins still run, so the gate is not skipped, and the files are named so
370
+ # the condition is visible instead of silent.
371
+ echo "[verify-fast] vitest selected NO test files for these changed files:" >&2
372
+ printf '%s\n' $scoped | sed 's/^/ - /' >&2
373
+ echo "[verify-fast] running the pinned gates only. VERIFY the selection by hand before trusting this:" >&2
374
+ echo "[verify-fast] bunx vitest related --run <file> (an empty result here is usually a WRAPPER bug, not a coverage gap)" >&2
375
+ echo "[verify-fast] VERIFY_FAST_UNCOVERED_FULL=1 forces the whole suite instead." >&2
376
+ if [ "${VERIFY_FAST_UNCOVERED_FULL:-0}" = "1" ]; then
377
+ rm -f "$_rel_out"; exec bash "$GATE" test -- bun run test
378
+ fi
379
+ _rel_rc=0
380
+ fi
381
+ if [ "$_rel_rc" -ne 0 ]; then rm -f "$_rel_out"; exit 1; fi
382
+ fi
383
+ rm -f "$_rel_out"
384
+
385
+ # Pinned gates that no import graph reaches. vitest positional filters are
386
+ # SUBSTRINGS, not a regex — one alternation string matches nothing and exits 1
387
+ # ("No test files found"), which reads as a failed gate. Pass one arg per pin.
388
+ #
389
+ # TIER-GATED, and MEMOISED. The pinned block is 44 files / 420 tests / ~20s and it
390
+ # ran on every cycle regardless of the diff -- a six-cycle plan paid it six times
391
+ # for a surface that never moved. Two independent cuts, neither of which checks
392
+ # less:
393
+ #
394
+ # 1. tier. do-tier.sh already sizes the change. A PATCH (a doc line, a comment)
395
+ # cannot reach the authority/receivers/parity surface these pins guard, so it
396
+ # does not pay for them. FIX and above run the full pin set, unchanged.
397
+ # DEFAULT UP here, the opposite of do-tier's own default-down: an unknown
398
+ # tier runs ALL the pins. Skipping a gate on a guess is the failure this
399
+ # harness has the most scars from.
400
+ # 2. memo. Identical pins over an identical tree reuse the recorded PASS.
401
+ #
402
+ # VERIFY_FAST_PINS_ALL=1 forces the whole set.
403
+ #
404
+ # THE TIMING CUT. The pins guard integration surfaces -- parity, boundary,
405
+ # authority, receivers. Integration is a property of the FINISHED plan, not of
406
+ # cycle 3 of 6. Running them per-cycle was testing at the wrong time: six
407
+ # identical answers to a question only the last one can actually decide.
408
+ #
409
+ # So mid-plan they are DEFERRED, not deleted. The debt is written to
410
+ # .verify-fast-debt in the repo root, and the close gate (FULL_VERIFY=1, which
411
+ # /close and ./deploy both export) runs the whole suite and clears it. Accuracy
412
+ # is MOVED to the moment it matters, never dropped: a plan cannot reach a ship
413
+ # gate with the debt outstanding, because the ship gate is the full suite.
414
+ #
415
+ # The deferral applies ONLY on a do/<slug> branch -- a one-off check on main has
416
+ # no later gate to defer to, so it pays now. And an outstanding debt on main means
417
+ # a plan landed without ever reaching its close gate: pay it here, immediately.
418
+ # A deferral that can be forgotten is a deleted gate wearing a different word.
419
+ if [ -f "$DEBT_FILE" ] && { [ -z "${_br:-}" ] || [ "${_br#do/}" = "${_br:-}" ]; }; then
420
+ echo "[verify-fast] outstanding deferred pins from a merged plan — paying now (branch ${_br:-detached})"
421
+ VERIFY_FAST_PINS_ALL=1
422
+ fi
423
+ if [ "${VERIFY_FAST_PINS_ALL:-0}" != "1" ] && [ -n "${_br:-}" ] && [ "${_br#do/}" != "${_br:-}" ]; then
424
+ printf '%s\t%s\t%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$_br" "$rel" >> "$DEBT_FILE"
425
+ echo "[verify-fast] pins DEFERRED to the close gate (branch $_br) — debt recorded in .verify-fast-debt; FULL_VERIFY=1 clears it"
426
+ echo "[verify-fast] PASS (fast lane, pins deferred — full gate runs at /close and ./deploy)"
427
+ exit 0
428
+ fi
429
+
430
+ _tier="$(printf '%s\n' $scoped | sed "s#^#${rel}/#" | bash "$ROOT/.claude/scripts/do-tier.sh" 2>/dev/null | jq -r '.tier // "UNKNOWN"' 2>/dev/null || echo UNKNOWN)"
431
+ IFS='|' read -r -a _pins <<< "$PINS"
432
+ if [ "${VERIFY_FAST_PINS_ALL:-0}" != "1" ] && [ "$_tier" = "PATCH" ]; then
433
+ echo "[verify-fast] tier=PATCH — pins narrowed to the parity/boundary core (FIX+ runs all $PINS)"
434
+ _pins=(parity boundary)
435
+ else
436
+ echo "[verify-fast] tier=$_tier — running the full pin set"
437
+ fi
438
+ if _memo_hit "$rel" -- run "${_pins[@]}"; then
439
+ echo "[verify-fast] pins: cache HIT — no slot taken"
440
+ elif ! bash "$GATE" test -- bash "$ROOT/.claude/scripts/test-cached.sh" "$rel" -- run "${_pins[@]}"; then
441
+ echo "[verify-fast] pinned gate FAILED (pins: ${_pins[*]})" >&2
442
+ exit 1
443
+ fi
444
+
445
+ echo "[verify-fast] PASS (fast lane — full gate still runs at /close and ./deploy)"
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env bash
2
+ # vespio-sync.sh — one-way harness sync, one-ie -> apps/vespio.
3
+ #
4
+ # Vespio is an AGENCY repo (clients/, site/, web/, ai/, packages/). It is not
5
+ # the monorepo: no schema/, no pay/, no channels/, no text/ promise layer. So
6
+ # "sync everything" is the wrong instinct — most of one-ie's harness hard-asserts
7
+ # against a tree vespio does not have.
8
+ #
9
+ # THE AUTHORITY IS THE MANIFEST IN factory-repo.sh. It already classifies every
10
+ # script portable | needs-env | monorepo-only. This script reads that manifest
11
+ # rather than keeping a second list that could drift from it. An UNCLASSIFIED
12
+ # script does not ship — silence is never a licence.
13
+ #
14
+ # bash .claude/scripts/vespio-sync.sh --check # what's missing/drifted (read-only)
15
+ # bash .claude/scripts/vespio-sync.sh # copy, then show the diff
16
+ # bash .claude/scripts/vespio-sync.sh --commit # copy + commit (never pushes)
17
+ #
18
+ # Never pushes. Never stages by wildcard — every path is named. The 6 modified
19
+ # clients/*/data/lifecycles/*.toml in vespio belong to someone else's work and
20
+ # must never ride along in a sync commit.
21
+ set -uo pipefail
22
+
23
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
24
+ VESPIO="${VESPIO_ROOT:-$ROOT/../apps/vespio}"
25
+ MODE="${1:-}"
26
+
27
+ [ -d "$VESPIO/.git" ] || { echo "no vespio repo at $VESPIO"; exit 1; }
28
+
29
+ # ROOT is derived from BASH_SOURCE, so invoking this by a RELATIVE path from
30
+ # inside vespio resolves ROOT to vespio and syncs the tree onto itself. Assert
31
+ # the source is really one-ie before touching anything.
32
+ [ -f "$ROOT/schema/one.tql" ] || {
33
+ echo "refusing: \$ROOT ($ROOT) is not the one-ie monorepo (no schema/one.tql)."
34
+ echo "run this from /Users/toc/Server/one-ie, or by absolute path."
35
+ exit 1
36
+ }
37
+ [ "$(cd "$ROOT" && pwd -P)" != "$(cd "$VESPIO" && pwd -P)" ] || {
38
+ echo "refusing: source and destination are the same tree"; exit 1
39
+ }
40
+
41
+ # --- the shippable set, read from the single manifest ------------------------
42
+ shippable() {
43
+ sed -n '/^manifest() {/,/^MANIFEST/p' "$ROOT/.claude/scripts/factory-repo.sh" \
44
+ | grep -E '^(portable|needs-env) ' | awk '{print $2}' | sort
45
+ }
46
+ unclassified() {
47
+ bash "$ROOT/.claude/scripts/factory-repo.sh" --check-portability 2>/dev/null \
48
+ | sed -n 's/.*FAIL unclassified: //p' | sort
49
+ }
50
+
51
+ # Commands that make sense in an agency tree. Deliberately excluded and why:
52
+ # deploy.md db-sync.md release.md -> 5-service monorepo pipeline
53
+ # one.md -> nine fleets over text/ docs vespio lacks
54
+ # oo-push.md rag.md -> monorepo-specific surfaces
55
+ COMMANDS="browser.md cc-connect.md chat.md close.md create.md do.md do-autonomous.md
56
+ do-improve.md do-show.md go.md improve.md kill.md notify.md restart.md
57
+ see.md skill-create.md sync.md"
58
+
59
+ # Skills that are tree-agnostic craft. Excluded: typedb, sdk, mcp, cli, puck,
60
+ # promise-* (need schema/, packages/, or the text/ promise layer).
61
+ SKILLS="astro react19 shadcn writer voice docs"
62
+
63
+ copied=0; missing=0
64
+ say() { printf ' %-14s %s\n' "$1" "$2"; }
65
+
66
+ ship_one() { # ship_one <relpath under .claude>
67
+ local rel="${1:-}"; [ -n "$rel" ] || return 0
68
+ local src="$ROOT/.claude/$rel" dst="$VESPIO/.claude/$rel"
69
+ [ -e "$src" ] || { say "ABSENT" "$rel"; missing=$((missing+1)); return; }
70
+ if [ "$MODE" = "--check" ]; then
71
+ if [ ! -e "$dst" ]; then say "MISSING" "$rel"; missing=$((missing+1))
72
+ elif ! diff -q "$src" "$dst" >/dev/null 2>&1; then say "DRIFTED" "$rel"; missing=$((missing+1)); fi
73
+ return
74
+ fi
75
+ mkdir -p "$(dirname "$dst")"
76
+ # A second run must be identical to the first. `cp -R src dst` NESTS when dst
77
+ # already exists (dst/src), so a re-sync silently built skills/writer/writer.
78
+ # Remove the destination first — this is a mirror, not an accumulation.
79
+ rm -rf "$dst"
80
+ # -L dereferences symlinks: .claude/skills/livekit-agents points outside the
81
+ # tree and would land in vespio as a dangling link.
82
+ cp -RL "$src" "$dst" && copied=$((copied+1))
83
+ }
84
+
85
+ echo "== vespio-sync $ROOT -> $VESPIO"
86
+ un="$(unclassified)"
87
+ if [ -n "$un" ]; then
88
+ echo "-- NOT SHIPPING (unclassified in the manifest — classify them to ship):"
89
+ echo "$un" | sed 's/^/ /'
90
+ fi
91
+
92
+ echo "-- scripts (manifest: portable + needs-env)"
93
+ while read -r f; do [ -n "$f" ] && ship_one "scripts/$f"; done <<< "$(shippable)"
94
+
95
+ echo "-- rules"
96
+ for f in api.md astro.md design.md documentation.md engine.md react.md ui.md; do ship_one "rules/$f"; done
97
+
98
+ echo "-- agents"
99
+ for f in w1-recon.md w2-decide.md w3-edit.md w4-verify.md; do ship_one "agents/$f"; done
100
+
101
+ echo "-- commands"
102
+ for f in $COMMANDS; do ship_one "commands/$f"; done
103
+
104
+ # /vespio has TWO bodies: the monorepo-side one (sync + push, lives in
105
+ # .claude/commands/vespio.md here) and the vespio-side one (start the fleet).
106
+ # Shipping the monorepo body would tell a vespio clone to run vespio-sync.sh,
107
+ # which is monorepo-only and not there — a command that lies from inside the
108
+ # repo it ships to. So the REMOTE body is authored separately and mapped on.
109
+ ship_mapped() { # ship_mapped <src rel> <dst rel>
110
+ local src="$ROOT/.claude/$1" dst="$VESPIO/.claude/$2"
111
+ [ -f "$src" ] || { say "ABSENT" "$1"; missing=$((missing+1)); return; }
112
+ if [ "$MODE" = "--check" ]; then
113
+ if [ ! -e "$dst" ]; then say "MISSING" "$2"; missing=$((missing+1))
114
+ elif ! diff -q "$src" "$dst" >/dev/null 2>&1; then say "DRIFTED" "$2"; missing=$((missing+1)); fi
115
+ return
116
+ fi
117
+ mkdir -p "$(dirname "$dst")"; rm -rf "$dst"; cp -L "$src" "$dst" && copied=$((copied+1))
118
+ }
119
+ ship_mapped "commands/vespio.remote.md" "commands/vespio.md"
120
+
121
+ echo "-- skills"
122
+ for s in $SKILLS; do ship_one "skills/$s"; done
123
+
124
+ if [ "$MODE" = "--check" ]; then
125
+ echo "== $missing item(s) missing or drifted"
126
+ [ "$missing" -eq 0 ] && echo " vespio is current."
127
+ exit 0
128
+ fi
129
+
130
+ echo "== copied $copied item(s)"
131
+ cd "$VESPIO"
132
+ echo "-- vespio diff (.claude only):"
133
+ git status --porcelain .claude | sed 's/^/ /'
134
+
135
+ if [ "$MODE" = "--commit" ]; then
136
+ # explicit paths only — never `git add -A`. A neighbour's uncommitted
137
+ # clients/*/lifecycles edits must not be swept into a harness sync.
138
+ git add .claude
139
+ if git diff --cached --quiet; then echo "nothing to commit"; exit 0; fi
140
+ git commit -q -F - <<'MSG'
141
+ chore(claude): sync harness from one-ie
142
+
143
+ Manifest-driven — the shippable set is read from factory-repo.sh's
144
+ portability manifest, not a second list. Unclassified scripts do not ship.
145
+ Excluded by design: the 5-service deploy pipeline, the text/ promise layer,
146
+ and schema/-dependent skills, none of which exist in this tree.
147
+ MSG
148
+ echo "committed. Review, then push by hand: git -C $VESPIO push origin main"
149
+ fi