refactor: dedupe Go handler/operation boilerplate (#42, #43, #46, #47, #48) #220

Merged
dries merged 5 commits from fix-42 into main 2026-07-04 19:53:54 +02:00
Owner

Addresses the DUP-GO dedup issues, one commit each:

  • #42listViaAdapter[T] collapses the identical agents/commands/questions session handlers. Scope reduced from the issue: handleSessionPermissions has since grown auto-approve resurrection logic and handleSessionModels returns a pointer response, so both keep their own bodies.
  • #43marshalRequest centralises the 8x-repeated fmt.Errorf("marshal request: %w", err) wrapping in opencode/operations.go. The table-driven rewrite of the nine operations was skipped deliberately: they diverge in HTTP verb, retry, and cache-invalidation behaviour.
  • #46validateStateRequest extracts the shared validateID + resolvePlatformIDForState preamble from the seen/archive/pin handlers.
  • #47paginateSlice[T] + costDescLess dedupe the offset/limit clamping and cost-descending sort in populateSessionLog / populateProjectLog, with table-driven tests.
  • #48parseAbsDir extracts the required-and-absolute ?dir= validation from five GET handlers (git, worktree, project-upstream). The suggested resolveWorktreeRoot extraction is obsolete: those handlers now delegate through the hostsvc router.

Not included — already fixed on main:

  • #44 — both handleSessions and handleSessionsNotify already share fanOutSessions (internal/server/fanout.go). Issue can be closed as done.

Bonus fix (first commit): initOriginRepo (server) and initRepo (hostsvc/local) inherited os.Environ() for git subprocesses. When the test suite runs inside a git hook (pre-commit exports GIT_DIR/GIT_INDEX_FILE), their git init/remote/add/commit operated on the real repository instead of the temp dir — observed live as a stray init commit by t <t@e> landing on this branch during a commit. Reproduced red (hook-env test run corrupted the repo), green after switching to gitexec.CleanEnv() like every other git-spawning helper.

Verified: go vet ./..., full go test ./... (one unrelated flake: TestEnsureToolPathRecoversBinary races on PATH under full-suite parallelism; passes in isolation and on main), platform-branching check.

Addresses the DUP-GO dedup issues, one commit each: - **#42** — `listViaAdapter[T]` collapses the identical agents/commands/questions session handlers. Scope reduced from the issue: `handleSessionPermissions` has since grown auto-approve resurrection logic and `handleSessionModels` returns a pointer response, so both keep their own bodies. - **#43** — `marshalRequest` centralises the 8x-repeated `fmt.Errorf("marshal request: %w", err)` wrapping in `opencode/operations.go`. The table-driven rewrite of the nine operations was skipped deliberately: they diverge in HTTP verb, retry, and cache-invalidation behaviour. - **#46** — `validateStateRequest` extracts the shared validateID + resolvePlatformIDForState preamble from the seen/archive/pin handlers. - **#47** — `paginateSlice[T]` + `costDescLess` dedupe the offset/limit clamping and cost-descending sort in `populateSessionLog` / `populateProjectLog`, with table-driven tests. - **#48** — `parseAbsDir` extracts the required-and-absolute `?dir=` validation from five GET handlers (git, worktree, project-upstream). The suggested `resolveWorktreeRoot` extraction is obsolete: those handlers now delegate through the hostsvc router. **Not included — already fixed on main:** - **#44** — both `handleSessions` and `handleSessionsNotify` already share `fanOutSessions` (`internal/server/fanout.go`). Issue can be closed as done. **Bonus fix (first commit):** `initOriginRepo` (server) and `initRepo` (hostsvc/local) inherited `os.Environ()` for git subprocesses. When the test suite runs inside a git hook (pre-commit exports `GIT_DIR`/`GIT_INDEX_FILE`), their `git init/remote/add/commit` operated on the *real* repository instead of the temp dir — observed live as a stray `init` commit by `t <t@e>` landing on this branch during a commit. Reproduced red (hook-env test run corrupted the repo), green after switching to `gitexec.CleanEnv()` like every other git-spawning helper. Verified: `go vet ./...`, full `go test ./...` (one unrelated flake: `TestEnsureToolPathRecoversBinary` races on PATH under full-suite parallelism; passes in isolation and on main), platform-branching check.
initOriginRepo (server) and initRepo (hostsvc/local) inherited
os.Environ() for their git subprocesses. When the test suite runs
inside a git hook (pre-commit exports GIT_DIR / GIT_INDEX_FILE), those
vars override cmd.Dir and the helpers' git init/remote/add/commit
operate on the real repository instead of the t.TempDir() repo —
observed as a stray 'init' commit by 't <t@e>' landing on the branch
being committed. Use gitexec.CleanEnv() like every other git-spawning
test helper.
The agents, commands, and questions handlers were structurally
identical 11-line functions. Extract listViaAdapter[T] and reduce each
to a one-liner. Permissions and models keep their own bodies: the
former has auto-approve resurrection logic, the latter returns a
pointer response.

Closes #42
The fmt.Errorf("marshal request: %w", err) wrapping appeared
identically 8 times across the mutating operations. Centralise it in
one marshalRequest helper. The table-driven rewrite suggested in the
issue is skipped deliberately: the nine operations diverge in HTTP verb,
retry, and cache-invalidation behaviour, so a table would obscure more
than it saves.

Closes #43
handleSeenSession, handleArchiveSession, and handlePinSession repeated
the same validateID + resolvePlatformIDForState block. Extract
validateStateRequest and call it from all three.

Closes #46
populateSessionLog and populateProjectLog ended with identical
offset/limit clamping blocks and near-identical cost-descending sort
comparators. Extract paginateSlice[T] and costDescLess, with
table-driven tests for both.

Closes #47
refactor(server): extract parseAbsDir for the ?dir= query validation
All checks were successful
CI / Frontend (pull_request) Successful in 3m37s
CI / Backend (pull_request) Successful in 3m52s
CI / Build Desktop (macOS arm64) (pull_request) Successful in 1m21s
CI / Playwright E2E (pull_request) Successful in 7m24s
CI / Build (pull_request) Successful in 5m9s
CI / Semantic Tag (pull_request) Has been skipped
CI / Coverage Ratchet (pull_request) Successful in 5m37s
59ce9688b5
The required-and-absolute dir validation block was repeated in five
GET handlers across the git, worktree, and project-upstream files.
Extract parseAbsDir (which also applies normaliseDirParam so all sites
share one code path). The resolveWorktreeRoot extraction suggested in
the issue is obsolete: those handlers now delegate through the hostsvc
router and no longer duplicate ResolveRepoRoot error handling.

Closes #48

Coverage ratchet: pass

Suite Baseline This PR Δ
go 70.30% 70.30% +0.00
frontend 59.12% 59.12% +0.00

Tolerance: -0.1%. Baseline stored on gh-pages.

<!-- coverage-ratchet --> ### Coverage ratchet: ✅ pass | Suite | Baseline | This PR | Δ | | |---|---|---|---|---| | go | 70.30% | 70.30% | +0.00 | ✅ | | frontend | 59.12% | 59.12% | +0.00 | ✅ | _Tolerance: -0.1%. Baseline stored on `gh-pages`._
Merge remote-tracking branch 'origin/main' into fix-42
All checks were successful
CI / Frontend (pull_request) Successful in 4m27s
CI / Backend (pull_request) Successful in 5m35s
CI / Playwright E2E (pull_request) Successful in 7m53s
CI / Build Desktop (macOS arm64) (pull_request) Successful in 2m48s
CI / Build (pull_request) Successful in 4m17s
CI / Coverage Ratchet (pull_request) Successful in 6m45s
CI / Semantic Tag (pull_request) Has been skipped
b5d578e2b1
# Conflicts:
#	internal/hostsvc/local/host_test.go
#	internal/server/handlers_resolve_targets_test.go
dries force-pushed fix-42 from b5d578e2b1
All checks were successful
CI / Frontend (pull_request) Successful in 4m27s
CI / Backend (pull_request) Successful in 5m35s
CI / Playwright E2E (pull_request) Successful in 7m53s
CI / Build Desktop (macOS arm64) (pull_request) Successful in 2m48s
CI / Build (pull_request) Successful in 4m17s
CI / Coverage Ratchet (pull_request) Successful in 6m45s
CI / Semantic Tag (pull_request) Has been skipped
to d1114a9a65
Some checks failed
CI / Frontend (pull_request) Successful in 3m51s
CI / Backend (pull_request) Successful in 3m8s
CI / Build Desktop (macOS arm64) (pull_request) Successful in 4m48s
CI / Playwright E2E (pull_request) Successful in 8m5s
CI / Coverage Ratchet (pull_request) Successful in 7m13s
CI / Build (pull_request) Successful in 4m49s
CI / Semantic Tag (pull_request) Has been skipped
CI / Frontend (push) Successful in 3m20s
CI / Backend (push) Successful in 3m28s
CI / Build Desktop (macOS arm64) (push) Successful in 3m17s
CI / Build (push) Successful in 5m7s
CI / Playwright E2E (push) Successful in 7m38s
CI / Coverage Ratchet (push) Successful in 7m27s
CI / Semantic Tag (push) Successful in 11s
Release / Build Frontend (push) Successful in 1m55s
Release / Build darwin/amd64 (push) Has been cancelled
Release / Build linux/amd64 (push) Has been cancelled
Release / Build darwin/arm64 (push) Has been cancelled
Release / Build linux/arm64 (push) Has been cancelled
Release / Publish Release (push) Has been cancelled
Release / Build Desktop (macOS arm64) (push) Has been cancelled
2026-07-04 19:34:01 +02:00
Compare
dries merged commit d1114a9a65 into main 2026-07-04 19:53:54 +02:00
Sign in to join this conversation.
No description provided.