Two-class landing authority: mechanical changes land without FLIGHT, substantive ones reach FLIGHT as one batch
rank: decision outranks: []
Section titled “rank: decision outranks: []”Two-class landing authority: mechanical changes land without FLIGHT, substantive ones reach FLIGHT as one batch
Section titled “Two-class landing authority: mechanical changes land without FLIGHT, substantive ones reach FLIGHT as one batch”Date: 2026-10-10 Status: closed Supersedes: none Superseded-by: none — current MR-compliance: MR-4, MR-5, MR-7, MR-9, MR-18, MR-20
Context
Section titled “Context”Every change to this repo waits for FLIGHT, including the ones that carry no
judgement. Three measurements, taken 2026-10-10 at a31d9f4a:
- 24 decision docs are still
**Status:** open(git grep -l -E '^\*\*Status:\*\* *open' -- docs/decisions, template excluded). The 2026-09-30 sort put 11 of them as built and never countersigned. Each needs two steps: FLIGHT ticks the box, then a scribe PR flipsopen→closed. The scribe PR is already mechanical by precedent (#539, #549, #551, #559). It still queues behind FLIGHT, because auto mode refuses an agentgh pr merge(“Merge Without Review”, eva-hq handoff 2026-10-09). - FLIGHT merged every scribe close itself.
83496e6d(#549) is a four-line diff: one**Status:**line and an appendedCountersigned bynote under a box FLIGHT had already ticked inc1fd547e. FLIGHT’s merge added no information. - The bots already land without FLIGHT, unclassified.
chore(state)(state/**, petrova-bot),chore(irina): record-history(irina/history/**, petrova-bot) andchore(docs): sync(docs/site/generated/**, petrova-docs-bot) push straight to main. No document says which shapes may do that, so nothing stops a wider shape from riding the same path.
There is a constraint the design has to respect. petrova-codes,
eva-hq and mary-wiki are private repos on the GitHub Free plan.
GET /repos/petrova-codes/petrova/branches/main/protection and
/rulesets both return 403 Upgrade to GitHub Pro, checked 2026-10-10.
GitHub cannot require a check here. Any rule that says “required
checks green” has to be enforced by whoever lands the change, or the org
has to change plan.
The registry’s own note for this repo still binds:
“changes to the control plane go through human PR review only, not
agent-fleet automation” (registry.yaml, petrova-codes.notes). This
decision keeps that rule for everything except the closed list in
§1, and it takes the human out of the merge only. The human still holds
the countersign.
Decision
Section titled “Decision”1. Two classes, decided by the diff, not by the author
Section titled “1. Two classes, decided by the diff, not by the author”Every PR to petrova-codes/petrova main is either mechanical (M) or
substantive (S). A PR is M only if every changed file matches one
of the M shapes below and no S trigger fires. Anything else is S,
including a shape the classifier cannot parse. The classifier fails
closed.
M shapes. Each is a rule a check can test from the PR’s diff against
its merge base, plus git blame on main:
| Rule | Paths | Diff shape (all conditions) |
|---|---|---|
| M1 scribe close | docs/decisions/*.md, modified only | (a) exactly one removed line, **Status:** open, replaced by **Status:** closed. (b) Every other hunk is pure addition, and lies inside the ## Sign-off section or directly under the **Status:** line. (c) On main, the doc’s Sign-off section already carries a checked box (- [x]) or a - Human: line. git blame on main attributes that line to a commit whose author email is on the human allowlist (§4). (d) The added text contains Countersigned by \human:and a commit sha that resolves on main. (e)petrova validate` passes. |
| M2 front-matter normalisation | docs/decisions/*.md, docs/findings/*.md, modified only | Every removed and added line lies in the YAML front matter or the bold header block, which is everything before the first ## heading. The single exception is appending a ## Sign-off section whose only box is unchecked (the #496 trap). No added line matches - \[x\], - Human: or Countersigned by. No **Status:** value changes. No added line sets status: to closed, superseded, ratified or accepted. Example of the shape: mary-wiki #813 / #816, countersign: owed → countersign: [ ]. |
| M3 scribe note / supersession pointer | docs/decisions/*.md, docs/findings/*.md, modified only | Pure addition, with zero removed lines. Either (i) appended at end of file under a line starting Scribe note (YYYY-MM-DD), or (ii) the MR-7 prepended block > **Superseded** by \docs/decisions/, where exists on main atStatus: closed`. |
| M4 ledger / handoff | docs/audit/handoff-*.md, docs/audit/contract-sweep-*.md, docs/audit/state.md, docs/evidence/** | Added or modified. Markdown only. docs-lint-equivalent checks pass (links resolve in-repo, no secret patterns). |
| M5 state / snapshot | state/**, irina/history/**, docs/site/generated/** | Commit subject starts chore(state), chore(irina): record-history or chore(docs): sync. Authored by the workflow identity that produces it today (petrova-bot, petrova-docs-bot). This ratifies the current direct push. It does not add a new path. |
S triggers. Any one of these makes the whole PR S:
- any change outside the M paths above, and in particular
registry.yaml,registry.schema.json,core/playbook/**(META-RULES),.github/**,.petrova/**,cli/**,host/**,irina/**(exceptirina/history/**),dashboard/**; - an added file under
docs/decisions/(a new decision is always S, and its countersign is S); - any added line that ticks a box (
- [x]) or writes a- Human:line. Only FLIGHT attests, the 2026-08-10 rule; - any
**Status:**change other than M1’sopen→closed, and any edit to## Decision,## Sign-offbox text,**Supersedes:**or**Superseded-by:**in an existing doc; - rulings, ratifications, and anything a doc says “needs a ruling”;
- outside git: every write to production variables or secrets (Railway, Vercel, GitHub Actions variables and secrets, Anthropic workspace limits), every fleet-host write (hermes eu-1), every repo-settings change. These never become M, whatever their size.
2. Who lands each class
Section titled “2. Who lands each class”- M: an agent or the
petrova-irinaGitHub App (app id 5213498) may land M PRs, by squash, once all of the following hold.- The PR carries the label
landing:mechanical, and the classifier checklanding-class(§5) reportsMwith the rule per file. - Every check run and status on the head sha is
success,skippedorneutral. None is pending. The lander checks this itself, because GitHub cannot (see Context). The gate is zero pending, not “no failure” (the #551 trap). - No open issue labelled
landing:mechanical-incidentexists (§6). - The repo variable
LANDING_MECHANICAL_ENABLEDreads exactly1. Any other value means off. M5 keeps its current direct push and its current identities.
- The PR carries the label
- S: FLIGHT only. An agent drafts and opens the PR, as today. When the
PR merges
openso jzero can list it (jzero lists only default-branch records), the merge is S, and FLIGHT does it.
3. How S reaches FLIGHT: one batch, every item in plain English
Section titled “3. How S reaches FLIGHT: one batch, every item in plain English”S items go to FLIGHT through jzero.run/approvals, gathered into one batch per session. Each item is never a bare label. It carries:
- Plain English, 2–3 lines: what it decides and why, with a choices table where there is a choice.
- Where: repo, path, the jzero row, and
https://github.com/<org>/<repo>/edit/main/<path>. - The exact change: the old line, then the new line, with the ruling text pre-written for FLIGHT to accept or edit.
- What it unblocks, and what must happen first.
This is the “Asking FLIGHT to sign or rule” form in eva-hq
.claude/skills/operating-the-eva-fleet/SKILL.md, made binding for S.
A “GO” given before FLIGHT has seen the content is not a ruling.
4. The human allowlist
Section titled “4. The human allowlist”M1(c) treats a box as human-ticked only if git blame on main attributes it
to a commit authored by flight@arno.host or alex@devarno.com, the
human principal on arno.host. If the box was squashed into a commit
FLIGHT merged, the PR’s own pre-squash commit must be authored by those
addresses (the c1fd547e → 955e7861 pattern). The check follows the
squash through the PR’s commits. Agent identities (fleet@devarno.cloud,
ops@devarno.cloud, any *[bot], noreply@anthropic.com as author) never
count. The allowlist is part of this decision. Changing it is S.
5. The classifier check
Section titled “5. The classifier check”A workflow, landing-class, runs on every PR. It computes the class from
the diff alone: path match, then line-shape match per §1 on the
unified diff with -U0, then the git blame lookup for M1(c). The label
is a claim, and the check verifies it.
- The check is green when the computed class equals the label
(
landing:mechanical→ M; no label → S, always green). - The check is red when the label says M and the diff is S. It names the first rule that failed, by file and line.
- It writes a job summary listing each file and the rule it matched.
The check is a build consequence of this decision, not part of it. It lands as its own PR after the countersign, with tests against the real diffs of #539, #549, #551, #559 (M1), mary-wiki #816’s shape ported as a fixture (M2), #550 (S: new decision doc) and #496 (S: 18 status flips on older-style signatures, which need a human read).
6. Audit trail and FLIGHT’s post-hoc window
Section titled “6. Audit trail and FLIGHT’s post-hoc window”- Trailers. Every M squash commit carries
Landing-Class: mechanical (<rules>)andLanded-By: <identity>, and links thelanding-classrun.git log --grep 'Landing-Class: mechanical'is the complete list. - Review window: 7 days. FLIGHT reviews merged M PRs with
is:pr is:merged label:landing:mechanical(or in jzero, if that queue is built). Nothing waits on the review. A misclassified landing found in the window, or later, is reverted. The revert is an exact inverse of an M diff, so it is itself M, and FLIGHT or an agent at FLIGHT’s request may land it. - Incident halt. Anyone, agent or human, who finds a misclassified M
landing opens an issue labelled
landing:mechanical-incident. While one is open, §2(3) stops all M landings. Only FLIGHT closes it, and the lander checks the closer’s login. The issue names the rule that let the change through. Correcting that rule is S.
7. Revocation
Section titled “7. Revocation”FLIGHT can revoke the M class at three levels, all without an agent:
- Switch: set
LANDING_MECHANICAL_ENABLEDto anything but1. This takes effect on the next landing attempt. - Identity: remove
petrova-irina’s repository access, or itscontents: writepermission, on this repo. - Rule: a superseding decision (MR-7) that drops or narrows one M rule. Narrowing is the expected response to an incident. Widening any rule, or adding one, is S and needs a new countersigned decision.
Revocation never reopens a landed change. Reverts are separate and M.
Choices for FLIGHT
Section titled “Choices for FLIGHT”| # | Question | Options | Recommended |
|---|---|---|---|
| C1 | Who lands M PRs | (a) petrova-irina App token from a landing-class workflow job; (b) workflow GITHUB_TOKEN (petrova-bot); (c) an agent session with FLIGHT’s gh token | (a). It is a distinct, filterable identity, and its merges trigger push workflows (docs-invariants, main-red-alert, Railway Wait-for-CI). GITHUB_TOKEN merges trigger no workflows. (c) puts agent actions under FLIGHT’s name. |
| C2 | Post-hoc review window | none / 72 h / 7 days / 14 days | 7 days. It matches the weekly IRINA cycle and the weekly ledger. |
| C3 | Enforcing “checks green” on GitHub Free | (a) the lander self-gates (§2.2); (b) move the org to GitHub Team so branch protection can require landing-class and docs-invariants; (c) both | (a) now, and (b) when the plan changes. (a) is free and enforced at the only actor allowed to merge M. (b) also guards S merges, but it costs a plan change, and that is FLIGHT’s call. |
| C4 | Scope | petrova-codes only; also eva-hq and hermes; also mary-wiki | petrova-codes only. mary-wiki has its own ratification law (F-197/F-200, app-signed ratifications under R-0b). Extending there needs a mary-wiki status: proposed record. eva-hq already lets agents open PRs, and its ledger PRs need a check first (eva-hq docs-lint). |
| C5 | M4 paths | as listed; plus docs/plans/**, docs/superpowers/** | as listed. Plans and specs carry intent, which makes them S. |
Alternatives considered
Section titled “Alternatives considered”- Keep one class (status quo). Rejected. FLIGHT’s merge of a scribe close adds no information (#549), and the 11 built-but-unsigned docs each need a FLIGHT merge after a FLIGHT tick.
- Let agents tick boxes for “obvious” closes. Rejected, as it was in 2026-08-10. A countersign is an attestation, and an agent attesting is the failure the gate exists to prevent. M1 requires a human tick that already exists.
- Classify by author or by commit-message prefix. Rejected. A prefix is a claim and an author is not a shape. Only the diff can be checked. M5 uses the subject as an extra condition, never as the only one.
- The cosign auto-merge path (2026-07-28). Not reused. It needs a
second secret per batch and covers registry-edit verbs only, which stay
S here. It also calls
enableAutoMerge, which needs branch protection that this plan cannot have.
What this decision does NOT do
Section titled “What this decision does NOT do”- It does not change who countersigns. Every countersign, ruling, ratification, production variable and fleet-host write stays FLIGHT’s.
- It does not amend any meta-rule. MR-5’s direct-push boundary is untouched. M classes land by PR, except M5, which already pushes directly.
- It does not edit
registry.yaml’spetrova-codesnote now. The build PR carries that edit (precedent #475: registry edits ship with the implementing decision). After the countersign, the note names this doc as the bounded exception. - It does not grant
petrova-irinaany permission. FLIGHT grantscontents: writeandpull_requests: writeon this repo, a settings change. - It does not build a jzero batch view. The batch in §3 is a document FLIGHT reads, with links to the per-record rows. A jzero batch screen would be a junction-zero change of its own.
- It does not apply to mary-wiki, eva-hq, hermes, rocky or devarno-cloud.
Consequences
Section titled “Consequences”For code (after countersign, separate PRs):
.github/workflows/landing-class.ymlwith the classifier, its tests on the real diffs named in §5, and the lander job gated by §2.1–2.4.- The
registry.yamlnote edit.
For docs:
- eva-hq
operating-the-eva-fleetSKILL “Who lands what” gains the M/S split for petrova-codes, and the ledger records the first M landing.
For FLIGHT:
- Set
LANDING_MECHANICAL_ENABLED=1when ready (C1–C5 ruled first), grant the App access, and create the two labels.
For in-flight phases: none.
For invariants: none added, modified or repealed (MR-9: the classes are a landing convention, grounded in the 2026-08-10 countersign rule and FLIGHT’s request to cut the countersign bottleneck, cycles C4/C5).
For downstream consumers (D6): none. eva.consumption.v1 is unchanged.
References
Section titled “References”docs/decisions/2026-08-10-countersign-gates-closed-status.md(only a human ticks;openmeans countersign outstanding)docs/decisions/2026-07-28-cosign-automerge-firewall-optout.md(the existing opt-out; not reused, see Alternatives)docs/decisions/2026-09-25-cupel-rung-1a-merge-trailers-adopted.md(trailer precedent for the audit trail)registry.yamlpetrova-codes.notes(human-review-only rule this bounds)core/playbook/META-RULES.mdMR-5, MR-7, MR-18, MR-20- petrova #539, #549 (
83496e6d), #551, #559 (scribe-close shape, M1), #496 (Sign-off heading trap behind M2’s one exception) - mary-wiki #813, #816 (front-matter normalisation shape, M2)
eva-hq:.claude/skills/operating-the-eva-fleet/SKILL.md(“Asking FLIGHT to sign or rule”, the §3 form)docs/audit/state.md(Gates) andscripts/git-hooks/pre-push(the same 403 recorded earlier; the hook is the local backstop)- GitHub API, 2026-10-10: branch protection and rulesets
403on all three private Free repos (the C3 constraint)
Sign-off
Section titled “Sign-off”- Subagent: Claude Opus 5.5, 2026-10-10 (drafted; read at petrova
a31d9f4a, eva-hq6e5b838, mary-wiki5bd42d8) - Human countersign — flight@arno.host, 2026-10-27 (jzero.run T1) — the M/S classes in §1, the human allowlist in §4, the audit trail and revocation in §6–7, and rulings C1–C5 (recommended: C1 a, C2 7 days, C3 a now, C4 petrova-codes only, C5 as listed)
Countersigned by human:flight@arno.host on 2026-10-27 — box ticked in commit 3363c8a1, authored by the human (jzero.run T1, session alex@devarno.cloud): the classes, allowlist, audit trail and rulings as the box states them.
Status flipped open→closed on 2026-11-04 by an agent as scribe (MR-7).