OPER-168
OPER: PATCH to a frontmatter-blocker field should re-run admission, not just POST
Band
SMALL
Suggested
Justin Cooke
Body
--- feature_id: FEAT-studio-planning-validation filing_template_version: v1 decision_memo: docs/pdt/2026.09.09-observation-patch-frontmatter-readmit.md blocked_by: --- ## Context On 2026.09.09 OPER-155 was filed with prose "Depends on OPER-148" in a Related section. Admission's blocker parser read that prose, saw OPER-148 was not DONE, and rejected the ticket with `blocked_by_not_done`. The prose was rewritten to "Sequenced after" and a proper `blocked_by: OPER-148` frontmatter field was added via PATCH -- but admission did not re-run, so the ticket stayed AWAITING_HUMAN with the `admission-rejected` label even though the ticket now conformed to policy. The hole is `apps/web/src/app/api/studio/[team]/tasks/[id]/route.ts` PATCH -- it did not call `assertAdmissible`. OPER-151 (merged as PR #441) closes half of that hole: admission now runs on PATCH at all. It does NOT yet special-case the frontmatter-blocker fields, so a PATCH that fixes the exact rejection reason still leaves the `admission-rejected` label in place until the ticket is re-POSTed or manually promoted. ## Root cause OPER-151's admission-on-PATCH fires for every PATCH, which is correct as a floor. But it does not clear the `admission-rejected` label when the new body would pass admission, and it does not re-emit a passing admission trailer to the body. Downstream planners then see a mixed signal: body is compliant, but the label says "do not touch". ## Acceptance Criteria 1. When PATCH runs admission and the resulting decision is `ok=true`: a. Remove the `admission-rejected` label from the ticket if present. b. Replace the trailing `<!-- admission-rejected -->` marker (and its associated `**Admission rejected ...**` line) with `<!-- admission-passed-on-patch -->` and a one-line `**Admission re-checked on PATCH: PASS** -- <reason>` note. c. Transition the ticket status from `AWAITING_HUMAN` to `BACKLOG` so the planner picks it up on the next tick. 2. When PATCH runs admission and the resulting decision is `ok=false`: a. Preserve the `admission-rejected` label. b. Append (not replace) a new dated `<!-- admission-rejected -->` marker with the current failure reason, so the trailer history reads as a mini-changelog. 3. Self-test `scripts/patch-readmit-selftest.sh` (KNUCK-A032 pattern) proves: (a) blocker-not-done -> PATCH with correct frontmatter -> label cleared, status BACKLOG; (b) blocker-not-done -> PATCH with still-wrong body -> label kept, new marker appended; (c) benign PATCH of an unrelated field -> label state unchanged. 4. `docs/task-state-machine.md` records the AWAITING_HUMAN -> BACKLOG re-admission transition alongside the existing manual reclassification path. ## Verification - `bash scripts/patch-readmit-selftest.sh` passes locally and in CI. - Manual repro on OPER-155 (if still AWAITING_HUMAN): PATCH the body, watch the label clear and status flip to BACKLOG within one API call. ## Notes - Class: `Stale-Admission-Label`. Depends on OPER-151 having landed (it has, as of 2026.09.09 PR #441). - Do not weaken admission while fixing this; the fix is about label/status hygiene, not lenience.
Attachments
Loading attachments…
Comments
Loading comments…