feat(web): content_policy gates on all idea write-paths (sub-project C, Phase 3) #84

Merged
janpeter merged 5 commits from feat/copilot-content-policy-web into main 2026-06-14 10:03:32 +02:00
Owner

Sub-project C, Phase 3 - the AVG content-policy gate on all web/REST idea write-paths. Completes the platform enforcement (Phase 2 covered the MCP path).

What

  • lib/idea-content-policy.ts - checkIdeaContentAllowed(productId, text): loads the effective product.content_policy, parseContentPolicy (fail-closed on malformed), checkContentPolicy. One shared helper for all 4 sites.
  • actions/ideas.ts - gate createIdeaAction (before the create) + updateIdeaAction (re-check against the target product on a title/description/product_id change).
  • app/api/ideas/route.ts - gate POST (before the create).
  • app/api/ideas/[id]/route.ts - gate PATCH (re-check against the target product).
  • Test-hardstop (__tests__/lib/no-ungated-idea-write.test.ts) - fails if any of the 3 write-path files loses its checkIdeaContentAllowed reference.

No submodule bump needed

web main already vendors shared 9a0a0bd (via the merged migration PR #83), so @shared/content-policy + the content_policy column are already available. This PR is gate code only.

Tests

15 new (helper x4, actions x4, REST x4, hardstop x3). Full verify: lint 0 errors, typecheck clean, 1449/1449 tests (178 files).

Deploy

The migration is already live (Scrum4Me #83 deployed by 154), so these gates are deploy-safe. The checker itself was reviewed on scrum4me-shared #16 (3 codex + 3 adversarial rounds); this PR only wires it in (same pattern as the codex-akkoord'd mcp PR #53).

🤖 Generated with Claude Code

**Sub-project C, Phase 3** - the AVG content-policy gate on **all web/REST idea write-paths**. Completes the platform enforcement (Phase 2 covered the MCP path). ## What - **`lib/idea-content-policy.ts`** - `checkIdeaContentAllowed(productId, text)`: loads the effective `product.content_policy`, `parseContentPolicy` (fail-closed on malformed), `checkContentPolicy`. One shared helper for all 4 sites. - **`actions/ideas.ts`** - gate `createIdeaAction` (before the create) + `updateIdeaAction` (re-check against the *target* product on a title/description/product_id change). - **`app/api/ideas/route.ts`** - gate `POST` (before the create). - **`app/api/ideas/[id]/route.ts`** - gate `PATCH` (re-check against the target product). - **Test-hardstop** (`__tests__/lib/no-ungated-idea-write.test.ts`) - fails if any of the 3 write-path files loses its `checkIdeaContentAllowed` reference. ## No submodule bump needed web `main` already vendors shared `9a0a0bd` (via the merged migration PR #83), so `@shared/content-policy` + the `content_policy` column are already available. This PR is gate code only. ## Tests 15 new (helper x4, actions x4, REST x4, hardstop x3). Full verify: **lint 0 errors, typecheck clean, 1449/1449 tests** (178 files). ## Deploy The migration is already live (Scrum4Me #83 deployed by 154), so these gates are deploy-safe. The checker itself was reviewed on scrum4me-shared #16 (3 codex + 3 adversarial rounds); this PR only wires it in (same pattern as the codex-akkoord'd mcp PR #53). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
test(web): hardstop — idea write-paths must reference the content-policy gate
All checks were successful
CI / Lint, Typecheck, Test & Build (pull_request) Successful in 3m21s
CI / Deploy Manual (workflow_dispatch) (pull_request) Has been skipped
CI / Detect deploy-relevant changes (pull_request) Has been skipped
CI / Deploy Preview (PR) (pull_request) Has been skipped
CI / Deploy Production (main) (pull_request) Has been skipped
9119f1d439
fix(web): explicit detach (product_id: null) targets no product, not the old one (codex P3)
All checks were successful
CI / Lint, Typecheck, Test & Build (pull_request) Successful in 3m21s
CI / Deploy Manual (workflow_dispatch) (pull_request) Has been skipped
CI / Detect deploy-relevant changes (pull_request) Has been skipped
CI / Deploy Preview (PR) (pull_request) Has been skipped
CI / Deploy Production (main) (pull_request) Has been skipped
ad7d4ec2c6
updateIdeaAction + PATCH used `parsed.data.product_id ?? idea.product_id`, so an
explicit detach (product_id: null) fell through to the old product and got gated
against its policy. Now uses an explicit undefined-check so null → no policy. +2 tests.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
janpeter/Scrum4Me!84
No description provided.