OPER-206
Auto-derive verificationCmd from writeSets to eliminate silent rescopes
Suggested
Justin Cooke
Body
--- feature_id: FEAT-studio-planning-validation --- ## Context Every ticket filed via the Ledger POST API this session (OPER-184 through OPER-194, ten total) admitted successfully but was demoted to NEEDS_RESCOPE by the auto-planner within one tick. Root cause discovered 2026.09.13 by reading `apps/web/src/lib/planning-validator.ts` and `PLANNING_REJECTION_REASONS`: every ticket had `verificationCmd: null` on the row. Admission does not require verificationCmd (it is a shape check on body + writeSets + size + module), but the planner DOES require it as an admission-field second-stage check (`missing_verify_cmd` in `PLANNING_REJECTION_REASONS`). This is a systematic issue class: admission passes, planner rescopes, and the rescope reason is silent (see companion ticket for that). It costs one extra PATCH cycle per ticket + a full diagnostic pass to figure out which of the 9 reasons fired. The verificationCmd IS mechanically derivable from writeSets in the majority of cases: if writeSets contains `packages/<pkg>/src/**/*.ts`, verificationCmd is `pnpm --filter @operant/<pkg> test <matching test file>`. If writeSets contains `apps/web/src/**/route.ts`, verificationCmd is `pnpm --filter @operant/web test <matching .test.ts>`. If writeSets contains `.github/workflows/*.yml`, verificationCmd is `gh workflow view <workflow-name>` + a smoke test. ## What Extend admission (`assertAdmissible` or planner intake in `apps/web/src/lib/planning-validator.ts`) with a `deriveVerificationCmd(writeSets, module)` pass that: 1. If Task.verificationCmd is already set: skip (respect explicit). 2. Else, for each writeSet path, apply a rule from a small pattern table: - `packages/<pkg>/src/**/*.ts` → `pnpm --filter @operant/<pkg> test <matching src/__tests__/*.test.ts>` - `apps/<app>/src/**/route.ts` → `pnpm --filter @operant/<app> test <sibling *.test.ts>` - `apps/<app>/src/**/*.ts` (non-route) → `pnpm --filter @operant/<app> test <matching *.test.ts>` - `packages/database/prisma/schema/*.prisma` → `pnpm --filter @operant/database prisma migrate deploy && pnpm --filter @operant/database test` - `.github/workflows/*.yml` → `gh workflow view <workflow-name> --repo operant-health/operant-studio` - `docs/**` → `echo docs-only; true` (docs-only tickets do not need runtime verification) 3. Concatenate rules with `&&` when multiple writeSets match different patterns. 4. If NO pattern matches, still fail with `missing_verify_cmd` — the pattern table is best-effort, not required. 5. Auto-populate Task.verificationCmd via the same PATCH route the healer uses; emit `TASK_VERIFY_CMD_DERIVED` audit event. This is the second half of the "coverage by construction" ethic from D-29 — verification cmds should also be derived by construction from writeSets when they follow the boilerplate conventions, not required from the ticket filer. ## Acceptance Criteria - [ ] `deriveVerificationCmd(writeSets, module)` function added at `packages/planning-validation/src/derive-verify-cmd.ts` or similar - [ ] Pattern table covers the six rules above; extensible via a small config - [ ] Explicit Task.verificationCmd always wins over derived - [ ] Planner intake calls the deriver before rescoping on `missing_verify_cmd` - [ ] Deriver-generated cmds emit `TASK_VERIFY_CMD_DERIVED` audit event with the pattern that matched - [ ] Unit tests cover: packages src, apps route, apps non-route, prisma schema, workflows, docs-only, no-pattern-match fallback, explicit-wins-over-derived - [ ] Integration test: file a ticket with only writeSets set, verify planner derives verificationCmd instead of rescoping ## Blocked-by None. ## Size / Band S / assurance (single deriver module + planner intake call site). ## Discovered by Session 2026.09.10-2026.09.13. Ten filed tickets all rescoped on missing_verify_cmd. Fixed with manual PATCH in step 1 of the same session.
Attachments
Loading attachments…
Comments
Loading comments…