---
title: "A Gate-Driven ASDLC That Actually Runs"
canonical_url: "https://artefact.ist/artefact/e0925b70-d7d2-11f1-8a34-034b6ddf249f/a-gate-driven-asdlc-that-actually-runs"
author: "Paul"
published_at: "2026-11-03T21:53:00+00:00"
modified_at: "2026-11-03T22:37:33.452001+00:00"
type: "nibble"
word_count: 1563
tags: ["ai", "sdlc", "agentic", "gates", "provenance"]
---

# A Gate-Driven ASDLC That Actually Runs

Every manifesto eventually has to survive contact with a terminal. In my [last artefact](https://artefact.ist/artefact/0634fa40-d79c-11f1-93fe-037c03c214a6/why-the-agentic-sdlc-should-probably-be-governed-by-gates-not-sprints "Why the Agentic SDLC Should Probably Be Governed by Gates, Not Sprints") I argued that the two-week sprint should give way to **gates**: hard, verifiable checkpoints between the cycles of an agentic software development life cycle (ASDLC); opened only when both the machine, and the humans, sign. It is an easy thing to write, and a much harder thing to prove; diagrams are **free**, and enforcement is not.

So I built one. It is a small working git repository that follows the principles of that article, from a signed `intent.json` through to a trunk that refuses anything unsigned. It is deliberately simple, it has rough edges I will be candid about, and it *works*. It's not public ready, if only because of its simplistic subject matter. We need some *real* code.

## The Smallest Possible Patient

I chose the most modest artefact I could think of: a single static HTML page explaining the Pentagram Graph. No framework, no build step, no runtime. The point was never the page. The point was to see whether the *governance* holds when the thing being governed is trivial enough that nobody can blame the complexity for the outcome.

The tooling follows the same thinking. It is plain Python with no dependencies, plus git, and OpenSSH. No platform, no web UI, no ticket statuses to update. It is a local CLI gate runner, and a handful of git hooks, which is exactly the "provenance without bureaucracy" I described.

## Four Gates, One Chain

The repository implements four of the five nodes. Telemetry is absent for the honest reason that a static page has nothing to observe at runtime.

* **Intent:**`intent.json` carries the objective, constraints, security posture, a page-weight budget, a TokenOps budget, and five acceptance criteria. Each criterion must link to a real test. The machine check also rejects any intent that contradicts the system invariants. The Product Owner *and* the Engineering Lead must sign.
* **Specification:**`spec.json` is an executable contract of thirteen rules, each tracing back to an acceptance criterion. The red test suite is staged before a single line of HTML exists, so the tests fail first, as they should. The Architect signs.
* **Synthesis:** the page itself, carrying a provenance header that names the exact specification it was built against. The gate also checks the TokenOps ledger against the budget. The Engineering Lead signs.
* **Verification:** the full suite runs, and the report is bound to the precise files it tested. Zero failures, zero skips, zero exemptions. The QA Lead signs.

The chain is held together by *content*, not by status. The spec contains the intent's fingerprint, the page contains the spec's, and the verification report records all three. Change one character of the intent after sign-off, and every gate downstream locks itself, without anyone having to notice, or remember.

## Signatures, Not Status Columns

Every attestation is a short statement (the gate, the role, the signer, and a SHA-256 digest of the files governed), signed with `ssh-keygen -Y sign`. That is the same signature format git already uses for SSH commit signing, so there is nothing exotic to install, and nothing proprietary to trust.

Checking the whole graph is one command:

```
python tools/gate.py status
```

It reports every machine check, and every signature for every gate; and explains precisely why a gate is locked when it is. Signing is equally deliberate. The tool shows you exactly which files you are putting your name to, and asks you to type the gate name before it signs. A signature should be a considered *act*, not a reflex.

I spent the time trying to break it. A cowboy hotfix to the page locked Synthesis, and Verification; while leaving Intent, and Specification, untouched. Raising the token budget after sign-off locked everything. Hand-editing a signed statement produced a bad signature. One person attempting to sign the same gate as two roles was refused on *separation* of duties. Reformatting the intent JSON without changing its meaning, on the other hand, kept it open; the system cares about *meaning*, not whitespace.

## The Bilateral Veto in Practice

The part of the original article I most wanted to see working was the bilateral veto, and it is the part that feels most natural in use. The agent refuses to attest when the checks fail; it held the Synthesis gate shut until the token spend was recorded in the ledger. Humans cannot sign until the agent has attested, so nobody puts their name to something unvalidated. Equally, a page that passes every test still goes nowhere until a human is *satisfied* with it.

The most instructive moment came from my own testing. I committed a page change to the trunk, and it went straight through, even though Verification was locked. The original rule allowed work in progress inside a cycle and only checked the push. That was too lenient for the principle I had written: unfinished work should never land on trunk. The rule is now stricter. Commits and merge commits on `main` are refused unless every gate is open; work in progress lives on a branch and arrives on trunk only once it is *fully* signed. The governance improved because I tried to get round it, which is rather the point.

#### What It Does Not Do (Yet)

Candour matters more than polish here, so the limitations are documented in the repository itself.

* **One person, five identities:** the demo signers all live on one machine with unprotected keys. In real use each person holds their own key, ideally hardware-backed, and the agent's key exists only in CI.
* **The trust root can be staged:** the hooks read the list of signers from the commit being checked, so `.gates/` and `tools/` need code-owner protection and a CI check against trunk.
* **Hooks are advisory:**`git commit --no-verify` skips them. The real wall is the same check running as a required status on `main`.
* **The ledger is self-reported:** the synthesis entry is an estimate, not metered usage.

The next step is the obvious one: letting an agent do the synthesis itself, inside a loop that receives only the signed contract and the failing tests, records metered token spend, and stops dead at the budget ceiling.

## Governance You Can Clone

None of this required a change-advisory board, a workflow engine, or a single meeting. It is a few hundred lines of tooling, a handful of JSON manifests, and the cryptography already sitting on every developer's machine. The objection that gates mean a return to Waterfall does not survive a working example; the ceremony was never the governance.

The gate does not care what day of the sprint it is; it only cares whether the work is *right*. It's a *start.*

## Things that need attention.

### What would break with a team

**1. Enforcement is optional and local.** The hooks only run if each person sets `core.hooksPath`, and `--no-verify` skips them. For a team, the real enforcement has to be a required CI check on `main`, with GitHub branch protection so nobody can push to `main` directly, and all changes arrive through pull requests.

**2. Anyone can add themselves as a signer.** The list of signers and their roles is read from the commit being checked. So a pull request could add a new key, give it the Product Owner role and sign with it, all in one change, and it would pass. Fixing this takes two things:

* CI checks signatures against the list on `main`, never the one in the pull request.
* `.gates/` and `tools/` need code-owner approval to change.

**3. The agent's key is on everyone's laptop.** Anyone holding it can produce a "machine passed" signature for work that never passed. That key should exist only in CI. CI should also re-run the checks, and tests itself, rather than trust a committed report.

**4. It handles one piece of work at a time.** There's one `intent.json`, and one chain. Two people working on two features would both edit the same intent, spec, and signature files; and every merge would conflict, or invalidate the other's signatures. A team needs each intent in its *own* folder (for example `intents/INT-0002/`) with its own gates, and its own signatures.

**5. Re-signing gets tedious.** Every page edit needs the Engineering Lead, and QA Lead, to sign again, and the sign command shows a list of file fingerprints, not what *changed*. At team scale that produces the rubber-stamping we don't want. Signing should show the changes since the *last signed* version, so people sign what they've *actually* reviewed.

**6. People join and leave.** The demo has no way to retire, or revoke a key, or limit how long it stays valid. A signature from someone who's left still counts. OpenSSH's signer list supports start, and expiry dates per key, which would cover most of this. Real people should also use hardware-backed keys.

**7. Signing times aren't trustworthy.**`signed_at` comes from the signer's own clock, so you can't prove *when* something was signed. For audit-grade evidence we'd want a public signing log such as Sigstore's, or CI adding the timestamps.

**8. The tool itself isn't tested (loop-ception).**`gate.py` has no tests of its own, the HTML checker is basic, and Telemetry doesn't exist yet.

More to come, eventually...

## Linked Entries

- [Why the Agentic SDLC Should Probably Be Governed by Gates, Not Sprints](https://artefact.ist/artefact/0634fa40-d79c-11f1-93fe-037c03c214a6/why-the-agentic-sdlc-should-probably-be-governed-by-gates-not-sprints.md) (artefact)
