Skip to content

fix(lcm)!: store LCM sessions under their store scope - #2864

Merged
ScriptedAlchemy merged 11 commits into
masterfrom
cursor/lcm-sessions-store-scope-14ec
Oct 2, 2026
Merged

ScriptedAlchemy merged 11 commits into
masterfrom
cursor/lcm-sessions-store-scope-14ec

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Session rows that LCM creates before the rollout projection now take the project fields of the session store they live in (project_id/project_id for a project session store, user/user for the profile session store) instead of an invented unknown project. The rollout projection's working directory then only refines project_path.
  • The placeholder-replacement special case in observation reconcile is deleted, with LCM_UNKNOWN_PROJECT_KEY, LCM_LEGACY_PLACEHOLDER_PROJECT_KEY, and lcm_unscoped_session_project. A scope-shaped row merges through the ordinary reconcile rules.
  • LCM schema 13 → 14. Coordinator decision: the shipped-v13 admission guarantee (shipped_v13_profile_remains_admissible_for_projector_rebuild, cf4b1a2) is a compatibility promise the project no longer makes. A shipped v13 session store can hold LCM rows with the invented project; it is refused typed, listed in reset_required_stores, and recreated empty by the scoped tracedecay wipe --stale --yes (same shape as the session-temporal v6→v7 bump in perf(sessions)!: extend session generations, don't copy them #2761). No migration, no legacy-row repair.

Changes

  • tracedecay-store: session_project_fields(scope) is the one scope → (project_key, project_path) authority; StoreShardScopeV1::session_scope() names the scope of a shard's sessions (None for non-session shards).
  • tracedecay-lcm: compress, compress_retained_page, record_session_boundary take the store's ObservationScopeV1; ensure_session is private and leaves started_at unset; new LcmError::NotASessionStore (not_a_session_store, non-retryable); LCM_SCHEMA_VERSION = 14.
  • tracedecay-sessions / tracedecay-global-db: SessionStoreAccess::lcm_session_scope() resolves the scope from the registered binding and refuses non-session stores before a write transaction; reconcile's placeholder early return is removed.
  • The earlier "keep v13 and repair its shell rows" commit (fab4913) is reverted.
  • Tests: schema::tests::shipped_v13_profile_remains_admissible_for_projector_rebuild is replaced by shipped_v13_profile_refuses_typed_without_upgrading (v13 marker → ProfileResetRequired { found_version: Some(13), required_version: 14 } from admission and from ensure_lcm_schema, marker left at 13). The transport acceptance case is now session_stores_at_shipped_lcm_schema_13_refuse_sessions_only_until_their_scoped_reset: a real daemon over a profile whose project and profile session stores are aged to 13 keeps serving code, refuses session reads typed with the wipe --stale --yes remedy, doctor counts both as pending operator actions, the scoped reset deletes exactly those two stores (every other profile file byte-identical), and the restarted daemon serves sessions with an empty census.

Fail before / pass after

  • cargo test -p tracedecay-lcm --lib -- schema::tests::shipped_v13 with LCM_SCHEMA_VERSION set back to master's 13: FAILED a shipped v13 store must refuse admission: Current. With 14: ok.
  • session_project_rebind_tests::lcm_compression_before_the_rollout_stores_the_session_under_its_store_scope fails on master (row stored as ("unknown", "unknown")), passes here; a_rollout_starting_after_lcm_compression_keeps_its_own_start_time fails without the started_at fix.

Runtime proof (debug tracedecay built from this branch, isolated HOME/TRACEDECAY_DATA_DIR, daemon under systemd-run --user --scope -p MemoryMax=6G)

fresh markers: project=14 profile=14
== age both session stores to the shipped LCM schema 13
reset_required_stores: [{"authority":"LCM","found_version":13,"reason":"LCM profile schema 13 is incompatible with required schema 14; reset the profile","remedy":"tracedecay wipe --stale --yes","required_version":14,"store":"profile sessions"},{"authority":"LCM","found_version":13,"reason":"LCM profile schema 13 is incompatible with required schema 14; reset the profile","remedy":"tracedecay wipe --stale --yes","required_version":14,"store":"project sessions proj_860e8e21b6515288"}]
lcm_status project: {"kind":"reset_required","authority":"LCM","found":13,"required":14,"remedy":"tracedecay wipe --stale --yes"}
lcm_status user:    {"kind":"reset_required","authority":"LCM","found":13,"required":14,"remedy":"tracedecay wipe --stale --yes"}
== tracedecay wipe --stale --yes
wipe exit 0
reset profile sessions (4 entries removed from …/.tracedecay); the daemon recreates it empty
reset project sessions proj_860e8e21b6515288 (5 entries removed from …/.tracedecay/projects/proj_860e8e21b6515288); the daemon recreates it empty
reset_required_stores after reset: []
lcm_status project: null   (no problem)
rebuilt markers: project=14 profile=14

Verification (local; CI not consulted)

  • cargo test -p tracedecay-store -p tracedecay-lcm -p tracedecay-sessions -p tracedecay-session-runtime -p tracedecay-global-db (test-helpers): global_db lib 389, registered_schema_fail_closed 1, session_relation_graph 8; lcm lib 166 (2 ignored, as on master), lcm_suite 15; session_runtime lib 129, session_store_read_cost 7; sessions lib 539; store lib 73, store_suite 52; hotpath_coverage 1 each.
  • cargo test -p tracedecay --features test-transport,test-helpers: session_suite 437 passed; full mcp_suite 617 passed; transport_acceptance_suite -- stale_sessions_store_reset 6 passed (re-run on the final merged tip).
  • cargo clippy -p tracedecay-store -p tracedecay-lcm -p tracedecay-sessions -p tracedecay-session-runtime -p tracedecay-global-db -p tracedecay --all-targets (test features) -D warnings: clean. cargo fmt --all -- --check: clean.
  • cargo check --workspace --all-targets --target x86_64-pc-windows-gnu --features tracedecay/test-transport,tracedecay/test-helpers,tracedecay-cli/test-transport: exit 0.
  • Wire contract unchanged (NotASessionStore maps to existing failed/unavailable states); @tracedecay/sdk unchanged, the refusal surfaces as the existing reset_required problem.

BREAKING CHANGE: LCM schema 14. Session stores at LCM schema 13 (every profile written since v1.0.0-beta.50) refuse session features as reset-required for the LCM component (found 13, required 14) until tracedecay wipe --stale --yes resets exactly those session stores; code intelligence keeps serving meanwhile. LCM-created sessions now carry their store's project or profile scope instead of an invented unknown project.

ScriptedAlchemy and others added 5 commits October 2, 2026 00:56
BREAKING CHANGE: LCM schema 14. Session stores written by earlier versions hold LCM-created session rows with an invented project and report reset-required.
ensure_session lost its last external caller when session rows started taking their store scope; only compression may insert them. The rebind test now runs LCM compression without a wall-clock deadline.

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
LCM refuses to write session rows into a store whose shard holds no sessions; this pins that only profile and project session shards resolve a scope.

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…-store-scope-14ec

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
@changeset-bot

changeset-bot Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 330f879

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

ScriptedAlchemy and others added 3 commits October 2, 2026 03:41
LCM never observes when a host session starts. Writing unixepoch() into
the row it inserts let that insert time win the min() in session
reconcile, so a rollout that started later reported the LCM compression
time as its start.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Master pins shipped LCM v13 profiles as admissible (cf4b1a2), so the
13->14 bump would force a reset on every shipped store. Keep v13 and keep
replacing the invented-project shells those stores still hold; new LCM
rows already carry their store scope.
@ScriptedAlchemy ScriptedAlchemy changed the title fix(lcm)!: store LCM sessions under their store scope fix(lcm): store LCM sessions under their store scope Oct 2, 2026
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

Resolved the blocker above with option 1 in fab4913 (after merging current master in f5e8b93): LCM stays at schema 13, so shipped v13 profiles stay admissible (master's shipped_v13_profile_remains_admissible_for_projector_rebuild passes), and observation reconcile again replaces the legacy unknown / lcm-active-context shell rows those stores hold (now a private check in observation_projection/state.rs). New LCM rows still carry session_project_fields(scope). The change is no longer breaking, so I dropped the ! from the title; the BREAKING CHANGE footer in the description is stale and should not go into the squash commit.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

🐛 1 issue in files not directly in the diff

🐛 Legacy LCM sessions remain under invented projects

Admitting version-13 stores leaves LCM-only sessions under unknown or lcm-active-context. ensure_session never updates existing rows, and reconciliation runs only when an observation arrives. Project-scoped session reads cannot find those sessions.

Devin Review

LCM schema 14 replaces the shipped-v13 admission guarantee: a store at
the shipped v13 marker is refused with the typed LCM reset (found 13,
required 14), listed in reset_required_stores, and recreated empty by
the scoped reset. The unit test that pinned v13 as admissible now pins
the refusal; the transport acceptance case ages both session stores to
13 and proves refusal, scoped reset, and serving afterwards.

BREAKING CHANGE: session stores at LCM schema 13 (every profile written
since v1.0.0-beta.50) refuse session features until
`tracedecay wipe --stale --yes` resets exactly those session stores.
@ScriptedAlchemy ScriptedAlchemy changed the title fix(lcm): store LCM sessions under their store scope fix(lcm)!: store LCM sessions under their store scope Oct 2, 2026
@ScriptedAlchemy
ScriptedAlchemy merged commit 9431f86 into master Oct 2, 2026
14 of 24 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the cursor/lcm-sessions-store-scope-14ec branch October 2, 2026 13:33
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