From d97b4292a3473b1b83e45ac15425a27320db43ff Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Fri, 2 Oct 2026 08:04:23 +0200 Subject: [PATCH] docs(12): record post-research decisions and validation strategy --- .../12-CONTEXT.md | 10 +++ .../12-VALIDATION.md | 82 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 .planning/phases/12-p-ytarium-api-collections-and-albums/12-VALIDATION.md diff --git a/.planning/phases/12-p-ytarium-api-collections-and-albums/12-CONTEXT.md b/.planning/phases/12-p-ytarium-api-collections-and-albums/12-CONTEXT.md index 6e7b2ea..68811c1 100644 --- a/.planning/phases/12-p-ytarium-api-collections-and-albums/12-CONTEXT.md +++ b/.planning/phases/12-p-ytarium-api-collections-and-albums/12-CONTEXT.md @@ -91,6 +91,16 @@ Repo: `fonoteka.go`. Framework changes in `summercms.go` only where a gap is fou - Which errors per route need a recording and which a Go test with bodies from PHP source, where recording a case is impractical (e.g. a mid-transaction race). Recording is the default. - Plan count and split, subject to the plan-count checkpoint, "unit tests are the last plan" and the security-review agent (this phase touches authorization and public tokens). +### Post-research decisions (2026-10-02, confirmed at the plan-count checkpoint) +- **D-19:** Supersedes D-16 point 3. `meta.total`/`last_page` mirror Scout v10.25.0 `Builder::getTotalCount`: re-fetch up to `min(found, 1000)` ids from the engine and count them in SQL with the same access gating. The total is therefore re-gated and capped at 1000; stale/foreign ids still shorten the page. The D-18 leak test also asserts the total never counts a leaked row. +- **D-20:** `GET realtime/channels` (`{"data":{"collection":"collection:"}}`) is ported in this phase. `me/context` returns only its recorded flags (AI, org, Discogs, currency) and no channel. API-01 and success criterion 1 are reworded at plan time. +- **D-21:** Error bodies are reproduced as PHP serves them in production (`APP_DEBUG=false`), including Winter HTML error pages where PHP throws `HttpException` (switch, household, invitations, accept). The existing accept 410 fixture is re-recorded under `APP_DEBUG=false`; the invitation `ValidationException` envelopes are recorded. +- **D-22:** Photo and attachment URLs match PHP (`/storage/app/uploads/public//`). The Go attach URL prefix is fixed in this phase, restoring P5 D-16. +- **D-23:** The raw invitation token in River job args is encrypted with the app key and decrypted only inside the mail job. Amends D-13: the raw token never appears in plain text in `river_job.args`, logs or `summer_jobs.metadata`. +- **D-24:** The image guard accepts webp as PHP does. `golang.org/x/image` is bumped to v0.46.0 and its webp decoder registered (named here as the phase decision that authorises the dependency bump). +- **D-25:** The site-admin check in `me/context` reads user groups. User groups (`users_groups` and the groups relation) are added to the Go user plugin as an **additive, non-breaking** change; its existing contract is unchanged. The user signed off on this core-plugin change on 2026-10-02. +- **D-26:** Research-found bugs fixed in this phase: the token group applies exactly one `inv.scope:read|write` per route as PHP does (not `read` at group level), and deleting a collection removes its albums one by one so each album gets its Typesense removal and `deleted` broadcast, as PHP does. + diff --git a/.planning/phases/12-p-ytarium-api-collections-and-albums/12-VALIDATION.md b/.planning/phases/12-p-ytarium-api-collections-and-albums/12-VALIDATION.md new file mode 100644 index 0000000..0c85391 --- /dev/null +++ b/.planning/phases/12-p-ytarium-api-collections-and-albums/12-VALIDATION.md @@ -0,0 +1,82 @@ +--- +phase: "12" +slug: "p-ytarium-api-collections-and-albums" +# status lifecycle: draft (seeded by plan-phase) → validated (set by validate-phase §6) +# audit-milestone §5.5 distinguishes NOT-VALIDATED (draft) from PARTIAL (validated + nyquist_compliant: false) (#2117) +status: draft +nyquist_compliant: false +wave_0_complete: false +created: "2026-10-02" +--- + +# Phase 12 — Validation Strategy + +> Per-phase validation contract for feedback sampling during execution. + +--- + +## Test Infrastructure + +| Property | Value | +|----------|-------| +| **Framework** | Go `testing` (+ testify assert/require, Go fuzzing), testcontainers Postgres | +| **Config file** | none (`parity/parity_test.go` TestMain starts Postgres) | +| **Quick run command** | `cd fonoteka.go && go test ./plugins/golem15/fonoteka/... -short -count=1` | +| **Full suite command** | `cd fonoteka.go && go vet ./... && go test ./... -count=1`, plus `cd summercms.go && go vet ./... && go test ./... -count=1` for framework changes | +| **Parity command** | `cd fonoteka.go && go test ./parity -run 'TestParityCorpus|TestBroadcastGoldens|TestNuxtFlow' -count=1` | +| **Estimated runtime** | ~180 seconds (full suite, both repos, with containers) | + +--- + +## Sampling Rate + +- **After every task commit:** quick run command plus `go vet` in the touched repo +- **After every plan wave:** full suite in both repos plus the parity command +- **Before `/gsd-verify-work`:** full suite green in both repos; parity corpus with the new routes flipped to `ported` and passing; `check_corpus.go --require-recorded --check-secrets` green; `go test ./cmd/summer -run TestDocsTree` green +- **Max feedback latency:** 60 seconds (quick run) + +--- + +## Per-Task Verification Map + +Seeded from RESEARCH.md by requirement; task IDs are filled in once PLAN.md files exist. + +| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status | +|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------| +| TBD | 01 | 1 | framework | — | beachcomber `found`/weights, tide multipart + URL mask, Laravel-semantics validator + catalogs, attach URL export | unit | `cd summercms.go && go test ./modules/... -count=1` | ❌ W0 | ⬜ pending | +| TBD | 02 | 2 | API-01 | T-12-01, T-12-03, T-12-04, T-12-05 | Collections, switch, me/context, realtime/channels, share replays; token pin and narrowing | parity + integration | `go test ./parity -run TestParityCorpus/.*collection` | ❌ W0 | ⬜ pending | +| TBD | 03 | 3 | API-01 | T-12-06, T-12-07 | Invite mail enqueued in tx, absent on rollback; token encrypted in job args, never logged | integration + parity flow | `go test ./parity -run TestNuxtFlow/nuxt-collections` | ❌ W0 | ⬜ pending | +| TBD | 04 | 4 | API-02 | T-12-02, T-12-09, T-12-10, T-12-11 | Album CRUD, rating, photos, bulk, stats/value/missing/sync, search, lookups; upload guard; SSRF guard | parity + integration | `go test ./parity -run 'TestParityCorpus/.*albums|TestBroadcastGoldens'` | ❌ W0 | ⬜ pending | +| TBD | 05 | 5 | API-01, API-02 | T-12-01..T-12-11 | D-18 leak test (5 cases + total), request-DTO fuzz, route-table single-scope test | security + fuzz + unit | `go test ./plugins/golem15/fonoteka/... -run 'TestSearchLeak|TestRouteTable|FuzzWriteEndpoints' -count=1` | ❌ W0 | ⬜ pending | + +*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky* + +--- + +## Wave 0 Requirements + +- [ ] Re-record the HttpException cases under `APP_DEBUG=false` (accept 410, switch 404, household/members 404, invitations 404, token 404) — D-21 +- [ ] tide request `body_file` (multipart) + `url`/`thumb_url` disk-name normalizer + publication date masking (framework) +- [ ] Seed hooks or flows for a second user and an outsider (reuse `id:outsider` from Phase 11) +- [ ] Fake `beachcomber` engine with scripted ids and `found` for D-18/D-19 + +--- + +## Manual-Only Verifications + +| Behavior | Requirement | Why Manual | Test Instructions | +|----------|-------------|------------|-------------------| +| Recording new PHP fixtures against the isolated PHP instance | API-01, API-02 | Needs the running PHP reference instance and capture tooling | Follow the Phase 2 `tide` capture rules (private 0600 vars, no live tokens in git), then run `check_corpus.go --require-recorded --check-secrets` | + +--- + +## Validation Sign-Off + +- [ ] All tasks have `` verify or Wave 0 dependencies +- [ ] Sampling continuity: no 3 consecutive tasks without automated verify +- [ ] Wave 0 covers all MISSING references +- [ ] No watch-mode flags +- [ ] Feedback latency < 60s +- [ ] `nyquist_compliant: true` set in frontmatter + +**Approval:** pending