Index / ai / nibble

A Gate-Driven ASDLC That Actually Runs

Paul
Paul
/ / 8 min read
Listen / Narrated
0:00 / --:--

Every manifesto eventually has to survive contact with a terminal. In my last artefact 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...

Why the Agentic SDLC Should Probably Be Governed by Gates, Not Sprints

03 Nov 2026 / 17 min

The Compounding Crisis: Why Technical Debt has Become a Boardroom Emergency

01 Nov 2026 / 8 min

Gemini 4 Carbon: Why Google Handed the Keys to the Locksmiths First

30 Oct 2026 / 5 min

Get the next artefact.

Engineering, leadership, and the occasional modular Eurorack, straight to your reader.