Skip to content

feat: injectable session storage — ChatSessionRepository port (SDK P2, slice 2)#225

Merged
roackb2 merged 1 commit into
mainfrom
feat/sdk-injectable-session-store-p2
Jul 2, 2026
Merged

feat: injectable session storage — ChatSessionRepository port (SDK P2, slice 2)#225
roackb2 merged 1 commit into
mainfrom
feat/sdk-injectable-session-store-p2

Conversation

@roackb2

@roackb2 roackb2 commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

Second slice of SDK maturation P2 (pluggable storage, posture rung 5), following the merged artifact-repository pattern (#224): chat session persistence is now injectable, so a hosted service can back the session catalog and session bodies with its own storage instead of the file catalog under the state root.

Design

Port already existed — this slice makes it real. ChatSessionRepository (list / readCatalog / read / save) was an internal interface with one implementer; the engine and the whole turn path constructed FileChatSessionRepository from a sessionStoragePath string at nine call sites. Now:

  • Port cleanup: dropped deriveStoragePaths() from the port — it's file-layout detail only the File implementation needs internally. Hosted implementers no longer have to invent fake paths. Port doc added.
  • Resolve once at the engine boundary: createConversationEngine({ sessionRepository }) resolves config.sessionRepository ?? new FileChatSessionRepository({ sessionStoragePath }) in config normalization. The instance flows to:
    • the session service (create/read/update/rename/archive/lease/settings)
    • turn context building (ConversationTurnContextBuilder)
    • preflight lease acquisition + compaction seed persistence
    • completed-turn persistence
    • background memory-maintenance turn-summary writes
    • lease clearing (clearLeaseFromStorage now takes the repository)
    • quickstart runner pass-through (runQuickstartConversationCli({ sessionRepository }))
  • Direct EngineConversationTurnService.run(args) callers get the same resolve-once at the run() boundary (args.sessionRepository ?? File default), and every inner service receives the instance in its args instead of re-deriving storage from paths — deleting all nine scattered constructions.
  • Session policy stays in Heddle: leases, records, and compaction state remain service-owned; the repository only persists.

Curated surface (rung 5): exports ChatSessionRepository, FileChatSessionRepository, ChatSessionCatalog(Entry), and ChatSession so implementers have the full contract.

Proof

  • New engine test: session create/read/rename through an in-memory repository, asserting the on-disk chat-sessions.catalog.json is never created.
  • Reworked inner-service tests (preflight/persistence/memory) now inject repository instances through the port rather than paths — the turn path is exercised port-first.

Verification

  • yarn build — pass
  • yarn test:unit — 601 passed · yarn test:integration — 285 passed
  • yarn lint — clean

Docs: "Bring your own session storage" guide section, ladder README rung 5 updated, curated index rung-5 note updated. Traces and memory remain path-oriented — next slices, same pattern.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CjFdZgZhov8wdwFinVTfjz

Second pluggable-storage slice (SDK posture rung 5), following the
artifact-repository pattern: hosts can back chat session persistence with
their own storage instead of the file catalog under the state root.

- Document ChatSessionRepository as the session persistence port (catalog
  entries plus full session bodies) and drop deriveStoragePaths() from the
  port: it is file-layout detail only the File implementation needs, so
  hosted implementers no longer have to invent fake paths.
- createConversationEngine accepts sessionRepository and resolves the
  default FileChatSessionRepository once at the engine boundary. The
  instance flows to the session service and the whole turn path: turn
  context building, preflight lease/compaction persistence, completed-turn
  persistence, background memory-maintenance writes, and lease clearing.
  The quickstart runner passes it through.
- Inner turn services (context builder, preflight, persistence, memory
  maintenance) now receive the repository instance in their args instead
  of re-deriving storage from sessionStoragePath at nine call sites.
  EngineConversationTurnService.clearLeaseFromStorage takes the repository
  directly.
- Export ChatSessionRepository, FileChatSessionRepository, catalog types,
  and ChatSession from the curated rung-5 surface so implementers have the
  full contract.

Proof: a unit test drives session create/read/rename through an in-memory
repository and asserts the on-disk session catalog is never created.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjFdZgZhov8wdwFinVTfjz
@roackb2 roackb2 merged commit 04a6f00 into main Jul 2, 2026
5 checks passed
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