phase 2 review: fix SSE deadlock, MCP panic, lifecycle 500, NOT NULL bug
Reviewed the phase-2 implementation (parts 2–5) end to end. The suite hung
for 600s and several handlers were never exercised because there were no
tests for the new mutation/event/MCP surface. Fixes:
- CRITICAL: sseListener ran on context.Background() and held a pooled
connection forever, so pool.Close() deadlocked (600s test timeout).
NewHandler now takes a ctx that governs the listener; ListenAndServe and
the test helper cancel it before closing the pool.
- CRITICAL: MCP AddTool panicked ("missing input schema") at construction
under go-sdk v1.6.1 — so NewHandler (and every API handler) panicked.
Added object input schemas to all 8 tools via an objSchema helper.
- HIGH: PatchEntity parsed lifecycle transitions as map[string][]string but
the shape is {from:{to:{requires:[]}}}, so every state-change PATCH 500'd.
Parse the nested shape; allow same-state no-ops.
- HIGH: CreateEntity bound SQL NULL for attributes when omitted, violating
the NOT NULL column (the default only applies when omitted). Default to
'{}'.
- MED: serveSSEWriter ignored the request ctx (per-client goroutine leak on
disconnect) and set an invalid Content-Length: -1. Thread ctx through;
omit the header. writeSSE now nil-checks the flusher (io.Pipe path passed
nil → would have panicked on first event).
- MED: SSE `data:` leaked raw sqlcgen.Event (PascalCase, base64 JSONB).
Emit canonical gen.Event so SSE matches GET /events. Verified live.
- LOW: CreateEntity uses uuid.NewV7 (ADR-0005) + real actor from context in
audit; removed dead bearerAuth; fixed vet unkeyed-field warnings.
Tests (would have caught all of the above): entity create/patch with
If-Match 409/400, valid+invalid lifecycle transitions, idempotency replay,
duplicate-slug 409, abstract-type 422, event+audit side effects, MCP tool
registration. Live smoke test confirmed NOTIFY→listener→SSE delivery.
Also adds the missing Phase 2 deliverable: Gitea Actions CI (vet,
golangci-lint, govulncheck, generated-code drift guard, race tests against
TimescaleDB, docker build) and wires sqlc into `make generate`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
10
Makefile
10
Makefile
@@ -1,4 +1,4 @@
|
||||
.PHONY: build test lint generate dev migrate seed export clean
|
||||
.PHONY: build test test-db lint generate generate-check dev migrate seed export clean tidy
|
||||
|
||||
BINARY := oikos
|
||||
GO ?= go
|
||||
@@ -14,7 +14,7 @@ test-db:
|
||||
docker compose up -d postgres
|
||||
@sleep 3
|
||||
OIKOS_TEST_DATABASE_URL="postgres://oikos:$${OIKOS_DB_PASSWORD:-oikos_dev}@localhost:5432/oikos?sslmode=disable" \
|
||||
$(GO) test -race -count=1 ./internal/db/ ./internal/httpapi/
|
||||
$(GO) test -race -count=1 ./internal/db/ ./internal/httpapi/ ./internal/mcp/
|
||||
|
||||
lint:
|
||||
$(GO) vet ./...
|
||||
@@ -23,6 +23,12 @@ lint:
|
||||
generate:
|
||||
$(GO) run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.4.1 \
|
||||
-config api/codegen.yaml api/openapi.yaml
|
||||
$(GO) run github.com/sqlc-dev/sqlc/cmd/sqlc@v1.29.0 generate
|
||||
|
||||
# CI drift guard: regenerate and fail if the committed output changed.
|
||||
generate-check: generate
|
||||
@git diff --exit-code -- internal/httpapi/gen internal/db/sqlcgen \
|
||||
|| (echo "generated code is stale — run 'make generate' and commit" && exit 1)
|
||||
|
||||
migrate:
|
||||
$(GO) run ./cmd/oikos migrate
|
||||
|
||||
Reference in New Issue
Block a user