Skip to Content

← All archived runs

Run: db-findings-intake

run.md

Run: db-findings-intake

  • branch: claude/db-findings-intake
  • pr: #490

01_define/output/spec.md

Spec: Database audit findings as pipeline intake stubs

  • slug: db-findings-intake
  • personas: admin
  • touches: .claude/skills/db-auditor (triage logic + skill doc), packages/services/scripts/db-audit.ts (machine-readable findings export), pipeline/intake/db-audit-findings/ (new intake epic)
  • complexity: standard

Problem

The honest database audit (PR #489) now reliably surfaces leanness findings — unused/redundant indexes, missing tenant indexes, oversized documents, unbounded arrays, orphaned references — but nothing turns those findings into action. They land in a CI job summary and stop there; acting on them is ad-hoc and easily forgotten. Meanwhile the pipeline already has a clean way to capture and sequence work the team picks up: intake stubs in pipeline/intake/, spun out one-per-run with /pipeline new. Database-change requirements should flow the same way, so the admin / data-quality guardrails behind the "Refine the bridge" initiative convert detected bloat into shipped migrations instead of warnings nobody owns.

Proposed change

Make audit findings first-class pipeline intake, picked up exactly like features:

  • Add a dedicated intake epic folder pipeline/intake/db-audit-findings/ — the home for DB-change requirements, sitting beside the feature-stub epics. It holds a breakdown.md (current findings, sequenced) and one stub .md per actionable finding, in the standard intake stub format, so /pipeline new <stub> pre-seeds Define's spec with no special handling.
  • Extend the db-auditor skill into a triage pass (mirroring decompose): it takes the audit findings, interprets/triages them, and writes/refreshes the db-audit-findings stubs. Each finding maps to one independently-shippable stub (e.g. "drop redundant index tenantId_1_status_1 on leads") whose Build writes a single migration.
  • Give each stub a stable slug derived from the finding (kind + collection + index/field) so re-running the triage updates a finding in place rather than duplicating it, and skips findings already spun out (stub in _done/) or no longer reported (resolved) — a self-pruning breakdown.
  • Adopt the manually-seeded folder from PR #491. #491 already established pipeline/intake/db-audit-findings/ by hand — a breakdown.md (full 110-finding backlog) and the first stub (add-invoices-tenant-index.md). The triage writes into that same folder and is idempotent against the hand-written stubs: it matches on finding identity (kind + collection + index/field), so an already-captured finding (e.g. the invoices missing-tenant-index) is refreshed in place, never duplicated, and its existing filename is preserved.
  • So the triage can run where the DB is unreachable (the agent sandbox blocks TCP 27017), it consumes a machine-readable findings report: the audit script gains a JSON export (written to a path / surfaced by the nightly audit run) that the skill reads, decoupling stub generation from live DB access.

The audit's CI behaviour is unchanged (still honest, still advisory exit 0). This pass adds the detection → intake bridge; each spun-out run carries the actual fix through Define → Build → Release on the gated migration pipeline.

Acceptance criteria

  • pipeline/intake/db-audit-findings/ exists as an intake epic with a breakdown.md in the standard breakdown format.
  • Running the db-auditor triage writes one stub per actionable finding into that folder, each in the intake stub format (fields map onto Define's spec.md), persona admin, carrying the initiative/objective link.
  • Each stub is independently shippable as one run / one PR (its Build = a single migration) and includes a touches: hint.
  • Stub slugs are stable per finding (kind + collection + index/field); re-running the triage refreshes in place — no duplicate stubs for the same finding.
  • The triage skips findings already spun out (_done/) and prunes findings no longer present in the latest audit, so the breakdown reflects current reality.
  • The triage writes into the pipeline/intake/db-audit-findings/ folder seeded by PR #491 and is idempotent against its hand-written stubs — the existing add-invoices-tenant-index.md (and any other manual stub) is refreshed in place by finding identity, never duplicated.
  • The triage runs without live DB access by consuming a machine-readable audit findings report; the audit script can emit findings as JSON for that purpose.
  • The triage is read-only against the database — its only writes are the local intake files.
  • /pipeline new pipeline/intake/db-audit-findings/<finding-slug>.md spins a finding out exactly like a feature stub — no /pipeline router changes required.
  • The db-auditor skill doc describes the triage pass and the db-audit-findings epic.

Out of scope

  • Writing the actual migrations / dropping indexes — that is each spun-out run's Build, behind the gated preview/production migration pipeline from #489.
  • Auto-committing stubs from CI — we chose the human/agent-invoked triage pass; the nightly audit only needs to expose findings, not generate stubs.
  • Failing CI on findings, or changing the audit's advisory exit-0 behaviour.
  • A new /pipeline subcommand — the existing db-auditor skill plus /pipeline new already cover it.
  • The honest audit itself (PR #489) — this feature depends on it for trustworthy findings and is only meaningful once #489 is merged, but does not re-do that work.
  • The manual capture in PR #491 — that PR is the seed (the db-audit-findings folder + backlog + first stub), merged separately. This feature automates maintaining that folder; it does not re-create or discard #491's stubs.

Open questions

  • none. (Findings source is resolved: the triage consumes the audit's machine-readable report so it works without DB access; a live db:audit is used only where the DB is reachable.)

02_build/output/notes.md

Build notes: db-findings-intake

  • commits:
    • feat: db-findings-intake — machine-readable audit report + structured finding target
    • feat: db-findings-intake — db-auditor triage pass maintains db-audit-findings intake

What changed

  • packages/services/src/db/audit/index.ts: added a discrete target field to AuditFinding (the index name / field a finding is about, or "" for collection-wide) and populated it at all six push sites. This gives every finding a stable machine-readable identity (kind + collection + target) without parsing the prose message — the basis for stable stub slugs.
  • packages/services/scripts/db-audit.ts: added a JSON export. db:audit --json <path> (or --json=<path> / DB_AUDIT_JSON_PATH) writes the full AuditReport to a file. It's emitted before the exit-code branch, so the triage gets the complete picture (inspected, skipped, findings) even on an incomplete audit. The honest exit-code behaviour is unchanged.
  • .claude/skills/db-auditor/SKILL.md: extended the skill (v2) with the triage pass — mirrors /pipeline decompose. Documents: getting a findings report without a live DB (the JSON export); the db-audit-findings epic layout (breakdown.md + per-finding stubs + _done/); the stable finding-key identity (<kind>/<collection>/<target>) and slug derivation; idempotency, _done/ skipping and pruning; "what is actionable" (follow the breakdown's prioritisation, don't batch-generate); read-only-against-DB guarantee; and the /pipeline new hand-off (no router change).
  • pipeline/intake/db-audit-findings/add-invoices-tenant-index.md: added a - finding-key: missing-tenant-index/invoices/tenantId line so the triage matches #491's hand-written stub by identity and refreshes it in place — preserving its human-friendly filename, never duplicating it.
  • pipeline/intake/db-audit-findings/breakdown.md: recorded the stub's finding-key and added a "Triage maintenance" section describing how the db-auditor triage maintains the backlog.

Acceptance criteria status

  • pipeline/intake/db-audit-findings/ exists with a breakdown.md in the standard format — seeded by #491 (merged into this branch); the triage adopts and maintains it.
  • The triage writes one stub per actionable finding in intake stub format, persona admin, carrying the initiative/objective link — documented as the triage pass; the seed stub demonstrates the format.
  • Each stub is independently shippable (one run / one migration) and includes a touches: hint — enforced by the stub format + the triage's "independently shippable" rule.
  • Stub slugs are stable per finding (kind + collection + target) and refresh in place — the finding-key is the match key; target is now a discrete field, so the identity is derivable without parsing prose.
  • The triage skips findings in _done/ and prunes findings no longer reported — specified in the idempotency/pruning procedure.
  • The triage writes into the #491-seeded folder and is idempotent against its hand-written stubs — add-invoices-tenant-index.md now carries its finding-key and is matched by identity, not filename.
  • The triage runs without live DB access via a machine-readable report — db:audit --json <path> emits the AuditReport JSON the triage consumes.
  • The triage is read-only against the database — its only writes are the local intake files; stated in the skill front-matter, the pass intro, and a guardrail.
  • /pipeline new pipeline/intake/db-audit-findings/<finding-slug>.md spins a finding out like a feature stub — stubs use the standard intake format, so no /pipeline router change is needed.
  • The db-auditor skill doc describes the triage pass and the db-audit-findings epic.

Verify result

  • Mechanical checks (format · lint · typecheck · build) run in CI + the Vercel preview, not here. Release reads them back from the PR's check runs. The target field is required on AuditFinding and is set at all six push sites + the type, so the typecheck should stay green.

Notes for review

  • The triage is an agent-driven skill procedure, not a new script — matching decompose. The only runtime code change is the JSON export + the target field; the rest is the skill contract and the intake folder it maintains. This keeps the surface minimal (no new abstraction) per CONVENTIONS.
  • Stubs are not batch-generated for all 110 findings this round — by design. The breakdown's prioritisation (impact ÷ risk) governs what spins out; #491 already cut #1 (invoices). The triage makes that maintenance repeatable and idempotent, not exhaustive.
  • The sandbox can't reach MongoDB, so the JSON export path couldn't be exercised live here; the shape written is exactly the AuditReport returned by auditDatabase().

03_release/output/release.md

Release: db-findings-intake

  • pr: #490 · merged: pending (Ready to merge ticked, CI green)
  • CI: green — Format, Lint, Typecheck, Audit database, Migrate preview all success; Migrate production correctly skipped on the PR
  • technical docs: apps/docs/app/technical/development/database/page.mdx — documented the db:audit --json export, the discrete finding target, and the triage → pipeline/intake/db-audit-findings/ bridge. No further ci-cd impact (the audit/migrate CI workflows are unchanged).
  • business docs: no business docs impact — internal tooling/process, no user-facing behaviour change (no feature-role-matrix / service-journey / platform-overview change).
  • release notes: none — internal change. No end-user behaviour changed (no changelog entry); investor angle was already covered by #489's honest-audit + gated-migrations announcement, so no investor email (user decision).
  • deploy: pending production deploy verification after merge (docs app + help).
  • sent: none — internal change, no investor update.

Review summary

  • packages/services/src/db/audit/index.tstarget added to AuditFinding and populated at all six push sites; required field, set everywhere, so the type stays sound. Internal-only, no behaviour change to the existing report.
  • packages/services/scripts/db-audit.ts — JSON export resolves the path from --json <path> / --json=<path> / DB_AUDIT_JSON_PATH, writes the full AuditReport, emitted before the exit-code branch so the report is complete even on an incomplete audit. Honest exit codes unchanged. Exercised indirectly: the Migrate-preview CI job ran the updated package against the preview DB and succeeded.
  • .claude/skills/db-auditor/SKILL.md, pipeline/intake/db-audit-findings/** — skill + intake docs; no runtime surface.
  • No new abstraction; change is minimal and matches CONVENTIONS (arrow functions, type, named imports).

Acceptance check (vs spec)

  • pipeline/intake/db-audit-findings/ exists with breakdown.md — seeded by #491, adopted here.
  • Triage writes one stub per actionable finding (intake format, persona admin, initiative link) — documented triage pass; seed stub demonstrates the format.
  • Each stub independently shippable + touches: hint — enforced by the stub format and the triage rules.
  • Stable per-finding slugs, refresh in place — finding-key = <kind>/<collection>/<target>; target now discrete.
  • Skips _done/, prunes resolved findings — specified in the idempotency/pruning procedure.
  • Idempotent against #491's hand-written stub — add-invoices-tenant-index.md tagged with its finding-key, matched by identity not filename.
  • Runs without live DB via machine-readable report — db:audit --json <path> emits the AuditReport.
  • Read-only against the DB — only writes are local intake files.
  • /pipeline new <stub> spins out like a feature — standard intake format, no router change.
  • Skill doc describes the triage pass + the db-audit-findings epic.