Přeskočit na obsah

Tato stránka byla automaticky přeložena z angličtiny, jediného jazyka, ve kterém se tento web píše. Číst originál.

A push runs what CI would find first, and CI decides

A push runs what CI would find first, and CI decides

majordomus://rule/project.a-push-runs-what-ci-would-find-first@3aktivní ciproces

odkazuje na něj

Nic ve vrstvě to nejmenuje. Zpětné odkazy se odvozují, takže prázdný seznam je odpověď, ne opomenutí.

Zdůvodnění

Dne 2026-10-09 CI dvakrát odmítlo release 0.19.0, pokaždé kvůli něčemu, co lokální běh najde za pár sekund. Prvním byla strukturní kontrola, economics-claims, za 6 s a always: true. Druhým případ sady, 08, za 1 s. Ani jedno před pushem neběželo. Pre-push hook spustil finish --check, který hlásí požadované kontroly jako nikdy nenahlášené a žádnou z nich nespouští, a případy se vybíraly odhadem. Každé odmítnutí stálo okruh přes CI delší než hodinu, pak další derive za zámkem stroje, a to u jediné změny, na kterou vlastník čekal.

Celá úloha structure odpovědí není. Trvá asi 20 minut, sahá na API GitHubu a z jednoho stroje pushuje devět relací. Co push dluží, je ta část CI, která je levná, lokální a jistě selže stejným způsobem, vybraná podle toho, co CI měřilo.

Version 1 refused the push on every failed gate, and on a loaded machine that made the hook the slowest part of the work: on 2026-10-20 a push of a rescue ref spent 3490 s in the step at load 70 to 90, and a commit hook started a cold cargo build. On 2026-10-21 the owner relaxed the cooperation model (rule project.the-cooperation-model-is-the-owners-word, version 2, I3824): local gates are informative and CI holds the verdict. A finding CI will reach again is therefore reported here, not refused (I3830). A check CI cannot reach, or reaches only after a merge or a deploy it would break, still refuses: it takes seconds and nothing else would say it.

