A real DynoInvoice task, INV-T-11449: the audit purge policy for the control plane, merged on 29 October 2026. Below is the task's record trail, in order, with who did what. The code is in the public repository.
The labels separate Keel's records and checks, the product owner's decisions, and what our agents did. How the agents are organised is how we run DynoInvoice, not part of Keel.
INV-REQ-1378: personal data is kept for 5 years by default; keeping it longer needs a recorded justification per tenant (Singapore's PDPA). In Keel it links back to the paragraph of the requirements document it was taken from.
It is constrained by business rule INV-BR-1708: GST-registered businesses must keep records for at least 5 years. The task also implements INV-REQ-2588: every console operation writes an audit entry (who, what scope, why, the state before) in the same transaction as the change.
INV-AC-3823: an operator's audit purge records itself before it deletes, and never deletes an entry younger than the floor.INV-AC-11490: the floor is never under 5 years. The application takes the largest of the regional retention figures, but at least 5 years, and the database function refuses a cut-off younger than 5 years.Test names carry the criterion key, so each criterion can be traced to the tests that check it.
Before the task existed, the product owner answered three questions. In Keel each is a record with its options and the answer, linked to what it decides.
INV-Q-3028: the floor is the tenant's longest evidence-retention period, never under 5 years; deleting happens only through a deliberate, audited operation.INV-Q-3263: read the figure across regions and take the largest; if any region can't be reached, purge nothing.INV-Q-3267: give each reason for refusing a purge its own error code: the cut-off is younger than the floor, or the floor can't be determined.Working under the owner's delegation, our coordinating agent split the parent task into smaller ones (INV-Q-11446) and wrote the criterion that was missing (INV-Q-11488).
Keel marked the task ready only once its requirement had a criterion that could be checked. A coding agent can only claim a task that Keel has marked ready.
A context pack built from the task's links: the task text, plus the requirements and acceptance criteria it implements. Not the whole project, and not anyone's chat history.
Before asking for review, the coding agent ran the local checks, including the architecture rules and a check that every criterion key has tests, and broke the rule on purpose to confirm the named tests fail.
In round 1, our independent reviewer agent pointed out that the task text didn't settle three things. Our coordinating agent judged them technical rather than product questions, so they didn't go to the owner. Another agent wrote each one up in Keel as a design question with options and a default:
INV-Q-12780: measure retention as a calendar period or a fixed duration? A period, with UTC calendar arithmetic. A fixed duration drifts across leap days.INV-Q-12782: what if the floor can't be read? Fail closed: purge nothing.INV-Q-12784: what if the purge itself fails? Pass the error up unchanged.In our setup, an independent examiner agent checked the answers and the revised task against the owner's rulings, then published them in Keel, which doesn't let the author of a change approve it. Keel closed each question when its answer was published, and revision 3 of the task carried the answers and three more tests. No check was loosened.
The reviewer found the code already followed the answers, so the coding agent only added the three tests. The reviewer passed it, the merge request's CI pipeline was green, and the coding agent merged it on 29 October 2026. Both commits are in DynoInvoice's public mirror on GitHub:
A few of the test names, as they appear in the repository:
fun `INV-AC-3823 the floor is the maximum of the region figures`()
fun `INV-AC-3823 one region without a figure makes the floor undeterminable`()
fun `INV-AC-3823 five years back from 29 February is the last day of February`()
The task moved to done only when Keel's done check passed: every criterion of the requirement covered, each criterion key traced to named tests, and the change inside the task's declared scope. Keel kept the evidence with the task: the CI result, the merge request and the criterion checks. No agent marked it done.
| Product owner | The product rulings and the delegation. Nothing needed during the task. |
| Coordinating agent | Split the work, made the technical calls it was delegated, routed questions. |
| Writing agent | Wrote the questions, answers and task revision. It submits; it never publishes. |
| Independent examiner agent | Checked those against the rulings and published them. |
| Coding agent | Wrote the change, and merged it after review and green CI. |
| Reviewer agent | Reviewed against the task's done-when. It never merges. |
| Keel | The records and their links, the readiness check, closing answered questions, the done check and the history. |
| CI and the mirror | GitLab CI on the merge request; an hourly push to the public mirror on GitHub. |
Someone who opens the purge code a year from now, a person or an agent, can go from a test name to its criterion, from the criterion to the requirement and the statute behind it, and from there to the owner's rulings and the three answers settled in review. Those links are records in Keel, so nobody has to rely on a chat log or on memory to find them.
Keel is open to a small group of design partners: teams already building with coding agents. During the beta it's free, and we set up each team by hand. Questions about any step on this page? Write to [email protected].
Apply to be a design partner