@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,77 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# gc-finished.sh — "is this branch's work already on the platform?"
|
|
3
|
+
#
|
|
4
|
+
# ONE definition, sourced by both the sweep (do-auto.sh --gc) and its red proof
|
|
5
|
+
# (gc-content-check.sh). The matcher for hook:governor-escape lives in a shared
|
|
6
|
+
# lib for the same reason: a checker that re-implements what it checks proves
|
|
7
|
+
# only that two copies agree.
|
|
8
|
+
#
|
|
9
|
+
# NOT `rev-list --count BASE..branch`. That counts COMMITS, and a branch whose
|
|
10
|
+
# patch reached BASE under a different sha — cherry-picked, rebased, landed by a
|
|
11
|
+
# squash — keeps commits of its own for ever. Measured 2026-09-07 on this repo:
|
|
12
|
+
# fix/webhook-500's single patch was already in dev under another sha, so the
|
|
13
|
+
# count read "1 unmerged" and the sweep had been keeping a ~120MB checkout of
|
|
14
|
+
# work the platform already had. A sweep that cannot recognise its own work is
|
|
15
|
+
# not a sweep, it is a hoard.
|
|
16
|
+
#
|
|
17
|
+
# `git cherry` is the obvious fix and is NOT sufficient alone. A MERGE commit
|
|
18
|
+
# carries no patch of its own, so cherry reports 0-new for a branch that holds
|
|
19
|
+
# content nothing else has — merge/trunk-reconcile, the same day, 24 files and
|
|
20
|
+
# 3416 lines dev had never seen. Sweeping on cherry would have deleted the only
|
|
21
|
+
# checkout of a hand-made merge resolution.
|
|
22
|
+
#
|
|
23
|
+
# The predicate true in BOTH cases is content identity: every path the branch
|
|
24
|
+
# touched since the merge base is now byte-identical in BASE. A rebased duplicate
|
|
25
|
+
# passes; a merge carrying unique files fails; an ordinary unlanded branch fails.
|
|
26
|
+
|
|
27
|
+
# gc_finished <base> <branch> -> 0 = finished (its content is in base), 1 = not
|
|
28
|
+
gc_finished() {
|
|
29
|
+
local base="$1" b="$2"
|
|
30
|
+
local -a paths=()
|
|
31
|
+
# Paths the branch changed since the merge base. `...` (three dots) is the
|
|
32
|
+
# load-bearing part: `..` would also list what BASE changed underneath it, and
|
|
33
|
+
# a busy trunk would then make every branch look like it carries everything.
|
|
34
|
+
while IFS= read -r f; do [ -n "$f" ] && paths+=("$f"); done < <(git diff --name-only "${base}...${b}" 2>/dev/null)
|
|
35
|
+
# A branch that changed nothing since the merge base is finished by definition.
|
|
36
|
+
[ ${#paths[@]} -eq 0 ] && return 0
|
|
37
|
+
# Compare those paths as they stand NOW in each tip. --quiet exits 1 on any
|
|
38
|
+
# difference, which is the answer, not an error — never let `set -e` see it.
|
|
39
|
+
git diff --quiet "$base" "$b" -- "${paths[@]}" 2>/dev/null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# gc_carries <base> <branch> — how many paths the BRANCH still carries that base
|
|
43
|
+
# does not have. This is the number the sweep prints, so it has to mean what it
|
|
44
|
+
# says: `git diff --name-only base branch | wc -l` (two dots) counts everything
|
|
45
|
+
# that differs in EITHER direction, so a branch adding one file to a trunk that
|
|
46
|
+
# has moved on by 500 commits reports "carries 508 paths". Measured on this repo
|
|
47
|
+
# 2026-09-07: fix/webhook-500-again touches ONE file and read 508. The paths that
|
|
48
|
+
# belong to the branch are the ones it changed since the merge base AND that
|
|
49
|
+
# still differ from base now — the same set gc_finished tests.
|
|
50
|
+
gc_carries() {
|
|
51
|
+
local base="$1" b="$2" n=0 f
|
|
52
|
+
while IFS= read -r f; do
|
|
53
|
+
[ -n "$f" ] || continue
|
|
54
|
+
git diff --quiet "$base" "$b" -- "$f" 2>/dev/null || n=$((n+1))
|
|
55
|
+
done < <(git diff --name-only "${base}...${b}" 2>/dev/null)
|
|
56
|
+
echo "$n"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Dirt that is not work: build output, worktree bookkeeping, a regenerated data
|
|
60
|
+
# file. None of it is recoverable from a checkout and all of it regenerates, so
|
|
61
|
+
# it must not stand between a finished branch and its removal.
|
|
62
|
+
GC_EPHEMERAL="${GC_EPHEMERAL:-one\.ie/web/\.astro|one\.ie/web/\.wrangler|one\.ie/web/\.preview|\.dev\.pid|\.worktree-port|deploy-runs\.json|node_modules}"
|
|
63
|
+
|
|
64
|
+
# gc_real_dirt <worktree> — the uncommitted lines that are actually WORK, empty
|
|
65
|
+
# when the tree is clean or carries only ephemera.
|
|
66
|
+
#
|
|
67
|
+
# The `|| true` is load-bearing and is why this is a function rather than an
|
|
68
|
+
# inline pipeline. `grep -v` exits 1 when it filters EVERYTHING out — which is
|
|
69
|
+
# exactly the clean-worktree case — and the caller runs under `set -euo
|
|
70
|
+
# pipefail`, so `real="$(... | grep -v ...)"` killed the sweep the first time it
|
|
71
|
+
# met a worktree it was supposed to remove. Measured 2026-09-07: the sweep
|
|
72
|
+
# printed four keep lines, exited 1, and never reached its own summary; piped
|
|
73
|
+
# through `cat` or `tail` the failure was invisible, which is how it nearly got
|
|
74
|
+
# reported as a clean run.
|
|
75
|
+
gc_real_dirt() {
|
|
76
|
+
git -C "$1" status --porcelain 2>/dev/null | grep -vE "$GC_EPHEMERAL" || true
|
|
77
|
+
}
|
|
@@ -10,6 +10,23 @@
|
|
|
10
10
|
|
|
11
11
|
set -uo pipefail
|
|
12
12
|
|
|
13
|
+
# _GATE — route a heavy compute through the machine governor. A gate_lock only
|
|
14
|
+
# dedupes IDENTICAL work; a SLOT is what bounds N worktrees each running one of
|
|
15
|
+
# these at once (measured 2026-09-07: three concurrent 2.5GB typecheckers, every
|
|
16
|
+
# lock uncontended, load 171). Empty when already inside a gate, so a nested call
|
|
17
|
+
# inherits the outer slot rather than taking a second one.
|
|
18
|
+
#
|
|
19
|
+
# Resolves gate-run.sh from its OWN directory, deliberately: an earlier version
|
|
20
|
+
# keyed off $ROOT and got inserted above the line that sets it, so _GATE was
|
|
21
|
+
# silently empty and every call ran ungoverned -- a fail-OPEN, which is the exact
|
|
22
|
+
# defect this preamble exists to close.
|
|
23
|
+
_GATE=()
|
|
24
|
+
if [ "${GOVERN_IN_GATE:-0}" != "1" ]; then
|
|
25
|
+
_GR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/gate-run.sh"
|
|
26
|
+
[ -f "$_GR" ] && _GATE=( bash "$_GR" "compute:$(basename "${BASH_SOURCE[0]}")" -- )
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
|
|
13
30
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
14
31
|
cd "$ROOT" || exit 1
|
|
15
32
|
|
|
@@ -21,7 +38,7 @@ if [ ! -f "one.ie/web/$TEST_REL" ]; then
|
|
|
21
38
|
exit 1
|
|
22
39
|
fi
|
|
23
40
|
|
|
24
|
-
(cd one.ie/web && bunx vitest run "$TEST_REL") || {
|
|
41
|
+
(cd one.ie/web && "${_GATE[@]}" bunx vitest run "$TEST_REL") || {
|
|
25
42
|
echo "livekit-ratchet FAIL — more than one LiveKit block is registered" >&2
|
|
26
43
|
exit 1
|
|
27
44
|
}
|
package/scripts/machine-check.sh
CHANGED
|
@@ -93,7 +93,7 @@ fi
|
|
|
93
93
|
echo
|
|
94
94
|
echo "── orphans (parent died, work still burning CPU) ──"
|
|
95
95
|
ps -Ao pid=,ppid=,etime=,rss=,command= | awk '$2==1' \
|
|
96
|
-
| grep -E 'tsc --noEmit|vitest|bun run (verify|test)|node .*(one-ie|\.do-worktrees)|sleep [0-9]+$' \
|
|
96
|
+
| grep -E 'tsc --noEmit|vitest|bun run (verify|test)|node .*(one-ie|\.do-worktrees)|node_modules.*workerd serve|sleep [0-9]+$' \
|
|
97
97
|
| awk '{printf " %-7s %-9s %6.0fMB %s\n",$1,$3,$4/1024,substr($0,index($0,$5),55)}' \
|
|
98
98
|
|| echo " none"
|
|
99
99
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# memory-index-budget.sh — is the auto-memory index still inside the loader's budget?
|
|
3
|
+
#
|
|
4
|
+
# WHY THIS EXISTS. Measured 2026-09-12: MEMORY.md was 89,954 bytes / 479 lines,
|
|
5
|
+
# and a session loaded 128 lines = 25,162 bytes. The cut is a ~25KB BYTE budget.
|
|
6
|
+
# 73% of the index never reached context, and the only warning was a line at the
|
|
7
|
+
# BOTTOM of the file — inside the 73% nobody reads. The corpus passed the budget
|
|
8
|
+
# around June and nothing noticed for three months.
|
|
9
|
+
#
|
|
10
|
+
# The loader's own advice ("one line under ~200 chars") does not fix it: 245 of
|
|
11
|
+
# 479 lines violated it, and trimming every one yields 77,943 bytes — still 3.1x
|
|
12
|
+
# over. The constraint is total bytes, so that is what this measures.
|
|
13
|
+
#
|
|
14
|
+
# Growth is 7.3 files/day (Sep 2026), so an index cut by hand re-grows within
|
|
15
|
+
# weeks. This is the thing that says so.
|
|
16
|
+
#
|
|
17
|
+
# bash memory-index-budget.sh # check, exit 3 if over
|
|
18
|
+
# bash memory-index-budget.sh --self-test # prove it can go RED
|
|
19
|
+
set -uo pipefail
|
|
20
|
+
|
|
21
|
+
BUDGET_BYTES="${MEMORY_INDEX_BUDGET:-25162}" # measured cut, not a guess
|
|
22
|
+
WARN_AT_PCT="${MEMORY_INDEX_WARN_PCT:-80}"
|
|
23
|
+
|
|
24
|
+
_check() {
|
|
25
|
+
local idx="$1"
|
|
26
|
+
[ -f "$idx" ] || { echo "[memory-budget] no index at $idx"; return 2; }
|
|
27
|
+
|
|
28
|
+
local bytes lines pct
|
|
29
|
+
bytes=$(wc -c < "$idx" | tr -d ' ')
|
|
30
|
+
lines=$(wc -l < "$idx" | tr -d ' ')
|
|
31
|
+
pct=$(( bytes * 100 / BUDGET_BYTES ))
|
|
32
|
+
|
|
33
|
+
# How much actually loads: bytes are the cap, so count lines until the budget.
|
|
34
|
+
local fit
|
|
35
|
+
fit=$(awk -v b="$BUDGET_BYTES" '{s+=length($0)+1; if(s<=b) n++} END{print n+0}' "$idx")
|
|
36
|
+
local dark=$(( lines - fit ))
|
|
37
|
+
local darkpct=0
|
|
38
|
+
[ "$lines" -gt 0 ] && darkpct=$(( dark * 100 / lines ))
|
|
39
|
+
|
|
40
|
+
echo "[memory-budget] $idx"
|
|
41
|
+
echo " bytes $bytes / $BUDGET_BYTES budget (${pct}%)"
|
|
42
|
+
echo " lines $lines total, ~$fit reach context, $dark dark (${darkpct}%)"
|
|
43
|
+
|
|
44
|
+
if [ "$bytes" -gt "$BUDGET_BYTES" ]; then
|
|
45
|
+
echo " RED — over budget. ~${darkpct}% of the index never reaches context."
|
|
46
|
+
echo " Trimming long lines does not fix this; the cap is total bytes."
|
|
47
|
+
echo " Shrink to a hot set and move the tail to its files — they stay"
|
|
48
|
+
echo " reachable by their description:, which is the real recall key."
|
|
49
|
+
return 3
|
|
50
|
+
fi
|
|
51
|
+
if [ "$pct" -ge "$WARN_AT_PCT" ]; then
|
|
52
|
+
echo " WARN — ${pct}% of budget. At ~7 new memories/day this goes red soon."
|
|
53
|
+
return 0
|
|
54
|
+
fi
|
|
55
|
+
echo " ok — inside budget, whole index reaches context."
|
|
56
|
+
return 0
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
_self_test() {
|
|
60
|
+
local t; t=$(mktemp -d); local rc=0
|
|
61
|
+
# GREEN: a small index must pass.
|
|
62
|
+
printf 'a%.0s' $(seq 1 100) > "$t/small.md"
|
|
63
|
+
if _check "$t/small.md" >/dev/null 2>&1; then echo " ok green half: small index passes"
|
|
64
|
+
else echo " FAIL green half: small index did not pass"; rc=1; fi
|
|
65
|
+
# RED: an oversized index must fail, or this checker is theatre.
|
|
66
|
+
awk -v n=$((BUDGET_BYTES * 2)) 'BEGIN{for(i=0;i<n/50;i++) print "x-------------------------------------------------"}' > "$t/big.md"
|
|
67
|
+
if _check "$t/big.md" >/dev/null 2>&1; then echo " FAIL red half: oversized index PASSED — checker is broken"; rc=1
|
|
68
|
+
else echo " ok red half: oversized index correctly goes red"; fi
|
|
69
|
+
rm -rf "$t"
|
|
70
|
+
[ $rc -eq 0 ] && echo "[memory-budget] self-test PASS" || echo "[memory-budget] self-test FAIL"
|
|
71
|
+
return $rc
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
INDEX="${MEMORY_INDEX:-$HOME/.claude/projects/-Users-toc-Server-one-ie/memory/MEMORY.md}"
|
|
75
|
+
case "${1:-}" in
|
|
76
|
+
--self-test) _self_test ;;
|
|
77
|
+
--index) _check "${2:?--index needs a path}" ;;
|
|
78
|
+
*) _check "$INDEX" ;;
|
|
79
|
+
esac
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# npm-downloads.sh — all-time download counts for every @oneie package.
|
|
3
|
+
#
|
|
4
|
+
# bash .claude/scripts/npm-downloads.sh # table, all time
|
|
5
|
+
# bash .claude/scripts/npm-downloads.sh --json # machine-readable
|
|
6
|
+
# bash .claude/scripts/npm-downloads.sh --since 2026-01-01
|
|
7
|
+
# bash .claude/scripts/npm-downloads.sh --scope @other
|
|
8
|
+
#
|
|
9
|
+
# portability: needs-env (network + jq; no repo state, no credentials)
|
|
10
|
+
#
|
|
11
|
+
# WHY THIS IS NOT ONE CURL: npm's downloads API answers `last-day`, `last-week`
|
|
12
|
+
# and `last-month`, and for anything else a date range — but it REFUSES a range
|
|
13
|
+
# longer than 18 months (`{"error":"...exceeds maximum of 18 months"}`), and it
|
|
14
|
+
# answers that with HTTP 200. So "all time" is a WALK: start at the package's
|
|
15
|
+
# first publish (registry `time.created`), step in 17-month windows, and sum.
|
|
16
|
+
# A single wide range does not error loudly, it just returns nothing useful.
|
|
17
|
+
#
|
|
18
|
+
# THE SCOPED-NAME TRAP: `@oneie/sdk` must be percent-encoded as `@oneie%2Fsdk`
|
|
19
|
+
# in the downloads API path. Unencoded, the `/` reads as a path separator and
|
|
20
|
+
# the API returns a 404 that looks exactly like "this package has no downloads"
|
|
21
|
+
# — a zero you would believe. Encoded here, once.
|
|
22
|
+
#
|
|
23
|
+
# A package published days ago legitimately reads 0. That is not a failure, and
|
|
24
|
+
# it is reported as 0 with its first-publish date beside it so the number can be
|
|
25
|
+
# read in context rather than mistaken for a broken query.
|
|
26
|
+
set -uo pipefail
|
|
27
|
+
|
|
28
|
+
SCOPE="@oneie"; JSON=0; SINCE=""
|
|
29
|
+
while [[ $# -gt 0 ]]; do
|
|
30
|
+
case "$1" in
|
|
31
|
+
--json) JSON=1 ;;
|
|
32
|
+
--scope) SCOPE="$2"; shift ;;
|
|
33
|
+
--since) SINCE="$2"; shift ;;
|
|
34
|
+
-h|--help) sed -n '2,12p' "${BASH_SOURCE[0]}"; exit 0 ;;
|
|
35
|
+
*) echo "npm-downloads.sh: unknown flag '$1'" >&2; exit 2 ;;
|
|
36
|
+
esac
|
|
37
|
+
shift
|
|
38
|
+
done
|
|
39
|
+
|
|
40
|
+
command -v jq >/dev/null || { echo "npm-downloads.sh: needs jq" >&2; exit 2; }
|
|
41
|
+
|
|
42
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
43
|
+
|
|
44
|
+
# The package list comes from the workspace, not from `npm access list packages`
|
|
45
|
+
# — that command returns nothing for a granular token whose scope is per-package
|
|
46
|
+
# (measured 2026-09-14: "packages in scope: 0" while all nine were readable).
|
|
47
|
+
# The manifests on disk are the honest list.
|
|
48
|
+
pkgs=()
|
|
49
|
+
while IFS= read -r pj; do
|
|
50
|
+
n="$(jq -r '.name // empty' "$pj" 2>/dev/null)"
|
|
51
|
+
[[ "$n" == "$SCOPE/"* ]] || continue
|
|
52
|
+
[[ "$(jq -r '.private // false' "$pj")" == "true" ]] && continue
|
|
53
|
+
pkgs+=("$n")
|
|
54
|
+
done < <(find "$ROOT/packages" -maxdepth 2 -name package.json -not -path '*/node_modules/*' 2>/dev/null | sort)
|
|
55
|
+
|
|
56
|
+
(( ${#pkgs[@]} )) || { echo "npm-downloads.sh: no public $SCOPE packages found under packages/" >&2; exit 1; }
|
|
57
|
+
|
|
58
|
+
# add_months <YYYY-MM-DD> <n> — date(1) differs between BSD and GNU; try both.
|
|
59
|
+
add_months() {
|
|
60
|
+
date -j -v+"$2"m -f '%Y-%m-%d' "$1" '+%Y-%m-%d' 2>/dev/null \
|
|
61
|
+
|| date -d "$1 + $2 months" '+%Y-%m-%d' 2>/dev/null
|
|
62
|
+
}
|
|
63
|
+
today() { date -u '+%Y-%m-%d'; }
|
|
64
|
+
|
|
65
|
+
rows="[]"
|
|
66
|
+
for p in "${pkgs[@]}"; do
|
|
67
|
+
enc="${p/\//%2F}" # @oneie/sdk -> @oneie%2Fsdk
|
|
68
|
+
created="$(npm view "$p" time.created 2>/dev/null | head -1 | cut -c1-10)"
|
|
69
|
+
[[ -n "$created" ]] || { created="2024-01-01"; }
|
|
70
|
+
start="${SINCE:-$created}"
|
|
71
|
+
end="$(today)"
|
|
72
|
+
|
|
73
|
+
total=0; cursor="$start"
|
|
74
|
+
while [[ "$cursor" < "$end" ]]; do
|
|
75
|
+
stop="$(add_months "$cursor" 17)"
|
|
76
|
+
[[ -z "$stop" || "$stop" > "$end" ]] && stop="$end"
|
|
77
|
+
body="$(curl -sS --max-time 25 "https://api.npmjs.org/downloads/point/${cursor}:${stop}/${enc}" 2>/dev/null)"
|
|
78
|
+
# the API reports its refusals in a 200 body — read the field, not the status
|
|
79
|
+
err="$(printf '%s' "$body" | jq -r '.error // empty' 2>/dev/null)"
|
|
80
|
+
if [[ -n "$err" ]]; then
|
|
81
|
+
printf ' ! %s: %s\n' "$p" "$err" >&2
|
|
82
|
+
else
|
|
83
|
+
n="$(printf '%s' "$body" | jq -r '.downloads // 0' 2>/dev/null)"
|
|
84
|
+
[[ "$n" =~ ^[0-9]+$ ]] && total=$(( total + n ))
|
|
85
|
+
fi
|
|
86
|
+
[[ "$stop" == "$end" ]] && break
|
|
87
|
+
cursor="$(add_months "$stop" 0)"
|
|
88
|
+
done
|
|
89
|
+
|
|
90
|
+
wk="$(curl -sS --max-time 15 "https://api.npmjs.org/downloads/point/last-week/${enc}" 2>/dev/null | jq -r '.downloads // 0')"
|
|
91
|
+
ver="$(npm view "$p" version 2>/dev/null | tail -1)"
|
|
92
|
+
rows="$(printf '%s' "$rows" | jq --arg p "$p" --arg v "$ver" --arg c "$created" \
|
|
93
|
+
--argjson t "${total:-0}" --argjson w "${wk:-0}" \
|
|
94
|
+
'. + [{package:$p, version:$v, since:$c, all_time:$t, last_week:$w}]')"
|
|
95
|
+
done
|
|
96
|
+
|
|
97
|
+
if (( JSON )); then
|
|
98
|
+
printf '%s\n' "$rows" | jq .
|
|
99
|
+
exit 0
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
printf '\n %-24s %-9s %12s %10s %s\n' "PACKAGE" "VERSION" "ALL TIME" "LAST WK" "SINCE"
|
|
103
|
+
printf ' %s\n' "$(printf '─%.0s' {1..74})"
|
|
104
|
+
printf '%s' "$rows" | jq -r '.[] | [.package,.version,(.all_time|tostring),(.last_week|tostring),.since] | @tsv' \
|
|
105
|
+
| while IFS=$'\t' read -r p v a w s; do printf ' %-24s %-9s %12s %10s %s\n' "$p" "$v" "$a" "$w" "$s"; done
|
|
106
|
+
printf ' %s\n' "$(printf '─%.0s' {1..74})"
|
|
107
|
+
printf ' %-24s %-9s %12s %10s\n\n' "TOTAL" "" \
|
|
108
|
+
"$(printf '%s' "$rows" | jq '[.[].all_time]|add')" \
|
|
109
|
+
"$(printf '%s' "$rows" | jq '[.[].last_week]|add')"
|
package/scripts/one-agents.mjs
CHANGED
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
// node .claude/scripts/one-agents.mjs # write .claude/agents/one/
|
|
27
27
|
// node .claude/scripts/one-agents.mjs --check # exit 1 if regeneration would change anything
|
|
28
28
|
// node .claude/scripts/one-agents.mjs --chart # print the routing table, emit nothing
|
|
29
|
+
// node .claude/scripts/one-agents.mjs --models # which model each agent gets and WHY, emit nothing
|
|
30
|
+
// node .claude/scripts/one-agents.mjs --models --json # the same as JSON on stdout (nothing written)
|
|
29
31
|
// node .claude/scripts/one-agents.mjs --self-test
|
|
30
32
|
|
|
31
33
|
import { readFileSync, writeFileSync, readdirSync, mkdirSync, rmSync, existsSync } from 'node:fs'
|
|
@@ -48,15 +50,66 @@ const RESERVED = new Set([
|
|
|
48
50
|
'claude-code-guide', 'w1-recon', 'w2-decide', 'w3-edit', 'w4-verify', 'w4-tools',
|
|
49
51
|
])
|
|
50
52
|
|
|
51
|
-
// The Agent tool accepts ONLY these.
|
|
52
|
-
//
|
|
53
|
-
// by tier and never copied through.
|
|
53
|
+
// The Agent tool accepts ONLY these. MODEL_BY_TIER is the FALLBACK — the answer
|
|
54
|
+
// when the declared model is not expressible here, or when nothing is declared.
|
|
54
55
|
const MODEL_BY_TIER = {
|
|
55
56
|
chairman: 'opus', ceo: 'opus', director: 'opus',
|
|
56
57
|
specialist: 'sonnet', tool: 'haiku',
|
|
57
58
|
1: 'sonnet', 2: 'sonnet', '1': 'sonnet', '2': 'sonnet',
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
// The closed set. NOTHING outside it may ever reach the Agent tool — which is
|
|
62
|
+
// why resolveClaudeModel returns a member of this set and NEVER the declared
|
|
63
|
+
// string. `fable` is legal and has no tier that produces it: it can only ever
|
|
64
|
+
// arrive by being declared.
|
|
65
|
+
const CLAUDE_MODELS = new Set(['opus', 'sonnet', 'haiku', 'fable'])
|
|
66
|
+
|
|
67
|
+
/** The Anthropic family named by an OpenRouter-shaped id, or null.
|
|
68
|
+
* Deliberately FAMILY-shaped, not an id list: `anthropic/claude-opus-5`,
|
|
69
|
+
* `claude-sonnet-4-5`, `claude-sonnet-4-6` and whatever version ships next all
|
|
70
|
+
* answer the same way, so a normalisation pass over the corpus cannot silently
|
|
71
|
+
* change which Claude Code model an agent gets. */
|
|
72
|
+
function claudeFamily(id) {
|
|
73
|
+
const base = id.toLowerCase().split('/').pop()
|
|
74
|
+
if (!base.startsWith('claude')) return null
|
|
75
|
+
return base.split(/[^a-z]+/).find(w => w === 'opus' || w === 'sonnet' || w === 'haiku') ?? null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Which Claude Code model this agent runs on, and WHICH RUNG SAID SO.
|
|
80
|
+
*
|
|
81
|
+
* Three outcomes, and the generated file names the one that happened — an agent
|
|
82
|
+
* running on sonnet because its grok declaration cannot be expressed here is a
|
|
83
|
+
* different fact from one running on sonnet because a human chose sonnet, and a
|
|
84
|
+
* reader who cannot tell them apart will "fix" the wrong one.
|
|
85
|
+
*
|
|
86
|
+
* declared — the declared model is expressible; it travels.
|
|
87
|
+
* fallback — declared, but an OpenRouter id Claude Code cannot run (grok,
|
|
88
|
+
* kimi). A real limit of this bridge, not a bug in the agent.
|
|
89
|
+
* tier — nothing declared. Since C14 an absent `model:` means the
|
|
90
|
+
* PLATFORM default at runtime (`DEFAULT_MODEL = 'x-ai/grok-4.5'`,
|
|
91
|
+
* packages/sdk/src/billing.ts) — also not expressible here, so
|
|
92
|
+
* absent lands on tier for the same honest reason grok does.
|
|
93
|
+
*
|
|
94
|
+
* @returns {{model: string, source: 'declared'|'fallback'|'tier', note: string}}
|
|
95
|
+
*/
|
|
96
|
+
function resolveClaudeModel(declared, tier) {
|
|
97
|
+
const fallback = MODEL_BY_TIER[tier] ?? 'sonnet'
|
|
98
|
+
const d = typeof declared === 'string' ? declared.trim() : ''
|
|
99
|
+
if (!d) return { model: fallback, source: 'tier', note: `${fallback} (tier — no model declared)` }
|
|
100
|
+
if (CLAUDE_MODELS.has(d.toLowerCase())) {
|
|
101
|
+
const m = d.toLowerCase()
|
|
102
|
+
return { model: m, source: 'declared', note: `${m} (declared ${d})` }
|
|
103
|
+
}
|
|
104
|
+
const fam = claudeFamily(d)
|
|
105
|
+
if (fam) return { model: fam, source: 'declared', note: `${fam} (declared ${d})` }
|
|
106
|
+
return {
|
|
107
|
+
model: fallback,
|
|
108
|
+
source: 'fallback',
|
|
109
|
+
note: `${fallback} (tier fallback — declared ${d} is not expressible in Claude Code)`,
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
60
113
|
// Engineering agents touch the repo; everyone else touches the world.
|
|
61
114
|
const TOOLS_ENGINEERING = 'Read, Grep, Glob, Bash, Edit, Write, WebFetch'
|
|
62
115
|
const TOOLS_WORLD = 'Read, Grep, Glob, Bash, Write, WebFetch, WebSearch'
|
|
@@ -151,6 +204,11 @@ function collect() {
|
|
|
151
204
|
name,
|
|
152
205
|
src: rel,
|
|
153
206
|
tier: String(meta.tier ?? 'specialist'),
|
|
207
|
+
// The RAW declared value, or undefined. Nothing fills a default here: this
|
|
208
|
+
// script parses the file itself (`frontmatter(readFileSync(...))` above),
|
|
209
|
+
// never the web loader — which since C14 fills the platform default in and
|
|
210
|
+
// would make every single agent read as `declared`.
|
|
211
|
+
model: meta.model ? String(meta.model) : undefined,
|
|
154
212
|
reports_to: meta.reports_to ? String(meta.reports_to) : null,
|
|
155
213
|
domain: String(meta.domain ?? 'governance'),
|
|
156
214
|
title: meta.title ? String(meta.title) : name,
|
|
@@ -192,8 +250,73 @@ Backend routes live ONLY in \`one.ie/web/src/pages/api/\`.
|
|
|
192
250
|
gate. Say which lane you ran — a fast pass is never reported as a full pass. A test touching the
|
|
193
251
|
DOM needs \`// @vitest-environment jsdom\` on line 1.
|
|
194
252
|
|
|
253
|
+
**Where you collaborate, and where you plan.** \`/u/<slug>/in\` is the room. Its Spaces ARE
|
|
254
|
+
your \`subscribes:\` tags — \`FN_TAGS\` maps a department to bare words
|
|
255
|
+
(\`one.ie/web/src/lib/in/spaces.ts:28\`), so staking \`marketing\` IS having a desk in the
|
|
256
|
+
marketing Space. \`/u/<slug>/tasks\` is the same words as plannable rows.
|
|
257
|
+
|
|
258
|
+
**A task comment is a message in that room — one store, one renderer, one vocabulary.**
|
|
259
|
+
\`tasks:comment\` writes to the SAME D1 message store the inbox thread uses
|
|
260
|
+
(\`one.ie/web/src/lib/resolvers/tasks.ts:1639\`, thread id \`task:<tid>\` at \`:1665\`), and it
|
|
261
|
+
renders through the same \`Thread\` component the conversation uses
|
|
262
|
+
(\`one.ie/web/src/components/in/TaskDetail.tsx:13,859\`). An \`@mention\` in a comment body
|
|
263
|
+
INSERTs a \`follows\` row (\`resolvers/tasks.ts:1686-1693\`) — mentioning someone subscribes
|
|
264
|
+
them to that task. So: report where the work already is. Never open a second room, and never
|
|
265
|
+
invent a second vocabulary — the tag that routes your signal is the same word that names your
|
|
266
|
+
Space and tags the task.
|
|
267
|
+
|
|
195
268
|
**Proof discipline:** a presence grep is not proof. Probe the running thing. Report numbers you
|
|
196
269
|
actually measured, and say plainly when a check did not run — an unrun gate is not a pass.
|
|
270
|
+
|
|
271
|
+
## The four verbs of working together — claim · delegate · chat · chain
|
|
272
|
+
|
|
273
|
+
A meeting is not where you report. It is where you **take a row, hand a row, argue on the row,
|
|
274
|
+
and put the rows in order.** All four are receivers; none is a convention you can improvise.
|
|
275
|
+
|
|
276
|
+
claim tasks:claim {"data":{"tid":"task:<24hex>","workspace":"<slug>"}}
|
|
277
|
+
delegate tasks:reassign {"data":{"tid":"...","assignee":"<slug>","workspace":"..."}}
|
|
278
|
+
tasks:announce {"data":{"taskId":"...","tags":["bare","words"],"workspace":"..."}}
|
|
279
|
+
chat tasks:comment {"data":{"tid":"...","workspace":"...","body":"..."}}
|
|
280
|
+
thread:append {"data":{"slug":"one","group":"space:one","sender":"<you>","role":"assistant","content":"..."}}
|
|
281
|
+
chain tasks:subtask {"data":{"parent":"task:...","title":"...","blockedBy":["task:..."],"workspace":"..."}}
|
|
282
|
+
tasks:depend {"data":{"tid":"task:<blocked>","blockedBy":"task:<blocker>","workspace":"..."}}
|
|
283
|
+
|
|
284
|
+
**CHAINING IS NOT BOOKKEEPING — it is what makes the board sequence itself.** \`tasks:claim\`
|
|
285
|
+
is **blocker-gated**, and \`tasks:depend\` writes the very \`blocks\` edge that gate reads. So an
|
|
286
|
+
unchained board hands every agent every row at once and nothing knows what comes first; a chained
|
|
287
|
+
one refuses a claim whose prerequisites are still open. **Chain before you fan out, or the fan-out
|
|
288
|
+
is a race.** \`tasks:depend\` refuses a self-edge and a reverse edge, so a cycle cannot be filed.
|
|
289
|
+
|
|
290
|
+
**\`tasks:subtask\` is the verb for splitting work, not \`tasks:create\`.** It writes the row, its
|
|
291
|
+
notes, its tags, its \`containment\` edge to the parent AND every \`blockedBy\` prerequisite in ONE
|
|
292
|
+
pipeline — precisely so a child never appears claimable with an empty body or missing ordering.
|
|
293
|
+
A child made with \`tasks:create\` is an orphan that somebody will claim before it is ready.
|
|
294
|
+
|
|
295
|
+
**Delegation moves a row; it does not copy one.** \`tasks:reassign\` swaps the \`@<slug>\` tag in
|
|
296
|
+
place and announces on the new tag set. An empty assignee unassigns and moves a \`picked\` row back
|
|
297
|
+
to \`open\` — claimable means **open AND unassigned**. Never "delegate" by filing a second task:
|
|
298
|
+
that is how a board grows a duplicate \`Handle: <title>\` row for work that already had one.
|
|
299
|
+
|
|
300
|
+
**\`tasks:announce\` returns \`matched\`, and matched is not delivered.** It reports how many staked
|
|
301
|
+
actors the tags reached, not how many acted. Quote it as reach; never as done.
|
|
302
|
+
|
|
303
|
+
**The claimant is the attested caller, never a body field.** You cannot claim on someone else's
|
|
304
|
+
behalf — hand them the row with \`tasks:reassign\` and let them claim it. **Under the shared
|
|
305
|
+
gateway key the attested caller is the workspace, so every head's claim lands as \`@one\`** — follow
|
|
306
|
+
\`tasks:claim\` with \`tasks:reassign {assignee: "<your slug>"}\` in the same breath, or the ledger
|
|
307
|
+
cannot say who took the row.
|
|
308
|
+
|
|
309
|
+
**Two rules that keep a chain honest**, and they are the same rule twice:
|
|
310
|
+
- **A child's cast is a subset of its parent's.** Inherit by default, declare to narrow, **never
|
|
311
|
+
declare to widen.** A leaf must not quietly attach a stakeholder the parent never had.
|
|
312
|
+
- **Depth needs a floor.** A parent that spawns children without any settling is a leak, not a
|
|
313
|
+
big plan. Freeze the parent's outcome before its children mint, and let \`fade\`/\`warn\` decay
|
|
314
|
+
what goes quiet.
|
|
315
|
+
|
|
316
|
+
**No agent leaves a meeting without a tid.** Claimed, delegated (name who to), chained (name the
|
|
317
|
+
parent), or **declined with an argument** — a refusal on principle is sometimes the best act
|
|
318
|
+
available. "Blocked" with no named blocker is not a status, it is a shrug. A meeting whose output
|
|
319
|
+
is a document and no moved row has failed, however good the document.
|
|
197
320
|
`.trim()
|
|
198
321
|
|
|
199
322
|
/** How many agents stake each tag — used to drop the non-discriminating ones
|
|
@@ -206,7 +329,8 @@ function indexStakes(roster) {
|
|
|
206
329
|
|
|
207
330
|
function render(a, roster) {
|
|
208
331
|
if (STAKE_FREQ.size === 0) indexStakes(roster)
|
|
209
|
-
const
|
|
332
|
+
const resolved = resolveClaudeModel(a.model, a.tier)
|
|
333
|
+
const model = resolved.model
|
|
210
334
|
const isDirector = a.tier === 'director' || a.tier === 'ceo' || a.tier === 'chairman'
|
|
211
335
|
const tools = isDirector ? TOOLS_DIRECTOR
|
|
212
336
|
: a.domain === 'engineering' ? TOOLS_ENGINEERING
|
|
@@ -232,14 +356,27 @@ function render(a, roster) {
|
|
|
232
356
|
const stake = (own.length ? own : a.subscribes.length ? a.subscribes : [a.domain]).slice(0, 4).join(', ')
|
|
233
357
|
const desc = [
|
|
234
358
|
(a.tagline || `${a.title}.`).replace(/\s+/g, ' ').trim(),
|
|
235
|
-
`
|
|
359
|
+
`For: ${stake}.`,
|
|
236
360
|
].join(' ').replace(/"/g, "'")
|
|
237
361
|
|
|
238
362
|
const reports = roster.filter(r => r.reports_to === a.name).map(r => r.name)
|
|
363
|
+
// The delegation line is tier-shaped. "You are a director, not a specialist"
|
|
364
|
+
// reads as a demotion on the top rung, whose whole function is that nobody is
|
|
365
|
+
// checking it — so the chairman gets the sentence its own job actually needs.
|
|
366
|
+
const delegation = a.tier === 'chairman'
|
|
367
|
+
? `You are the top rung — nobody audits you, so ask what was MEASURED, never what was\nclaimed. Convene, press for the second answer, name disagreements. Do not do their work yourself.`
|
|
368
|
+
: `You are a director, not a specialist — decide which work is worth doing, name the agent\nthat owns the surface, and report back. Do not do their work yourself.`
|
|
239
369
|
const chain = reports.length
|
|
240
|
-
? `\n## Your reports\n\nThese agents report to you and are spawnable by name: ${reports.join(' · ')}.\
|
|
370
|
+
? `\n## Your reports\n\nThese agents report to you and are spawnable by name: ${reports.join(' · ')}.\n${delegation}\n`
|
|
241
371
|
: ''
|
|
242
372
|
|
|
373
|
+
// The provenance line goes in the MARKER block, NOT as a YAML comment inside
|
|
374
|
+
// the frontmatter. Measured: not one agent file Claude Code loads on this box
|
|
375
|
+
// carries a `#` line inside its frontmatter (0 of the 96 generated + 5
|
|
376
|
+
// hand-written), so a YAML comment there is unproven ground — and the failure
|
|
377
|
+
// would land on all 96 files at once, at a regeneration nobody is watching.
|
|
378
|
+
// The HTML comment below is proven to survive: it is the first line of every
|
|
379
|
+
// spawned agent's own system prompt.
|
|
243
380
|
return `---
|
|
244
381
|
name: ${a.name}
|
|
245
382
|
description: "${desc}"
|
|
@@ -248,6 +385,7 @@ model: ${model}
|
|
|
248
385
|
color: ${color}
|
|
249
386
|
---
|
|
250
387
|
${MARKER} from ${a.src} — do not hand-edit.
|
|
388
|
+
model: ${resolved.note}
|
|
251
389
|
Edit the platform agent, then re-run the generator. -->
|
|
252
390
|
|
|
253
391
|
${SUBSTRATE}
|
|
@@ -288,10 +426,53 @@ if (args.includes('--self-test')) {
|
|
|
288
426
|
// RED PROOF: an OpenRouter model id must never reach the Agent tool.
|
|
289
427
|
ok('grok id never mapped through', !Object.values(MODEL_BY_TIER).some(m => m.includes('/')))
|
|
290
428
|
|
|
429
|
+
// RED PROOFS for C16 — a declared Claude model must TRAVEL, and the generated
|
|
430
|
+
// file must SAY which of the three things happened. Literal fixtures, never
|
|
431
|
+
// corpus rows: exactly one agent declares opus today, so a corpus-keyed test
|
|
432
|
+
// would go red the moment that file is renamed.
|
|
433
|
+
const fx = (tier, model) => ({
|
|
434
|
+
name: 'fx', src: 'fx.md', tier, reports_to: null, domain: 'engineering',
|
|
435
|
+
title: 'Fixture', tagline: 'A fixture.', skills: [], subscribes: [], body: 'fixture body', model,
|
|
436
|
+
})
|
|
437
|
+
const declaredOpus = render(fx('specialist', 'anthropic/claude-opus-5'), [])
|
|
438
|
+
ok('declared opus travels', /^model: opus$/m.test(declaredOpus))
|
|
439
|
+
ok('declared opus says declared', declaredOpus.includes('model: opus (declared anthropic/claude-opus-5)'))
|
|
440
|
+
const declaredGrok = render(fx('specialist', 'x-ai/grok-4.5'), [])
|
|
441
|
+
ok('grok falls back to tier', /^model: sonnet$/m.test(declaredGrok))
|
|
442
|
+
ok('grok never travels verbatim', !declaredGrok.includes('x-ai/grok-4.5\n') && !/^model: .*\//m.test(declaredGrok))
|
|
443
|
+
ok('grok says tier fallback', declaredGrok.includes('model: sonnet (tier fallback — declared x-ai/grok-4.5 is not expressible in Claude Code)'))
|
|
444
|
+
const noModel = render(fx('specialist', undefined), [])
|
|
445
|
+
ok('no model declared falls back to tier', /^model: sonnet$/m.test(noModel))
|
|
446
|
+
ok('no model declared says so', noModel.includes('model: sonnet (tier — no model declared)'))
|
|
447
|
+
// The family map must be version-agnostic — C15 is normalising these ids right now.
|
|
448
|
+
ok('bare claude-sonnet-4-5 is expressible', /^model: sonnet$/m.test(render(fx('director', 'claude-sonnet-4-5'), [])))
|
|
449
|
+
ok('bare `sonnet` is expressible', /^model: sonnet$/m.test(render(fx('director', 'sonnet'), [])))
|
|
450
|
+
ok('fable stays a legal declared value', /^model: fable$/m.test(render(fx('specialist', 'fable'), [])))
|
|
451
|
+
ok('a director declaring haiku is honoured over its tier', /^model: haiku$/m.test(render(fx('director', 'anthropic/claude-haiku-4-5'), [])))
|
|
452
|
+
// C15 measured that OpenRouter's real ids use DOTS, not hyphens, for the
|
|
453
|
+
// version (`grok-4.5`), and 78 of 105 declared ids are not served at all — so
|
|
454
|
+
// the normalisation landing beside this one will rewrite the version
|
|
455
|
+
// separator. The family map must not care, and here is the proof that it does
|
|
456
|
+
// not: hyphen, dot, prefixed, unprefixed, all four answer the same.
|
|
457
|
+
ok('a dotted version resolves (anthropic/claude-sonnet-4.5)', /^model: sonnet$/m.test(render(fx('director', 'anthropic/claude-sonnet-4.5'), [])))
|
|
458
|
+
ok('a dotted unprefixed version resolves (claude-opus-4.6)', /^model: opus$/m.test(render(fx('specialist', 'claude-opus-4.6'), [])))
|
|
459
|
+
ok('a dotted haiku resolves (anthropic/claude-haiku-4.5)', /^model: haiku$/m.test(render(fx('ceo', 'anthropic/claude-haiku-4.5'), [])))
|
|
460
|
+
ok('a non-claude vendor id never resolves to a family', claudeFamily('x-ai/grok-4.5') === null && claudeFamily('moonshotai/kimi-k3') === null)
|
|
461
|
+
|
|
291
462
|
const roster = collect()
|
|
292
463
|
ok(`roster non-empty (${roster.length})`, roster.length > 20)
|
|
293
464
|
ok('no placeholder in roster', !roster.some(r => r.name.includes('<')))
|
|
294
|
-
|
|
465
|
+
// RED PROOF, STRENGTHENED for C16. Until a declared model could travel, this
|
|
466
|
+
// assertion read MODEL_BY_TIER[r.tier] — the table, not the emitted value —
|
|
467
|
+
// and so did `grok id never mapped through` above. Both were true and both
|
|
468
|
+
// were BLIND to the declared path, which is now the only way an OpenRouter id
|
|
469
|
+
// could reach the Agent tool. Assert on what render() actually writes, for
|
|
470
|
+
// every real roster row.
|
|
471
|
+
ok('every RESOLVED roster model is legal', roster.every(r => CLAUDE_MODELS.has(resolveClaudeModel(r.model, r.tier).model)))
|
|
472
|
+
const emitted = roster.map(r => (render(r, roster).match(/^model: (.*)$/m) ?? [])[1])
|
|
473
|
+
ok('no OpenRouter id reaches the Agent tool', emitted.every(m => CLAUDE_MODELS.has(m)))
|
|
474
|
+
ok('no emitted model carries a vendor slash', emitted.every(m => m && !m.includes('/')))
|
|
475
|
+
ok('every agent says which rung chose its model', roster.every(r => /\n model: .+ \((declared |tier)/.test(render(r, roster))))
|
|
295
476
|
const ceo = roster.find(r => r.name === 'ceo')
|
|
296
477
|
ok('ceo present', !!ceo)
|
|
297
478
|
ok('ceo drops the "*" wildcard subscribe', ceo && !ceo.subscribes.includes('*'))
|
|
@@ -316,7 +497,7 @@ if (args.includes('--self-test')) {
|
|
|
316
497
|
// EVERY session in this repo. This is the standing tax the design pays, so
|
|
317
498
|
// it is asserted, not assumed. ~90 chars x 80 agents ~= 7KB ~= 1.8k tokens.
|
|
318
499
|
const descBytes = roster.reduce((n, a) => n + render(a, roster).split('\n')[2].length, 0)
|
|
319
|
-
ok(`roster description budget ${(descBytes / 1024).toFixed(1)}KB <
|
|
500
|
+
ok(`roster description budget ${(descBytes / 1024).toFixed(1)}KB < 14KB`, descBytes < 14 * 1024)
|
|
320
501
|
|
|
321
502
|
console.log(fail === 0 ? `\nPASS — ${roster.length} agents` : `\nFAIL — ${fail} check(s)`)
|
|
322
503
|
process.exit(fail === 0 ? 0 : 1)
|
|
@@ -324,6 +505,21 @@ if (args.includes('--self-test')) {
|
|
|
324
505
|
|
|
325
506
|
const roster = collect()
|
|
326
507
|
|
|
508
|
+
if (args.includes('--models')) {
|
|
509
|
+
const rows = roster.map(a => ({ name: a.name, tier: a.tier, declared: a.model ?? null, ...resolveClaudeModel(a.model, a.tier) }))
|
|
510
|
+
const tally = { declared: 0, fallback: 0, tier: 0 }
|
|
511
|
+
for (const r of rows) tally[r.source]++
|
|
512
|
+
if (args.includes('--json')) {
|
|
513
|
+
console.log(JSON.stringify({ total: rows.length, tally, agents: rows }, null, 2))
|
|
514
|
+
process.exit(0)
|
|
515
|
+
}
|
|
516
|
+
for (const r of rows.sort((x, y) => x.source.localeCompare(y.source) || x.name.localeCompare(y.name))) {
|
|
517
|
+
console.log(` ${r.name.padEnd(28)} ${r.model.padEnd(7)} ${r.source.padEnd(9)} ${r.declared ?? '(none declared)'}`)
|
|
518
|
+
}
|
|
519
|
+
console.log(`\n${rows.length} agents — declared ${tally.declared} · tier fallback ${tally.fallback} · no model declared ${tally.tier}`)
|
|
520
|
+
process.exit(0)
|
|
521
|
+
}
|
|
522
|
+
|
|
327
523
|
if (args.includes('--chart')) {
|
|
328
524
|
const byDomain = {}
|
|
329
525
|
for (const a of roster) (byDomain[a.domain] ??= []).push(a)
|