Operant Studio
OPER-205

C9 milestone watcher: cron detects first Fiesta UAT close OR Piper DX SaMD event

Suggested
Justin Cooke

Body

---
feature_id: FEAT-studio-pipeline-observability
---

## Context

C9 memo (2026.09.10 Studio QA Positioning Shift, Amendment 3) shipped a revised Final Recommendation: position Operant Studio QA externally when the first of two evidence milestones lands — Fiesta UAT external-tester feedback loop closes at least once, OR Piper DX produces SaMD-shaped PSUR evidence. Whichever fires first is the trigger for external-publish reconvene.

Neither milestone has an automatic detector today. The C9 memo has no auto-reconvene loop; it relies on a human noticing the milestone and re-opening the memo. That fails the standing rule that every discovered systematic gap should propose a purpose-built gate — this ticket is the gate.

Two data sources exist today for automatic detection:

1. **Fiesta UAT close**: `TesterInvite` row (declared in D-28, seeded by POR-773 merged [REDACTED-DOB]) transitions to state `REVIEW_COMPLETE` for the first time in the Fiesta tenant. This is a QaTaskInboxTesterInvite state-machine event that already lands in the audit stream when OPER-127a rerun-loop machines dispatch.

2. **Piper DX SaMD event**: `PSUR` (Periodic Safety Update Report) event emitted from the Piper DX repo's clinical-events stream. Piper DX has an equivalent evidence-emission surface planned but not yet built. For v1 of this watcher, treat any Piper DX repo push tagged `psur:*` OR any Ledger ticket in Piper DX with label `evidence:samd` transitioning to DONE as the trigger.

## What

Add a scheduled cron watcher at `apps/web/src/app/api/cron/milestone-c9-external-publish/route.ts` that runs every 15 minutes and:

1. Queries the `TesterInvite` table for any Fiesta-tenant row transitioning to `REVIEW_COMPLETE` since last run.
2. Queries Ledger for any Piper DX task with `evidence:samd` label transitioning to DONE, OR any Piper DX repo push with tag matching `psur:*` (via GitHub Events API `PushEvent` filter).
3. On first detection of either milestone:
   - Emit `MilestoneFiredEvent` audit row with milestone kind (`fiesta_uat_close` OR `piperdx_samd`), timestamp, evidence pointer (TesterInvite id OR ticket id OR push SHA).
   - Auto-file a new OPER ticket titled `C9 Reconvene: External-publish trigger fired — <milestone_kind>` under FEAT-studio-pipeline-observability, priority=1, body pre-populated with evidence pointer + link to c9-2026.09.10-studio-qa-positioning-shift.md and the exact Amendment 3 clause.
   - Post to the shared project Slack channel (per D-28: team-visible, not DM) with the trigger + link to the auto-filed reconvene ticket.
   - Set a MilestoneWatcher.disabled=true flag so this watcher does not re-file on subsequent detections (it fires exactly once per milestone kind; if BOTH fire on the same tick, file BOTH tickets).

Idempotency: keep a `MilestoneWatcher` singleton row with `firstFiestaAt`, `firstPiperDxAt` fields. Non-null means fired.

## Acceptance Criteria

- [ ] `MilestoneWatcher` Prisma model added with singleton constraint (`teamId UNIQUE`), fields `firstFiestaAt`, `firstPiperDxAt`, `fiestaEvidenceRef`, `piperdxEvidenceRef`, `disabled`
- [ ] Migration adds the model with default row for the operant team
- [ ] Cron route runs every 15 minutes at `apps/web/src/app/api/cron/milestone-c9-external-publish/route.ts`
- [ ] Route queries TesterInvite for first Fiesta REVIEW_COMPLETE transition
- [ ] Route queries Ledger + GitHub for first Piper DX SaMD evidence event
- [ ] On first detection: emits MilestoneFiredEvent audit row
- [ ] On first detection: auto-files C9 reconvene ticket via Ledger POST
- [ ] On first detection: posts to shared project Slack channel
- [ ] Watcher fires exactly once per milestone kind (idempotent via MilestoneWatcher row)
- [ ] Unit test covers: no-trigger tick, Fiesta-only tick, Piper-only tick, both-on-same-tick, already-fired-no-double
- [ ] Integration test with mock TesterInvite + mock PushEvent

## Blocked-by

None. Depends on TesterInvite table (already exists per D-28 POR-773 merge). Depends on Piper DX evidence-emission surface, but v1 of this watcher uses the GitHub tag + Ledger label fallback so it does not block on Piper DX repo work.

## Size / Band

M / assurance (new cron route, new model, new migration, new watcher pattern that is reusable for future milestone triggers — this is the first Ledger-native milestone watcher).

## Discovered by

C9 memo 2026.09.10 Amendment 3 requested a Ledger-milestone reminder tied to first-of Fiesta UAT / Piper DX SaMD. Requested 2026.09.10, filed 2026.09.13.

## Verification

Ships when the cron route builds, the migration applies clean, unit + integration tests pass, and a smoke test setting TesterInvite.state to REVIEW_COMPLETE on a fixture Fiesta row triggers the reconvene ticket auto-file within one cron tick.

Attachments

Loading attachments…

Comments

Loading comments…