Data Model: Review-Cycle Verdict Seam Rebuild

Phase 1 output. Field lists below are the measured current shape; changes this mission makes are marked.

Entities

Authoritative verdict — ReviewResult on StatusEvent

Authoritative for which verdict is current and where its content lives.

FieldTypeNote
reviewerstr
verdictstra bare str with a comment, not a Literal — measured. Vocabulary "approved" / "changes_requested"
referencestra review-cycle:// URI. (feedback:// is the deprecated legacy form — cycle.py:264)
feedback_pathstrpointer to the verdict record

No field is added here. The override authority is the existing event-sourced ReviewOverride on the reduced review slot — see below.

Lifecycle: append-only.

review_result is NOT always populated. wp_state.py:183 short-circuits guard_for entirely when ctx.force is set, so --force — exposed on both agent status transition and move-task — produces in_review → approved events with review_result: null. Reproduced. This is a current, ongoing production, not legacy residue, and the FR-001 reader needs a defined behaviour for it.

Events carry verdict: "approved" for decisions that were waivers — today, not only historically. 23 override-worded approved verdicts exist across this repository's own logs; one from 2026-07-27 carries force=false and a reference reading "independent reviewer override". _mt_plan_review_result hard-codes verdict = "approved" for any in_review → {approved,done}.

Verdict record — ReviewCycleArtifact

Authoritative for what the reviewer said. Addressed by the event's pointer, not derived from it.

FieldNote
cycle_numberderivation changes: max(parsed) + 1, not len() + 1
wp_id, mission_slug, reviewer_agent, reviewed_at
verdictremains, but is no longer the authority for current verdict
bodyreviewer prose — exists nowhere on the event
affected_files, reproduction_commandexist nowhere on the event
override_actor, override_reasonread-only — no writer since 2026-07-01. Owned by wp-runtime-state-eviction-01KXWN13's deferred WP10, which is explicitly RETAINED as a migration-window safety net. Out of scope here; a cleanup pass would revert a landed operator decision

Lifecycle: not "written once, never rewritten" — arbiter._persist_in_artifact rewrites a persisted artifact's frontmatter in place today, and IC-09 keeps that path until it is retired. Not durably persisted until committed — a record on disk but absent from the git index is not authoritative for any read path.

Arbiter override

A first-class outcome alongside approval and rejection. The authority is the existing event-sourced ReviewOverride on the reduced review slot — a frozen four-field record (at/actor/wp_id/reason) with a complete predicate, already consumed by the merge gate. ADR 2026-07-19-1 pins this mechanism.

The four representations, with corrected membership:

#RepresentationDisposition
1ReviewOverride on the reduced review slotthe authority — retained
2arbiter_override frontmatter block (arbiter.py:437)retired into (1) by IC-09
3arbiter-override-N.json sidecarsretired into (1) by IC-09
4review_artifact_override_* artifact fieldsread-only, no writer; another mission's deferred WP10 — out of scope

Mandatory: a stated reason. The partition tension is resolved by ADR 2026-08-03-1: review-cycle artifacts become their own REVIEW_CYCLE kind on the COORD partition under coordination topologies, so (1) and the record it annotates share a surface. Under SINGLE_BRANCH / LANES both resolve to PRIMARY, unchanged.

Location resolution

The single derivation from work-package identity to verdict-record directory. Includes slug derivation, which is where the divergence actually lives — see research R5.

Invariant: for a given work package, every read, write, merge-gate and display path resolves one identical directory, through the REVIEW_CYCLE kind — COORD under coordination topologies, PRIMARY otherwise (ADR 2026-08-03-1). A caller-supplied directory is not a substitute for kind resolution on any path. A filename that cannot be resolved unambiguously is refused with a diagnostic, never degraded to the bare work-package id.

Migration behaviour: a pre-ADR mission's records live on the primary surface. A read that finds nothing at COORD falls back to PRIMARY and never reports "no verdict".

Invariants

#InvariantEnforced by
I-1A durably recorded approval exists iff the approval transition completedFR-001, FR-002
I-2A verdict record is never overwritten by a new oneFR-006 (max + 1 + collision refusal)
I-3Two concurrent distinct verdicts produce two records, or one explicit refusal — never silent lossFR-005
I-4An arbiter override is never recorded as an approval, in either storeFR-022, FR-011
I-5Every reader resolves a damaged record to its declared polarity; no safety gate fails openFR-012
I-6A file that is a prior verdict record is refused as feedback, by path or by contentC-002 behaviour floor — this is the #990 control
I-7The event and the artifact never disagree about which verdict is currentFR-001

I-7 needs a vocabulary mapping before it is checkable. The event's vocabulary is "approved" / "changes_requested"; the artifact's is "approved" / "rejected". Stated over two non-comparable value sets, the invariant cannot be evaluated. The mapping is a deliverable of the concern that owns FR-001.

I-6 is called out because FR-004 (repeat feedback must be recordable) creates pressure against it, and the shortest path to FR-004 is to delete it. That path is a constraint violation, not an implementation choice.

State transitions

    (no record)
         │  reviewer rejects
         ▼
   ┌──────────┐  reviewer approves        ┌──────────┐
   │ rejected │ ────────────────────────► │ approved │
   └──────────┘                           └──────────┘
         │                                      ▲
         │  arbiter overrides                   │
         ▼                                      │
   ┌────────────┐   NOT an approval ────────────┘
   │ overridden │   (I-4: distinct outcome, distinct record)
   └────────────┘

Each edge writes one verdict record and one status event, and the two either both land or neither does (I-1). The rejected → rejected self-edge is legal — a recurring defect re-reported — and is exactly the case FR-004 unblocks without breaching I-6.

Failure-state model

The states this mission makes unrepresentable, each currently reachable:

Current reachable stateClosed by
Committed approved record for a work package that never transitionedFR-002
Orphan record after a failed commit, making the identical retry permanently refusedFR-003
Approval transition succeeding on an uncommitted verdict after a process killFR-003
One of two concurrent verdicts silently destroyedFR-005
A new record overwriting a live one across a numbering gapFR-006
Approval over a live rejection with no record written anywhereFR-007
Override recorded as an approval reviewFR-022, FR-011
Neither verdict recordable at all under protected-primary coordFR-013 scope