Found while verifying #117 on the deployed compose stack (aep-rewrite HEAD, real Thunder login as a seeded test user):
GET /api/v1/projects/store-handmade-ceramics → 403 {"title":"Forbidden","detail":"insufficient permissions to perform this action"}
GET /api/v1/projects/store-handmade-ceramics/status → 200
GET /api/v1/projects/store-handmade-ceramics/files → 200
GET /api/v1/projects/store-handmade-ceramics/tags → 200
Impact: the console's ProjectLayout blocks on useProject (get-project), so the entire project view — overview cards, components, everything under the layout — renders the error state for regular users. The spec route still works because it only needs status/files. It also explains why the spec header shows the raw project slug instead of the display name.
The sibling repo-backed reads all pass the tenant gate, so this looks specific to the get-project path (OpenChoreo project read / its authz mapping) rather than the JWT or org resolution. Not a regression from #117/#118 — those PRs touch neither get-project nor authz.
Repro: seeded dev cluster, deployments/scripts/seed-test-users.sh user (e.g. mark/admin), open http://localhost:8091/projects/.
Found while verifying #117 on the deployed compose stack (aep-rewrite HEAD, real Thunder login as a seeded test user):
Impact: the console's
ProjectLayoutblocks onuseProject(get-project), so the entire project view — overview cards, components, everything under the layout — renders the error state for regular users. The spec route still works because it only needs status/files. It also explains why the spec header shows the raw project slug instead of the display name.The sibling repo-backed reads all pass the tenant gate, so this looks specific to the get-project path (OpenChoreo project read / its authz mapping) rather than the JWT or org resolution. Not a regression from #117/#118 — those PRs touch neither get-project nor authz.
Repro: seeded dev cluster,
deployments/scripts/seed-test-users.shuser (e.g. mark/admin), open http://localhost:8091/projects/.