homelabby/.planning/phases/06-lab-advisor/06-01-SUMMARY.md
Mikkel Georgsen 8237077728 docs(06-01): complete PostgreSQL store package plan
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 07:31:59 +00:00

4.6 KiB

phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
06-lab-advisor 01 store
postgresql
pgx
persistence
store
migrations
requires provides affects
internal/store
06-02
06-03
added patterns
github.com/jackc/pgx/v5 v5.9.1
github.com/jackc/puddle/v2 v2.2.2
github.com/jackc/pgpassfile v1.0.0
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761
pgxpool for connection pooling (not single conn)
Parameterized $N queries throughout (no string interpolation)
ErrNotFound sentinel for missing resources
Integration tests gated behind `//go:build integration` tag
created modified
internal/store/store.go
internal/store/migrations.go
internal/store/conversations.go
internal/store/store_test.go
go.mod
go.sum
Used dot-import in external test package to avoid verbose `store.NewStore` prefix while keeping package boundary clean
Used LEFT JOIN in GetConversation to handle conversations with zero messages, scanning nullable message columns
Added LIMIT 100 to ListConversations per T-06-01-04 (DoS guard for unbounded growth)
DSN never logged on connection error — only error text forwarded (T-06-01-02)
duration_seconds completed_at tasks_completed tasks_total files_created files_modified
124 2026-04-10T07:31:15Z 2 2 4 2

Phase 06 Plan 01: PostgreSQL Store Package Summary

One-liner: pgx/v5 connection pool with idempotent schema migrations and typed CRUD for conversations + messages, backed by live PostgreSQL at 10.5.0.109.

Tasks Completed

# Name Commit Files
1 Add pgx/v5 dep and create Store with RunMigrations 4bc22dc store.go, migrations.go, go.mod, go.sum
2 Conversation and message CRUD methods 623cff0 conversations.go, store_test.go

What Was Built

internal/store/store.go

Store struct wrapping *pgxpool.Pool. NewStore(ctx, dsn) opens the pool and pings before returning. Close() drains the pool. Pool() accessor gives migrations and tests direct pool access.

internal/store/migrations.go

RunMigrations(ctx, pool) executes two CREATE TABLE IF NOT EXISTS statements in dependency order (conversations first, then messages which references it via FK). Safe to call on every startup.

Schema:

  • conversations(id UUID PK DEFAULT gen_random_uuid(), started_at TIMESTAMPTZ DEFAULT now(), model TEXT DEFAULT '')
  • messages(id UUID PK, conversation_id UUID FK REFERENCES conversations ON DELETE CASCADE, role TEXT CHECK IN ('user','assistant','system'), content TEXT, created_at TIMESTAMPTZ DEFAULT now())

internal/store/conversations.go

Types: Message, Conversation, ConversationSummary, ErrNotFound.

Methods on *Store:

  • CreateConversation(ctx, model) (id string, err error)
  • AddMessage(ctx, conversationID, role, content) (id string, err error) — DB CHECK constraint rejects invalid roles
  • GetConversation(ctx, id) (*Conversation, error) — LEFT JOIN to handle zero-message conversations; returns ErrNotFound on miss
  • ListConversations(ctx) ([]ConversationSummary, error) — COUNT(m.id) aggregation with LIMIT 100 soft guard

internal/store/store_test.go

Integration tests (build tag: integration) covering all behaviors from the plan spec. Each sub-test creates its own fixtures and cleans up via deferred DELETE. Tests connect to the real PostgreSQL instance.

Deviations from Plan

None — plan executed exactly as written. The dot-import (import . "git.georgsen.dk/hwlab/internal/store") in the test file is a minor stylistic choice for readability in external test packages, consistent with common Go testing practice for the same package's integration tests.

Threat Mitigations Applied

Threat ID Mitigation
T-06-01-01 All queries use $1/$2 parameterized args via pgx; zero string interpolation in SQL
T-06-01-02 NewStore wraps error without including DSN; only pgx error text is forwarded
T-06-01-03 DB-level CHECK (role IN ('user','assistant','system')) — verified by test
T-06-01-04 LIMIT 100 added to ListConversations

Known Stubs

None — all methods are fully wired to the live database.

Self-Check: PASSED

  • internal/store/store.go: exists
  • internal/store/migrations.go: exists
  • internal/store/conversations.go: exists
  • internal/store/store_test.go: exists
  • Commit 4bc22dc: verified in git log
  • Commit 623cff0: verified in git log
  • go build ./...: PASSED
  • Integration tests: 12/12 PASSED