@@ -34,7 +34,6 @@ const MANIFEST_FILE = "manifest.jsonl";
3434const METADATA_FILE = "metadata.json" ;
3535const TOOL_OUTPUT_DIR = "tool-output" ;
3636const EVIDENCE_ARCHIVE_DIR = "evidence-archive" ;
37- /** Per-session ownership marker: records which store authored the shared base. */
3837const OWNER_MARKER_FILE = "session-owner.json" ;
3938
4039const log = getLogger ( [ LOG_NAMESPACE_ROOT , "session" , "context-store" ] ) ;
@@ -87,13 +86,8 @@ function sanitizeCallId(callId: string): string {
8786}
8887
8988/**
90- * Unique versioned base name for a transcript file (e.g.
91- * `turns-1730000000000-a1b2c3d4e5f6....jsonl`). A store whose transcript is
92- * foreign to the on-disk owner writes to one of these instead of the shared
93- * base, so concurrent sessions keep disjoint files. The `-<epoch>-<uuid>`
94- * infix never collides with the numbered segment pattern (`turns-0001.jsonl`),
95- * so the base readers ignore these by design. The UUID suffix keeps names
96- * unique even when two diverts happen within the same millisecond.
89+ * Epoch+uuid avoids numbered segments (`turns-0001.jsonl`) that base
90+ * readers treat as live history.
9791 */
9892export function versionedBaseName ( baseName : string ) : string {
9993 const dot = baseName . lastIndexOf ( "." ) ;
@@ -360,12 +354,6 @@ async function readBaseTurnsFromDisk(dir: string): Promise<ConversationTurn[]> {
360354 return parseSegmentTurns ( text , true , TURNS_FILE , true ) ;
361355}
362356
363- /**
364- * Ownership marker for the shared base. Absent on a fresh/legacy dir; once a
365- * store writes the shared base first it claims the dir with its owner token,
366- * so a later store can tell "I authored this transcript" from "a foreign
367- * store did" without guessing from content shape.
368- */
369357async function readOwnerMarker (
370358 dir : string ,
371359) : Promise < { ownerId : string } | null > {
@@ -567,15 +555,8 @@ export interface SessionStores {
567555export interface SessionStoreOptions {
568556 signer ?: CommitSigner ;
569557 /**
570- * Stable identity of the session this store belongs to (e.g. the session
571- * id). Stores with the same id on one dir are the same session: resuming
572- * stores recognize the on-disk ownership marker as their own, so their
573- * rewrites (compaction) stay canonical on the shared base. Stores with
574- * different (or no) ids are foreign to each other and divert to disjoint
575- * versioned files instead of clobbering the shared transcript. When omitted
576- * the store uses a per-instance random token, so any two stores on one dir
577- * are treated as foreign unless the incoming transcript extends the on-disk
578- * history (a plain resume).
558+ * Same id shares the marker; omit for a random token, foreign unless the
559+ * transcript extends history.
579560 */
580561 ownerId ?: string ;
581562}
@@ -594,13 +575,7 @@ export async function createSessionStores(
594575 const base = await createIsogitStore ( dir , signer ) ;
595576 const pendingBlobFilepaths = new Set < string > ( ) ;
596577 const pendingSegmentPaths = new Set < string > ( ) ;
597- // Stable per-session owner token (the caller's session id, or a random
598- // per-instance token). The on-disk ownership marker is compared against it
599- // to tell "I authored this transcript" from "a foreign store did".
600578 const ownerToken = opts ?. ownerId ?? globalThis . crypto . randomUUID ( ) ;
601- // Base file each writer owns. A store that detects a foreign on-disk owner
602- // (a concurrent session sharing the dir) switches both bases to disjoint
603- // versioned names so its writes never clobber the other session.
604579 let turnsBaseName = TURNS_FILE ;
605580 let promptBaseName = PROMPT_FILE ;
606581 let versionedBase : string | null = null ;
@@ -667,9 +642,7 @@ export async function createSessionStores(
667642 liveTurnRefs = [ ...turns ] ;
668643 return ;
669644 }
670- // Fresh-store first write: the ownership decision and the first write are
671- // serialized per dir so two concurrent fresh stores cannot both claim the
672- // shared base (one becomes owner, the other diverts instead of racing).
645+ // First claim is per-dir so two fresh stores cannot both own the base.
673646 await withResolvedDirLock ( dir , async ( ) => {
674647 if ( liveTurnRefs !== null ) {
675648 if ( refPrefixLength ( liveTurnRefs , turns ) < liveTurnRefs . length ) {
@@ -682,39 +655,30 @@ export async function createSessionStores(
682655 }
683656 const marker = await readOwnerMarker ( dir ) ;
684657 if ( marker === null ) {
685- // No owner yet: this store claims the shared base.
686658 await writeOwnerMarker ( dir , ownerToken ) ;
687659 await writeSegmented ( writeTurnsSegmented , turns ) ;
688660 liveTurnRefs = [ ...turns ] ;
689661 return ;
690662 }
691663 const live = await loadLiveTurnsFromDisk ( ) ;
692664 if ( marker . ownerId === ownerToken ) {
693- // This store is the session owner (resuming with a stable id): any
694- // rewrite, including a compaction that replaces history, is canonical
695- // on the shared base — never divert an owner's own rewrite.
665+ // Matching owner keeps compaction on the shared base, not a divert.
696666 await writeSegmented ( writeTurnsSegmented , turns ) ;
697667 liveTurnRefs = [ ...turns ] ;
698668 return ;
699669 }
700670 if ( live . length > 0 && contentPrefixLength ( live , turns ) < live . length ) {
701- // The on-disk transcript was authored by a foreign store and this
702- // store's transcript is not an extension of it: rewriting the shared
703- // base would clobber the other session's records. Divert to a
704- // disjoint versioned base; the write is staged and lands at commit so
705- // a crash before then leaves no orphaned versioned file.
671+ // Stage until commit so a crash leaves no orphaned versioned file.
706672 divertToVersionedBases ( ) ;
707673 unpublishedRewrite = [ ...turns ] ;
708674 return ;
709675 }
710- // Foreign marker but the incoming transcript extends the on-disk history
711- // (a plain resume without a stable id): continue the shared base.
676+ // Prefix extension with no stable id resumes the shared base.
712677 await writeSegmented ( writeTurnsSegmented , turns ) ;
713678 liveTurnRefs = [ ...turns ] ;
714679 } ) ;
715680 }
716681
717- /** Live turns from disk for a fresh store (base + extra segments). */
718682 async function loadLiveTurnsFromDisk ( ) : Promise < ConversationTurn [ ] > {
719683 const extraTexts = await readExtraSegmentTexts ( dir , TURNS_FILE ) ;
720684 let baseTurns : ConversationTurn [ ] ;
@@ -782,20 +746,11 @@ export async function createSessionStores(
782746 }
783747 }
784748 if ( versionedBase !== null && liveTurnRefs === null ) {
785- // A divert is staged but has not landed at commit yet. No versioned
786- // file may be written before commit — a crash would orphan it — so the
787- // prompt snapshot is deferred to the next cycle (after the commit, the
788- // versioned base exists and liveTurnRefs is set).
749+ // No versioned file before commit; a crash would orphan it.
789750 return ;
790751 }
791752 if ( live === null || ! promptEqualsLiveTurns ( live , turns ) ) {
792- // The prompt is a materialized snapshot and legitimately differs from
793- // the live turns on a normal resume (context transforms, ephemeral
794- // turns, summaries), so a differing prompt alone never proves a foreign
795- // store. Ownership is decided by writeTurns: only a foreign owner with a
796- // divergent transcript diverts to versioned bases, and
797- // writePromptSegmented is rebound with it. Prompt writes therefore stay
798- // on the shared base unless that divert already happened.
753+ // A differing prompt is normal on resume and must not divert.
799754 await writeSegmented ( writePromptSegmented , turns ) ;
800755 return ;
801756 }
0 commit comments