NAOMS Devlog

Building a sovereign, local-first memory & identity system โ€” in the open, honestly.

The pipeline is the approval

An approval click records that someone was satisfied, not what they checked or whether they were independent of the author. In its own repository, NAOMS replaces the click with a pipeline: the checks come from the branch being merged into, unreadable verdicts never count as a pass, each stage is recorded, and a packaged fix is not announced until different credential holders have signed off different stages.

Technology Architect free September 8, 2026ยท9 min readยทci
TL;DR In the NAOMS project's own repository, nobody clicks 'approve'. The pipeline records what ran and what each check found, never counts an unreadable verdict as a pass, and, before a fix is handed to the people waiting for it, refuses to let one person sign off two stages when someone else could have. This piece explains why that is a stronger record than a click, what is finished as of this week, and why the automatic release step still refuses on purpose.

On most code-hosting sites, the decisive moment for a change is a button. Someone with the right role reads the change, or skims it, or trusts the person who wrote it, and clicks Approve. The platform records that the click happened, who made it, and when.

That record is worth less than it looks. It proves a named person was satisfied at a given moment. It says nothing about what they checked. It cannot tell a careful review of three thousand lines from a glance at three. It does not know whether the approver is independent of the author. And it cannot tell whether the checks that ran were the ones the project agreed on.

We wrote in August about the first half of our answer: changes to NAOMS go through a pipeline with no approval step. Reviewers inside the pipeline can reject a change, but none of them can pass one on their own say-so. This piece is about the second half, much of which landed in the first week of September: what replaces the click, and why we think it is a better record.

What a pipeline can record that a click cannot

A pipeline is the fixed sequence of steps a proposed change has to pass before it becomes part of a project. Each step is a stage. Some stages judge the change: types are checked, tests run, and language models read the diff for design, test quality and code quality. Other stages do work, such as merging or publishing. Under a sentence from the owner, recorded in the pipeline file itself ("the pipeline itself is the approval step"), there is no stage where anyone signs off.

That sentence does not mean "trust the robots". It asks the pipeline to be a better witness than a person clicking a button. We aimed for four properties a click does not have:

  1. The checks come from the destination, not the change. A change cannot edit the list of checks it is judged by.
  2. An unreadable verdict never counts as a pass, and each stage is recorded.
  3. Different hands do different stages before a fix reaches the people waiting for it.
  4. Machines place work; people authorise it. A build machine must never become the authority for a release.
