Exit condition states
An exit condition has one state and a set of properties. The state is what the auditor has decided. The properties describe the condition itself and modify how the state is treated by /akka:ship and the verdict.
The three states
The state answers one question: has the auditor decided this condition, and if so, what did it decide?
| State | Meaning |
|---|---|
|
The auditor has not decided the condition. The condition carries a reason that says what has to happen for it to leave |
|
The auditor ran and the condition passed. |
|
The auditor ran and the condition failed. A prohibition with no auditor mapped to it is also |
Every exit condition is in exactly one of these three states. There are no others.
The three properties
Each property is set on the condition, alongside its state. Properties are not states. A property answers a different question from "what did the auditor decide."
| Property | Question it answers |
|---|---|
|
Who guarantees this condition, and who is allowed to change it. Values: |
|
Has a person accepted the condition in its current state to permit shipping? A waiver is null, or a record with an accepter, a reason, and an expiration. A waiver does not change the state — the auditor still runs and the state still shows what it decided — but a waiver permits shipping despite |
|
Does this condition apply to this project? Values: |
An akka-intrinsic condition is guaranteed by the Akka platform. Its auditor is trivial: it returns green immediately with the reason platform-guaranteed. This preserves the rule that every green is the result of an auditor run. Intrinsic conditions are annotated [akka] on the manifest so a reader sees which conditions the platform covers and which the project covers.
Reasons on open
An open condition carries a reason that says what has to happen. The reason is one of two values.
| Reason | Meaning |
|---|---|
|
A person working on this project can move the condition forward. The reason payload names the specific action — record a receipt, approve a proposed command, answer a spec question, decide a waiver or tier promotion, or confirm whether the condition applies. The manifest shows the action inline. |
|
The condition cannot be resolved by anyone working on this project. The fix is elsewhere. Two cases fall under this reason: a required tool is missing on this machine (install the tool, or run the check on a machine that has it), or the organization’s policy lists a requirement without attaching a way to verify it (contact the policy owner and ask them to add a verification method). The manifest shows which case and who to contact. |
Two reasons are enough. The specific action lives in the reason payload, next to the condition on the manifest, so the developer can read what to do without looking up a code.
The state machine
Transitions are triggered by named events. Nothing else moves the state.
activate
────────► open ──── auditor passes ──► green
└───── auditor fails ───► red
green ─── project changes ──► green (stale)
green (stale) ── auditor re-runs ──► green or red
red ── re-evaluate ──► open ──► green or red
Rendered as a state diagram:
stateDiagram-v2
[*] --> open: activate
open --> green: auditor passes
open --> red: auditor fails
green --> green_stale: project changes
green_stale --> green: auditor re-runs, passes
green_stale --> red: auditor re-runs, fails
red --> open: re-evaluate
Notes on the transitions:
-
An intrinsic condition activates directly to
greenbecause its trivial auditor runs on activation. -
green_staleisgreenwith astale = trueoverlay, not a fourth state. See Drift and re-evaluation. -
redreturns toopenwhen the developer requests a re-evaluation (for example, after a fix). The re-evaluation runs the auditor, which then goes togreenorred. -
Removing a condition from scope sets
applicable = false. The condition stays visible with that marker; the state does not change.
Drift and re-evaluation
When an auditor returns green, the CLI records a verified_at timestamp on the condition. Before /akka:ship proceeds, the CLI computes the most recent modification time in the project tree — every file under the project root except .akka/, .git/, build outputs, and paths matched by .gitignore — and compares it to each green condition’s verified_at.
If any file has changed after the condition was verified, the condition is marked stale. The manifest still displays it as green (the previous verdict is honest — it was green at that time), and the manifest adds a stale — auditor must re-run before ship annotation.
/akka:ship refuses while any condition is stale. The refusal lists the stale conditions and offers to run their auditors. Re-running an auditor updates verified_at on success or flips the state to red on failure.
Drift detection is coarse in this release: any change anywhere in the project stales every green condition. A future release may add per-condition drift scopes so that unrelated changes do not stale a condition whose inputs did not change.
Waivers and the ship rule
A waiver is a property, not a state. When a waiver is present:
-
The condition’s state is whatever the auditor decided (
open,green, orred). -
The manifest shows the state as usual, plus the waiver record (
~ waived by <accepter> until <date> — <reason>). -
/akka:shiptreats aredoropencondition covered by an effective (unexpired) waiver as satisfied for the purpose of the ship verdict. -
When the waiver expires, the CLI removes the waiver record. The condition returns to being blocking in whatever state it is in.
The ship rule under this model:
READY_TO_SHIP requires every applicable condition to be either (a) green and not stale, or (b) covered by an effective waiver.
Any other combination is NOT_READY.
One worked example per state
open — needs-user-action
FUNC-PAYMENT-PROCESSORS was activated by /akka:specify, and the spec contained an unanswered [CLARIFY-3] marker asking which payment processors the system supports. The auditor cannot decide the condition without the answer.
Manifest line:
○ FUNC-PAYMENT-PROCESSORS needs your action
→ Your spec has an open question. Open the spec, find [CLARIFY-3], and
write the answer. The check will re-run automatically.
open — blocked-outside-project
CONTENT-LANGUAGE uses the introspective content-vale check. The developer’s machine does not have Vale installed.
Manifest line:
○ CONTENT-LANGUAGE blocked outside this project
→ This check needs Vale installed on this machine. Install Vale (see
/akka:setup#vale) or run this check on a machine that has it.
green
OPS-COMPILES is bound to the built-in maven-compile check. mvn compile returned zero. The condition is green with verified_at set to the run timestamp.
Manifest line:
✓ OPS-COMPILES green
green (intrinsic)
REL-OBSERVABILITY has provenance: akka-intrinsic. Its trivial auditor returned green on activation with reason platform-guaranteed.
Manifest line:
✓ REL-OBSERVABILITY [akka] green — platform-guaranteed
green (stale)
OPS-COMPILES was green at 09:12. At 09:31 the developer edited a source file. latest_change_at is now 09:31, which is after verified_at. The condition is green (stale).
Manifest line:
✓ OPS-COMPILES green · stale — auditor must re-run before ship
/akka:ship refuses. Re-running the auditor either restores green with a fresh verified_at or flips the condition to red.
red
SEC-SECRETS is bound to the built-in git-secrets check. The check found a credential-like string in a committed file. The condition is red.
Manifest line:
✗ SEC-SECRETS red — git-secrets found a credential-like
string in src/config/dev.yaml
red with waiver
The developer opened a 14-day waiver on COV-UNIT-THRESHOLD during a migration. A reviewer accepted the waiver. The auditor still runs and still reports the coverage number.
Manifest line:
✗ COV-UNIT-THRESHOLD red — coverage 71%, threshold 80%
~ waived by lead-eng until 2026-08-14 — migration
The ship verdict counts this condition as satisfied while the waiver is active.
Inapplicable
INFRA-K8S-NETPOL is a Kubernetes-specific condition. The project does not deploy to Kubernetes. The developer marked the condition applicable = false through /akka:scope.
Manifest line:
— INFRA-K8S-NETPOL not applicable — project does not deploy to
Kubernetes
The condition stays visible on the manifest so a reviewer can see what was excluded and why. It does not count toward the ship verdict.
Verdicts
/akka:conform and /akka:ship return one of two verdicts.
| Verdict | Meaning |
|---|---|
|
Every applicable, in-scope condition is |
|
Anything else. The manifest lists the conditions blocking the ship, each with its state, its reason (for |
/akka:ship releases only when the verdict is READY_TO_SHIP in enforced mode. In à la carte mode the ship proceeds anyway, with the verdict recorded as an override on the conformance receipt.
Ship tiers
Every condition is declared at one of three ship tiers. The tier names when the condition must be green, orthogonal to its state (open/green/red), its kind (introspective/provisioned/delegated), and its properties (provenance/applicable/waiver).
| Tier | Meaning |
|---|---|
|
Must be green in the developer’s own loop, before any form of |
|
Must be green before the change is opened for team review. Runs in minutes, may reach shared systems, does not touch production. SAST, coverage thresholds, integration tests, provisioned-auditor currency (harness lock matches policy version). |
|
Must be green before the artifact reaches production. Expensive or external — DAST, penetration-test attestation, security review sign-off, DPIA, live-service probes, delegated attestations with fresh receipts. |
Tiers are monotonic. A release-time ship requires every review-time condition to be green, which requires every author-time condition to be green. Requesting a higher tier adds gates; it never skips lower ones.
Author-time is a precondition, not a ship target. You never invoke /akka:ship author. Author-time conditions are what every ship, review or release, requires before it runs its higher-tier auditors. If any author-time condition is red, open, or stale, /akka:ship refuses with an author-tier precondition failure regardless of the requested target. An active waiver satisfies the precondition the same way it satisfies any other condition. A waiver is a recorded, time-bound acceptance, not a bypass.
The precondition is also what makes the tiering cheap. The author tier is evaluated first, on its own. In Enforced mode a ship that fails it stops there. The review-tier and release-tier auditors do not run, so you do not wait on a scan whose result cannot change the outcome. The verdict covers the author tier only. The higher tiers were skipped rather than failed, so no status is reported for them.
À la carte mode ships regardless of the verdict, so stopping early would withhold information you are about to override. There the precondition is reported and the run continues through every in-scope tier.
Assigning a tier
Every condition ships with a default tier:
-
Prohibitions default to
author— they must always hold, so they run every commit. -
Attested (delegated) conditions default to
release— the receipt is external and expensive. -
Every other condition defaults to
review.
The default library overrides these defaults on cheap, critical conditions (compile, tests, dependency resolution, the two locked process-integrity gates) by declaring them at author explicitly. The organization policy may override any condition’s tier in a single place; individual developers do not re-tier conditions.
Ship verbs
/akka:ship requires an explicit target and fails when called without one. Two targets are accepted:
| Command | What it runs |
|---|---|
|
Runs the auditors for every applicable exit condition tagged |
|
Runs the auditors for every applicable exit condition at any tier ( |
On success, /akka:ship runs the ship steps the organization declared in its policy for that target — for example, pushing the branch and opening a pull request on a review ship, or deploying to production on a release ship. Ship steps are org-specific; /akka:ship verifies the exit conditions and then runs whatever the policy says.
Every condition remains visible on the manifest at every tier. A condition out of scope for the current ship target is displayed but not counted in the verdict.