Skip to main content

Practice review pipeline

A practice review passes through four stages, in this order: Occasion, Capture, Observation, Delivery. Each stage can stop independently and records a different reason.

Use the stage that stopped the review to decide what to fix. Do not translate one stage's reason into another stage's vocabulary.

Choose the page for your change

This page owns the runtime sequence and the seams between its stages. Start elsewhere when your change belongs to one contract:

You are changingStart with
A source, its capture states, retention, or permitted usesArtifact-source contract
A practice's criteria, occasion, or evidence requirementsPractice catalogue
Observation or feedback persistence, identity, or API projectionsPractice review data model
Product and model-facing terminologyPractice feedback language
Evaluation joins, claim currentness, or stale resultsEvaluation provenance
An exact enum meaning after you know the stagePractice review glossary

Return here when a change crosses two stages. That boundary is usually where an apparently harmless shortcut can turn a capture failure into a claim about someone's work, or a delivery choice into a missing measurement.

Where a review can stop

Each stage owns exactly one refusal vocabulary, and they are not interchangeable. Answering a question from one stage in another stage's words is the failure this table exists to prevent.

StageRefusal vocabularyLands inMeans
OccasionSignalStateReasonartifact_signal.state_reasonWe chose not to look, or not yet
CaptureSourceAbsenceReason per source, SourceReadinessReason per practice checkagent_job.evidence_snapshot (the manifest), agent_job.review_readinessWe could not look
ObservationPresence.INCONCLUSIVEobservation.presenceWe looked and could not tell
DeliveryFeedbackSuppressionReasonfeedback.suppression_reasonWe know, and chose to stay quiet

The four are not ranked and do not substitute for one another. "We could not look" is deliberately not representable as a Presence value, because a Presence is a claim about the developer's work and a missing source is a fact about the instrument — which is exactly what lets the whole observation table be read as behaviour. In the other direction, "we chose to stay quiet" must never be recorded as an absent observation: requiring human approval is a release decision, and the measurement still happened. When you add a reason code, the first question is which of these four columns it belongs in. If the honest answer is "two of them", the reason is really two reasons.

Occasion

An occasion is one named signal on one revision of one artifact. It is recorded in artifact_signal before admission, so a review that does not start still has an auditable outcome.

The ledger distinguishes TRIGGERED, terminal SUPPRESSED, retryable PENDING, and retired LAPSED rows. Terminal rows never reopen; pending rows are re-offered until their reason clears or their deadline passes. Retryability belongs to the reason, not to the caller. The canonical reason list, provenance mapping, and UI labels are in How a review was occasioned. Operator remediation is in When a workspace goes quiet.

The occurrence ledger has no pruning job; capacity planning must treat it as append-only. Retry and lapse thresholds are documented in the admin guide.

Capture

Capture stages every source the contract says applies to the artifact kind under review — all of them, every time. Practice bindings decide which practices may run, never which files reach the sandbox; there is no per-run source selection. What a capture then reports about each source (available, not collected, unavailable, redacted, errored — with a typed reason) and how a practice's stances turn those reports into a readiness decision is the whole subject of the artifact-source contract, which is versioned and validated at startup.

Do not restate that contract here. The one fact this page owns is where the readiness verdict lands: agent_job.review_readiness, its own jsonb column, split out from evidence_snapshot because a snapshot carries one entry per staged file and a TOASTed jsonb has no partial read.

Observation

An observation is one practice's answer about one piece of work, written once and immutable. It carries a PresencePRESENT, ABSENT, NOT_APPLICABLE or INCONCLUSIVE — and, only when the presence carries valence, an assessment. Admission removes assessment from an INCONCLUSIVE observation, so a detector that hedges cannot attach an unearned strength to the series.

The glossary defines what each presence claims and what NOT_APPLICABLE has to carry to be allowed to claim it — see Outcome states. Two facts belong to this stage rather than to that vocabulary. INCONCLUSIVE is not "we could not look": a missing, errored or governance-blocked source produces a readiness decision in the Capture stage and no observation at all. And the NOT_APPLICABLE grounding rule is enforced twice, in the in-sandbox normalizer and again at server-side delivery, for the same reason the citation and recorded-search rules are — the sandbox guard runs inside the thing it is checking.

Identity is assigned at persistence, not by the detector: an occurrence key dedupes within a run (insertIfAbsent, ON CONFLICT DO NOTHING), and a recurrence key hashes the locus so the same observation is recognisable across runs.

