@oneie/claude 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/agents/abm-strategist.md +67 -1
- package/agents/ads-meta.md +67 -1
- package/agents/analyst.md +67 -1
- package/agents/animator.md +108 -0
- package/agents/architect.md +269 -20
- package/agents/brand-guardian.md +67 -1
- package/agents/brand-strategist.md +67 -1
- package/agents/campaign-content.md +67 -1
- package/agents/campaign-email.md +67 -1
- package/agents/campaign-sms.md +67 -1
- package/agents/campaign-social.md +67 -1
- package/agents/cco.md +83 -2
- package/agents/ceo.md +108 -11
- package/agents/chairman.md +197 -0
- package/agents/cmo.md +82 -2
- package/agents/community-greeter.md +67 -1
- package/agents/community-moderator.md +67 -1
- package/agents/compliance.md +67 -1
- package/agents/copywriter.md +67 -1
- package/agents/creative-strategist.md +67 -1
- package/agents/cro.md +81 -1
- package/agents/cto.md +266 -28
- package/agents/customer-interviewer.md +67 -1
- package/agents/customer-researcher.md +67 -1
- package/agents/customer-success-manager.md +67 -1
- package/agents/customer-trainer.md +67 -1
- package/agents/cxo.md +82 -1
- package/agents/demand-creator.md +67 -1
- package/agents/demo-mover.md +67 -1
- package/agents/demo-specialist.md +67 -1
- package/agents/demo-thai-family-law.md +67 -1
- package/agents/designer.md +67 -1
- package/agents/discovery-caller.md +67 -1
- package/agents/doctor.md +269 -0
- package/agents/educate-coach.md +67 -1
- package/agents/elevate-tutor.md +67 -1
- package/agents/email-lifecycle-marketer.md +67 -1
- package/agents/engage-specialist.md +67 -1
- package/agents/events-coordinator.md +67 -1
- package/agents/foundation-builder.md +67 -1
- package/agents/funnel-architect.md +67 -1
- package/agents/gift-creator.md +67 -1
- package/agents/google-ads.md +67 -1
- package/agents/guide.md +67 -1
- package/agents/helpdesk-dispatcher.md +67 -1
- package/agents/hook-specialist.md +67 -1
- package/agents/identify-optimizer.md +67 -1
- package/agents/implementer.md +313 -45
- package/agents/incident-commander.md +67 -1
- package/agents/insights-lead.md +87 -1
- package/agents/journey-runner.md +67 -1
- package/agents/linkedin-ads.md +67 -1
- package/agents/live-sales-chat.md +67 -1
- package/agents/market-researcher.md +67 -1
- package/agents/media-buyer.md +67 -1
- package/agents/memory-keeper.md +195 -0
- package/agents/movers-customer-researcher.md +67 -1
- package/agents/movers-foundation-builder.md +67 -1
- package/agents/movers-market-researcher.md +67 -1
- package/agents/movers-pricing-strategist.md +67 -1
- package/agents/nurture-architect.md +67 -1
- package/agents/offer-architect.md +67 -1
- package/agents/onboarder.md +67 -1
- package/agents/onboarding-specialist.md +67 -1
- package/agents/operations-dashboard.md +87 -1
- package/agents/perf-engineer.md +333 -37
- package/agents/playbook-writer.md +67 -1
- package/agents/plg-strategist.md +67 -1
- package/agents/positioning-architect.md +67 -1
- package/agents/press-officer.md +67 -1
- package/agents/pricing-strategist.md +67 -1
- package/agents/privacy-officer.md +67 -1
- package/agents/referral-manager.md +67 -1
- package/agents/refine-analyst.md +67 -1
- package/agents/release-manager.md +446 -39
- package/agents/renewals-upsell-rep.md +67 -1
- package/agents/review-engineer.md +319 -45
- package/agents/rewards-steward.md +67 -1
- package/agents/sales-call-coach.md +67 -1
- package/agents/sales-closer.md +67 -1
- package/agents/security-auditor.md +343 -48
- package/agents/sell-closer.md +67 -1
- package/agents/share-amplifier.md +67 -1
- package/agents/social-media-manager.md +67 -1
- package/agents/storyteller.md +301 -0
- package/agents/strategist.md +67 -1
- package/agents/strategy-aligner.md +67 -1
- package/agents/support-agent.md +67 -1
- package/agents/tagger.md +327 -0
- package/agents/tech-writer.md +195 -22
- package/agents/test-engineer.md +398 -29
- package/agents/tiktok-ads.md +67 -1
- package/agents/tracking-engineer.md +67 -1
- package/agents/trailkeeper.md +181 -0
- package/agents/upsell-strategist.md +67 -1
- package/agents/voice.md +67 -1
- package/agents/w1-recon.md +1 -1
- package/agents/w2-decide.md +1 -1
- package/agents/w3-edit.md +8 -2
- package/agents/w4-verify.md +13 -0
- package/agents/workflow-optimiser.md +81 -1
- package/commands/close.md +916 -160
- package/commands/deploy.md +102 -724
- package/commands/do.md +58 -2
- package/commands/sweep.md +159 -0
- package/commands/tasks.md +222 -0
- package/hooks/scripts/dev-only.sh +135 -0
- package/hooks/scripts/git-add-guard.sh +37 -2
- package/hooks/scripts/session-start.sh +32 -4
- package/package.json +1 -1
- package/rules/scripts.md +85 -0
- package/scripts/CLAUDE.md +315 -0
- package/scripts/ad-copy-lint.sh +656 -0
- package/scripts/agent-actor-parity.sh +129 -0
- package/scripts/blocks-manifest-cached.sh +100 -0
- package/scripts/chat-context-check.sh +89 -0
- package/scripts/chrome.mjs +18 -0
- package/scripts/close-metrics.sh +587 -0
- package/scripts/close-owner.sh +326 -0
- package/scripts/db-sync-lock-check.sh +116 -0
- package/scripts/deploy-emit.sh +311 -0
- package/scripts/deploy-gate-check.sh +155 -0
- package/scripts/deploy-ready.sh +78 -0
- package/scripts/deploy-record.sh +605 -0
- package/scripts/deploy-schema-check.sh +58 -0
- package/scripts/deploy.sh +393 -243
- package/scripts/do-auto.sh +127 -26
- package/scripts/do-board.sh +429 -0
- package/scripts/do-close.sh +1184 -0
- package/scripts/do-consumer-sweep.sh +18 -1
- package/scripts/do-decide.sh +476 -0
- package/scripts/do-fleet.sh +8 -2
- package/scripts/do-plan-json.mjs +110 -12
- package/scripts/do-prove-selftest.sh +108 -0
- package/scripts/do-prove.sh +86 -10
- package/scripts/do-rank.py +200 -3
- package/scripts/do-reconcile.sh +73 -12
- package/scripts/do-signal.sh +101 -23
- package/scripts/do-smoke.sh +18 -1
- package/scripts/do-w4-gates.sh +11 -1
- package/scripts/do-world-check.sh +153 -0
- package/scripts/download-stats.sh +172 -0
- package/scripts/factory-brief-check.sh +330 -0
- package/scripts/factory-check.sh +18 -1
- package/scripts/factory-close-check.sh +257 -0
- package/scripts/factory-emit.sh +211 -0
- package/scripts/factory-executor-check.mjs +353 -0
- package/scripts/factory-peak.sh +301 -0
- package/scripts/factory-repo.sh +71 -0
- package/scripts/factory-review-check.mjs +61 -0
- package/scripts/factory-tasks-check.sh +18 -1
- package/scripts/fixtures/factory-brief-real.md +44 -0
- package/scripts/flywheel-outcome.sh +63 -0
- package/scripts/gate-reaper-check.sh +98 -0
- package/scripts/gate-reaper.sh +9 -0
- package/scripts/gate-watchdog.sh +619 -0
- package/scripts/gc-content-check.sh +142 -0
- package/scripts/gh-traffic-capture.sh +153 -0
- package/scripts/govern-order-check.sh +202 -0
- package/scripts/governor-doors-check.sh +86 -5
- package/scripts/health.sh +448 -0
- package/scripts/id-inventory.mjs +418 -0
- package/scripts/incident.sh +212 -0
- package/scripts/land.sh +755 -45
- package/scripts/lib/gc-finished.sh +77 -0
- package/scripts/livekit-ratchet.sh +18 -1
- package/scripts/machine-check.sh +1 -1
- package/scripts/memory-index-budget.sh +79 -0
- package/scripts/npm-downloads.sh +109 -0
- package/scripts/one-agents.mjs +204 -8
- package/scripts/one-resume.sh +31 -3
- package/scripts/pr-body.sh +335 -0
- package/scripts/preview-fd-check.sh +289 -0
- package/scripts/redirect-lint.sh +169 -0
- package/scripts/release.sh +40 -6
- package/scripts/resume-lost-sessions.sh +68 -0
- package/scripts/shoot-pages.mjs +140 -0
- package/scripts/signal-meta-backfill.ts +451 -0
- package/scripts/signal-watch.sh +63 -6
- package/scripts/speed-cache-check.sh +12 -2
- package/scripts/sweep.sh +426 -0
- package/scripts/task-titles-dump.ts +101 -0
- package/scripts/test-cached.sh +47 -10
- package/scripts/test-lanes.sh +14 -0
- package/scripts/thread-name-backfill.ts +215 -0
- package/scripts/triage-shape-check.sh +149 -0
- package/scripts/tsc-cached.sh +155 -8
- package/scripts/typedb-flake-check.sh +3 -1
- package/scripts/urls-lint.sh +8 -0
- package/scripts/verify-board-doors.sh +80 -0
- package/scripts/verify-fast.sh +159 -6
- package/scripts/worktree-up.sh +21 -3
- package/skills/astro/SKILL.md +9 -3
- package/skills/astro/optimize-performance.md +3 -2
- package/skills/cloudflare/SKILL.md +3 -2
- package/skills/cloudflare-security-audit/AI-AND-LLM.md +83 -0
- package/skills/cloudflare-security-audit/ATTACK-CLASSES.md +130 -0
- package/skills/cloudflare-security-audit/CLIENT-SIDE.md +83 -0
- package/skills/cloudflare-security-audit/CLOUD-AND-DEPLOYMENT.md +86 -0
- package/skills/cloudflare-security-audit/DATA-ISOLATION-AND-LIFECYCLE.md +84 -0
- package/skills/cloudflare-security-audit/DESKTOP-MOBILE-AND-LOCAL-IPC.md +89 -0
- package/skills/cloudflare-security-audit/HUNTING.md +251 -0
- package/skills/cloudflare-security-audit/LICENSE +21 -0
- package/skills/cloudflare-security-audit/MEMORY-SAFETY-AND-BINARY.md +101 -0
- package/skills/cloudflare-security-audit/PROTOCOLS-RPC-AND-MESSAGING.md +81 -0
- package/skills/cloudflare-security-audit/PROVENANCE.md +78 -0
- package/skills/cloudflare-security-audit/RECONNAISSANCE.md +156 -0
- package/skills/cloudflare-security-audit/RESOURCE-EXHAUSTION-AND-AVAILABILITY.md +78 -0
- package/skills/cloudflare-security-audit/SKILL.md +192 -0
- package/skills/cloudflare-security-audit/SUPPLY-CHAIN-AND-RELEASE.md +73 -0
- package/skills/cloudflare-security-audit/VALIDATION-AND-REPORTING.md +186 -0
- package/skills/cloudflare-security-audit/WEB-PROTOCOL-AND-AUTH.md +105 -0
- package/skills/cloudflare-security-audit/report-schema.json +461 -0
- package/skills/cloudflare-security-audit/validate-coverage-ledger.cjs +872 -0
- package/skills/cloudflare-security-audit/validate-coverage-ledger.test.cjs +740 -0
- package/skills/cloudflare-security-audit/validate-findings.cjs +773 -0
- package/skills/cloudflare-security-audit/validate-findings.test.cjs +652 -0
- package/skills/deploy/REFERENCE.md +713 -0
- package/skills/deploy/SKILL.md +140 -0
- package/skills/fleet-audit/SKILL.md +58 -0
- package/skills/meeting/SKILL.md +220 -0
- package/skills/planning/SKILL.md +256 -0
- package/skills/shadcn/SKILL.md +1 -1
- package/skills/typedb/SKILL.md +7 -0
- package/skills/voice/SKILL.md +94 -6
- package/skills/voice/corpus-check.sh +87 -0
- package/templates/template-agent.md +7 -1
- package/templates/template-feature.md +9 -0
- package/templates/template-todo.md +29 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# close-owner.sh — resolve WHO owns a close and WHO they report to.
|
|
3
|
+
#
|
|
4
|
+
# manifest: needs-env
|
|
5
|
+
# (reads $ROOT/one.ie/ai/agents — degrades to `owner=ceo via=fallback hits=0`
|
|
6
|
+
# off-monorepo, which is a no-op, not a lie. Not `portable`: it names the path.)
|
|
7
|
+
#
|
|
8
|
+
# /close needs two deterministic facts before any model speaks: which agent owns
|
|
9
|
+
# the work, and which agent it reports to. Both are declared in the roster
|
|
10
|
+
# (one.ie/ai/agents/*/agent.md); neither is a judgment call. This script answers
|
|
11
|
+
# them so the command executes a fact instead of substituting for one.
|
|
12
|
+
#
|
|
13
|
+
# close-owner.sh --tags "engineering,contracts" -> owner + superior chain
|
|
14
|
+
# close-owner.sh --owner cmo -> just the chain above cmo
|
|
15
|
+
# close-owner.sh --roster -> coverage census
|
|
16
|
+
# close-owner.sh --self-test -> proves it can go RED
|
|
17
|
+
#
|
|
18
|
+
# THE MATCH RULE, and why it is the bare words only:
|
|
19
|
+
# `subscribes:` carries TWO shapes and they are different lanes (root CLAUDE.md).
|
|
20
|
+
# - signal: campaign:brief <- workflow trigger_source, read by triggerWorkflows
|
|
21
|
+
# - marketing <- the routing STAKE, read by matchSubscribers
|
|
22
|
+
# A namespaced tag matches zero signals. Matching on it would hand work to an
|
|
23
|
+
# agent the world will never actually route to.
|
|
24
|
+
set -uo pipefail
|
|
25
|
+
|
|
26
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
27
|
+
AGENTS="$ROOT/one.ie/ai/agents"
|
|
28
|
+
|
|
29
|
+
# specialist first: ties break DOWNWARD. Sending a director to do specialist work
|
|
30
|
+
# is how a director stops being available for the judgment only they can make.
|
|
31
|
+
_tier_rank() {
|
|
32
|
+
case "${1:-}" in
|
|
33
|
+
specialist) echo 0 ;; director) echo 1 ;; ceo) echo 2 ;; chairman) echo 3 ;;
|
|
34
|
+
*) echo 9 ;; # tool / undeclared — never an owner
|
|
35
|
+
esac
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_field() { grep -m1 "^$2:" "$1" 2>/dev/null | cut -d' ' -f2- | tr -d '\r' | xargs 2>/dev/null; }
|
|
39
|
+
|
|
40
|
+
# The bare stake words only — skip every `- signal:` line.
|
|
41
|
+
#
|
|
42
|
+
# TWO YAML SHAPES, and missing the second one made 24 agents invisible.
|
|
43
|
+
# Block: subscribes:\n - ship\n - deploy
|
|
44
|
+
# Inline: subscribes: [engineering, do-event, ship, deploy, contracts]
|
|
45
|
+
# The old parser matched `/^subscribes:/` and immediately `next`ed — which for the
|
|
46
|
+
# inline shape SKIPS THE ONLY LINE THE TAGS ARE ON, so the agent parsed as having
|
|
47
|
+
# no stake and was silently unreachable by tag. Measured 2026-09-15: 24 of ~108
|
|
48
|
+
# agent files use the inline shape, and `close-owner.sh --tags ship` answered
|
|
49
|
+
# `hits=0 confidence=none` while release-manager/agent.md:18 stakes both `ship`
|
|
50
|
+
# and `deploy`. Every close on those tags fell through to the CEO fallback and
|
|
51
|
+
# read as "nobody owns this area" — the routing was wrong, not the roster.
|
|
52
|
+
_stakes() {
|
|
53
|
+
awk '
|
|
54
|
+
/^subscribes:/ {
|
|
55
|
+
if ($0 ~ /\[/) { # inline flow list — tags are on THIS line
|
|
56
|
+
l=$0; sub(/^[^[]*\[/,"",l); sub(/\].*$/,"",l)
|
|
57
|
+
n=split(l,a,",")
|
|
58
|
+
for (i=1;i<=n;i++) {
|
|
59
|
+
t=a[i]; gsub(/^[ \t"]+|[ \t"]+$/,"",t)
|
|
60
|
+
if (t !~ /^signal:/ && t !~ /:/ && t != "") print t
|
|
61
|
+
}
|
|
62
|
+
f=0; next
|
|
63
|
+
}
|
|
64
|
+
f=1; next # block list — tags are on following lines
|
|
65
|
+
}
|
|
66
|
+
f&&/^[a-zA-Z_]+:/{f=0}
|
|
67
|
+
f&&/^[[:space:]]*-[[:space:]]/{
|
|
68
|
+
line=$0; sub(/^[[:space:]]*-[[:space:]]*/,"",line)
|
|
69
|
+
if (line !~ /^signal:/ && line !~ /:/ && line != "") print line
|
|
70
|
+
}' "$1" 2>/dev/null | tr -d '\r' | xargs -n1 2>/dev/null
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_superior() { # walk the declared chain, then the tier ladder
|
|
74
|
+
local name="$1" hops=0 out=""
|
|
75
|
+
while [ -n "$name" ] && [ "$hops" -lt 8 ]; do
|
|
76
|
+
local f="$AGENTS/$name/agent.md"
|
|
77
|
+
[ -f "$f" ] || break
|
|
78
|
+
local rt; rt="$(_field "$f" reports_to)"
|
|
79
|
+
if [ -z "$rt" ]; then
|
|
80
|
+
case "$(_field "$f" tier)" in
|
|
81
|
+
specialist) rt="ceo" ;; # no director declared for its domain
|
|
82
|
+
director) rt="ceo" ;;
|
|
83
|
+
ceo) rt="chairman" ;;
|
|
84
|
+
chairman) rt="" ;; # answers to the human
|
|
85
|
+
*) rt="ceo" ;; # repo law: CEO is the standing receiver
|
|
86
|
+
esac
|
|
87
|
+
fi
|
|
88
|
+
[ -z "$rt" ] && break
|
|
89
|
+
out="$out $rt"; name="$rt"; hops=$((hops+1))
|
|
90
|
+
done
|
|
91
|
+
echo "$out" | xargs 2>/dev/null
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
cmd_roster() {
|
|
95
|
+
local tot=0 rt=0 ti=0 sub=0 bare=0
|
|
96
|
+
for d in "$AGENTS"/*/; do
|
|
97
|
+
local f="$d/agent.md"; [ -f "$f" ] || continue
|
|
98
|
+
tot=$((tot+1))
|
|
99
|
+
[ -n "$(_field "$f" reports_to)" ] && rt=$((rt+1))
|
|
100
|
+
[ -n "$(_field "$f" tier)" ] && ti=$((ti+1))
|
|
101
|
+
grep -q '^subscribes:' "$f" && sub=$((sub+1))
|
|
102
|
+
[ -n "$(_stakes "$f")" ] && bare=$((bare+1))
|
|
103
|
+
done
|
|
104
|
+
echo "agents=$tot reports_to=$rt tier=$ti subscribes=$sub bare_stakes=$bare"
|
|
105
|
+
echo "note: agents WITHOUT bare stakes cannot be matched by tag — they need the ladder fallback."
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# How many agents stake a given word. A word many agents carry cannot single one
|
|
109
|
+
# out — `marketing` is on 16 of them, so one hit against it is a tie broken by
|
|
110
|
+
# directory order, not a match.
|
|
111
|
+
GENERIC_AT=${CLOSE_OWNER_GENERIC_AT:-3}
|
|
112
|
+
|
|
113
|
+
# THE INDEX — one awk pass over the whole roster, built once, reused by every
|
|
114
|
+
# lookup. It replaced a per-agent `_stakes` fork called from inside two nested
|
|
115
|
+
# loops: a --self-test spawned ~1300 awk processes and took 101s. Same answers,
|
|
116
|
+
# one pass. Rows are `name<TAB>tier<TAB>reports_to<TAB>stake`, one per stake.
|
|
117
|
+
# The memo is a FILE, not a variable. Every `$(_index)` runs in a subshell, so a
|
|
118
|
+
# variable assignment inside it is discarded the moment the subshell exits — the
|
|
119
|
+
# memo silently never hit and awk re-ran on every call (measured: no improvement
|
|
120
|
+
# at all, 101s -> 70s, when the whole point was one pass).
|
|
121
|
+
_INDEX_FILE="${TMPDIR:-/tmp}/close-owner-index.$$"
|
|
122
|
+
trap 'rm -f "$_INDEX_FILE"' EXIT
|
|
123
|
+
_index() {
|
|
124
|
+
[ -s "$_INDEX_FILE" ] && { cat "$_INDEX_FILE"; return; }
|
|
125
|
+
awk '
|
|
126
|
+
FNR==1 { name=""; tier=""; rt=""; insub=0
|
|
127
|
+
n=split(FILENAME,p,"/"); dir=p[n-1] }
|
|
128
|
+
/^name:/ { sub(/^name:[[:space:]]*/,""); name=$0 }
|
|
129
|
+
/^tier:/ { sub(/^tier:[[:space:]]*/,""); tier=$0 }
|
|
130
|
+
/^reports_to:/ { sub(/^reports_to:[[:space:]]*/,""); rt=$0 }
|
|
131
|
+
# Both YAML shapes — see _stakes() above for why the inline one is not optional.
|
|
132
|
+
# `name:` precedes `subscribes:` in every agent.md, so name is already bound here.
|
|
133
|
+
/^subscribes:/ {
|
|
134
|
+
if ($0 ~ /\[/) {
|
|
135
|
+
il=$0; sub(/^[^[]*\[/,"",il); sub(/\].*$/,"",il)
|
|
136
|
+
ic=split(il,ia,",")
|
|
137
|
+
for (ii=1;ii<=ic;ii++) {
|
|
138
|
+
it=ia[ii]; gsub(/^[ \t"]+|[ \t"]+$/,"",it)
|
|
139
|
+
if (it !~ /^signal:/ && it !~ /:/ && it != "")
|
|
140
|
+
print (name==""?dir:name) "\t" tier "\t" rt "\t" it
|
|
141
|
+
}
|
|
142
|
+
insub=0; next
|
|
143
|
+
}
|
|
144
|
+
insub=1; next
|
|
145
|
+
}
|
|
146
|
+
insub && /^[a-zA-Z_]+:/ { insub=0 }
|
|
147
|
+
insub && /^[[:space:]]*-[[:space:]]/ {
|
|
148
|
+
l=$0; sub(/^[[:space:]]*-[[:space:]]*/,"",l); gsub(/[[:space:]]+$/,"",l)
|
|
149
|
+
if (l !~ /^signal:/ && l !~ /:/ && l != "") {
|
|
150
|
+
print (name==""?dir:name) "\t" tier "\t" rt "\t" l
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
' "$AGENTS"/*/agent.md 2>/dev/null > "$_INDEX_FILE"
|
|
154
|
+
cat "$_INDEX_FILE"
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
_tag_breadth() {
|
|
158
|
+
_index | awk -F'\t' -v T="$1" '$4==T{a[$1]=1} END{print length(a)}'
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
# Every agent whose stakes intersect the tags, tier-ranked. This is what a refusal
|
|
162
|
+
# hands upward — a decision without the options is not a decision.
|
|
163
|
+
_candidates() {
|
|
164
|
+
local tags="$1"; local IFS=,; read -ra want <<< "$tags"; unset IFS
|
|
165
|
+
local agent tier_w
|
|
166
|
+
while IFS=$'\t' read -r agent tier_w; do
|
|
167
|
+
[ -n "$agent" ] || continue
|
|
168
|
+
local rank; rank="$(_tier_rank "$tier_w")"
|
|
169
|
+
[ "$rank" -ge 9 ] && continue
|
|
170
|
+
local hits=0 s w
|
|
171
|
+
for s in $(_index | awk -F'\t' -v A="$agent" '$1==A{print $4}'); do
|
|
172
|
+
for w in "${want[@]}"; do [ "$(echo "$w" | xargs)" = "$s" ] && hits=$((hits+1)); done
|
|
173
|
+
done
|
|
174
|
+
[ "$hits" -gt 0 ] && echo "$rank:$agent($hits)"
|
|
175
|
+
done <<< "$(_index | awk -F'\t' '{print $1"\t"$2}' | sort -u)" | sort | cut -d: -f2
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
cmd_resolve() {
|
|
179
|
+
local tags="$1" best="" best_rank=9 best_hits=0 best_specific=0 contenders=0
|
|
180
|
+
local IFS=,; read -ra want <<< "$tags"; unset IFS
|
|
181
|
+
|
|
182
|
+
# Breadth is a property of the TAG, not of the agent being examined, and only
|
|
183
|
+
# the requested tags matter — so it is computed once here rather than inside
|
|
184
|
+
# the agent loop. Calling _tag_breadth per matching stake made this quadratic
|
|
185
|
+
# over file reads (a --self-test went from ~1s to >120s); bash 3.2 has no
|
|
186
|
+
# associative arrays, so the answers ride a parallel indexed array.
|
|
187
|
+
local -a want_breadth=()
|
|
188
|
+
local i
|
|
189
|
+
for i in "${!want[@]}"; do
|
|
190
|
+
want[$i]="$(echo "${want[$i]}" | xargs)"
|
|
191
|
+
want_breadth[$i]="$(_tag_breadth "${want[$i]}")"
|
|
192
|
+
done
|
|
193
|
+
|
|
194
|
+
# One pass over the index: agent -> its stakes, already parsed.
|
|
195
|
+
local line agent tier_w
|
|
196
|
+
while IFS=$'\t' read -r agent tier_w; do
|
|
197
|
+
[ -n "$agent" ] || continue
|
|
198
|
+
local rank; rank="$(_tier_rank "$tier_w")"
|
|
199
|
+
[ "$rank" -ge 9 ] && continue # tool / undeclared: not an owner
|
|
200
|
+
local hits=0 specific=0 s j
|
|
201
|
+
for s in $(_index | awk -F'\t' -v A="$agent" '$1==A{print $4}'); do
|
|
202
|
+
for j in "${!want[@]}"; do
|
|
203
|
+
if [ "${want[$j]}" = "$s" ]; then
|
|
204
|
+
hits=$((hits+1))
|
|
205
|
+
[ "${want_breadth[$j]}" -le "$GENERIC_AT" ] && specific=$((specific+1))
|
|
206
|
+
fi
|
|
207
|
+
done
|
|
208
|
+
done
|
|
209
|
+
[ "$hits" -eq 0 ] && continue
|
|
210
|
+
contenders=$((contenders+1))
|
|
211
|
+
if [ "$specific" -gt "$best_specific" ] \
|
|
212
|
+
|| { [ "$specific" -eq "$best_specific" ] && [ "$hits" -gt "$best_hits" ]; } \
|
|
213
|
+
|| { [ "$specific" -eq "$best_specific" ] && [ "$hits" -eq "$best_hits" ] && [ "$rank" -lt "$best_rank" ]; }; then
|
|
214
|
+
best="$agent"; best_rank="$rank"; best_hits="$hits"; best_specific="$specific"
|
|
215
|
+
fi
|
|
216
|
+
done <<< "$(_index | awk -F'\t' '{print $1"\t"$2}' | sort -u)"
|
|
217
|
+
|
|
218
|
+
if [ -z "$best" ]; then
|
|
219
|
+
# Not a failure — the documented fallback. world:route answers reason:'ceo'
|
|
220
|
+
# for a workspace with no history, and the CEO is the standing receiver.
|
|
221
|
+
echo "owner=ceo via=fallback(no-tag-match) hits=0 confidence=none"
|
|
222
|
+
echo "chain=$(_superior ceo)"
|
|
223
|
+
return 0
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
# THE REFUSAL. Evidence that cannot separate one candidate from several is not
|
|
227
|
+
# a match — it is directory order wearing a receipt. Modelled on factory:size
|
|
228
|
+
# returning `unsized` for an empty path set (resolvers/factory.ts:206) rather
|
|
229
|
+
# than guessing a tier. Refusing routes to the ladder, which explains itself.
|
|
230
|
+
if [ "$best_specific" -eq 0 ] && [ "$contenders" -gt 1 ]; then
|
|
231
|
+
echo "owner=REFUSED via=ambiguous hits=$best_hits contenders=$contenders confidence=low"
|
|
232
|
+
echo "reason=only generic tags matched (each carried by >$GENERIC_AT agents); $contenders candidates were indistinguishable"
|
|
233
|
+
# A refusal that names nothing forces the asker to redo the search. The whole
|
|
234
|
+
# point of refusing is to hand the decision UP with the evidence attached, so
|
|
235
|
+
# the shortlist rides with it — this is the notify-a-human payload.
|
|
236
|
+
echo "candidates=$(_candidates "$tags" | tr '\n' ' ' | xargs)"
|
|
237
|
+
echo "fallback=ceo chain=$(_superior ceo)"
|
|
238
|
+
return 0
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
local conf="high"; [ "$best_specific" -eq 0 ] && conf="medium"
|
|
242
|
+
echo "owner=$best via=subscribes hits=$best_hits specific=$best_specific contenders=$contenders confidence=$conf"
|
|
243
|
+
echo "chain=$(_superior "$best")"
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
cmd_self_test() {
|
|
247
|
+
local fails=0
|
|
248
|
+
_expect() { # name, actual, expected-substring
|
|
249
|
+
if grep -q -- "$3" <<< "$2"; then echo " ok $1"; else echo " RED $1 — got: $2"; fails=$((fails+1)); fi
|
|
250
|
+
}
|
|
251
|
+
echo "close-owner --self-test"
|
|
252
|
+
|
|
253
|
+
# The case that shipped broken: this close's own tags named one of five
|
|
254
|
+
# candidates on a single generic hit, and the tie broke on directory order.
|
|
255
|
+
_expect "the tags THIS close used are REFUSED, not confidently answered" \
|
|
256
|
+
"$(cmd_resolve 'engineering,inbox,tasks')" "owner=REFUSED"
|
|
257
|
+
_expect "a refusal still routes — it names the ladder fallback" \
|
|
258
|
+
"$(cmd_resolve 'engineering,inbox,tasks')" "fallback=ceo"
|
|
259
|
+
_expect "a bare generic tag alone is ambiguous, not a match" \
|
|
260
|
+
"$(cmd_resolve engineering)" "owner=REFUSED"
|
|
261
|
+
# CORRECTED BELIEF, not a weakened test. This assertion used to read "a
|
|
262
|
+
# marketing tag reaches a marketing agent". It does not and never did:
|
|
263
|
+
# `marketing` is staked by 16 agents, `sql` by 17. The old test passed because
|
|
264
|
+
# the resolver picked one by directory order and the assertion only checked
|
|
265
|
+
# that SOMETHING was picked. The truthful assertion is that it refuses — and
|
|
266
|
+
# that the refusal carries the shortlist, so the decision can be made upward.
|
|
267
|
+
_expect "a 16-agent tag refuses rather than picking by directory order" \
|
|
268
|
+
"$(cmd_resolve marketing)" "owner=REFUSED"
|
|
269
|
+
_expect "the refusal hands up a candidate shortlist, not just a no" \
|
|
270
|
+
"$(cmd_resolve marketing)" "candidates="
|
|
271
|
+
_expect "a specific tag (1 agent stakes it) still resolves with high confidence" \
|
|
272
|
+
"$(cmd_resolve spec)" "confidence=high"
|
|
273
|
+
_expect "an unknown tag falls back to the CEO, it does not fail" \
|
|
274
|
+
"$(cmd_resolve zzz-no-such-tag)" "owner=ceo"
|
|
275
|
+
_expect "the chain from a director ends at the chairman" \
|
|
276
|
+
"chain=$(_superior cmo)" "chairman"
|
|
277
|
+
_expect "the chain from the ceo is just the chairman" \
|
|
278
|
+
"chain=$(_superior ceo)" "chairman"
|
|
279
|
+
|
|
280
|
+
# A namespaced trigger word must NEVER match — it is the other lane.
|
|
281
|
+
local ns; ns="$(cmd_resolve 'campaign:brief')"
|
|
282
|
+
_expect "a namespaced trigger word matches NOBODY (falls back)" "$ns" "owner=ceo"
|
|
283
|
+
|
|
284
|
+
# THE INLINE-YAML SHAPE. `subscribes:` is written two ways across the roster and
|
|
285
|
+
# for a long time only one was parsed: `/^subscribes:/ … next` skipped the very
|
|
286
|
+
# line an inline `[a, b, c]` keeps its tags on, so those agents had NO stake as
|
|
287
|
+
# far as this resolver was concerned. Measured 2026-09-15 — 24 of ~108 agent
|
|
288
|
+
# files use the inline shape, and `--tags deploy,release,ship` answered
|
|
289
|
+
# `hits=0 confidence=none` while release-manager/agent.md:18 reads
|
|
290
|
+
# `subscribes: [engineering, do-event, ship, deploy, contracts]`. It did not look
|
|
291
|
+
# like a bug: "no agent staked this" is a legitimate answer this script gives on
|
|
292
|
+
# purpose, so every close on those tags fell through to the CEO and read as an
|
|
293
|
+
# unowned area of the company rather than a parser that could not see the roster.
|
|
294
|
+
# These two assert the inline shape resolves; the file they depend on is a real
|
|
295
|
+
# one, so a rewrite of that frontmatter to the block shape keeps them green.
|
|
296
|
+
_expect "an INLINE subscribes: [a, b] list is parsed (not skipped)" \
|
|
297
|
+
"$(cmd_resolve 'deploy,ship')" "owner=release-manager"
|
|
298
|
+
_expect "an inline stake carries full confidence, like a block one" \
|
|
299
|
+
"$(cmd_resolve 'deploy,ship')" "confidence=high"
|
|
300
|
+
|
|
301
|
+
# RED PROOF: gut the mechanism the resolver ACTUALLY reads and assert the
|
|
302
|
+
# matches stop working. A checker that stays green against a gutted mechanism
|
|
303
|
+
# proves nothing — and this one caught exactly that on itself: it used to gut
|
|
304
|
+
# `_stakes`, and when the resolver was rewired onto `_index` the proof went on
|
|
305
|
+
# passing while guarding a function nothing called any more. It was repointed,
|
|
306
|
+
# never relaxed. If you move the mechanism again, move this with it.
|
|
307
|
+
echo " -- red proof --"
|
|
308
|
+
_index() { echo ""; }
|
|
309
|
+
local gutted; gutted="$(cmd_resolve spec)"
|
|
310
|
+
if grep -q "via=fallback" <<< "$gutted"; then
|
|
311
|
+
echo " ok gutted index falls back — the check is load-bearing"
|
|
312
|
+
else
|
|
313
|
+
echo " RED gutted index STILL matched: $gutted"; fails=$((fails+1))
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
echo
|
|
317
|
+
[ "$fails" -eq 0 ] && { echo "PASS"; return 0; } || { echo "FAIL ($fails)"; return 1; }
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
case "${1:---help}" in
|
|
321
|
+
--tags) shift; cmd_resolve "${1:-}" ;;
|
|
322
|
+
--owner) shift; echo "chain=$(_superior "${1:-}")" ;;
|
|
323
|
+
--roster) cmd_roster ;;
|
|
324
|
+
--self-test) cmd_self_test ;;
|
|
325
|
+
*) sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//' ;;
|
|
326
|
+
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
|