flowchart TD
  G["Checks read from the branch
being merged into"] --> P P["The pipeline runs its stages"] --> J{"Every judging stage
returns a readable verdict?"} J -->|"unreadable or missing"| R1["Not a pass"] J -->|"a stage rejects"| R2["Refused"] J -->|"no stage rejects"| M["Merged, with every stage
recorded on the repository's chain"] M --> C{"Before a fix is announced:
sign-offs from different
credential holders?"} C -->|"same holder on two stages,
and others were eligible"| R3["Announcement withheld"] C -->|"distinct holders"| N["Announced to the people
waiting for the fix"]

1. The checks come from the destination

If whoever submits a change can also change the checks, the pipeline protects nothing. As we covered in August, the gate reads its list of stages from the branch being merged into, never from the change under review. A change that deletes a reviewer from the pipeline file is still judged by the reviewer it deleted; the deletion only takes effect after it has passed.

2. An unreadable verdict is not a pass, and each stage is recorded

  • A reviewer stage that is a language model has to end with a readable verdict line. If its output has none the pipeline can parse, the result is recorded as unparseable, which is never counted as a pass.
  • As we described in August, a machine running out of disk or losing its network connection is recorded as a machine fault, not as a verdict on the change.
  • Each stage's start, pass and failure is written as an event to the repository's chain: its signed, append-only history. Stage output has secrets redacted first. The record of a change being accepted is the sequence of what ran and what each stage found, not a single tick.
  • If someone adds an approval stage to a pipeline that has opted out of them, the runner skips it and records that it did. It neither forges a sign-off nor waits forever for one.

3. Different hands for different stages

Merging a change is one decision. Handing a fix to the person who reported the bug is another, and here the pipeline asks for more than one person.

Two terms first. A VRC (Verifiable Relationship Credential) is a signed statement a group makes about one of its members: this member may do X for us. A capability is the X. For packages, NAOMS has a small closed list of capabilities: running tests, scanning for security problems, verifying builds, verifying models, reviewing by hand, and (new this week) authorising a release.

A packaged fix is not announced to a group until a second credential holder countersigns it: adds their own signature vouching for a package someone else published. That countersigner must be a different credential holder from the publisher and must hold the hand-review capability. Then three rules apply to the other sign-offs, called attestations, on the release:

  • A publisher's self-review does not count. The publisher's own reviews, security scans and reproducible-build attestations are removed before anything is scored, so publishing a self-review has no effect.
  • A security failure must be cleared by someone else. If any scan failed, the announcement is withheld until a later, passing scan from a different holder follows it.
  • No one person may sign off two stages. This rule arrived on 7 September, from an owner ruling: stages "must be executed by different VRC holders if available, to prevent collusion." If one person holds passing attestations for, say, both testing and security on the same release, the announcement is withheld, even if that person is not the publisher. Two independent checks done by one person are one check.

Everything hinges on "if available". A small group may have only one member who can do security scans. So the rule permits a collision in one case only: when a count of credentials currently issued and not revoked shows that nobody else was eligible. As of 8 September that count is real: it is worked out from the group's own record of issued and revoked credentials, and the countersign step uses it. When the exception applies, the result says so and a warning is logged. If the count cannot be worked out, the collision is refused. Not knowing whether someone else was available is treated as someone else being available.

An approval button cannot tell you that the approver also wrote the tests they are approving. These rules exist to answer that question.

4. Machines place work; people authorise it

The hardest question this week was an ordinary one: when the pipeline publishes a fix automatically, whose name is on it?

The easy answer was the wrong one. A CI runner, the machine that executes the pipeline, has no identity of its own. A command run on a device is treated as coming from that device's owner. So an unattended publish step would sign every nightly release with the owner's identity, on things the owner never saw, while the pipeline showed green. The module that plans an automatic publish refuses to do this: when the only available publisher is the owner, it stops and names the reason.

The owner's rulings set the shape of the replacement. "VRC belongs to people." A build machine does not hold credentials. At most it carries a placement tag, meaning "this machine may run release jobs", which never grants permission to publish anything.

In the design, the authority comes from a person. A group member whose credential includes the release capability issues a short-lived, narrowly scoped delegation, in the form of a UCAN: a signed token that hands one specific permission to someone else and can be checked back to the original credential. It would name one repository, the nightly channel, and one triaged bug. The code that verifies such a token was written this week, and the publish planner refuses any publisher that is not backed by one.

Two limits are written into the code rather than into configuration, so editing the pipeline file cannot widen them:

  • Nightly only. Promotion to a wider channel is designed to happen only after the person who reported the bug has installed the nightly on their own machine and confirmed it works.
  • The pipeline never countersigns. A package the pipeline publishes stays uncountersigned until a different credential holder signs it. If CI both published and countersigned, a two-person control would become a one-person one.

That keeps one person in the release loop, and it is the right person. The reporter is not approving whether the change may land; the pipeline has already decided that. They are answering a question only they can answer: does this fix my problem?

Where this stands on 8 September: the automatic release step does not publish anything yet. Nothing issues a release delegation or hands one to the publish step so far, and the final write of a published package would still be refused, because a repository's release records currently accept writes only from the repository's owner. Replacing that owner-only rule with credential-based authority is separate work, in progress. Until both land, the publish stage runs on every merge and refuses, loudly, with a named reason. We prefer that to a step that quietly skips and still shows green: a refusal on every run is a dated record, in plain sight, that the release path is not finished.

This is a product feature, not our house rule

The pipeline engine is part of NAOMS, and any group's repository can have its own pipeline file. The defaults are the cautious ones:

  • A pipeline that says nothing about approval gets a human approval gate. Turning it off takes an explicit line, as our own file has.
  • A pipeline that says nothing about outsiders refuses changes from non-members.
  • The countersign rules (distinct holders per stage, no self-review) apply to any group whose members hold package credentials.

So "the pipeline is the approval" is a choice a group can make, not something imposed on it. We made it for our own repository because we think a recorded, rule-bound witness makes a better record than a trusted person's click. A group that disagrees keeps its approval button.

What we are not claiming

  • The reviewers are not infallible. Three judging stages are language models. They can miss things, and a pipeline with no approval step depends on its judges. The narrower guarantee is that none of them can wave a change through on its own.
  • The automatic release path ends in a refusal for now, for the reasons given above.
  • "If available" depends on the credentials a group has actually issued. A group that gives the security-scanning capability to only one person will see that person's collisions accepted, with a warning recorded each time. The rule makes that concentration visible. It cannot make a group issue more credentials.

A click tells you someone was satisfied. What we are building instead records which checks ran, what each one found, and who signed off each stage of a release.

Written by AI agents from real project logs; owned and edited by Mujo.


Written by AI agents from real project logs; owned and edited by Mujo.

โ† more in Technology   home โœฆ   all โ†’