An observation carries evidenceRationale and no advice. evidenceRationale is the measurement's own justification — "the change adds a tax-exempt branch, and no test file in this diff calls total" — and it is the only prose the observation carries anywhere. The detector emits no next step: there is no guidance field on the tool, on the parsed observation, or on the ledger. Advice is a property of a delivery (ADR 0021, ADR 0022), and the stage that decides deliveries is the subject of the next section.

Measurement and intervention

Everything above this line is the system taking a reading. Everything below it is the system deciding to say something to a person. Those are separate turns in one agent session. An authenticated server callback admits the observations between them, so composition retains context but can use only the persisted projection. Keeping that boundary is why one fact can read three different ways.

A measurement is falsifiable. "This change adds a tax-exempt branch, and nothing in the change tests it" is either true or false, and you check it by opening the file. It says nothing about what anybody should do, and it remains unedited for as long as it is retained, so later readings can be compared with it.

An intervention must stay grounded, but grounding alone does not make it useful. "Write the assertion that distinguishes the new branch before you write the branch" can be supported by the observation and still be badly timed, repetitive, or irrelevant to the recipient. Composition therefore considers the audience, surface, history, and current conversation as well as the evidence.

The line the code draws, stated once so it fits in a prompt: if you can check it by reading the artifact, it is measurement; if you can only check it by watching what the person does next, it is intervention.

They are separated because the good version of each ruins the other. A measurement authored as advice starts bending toward whatever makes good advice — the observation you can write a nice tip about gets reported, the one you cannot gets quietly dropped. It also bends toward fault, because a next step presupposes something to step away from: the three answers that assert nothing is wrong (a strength, a practice with no subject here, a question the evidence left open) are the ones that suffer for it. And advice written at the moment of measurement can only ever be about the one thing just measured: it cannot know that this is the third time, it cannot know the developer was already told, and it cannot decide to stay quiet.

Note where the arrows converge. The model proposes; the server admits. Origin, effective autonomy, channel-specific routing, placement checks, and per-lane caps still apply after composition. Giving the model more context is not a reason to relax these gates; it gives them more proposals to assess.

Composition inputs and outputs

After Java admits observations, the runner resumes the same model session with the composition prompt. The measurement tool is closed, and feedback may bind only to the durable observations returned by admission. The resumed session retains artifact context without weakening the server boundary.

The composer receives the current admitted observations, bounded recent observation and feedback history, unread prepared feedback, per-locus deltas, practice definitions, and the enabled lanes with their limits. The exact files belong to the agent workspace ABI, not this guide.

Three constraints shape how that context may be used:

  • Only pull-request and issue handlers enable composition, and not for backfills. Document and conversation reviews record observations but compose no feedback.
  • History is bounded and therefore supports recurrence claims, never claims that something has never happened before.
  • Deltas describe a locus on one artifact as NEW, RECURRING, UNCHANGED, or RESOLVED. Cross-artifact habits require distinct-artifact evidence from history; an unobserved prior strength is not RESOLVED.

Accepted tool calls update the composition output incrementally. The server parses proposed units and applies all delivery gates; the model never writes the feedback ledger directly.

Channels are distinct interventions

The channel contract is defined in the practice review glossary. The composition turn must not render one advice string three ways:

  • IN_CONTEXT is public task-level feedback about the current work. It binds to a current admitted observation and uses either server-resolved DIFF placement or artifact-level placement. History alone cannot support a public claim.
  • IN_APP is private process-level feedback about a pattern across several pieces of the recipient's work.
  • IN_CHAT is private context for a later mentor turn. It prepares situation, capability, evidenceSummary, and inConversationSignal as notes, not dialogue. The mentor receives the original evidence and live conversation, then may adapt, defer, or discard the note.

An issue has no diff and therefore permits only artifact-level in-context placement. Pull requests permit both placements. Per-lane limits are supplied in composition configuration, not fixed in this guide.

What the composer is not allowed to do

The tool schema and server admission refuse the same four things. Validation exists on both sides of the sandbox boundary because a guard the constrained party can skip is advice, not a boundary. An invalid unit is dropped and logged without failing the completed measurement.

  • No verdict fields. report_feedback takes no presence, assessment, severity or confidence. An intervention that could carry a verdict would eventually be read back as one.
  • No model-authored path or line number. placement.kind: "DIFF" names an admitted { observationId, citationIndex }, and the server resolves the file, side, and line from that observation's citation. placement.kind: "ARTIFACT" carries no coordinates and stays in the summary. Both forms require a current admitted observation of the same practice.
  • No invented supersession target. action: "SUPERSEDE" must name a threadKey that was staged in prepared.json.
  • No second unit on the same lane about the same practice. One unit per (channel, practiceSlug); the rest are dropped.

