Skip to content

refactor: dedup daemon handler capability/session/recordAction boilerplate#904

Merged
thymikee merged 1 commit into
mainfrom
refactor/daemon-handler-dedup
Jun 27, 2026
Merged

refactor: dedup daemon handler capability/session/recordAction boilerplate#904
thymikee merged 1 commit into
mainfrom
refactor/daemon-handler-dedup

Conversation

@thymikee

Copy link
Copy Markdown
Member

What

Collapses three repeated daemon-handler boilerplate patterns into shared helpers. Pure-deletion dedup, no behavior change.

1. Capability guard -> requireCommandSupported(command, device, { message?, hint? })

The verbatim if (!isCommandSupportedOnDevice(cmd, device)) return errorResponse('UNSUPPORTED_OPERATION', '<cmd> is not supported on this device') block recurred ~21x. New helper (next to errorResponse in src/daemon/handlers/response.ts) returns the failure response or null:

  • The richest variant (request-generic-dispatch.ts) also called unsupportedHintForDevice — that hint is folded in behind a hint: true option, so that site keeps its hint and every other site stays identical (no hint key).
  • Custom-message sites (react-native, session-state shutdown, install-source, snapshot-runtime diff/snapshot) pass message to preserve their exact text.

2. No-active-session literal -> noActiveSessionError() / NO_ACTIVE_SESSION_MESSAGE

The 'No active session. Run open first.' SESSION_NOT_FOUND literal was duplicated across ~9 sites (handlers using errorResponse, the inline request-router finalize object, and the AppError throw in interaction-runtime). All now reference the single builder/constant. The if (!session) return ... guard structure is preserved so TypeScript narrowing is unchanged.

3. recordAction literal -> existing recordSessionAction wrapper

The inline sessionStore.recordAction(session, { command, positionals, flags, result }) object literal was constructed at ~15 routable sites. recordSessionAction (handler-utils.ts, previously 1 caller) gained optional { positionals?, flags? } overrides to cover the variants (resolved positionals, stripped publicFlags, hardcoded []). Routed: find (6 sites + recordFindAction), record-trace (2), record-trace-recording (2), session (2), session-close, session-perf-xctrace, install-source.

Sites intentionally left as-is (would change recorded shape or have no req in scope): session-open (carries a runtime field), request-generic-dispatch/interaction-common recorders (no req param), and the pre-existing recordIfSession/recordSnapshotRuntimeAction local wrappers.

LOC dropped

Net -56 (193 insertions / 249 deletions across 24 files, including the +~43 lines of new shared helpers).

Validation

  • tsc -p tsconfig.json --noEmit — clean
  • oxfmt --write + oxlint --deny-warnings on all changed files — clean
  • vitest run daemon — 901/902 pass; the single failure (daemon-client > downloadRemoteArtifact removes partial files after mid-stream aborts) is unrelated network/abort-timing flakiness (untouched file) and passes in isolation.

behaviorless

Every site keeps its exact error code/message (and hint where present) and recorded action shape: requireCommandSupported's default message is \${command} is not supported on this device`(verifiedPUBLIC_COMMANDS.clipboard/.typeequal their literals);message/hintoptions reproduce the non-default variants;noActiveSessionError()returns the identical{ ok: false, error: { code, message } }; recordSessionAction` overrides reproduce each site's positionals/flags exactly.

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.4 MB 1.4 MB -1.8 kB
JS gzip 445.7 kB 445.5 kB -207 B
npm tarball 584.6 kB 584.6 kB +6 B
npm unpacked 2.0 MB 2.0 MB -1.8 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 27.6 ms 26.3 ms -1.3 ms
CLI --help 46.9 ms 48.2 ms +1.4 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/9722.js +125 B -65 B
dist/src/session.js -791 B -64 B
dist/src/find.js -363 B -57 B
dist/src/selector-runtime.js -153 B -47 B
dist/src/snapshot.js -142 B -21 B

…plate

Collapse three repeated daemon-handler patterns into shared helpers in
src/daemon/handlers/response.ts and handler-utils.ts:

- requireCommandSupported(command, device, { message?, hint? }) replaces ~21
  verbatim isCommandSupportedOnDevice UNSUPPORTED_OPERATION guards. The richest
  variant (generic dispatch) folds the unsupportedHintForDevice hint behind the
  `hint` option; custom-message sites pass `message`.
- noActiveSessionError() / NO_ACTIVE_SESSION_MESSAGE replace the duplicated
  'No active session. Run open first.' SESSION_NOT_FOUND literal across handlers
  (and the inline router/AppError variants).
- recordSessionAction gains optional positionals/flags overrides so ~15 inline
  sessionStore.recordAction({ command, positionals, flags, result }) literals
  (find, record-trace, record-trace-recording, session, session-close,
  session-perf-xctrace, install-source) route through the one wrapper.

behaviorless: every site keeps its exact error code/message (and hint where
present) and recorded action shape; helpers reproduce each variant.
@thymikee

Copy link
Copy Markdown
Member Author

Reviewed the refactor pass directly. I did not find behavior issues in the shared unsupported-command/session/action-recording helpers: the existing custom messages, stripped find flags, resolved positionals, and generic-dispatch hints are preserved.

Current blocker: GitHub reports the PR as conflicting, and local merge simulation reproduces a single content conflict in src/daemon/handlers/snapshot-alert.ts. It looks mechanical: main changed the alert timeout/import shape while this PR switched the capability guard to requireCommandSupported('alert', device). Please rebase and resolve that import/guard conflict, then rerun checks before merge.

@thymikee thymikee force-pushed the refactor/daemon-handler-dedup branch from ff9a145 to 9d77442 Compare June 27, 2026 13:02
@thymikee

Copy link
Copy Markdown
Member Author

Done — rebased onto main and resolved the snapshot-alert.ts conflict. It was exactly the mechanical import/guard overlap you described: kept main's import homes (alert constants from alert-contract.ts, parseTimeout from utils/parse-timeout.ts, since #901 deleted parse-utils.ts) plus this PR's requireCommandSupported('alert', device) guard, and dropped the now-unused isCommandSupportedOnDevice import. Re-ran checks: tsc --noEmit clean, oxfmt + oxlint clean on all 24 files, 636 targeted daemon tests pass. Now MERGEABLE.

@thymikee

thymikee commented Jun 27, 2026

Copy link
Copy Markdown
Member Author

Re-reviewed after the rebase with plans/perfect-shape.md in mind. No new findings.

The conflict resolution preserved main's alert import split and this PR's requireCommandSupported('alert', device) guard. The PR remains a narrow daemon-boilerplate dedup: it reduces repeated unsupported/session/action-recording code without changing command identity ownership, platform routing, or ADR 0003 daemon registry boundaries. merge-tree is clean against origin/main, and GitHub shows all 20 checks green.

@thymikee thymikee merged commit 91d1b4c into main Jun 27, 2026
20 checks passed
@thymikee thymikee deleted the refactor/daemon-handler-dedup branch June 27, 2026 13:29
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-06-27 13:29 UTC

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