feat: sessions service adapter — REST CRUD for sessions/milestones/episodes #50

Closed
opened 2026-09-27 14:40:39 +00:00 by rock · 1 comment
Owner

Goal

Add session management routes to api-gw. Internal only (poimen-frontend consumer). No OpenAPI spec required — shapes documented in POIMEN.md.

Routes

POST   /v1/sessions                   create session
GET    /v1/sessions/:id               get session
PATCH  /v1/sessions/:id               update (rename, set goal, archive)
GET    /v1/sessions                   list sessions (status, cwd filter)
POST   /v1/sessions/:id/milestones    create milestone
GET    /v1/sessions/:id/episodes      list episodes
GET    /v1/sessions/:id/episodes/:eid get episode + artifact_url

Implementation

  • New SessionAdapter in internal/serviceadapter/
  • Reads/writes PostgreSQL poimen-sessions DB in poimen namespace
  • Uses existing pgx driver pattern from other adapters
  • Auth: same JWT validation as all other routes

Notes

  • Episode artifact_url resolves to MinIO presigned URL
  • Summaries inline, full artifacts in MinIO
  • episode_id is sha256(session_id+milestone_id+attempt) — accept on write, return on read
## Goal Add session management routes to api-gw. Internal only (poimen-frontend consumer). No OpenAPI spec required — shapes documented in POIMEN.md. ## Routes ``` POST /v1/sessions create session GET /v1/sessions/:id get session PATCH /v1/sessions/:id update (rename, set goal, archive) GET /v1/sessions list sessions (status, cwd filter) POST /v1/sessions/:id/milestones create milestone GET /v1/sessions/:id/episodes list episodes GET /v1/sessions/:id/episodes/:eid get episode + artifact_url ``` ## Implementation - New `SessionAdapter` in `internal/serviceadapter/` - Reads/writes PostgreSQL `poimen-sessions` DB in poimen namespace - Uses existing `pgx` driver pattern from other adapters - Auth: same JWT validation as all other routes ## Notes - Episode `artifact_url` resolves to MinIO presigned URL - Summaries inline, full artifacts in MinIO - `episode_id` is sha256(session_id+milestone_id+attempt) — accept on write, return on read
Author
Owner

Harness cycle complete — PR #51

Routes: backend (coder-impl) → testing (coder-tests) → review×2 → fixes → gate ✅

Files changed

File Change
internal/sessions/handler.go NEW — Handler with 7 endpoints, ingest() + gql() helpers, EpisodeID
internal/serviceadapter/session_adapter.go NEW — NewSessionAdapter() + GetSessionSpec()
internal/sessions/handler_test.go NEW — 30 tests, all pass
cmd/gateway/main.go +3 lines — register NewSessionAdapter()
internal/serviceadapter/adapters.go +3 lines — sessions factory case

Route mapping

X-Service: sessions
X-Resource: sessions   POST  → create session      (?session_id auto-generated)
X-Resource: sessions   GET   → list sessions        (?status=, ?cwd=, ?limit≤200)
X-Resource: session    GET   → get session          (?session_id=)
X-Resource: session    PATCH → update session       (?session_id=)
X-Resource: milestones POST  → create milestone     (?session_id=)
X-Resource: episodes   GET   → list episodes        (?session_id=)
X-Resource: episode    GET   → get episode+artifact (?session_id=, ?episode_id=)

Key decisions

  • episode_id = sha256(session_id+milestone_id+attempt) — §4.14 Q6
  • Content JSON-encoded — injection-safe for user fields
  • tracing.NewTransport wired — traceparent propagated to poimen-memory + poimen-graphql
  • GraphQL errors array detected, surfaced as 502
  • limit capped at 200 on list endpoints

Reviewer verdicts (post-fix)

  • reviewer-quality: SIGN-OFF
  • reviewer-sre: SIGN-OFF

PR: #51

## Harness cycle complete — PR #51 **Routes:** backend (coder-impl) → testing (coder-tests) → review×2 → fixes → gate ✅ ### Files changed | File | Change | |---|---| | `internal/sessions/handler.go` | NEW — Handler with 7 endpoints, ingest() + gql() helpers, EpisodeID | | `internal/serviceadapter/session_adapter.go` | NEW — NewSessionAdapter() + GetSessionSpec() | | `internal/sessions/handler_test.go` | NEW — 30 tests, all pass | | `cmd/gateway/main.go` | +3 lines — register NewSessionAdapter() | | `internal/serviceadapter/adapters.go` | +3 lines — sessions factory case | ### Route mapping ``` X-Service: sessions X-Resource: sessions POST → create session (?session_id auto-generated) X-Resource: sessions GET → list sessions (?status=, ?cwd=, ?limit≤200) X-Resource: session GET → get session (?session_id=) X-Resource: session PATCH → update session (?session_id=) X-Resource: milestones POST → create milestone (?session_id=) X-Resource: episodes GET → list episodes (?session_id=) X-Resource: episode GET → get episode+artifact (?session_id=, ?episode_id=) ``` ### Key decisions - `episode_id = sha256(session_id+milestone_id+attempt)` — §4.14 Q6 - Content JSON-encoded — injection-safe for user fields - `tracing.NewTransport` wired — traceparent propagated to poimen-memory + poimen-graphql - GraphQL `errors` array detected, surfaced as 502 - limit capped at 200 on list endpoints ### Reviewer verdicts (post-fix) - **reviewer-quality**: SIGN-OFF - **reviewer-sre**: SIGN-OFF **PR:** https://forgejo.riotpiao.com/riotpiao-poimen/homelab-frontend/pulls/51
rock closed this issue 2026-09-28 01:53:24 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: riotpiao-poimen/homelab-frontend#50