fix(§22.4a): scope RFCView entry-detail fetch to its collection (v0.52.1)

The §9 deployed-environment E2E harness (0.52.0), run against a PPE host
with per-collection-isolated content, surfaced a latent multi-collection
bug: RFCView computed the collection id from the route but called
getRFC(pid, slug) without it, so a named-collection entry was always
fetched via the project default-collection route — which 404s for an entry
that exists only in a named collection ("Error: Not found"; metadata panel
absent). Local/Tier-1 stacks masked it (same slug also reachable via the
default collection). Thread cid through all three getRFC call sites; re-run
the load effect on collection change.

Harness/test-infra (not in the deployed artifact):
- e2e: pre-record cookie consent via addInitScript (lib/fixtures.js) so the
  bottom-fixed consent banner can't intercept catalog row-select clicks on
  the slower deployed edge.
- testing/seed-ppe.sh: fail loudly on any non-2xx Gitea response (a
  swallowed 403 org-repo create had reached the deploy as a 502).
- testing/ppe-deploy-and-test.sh: seed via the Keychain admin token
  (write:organization needed to create the PPE repos); store the E2E secret
  newline-free; read EXPECT_VERSION from VERSION.

Patch bump 0.52.0 → 0.52.1; CHANGELOG updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-08 06:45:01 -07:00
parent 9c8035bdbd
commit ba37da927a
8 changed files with 124 additions and 23 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "rfc-app-frontend",
"private": true,
"version": "0.52.0",
"version": "0.52.1",
"type": "module",
"scripts": {
"dev": "vite",
+12 -5
View File
@@ -131,7 +131,14 @@ export default function RFCView({ viewer }) {
const [drawerOpen, setDrawerOpen] = useState(false)
useEffect(() => {
getRFC(pid, slug).then(entry => {
// §22.4a: an entry in a NAMED collection must be fetched collection-
// scoped. Omitting `cid` falls back to the project's DEFAULT-collection
// route (/api/projects/<pid>/rfcs/<slug>), which 404s for an entry that
// lives only in a named collection — surfacing as "Error: Not found" and
// a missing metadata panel. (Tier-1 masked this when the same slug was
// also reachable via the default collection; PPE's isolated content
// exposed it.)
getRFC(pid, slug, cid).then(entry => {
setEntry(entry)
// v0.15.0 — analytics: fire RFC Viewed once per slug load.
// We key on the slug param rather than the loaded entry so a
@@ -146,7 +153,7 @@ export default function RFCView({ viewer }) {
setSelectedModel(def || models?.[0]?.id || '')
})
.catch(() => {})
}, [slug, pid])
}, [slug, pid, cid])
// §22.4a SLICE-4: load the collection's metadata field schema for the
// detail panel. Independent of the entry load; the panel reads the entry's
@@ -194,12 +201,12 @@ export default function RFCView({ viewer }) {
setActionError(null)
try {
const res = await unretireRFC(slug)
getRFC(pid, slug).then(setEntry).catch(() => {})
getRFC(pid, slug, cid).then(setEntry).catch(() => {})
if (res?.state) navigate(entryPath(pid, slug))
} catch (err) {
setActionError(err.message)
}
}, [slug, navigate, pid])
}, [slug, navigate, pid, cid])
// Load main view + branch view whenever slug/branch changes.
useEffect(() => {
@@ -985,7 +992,7 @@ export default function RFCView({ viewer }) {
onCompleted={() => {
setShowGraduateDialog(false)
// The catalog row and the RFC view now reflect `active`.
getRFC(pid, slug).then(setEntry).catch(() => {})
getRFC(pid, slug, cid).then(setEntry).catch(() => {})
getRFCMain(slug).then(setMainView).catch(() => {})
}}
/>