Skip to content

fix(routing): bind a sticky pin to the account identity it was placed under - #86

Merged
ualtinok merged 1 commit into
cortexkit:mainfrom
iceteaSA:feat/sticky-identity-binding
Aug 13, 2026
Merged

fix(routing): bind a sticky pin to the account identity it was placed under#86
ualtinok merged 1 commit into
cortexkit:mainfrom
iceteaSA:feat/sticky-identity-binding

Conversation

@iceteaSA

@iceteaSA iceteaSA commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Branched off main at v0.5.2. Single commit, 935 pass / 0 fail (from 930).

The pin recorded a slot, not an account

StickyAssignment.accountId is 'main' or a fallback's storage id — a slot. Which ChatGPT account occupies that slot can change underneath it: switching the main account does it, and so does re-logging a fallback, because upsertAccount dedups identity → id → label and deliberately reuses the storage id.

When that happens every session pinned to the slot stays pinned, but to a different account than the operator chose, with a cold prompt cache. Cache warmth is the entire justification for pinning, so the pin was then protecting a cache that does not exist while also overriding the operator's choice.

Placement now records the identity alongside the slot, and a pin whose recorded identity differs from the account's current one is treated as absent — the session is placed again against real quota. This mirrors what QuotaEntry.accountId and setFallback already do one layer over; the mechanism was in the codebase, it just was not applied to pins.

Two constraints that mattered more than the feature

Fail open. Invalidation fires only when both identities are known strings and they differ. Identity is not always resolvable, and invalidating on missing data would re-place sessions at random and bust the caches this mode exists to protect — strictly worse than the bug. An unknown on either side retains the pin.

Migration retains. Every pin written before this change carries no identity. Dropping them on first resolve would clear every warm cache on upgrade, so they are retained and stamped instead; the check works from the second resolve onward.

Both are absence assertions, which is worth stating because it changes how they were verified.

Testing

Six production hunks, each mutated individually. Reverse-removal proves a positive assertion, but it cannot prove an absence one — deleting code moves it further into compliance with "unknown never invalidates", so the test can never redden. Those three used the inverse mutation instead: make the code invalidate unconditionally and confirm the fail-open tests break.

Hunk Mechanic What reddened
hasStickyIdentityMismatch reverse-removal known identity change no longer re-places
fail-open typeof gates inverse pre-upgrade + current-unknown retention both break
migration stamp reverse-removal pre-upgrade pin no longer stamped
tolerant read inverse malformed identity no longer normalized away
wireAccountIdByAccount threading reverse-removal integration placement breaks
churn clause inverse matching identity starts rewriting every resolve

The churn one is worth calling out: a matching-identity pin must not trigger a write on every resolve, or the unlocked fast path stops being fast and every request takes the lock.

Also verified rather than assumed: the threading feeds from the serving account's identity (wireAccountId on the candidate) and not the internal quota key — that key is the literal 'main' for whichever account is primary, so using it would have silently defeated the whole change while leaving every test green. TTL expiry, the 256-entry cap, and LRS eviction are untouched, and the wire identity does not reach any log line.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Bind sticky pins to the actual account identity so switching the main account or re‑logging a fallback no longer keeps sessions pinned to the wrong (cold‑cache) account. Old: pins recorded only the slot; New: pins store a wire account id and are treated as absent only when both the stored and current identities are known and differ.

  • Pass wireAccountIdByAccount into placement and persist wireAccountId on pins; stamp pre‑upgrade pins on first resolve.
  • Fail‑open: unknown identity on either side retains the pin; malformed stored identities are ignored without dropping the pin.
  • Exclude only the invalidated pin from pending‑byte accounting when choosing its replacement; other sessions’ pins remain counted.
  • Avoid rewrite churn: skip writes when identities match; update metadata only on inputBytes growth, lastSeen touch, or first identity stamp.

Written for commit a7b1745. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/opencode/src/sidebar-state.ts
… under

A pin recorded the slot it chose - 'main' or a fallback's storage id -
not which ChatGPT account that slot held. Switch the main account, or
re-login a fallback (upsertAccount dedups by identity then id then label,
so the storage id is deliberately reused), and every session pinned to
that slot stays pinned while the account underneath it changed. The
session then holds a pin to an account the operator did not choose,
whose prompt cache is cold - and cache warmth is the whole reason the
pin exists.

Placement now records the identity alongside the slot, and a pin whose
recorded identity differs from the account's current one is treated as
absent, so the session is placed again against real quota.

The check fires only when both identities are known and differ. An
unknown on either side retains the pin: identity is not always
resolvable, and invalidating on missing data would re-place sessions at
random and bust the caches this mode protects - worse than the bug. Pins
written before this change carry no identity, so they are retained and
stamped on first resolve rather than dropped, which would have cleared
every warm cache on upgrade.

Invalidating created a state pins did not previously have: present in
the map, but no longer usable. Pending-byte accounting predates that
state and counted such a pin as live pressure on its old slot, so a
session's replacement was steered by load belonging to the identity it
had just been unpinned from. The invalidated pin is now excluded from
the pending bytes its own replacement is placed against. Only that one
entry, and only on a known-to-known mismatch: other sessions' pins are
not this call's business, and re-filtering the whole map by identity
would drop un-stamped pre-upgrade pins from the accounting, which is the
same fail-open violation in a different place.
@iceteaSA
iceteaSA force-pushed the feat/sticky-identity-binding branch from 8d00ee7 to a7b1745 Compare August 12, 2026 14:43
@ualtinok
ualtinok merged commit fbf1b21 into cortexkit:main Aug 13, 2026
5 checks passed
@iceteaSA
iceteaSA deleted the feat/sticky-identity-binding branch August 13, 2026 20:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants