Skip to content

fix(state): key the session on the project, not the working directory - #43

Merged
anilcancakir merged 1 commit into
masterfrom
fix/session-key-project-root
Aug 20, 2026
Merged

fix(state): key the session on the project, not the working directory#43
anilcancakir merged 1 commit into
masterfrom
fix/session-key-project-root

Conversation

@anilcancakir

Copy link
Copy Markdown
Contributor

What

StateFile.projectRootFor walks from the working directory to the nearest ancestor holding a pubspec.yaml, and both the session key and the projectRoot start records use it.

Why

The session isolation shipped in #40 only held for callers standing in the repo root.

sessionOwnershipError deliberately lets a command run from backend/ or a package subdirectory through. But sessionPathFor hashed the cwd, so from that same subdirectory the session FILE lookup missed, fell back to the shared ~/.artisan/state.json pointer, and with two apps up the ownership guard then refused the command for driving somebody else's app. A false refusal, on exactly the layout the guard was written to permit.

Design notes

  • Nearest pubspec, not outermost. That is the unit artisan start boots; two packages in one repository are two apps and want two sessions. Pinned by a test.
  • No pubspec anywhere falls back to the directory itself. Climbing to the filesystem root would land every non-Dart caller in one shared session, which is the failure this subsystem exists to remove. Pinned by a test.
  • start records the walked root too. A raw cwd there would make a start from a subdirectory record a root the ownership check measures every later command against, refusing the ones run from the package root. That would have traded one false refusal for another.

Testing

Three new cases in test/state/state_file_session_test.dart (subdirectory resolves to the project session, nested package keeps its own, no-pubspec keys on itself). Verified red before the fix: the subdirectory hashed to a different session.

dart format zero diff, dart analyze zero issues, 1222 tests green.

`sessionOwnershipError` blesses running from a package subdirectory, but
`sessionPathFor` hashed `Directory.current.path`. A command from there
missed its own session file, fell back to the shared pointer, and with two
apps up was refused for driving somebody else's: a false refusal, and the
defeat of the isolation 0.0.10 shipped. It only held for callers standing
in the repo root.

`projectRootFor` walks to the nearest ancestor holding a `pubspec.yaml`.
Nearest rather than outermost, because that is the unit `artisan start`
boots; two packages in one repository are two apps. No pubspec anywhere up
the chain falls back to the directory itself, so a non-Dart caller does not
end up sharing one session with every other.

`start` records the same walked root, because a raw cwd there would make a
start from a subdirectory record a root that the ownership check then
measures every later command against, refusing the ones run from the
package root.
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@anilcancakir
anilcancakir merged commit 6518bb8 into master Aug 20, 2026
2 checks passed
@anilcancakir
anilcancakir deleted the fix/session-key-project-root branch August 20, 2026 18:21
anilcancakir added a commit that referenced this pull request Aug 20, 2026
Patch release for #43: the session isolation 0.0.10 introduced only held for callers standing in the repo root, and a command run from a package subdirectory got a false refusal on exactly the layout the ownership guard was written to permit.

All six version stamps bumped in one pass, using the list the 0.0.10 cut had to discover: pubspec, the MCP handshake string, both example pubspecs, the skill stamp, and the example lockfile. The two example pubspecs moved together, so pub resolved cleanly rather than failing with `version solving failed`, and the MCP guard test confirms the handshake string moved with the package.
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.

1 participant