The outcome gate and evidence
kanman never takes the executor's word for it. The outcome gate proves each story against its acceptance spec, and the evidence pack shows the proof.
Coding agents are good at saying they are done. The outcome gate is how kanman checks that they are. The executor never declares a story finished: kanman runs the gates itself, on infrastructure the executor does not control, and moves the story only when the gates pass. Everything it checked ends up in the evidence pack on the pull request.
The gates
Each gated move of a story has its own checks:
| Move | Gates |
|---|---|
| Refining to Ready | spec_red, spec_honest: the acceptance spec exists on the protected path, runs, currently fails, and does not mock its own target |
| In Progress to Review | diff_guard, policy_diff, spec_green_ephemeral: no executor commit touched the acceptance spec, the diff respects denied paths and size limits, and the spec passes against a freshly provisioned environment |
| Review to Done | ci, reviewer, clean_room: CI is green, the reviewer run passed every criterion, and the spec passes on a clean-room checkout with the proof artifacts stored. Then the pull request is mergeable, or merged automatically if the policy allows it |
New commits on the pull request during Review are checked again; they never move the story on their own.
kanman performs every gated move itself. When someone moves a story into Ready, on the team page or in the tracker, kanman places it in Refining first and runs the Ready gate; the card shows Checking the acceptance spec until the spec fails the way it should. Moves back to Ready from later columns are direct, because those stories already passed.
On your git host, kanman reports the result as the commit status kanman/outcome-gate. Make it a required status check in your branch protection so a pull request cannot be merged before the gate passed.
Red before Ready
A spec that already passes before any code is written does not test the new behaviour. So when kanman authors the spec from the story’s Demonstrate block, the story stays in Refining unless the spec runs and fails. The story page then shows Spec: red (expected), kanman commits the spec to the branch kanman/spec/<KEY>, and the story moves to Ready.
If kanman cannot write a working spec, it tries once more with the gate output. If that fails too, a Check failed decision asks you to improve the Demonstrate step and retry, or to keep the story in Refining.
Honest specs
A spec must test the real thing. A static check rejects specs that mock their own target, for example with route interception, MSW, nock or module mocks, before the spec ever runs. Mocks of third-party services are fine. The story stays in Refining with a reason such as Spec mocks its own target: route interception of /api/invoices.
The diff guard
The acceptance spec lives on a protected path, .kanman/acceptance/<KEY>/. Executors may never change it: the path is denied for every preset and cannot be allowed. If an implementation commit touches it anyway, or the spec folder no longer matches the version approved at Ready, the run stops with Diff guard: the acceptance spec was changed by the implementation, no pull request is marked mergeable, and the decision inbox offers Revert spec changes and retry or Reject. kanman also stamps commit authorship itself, so it knows which commits came from the executor.
Clean-room check
The final check never runs in the executor’s workspace. kanman checks out the branch fresh, restores the approved spec, provisions a new environment as your acceptance manifest describes, and runs the spec there. Traces, screenshots, videos or apply logs are stored as proof. A green spec without stored artifacts does not count.
Gate reference
| Gate id | Checks |
|---|---|
spec_red |
The spec fails before the change |
spec_honest |
The spec does not mock its own target |
spec_green_ephemeral |
The spec passes in a freshly provisioned environment |
diff_guard |
No executor commit touched .kanman/acceptance/, and the spec is unchanged since Ready |
policy_diff |
Denied paths, max_changed_files and max_changed_lines on the final diff |
clean_room |
The spec passes on a clean checkout in a fresh environment, with proof artifacts |
ci |
Your CI pipeline is green. While CI is still running, kanman waits |
reviewer |
An independent reviewer run checked every criterion against the diff |
verify_local |
The local verify command from .kanman/verify.json passed. Not run yet, see that page |
Each gate result has a verdict (pass, fail, error or skipped), a reason and links to its artifacts. Results are logged as gate_passed or gate_failed in the audit log.
The second signal: the reviewer run
Next to the spec, a reviewer run checks each acceptance criterion against the diff, with file and line references, and flags scope creep. A criterion without a code reference does not pass. The reviewer uses a model from the other vendor (a Codex model when Claude Code wrote the code, and the other way round), so the code is never reviewed by the model that wrote it. Its result per criterion is written back to the story.
When a gate fails
- kanman starts one automatic rework attempt and gives the executor the gate output and the reviewer’s findings (
rework_attempts). - If that attempt fails too, the story goes to the decision inbox as Verification failed, with kanman’s recommendation and options.
- Each gate has a failure budget (
gate_failure_budget, 3 by default), counted over all runs of the story. When it is used up, the run is parked with Repeated gate failure and you decide what happens next. - A failed diff guard is never reworked automatically; it always asks.
Problems of the test environment itself (clone, provisioning, ready check, setup) are retried quietly twice and do not count against the failure budget.
A failed gate never turns into an endless loop.
The evidence pack
When the Review gates pass, kanman writes the evidence pack. It is stored on the run, shown on the run page, and posted once as a pull request comment and once as a tracker comment. It contains:
- the plan and the approaches considered
- the acceptance criteria, each passed, failed or unknown, with file and line references
- the gate results with their reasons and links to traces, screenshots and logs
- test counts and the CI link
- cost against budget, duration and number of attempts
- whether outcome proof exists
The run page also lists the policy decisions taken during the run.
A shortened example as it appears on a pull request (the comment shows a status mark in front of each line):
Evidence: SBX-12 (run-7f3k)
The acceptance spec passed in a clean environment.
Plan
Add GET /invoices/export with a date-range filter and a CSV serializer.
Acceptance criteria
- Passed ac-1 Given a customer with 3 invoices in March ... (src/invoices/export.ts:42)
- Passed ac-2 Given ... (src/invoices/export.ts:61, src/invoices/csv.ts:12)
Checks
- Passed spec_green_ephemeral: Spec passed in a fresh environment [1]
- Passed diff_guard: The implementation did not touch the acceptance spec
- Passed policy_diff: The change stays within the team policy
- Passed ci: CI passed
- Passed reviewer: All 2 criteria passed review
- Passed clean_room: Spec passed on a clean checkout [1] [2]
Tests: 12 passed, 0 failed
CI: success
Cost: €0.84 / €2.00 · Duration: 11 min · Attempts: 1
The run page shows the evidence pack in your language. The comments on the pull request and in the tracker are written in English for now.
Repositories without a manifest
Without an acceptance manifest there is no spec to run. Only the Trial preset allows this, so you can start a pilot before adding a manifest. kanman then falls back to CI, the test plan and the reviewer run, records the spec gates as skipped, and the evidence pack says plainly: No acceptance spec was run, so the outcome is not proven. Review the change carefully. With any other preset, a story in a repository without a manifest stays in Refining. Add a manifest to get the full gate: see Add an acceptance manifest to your repo.
Tip
The pilot sandbox repository ships with a manifest, so you can watch the full gate in the quickstart before you set it up on your own code.
Related
Last updated: January 1, 0001
Open kanman