Skip to content

refactor(daemon): split src/daemon/types.ts into request types and session state - #2346

Open
thymikee wants to merge 3 commits into
mainfrom
claude/2338-daemon-types-split
Open

refactor(daemon): split src/daemon/types.ts into request types and session state#2346
thymikee wants to merge 3 commits into
mainfrom
claude/2338-daemon-types-split

Conversation

@thymikee

@thymikee thymikee commented Sep 6, 2026

Copy link
Copy Markdown
Member

Summary

Closes #2338. src/daemon/types.ts served the dispatch request shape and the live session record from one file, and sat in the only daemon type cycle. Three modules replace it:

  • src/daemon/daemon-request-wire.tsDaemonWireRequest: no internal key, no property path to SessionState or DeviceLease. This is what F1 (refactor(daemon): extract the session event journal into a workspace package #2341) consumes; it carries command, flags, meta.requestId, meta.clientArtifactPaths.
  • src/daemon/daemon-request.tsDaemonRequest = wire + internal (DaemonRequestInternal stays unexported), plus DaemonResponse/DaemonResponseData/DaemonInvokeFn/DaemonArtifact.
  • src/daemon/session-state.tsSessionState, SessionRef, SessionScope, SessionRuntimeHints, and the interaction-surface shapes.

The cycle is cut by ref-frame-slot.ts, declared below both ref-frame.ts and session-state.ts. It owns the frame value (class still unexported, so the type stays nominal and unconstructible outside); ref-frame.ts keeps every transition and session.refFrame write — R7 rows refFrame and snapshotScopeSource are unchanged.

No behavior change; no re-export shim at the old path. 407 files touched, 386 of them import-line-only — the rename/move exemption applies to the diff budget.

R9 type cycles (production graph): 19 → 18. src/daemon/ref-frame.ts ↔ src/daemon/types.ts is gone; the other 18 are byte-identical, so no new cycle appeared. Largest cycle unchanged at 6 files (packages/provider-webdriver/*).

Production importer split after the change: 197 files — 82 request-only, 42 state-only, 73 both (issue baseline: 79/35/83).

Validation

Tested at 688b1a5 (the pushed head) after pnpm install --frozen-lockfile && pnpm build.

pnpm check:affected --run: every stage passes — format, lint, typecheck, layering, di-seams, fallow, gate-manifest, build, package, integration — except 2 pre-existing failures in vitest-related: app-log-session-resource.test.ts > unwritable tombstone… and durable-capture-resource.test.ts > an unconfirmed failed-adoption cleanup…. Both fail identically on origin/main (27a97ee) in this container: it runs as root, so their chmod-based unwritable-tombstone setup cannot deny a write. 434/436 files, 3109/3111 tests pass. Verify on CI, where the runner is unprivileged.

Layering guard: R7 (31 fields classified, every write inside its owner), R9 (largest cycle 6), R10 (R7 pressure at merge-base, 2 external daemon request/session-state importers → client/client-normalizers.ts, remote/daemon-artifacts.ts, both on daemon-request.ts) all green. The type-level test is src/daemon/__tests__/daemon-request-wire.test.ts; I verified it fails tsc when an internal/SessionState path is reintroduced.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ujrc8LYmvM249WY8921J1Y


Generated by Claude Code

…e modules

`src/daemon/types.ts` served two audiences from one file: the dispatch request
shape and the daemon's live session record. It also sat in the only daemon type
cycle — it imported `RefFrame` from `ref-frame.ts`, which imported `SessionState`
back — so neither file could be read in isolation.

Three modules replace it, each importing only downward:

- `daemon-request-wire.ts` declares `DaemonWireRequest`: a dispatched request
  with no `internal` key and no property path to `SessionState` or `DeviceLease`,
  so a consumer can read a request's command, flags and public metadata without
  depending on the session record.
- `daemon-request.ts` adds the daemon-only half (`DaemonRequestInternal`, which
  stays unexported) plus the response vocabulary.
- `session-state.ts` owns `SessionState` and the shapes only it holds.

The cycle is cut by `ref-frame-slot.ts`, declared below both `ref-frame.ts` and
`session-state.ts`: it owns the frame VALUE (the class stays unexported, so the
type remains nominal and unconstructible from outside), while `ref-frame.ts`
keeps every lifetime transition and every `session.refFrame` write.

No behavior change: every importer moves to the module owning the symbol it
uses, with no re-export shim at the old path. `client-normalizers.ts` takes
`SessionRuntimeHints` from `@agent-device/kernel/contracts`, which declares it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ujrc8LYmvM249WY8921J1Y
A type-level walk over `DaemonWireRequest` fails `tsc` if the shape regains an
`internal` key or grows a property path back to `SessionState` or `DeviceLease`.
Positive controls over `DaemonRequest` prove the walk finds both when they are
there, so a walk that never matches anything cannot pass by accident.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ujrc8LYmvM249WY8921J1Y
R7 now locates the `SessionState` declaration by the declaration itself rather
than by a recorded path: `sessionStateWritePressure` measures the merge-base
tree too, and that tree still declares it in `daemon/types.ts` — a path constant
would measure it as zero pressure and bank the headroom.

R10's external-importer ratchet covers all three modules that replaced
`daemon/types.ts`, so moving a symbol between them cannot reopen the boundary to
a new outside zone. The recorded membership is unchanged: `client-normalizers.ts`
and `remote/daemon-artifacts.ts` both import `daemon-request.ts` only.

The `live-state-shape` and session-resource declaration sites move with
`SessionState`; the depgraph lookalike fixture takes a new plausible path now
that `daemon/session-state.ts` is the real root.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ujrc8LYmvM249WY8921J1Y
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.49 MB 4.49 MB +61 B
Package (unpacked) 4.49 MB 4.49 MB +61 B
Package (download) 1.33 MB 1.33 MB +9 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 23.9 ms 24.6 ms +0.6 ms
CLI --help 68.4 ms 68.6 ms +0.3 ms

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.

refactor(daemon): split src/daemon/types.ts into request types and session state

2 participants