Postmortem — Backend /assets consumers fail in native local dev
What we asked for
Fix the Docker-only hard-coded /assets path so native local dev works: a single ASSETS_PATH env var defaulting to /assets, all consumers resolving through it, dev scripts setting it — delivered as a PR that is ready to merge.
What /do delivered vs intended
The functional change was correct and fully verified (all four ACs plus both prevention tests, confirmed by two review passes), but the PR arrived with a failing Lint CI check: golangci-lint's gci rule flagged pkg/sendgrid/template.go:11 and services/pdf.go:9 — the new pkg/env import was placed in the third-party import group instead of the required local-prefix group. A PR that fails CI is not "done"; the human found the failure on review.
Why the gap happened
Skill/agent gap: no pipeline stage owns "run the project's own quality gates before the PR." The defect entered at implement time (Codex placed the import in the wrong group — something gofmt, go build, and go test all accept) and then passed every subsequent stage because no stage runs the repo's lint:
- The plan's quality-check task listed only
go build ./... and go test ... (plan.md Task 8) — lint never became a plan requirement, even though apps/backend/README.md says "Before opening a pull request, run the linter and tests with make".
- The implementer's report explicitly surfaced the hole and nothing acted on it: "lint: not run (not requested by plan)".
- The backend-verifier proves ACs and rubric blockers; the
backend-api rubric has no "project lint/CI gates pass" item, so lint was out of its scope too.
- Both code reviewers review for correctness and security, not formatting; the Codex reviewer's read-only sandbox couldn't even run the test suite, let alone lint.
/do Step 5 (PR prep) commits, rebases, and pushes with no CI-equivalent gate.
Contributing factor: golangci-lint is not installed on this machine, so even an implementer inclined to run it would have hit a missing tool and needed an instruction on what to do about that (install, or flag as a blocker — not skip silently).
The code defect itself (import grouping in two files) is a symptom — a two-line fix routed through the normal PR flow, not this postmortem.
What to change so it doesn't recur
Proposed (one change): add a quality-gates step to /do Step 5 — the single choke point every implementation path (Codex implementer, frontend-implementer, fix rounds) passes through before a PR exists.
File: tyler/.claude/skills/do/SKILL.md (mirror: ~/.claude/skills/do/SKILL.md), Step 5 first bullet. Proposed edit:
All commit/PR prep lives here:
+- Run each touched app's own quality gates first — the lint/test entry point
+ its `AGENTS.md`/README/Makefile names (e.g. `make` in `apps/backend`); CI
+ runs these, so the PR is not ready until they pass locally. A missing tool
+ is a blocker to resolve (install it or ask the user), never a step to skip.
- Commit selectively (only this run's files, never `git add -A`; secret-scan
the staged diff), message style `type: short imperative summary`. Rebase
onto the origin default branch; push (`--force-with-lease` on rewrites).
Verdict: pending human approval — not applied.
Alternatives considered and set aside (one change per postmortem): adding lint to the implementer role instructions (misses the frontend-implementer and fix rounds), adding a rubric blocker (rubrics are per-change-type; this gate is universal), and a plan-template quality-gates section (reviewers didn't flag its absence this time, so the plan is a weaker choke point than Step 5).
System change (deferred)
Future: open a GitHub issue against dcouple/skills carrying this note. Off for now.
Work item: bloomapi/bloom-mono#323 · PR: https://github.com/bloomapi/bloom-mono/pull/332
🤖 Generated with Claude Code
Postmortem — Backend
/assetsconsumers fail in native local devWhat we asked for
Fix the Docker-only hard-coded
/assetspath so native local dev works: a singleASSETS_PATHenv var defaulting to/assets, all consumers resolving through it, dev scripts setting it — delivered as a PR that is ready to merge.What
/dodelivered vs intendedThe functional change was correct and fully verified (all four ACs plus both prevention tests, confirmed by two review passes), but the PR arrived with a failing
LintCI check:golangci-lint'sgcirule flaggedpkg/sendgrid/template.go:11andservices/pdf.go:9— the newpkg/envimport was placed in the third-party import group instead of the required local-prefix group. A PR that fails CI is not "done"; the human found the failure on review.Why the gap happened
Skill/agent gap: no pipeline stage owns "run the project's own quality gates before the PR." The defect entered at implement time (Codex placed the import in the wrong group — something
gofmt,go build, andgo testall accept) and then passed every subsequent stage because no stage runs the repo's lint:go build ./...andgo test ...(plan.md Task 8) — lint never became a plan requirement, even thoughapps/backend/README.mdsays "Before opening a pull request, run the linter and tests withmake".backend-apirubric has no "project lint/CI gates pass" item, so lint was out of its scope too./doStep 5 (PR prep) commits, rebases, and pushes with no CI-equivalent gate.Contributing factor:
golangci-lintis not installed on this machine, so even an implementer inclined to run it would have hit a missing tool and needed an instruction on what to do about that (install, or flag as a blocker — not skip silently).The code defect itself (import grouping in two files) is a symptom — a two-line fix routed through the normal PR flow, not this postmortem.
What to change so it doesn't recur
Proposed (one change): add a quality-gates step to
/doStep 5 — the single choke point every implementation path (Codex implementer, frontend-implementer, fix rounds) passes through before a PR exists.File:
tyler/.claude/skills/do/SKILL.md(mirror:~/.claude/skills/do/SKILL.md), Step 5 first bullet. Proposed edit:Verdict: pending human approval — not applied.
Alternatives considered and set aside (one change per postmortem): adding lint to the implementer role instructions (misses the frontend-implementer and fix rounds), adding a rubric blocker (rubrics are per-change-type; this gate is universal), and a plan-template quality-gates section (reviewers didn't flag its absence this time, so the plan is a weaker choke point than Step 5).
System change (deferred)
Future: open a GitHub issue against
dcouple/skillscarrying this note. Off for now.Work item: bloomapi/bloom-mono#323 · PR: https://github.com/bloomapi/bloom-mono/pull/332
🤖 Generated with Claude Code