Operant Studio
OPER-146

Ticket-filing helper (OPER-58 admission-contract enforcement, agent-side) — D-25 next action 1

Band
MEDIUM
Suggested
Justin Cooke

Body

feature_id: FEAT-studio-planning-validation
decision_memo: docs/pdt/2026.09.09-d25-admission-fields.md
filing_template_version: 1

## Context

Six agent-filed follow-up tickets in the last two days (OPER-139/140/141/142 filed by the guard-inventory checker; OPER-143 mislabel; OPER-145 the check-ticket-trailer self-test gap) all landed in `NEEDS_RESCOPE` on creation because the filing agent did not populate `size`, `module`, `verificationCmd`, or resolvable `writeSets`. Every one required a human PATCH to become dispatchable.

The planning-validator (`apps/web/src/lib/planning-validator.ts:56-108`) is doing exactly what OPER-58 designed it to do. `docs/task-state-machine.md` explicitly flags `NEEDS_RESCOPE` as intentionally not auto-recoverable — that property is a load-bearing invariant of the whole dispatcher pipeline. The problem is not the validator. The problem is that agents that FILE tickets do not know about the admission-fields contract, so the field-population step is silently missing on every agent-filed ticket. Studio API stays strict; the fix goes on the write side.

PDT memo D-25 (`docs/pdt/2026.09.09-d25-admission-fields.md`) recommended Option A: enrich at the source. Add a typed `fileTicketForFollowup(class, subject, inputs)` helper that every agent-side ticket-filing path MUST call. The `class` parameter is a closed TS union (e.g. `guard-selftest-gap`, `admission-fields-fix`, `dispatcher-fix`, `docs-only`) — a new class requires a code change and a PR review. Per PDT memo D-27 (2026.09.09), the helper additionally accepts `target_repo?: TargetRepoName` as a typed optional parameter on every ticket class; when present, the helper writes `target_repo: <value>` into the body frontmatter. Valid values per class are documented in the template registry and must be on the team's target-repo allowlist (enforced at admission by OPER-155 / D-27 next action 1). The helper populates `size`, `module`, `verificationCmd`, `writeSets`, `## Acceptance Criteria`, and body-length padding from a per-class template registry; the agent supplies the class + subject-specific inputs; the helper enforces the rest.

The template registry is VERSIONED — the helper writes a `filing_template_version: N` frontmatter line into every ticket body, matching the same versioned-contract pattern that ticket-admission.ts already uses for featureMapChecksum.

## Acceptance Criteria

- New file `apps/web/src/lib/ticket-filing.ts` exports `fileTicketForFollowup(class, subject, inputs)` with a typed `class` union covering at minimum: `guard-selftest-gap`, `admission-fields-fix`, `dispatcher-fix`, `docs-only`, `ci-only`.
- Per-class template registry populates `size`, `module`, `verificationCmd`, `writeSets`, `## Context`, `## Acceptance Criteria`, and a `filing_template_version: N` frontmatter line.
- Every agent-side ticket POST path (packages/agent-* and any dispatcher-triggered filing seam) is wired through the helper — no direct `fetch('/api/studio/[team]/tasks', ...)` calls remain in agent code.
- Unit tests in `apps/web/src/lib/ticket-filing.test.ts` cover: each class round-trips through the validator without rejection; an unknown class is a compile error (type-level test); template version is stamped and readable back.
- Retroactive: OPER-139/140/141/142 + OPER-145 are re-filed (or PATCHed with the same field set the helper would have produced) so they leave `NEEDS_RESCOPE`.
- **D-27 amendment (2026.09.09):** helper signature is `fileTicketForFollowup(class, subject, inputs, options?: { target_repo?: TargetRepoName })`. `TargetRepoName` is a typed string union ("operant-studio" | "portico" | "roster" | "piperdx"). When `target_repo` is supplied, helper writes `target_repo: <value>` into body frontmatter. Per-class template registry documents which values are valid for that class (e.g. `dispatcher-fix` allows "operant-studio" or "portico"; `docs-only` allows any). Test coverage: (a) Operant-team `dispatcher-fix` with `target_repo: portico` writes the frontmatter correctly; (b) unknown target_repo value is a compile error (type-level test); (c) omitting `target_repo` leaves the frontmatter absent (team default used at dispatch).
- No change to `apps/web/src/lib/planning-validator.ts` in this ticket — validator stays strict.

## Non-goals

- LLM-based auto-classification of missing fields (Option B in D-25, explicitly rejected).
- Changes to the planning-validator's required-field set — that's a follow-on ticket.
- Changes to human-filed ticket paths (UI, console) — this ticket covers agent-filed only.

## Related

- Depends on nothing; unblocks the follow-on template-version validator ticket.
- Blocks the retroactive PATCH of OPER-139/140/141/142/145 until the helper is available.

Attachments

Loading attachments…

Comments

Loading comments…