Skip to content

Two-class landing authority: mechanical changes land without FLIGHT, substantive ones reach FLIGHT as one batch


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

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 flips open→closed. The scribe PR is already mechanical by precedent (#539, #549, #551, #559). It still queues behind FLIGHT, because auto mode refuses an agent gh 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 appended Countersigned by note under a box FLIGHT had already ticked in c1fd547e. 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) and chore(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.

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:

RulePathsDiff shape (all conditions)
M1 scribe closedocs/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:` on YYYY-MM-DDand a commit sha that resolves on main. (e)petrova validate` passes.
M2 front-matter normalisationdocs/decisions/*.md, docs/findings/*.md, modified onlyEvery 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 pointerdocs/decisions/*.md, docs/findings/*.md, modified onlyPure 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/` on YYYY-MM-DD., where exists on main atStatus: closed`.
M4 ledger / handoffdocs/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 / snapshotstate/**, 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/** (except irina/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’s open→closed, and any edit to ## Decision, ## Sign-off box 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.
  • M: an agent or the petrova-irina GitHub App (app id 5213498) may land M PRs, by squash, once all of the following hold.
    1. The PR carries the label landing:mechanical, and the classifier check landing-class (§5) reports M with the rule per file.
    2. Every check run and status on the head sha is success, skipped or neutral. None is pending. The lander checks this itself, because GitHub cannot (see Context). The gate is zero pending, not “no failure” (the #551 trap).
    3. No open issue labelled landing:mechanical-incident exists (§6).
    4. The repo variable LANDING_MECHANICAL_ENABLED reads exactly 1. Any other value means off. M5 keeps its current direct push and its current identities.
  • S: FLIGHT only. An agent drafts and opens the PR, as today. When the PR merges open so 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:

  1. Plain English, 2–3 lines: what it decides and why, with a choices table where there is a choice.
  2. Where: repo, path, the jzero row, and https://github.com/<org>/<repo>/edit/main/<path>.
  3. The exact change: the old line, then the new line, with the ruling text pre-written for FLIGHT to accept or edit.
  4. 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.

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.

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>) and Landed-By: <identity>, and links the landing-class run. 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.

FLIGHT can revoke the M class at three levels, all without an agent:

  1. Switch: set LANDING_MECHANICAL_ENABLED to anything but 1. This takes effect on the next landing attempt.
  2. Identity: remove petrova-irina’s repository access, or its contents: write permission, on this repo.
  3. 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.

#QuestionOptionsRecommended
C1Who 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.
C2Post-hoc review windownone / 72 h / 7 days / 14 days7 days. It matches the weekly IRINA cycle and the weekly ledger.
C3Enforcing “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.
C4Scopepetrova-codes only; also eva-hq and hermes; also mary-wikipetrova-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).
C5M4 pathsas listed; plus docs/plans/**, docs/superpowers/**as listed. Plans and specs carry intent, which makes them S.
  • 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.
  • 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’s petrova-codes note 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-irina any permission. FLIGHT grants contents: write and pull_requests: write on 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.

For code (after countersign, separate PRs):

  • .github/workflows/landing-class.yml with the classifier, its tests on the real diffs named in §5, and the lander job gated by §2.1–2.4.
  • The registry.yaml note edit.

For docs:

  • eva-hq operating-the-eva-fleet SKILL “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=1 when 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.

  • docs/decisions/2026-08-10-countersign-gates-closed-status.md (only a human ticks; open means 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.yaml petrova-codes.notes (human-review-only rule this bounds)
  • core/playbook/META-RULES.md MR-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) and scripts/git-hooks/pre-push (the same 403 recorded earlier; the hook is the local backstop)
  • GitHub API, 2026-10-10: branch protection and rulesets 403 on all three private Free repos (the C3 constraint)
  • Subagent: Claude Opus 5.5, 2026-10-10 (drafted; read at petrova a31d9f4a, eva-hq 6e5b838, mary-wiki 5bd42d8)
  • 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).