@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,268 @@
1
+ #!/usr/bin/env bash
2
+ # close-owner.sh — resolve WHO owns a close and WHO they report to.
3
+ #
4
+ # manifest: portable
5
+ #
6
+ # /close needs two deterministic facts before any model speaks: which agent owns
7
+ # the work, and which agent it reports to. Both are declared in the roster
8
+ # (one.ie/ai/agents/*/agent.md); neither is a judgment call. This script answers
9
+ # them so the command executes a fact instead of substituting for one.
10
+ #
11
+ # close-owner.sh --tags "engineering,contracts" -> owner + superior chain
12
+ # close-owner.sh --owner cmo -> just the chain above cmo
13
+ # close-owner.sh --roster -> coverage census
14
+ # close-owner.sh --self-test -> proves it can go RED
15
+ #
16
+ # THE MATCH RULE, and why it is the bare words only:
17
+ # `subscribes:` carries TWO shapes and they are different lanes (root CLAUDE.md).
18
+ # - signal: campaign:brief <- workflow trigger_source, read by triggerWorkflows
19
+ # - marketing <- the routing STAKE, read by matchSubscribers
20
+ # A namespaced tag matches zero signals. Matching on it would hand work to an
21
+ # agent the world will never actually route to.
22
+ set -uo pipefail
23
+
24
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
25
+ AGENTS="$ROOT/one.ie/ai/agents"
26
+
27
+ # specialist first: ties break DOWNWARD. Sending a director to do specialist work
28
+ # is how a director stops being available for the judgment only they can make.
29
+ _tier_rank() {
30
+ case "${1:-}" in
31
+ specialist) echo 0 ;; director) echo 1 ;; ceo) echo 2 ;; chairman) echo 3 ;;
32
+ *) echo 9 ;; # tool / undeclared — never an owner
33
+ esac
34
+ }
35
+
36
+ _field() { grep -m1 "^$2:" "$1" 2>/dev/null | cut -d' ' -f2- | tr -d '\r' | xargs 2>/dev/null; }
37
+
38
+ # The bare stake words only — skip every `- signal:` line.
39
+ _stakes() {
40
+ awk '/^subscribes:/{f=1;next} f&&/^[a-zA-Z_]+:/{f=0} f&&/^[[:space:]]*-[[:space:]]/{
41
+ line=$0; sub(/^[[:space:]]*-[[:space:]]*/,"",line)
42
+ if (line !~ /^signal:/ && line !~ /:/ && line != "") print line
43
+ }' "$1" 2>/dev/null | tr -d '\r' | xargs -n1 2>/dev/null
44
+ }
45
+
46
+ _superior() { # walk the declared chain, then the tier ladder
47
+ local name="$1" hops=0 out=""
48
+ while [ -n "$name" ] && [ "$hops" -lt 8 ]; do
49
+ local f="$AGENTS/$name/agent.md"
50
+ [ -f "$f" ] || break
51
+ local rt; rt="$(_field "$f" reports_to)"
52
+ if [ -z "$rt" ]; then
53
+ case "$(_field "$f" tier)" in
54
+ specialist) rt="ceo" ;; # no director declared for its domain
55
+ director) rt="ceo" ;;
56
+ ceo) rt="chairman" ;;
57
+ chairman) rt="" ;; # answers to the human
58
+ *) rt="ceo" ;; # repo law: CEO is the standing receiver
59
+ esac
60
+ fi
61
+ [ -z "$rt" ] && break
62
+ out="$out $rt"; name="$rt"; hops=$((hops+1))
63
+ done
64
+ echo "$out" | xargs 2>/dev/null
65
+ }
66
+
67
+ cmd_roster() {
68
+ local tot=0 rt=0 ti=0 sub=0 bare=0
69
+ for d in "$AGENTS"/*/; do
70
+ local f="$d/agent.md"; [ -f "$f" ] || continue
71
+ tot=$((tot+1))
72
+ [ -n "$(_field "$f" reports_to)" ] && rt=$((rt+1))
73
+ [ -n "$(_field "$f" tier)" ] && ti=$((ti+1))
74
+ grep -q '^subscribes:' "$f" && sub=$((sub+1))
75
+ [ -n "$(_stakes "$f")" ] && bare=$((bare+1))
76
+ done
77
+ echo "agents=$tot reports_to=$rt tier=$ti subscribes=$sub bare_stakes=$bare"
78
+ echo "note: agents WITHOUT bare stakes cannot be matched by tag — they need the ladder fallback."
79
+ }
80
+
81
+ # How many agents stake a given word. A word many agents carry cannot single one
82
+ # out — `marketing` is on 16 of them, so one hit against it is a tie broken by
83
+ # directory order, not a match.
84
+ GENERIC_AT=${CLOSE_OWNER_GENERIC_AT:-3}
85
+
86
+ # THE INDEX — one awk pass over the whole roster, built once, reused by every
87
+ # lookup. It replaced a per-agent `_stakes` fork called from inside two nested
88
+ # loops: a --self-test spawned ~1300 awk processes and took 101s. Same answers,
89
+ # one pass. Rows are `name<TAB>tier<TAB>reports_to<TAB>stake`, one per stake.
90
+ # The memo is a FILE, not a variable. Every `$(_index)` runs in a subshell, so a
91
+ # variable assignment inside it is discarded the moment the subshell exits — the
92
+ # memo silently never hit and awk re-ran on every call (measured: no improvement
93
+ # at all, 101s -> 70s, when the whole point was one pass).
94
+ _INDEX_FILE="${TMPDIR:-/tmp}/close-owner-index.$$"
95
+ trap 'rm -f "$_INDEX_FILE"' EXIT
96
+ _index() {
97
+ [ -s "$_INDEX_FILE" ] && { cat "$_INDEX_FILE"; return; }
98
+ awk '
99
+ FNR==1 { name=""; tier=""; rt=""; insub=0
100
+ n=split(FILENAME,p,"/"); dir=p[n-1] }
101
+ /^name:/ { sub(/^name:[[:space:]]*/,""); name=$0 }
102
+ /^tier:/ { sub(/^tier:[[:space:]]*/,""); tier=$0 }
103
+ /^reports_to:/ { sub(/^reports_to:[[:space:]]*/,""); rt=$0 }
104
+ /^subscribes:/ { insub=1; next }
105
+ insub && /^[a-zA-Z_]+:/ { insub=0 }
106
+ insub && /^[[:space:]]*-[[:space:]]/ {
107
+ l=$0; sub(/^[[:space:]]*-[[:space:]]*/,"",l); gsub(/[[:space:]]+$/,"",l)
108
+ if (l !~ /^signal:/ && l !~ /:/ && l != "") {
109
+ print (name==""?dir:name) "\t" tier "\t" rt "\t" l
110
+ }
111
+ }
112
+ ' "$AGENTS"/*/agent.md 2>/dev/null > "$_INDEX_FILE"
113
+ cat "$_INDEX_FILE"
114
+ }
115
+
116
+ _tag_breadth() {
117
+ _index | awk -F'\t' -v T="$1" '$4==T{a[$1]=1} END{print length(a)}'
118
+ }
119
+
120
+ # Every agent whose stakes intersect the tags, tier-ranked. This is what a refusal
121
+ # hands upward — a decision without the options is not a decision.
122
+ _candidates() {
123
+ local tags="$1"; local IFS=,; read -ra want <<< "$tags"; unset IFS
124
+ local agent tier_w
125
+ while IFS=$'\t' read -r agent tier_w; do
126
+ [ -n "$agent" ] || continue
127
+ local rank; rank="$(_tier_rank "$tier_w")"
128
+ [ "$rank" -ge 9 ] && continue
129
+ local hits=0 s w
130
+ for s in $(_index | awk -F'\t' -v A="$agent" '$1==A{print $4}'); do
131
+ for w in "${want[@]}"; do [ "$(echo "$w" | xargs)" = "$s" ] && hits=$((hits+1)); done
132
+ done
133
+ [ "$hits" -gt 0 ] && echo "$rank:$agent($hits)"
134
+ done <<< "$(_index | awk -F'\t' '{print $1"\t"$2}' | sort -u)" | sort | cut -d: -f2
135
+ }
136
+
137
+ cmd_resolve() {
138
+ local tags="$1" best="" best_rank=9 best_hits=0 best_specific=0 contenders=0
139
+ local IFS=,; read -ra want <<< "$tags"; unset IFS
140
+
141
+ # Breadth is a property of the TAG, not of the agent being examined, and only
142
+ # the requested tags matter — so it is computed once here rather than inside
143
+ # the agent loop. Calling _tag_breadth per matching stake made this quadratic
144
+ # over file reads (a --self-test went from ~1s to >120s); bash 3.2 has no
145
+ # associative arrays, so the answers ride a parallel indexed array.
146
+ local -a want_breadth=()
147
+ local i
148
+ for i in "${!want[@]}"; do
149
+ want[$i]="$(echo "${want[$i]}" | xargs)"
150
+ want_breadth[$i]="$(_tag_breadth "${want[$i]}")"
151
+ done
152
+
153
+ # One pass over the index: agent -> its stakes, already parsed.
154
+ local line agent tier_w
155
+ while IFS=$'\t' read -r agent tier_w; do
156
+ [ -n "$agent" ] || continue
157
+ local rank; rank="$(_tier_rank "$tier_w")"
158
+ [ "$rank" -ge 9 ] && continue # tool / undeclared: not an owner
159
+ local hits=0 specific=0 s j
160
+ for s in $(_index | awk -F'\t' -v A="$agent" '$1==A{print $4}'); do
161
+ for j in "${!want[@]}"; do
162
+ if [ "${want[$j]}" = "$s" ]; then
163
+ hits=$((hits+1))
164
+ [ "${want_breadth[$j]}" -le "$GENERIC_AT" ] && specific=$((specific+1))
165
+ fi
166
+ done
167
+ done
168
+ [ "$hits" -eq 0 ] && continue
169
+ contenders=$((contenders+1))
170
+ if [ "$specific" -gt "$best_specific" ] \
171
+ || { [ "$specific" -eq "$best_specific" ] && [ "$hits" -gt "$best_hits" ]; } \
172
+ || { [ "$specific" -eq "$best_specific" ] && [ "$hits" -eq "$best_hits" ] && [ "$rank" -lt "$best_rank" ]; }; then
173
+ best="$agent"; best_rank="$rank"; best_hits="$hits"; best_specific="$specific"
174
+ fi
175
+ done <<< "$(_index | awk -F'\t' '{print $1"\t"$2}' | sort -u)"
176
+
177
+ if [ -z "$best" ]; then
178
+ # Not a failure — the documented fallback. world:route answers reason:'ceo'
179
+ # for a workspace with no history, and the CEO is the standing receiver.
180
+ echo "owner=ceo via=fallback(no-tag-match) hits=0 confidence=none"
181
+ echo "chain=$(_superior ceo)"
182
+ return 0
183
+ fi
184
+
185
+ # THE REFUSAL. Evidence that cannot separate one candidate from several is not
186
+ # a match — it is directory order wearing a receipt. Modelled on factory:size
187
+ # returning `unsized` for an empty path set (resolvers/factory.ts:206) rather
188
+ # than guessing a tier. Refusing routes to the ladder, which explains itself.
189
+ if [ "$best_specific" -eq 0 ] && [ "$contenders" -gt 1 ]; then
190
+ echo "owner=REFUSED via=ambiguous hits=$best_hits contenders=$contenders confidence=low"
191
+ echo "reason=only generic tags matched (each carried by >$GENERIC_AT agents); $contenders candidates were indistinguishable"
192
+ # A refusal that names nothing forces the asker to redo the search. The whole
193
+ # point of refusing is to hand the decision UP with the evidence attached, so
194
+ # the shortlist rides with it — this is the notify-a-human payload.
195
+ echo "candidates=$(_candidates "$tags" | tr '\n' ' ' | xargs)"
196
+ echo "fallback=ceo chain=$(_superior ceo)"
197
+ return 0
198
+ fi
199
+
200
+ local conf="high"; [ "$best_specific" -eq 0 ] && conf="medium"
201
+ echo "owner=$best via=subscribes hits=$best_hits specific=$best_specific contenders=$contenders confidence=$conf"
202
+ echo "chain=$(_superior "$best")"
203
+ }
204
+
205
+ cmd_self_test() {
206
+ local fails=0
207
+ _expect() { # name, actual, expected-substring
208
+ if grep -q -- "$3" <<< "$2"; then echo " ok $1"; else echo " RED $1 — got: $2"; fails=$((fails+1)); fi
209
+ }
210
+ echo "close-owner --self-test"
211
+
212
+ # The case that shipped broken: this close's own tags named one of five
213
+ # candidates on a single generic hit, and the tie broke on directory order.
214
+ _expect "the tags THIS close used are REFUSED, not confidently answered" \
215
+ "$(cmd_resolve 'engineering,inbox,tasks')" "owner=REFUSED"
216
+ _expect "a refusal still routes — it names the ladder fallback" \
217
+ "$(cmd_resolve 'engineering,inbox,tasks')" "fallback=ceo"
218
+ _expect "a bare generic tag alone is ambiguous, not a match" \
219
+ "$(cmd_resolve engineering)" "owner=REFUSED"
220
+ # CORRECTED BELIEF, not a weakened test. This assertion used to read "a
221
+ # marketing tag reaches a marketing agent". It does not and never did:
222
+ # `marketing` is staked by 16 agents, `sql` by 17. The old test passed because
223
+ # the resolver picked one by directory order and the assertion only checked
224
+ # that SOMETHING was picked. The truthful assertion is that it refuses — and
225
+ # that the refusal carries the shortlist, so the decision can be made upward.
226
+ _expect "a 16-agent tag refuses rather than picking by directory order" \
227
+ "$(cmd_resolve marketing)" "owner=REFUSED"
228
+ _expect "the refusal hands up a candidate shortlist, not just a no" \
229
+ "$(cmd_resolve marketing)" "candidates="
230
+ _expect "a specific tag (1 agent stakes it) still resolves with high confidence" \
231
+ "$(cmd_resolve spec)" "confidence=high"
232
+ _expect "an unknown tag falls back to the CEO, it does not fail" \
233
+ "$(cmd_resolve zzz-no-such-tag)" "owner=ceo"
234
+ _expect "the chain from a director ends at the chairman" \
235
+ "chain=$(_superior cmo)" "chairman"
236
+ _expect "the chain from the ceo is just the chairman" \
237
+ "chain=$(_superior ceo)" "chairman"
238
+
239
+ # A namespaced trigger word must NEVER match — it is the other lane.
240
+ local ns; ns="$(cmd_resolve 'campaign:brief')"
241
+ _expect "a namespaced trigger word matches NOBODY (falls back)" "$ns" "owner=ceo"
242
+
243
+ # RED PROOF: gut the mechanism the resolver ACTUALLY reads and assert the
244
+ # matches stop working. A checker that stays green against a gutted mechanism
245
+ # proves nothing — and this one caught exactly that on itself: it used to gut
246
+ # `_stakes`, and when the resolver was rewired onto `_index` the proof went on
247
+ # passing while guarding a function nothing called any more. It was repointed,
248
+ # never relaxed. If you move the mechanism again, move this with it.
249
+ echo " -- red proof --"
250
+ _index() { echo ""; }
251
+ local gutted; gutted="$(cmd_resolve spec)"
252
+ if grep -q "via=fallback" <<< "$gutted"; then
253
+ echo " ok gutted index falls back — the check is load-bearing"
254
+ else
255
+ echo " RED gutted index STILL matched: $gutted"; fails=$((fails+1))
256
+ fi
257
+
258
+ echo
259
+ [ "$fails" -eq 0 ] && { echo "PASS"; return 0; } || { echo "FAIL ($fails)"; return 1; }
260
+ }
261
+
262
+ case "${1:---help}" in
263
+ --tags) shift; cmd_resolve "${1:-}" ;;
264
+ --owner) shift; echo "chain=$(_superior "${1:-}")" ;;
265
+ --roster) cmd_roster ;;
266
+ --self-test) cmd_self_test ;;
267
+ *) sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//' ;;
268
+ esac
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env bash
2
+ # db-sync-lock-check.sh — the property `sync-from-prod.ts` needs to be safe under
3
+ # N concurrent sessions.
4
+ #
5
+ # WHY THIS EXISTS. `hook:db-sync` (settings.json, SessionStart) fires
6
+ # `bun run db:sync` on EVERY session start, and the script writes a SHARED path
7
+ # (`/tmp/one-prod-sync/d1-export.sql`). On 2026-09-05, with 7 sessions open, two
8
+ # runs were caught mid-flight by lsof holding that one file open for write —
9
+ # two write fds, one inode, identical offset — and each would then have dropped
10
+ # and re-imported the local `one-owners` tables underneath the other. The bug is
11
+ # not the wasted export; it is the corrupted local mirror.
12
+ #
13
+ # C1 a lone runner acquires, completes, and RELEASES
14
+ # C2 a second runner SKIPS (exit 0) while a live owner holds the lock
15
+ # C3 a dead owner's lock is reaped — the next runner proceeds
16
+ # C4 the TTL branch reaps too, so the pid branch is not the only one
17
+ # R1 RED-PROOF: with the guard removed, C2 must FAIL. A checker that stays
18
+ # green against a gutted implementation is not a check.
19
+ #
20
+ # Sandboxed via ONE_SYNC_TMP_DIR — never touches a real /tmp/one-prod-sync.
21
+ # Runs with an empty --only scope, so no network call and no local mutation.
22
+ #
23
+ # bash .claude/scripts/db-sync-lock-check.sh
24
+ set -uo pipefail
25
+
26
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
27
+ SCRIPT="$ROOT/one.ie/web/scripts/sync-from-prod.ts"
28
+ SANDBOX="$(mktemp -d "${TMPDIR:-/tmp}/db-sync-lock-check.XXXXXX")"
29
+ trap 'rm -rf "$SANDBOX"' EXIT
30
+
31
+ fails=0
32
+ ok() { printf ' \033[32mPASS\033[0m %s\n' "$1"; }
33
+ bad() { printf ' \033[31mFAIL\033[0m %s\n' "$1"; fails=$((fails + 1)); }
34
+
35
+ # Dummy creds: the script only checks PRESENCE, and an empty --only scope makes
36
+ # no API call. Never put real credentials in a checker.
37
+ run_sync() { # run_sync <tmpdir> <script> -> stdout+stderr, exit code preserved
38
+ CLOUDFLARE_EMAIL='check@example.invalid' \
39
+ CLOUDFLARE_GLOBAL_API_KEY='not-a-real-key' \
40
+ ONE_SYNC_TMP_DIR="$1" \
41
+ bun "$2" --only= 2>&1
42
+ }
43
+
44
+ echo "── db-sync single-writer lock ──"
45
+
46
+ # ── C1 lone runner: acquires, completes, releases ───────────────────────────
47
+ d="$SANDBOX/c1"; mkdir -p "$d"
48
+ out="$(run_sync "$d" "$SCRIPT")"; rc=$?
49
+ if [ $rc -eq 0 ] && grep -q 'DONE' <<<"$out"; then
50
+ ok "C1 lone runner completes"
51
+ else
52
+ bad "C1 lone runner completes (rc=$rc): $out"
53
+ fi
54
+ if [ ! -d "$d/.lock" ]; then
55
+ ok "C1 lock released on exit"
56
+ else
57
+ bad "C1 lock LEAKED at $d/.lock"
58
+ fi
59
+
60
+ # ── C2 contention: a live owner makes the second runner skip ────────────────
61
+ # $$ is this shell — guaranteed alive, and never needs killing.
62
+ d="$SANDBOX/c2"; mkdir -p "$d/.lock"; printf '%s' "$$" > "$d/.lock/pid"
63
+ out="$(run_sync "$d" "$SCRIPT")"; rc=$?
64
+ if [ $rc -eq 0 ] && grep -q 'skipping this run' <<<"$out" && ! grep -q 'DONE' <<<"$out"; then
65
+ ok "C2 second runner skips under a live owner"
66
+ else
67
+ bad "C2 second runner did NOT skip (rc=$rc): $out"
68
+ fi
69
+ if [ -f "$d/.lock/pid" ] && [ "$(cat "$d/.lock/pid")" = "$$" ]; then
70
+ ok "C2 skipping runner did not steal the owner's lock"
71
+ else
72
+ bad "C2 owner's lock was clobbered by the skipper"
73
+ fi
74
+
75
+ # ── C3 a dead owner's lock is reaped ────────────────────────────────────────
76
+ bash -c 'exit 0' & deadpid=$!; wait $deadpid 2>/dev/null # pid is now dead
77
+ d="$SANDBOX/c3"; mkdir -p "$d/.lock"; printf '%s' "$deadpid" > "$d/.lock/pid"
78
+ out="$(run_sync "$d" "$SCRIPT")"; rc=$?
79
+ if [ $rc -eq 0 ] && grep -q 'reaping stale lock' <<<"$out" && grep -q 'DONE' <<<"$out"; then
80
+ ok "C3 dead owner's lock reaped, runner proceeds"
81
+ else
82
+ bad "C3 dead owner's lock NOT reaped (rc=$rc): $out"
83
+ fi
84
+
85
+ # ── C4 the TTL branch reaps as well (owner alive, lease expired) ────────────
86
+ d="$SANDBOX/c4"; mkdir -p "$d/.lock"; printf '%s' "$$" > "$d/.lock/pid"
87
+ touch -A -013000 "$d/.lock" 2>/dev/null || touch -d '90 minutes ago' "$d/.lock" 2>/dev/null
88
+ out="$(run_sync "$d" "$SCRIPT")"; rc=$?
89
+ if grep -q 'reaping stale lock' <<<"$out" && grep -q 'DONE' <<<"$out"; then
90
+ ok "C4 expired lease reaped even though the owner is alive"
91
+ else
92
+ bad "C4 TTL branch did not fire — pid liveness is the only reaper: $out"
93
+ fi
94
+
95
+ # ── R1 RED-PROOF: gut the guard, C2 must fail ───────────────────────────────
96
+ GUTTED="$SANDBOX/gutted.ts"
97
+ sed 's/^if (!acquireLock()) {$/if (false) {/' "$SCRIPT" > "$GUTTED"
98
+ if ! grep -q 'if (false) {' "$GUTTED"; then
99
+ bad "R1 could not gut the guard — the anchor moved, this checker is blind"
100
+ else
101
+ d="$SANDBOX/r1"; mkdir -p "$d/.lock"; printf '%s' "$$" > "$d/.lock/pid"
102
+ out="$(run_sync "$d" "$GUTTED")"
103
+ if grep -q 'skipping this run' <<<"$out"; then
104
+ bad "R1 gutted build STILL skipped — C2 is not testing the guard"
105
+ else
106
+ ok "R1 red-proof: gutted guard stops skipping, so C2 has teeth"
107
+ fi
108
+ fi
109
+
110
+ echo
111
+ if [ $fails -eq 0 ]; then
112
+ echo "ALL GREEN — the lock holds and the checks can go red"
113
+ exit 0
114
+ fi
115
+ echo "RED — $fails check(s) failed"
116
+ exit 1
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+ # deploy-dev.sh - ship one.ie/web to https://dev.one.ie (worker `one-dev`).
3
+ #
4
+ # THE TWO-TIER RULE:
5
+ # dev.one.ie - agents finish loops here. MINIMUM gate: the fast lane only.
6
+ # one.ie - humans promote here. FULL gate: ./deploy (FULL_VERIFY=1).
7
+ # A fast pass is never reported as a full pass.
8
+ set -euo pipefail
9
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
10
+ WEB="$ROOT/one.ie/web"
11
+ cd "$WEB"
12
+
13
+ echo "[deploy-dev] target: https://dev.one.ie worker: one-dev"
14
+
15
+ # 2. the MINIMUM gate - fast lane, through the governor. Never the full suite.
16
+ if [ "${DEV_SKIP_GATE:-0}" = "1" ]; then
17
+ echo "[deploy-dev] gate SKIPPED (DEV_SKIP_GATE=1) - you asked for it"
18
+ else
19
+ echo "[deploy-dev] gate: FAST LANE (not a full pass)"
20
+ bash "$ROOT/.claude/scripts/gate-run.sh" devgate -- bun run verify:fast
21
+ fi
22
+
23
+ # 3. build + ship against the dev config
24
+ bash "$ROOT/.claude/scripts/gate-run.sh" devbuild -- bun run build
25
+
26
+ # derive the dev config FROM the astro build output, so it can never drift from
27
+ # what production actually ships (and so `main` + the assets binding come along)
28
+ python3 "$ROOT/.claude/scripts/gen-dev-config.py" || exit 1
29
+ npx wrangler deploy --config dist/server/wrangler.dev.json
30
+
31
+ # 4. prove it answers
32
+ code=$(curl -s -o /dev/null -w '%{http_code}' https://dev.one.ie/ --max-time 20 || echo 000)
33
+ echo "[deploy-dev] https://dev.one.ie -> HTTP $code"
34
+ [ "$code" = "200" ] || { echo "[deploy-dev] FAILED - dev.one.ie did not answer 200"; exit 1; }
35
+ echo "[deploy-dev] done. Promote to production with: ./deploy (full gate, human decision)"