Skip to main content

Finality and human sign-off

A paired session produces messages, handoffs, notes, and artifacts. At some point the work is done - and that judgment deserves to be recorded, not just declared in chat. In ContextRelay, finality is the moment a piece of work is marked complete. It is written into the shared ledger as a durable entry, so the "we're finished" decision is auditable alongside everything that led up to it.

This page explains the why (finality is the ship decision, so it stays human-gated by default) and the how (the propose_final tool, the /contextrelay:finalize command, and the ctxrelay finalize CLI that controls the mode).

What finality is - and is not

Finality records a judgment that work is complete. It does not push, merge, publish, or otherwise touch git. Marking work final and shipping it are separate steps: git writes remain the coordinator's job, and outward actions stay read-only by default. Finality is the signal; the human (or the coordinator, under policy) still performs the actual release.

What gets recorded

When an agent proposes finality, ContextRelay appends a finality entry to the ledger for the current session. The proposal carries:

  • summary - what is complete, in one or two concise sentences (required).
  • evidence - the concrete proof of completeness: checks that passed, files touched, commands run, or ledger entries that back the claim (required).
  • remaining_risk - any meaningful unverified risk, or a note that none is known (optional).
  • handles_handoff_id - optionally, the handoff id (or ids) this finality resolves (optional; accepts a single id or a list).

The recorded entry is visible everywhere session state is surfaced: the task_state and session_info tools both report finality state, and the browser Command Deck timeline shows the proposal in context. When a finality entry resolves a tracked handoff, that handoff's task lane flips to a finalized status with the evidence attached, so the board reflects the close-out.

The default: manual finality (human-gated)

By default, finality is manual. This corresponds to autonomy.autoFinalize = false in .contextrelay/config.json. In manual mode, when an agent calls propose_final, ContextRelay records a finality proposal that awaits explicit human acceptance - the agent does not get to declare the work shipped on its own.

Why human-gated by default

Finality is the ship decision - the last mile where "looks done" becomes "is done." Keeping it manual keeps a human in the loop for that final call, consistent with ContextRelay's read-only-by-default safety stance. An agent can assemble the case for completion and present the evidence; a person decides whether to accept it.

There are two ways an agent records a finality proposal:

  • The propose_final MCP tool - available to both Claude and Codex. The agent calls it directly with the summary, evidence, and optional fields above.
  • The /contextrelay:finalize command (Claude) - a guided wrapper. It first calls session_info; if there is an active handoff or a backup agent still in flight, it reports the blocker instead of proposing finality. Otherwise it gathers recent ledger evidence with read_context, calls propose_final, and reports whether finality was recorded automatically or needs human acceptance. It does not modify any files.

Where blockers show up

Finality is not just a rubber stamp. The task_state tool returns finality blockers - most importantly, open handoffs that the proposal does not explicitly resolve. A clean close-out means there is no work still owed to the other agent. The browser Command Deck surfaces the same picture (the finality entry and the task lanes), but the viewer is strictly for inspection: it cannot send agent messages, approve tool calls, publish, or mutate git state. Acceptance happens out-of-band by a human acting on what the ledger shows; the viewer is a window, not a control panel.

Auto mode: opt-in automatic finality

If you want an agent to be able to record finality without waiting for a human - for example, in a tightly-scoped autonomous run where "checks pass and the ledger is clean" is a sufficient bar - you can switch finality to auto:

# Allow Claude to record finality automatically when criteria are met
ctxrelay finalize auto

# Revert to manual: proposals require human acceptance
ctxrelay finalize manual

# Show the current mode
ctxrelay finalize status

ctxrelay finalize auto sets autonomy.autoFinalize = true. In auto mode, a propose_final call records a finality decision (an accepted finalization) rather than a pending proposal - but only when the completion criteria hold.

Auto mode still fails closed on open work

Even with autoFinalize = true, auto-finalization is blocked while there is an active handoff or a backup agent still in flight. In that situation ContextRelay does not silently finalize: it records the proposal for human review and reports the blocking reason. Auto mode removes the human from the clean-completion path; it does not let an agent declare victory while work is still owed elsewhere.

The same toggle is available from the native dashboard. In the TUI (ctxrelay tui, or just ctxrelay), press f to toggle auto-finality, and the Finality metric reflects the current mode. ctxrelay status (and --json) also report the finality state for the session.

Binaries and package

The CLI is published as @proofofwork-agency/contextrelay. The commands contextrelay, ctxrelay, and context-relay are interchangeable - this page uses ctxrelay.

For most sessions, the manual default is the right one:

  1. The implementing agent finishes and verifies (checks pass, diffs reviewed).
  2. It calls /contextrelay:finalize (Claude) or propose_final (either agent), attaching real evidence and naming any handoffs it resolves via handles_handoff_id.
  3. ContextRelay records the proposal and surfaces any blockers (open handoffs, backup in flight) in task_state.
  4. A human reads the proposal and its evidence in the ledger or the Command Deck, then accepts it and performs the actual ship step (the coordinator handles git).

Reserve ctxrelay finalize auto for runs where you have explicitly decided that automatic close-out on clean completion is acceptable, and switch back to ctxrelay finalize manual when you want the human gate restored.

Next steps