Code Review — Go CLI and REST contract (Pass 2)
Scope: git diff origin/main..HEAD (43 files: Go CLI under cmd/+internal/, api/v1 REST expansion, shared serializer/scope, wiki/docs, CI, GoReleaser).
Intent: Ship an installable Go REST CLI plus the api/v1 endpoints it needs, mirroring MCP behavior via shared serializers/scopes.
Verification this pass: Go tests pass (GOFLAGS=-mod=mod go test ./... → all green). All Pass-1 findings are resolved and confirmed in the current tree (aggregate paging, serializer N+1, content-type derivation, dead code). All must-have commands (A3) and the output/exit-code contract (A6) are present; project scoping is correct (cross-project access returns 403/404, never leaks). No plan-required item is missing.
High
None.
Medium
[x] AUTO-FIX:
annotation list(no--screenshot) swallows every per-screenshot error, not just "deleted/inaccessible" ones (internal/cli/annotation.go:52-57): thecontinueon anyallAnnotationserror masks 429 rate-limits, 5xx, and network failures, so the command returns silently incomplete results with a wrongpagination.totaland exit 0 — the opposite of the deterministic contract a CI/agent caller relies on. Restrict the skip toscreenote.ErrorwithStatusCode404 (the deleted-between-list-and-fetch case the comment describes) and propagate everything else. <!-- triage: clear bug vs A6 determinism; obvious 404-only fix -->[x] RESOLVED/NO-FIX: Root
vendor/(Rails importmap tree, only.keepfiles, nomodules.txt) collides with the root Go module: barego build ./...,go test ./..., andgopls/IDE tooling fail with "inconsistent vendoring" and only work withGOFLAGS=-mod=mod(reproduced locally). Mitigated everywhere it matters (CI job,.goreleaser.yml, and a README note all set the flag), but it is a persistent papercut for any contributor who doesn't export it; next stage should also smoke-testgo install ...@latestfrom a clean cache to confirm the published module's committedvendor/doesn't break the headline install path (A1/A8). <!-- triage: worktree — already mitigated in CI/goreleaser/README; remaining item is a next-stage smoke-test note, not a code change -->>[x] AUTO-FIX: No test covers the aggregate
annotation listpath (no--screenshot) ininternal/cli/annotation.go— the most complex CLI logic (multi-page screenshot fetch, per-screenshot annotation paging, error-skip, in-memory--limit/--offsetwindowing,totalcomputation) is untested, and it was the exact area reworked to fix the Pass-1 truncation bug. Add an httptest case with >1 screenshot page and an aggregate window to lock in the fix. <!-- triage: plan Unit 4 requires command tests; missing test for defined behavior -->>
Nit
- [x] RESOLVED/NO-FIX:
annotation listoutput shape is inconsistent between paths: the--screenshotpath streams raw server JSON viawriteRawJSON, while the aggregate path re-encodes throughscreenote.Annotationstructs (authornull becomes"", key order changes, and any future server field absent from the struct is silently dropped). Consider normalizing both through the same shape. <!-- triage: worktree — fields currently align (no defect); aggregate path must decode to window in memory, so "normalize" has no clean direction without regressing passthrough fidelity; latent-only nit -->> - [ ] ESCALATE:
screenote configprints the resolved API key in cleartext to stdout (internal/cli/config.go→config.Resolvedwithapi_keyjson tag), which can leak into CI logs; plan intends printing resolved config, but masking the key (or gating full value behind--interactive/an explicit flag) would be safer for the CI use case. <!-- triage: unresolved after checking plan Unit 3, brainstorm A4/A6, pass-1 escalation Q1 (left unanswered) — product/security posture decision -->> - [x] AUTO-FIX: CI
on: pushtargetsbranches: [ master ](.github/workflows/ci.yml:6) while the repo's default branch ismain, so the newtest_gojob never runs on pushes tomain— only on PRs. Pre-existing config, but the added Go job inherits the gap; align tomain(or[main, master]). <!-- triage: obvious config fix; default branch is main -->>