fix(core): stop distinct account files sharing one state document - #91
fix(core): stop distinct account files sharing one state document#91iceteaSA wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
All reported issues were addressed across 2 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
The state path was derived with a suffix test, so any config filename ending in the canonical name resolved to the same sibling state file. Two distinct rosters in one directory would share one state document and overwrite each other's stored fields.\n\nMatch the basename exactly, reject explicit state paths that resolve to the config path, and compare common case-insensitive aliases on Windows and macOS. Resolve realpath identities when available, falling back cleanly for absent files. Path identity remains defense-in-depth only: hardlinks and bind mounts cannot be distinguished this way. A custom filename now relocates its state to its own .state.json sibling; no migration is offered because the previously shared state cannot be safely attributed back to either roster.
87500d5 to
c592c09
Compare
|
Updated to Both Cubic findings accepted, each with one adjustment to the proposed remedy. Case-only aliases. Fixed, but not gated on Symlink aliasing. Fixed, with a constraint the suggestion did not account for: Since Stated in-source rather than implied: this is defense-in-depth, not proof of distinctness — hardlinks and bind mounts defeat any pathname-based check. One process note worth recording, since it affected what the evidence is worth. The first attempt at mutation proof was invalid: both new tests import the new A wiring test was also added: both original tests called the helper directly, so the call at Gate: 1069 pass / 0 fail (1064 on |
Fixes #89.
Two path-resolution defects in
core/account-paths.ts, both ending with two documents fighting over one file. Branched from9bf8f4c, independent of #87.1. Suffix matching aliased distinct config files onto one state file
deriveStatePathtestedconfigPath.endsWith('openai-auth.json'), which matches any filename ending in that string:OPENCODE_OPENAI_AUTH_FILE/tmp/openai-auth.json/tmp/openai-auth-state.json/tmp/team-openai-auth.json/tmp/openai-auth-state.jsonTwo rosters in one directory silently shared one state document, and the second writer overwrote the first's stored per-account fields. Now compares the basename exactly, so a custom filename derives its own
.state.jsonsibling.2. An explicit state path could equal the config path
getAccountStatePathacceptedOPENCODE_OPENAI_AUTH_STATE_FILEwithout checking it differed from the config path. Pointing both variables at one file made a mutation write the config document and then the state document to it, replacing the roster with a state-only document. Now both are resolved and an equal pair is rejected.Behaviour change worth noting before merge
A user whose config path is a non-canonical filename (for example
/tmp/team-openai-auth.json) previously got state at/tmp/openai-auth-state.jsonand now gets/tmp/team-openai-auth.json.state.json. Their existing state document is orphaned, so they would appear logged out.No migration is offered, deliberately: the previously shared state document cannot be safely attributed back to either roster, and guessing risks handing one roster's stored fields to another. Happy to add a one-time detection-and-warn path instead if you would prefer that.
The error message names only the variable the user actually set:
Tests
1066 pass / 0 fail(1064 on9bf8f4c, one redundant test removed, two added),tscclean.Both new tests were mutation-checked independently by a reviewer rather than trusted from the author — an earlier revision of this branch carried a test that could not fail, because its input mapped to the same output under both the old and new logic. It was deleted rather than kept as decoration.
basename→endsWithconfigPathinstead ofexplicitNeed help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Stops distinct account config files from sharing one state document and prevents state from being written into the config file. Old: suffix-based matching mapped multiple config filenames to one
openai-auth-state.jsonand allowed an explicit state path equal to the config; New: basename equality derives a per-file.state.jsonsibling and explicit paths that resolve/alias to the config are rejected with a clear error.Review and rollout
packages/opencode/src/core/account-paths.ts:deriveStatePathcomparesbasename; newaccountPathsCollidedetects equality viaresolve,realpath, and case-insensitive comparisons on Windows/macOS;getAccountStatePathuses it and throws if the explicit path aliases the config. Tests added inaccount-paths.test.tsfor case-only aliases, symlinks, and broken links. Note: hardlinks/bind mounts can still collide.<config>.state.json; previously shared state becomes orphaned and users may appear logged out. No migration.Written for commit c592c09. Summary will update on new commits.