Audit Log Ordering Implementation Plan
Audit Log Ordering Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Return audit logs newest first with deterministic pagination.
Architecture: Ordering belongs in the audit repository because audit IDs are random and the existing (created_at, id) index already defines the intended stable key. The repository returns an opaque composite (created_at, id) cursor and resolves legacy ID-only cursors by looking up the referenced row’s creation time.
Tech Stack: Go, SQL, SQLite/MySQL, Vitest-compatible repository contract tests.
Spec: docs/superpowers/specs/2026-09-09-token-scope-route-edit-audit-design.md
Global Constraints
- Audit list order is
created_at DESC, id DESC. - The same timestamp must produce a deterministic order.
- Cursor values remain opaque to API clients.
- Existing ID-only cursors must not strand a user mid-page after upgrade.
- No database schema change is required because
idx_audit_created(created_at, id)already exists.
Task 1: Repository ordering and stable cursor
Files:
- Test:
internal/storage/audit_repository_test.go - Modify:
internal/storage/repository.go
Interfaces:
- Consumes:
AuditRepository.List(ctx context.Context, cursor string, limit int) (Page[AuditLog], error). - Produces:
encodeAuditCursor(createdAt time.Time, id string) string. -
Produces:
(*auditRepo).decodeAuditCursor(ctx context.Context, cursor string) (createdAt string, id string, err error). -
Step 1: Write the failing repository test.
TestAuditRepositoryListsNewestFirstWithStablePaginationcreates four records whose ID lexicographic order differs from creation-time order. It asserts:page 1 = [newest, newer tie with higher ID] page 2 = [newer tie with lower ID, oldest] legacy ID cursor after the first tie = [lower-ID tie, oldest] -
Step 2: Verify the red phase.
go test ./internal/storage -run TestAuditRepositoryListsNewestFirstWithStablePagination -count=1Actual failure before the fix:
audit IDs = [a-audit-newest b-audit-tie], want [a-audit-newest m-audit-tie] -
Step 3: Implement keyset ordering.
Encode cursors as
created_at + NUL + id, filter with(created_at < cursor_time OR (created_at = cursor_time AND id < cursor_id)), and order bycreated_at DESC, id DESC. For a legacy cursor without NUL, loadcreated_atby audit ID before applying the same keyset filter. -
Step 4: Verify the focused test.
go test ./internal/storage -run TestAuditRepositoryListsNewestFirstWithStablePagination -count=1Expected result: PASS.
Task 2: Contract documentation and verification
Files:
- Modify:
docs/api/openapi.yaml - Modify:
docs/user-guide/server-admin.md - Modify:
docs/pull-requests/2026-09-09-token-scope-route-edit-audit.md
Interfaces:
- Consumes:
GET /api/v1/audit-logs?cursor=&limit=. -
Produces: documented newest-first order and opaque-cursor behavior.
-
Step 1: Document API order.
Add
summary: List audit logs newest firstand describecreatedAt DESC, id DESC, opaque cursors, and legacy-cursor compatibility. -
Step 2: Document the management UI behavior.
Explain in the Server guide that Audit Logs displays newest events first and uses a deterministic ID tie-breaker.
-
Step 3: Run Go verification.
go test ./... -count=1 go test -race ./... go vet ./... git diff --checkExpected result: all commands pass.
Completion Record
This is a retroactive plan for a bug fix completed in this session. The red-phase output above is the actual observed failure; no earlier red run is claimed. No commit, push, merge, or remote PR was performed.