From 0ff155ae175906dbebf30fa57a71c0935280ad62 Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Sat, 25 Apr 2026 23:48:27 +0300 Subject: [PATCH] docs(task): record execution conclusion across all 9 phases --- docs/tasks/lethe-server.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/tasks/lethe-server.md b/docs/tasks/lethe-server.md index 4c50b60ec37ace359069ed431eb8db55c32cb5b4..a132e82428935d9f4e193056a234b6b511695649 100644 --- a/docs/tasks/lethe-server.md +++ b/docs/tasks/lethe-server.md @@ -1,6 +1,6 @@ # lethe-server -**Status:** Plan +**Status:** Execute (verify pending) **Module:** `sourcecraft.dev/bigbes/lethe` **Branch:** master **Worktree:** none @@ -410,5 +410,15 @@ Greenfield — no compat surface. Wire format is the only forward-compat concern - Each phase from 2 onward must remove the dep it adopts from `internal/deps/deps.go`; Phase 9 deletes the file. - README's Caddy/Authelia snippets use `auth.example.com` placeholders; replace with phoebe-specific values when the production deploy lands (out of scope for this task). - **Phase 4 finding (steward unwind gap)**: `steward.Manager.Init` returns on the first failing `CallInit` and does **not** iterate back over previously-initialized assets to call `Destroy`. The canary test `TestStewardUnwindsOnInitFailure` (in `internal/platform/health/steward_unwind_test.go`) is intentionally red on master to document this. **Phase 9 main must compensate**: track each component as it init's and, on `Init` error, walk the list in reverse calling `Destroy` directly on each (don't try `mgr.Stop`/`mgr.Destroy` — those panic unless the manager has reached Started). Once Phase 9 lands the explicit unwind, either delete the canary test or convert it to assert the new compensating behavior. -- **Phase 5 consistency fix (folded into commit `1b215bc` via amend)**: chi's default 404/405 handlers wrote text/plain, violating the invariant "errors leaving any HTTP handler are rendered as RFC 7807". Added explicit `chi.Router.NotFound`/`MethodNotAllowed` handlers that call `apierror.Render` with `NOT_FOUND` / `METHOD_NOT_ALLOWED` codes. Added `METHOD_NOT_ALLOWED → 405` entry to the apierror code-status map. Added two regression tests (`TestRouter_NotFoundReturnsProblemJSON`, `TestRouter_MethodNotAllowedReturnsProblemJSON`). +- **Phase 5 consistency fix (folded into commit `3c45b48` via amend)**: chi's default 404/405 handlers wrote text/plain, violating the invariant "errors leaving any HTTP handler are rendered as RFC 7807". Added explicit `chi.Router.NotFound`/`MethodNotAllowed` handlers that call `apierror.Render` with `NOT_FOUND` / `METHOD_NOT_ALLOWED` codes. Added `METHOD_NOT_ALLOWED → 405` entry to the apierror code-status map. Added two regression tests. +- **Phase 7**: added `UNSUPPORTED_MEDIA_TYPE → 415` entry to `apierror.codeStatus` (ingest handler enforces `application/x-ndjson` Content-Type). Repository simulates DB-down by closing the underlying `*sql.DB` (cleaner than service-faking, mirrors real driver-disconnect failure). Service-level FK test omitted because the schema makes it unreachable through the Service path (parent session is upserted in the same chunk); equivalent Repository-level test pins the wrap-and-classify code path. +- **Phase 8**: introduced `JSONText` (sql.Scanner wrapper) for nullable TEXT-JSON columns — `json.RawMessage` cannot Scan NULL directly. External JSON shape unchanged. If Phase #3 (search) wants the same scan-safety, factor up to `internal/pkg/sqljson`. +- **Phase 9**: refactored `Server.Start` to `net.Listen` first then `http.Serve(listener)` plus added `Server.Addr()` so `:0` binds report the kernel-assigned port — enables the e2e smoke to bind to a random port without races. `cmd/lethe/main.go` uses a `run() int` shell so tests can drive it. Steward unwind canary `internal/platform/health/steward_unwind_test.go` deleted; `main.go`'s reverse-order `unwindOnError` compensator is now the production guarantee. Bootstrap stderr slog handler installed before any asset registration so the unwind path always has a logger. + +### Final state + +- All 9 phases committed (`4ca03be` → `53221c9`). +- `go test ./... -race -count=1` fully green; no allowed-red exception. +- `go vet`, `gofmt -l`, `go mod tidy`, `golangci-lint run ./...` all clean. +- Manual smoke: lethe binary built and ran against `config.example.yaml`; `/healthz`, `/readyz`, `/metrics`, unauthed `/api/v1/sessions` (401), authed `/api/v1/sessions` with `Remote-User: bigbes` (200) all behaved as designed; SIGTERM triggered clean shutdown via the steward graph. - **Phase 3 → Phase 7 contract pin**: `INSERT … ON CONFLICT … DO UPDATE` fires the UPDATE trigger on SQLite (verified by `TestUpsertFiresUpdateTriggerAndKeepsFTSCoherent`). Regular FTS5 (not contentless / external content) was chosen so `WHERE owner = ?` works on the FTS table without a join — accepted the storage cost (`content` duplicated in real table + FTS shadow). Composite key order is `(owner, tool, host, session_id[, turn_id])` everywhere; ingest INSERT/UPDATE/ON CONFLICT clauses must match. `started_at`/`ended_at`/`source_file` are `NOT NULL` — ingest derives `started_at` from `MIN(turn.timestamp)` when `SessionMeta.StartedAt` is absent.