8.0 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | duration | completed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 03-first-vertical-slice-genres-end-to-end | 03 | api |
|
|
|
|
|
|
|
|
|
8min | 2026-09-17 |
Phase 03 Plan 03: Typed params, isolated rollback, first real parity pass Summary
Typed ServeMux params, per-plugin migrate:rollback, and one honest Go pass of the recorded PHP genres fixture (154 recorded, 1 passing, 153 pending)
Performance
- Duration: 8 min
- Started: 2026-09-17T18:14:59Z
- Completed: 2026-09-17T18:23:20Z
- Tasks: 2
- Files modified: 17
Accomplishments
surf.IntParam,Where, andWhereIncompile regex/enum constraints at registration;examples/helloserves/items/1, 404s malformed and unknown IDs, and rejects enum values outside the allow-list.migrate:statuslists each plugin's applied IDs;migrate:rollback --plugin=golem15.fonotekarolls back only the genre seed and returnslagoon.ErrUnknownPluginfor a missing plugin.newTargetboots the real app; the temporarygenreshook seeds Alice, an owned collection, a test-only JWT, and colliding IDs from CanonicalGenres order. The unmodifiedget_genres_jwt.yamlpasses. Corpus: 154 recorded, 1 ported/passing, 153 pending.
Task Commits
Each task was committed atomically (framework summercms.go then app fonoteka.go when both change):
- Task 1: Expose reversible plugin migrations and typed route parameters
8e3bf266d860c7416e8988845b70b190535c4b55(feat, summercms.go)91e3df41e33c85818f4cc3470668853f04b67315(feat, fonoteka.go)
- Task 2: Turn the recorded genres route into one honest Go parity pass
0cc1a4da84ced8ed2451f2ea1c9a045f1c2ea682(feat, fonoteka.go)
Plan metadata: (this commit)
Files Created/Modified
surf/params.go—IntParam,Regex,Enum; constraints compiled at registrationpact/capabilities.go—Router.Where/WhereInlagoon/migrations.go—ErrUnknownPlugin,ErrNoMigrations, isolatedRollbackLastlagoon/commands.go—migrate:statusprints every applied IDexamples/hello/plugins/greeter/plugin.go—{id}regex route and enum/kinds/{kind}../fonoteka.go/parity/genres_seed_test.go— trusted GORM Alice/collection hook and test-only JWT../fonoteka.go/parity/parity_test.go— realapp.Handler, skip global bootstrap whenseed_hookis set, passing count after successful replay, mutated-response check../fonoteka.go/parity/manifest.yaml—GET /_fonoteka/api/v1/genres jwtisportedwithseed_hook: genres../fonoteka.go/README.md— ICUpl-PLcreate,migrate,migrate:status,migrate:rollback --plugin=golem15.fonoteka,serve
Decisions Made
- Constraints are compiled when the route is registered; enum values are an allow-list, not a regex built from request text.
- A ported route with
seed_hookdoes not replay unported global bootstrap endpoints. - Passing is incremented only after the ported subtest succeeds.
- Fixture collisions
id:token=1,id:wishlist-album=2,id:genre=4come from CanonicalGenres insertion order.
Deviations from Plan
Auto-fixed Issues
1. [Rule 3 - Blocking] Synthetic proof tests kept newSyntheticHandler after newTarget became the real app
- Found during: Task 2 (real handler swap)
- Issue:
TestParitySyntheticand the contractsynthetic-postgressubtest posted/synthetic/itemsthroughnewTarget. The real app does not mount those routes. - Fix: Call
newSyntheticHandlerfor the Phase 2 synthetic proof;newTargetbootsapp.Handler. - Files modified:
../fonoteka.go/parity/synthetic_test.go,../fonoteka.go/parity/parity_contract_test.go,../fonoteka.go/parity/parity_test.go - Verification:
go test ./parityincludingTestParitySyntheticandTestParityCorpus - Committed in:
0cc1a4d(Task 2)
2. [Rule 3 - Blocking] Rollback isolation uses a dedicated rollback_iso database
- Found during: Task 1 (rollback test)
- Issue: Shared TestMain Postgres would lose the 15 genre rows (and shift serial IDs) if seed rollback ran in-place.
- Fix: Create ICU
pl-PLdatabaserollback_iso, migrate/rollback there, leave the parity pool untouched. - Files modified:
../fonoteka.go/parity/migrate_test.go - Verification:
TestRollbackLastIsolatesFonotekaplus later genre/parity tests still see ids 1–15 - Committed in:
91e3df4(Task 1)
3. [Discretion] pact.Router gained Where/WhereIn
- Found during: Task 1 (typed params)
- Issue: Plan listed
surf/params.gobut plugins receivepact.Router; PHP->where()needs a method on that interface. - Fix: Add
Where/WhereIntopact.Routerand implement them on*surf.Router/*surf.Group. - Files modified:
pact/capabilities.go,surf/router.go - Verification: hello typed-route tests and
go vet ./... - Committed in:
8e3bf26(Task 1)
Total deviations: 3 (2 blocking test-isolation, 1 interface completeness) Impact on plan: No scope creep. QA-04 is green on one route; 153 remain pending.
Issues Encountered
None that blocked the slice. go.work.sum and .planning/config.json _auto_chain_active were left uncommitted.
User Setup Required
None - no external service configuration required beyond the existing Postgres ICU pl-PL database.
Next Phase Readiness
Ready for 03-04-PLAN.md (unit, integration, and security verification). The genres corpus route is ported. Do not treat pending routes as passing.
Self-Check: PASSED
- Key files exist on disk (
surf/params.go,lagoon/commands.go,../fonoteka.go/parity/genres_seed_test.go,../fonoteka.go/parity/parity_test.go) git log --grep=03-03returns Task 1 and Task 2 commits in both repos- Acceptance: per-plugin rollback isolation, hello typed/enum 404s, unmodified
get_genres_jwt.yaml200 against the real handler, corpus 154/1/153, mutated response fails, no tide or fixture edits,go vet/go test ./...green in both repos
Phase: 03-first-vertical-slice-genres-end-to-end Completed: 2026-09-17