OPER-60
Ship admission gate v0.1 (validator + PlanningRejection + NEEDS_RESCOPE enum)
Body
# OPER-60 — Ship admission gate v0.1 Implement the pre-build admission gate ratified in `docs/pdt/2026-07-28-admission-gate-v0.1.md` (PR #171), as descoped by `docs/doc/2026-07-29-admission-gate-risk-classifier.md`. This ticket dogfoods the gate itself: it satisfies every field the gate will enforce. ## Scope Single PR containing: 1. **`NEEDS_RESCOPE` enum value** on `TaskStatus` (Prisma migration + Zod schema + type exports). 2. **`PlanningRejection` table** — one row per rejection with `reasons: string[]` (fixed vocabulary — see Toma's non-negotiable below), `rejectedAt`, `taskId` FK, `plannerRunId` FK. Prisma model + migration + repository method. Reasons queryable via plain SQL — no JSONB unpacking (Priya's non-negotiable). 3. **Task field validator** in `apps/web/src/lib/planning-validator.ts` — checks presence of `size`, `writeSets` (non-empty array), `verificationCmd`, `module`, and body validity (≥ 800 chars, contains `## Acceptance Criteria` heading). Missing/malformed → verdict `fail_missing_admission_fields` + PlanningRejection row + status transition to `NEEDS_RESCOPE`. **`band` is NOT enforced by this gate** (descoped 2026-07-30). 4. **`writeSets` extractor** with confidence score. Parses ticket body for filepath-shaped strings (regex + AST heuristics). If confidence < 0.7, ticket routes to `NEEDS_RESCOPE` with `reasons: ["writesets_unresolved"]` rather than admitting with guessed values (Ren's mitigation). 5. **PHI reviewer-routing gate** — dispatcher and reviewer-routing logic MUST gate on `phiInScope = true` explicitly. Emits audit action `phi_task_routed_to_phi_reviewer` on every routing decision (Vela non-negotiable — ships in this PR, not follow-on). If a task has `phiInScope = true` and no PHI-eligible reviewer is available, task routes to `NEEDS_RESCOPE` with reason `phi_no_reviewer` (Roan non-negotiable). 6. **Audit action** `task.plan_rejected_missing_fields` wired into the existing audit-emit path. ## Fixed vocabulary for `PlanningRejection.reasons` (Toma non-negotiable) Values are the ONLY allowed strings — no free-text. Enforced via Zod at the write path. - `missing_size` - `missing_writesets` - `writesets_unresolved` - `missing_verify_cmd` - `missing_module` - `body_too_short` - `missing_acceptance_criteria` - `phi_no_reviewer` ## Non-goals - Risk-tier classifier (LOW/MED/HIGH/PHI) — **descoped 2026-07-30** per DOC memo. Future PDT convene. - Concept card in Studio (2B, phased in v0.2 — separate ticket). - Migrating existing 31 `PLANNING_VALIDATED` tickets — that's the rescope wave, tracked separately. - Auto-CANCEL on stubs — explicitly rejected by Justin on 2026-07-29 (manual review only). - Changes to `ClassificationBand` enum, `Classification` table, or `ClassifierConfig` — untouched (Nika non-negotiable). ## Acceptance Criteria - [ ] `NEEDS_RESCOPE` present in `TaskStatus` enum with Prisma migration file in `packages/database/prisma/migrations/`. - [ ] `PlanningRejection` table exists with FK to Task and to `PlanningRun`. `reasons` is a `text[]` column with Zod enum validation at write path. - [ ] Planning validator rejects any task missing `writeSets` (empty array counts as missing), `size`, `verificationCmd`, `module`, or body < 800 chars, or body without `## Acceptance Criteria` heading. - [ ] Rejected tasks transition to `NEEDS_RESCOPE`; row appears in `PlanningRejection` with `reasons` array using only the fixed vocabulary. - [ ] `writeSets` extractor emits confidence score; tasks with confidence < 0.7 route to NEEDS_RESCOPE with reason `writesets_unresolved`. - [ ] Dispatcher gates PHI-task routing on `phiInScope = true` with audit-emit `phi_task_routed_to_phi_reviewer` on every routing decision. - [ ] Tasks with `phiInScope = true` and no PHI-eligible reviewer route to `NEEDS_RESCOPE` with reason `phi_no_reviewer`. - [ ] `docs/task-state-machine.md` diagram already updated in PR #171; validator implementation must match it. - [ ] Audit action `task.plan_rejected_missing_fields` emits for every NEEDS_RESCOPE transition. - [ ] No changes to `ClassificationBand`, `Classification`, or `ClassifierConfig` in this PR. - [ ] `SELECT reasons FROM "PlanningRejection"` returns plain text-array on inspection (no JSONB unpacking). - [ ] Green CI, no linting failures, unit test coverage ≥ 80% on new modules. ## Verification Run `pnpm test packages/database apps/web/src/lib/planning-validator.test.ts` — all green. Run `pnpm prisma migrate deploy` on preview branch, confirm `NEEDS_RESCOPE` and `PlanningRejection` present. Manually POST a task missing `writeSets` to the validate-plan route, confirm 200 with `verdict=fail_missing_admission_fields` and status `NEEDS_RESCOPE`. ## Reasoning references - Ratification memo: `docs/pdt/2026-07-28-admission-gate-v0.1.md` (PR #171) - DOC descope memo: `docs/doc/2026-07-29-admission-gate-risk-classifier.md` (Suki non-negotiable) - State machine: `docs/task-state-machine.md` (updated in PR #171) - Prior art: OPER-58 (detection), OPER-59 (in-process planner), OPER-27 (queue_liveness breakout)
Attachments
Loading attachments…
Comments
Loading comments…