Table of contents
- Forge flow — branching, merging, and the steward
- The model
- The thirteen laws
- 1. Single base
- 2. The tag horizon
- 3. The drift invariant
- 4. Backflow
- 5. Provenance by citation
- 6. Origin is a tag-anchored mirror
- 7. Tagging is a HITL signing ceremony
- 8. Curation seats
- 9. Refs cite tickets, and the PR closes them
- 10. Merge style at review
- 11. Approval and the mechanical merge
- 12. Steward scope
- 13. Tracker lifecycle — states, overlays, and the no-bounce doctrine
- The build queue
- Provenance
Forge flow — branching, merging, and the steward
Rename notice (2026-08-07, freeholder declaration): this law is renamed Ratification — the process by which a member's work becomes binding in the Ting. The name "Forge flow" described the tool, not the law. The rename is declared in the [Ting declaration] and propagates to this page, the steward specs, issue #22, and every AGENTS.md in a later migration pass. Until that pass, "Forge flow" and "Ratification" name the same law.
Ratified 2026-08-04, owner + agent.odin, clause by clause (law 13 ratified the same day, after the page first landed). Execution is tracked in issue #22 (FM-FORGE-FLOW). Where this page and the implementation disagree, the code wins — but then this page must be repaired in the same breath.
Scope: forge-wide. This law binds every repository on jo.et0.pw — there is
no deviation when a swarm of agents goes live. Each repo instantiates it at
birth through its declared steward spec (law 12): fastlane, protections, label
spec, tango/<ticket>-<slug> naming. The page lives in flaky-mesh's wiki for
provenance; its authority does not end there.
Transition clause (2026-08-04): work dispatched before this ratification — flaky-mesh PR #23, lar.ad/modron PRs #14, #16, #17 — merges owner-direct, grandfathered. The no-bounce doctrine applies to the process itself: the miss (a mold ratified after dispatch) is charged to who should have spoken earlier, not to the dispatched agents. The mold binds work dispatched after ratification. A boundary marker, not a precedent.
The model
agent workspace ──push──▶ tango/<ticket>-<slug> ──PR──▶ fastlane ──curation──▶ main ──tag-time mirror──▶ origin (GitHub)
fastlane is agent-governed; main is human-anchored. The further from the human, the more mechanical the gate. Agents propose and police each other on fastlane; only the owner and one designated seat touch main; only the owner's hand publishes.
The thirteen laws
1. Single base
Every PR sits on fastlane tip, always. No epic integration branches. On any
push to fastlane — steward merges and owner direct pushes alike — the steward
rebases every open PR onto the new tip through Forgejo's own update API
(POST /api/v1/repos/{owner}/{repo}/pulls/{index}/update?style=rebase).
Conflicts return the PR to its agent with the evidence; the steward never
resolves. The forge-side rebase is authoritative; change-id headers
(git.write-change-id-header, law-adjacent tooling) make the agent's re-sync
silent instead of a divergence ritual.
Why: a queue of stale PRs is a queue of lies. Rebase-on-advance keeps every open proposal honest at all times, and conflicts signal dead assumptions, which belong to the ticket's owner — never to a bot.
2. The tag horizon
History at or below the newest version tag on main is immutable. The untagged
tail may be absorbed, squashed, and re-curated. Tags never move — the
horizon and origin's fast-forward guarantee both rest on that single
invariant. Local enforcement: jj immutable_heads() extended with tags().
Forge enforcement: the push whitelist (law 8) with force-push permitted but
disciplined — Forgejo cannot express "only above the last tag", so that
clause is law, not mechanism.
Why: jj curation does not stop being useful at promotion. The horizon makes rewrite safe by bounding it: everything the fleet or origin may have seen is frozen; everything above is still clay.
3. The drift invariant
After every promotion, diff main fastlane is empty except for deliberately
held-back changes. Drift means main is no longer a cut of fastlane — it is a
fork, and the model has failed silently.
4. Backflow
Anything that lands on main outside the fastlane path — a promotion-time fix, an emergency — is committed back into fastlane immediately. This is the classic two-branch failure mode; gitflow died of it.
5. Provenance by citation
Curated main commits cite their source (PR #n, fastlane commit). Change-id
headers give this a mechanical assist: the same change id survives steward
rebases and is greppable end to end. Six months from now, every line in main
must answer "why do you exist" without archaeology.
6. Origin is a tag-anchored mirror
Untagged commits never leave the forge. Origin (GitHub) receives main at tag
time — always a fast-forward, since new tags always descend from old ones —
plus the tags themselves. Subscribing to forge main is consent to tail
rewrites; tags are the only states guaranteed stable. One-time surgery: the
GitHub repo currently holds the unrelated bootstrap commit a430f4db; the
first mirror push is a deliberate reset, ff-forever afterward. Forgejo's
built-in push mirror is all-refs and therefore wrong for this — the mirror
is a scheduled job on bifrost, declared in the flake.
7. Tagging is a HITL signing ceremony
Tags are minted by the owner, signed with the owner's GPG key, which does not
auto-unlock — every signature is a deliberate act, by design. Explicit
git tag -s in the colocated repo; no tag.gpgSign default, so signing is
never a habit. Agents never tag: no agent holds the key, and the key never
stands unlocked waiting to be borrowed. The public key is registered on
Forgejo and GitHub so signatures verify in both UIs. Consequence accepted
knowingly: tags are rare, so origin sits behind fastlane most of the time —
origin is the release archive, not the working surface.
8. Curation seats
Push to main = the owner plus at most one agent persona per org. The forge's org layout (2026-08-04) and its seats:
larandar(flaky-mesh + infra): odinswarm(sleipnir — moved to its own org 2026-08-04): teylalar.ad(modron, hydramem): odin — same seat, lighter posture: lar.ad's steward spec (law 12) is deliberately more relaxed than flaky-mesh's. Relaxation is declared in the spec, not deviated from the law.
A persona may hold seats in more than one org; the cap is per org, not per persona. And a seat is curation authority, not a fence: a seat holder works in any org as an ordinary agent — PRs to fastlane, tickets, review — the seat governs only who curates main.
Enforced by the branch-protection push whitelist — this law is mechanical. The steward rides the seat identity. Blast radius of a compromised or misbehaving seat is exactly the untagged tail of main — bounded by law 2, recoverable from the last signed tag.
9. Refs cite tickets, and the PR closes them
PR branches are tango/<ticket>-<slug>, naming the lead ticket — the one
the work was picked up under. Local tango workspace names follow the same
convention so the local bookmark and the pushed ref are one name end to end.
This is not bureaucracy — it is the naming scheme enforcing the issue-authority
rule.
The unit is a session, not a ticket. One session opens one or more PRs; one PR closes one or more tickets. The old reading — a PR per ticket — is withdrawn: it forced either an artificially wide ticket or an artificially narrow PR whenever the natural unit of work sat across a few tickets, and it made "which ticket does this branch belong to" a question with no honest answer.
What survives is the floor and the ceiling:
- No ticket, no branch, no PR. A session that has not claimed a ticket has nothing to propose.
- Every PR closes at least one ticket, or says in its body why it closes none. That is what keeps the many-to-many honest — the count of PRs and the count of tickets need not match, but nothing may land unattributed.
- A session splits its work where the review does. Several PRs off one session is the normal case when the pieces are reviewable apart; it is not a concession. Each still sits on fastlane tip (law 1) and is judged on its own.
A PR body must carry a closing keyword for every ticket it delivers —
Closes #n, one per line, at the end of the body. Not (#n) in prose, not
Refs #n, not a link. The forge acts on the keyword and nothing else, so a
citation the forge cannot parse is a citation that does not close anything.
Closes #n— this PR delivers ticket #n. The forge closes it at merge.Refs #n— related, not delivered. Roots, epics, and tickets a later PR will close. A PR whose body carries onlyRefslines closes nothing, and that must be a deliberate statement rather than an oversight.
Commits may carry the same keyword; the PR body is authoritative because it
is what survives a squash (law 10) and what the merger last read. A commit-only
Closes #n on a squashed PR reaches the forge as body text and does close, but
relying on that means the guarantee depends on the merge style, which is chosen
per-PR. Write it in the body.
No ticket delivered, no merge. A PR that neither closes a ticket nor states in the body why it closes none is not ready; the reviewer withholds approval. Applies to the author, not the steward — the steward observes and reports, it does not author PR bodies.
Why: law 13 already says tickets close at fastlane merge and names the PR body as the mechanism, but nothing made anyone write the line. Every merged PR on the forge up to 2026-08-06 carried none, so trackers drifted: work landed on fastlane and the tickets stayed open, which makes an open-issue count describe nothing. A convention nobody can forget is worth more than one everybody does — the branch name already links the lead ticket mechanically, so the closing keyword is the one remaining hand-written step, and it is now required rather than assumed.
It is also what makes session-as-the-unit safe. Once a PR may close several tickets and a session may open several PRs, the branch name alone no longer tells the steward what closed — only the keyword does. The two halves of this law arrived together for that reason.
10. Merge style at review
Squash raw PRs; preserve queen-curated lines (rebase/ff-merge). The
invariant is that fastlane stays feature-sized, not that every PR is one
commit. The curated label on the PR is the machine-readable verdict the
steward reads; the merger chooses and owns the choice.
11. Approval and the mechanical merge
Any collaborator on the repo may approve — except the PR's own author. Self-approval would make the gate theater: drone authors, queen (or peer) approves, steward merges. The steward merges mechanically once the gate is green: approved, atop fastlane tip, no conflicts. Direct push by authority holders is the accepted bypass — authority is the bypass; abuse is a trust event, made visible by laws 2 and 5.
12. Steward scope
The steward fills what Forgejo cannot enforce, and nothing more:
- Forge-side conformance is the steward. Branch protections, labels, PR
defaults, webhooks — read via API, compared against the declared spec,
remediated or (for anything destructive) alarmed. Birth is an act: new
repos are bootstrapped by a declared
just forgejorecipe; the steward reconciles forever after. - Client-side bootstrap is the devshell. jj conventions
(
write-change-id-header,immutable_heads() | tags()) live in clones on machines; a service on bifrost cannot and should not reach into workstations.justdoctor recipes assert them on entry. The steward only observes the effects — e.g. change-id header presence on pushed refs. - Not its job: CI, merge queues, conflict resolution, authoring.
13. Tracker lifecycle — states, overlays, and the no-bounce doctrine
The tracker is a state machine: every label is either a state or an overlay, and every transition has an owner.
States — pipeline position. Mutually exclusive; exactly one (or none) per issue; they gate:
| state | meaning | set by |
|---|---|---|
| (none) | filed, unclaimed | filer |
in-progress |
claimed, workspace active | the agent, by self-assigning — exactly one assignee, or none when idle |
in-review |
PR open against fastlane | steward (PR webhook; every ticket the body closes, not just the lead one in the tango/<ticket>-<slug> branch name — one PR may cover several, law 9) |
conflicted |
steward rebase failed; work returned to its agent | steward (update-API failure), with evidence in a comment; back to in-review on the fix push |
| closed | merged to fastlane | Closes #n in the merged PR body — required by law 9, not optional |
Overlays — orthogonal facts. Combinable with any state; they never move position and never gate:
| overlay | meaning | set by |
|---|---|---|
blocked |
at least one open Blocked by edge | steward, derived only — never hand-set. Purely informative: an agent may still claim deliberately |
needs-decision |
parked on an owner call | hand-set intent; work pauses, position does not move |
curated |
merge-style verdict (law 10) | approver — PR label, not issue label |
Tickets close at fastlane merge — and there is no bounce. A ticket is a
unit of specification. The PR is judged against its ticket, not against the
ideal. An implementation defect is ordinary review: changes requested, the PR
stays in-review. A specification miss — the ticket asked for the wrong
thing or too little — discovered in grooming or review triggers a new,
linked ticket, never a bounce of the PR and never a resurrection of a
closed one. That is what makes closing at merge honest: "closed" means
"delivered to fastlane as specified", and law 3's drift invariant is what
guarantees closed tickets reach main.
The asymmetry is deliberate and comes from production: a spec miss found late is charged to whoever should have spoken earlier — the groomer, the reviewer — never to the implementer for failing to foresee complexity. Seniors speak early; juniors are not required to be prophets.
The doctrine has one leniency: an external event may reevaluate the ticket. If the ground moves before the PR merges — another ticket landing, an upstream change, the world shifting — the ticket itself may be reevaluated (amended, rescoped, closed as superseded) instead of mechanically merging plus filing a follow-up. Mid-flight, while the PR is in review, that reevaluation needs the dev's approval: the implementer consents to the ground shifting under them, or the no-bounce rule applies.
Steward automation of the derivable. PR opened → in-review (strips
in-progress); rebase failure → conflicted + evidence + reassignment to
the author; fix push → in-review; merge → close.
Closure is the forge's, from the law-9 keyword — the steward does not close tickets by inference. What it does is make a missing keyword loud instead of silent: on PR open, if the body carries no closing keyword and the branch name names a ticket, the steward comments with the line the author should have written. A nudge on the PR, not an edit of it and not a block on the merge; authorship stays with the author (law 12), and the reviewer is the gate.
The blocked overlay is
recomputed on issue-close events and on the periodic conformance sweep:
any open edge → label present; last edge closed → label removed plus an
"unblocked: #n closed" comment, so the waiting party gets a pulse.
Epics are not issues. Campaigns and groupings live in Forgejo Projects;
an issue never carries epic state. The Epic:-style holding issues (e.g. #9)
migrate to Projects when the steward spec lands.
Labels are declared in the steward's conformance spec (law 12) — states,
overlays, and the blocked derivation rule alike. No hand-created labels, no
renames outside the spec.
The build queue
Tracked as checkboxes in issue #22:
collaborator grants for agent.*, branch protections on fastlane and main,
the steward service on bifrost, the tag-time mirror job (incl. the one-time
bootstrap reset), repo jj config + devshell doctor, the owner's GPG public
key on both forges, a sweep of stale local lines — plus the law-13 work: the
steward label spec (states, overlays, blocked derivation) and adopting
Forgejo Projects for epics, migrating #9 — plus the law-9 amendment: the
missing-closing-keyword nudge on PR open, and a one-time reconciliation sweep
of PRs merged before 2026-08-06, whose tickets were left open.
Provenance
Amended 2026-08-06 (law 9, owner-directed), in two parts:
- The closing keyword is required in the PR body rather than named as a
mechanism nobody had to use. Cause: every PR merged on the forge up to that
date carried none — including
swarm/vordrPR #12, whose four delivered spec tickets (#7, #8, #9, #11) were closed by hand afterwards. - The unit is a session, not a ticket — one session, one or more PRs; one PR, one or more tickets. Withdraws the implicit ticket-per-PR reading, which distorted either the ticket or the PR whenever the natural unit of work sat across a few tickets.
They are one amendment: once the mapping is many-to-many, the branch name stops
being sufficient evidence of what closed, and only the keyword is. Law 13's
close row, its in-review row, and the steward's automation list were repaired
in the same breath.
Designed and ratified clause-by-clause on 2026-08-04 between the owner and
agent.odin. Live-verified facts behind the design: the forge held only
main (c6816528, in sync with the workstation); GitHub held only the
bootstrap commit; zero PRs had ever existed on the forge.