Skip to content

SCRUM-216 — Config: version history, one version, and diff

Plan ref: CFG-B6 (docs/11-admin-plane-plan.md). Stacked on SCRUM-215.

What changed (all viewer)

Route Returns
GET /namespaces/{ns}/versions?before=&limit= {"versions":[{version, schema_version, sha256, message, created_by, created_at}], "next_before": <int\|null>}, newest first; limit 50 default, 200 max
GET /namespaces/{ns}/versions/{v} the list fields + size (length of the canonical bytes) + document
GET /namespaces/{ns}/diff?from=<v\|draft>&to=<v\|draft> {"from":{ref,document}, "to":{ref,document}, "changes":[…]}

changes comes from the new internal/diff package: {"op":"replace","path":"/weapons/0/damage","from":5,"to":7}, add (to only), remove (from only). Objects are compared key by key in sorted order (output is deterministic); arrays index by index (a reorder shows as index replaces — the UI gets both full documents for a richer view); numbers compare by canonical value (1 = 1.0); a type change is one replace; paths are RFC 6901 (~0, ~1); the root is ""; [] when identical, never null.

Errors: bad limit/before/{v}/from/to → 400 validation_failed; unknown version → 404; unknown ns → 404.

How to verify

cd services/config
export CONFIG_TEST_DATABASE_URL='postgres://USER:PASS@127.0.0.1:5433/config_test?sslmode=disable'
go vet ./... && go test -race -count=1 ./...
go test -count=1 -v -run 'Diff|History' ./internal/diff/ ./internal/server/
Test Proves
diff.TestDiff nested replace, add/remove key, array grow/shrink/change, reorder as index replaces, object→array as one replace, 1 vs 1.0 equal, / and ~ escaped
diff.TestDiffIdenticalIsNeverNull, …OrderIsDeterministic, …RejectsInvalidJSON as named
server.TestVersionHistoryAndDiffThroughTheLiveServer three real versions: newest-first list; limit=2 → next_before=2; before=2 → v1 and null; single version has size + document; diff 1→3 and 3→draft; 404/400 cases; viewer can read

By hand (viewer token $T):

curl -s -H "Authorization: Bearer $T" "localhost:8080/api/admin/config/namespaces/ui.motd/diff?from=1&to=draft"

Results at time of writing

  • go vet, go test -race ./... against Postgres 16: pass (8 packages).

How it was built

DeepSeek run scoped (Landlock) to services/config (171 s, ~36k output tokens). Claude review: diff semantics read against the spec, suite re-run. No changes needed.