Version 3 refuses one more thing: a failed gate or case on a push that carries a train top, the landing seat's combined top under refs/heads/integration/train-*. On 2026-10-31 at 09:12Z train 13's top (a428913c05d, #2784, 29 members) was pushed although this step had named failures in four structure gates (development semantics, the collision check, economics claims, rust quality), because exit 1 was reported, not refused. CI's run 41187739613 failed the same four in its structure job by 09:44Z, with shell lint, five integration tests and coverage beside them. The top was recomposed twice, and master, which had stood at 6b30ede2d13 since 2026-10-30 18:27Z, stayed there for more than 29 hours. A member's own branch is still reported: there, CI's verdict comes in time and costs one run. A top's run is the widest and slowest the repository has, and every member waits on it, so a failure the push has already named is not worth a run. The landing seat's procedure is in docs/INTEGRATION.md ("A train top is judged before it is pushed"). This rule refuses by the name: a top pushed under a name outside integration/train-* is only reported. The name is the landing seat's practice, set out in that section, not a clause of project.the-cooperation-model-is-the-owners-word.

No host may start a build below 35 GiB free (that rule's clause 7), and on 2026-11-01 the push that raised this rule ran with no executable of the tree's generation: its gates exited 12 with the launcher's refusal and its cases failed for that alone, which said nothing about the change. So, when no executable exists, exactly two things are left to CI: a gate whose exit 12 is the launcher's own refusal (run: just build), named NOT JUDGED, and a fast case that calls rust_bin, left out by name. That is not a promise that a top can be pushed without an executable. A gate that exits 12 for a reason of its own (CANNOT MEASURE, as collision-check, nav-check, worktree-check and evidence-check say it), and a fast case that reaches the executable without calling rust_bin, still fail and still refuse a top.

Požadované chování

  • .githooks/pre-push calls scripts/ci/run-plan --before-push after finish --check. The policy declares it (enforcement: structure-gates-on-push, wired by git-hook:pre-push), so doctor finds a clone whose hook stops calling it: the commit hook reports that finding and CI's core-check, which runs doctor again, refuses it.
  • The step computes the push's plan, as CI does, from the commits the push sends: the merge base up to HEAD (scripts/ci-plan --head HEAD), never the working tree. An untracked file is not a change a push carries; on 2026-10-22 git's .merge_file_* temporaries, left at a checkout's root by an interrupted merge, put a push that only deleted remote refs into the full plan for more than 15 minutes (#2372). The step refuses the push (exit 12) when the plan cannot be computed. scripts/ci-plan refuses a base or head that names no commit. It used to pipe a failing git diff into sort, whose status hid the failure, and so returned a plan computed from nothing with exit 0.
  • Kontroly. Spouští vybrané strukturní kontroly plánu, které nejsou v network: true označeny .ai/repo/ci/gates.yaml a jejichž zaznamenané sekundy nejsou víc než MJ_PUSH_GATE_SECONDS (výchozí 15). Sekundy pocházejí z .ai/repo/ci/structure-durations.tsv, které scripts/ci-baseline --structure-durations zapisuje z artefaktů ci-metrics-structure, jež CI nahrává. Bere je od nejlevnější, dokud jejich součet zůstává v MJ_PUSH_GATE_BUDGET (výchozí 120).
  • Skipped gates. Every gate left out prints one line naming it and why: it reaches the network, it has no recorded duration, it is over the per-gate limit, or it is past the budget. A missing durations file refuses the push (exit 12).
  • Případy. Spouští případy, které .ai/repo/ci/suite-durations.tsv zaznamenal nejvýše MJ_PUSH_CASE_SECONDS (výchozí 5), po čtyřech zároveň. Jmenovitě vynechává případ, který deklaruje majordomus-exclusive nebo spouští server (serve_up, mcp_shared, serve ensure|start, launcher MCP). Rychlé je to, co zaznamenalo CI. Případ se serverem měří časování a porty a na stroji, který sdílí devět relací, selže kvůli zátěži, ne kvůli změně: pět jich tak selhalo při prvním běhu.
  • Bound. It reports its measured time against MJ_PUSH_BOUND (default 180). An overrun names the slowest steps and never refuses the push: a busy machine must not fail it, or people route around the step.
  • Not judged here. When the step finds no executable of this tree's generation (its line executable: none), a gate that exits 12 and whose output carries the launcher's refusal (lib/rust_bin.sh's run: just build line) is printed NOT JUDGED <gate>, collected in the last line as not judged here: <gates>, and does not count as a failure, and a fast case that calls rust_bin (test/lib.sh) is left out by name. A gate that exits 12 without that line, and any exit 12 while an executable is present, is a failure like any other. A step whose only findings are not judged exits 0 and ends nothing judged here failed.
  • Verdict. A failed gate or case exits 1 with the tail of its output and a last line that names it and says CI would refuse the push for the same reason. The hook prints reported, not refused: CI holds the verdict and lets the push through; CI's validate refuses the same head. Any other non-zero exit of the step, 12 above all, refuses the push: what cannot be measured is not passed.
  • Train tops. The hook collects the remote refs of the push that receive a commit. When the step exits 1 and one of them is under refs/heads/integration/train-*, the hook prints refused: this push carries the train top <names>, and a train top is judged before it is pushed and exits 1, whatever else the push carries. A train ref that is only deleted is no top.
  • Stdin. Každá kontrola a každý případ běží se zavřeným stdin. Stdin hooku je seznam refů. test/run.sh dal každému případu stdin runneru, protože úloha na pozadí pod řízením úloh si jej ponechává, takže případ, který četl stdin, čekal věčně na otevřené rouře: případ 54, 39 minut, téhož dne.
  • Deletions. A push whose every ref has an all-zero local sha carries no commit, so the hook exits 0 before finish --check and the plan, as it does for refs/majordomus/* (I4008: 16 deletions waited 2400 s on the build lock, 2026-10-22); one ref that carries a commit and the whole hook applies.

What refuses and what reports

HookKontrolaLocal verdictProč
pre-pushbin/majordomus finish --checkrefusesthe task's scope and contract exist only on this machine
pre-pushscripts/case-numbers checkrefuseseach branch is clean alone; the pair exists only after a merge
pre-pushscripts/ci/run-plan --before-push, exit 12refusesa plan that cannot be computed is not a pass
pre-pushscripts/ci/run-plan --before-push, exit 1, on a push that carries no train topreportsCI's structure job and suite run the same gates and cases
pre-pushscripts/ci/run-plan --before-push, exit 1, on a push that carries a refs/heads/integration/train-* refrefusesa top's CI run is the widest the repository has and every member waits on it; train 13 was pushed over four failures this step had named (2026-10-31)
pre-pusha gate's exit 12 that is the launcher's refusal (run: just build), with no executable of the tree's generation, any pushnot judged: named, never exit 1it says nothing about the change, and no host may build below the 35 GiB floor
pre-pusha gate's exit 12 for a reason of its own (CANNOT MEASURE), any pusha failure: reported, or refused on a train topwhat cannot be measured is not passed
pre-pushscripts/rust-check --docreportsCI's rustdoc job refuses the same head
pre-commitbin/majordomus doctorreportsCI's core-check runs doctor and refuses
pre-commitbin/majordomus-cli worktree guardrefusesCI checks out a fresh clone and never sees which worktree a commit came from (rule project.worktree-topology)
pre-commitscripts/pages currentrefusesa stale derive breaks the deploy for whoever lands next
pre-commitscripts/ci/providers-check --permissions-onlyrefusesthe owner-authorized SSH permissions of every native client

A reported finding prints one line on stderr, <hook>: <check> failed (exit <n>); reported, not refused: CI holds the verdict, after the check's own output. The hooks carry the report function themselves; there is no sourced helper.

Chování při selhání

A refusing check exits the hook with its own code, naming what failed. A reporting check never changes the hook's exit. There is no flag that skips any step, as there is no --no-verify.

Ověření

test/cases/1021_a_push_runs_what_ci_would_find_first.sh řídí krok nad plánem a trváními, které zapisuje. Dokazuje:

  • a cheap gate runs, and a cheap failing gate exits 1 with its output and a line that names it and says CI holds the verdict;
  • každý druh vynechané kontroly je jmenován s důvodem: síť, žádné zaznamenané trvání, přes limit na kontrolu a za rozpočtem;
  • a missing durations file, or a plan that cannot be computed (an unknown base), exits 12;
  • hook volá krok, politika deklaruje vynucování a plán nese síťový příznak modelu;
  • případ, který čte stdin v rouře, jež se nikdy nezavře, už runner nezablokuje.

test/cases/4008_a_push_that_only_deletes_plans_nothing.sh: a deletion-only push runs no finish, case-numbers or run-plan, and a mixed push still runs all three.

test/cases/3948_a_local_gate_reports_and_ci_decides.sh runs the real hooks in a fixture whose checks are stubs with chosen exit codes. It proves:

  • a push whose step exits 1 exits 0, with the gate's line and the hook's report line;
  • a push whose step exits 1 and that carries a train ref is refused with 1, naming the top, alone and beside a feature ref; a green top goes on; a push that only deletes a train ref beside a feature ref is reported, not refused;
  • with no executable, a train top whose gate exits 12 with the launcher's refusal goes on with that gate NOT JUDGED and the case that calls rust_bin left out; beside a failed case the same top is refused, naming both; a gate that exits 12 for a reason of its own is a failure and the top is refused; with an executable, the gate's exit 12 is a failure and the top is refused;
  • a push whose step exits 12, whose finish --check fails or whose case numbers collide is refused with that code;
  • a push that changes the crate and whose rustdoc fails exits 0 with a report line;
  • a commit whose doctor fails exits 0 with a report line, and one whose pages current, worktree guard or SSH permissions check fails is refused with that code;
  • each call is still on a line doctor's wiring check matches.

První běh kroku na vlastní branchi tu branch odmítl kvůli nepřipnutému sort (order-check), dřív než ji viděla jakákoli CI.

Odkud to pochází

kanonický soubor
.ai/repo/rules/project/a-push-runs-what-ci-would-find-first.v3.md
identity
project.a-push-runs-what-ci-would-find-first@3
dostupné také jako
Zdroj MCP majordomus://rule/project.a-push-runs-what-ci-would-find-first@3 · majordomus entity show rule/project-a-push-runs-what-ci-would-find-first-3 · GET /api/v1/entity?uri=majordomus://rule/project.a-push-runs-what-ci-would-find-first@3
v Cockpitu
/cockpit/objects/rule/project-a-push-runs-what-ci-would-find-first-3

Tato stránka je generována z výše uvedeného souboru pomocí scripts/generate-site-data; úprava stránky nic nemění, úprava souboru změní všechna rozhraní. Výše uvedené vztahy jsou hrany docs/generated/graph.json, které spustitelný program zapisuje ze stejného indexu.

Prohledává názvy a popisy všech stránek, které tento web publikuje.