Stability audit: anxious/agent-wip/release-policy-derivation measurement unstable across windows #15
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Headline: anxious/agent-wip/release-policy-derivation is thin at 0.00% of spend in run 2026-07-29T01-00-00Z and measured at 11.85% in this window — the confidence label depends on the measurement window, not on the candidate.
Gate: FR-4.5, matched by slug
Candidate: anxious/agent-wip/release-policy-derivation (skill anxious:agent-wip), target anxious
Prior window (run 2026-07-29T01-00-00Z):
Current window (run of 2026-07-30):
Finding 1 — Measurement strength flip: The evidence label changed from "thin" to "measured" depending only on which measurement window was used. The same candidate would be described differently to a human depending only on when the audit ran.
Finding 2 — Threshold crossing: Share of audited spend moved from 0.0% -> 0.1185% against a 0.02 filing threshold, so the windows land on opposite sides of the 2% filing threshold. The value claim is a window artifact.
Finding 3 — Consequence this run: The candidate was downgraded from
filetoboundary-questionand was NOT filed on the target's repo.Finding 4 — New this run — snapshot persistence gap: The downgrade was only detectable after the local snapshot store was seeded by hand. The two prior real anxious runs had been written to scratch stores under a job tmp dir and were never persisted to the default store (~/.cache/inference-arbitrage/wiki) nor published to the Gitea wiki. On a clean run the gate would report "unchecked — no prior window to compare" and would file the unstable candidate without the safeguard.
Cross-reference: kotkan/claude-plugin-inference-arbitrage#11 describes this same defect, found by hand during Phase 7 acceptance and now closed. It predates the
<!-- ia-stability: ... -->marker mechanism and therefore carries no marker, which is why the automated marker search on this run did not find it and correctly opened this issue as the first marker-bearing report rather than commenting there.This issue is now the idempotency anchor for the candidate
anxious/agent-wip/release-policy-derivation: subsequent audits that re-encounter the same instability will find this marker and comment here instead of filing again.Investigation: not a code defect — a real third measurement window resolves the question
Repo state checked first:
git statusonmainin~/projects/claude-plugins/inference-arbitragewas clean and in sync withorigin/main(HEADba445cf, "Phase 8: publish").git logshows today's other fixes (6b634fdfixing kotkan/claude-plugin-inference-arbitrage#16, plus spec-review/memory-wiring/publish commits) did not touch this issue — #16 fixed a rendering bug (decline_reason()inbin/audit-snapshotblamed the wrong gate for this same candidate's downgrade) but left the underlying stability question — is the 11.85% reading real or a window artifact? — completely open. This issue's body is still accurate as filed; nothing has changed since.What I did: per the issue's own suggestion, ran the missing third and fourth measurement windows against real
~/.claude/projectstranscripts, using the actualbin/offload-scanscript (never counted anything by hand):Then read the
anxious:agent-wipskill row'soffload_value(the fieldboundary-classifyactually grades on —offload_value = int(offload_waste * (1 - judgment_density) * repetition_factor), a skill-level composite, not a single ngram's raw waste — verified this is the field by recomputing it from the row's ownoffload_waste/judgment_density/repetition_factorand matching the stored snapshot'soffload_valueexactly:1,332,195 * (1-0.024) * 3.0 = 3,900,668, bit-for-bit the number in the persisted snapshot).Real results, four real windows, same script, same candidate:
Root cause (not the issue title): this is not "the confidence label depends on the measurement window" in the sense of noisy/random flipping — it's a monotonic accumulation problem for a low-frequency-but-real candidate. Invocations climb steadily with window width (0 → 2 → 12 → 15) and share climbs with them (0% → 0.72% → 11.73% → 13.87%). A 7-day or 14-day window is simply too short to catch this skill's roughly one-invocation-per-2-days rate; nothing about the candidate's economics changes, only how much of its sparse usage a short window happens to sample. Critically, the new independent 60-day window does not contradict the 27-day "measured" reading — it strengthens it (13.87% > 11.73%, 15 invocations ⊇ the same 12). If this were a genuine artifact, the 60-day pass had every opportunity to dilute the share back down (it adds 33 more days and 3 more sessions of denominator) and instead it went up.
Conclusion: the evidence stabilizes on the "measured, above the 2% filing threshold" side once the window is ≥~27 days. The
thin/0% reading that triggered this issue's FR-4.5 downgrade was itself the artifact — a too-short comparison window, not noise in the candidate.needs_code_change: false. No script bug found inoffload-scan,boundary-classify, orstability-classify— the gate did exactly what FR-4.5 says it should when it saw the 7-day snapshot disagree with the 27-day one; it just didn't have a wider, corroborating window available at the time to weigh against the thin one.What I did not do: I did not write these new windows into
~/.cache/inference-arbitrage/wiki(the store the FR-4.5 gate reads) — that store already carries a note (this issue's Finding 4) that it was seeded by hand rather than through the normal publish path, and I didn't want to compound that with another manually-inserted entry outside the pipeline's ownboundary-classify→stability-classify→audit-snapshot writesequence. The right way to make this durable is a full, real audit run (see below), not a partial snapshot of just theoffload-scannumbers.Recommended next step (for a human or the next audit run, not a code change)
Run a real, full
offload-auditpass onanxiousend-to-end (perskills/offload-audit/SKILL.md) using a ≥30-day window (30 or 60 both work; 60 is what I validated above), through the actual pipeline so it persists to the real store and re-triggers FR-4.5 against history honestly:If
stability-classifysees this new ≥60-day snapshot land on the "measured" side against the most recent prior "measured" snapshot (07-29T00-00-00Z or 07-30T00:31:45Z, both alreadymeasuredat ~11.9%), the candidate isstableand clears FR-4.5 — it should file normally againstoleks/anxious(or whichever repo the target maps to) on the next real run. Only the 7-day/14-day windows were ever the outliers.Separately, worth a human policy call (not something I'm deciding here): should FR-4.5 treat a
thinsnapshot built from a window under some minimum width (e.g. 21–30 days) as too weak to trigger a downgrade on its own, given a genuinely low-frequency candidate can lookthinin any short window without being unstable? That's a rubric/threshold question, not a bug in the code as written — flagging it for a human, not acting on it.No new code-level bug surfaced during this investigation (kotkan/claude-plugin-inference-arbitrage#16 already covers the one that did, filed and fixed earlier today, before I started).
This is not a code-defect issue — it needs a third real measurement window, not a fix
Cross-referenced while investigating kotkan/claude-plugin-inference-arbitrage#12 (a different, secondary finding split from #11). This issue (#15) is the FR-4.5 stability-gate output itself: the gate did its job — it caught that
anxious/agent-wip/release-policy-derivationflips between "thin, 0.00%" (prior window, 69 sessions, 2026-07-22→07-29) and "measured, 11.85%" (current window, 170 sessions, 2026-07-03→07-30), straddling the 2% filing threshold, and correctly downgraded the candidate fromfiletoboundary-questioninstead of filing it. That is the gate working as designed (commitdcec9ca), not a bug.Two windows can't distinguish "this candidate is genuinely thin and the 11.85% run is a fluke/artifact" from "this candidate is genuinely significant and the 0.00% run under-sampled it" — you need a third, independent window to see which side two-out-of-three lands on. No code change needed.
needs_code_change: false.Concrete plan for a second agent to execute
Do not reuse either existing window. Prior window was 7 days (2026-07-22→07-29, 69 sessions); current window was ~27 days (2026-07-03→07-30, 170 sessions) per this issue's body. Pick a window with different bounds from both, against real
~/.claude/projectstranscripts (no synthetic fixtures) — e.g. 14 days (--since 2026-07-16 --until 2026-07-30) or 60 days (--since 2026-05-31 --until 2026-07-30) viabin/offload-scan's--since/--untilflags (see--help/usage at the top ofbin/offload-scan).Run offload-scan for the
anxiousplugin over that window:(confirm exact flag names against
bin/offload-scan --helpbefore running — do not guess).Run audit-snapshot (or whatever wraps offload-scan + FR-4.5 gate + persistence — see
bin/audit-snapshot) against that scan, ensuring it persists to the default snapshot store this time:~/.cache/inference-arbitrage/wiki(per this issue's Finding 4 — the prior two real runs were NOT persisted there, they lived under a job tmp dir, which is why the gate had no prior window to compare against until the store was seeded by hand). Do not use a scratch/tmp store for this run.Read the new run's verdict for
anxious/agent-wip/release-policy-derivation:share_of_audited_spendand measurement-strength label ("thin" vs "measured") for this window.agent-wipactivity concentrated in early July that a 27-day window captured and a 7-day window didn't. Evidence staysboundary-question; do not file. Comment that conclusion, with numbers, on this issue.offload-audit/filing-plan pipeline, referencing this issue and kotkan/claude-plugin-inference-arbitrage#12/#15 as the provenance trail, and comment the outcome here.Either way, persist the new snapshot properly (step 3) so a fourth run, if ever needed, has three real prior windows to compare against instead of needing another hand-seed.
No files need to change to execute this plan — it is a data-collection/measurement action against the existing, working tooling (
bin/offload-scan,bin/audit-snapshot), not a code fix.Fixed in code: FR-4.5.1 — a narrow window may not overturn a wide
measuredreadingCommit
88fc58bonmain(plugin v0.6.3 → v0.7.0, new gate behavior). Thisacts on the policy question the investigation comment above flagged for a human
("should FR-4.5 treat a
thinsnapshot from a too-short window as too weak totrigger a downgrade") — the user confirmed the decision, so the gate now
implements it.
The rule
A
thin/unmeasuredreading only gets to overturn ameasuredone when it wassampled over a comparably wide window. All three must hold for the carve-out to
fire:
measured;thinorunmeasured;width(current) < 0.9 × width(prior), where width is the observedwindow.until - window.sincespan in days.Then the candidate takes a third verdict,
insufficient-window: not filed,not downgraded to
boundary-question, no FR-6.5 self-report, and carriedforward — the wider prior snapshot stays the standing comparison point, so the
next audit over an adequate window compares against it and not against this
window's under-sample. It lands in a new
stability_deferralslist (deliberatelynot
stability_findings, which is what the caller files self-reports from), andcarries
window_parity(both widths, the floor, the ratio),suppressed_flips(what would have been reported), and a
carry_forwardline naming the minimumwidth to re-audit at.
Why a ratio and not "at least 21–30 days". The adequate width is a property of
the candidate's invocation rate, which the auditor learns from the measurement
and does not know in advance: this candidate needs ~27 days, a once-per-hour step
is fully measured in one. An absolute floor would be a guess about frequency
dressed up as a threshold. What the gate can know without guessing is that a
window materially narrower than the one that produced the
measuredreadingcannot fairly contradict it.
0.9rather than1.0because window bounds areobserved session spans, so two nominally identical windows differ by hours.
What still downgrades, unchanged (all three regression-tested):
thinprior →measuredcurrent) — there the earlierreading is the weak one;
measured— a window thatreached
measureddid observe the candidate, so a moving share is a claim aboutrelative spend, not a sampling failure, and narrowness does not excuse it.
No silent fallback: if either window lacks both bounds, width is not
computable, the flip is not excused, and
window_parity.comparableisnullwith an explicit
why. Unreadable windows are visible, never an exemption.Re-verified against the real data, live store
Run against
~/.cache/inference-arbitrage/wiki(not a fixture store), candidateanxious/agent-wip/release-policy-derivation:thin, 0.00%2026-07-30T00:31:45Z— 26.49d,measured, 11.85%insufficient-window,downgraded: 0,stability_findings: []; parityrequired_days: 23.84measured, 13.87%measured11.85% priorstable, verdictfile— reports normallySo the exact comparison that produced this issue now says "window too narrow to
compare fairly" instead of "the confidence label depends on the calendar", and the
two adequately wide windows still resolve cleanly on the measured side — matching
the 0 → 2 → 12 → 15 invocation accumulation documented above.
Changed
bin/stability-classify—window_days/window_parity/deferrable, theinsufficient-windowverdict,stability_deferrals,window_parityrecorded ondowngrade findings too (so a reader sees the carve-out was considered and did not
apply),
insufficient_window+window_parity_ratioin the gate block.bin/audit-snapshot— a deferral is recorded in the snapshot'snotesratherthan silently dropped (it belongs in neither
candidatesnorboundary_questions).design/spec.md— FR-4.5.1 as a testable requirement; FR-7.3 covers the newoutcome; S4 restated (still "mechanically refuses a candidate whose evidence
is a window artifact", now refused honestly).
design/rubric.md§5b — the third boundary: "a narrow window is not instabilityeither", with this candidate's four real windows as the worked example.
skills/offload-audit/SKILL.mdstep 4b — explicitly: do not file a deferralon the target's repo and do not self-report it here.
tests/stability.test.sh— 53 assertions, all pass. New/updated cases: (f) real7d-vs-26d →
insufficient-windowwith no self-report; (f2) real 60d-vs-26d →stable, files normally; (j) synthetic narrow-vs-wide → deferral; (k) reversedirection → still downgrades; (l) 28d-vs-30d comparable flip → still downgrades;
(m)
measured→measuredthreshold crossing on a narrow window → stilldowngrades; (n) unreadable widths → downgrades, comparability
null.tests/calibration/anxious-60d.{scan,classified}.json— the real 60-day scan(176 sessions, 2026-06-26 → 2026-07-30) and the candidate's real 60-day
measurement (15 invocations,
offload_value4,953,520 / 35,722,973 weightedtokens = 13.87%), masked shapes only per FR-3.5.
Full suite: 7/7 test files pass (
for f in tests/*.test.sh; do bash "$f"; done).Does this resolve the issue? Not closing it — two things remain
Leaving this open for a human call, per the request:
the live store still got there by hand-seeding, not through a real
offload-scan → boundary-classify → stability-classify → audit-snapshot writepass published to the wiki. The recommended next step from the investigation
comment still stands: one real full
offload-auditrun onanxiousover a≥30-day window, persisted to the default store and published to the wiki, so
the standing comparison point is a pipeline artifact. Only after that will a
clean-machine run behave the way this fix assumes. (Worth noting the fix makes
that run safer, not unnecessary: a cold-store run still reports
uncheckedand files.)
measuredcurrent window compared against a narrowthinprior (e.g. thiscandidate's 60-day reading against the leftover 7-day snapshot
2026-07-29T01-00-00Z) still downgrades toboundary-questionand stillself-reports — the reverse direction was explicitly kept as-is. That is
defensible (the wide window genuinely contradicts the narrow one, and the human
was told two things) but it means a stale narrow snapshot in the store can
still suppress a good wide reading once. Symmetrising it — ignoring a
too-narrow prior outright — would be a second, separate decision, so I have
not taken it. Flagging, not acting.
Real end-to-end pipeline run against
anxious, 2026-07-30T20:48:20Z (FR-4.5.1 fix, kotkan/claude-plugin-inference-arbitrage@88fc58b, plugin now v0.8.1)This closes the gap this issue's last comment flagged as still missing: every prior wide-window snapshot for
anxiousin the local store was seeded by hand viaaudit-snapshot import. This run went through the actualbin/scripts end to end and persisted a genuinely pipeline-produced entry.What ran (all real, against real
~/.claude/projectstranscripts and the real Gitea API — no scratch store):bin/plugin-inventory ~/projects/claude-plugins/anxious --json→inv.jsonbin/offload-scan --plugin anxious --days 60 --json→scan.json(window 2026-06-26 → 2026-07-30, 179 sessions, 37,037,942 weighted tokens audited)bin/candidate-digest inv.json scan.json --json→digest.jsonanxious/agent-wip/release-policy-derivation(reusing this candidate's already-established real content: the WIP-release staleness digest,llm-over-script-digest, ABSTAIN escalation path) →judgments.jsonbin/boundary-classify judgments.json→classified.json: high confidence, verdictfile, 13.42% of audited spend, offload_value 4,968,793 over 14 invocations (close to this issue's own investigation figure of 4,953,520/13.87% from a slightly earlier exact window — expected variance from a live rolling 60-day window, not a discrepancy)Data/anxious/snapshots.jsonlfrom this wiki viawiki_read, imported viabin/audit-snapshot import(0 new: local store was already 1 run ahead of the wiki, from a prior real run today; import is idempotent byrun_id)bin/stability-classify --classified classified.json --target anxious --scan scan.json --wiki-checked imported→stable.json: compared against the most recentmeasuredprior snapshot (run2026-07-30T14:24:31Z, 27-day window, 11.35% share) — both measured, share stayed within the filing zone on both sides, no downgrade, noinsufficient-windowdeferral. Confirms the FR-4.5.1 fix is working: the gate correctly picked the widemeasuredprior rather than the stale thin one that caused this issue originally.list_issues(labels=['token-offload'])+search_issues('ia-candidate: anxious/agent-wip/release-policy-derivation')onoleks/claude-plugin-anxious— found the marker already present on oleks/claude-plugin-anxious#30 (filed in the prior real run once it first cleared the gate).filing-plan plancorrectly resolved this to acommentaction, not a re-file.bin/audit-snapshot write→ persisted run2026-07-30T20:48:20Zto the default store (~/.cache/inference-arbitrage/wiki), and published all three pages to this wiki viawiki_write:Audits/anxious/2026-07-30,Audits/anxious/Latest, and an append toData/anxious/snapshots.jsonl(now 5 runs recorded).Result:
anxious/agent-wip/release-policy-derivationcleared FR-4.5/FR-4.5.1 cleanly this run and commented (not re-filed, since oleks/claude-plugin-anxious#30 already carries the marker). The store now holds a real, pipeline-produced, wiki-published entry foranxiousrather than only hand-seeded ones.Left alone per instruction: the residual design question from this issue's last comment (a wide
measuredcurrent window vs. a narrowthinprior still downgrading by design) is untouched — that's a human policy call, not resolved by this run.Not closing this issue — leaving that to a human/parent review.