Skip to content

Project Delivery projection

Project Delivery answers "how is the project actually going?" at checkpoint granularity, and it is built so that a good-looking answer cannot hide a weak one.

Like the Board it is a read-only fold with its own watermark. Unlike the Board it is not about individual tickets: each row is a delivery checkpoint.

The problem it solves

Status reporting drifts optimistic. A row says "done", and three separate things are quietly conflated: the work was merged, the work was verified, and the evidence for that verification is still valid.

Project Delivery keeps them apart by carrying two states per row plus explicit reasons.

Headline state versus underlying maturity

Field Values
headline_state planned, in_progress, ready_to_land, merged, verified, released, blocked, done
underlying_maturity planned, in_progress, ready_to_land, merged, verified, released

blocked and done exist only as headline states. underlying_maturity cannot be blocked, so a blocked row still tells you how far the work genuinely got. A row that reads done still exposes the maturity that earned it.

Every row states its own trust

These fields are required on every row. They are the reason this projection does not overclaim. The values are the exact strings you will read in the JSON output; the right-hand column says what each one means:

Field Values Reads as
criteria {proven, declared} Proof coverage as a fraction. Anything not proven is excluded from proven and named in the reasons
freshness fresh, stale, STATE_UNKNOWN Whether the view has caught up. STATE_UNKNOWN means "cannot be established" — never "fine"
confidence development_degraded, disaster_safe, STATE_UNKNOWN How far the environment itself can be trusted: development_degraded is a development environment making no data-safety promise
health CP3_D_NOT_PROVEN, CURRENT, STATE_UNKNOWN Whether the disaster-recovery checkpoint has been proven; CP3_D_NOT_PROVEN means it has not
durability CP3_D_NOT_PROVEN, CP3_D_PROVEN, STATE_UNKNOWN Whether writes are proven to survive losing this host, rather than assumed to
recovery EXTERNAL_FAILURE_DOMAIN_UNPROVEN / _PROVEN, STATE_UNKNOWN Whether a standby outside this machine's failure domain has been proven
data_class RECONSTRUCTIBLE_ONLY, DISASTER_SAFE_CTOWER_ENGINEERING, STATE_UNKNOWN What may be stored here. RECONSTRUCTIBLE_ONLY means only data you could rebuild from elsewhere
derivation_reasons at least one string Why this row says what it says

derivation_reasons has minItems: 1. A row cannot assert a state without giving a reason for it.

At this revision the only environments that exist are development and test fixtures, and they report CP3_D_NOT_PROVEN with development_degraded — durability unproven, environment not to be trusted with real work. That is the honest reading, not a defect.

Freshness and rebuild

Every row carries how far the underlying record had got when it was folded (source_record_position, projection_record_position), when that happened (reconciled_at), when the answer goes stale (freshness_due_at), a digest of the row's meaning (projection_semantic_digest), and which rebuild produced it (rebuild_generation).

The projection is disposable: rows can be deleted and deterministically rebuilt from the record. Rebuilding at the same source watermark must reproduce byte-equivalent semantic rows and the same derivation reasons — that is what projection_semantic_digest is for. A projection you cannot rebuild is a second source of truth, and ctower does not allow one.

SPEC.md requires that expiry, revocation, a dependency-digest change, rollback, an incident, or a superseding outcome remove exactly the conditions that depended on invalidated proof before a row may keep saying done. None of those invalidation sources exists at this revision — only candidate-digest invalidation does, and it names evidence and verdict IDs rather than rows. Doing it slot by slot is part of the typed evidence slots rule, which is specified and not built.

Reading it

ctl --base-url http://127.0.0.1:8080 project delivery query ctower --output json

--output text (the default) renders a compact CLI projection; --output json emits the deterministic structured view. The operation is GET /v1/projects/{project_key}/delivery, requires an authenticated principal, and project_key currently accepts only ctower.

Each HTTP row carries qualifying_stage_slots. A slot has slot_key, state, assigned_seat, and signing_seat. assigned_seat is a closed tagged union: an assigned value is exactly {"state":"assigned","seat":{...}}, while an unassigned value is exactly {"state":"unassigned"} and cannot carry seat. The nested seat has seat_key, seat_label, and the pinned catalog_revision (catalog_key, positive revision, and content_digest). signing_seat has the same seat shape or is null.

Compact output follows every checkpoint with one line per qualifying slot:

slot=<slot-key> state=<filled|unfilled|unknown> assigned=<label>[<seat-key>]@<catalog-key>@<revision>|unassigned signed=<label>[<seat-key>]@<catalog-key>@<revision>|-

The assigned text follows assigned_seat.state; it never infers assignment from the presence of a seat. The signed dash represents a null signing seat.

If the projection cannot serve a trustworthy answer it refuses with project-delivery-unavailable rather than returning a stale row.

Implementation status

The read-only contract, the strict schema, and the CLI text/JSON projection are implemented. This evidence is available through the command line only. A product browser view is planned for a later increment.

  • Board lanes — the ticket-granularity projection.
  • Proof — where criteria.proven comes from.
  • Delivery state — the current capability matrix in the engineering record.