ba445cf4c9
README replaced (was still 'scaffolding, pre-implementation' at v0.5.1 with two real audits already filed): usage, the six pipeline steps, the rubric's four positions in one table, and three worked examples from real runs (oleks/claude-plugin-cluster#56, oleks/claude-plugin-anxious#29, kotkan/claude-plugin-inference-arbitrage#15). Publish scoped to local use per explicit user decision: already registered in oleks-local (1.2c, since Phase 1); deliberately not added to the public claude-plugin-index or verified on claude-plugins.oleks.space. tasks.md 8.2 records that as a decision, not an oversight, so a later session doesn't 'fix' it by publishing publicly without asking. 8.3 (optional cc-tokens upstream PR) stays open, not required for this release. 8.4 (memory checkpoint) already satisfied — FR-9 writes on every run plus this session's manual checkpoints.
13 KiB
13 KiB
tasks.md — build order for inference-arbitrage
Ordered so that each phase is independently verifiable and the risky part (the rubric) is calibrated against known-answer fixtures before anything is filed to a real tracker.
Track as Gitea issues on <org>/claude-plugin-inference-arbitrage under a
v0.1.0 milestone, via anxious:issuer. Reference in full owner/repo#num form.
Phase 0 — Decisions to close first
These block design, not just code. Answers go into Methodology/Calibration.md.
- 0.1 Resolve Q1: the
cc-tokensdependency. Resolved as option (a) + (b):bin/offload-scanlocatescc-tokensonPATHfirst, then falls back to globbing~/.claude/plugins/cache/*/token-budget/*/bin/cc-tokens, and fails loudly with an install hint if neither resolves. Never vendored. Upstreamingcc-tokens attribute --by skill|plugin --jsonstays optional (8.3). - 0.2 Resolve Q3: confirm summary-only degradation is acceptable for
plugins with no writable repo (official/third-party marketplace).
Resolved: yes — shipped as FR-6.4 /
filing: unavailable(6.5). - 0.3 Resolve Q6: confirm the masking list, and decide whether corp
(
imagex:*) plugins are excluded from the shared wiki entirely. - 0.4 Resolve Q5: build the Goodhart counter-metric (wontfix/revert rate) in v1, or note it as a manual check?
- 0.5 Confirm the plugin name
inference-arbitrage(vs. keeping the placeholdertoken-offload-audit). Resolved:inference-arbitrage; directory renamed to match the manifest name. - 0.6 Choose the Gitea org:
oleksorkotkan. Resolved:kotkan—kotkan/claude-plugin-inference-arbitrage, alongsidetoken-budget,worktree-discipline, andanti-patterns.
Phase 1 — Repo scaffold
- 1.1 Rename
~/projects/claude-plugins/token-offload-audit/to match the confirmed manifest name (workspace convention: directory == manifestname), and build out the PLAN §2 layout:plugin.jsonv0.1.0, MIT LICENSE,.gitignorewith.cache/,.claude/,*.log. The four spec files move with the directory and stay in-tree as the design record. - 1.2
git init -b maininside the plugin subdir (the workspace root is deliberately not a repo), create<org>/claude-plugin-<name>on Gitea with no auto-init (viacluster:gitea-agent), pushmain. - 1.2b Verify
bin/scripts carry the executable bit (git ls-files -s→100755) — three shipped scripts make this a live concern. Re-checkgit statusafter every push: the global pre-push hook auto-formats markdown/JSON and may append its own commit. - 1.2c Register in the local
oleks-localmarketplace (~/projects/claude-plugins/.claude-plugin/marketplace.json,"source": "./<name>") so Phases 2–7 can test via@oleks-localwithout publishing anything publicly. - 1.3 Initialize its wiki with
Methodology/Rubric.md(rubric.md verbatim, v1.0.0) and emptyAudits/+Data/scaffolding. - 1.4
.woodpecker/test.yamlrunningtests/run-all.sh. CI verifies; nothing heavy runs on emmett.
Phase 2 — Static pass
- 2.1
bin/plugin-inventory: target resolution (path or name, semver pick, skip.orphaned_at), frontmatter parsing (~30 lines, no PyYAML). - 2.2 Signal extraction: procedural density, verb ratio, command-block signatures, rule tables, script coverage, hook/prose drift.
- 2.3
references/signals-catalog.mdwith the verb lexicons and thresholds, loaded at runtime — tuning must be a reviewable diff. - 2.4
tests/inventory.test.shagainst a fixture plugin tree. - 2.5 Calibration run against
token-budget,worktree-discipline,anxious,memory. Record the actual numbers inMethodology/Calibration.mdand tune thresholds until the ordering matches the human read:token-budgetcleanest,anxiousrichest in candidates.
Phase 3 — Dynamic pass
- 3.1
bin/offload-scanskeleton: streaming JSONL reader, attribution filter,(message.id, requestId)de-dup — delegating token/cost arithmetic tocc-tokensper decision 0.1. Do not reimplement the pricing table or the streaming-snapshot max. - 3.2 Invocation reconstruction (contiguous attributed runs per session).
- 3.3 Turn classification: mechanical / judgment / retry, with the mechanical tool set and the output-token threshold in the signals catalog.
- 3.4 Tool-signature normalization and masking (also satisfies FR-3.5).
- 3.5 N-gram mining: recurring sequences ≥3 long in ≥3 invocations.
- 3.6 Metrics: MTR,
offload_waste, read amplification, retry density, judgment density, fan-out multiplier, compositeoffload_value. - 3.7 Attribution coverage computation (FR-3.4).
- 3.8
tests/scan.test.shagainst synthetic fixture transcripts with hand-computed expected metrics. Do not test against real transcripts — they contain private content and they change. - 3.9 Verify runtime and memory on the real history: one process, streaming, bounded. Record wall time in the calibration page.
Phase 4 — Rubric and classification
- 4.1
references/boundary-rubric.md=rubric.mdverbatim. - 4.2
skills/boundary-rubric/SKILL.md— usable standalone on a single step, before any code exists. - 4.3
agents/offload-analyst.mdwith the PLAN §9 behavioral rules, including the hard gate: no overrule case → boundary question, never filed. - 4.4 Calibration against known answers — the load-bearing test.
-
token-budget→ must yield zerohigh-confidence candidates (S2). -worktree-disciplinewithbin/worktree-auditmasked out of the inventory → must flag worktree classification ashigh(S3). - the two-windowanxiousfixture → must downgraderelease-policy-derivationon the stability gate (S4). If either fails, the rubric is wrong and Phase 5 does not start. - 4.5 Record the calibration outcome and any threshold change, with its
evidence, in
Methodology/Calibration.md.
Phase 5 — Snapshots and trend
- 5.1
bin/audit-snapshot write|list|diff; schema per PLAN §6. - 5.2 Candidate identity resolution (issue → signature → slug) and signature-drift detection.
- 5.3 Volume-normalized diffing: cost-per-invocation and share-of-spend primary, absolute tokens as context only (FR-5.4).
- 5.4
resolvedvsclaimed-fixed-unconfirmedlogic (FR-5.5). - 5.5 Wiki read/write;
Latest.mdregeneration. Resolved as: the script is offline and filesystem-only, mirroring the wiki tree into a local store;audit-snapshot pagesemits what to publish and the caller (skill, orcluster:gitea-agent) performs the wiki writes, andaudit-snapshot importseeds a cold cache from a wiki-fetchedsnapshots.jsonl. See thebin/audit-snapshotdocstring. - 5.6
skills/offload-trend/SKILL.md. - 5.7
tests/snapshot.test.sh— two synthetic snapshots, assert the diff.
Phase 6 — Output paths
- 6.1
references/issue-template.mdper PLAN §10. - 6.2
anxious:issuerdelegation path; create thetoken-offloadlabel on a target repo on first audit (implicitly, viaissuer). - 6.3 Idempotency (FR-6.3) —
ia-candidatemarker search before filing; comment on the existing issue instead of duplicating. Verify by running the same audit twice (S5). This is the highest-consequence correctness detail in the whole plugin. - 6.4 Summary page generation →
Audits/<target>/<date>+Latest. - 6.5 Graceful
filing: unavailablefor repo-less targets (FR-6.4). - 6.6
skills/offload-audit/SKILL.mdandcommands/offload-audit.mdtying the whole procedure together.
Phase 7 — Acceptance
- 7.1 S1 — audits four different plugins with no target-specific code.
- 7.2 S2 —
token-budget→ "nothing to offload." - 7.3 S3 — masked
worktree-discipline→ rediscovers the known cut. - 7.4 S4 — the FR-4.5 evidence-stability gate mechanically catches a real
unstable candidate in
anxious(agent-wip/release-policy-derivation) without human review. The original framing — "a candidate the user agrees is genuine" — is retired: a human triage catching a bad candidate is not a repeatable test, and the one that ran caught this candidate because its evidence flipped across windows. That check is nowbin/stability-classify, asserted against the real two-window evidence intests/stability.test.shcase (f). - 7.5 S5 — double-run files no duplicate.
- 7.6 S6 — two-snapshot trend is computable and volume-normalized.
- 7.7 S7 — audit
inference-arbitragewithinference-arbitrage(FR-8.2). Any parsing or aggregation found living in skill prose is a bug to fix before release. - 7.8 S8 — confirm the run stays inside emmett's constraints.
Phase 7.9 — Memory integration (FR-9)
Numbered 7.9 rather than 8.x because it lands before publish, by explicit user decision on 2026-07-29 ("build it now, even if it delays publish"): a v0.1.0 that ships without it would publish an audit loop that forgets every run.
- 7.9.1
audit-snapshot memory-queries— the read side. Deterministic queries over both wings, packed to mempalace's 250-char limit, resolvable from the inventory alone so the read step does not depend on a pass that has not run yet. - 7.9.2
audit-snapshot memory-notes— the write side. Per-run summary, qualitative judgment calls, cross-target lessons, plus knowledge-graph triples. Same contract aspages: filesystem in, JSON out, no MCP call. - 7.9.3 Redaction by allowlist (FR-9.4). Memory renderers do not
reuse the wiki renderers, because
snapshot.notesis caller-supplied free text that legitimately reaches a wiki page and must never reach a drawer. - 7.9.4 Wire into
skills/offload-audit/SKILL.md(step 2b read, step 6b write),agents/offload-analyst.md(procedure §1b and §7), and the agent'stools:frontmatter (mcp__plugin_memory_*). - 7.9.5 Live round trip. Verified by hand 2026-07-29 against the real
anxioussnapshot:add_drawerintoanxious/inference-arbitrage-auditsand intoclaude-plugins/inference-arbitrage-lessons, both retrieved by the script's own emitted queries;kg_addofanxious → audited_by → inference-arbitrage. The MCP calls are not covered bytests/memory.test.shand are not faked there — see FR-9.5 on why the untestable seam is one verbatim call wide. - 7.9.6
tests/memory.test.sh— wing/room resolution, query packing, drawer selection per run shape, rendered content, redaction, kg facts.
Phase 8 — Publish
- 8.1 README with the thesis, the rubric summary, and worked examples.
- 8.2 Publish per
plugin-publishing, scoped to local use: pushed tomain, already registered inoleks-local(1.2c). Deliberately not added to~/projects/claude-plugin-index/plugins.jsonor verified onclaude-plugins.oleks.space— user's explicit call, since this plugin is used only througholeks-local. Revisit if that changes. - 8.3 Optional, from decision 0.1: PR
cc-tokens attribute --by skill|plugintotoken-budget. - 8.4 Memory checkpoint — the rubric and the calibration findings into
claude-plugins, roominference-arbitrage-lessons. Written againstwing_claude_memory/working-practicebefore FR-9 existed; that predates the wing/room split FR-9.3 actually shipped with, and having two destinations for the same cross-target judgment defeats the point of a single accumulating room. Use the onebin/audit-snapshot memory-notesalready writes to, so a manual checkpoint and an automated one land in the same place.
Dependency notes
- Phase 3 depends on decision 0.1.
- Phase 4.4 gates Phase 5 and 6. Do not file a single real issue until the rubric passes calibration — the plugin's credibility is spent on its first wrong recommendation.
- Phase 2 and Phase 3 are otherwise independent and can proceed in parallel; both feed Phase 4.
- The static pass alone (Phases 1–2 + 4) is already a shippable v0.1.0 if the
dynamic pass proves harder than expected. It answers "what could be a script"
without "and what is it costing you" — less compelling, but useful, and it
degrades honestly by marking every candidate
unmeasured(FR-2.4).