refactor(server): extract agent-type domain logic out of HTTP handler #1143
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/agent-hooks!1143
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "code-lead/1136"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
agent_type/agent_type_container/agent_type_routing/agent_type_configCRUD, and provider-chain parsing out ofapps/server/src/http/handlers/agent-types-settings.ts(1737 lines) into a newapps/server/src/domain/agent-type/module.main.tsis unchanged.Module layout
domain/agent-type/types.ts— DB row shapes, scope-set constants,DomainResultdiscriminated union (ok/err/errsconstructors).domain/agent-type/parsing.ts— pure JSON-field parsers + DB-row → API-view formatters.domain/agent-type/revisions.ts—insertRevision+ per-table snapshot serialisers for theconfig_revisionaudit log (AT-7).domain/agent-type/service.ts—listAgentTypes,getAgentTypeView,upsertIdentity/upsertContainer/upsertRouting/upsertDispatch,deleteIdentity/deleteContainer/deleteRouting,restoreAgentTypeRevision. Callers seeDomainResult<T>— noRequest/Responseimports.domain/agent-type/service.test.ts— domain-layer unit tests that exercise revision auditing, scope validation, and per-field error shapes without constructing a Request, satisfying the AC "domain module is unit-testable without HTTP context".Out of scope (per the issue)
agent-types-settings.ts)Closes #1136
Test plan
just qa— typecheck + Biome + 3359 tests all passagent-types-settings.test.tssuite passes unmodifieddomain/agent-type/service.test.tssuite covers revision / scope / per-field error paths via the service directly🤖 Generated with Claude Code
Move revision auditing, scope validation, agent_type/container/routing/config CRUD, and provider-chain parsing from `http/handlers/agent-types-settings.ts` (1737 lines) into a new `domain/agent-type/` module split across: - `types.ts` — DB row shapes, scope sets, DomainResult union - `parsing.ts` — pure JSON field parsers + view formatters - `revisions.ts` — insertRevision + per-table snapshot serialisers - `service.ts` — listAgentTypes / getAgentTypeView / upsert* / delete* / restoreAgentTypeRevision; returns DomainResult so callers never see HTTP. The HTTP handler shrinks to ~190 lines and is a pure parse-request → call-domain → serialize-response layer; route registration in main.ts is unchanged and the API contract (request/response shape, status codes, revision audit semantics) is preserved verbatim. A new domain-layer test suite (`service.test.ts`) exercises the same revision / scope-validation / error-shape paths without constructing a Request, satisfying the AC of #1136 ("domain module is unit-testable without HTTP context"). The existing handler tests continue to pass unmodified. Closes #1136 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>Code Review —
refactor(server): extract agent-type domain logic out of HTTP handlerCI: ✅ green · Mergeable: ✅ · 3 359 tests pass
Overview
Exactly what the issue asked for. The 1 737-line HTTP handler is replaced by a ~110-line HTTP glue shell, with all domain logic landing in four cohesive new files under
domain/agent-type/. The API contract (request/response shapes, status codes, revision-audit semantics) is preserved verbatim, verified by the unmodified handler test suite.What's good
Handler is now a pure HTTP boundary. Every exported handler reduces to: parse request → call domain service → map
DomainResult<T>toResponse. No business logic leaks back in.DomainResult<T>discriminated union is clean. Theok()/err()/errs()constructors are simple and consistent. The typederrorsmap onDomainErrsmatches the existing per-field error shape the clients already expect — no client-visible change.Testability AC is met.
service.test.ts(496 lines) drives all upsert / delete / restore / list / get paths through the service directly, with noRequestorResponsein sight. The full revision-auditing and scope-validation paths are now exercised independently of the HTTP layer. Good coverage of the error paths too (404, 400 witherrorsmap, scope rejection).Revision auditing is faithfully preserved.
insertRevision+ the four snapshot serialisers are a direct lift; the hash function, thecommentfield on "restored from revision #N", and the "deleted" sentinel are all intact.Legacy restore error shape is handled explicitly. The
handleRestoreAgentTypeRevisionpreserving{ ok: false, error }rather than adopting the new{ error }shape — with a comment calling it out — is the right call. Breaking that silently would have been worse.Module boundaries hold. No raw
bun:sqliteimports outsideinfrastructure/database/(consistent withsql-layer-check). The new domain files import from the DB layer through the normal path.Minor observations (none blocking)
service.tsis 1 429 lines. It's cohesive (single bounded context) and the Drizzle migration milestone (#37) will likely reshape it anyway, so no action needed now. Just flagging it so it doesn't drift further before that work lands.db: Database = getDb()trailing-optional pattern. Works fine and is consistent with how existing handler tests are structured. The alternative (constructor injection or a factory) would be a larger interface change — out of scope here and fine to leave as-is.createdBy?is optional on every service function. In the production path the handler passes the auth callback result; in tests it's often omitted. Worth confirming the DB column allows NULL forchanged_by(presumably it does, since the old code had the same optionality), but this is unchanged behaviour.handleUpsertDispatchposition in the handler file changed. Purely cosmetic — the routing table inmain.tsis untouched. No concern.Verdict
Clean, well-scoped refactor. The extraction is complete, the tests satisfy the AC, and the API surface is unchanged.
LGTM — approving.