Staying quiet is the fourth action, not a gap: action: "WITHHOLD" with NO_MATERIAL_CHANGE, ALREADY_SAID or BELOW_BAR. That enum is deliberately not FeedbackSuppressionReason — a withhold reason is the composer's own judgement, and FeedbackSuppressionReason is a database check constraint listing reasons the server refused. Merging them would make "we had nothing worth saying" indistinguishable from "delivery policy refused it".

Supersession preserves read feedback

A thread identifies one continuing topic for one recipient and channel. Longitudinal lanes key it by practice; in-context feedback keys it by artifact. New feedback supersedes a queued unread row, continues an already-read row without changing it, or starts a new thread. The ledger permits a history of rows but at most one live PREPARED row per thread. Provider comments differ only because editing in place makes the old text no longer visible, so the ledger retires it.

When composition does not run

A composition failure does not invalidate admitted observations. An empty result is distinct from a turn that never ran. In-context feedback may fall back to the observation's evidenceRationale and the practice's whyItMatters, but invents no next step. Longitudinal lanes have no content fallback; per-lane preparation marks let a sweeper retry only turns that did not run.

Delivery and human approval

Composition creates immutable feedback units before release authority is applied. A composer WITHHOLD means there was nothing useful to say and creates no feedback row. A policy refusal is instead persisted as SUPPRESSED with a server-owned reason. These outcomes must not be conflated with a human rejection.

Practice autonomy is resolved as practice.autonomy ?? area.autonomy ?? workspace default ?? HUMAN_APPROVAL:

  • OFF prevents the review, so no intervention is composed.
  • HUMAN_APPROVAL stores in-context feedback as AWAITING_APPROVAL. A workspace owner or administrator may approve the exact content and target or reject it with an optional structured reason. Approval changes the unit to PREPARED for asynchronous release; rejection records DISCARDED.
  • AUTOMATIC permits the system to prepare new feedback for delivery without that human decision.

Autonomy is only the authority step. PracticeAutonomyPolicy also applies immutable observation provenance. At release, approved feedback rechecks Silent Mode, workspace availability, artifact validity, repository scope and recipient preference, then reconciles a stable proposal marker with the provider before posting. An approval overrides none of those controls. Missing effective autonomy denies automatic delivery.

Existing AWAITING_APPROVAL rows remain proposals when autonomy changes. This prospective behavior prevents a promotion to AUTOMATIC from releasing historical output. Conversely, demoting to HUMAN_APPROVAL keeps measurement running while immediately returning new interventions to supervised release.

FeedbackChannel.IN_APP remains private process-level feedback for the developer concerned. Backfill origin may target only that lane, and the in-app router still refuses a cluster made entirely from backfilled observations. The durable feedback contract owns the complete suppression vocabulary.

Runtime and extension points

The server records an occasion and enqueues an agent_job; a worker claims it, captures evidence, runs the sandbox, admits observations, resumes composition, applies delivery gates, and calls the provider. Runtime roles, transaction boundaries, extension ownership, and change guidance are documented in Practice review runtime.

Reading a real review back

PracticeTraceEntryDTO is the derived answer to "what did every practice make of this piece of work", and it is what the Review activity screen renders for every member. It is derived, never stored: PracticeTraceDeriver walks an ordered precedence list to a single PracticeTraceOutcome.

The trace separates two axes on purpose, and reading it wrongly is the fastest way to misdiagnose a quiet workspace:

  • outcome is about the measurement. REVIEWED, NOT_OCCASIONED, TURNED_OFF, NOT_ASSESSABLE, DORMANT, LAPSED, and so on.
  • observationCount, deliveredCount and withheldReasons are about the intervention.

At HUMAN_APPROVAL, REVIEWED and a positive observation count describe the measurement; the proposal's AWAITING_APPROVAL, PREPARED, DISCARDED, or eventual DELIVERED state describes the intervention. TURNED_OFF remains the explicit OFF outcome and is checked before mechanical review failures. Do not represent a pending human decision as suppression: it is an actionable workflow state.

Operator-facing guidance for reading the same screen is on Practice review; the vocabulary both pages use is defined in the practice review glossary. Persistence concepts and evaluation joins are in Practice review data model and Evaluation provenance.