Skip to content

Run the instruction drift check as a step - #994

Merged
sehkone merged 3 commits into
mainfrom
sehkone/issue-993
Sep 10, 2026
Merged

Run the instruction drift check as a step#994
sehkone merged 3 commits into
mainfrom
sehkone/issue-993

Conversation

@sehkone

@sehkone sehkone commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Closes #993.

The drift check was a job calling aicers/agent-instructions/.github/workflows/check-drift.yml@main as a reusable workflow. Actions is free on a public repository, so there is no bill to cut here — what the arrangement cost was a job: a scheduling slot and a row in every run, and this is the busiest consumer on the list, with at least 300 CI runs in the last thirty days. As a step it runs inside a job that already exists. aicers/agent-instructions#60 shipped the check as a composite action, and every other consumer already carries it this way.

What changed

  • ci.yml: the instructions job is gone; check gains id: checkout on its checkout and - uses: aicers/agent-instructions/check-drift@main as its last step, after cargo audit.
  • update-instructions.yml: the header comment said the apply moves the pin in .agents/instructions.toml. No such file exists in any consumer — the pin is .agent-instructions.toml. The other copy of that wrong path was in the job comment this change deletes.

Nothing else in check is touched — Swatinem/rust-cache, actions/setup-python and the marketplace lint actions in that job are separate questions, and folding any of them in would bury this change. .agent-instructions.toml and the marked blocks in AGENTS.md are untouched, check stays on ubuntu-latest, and the check does the same work with the same messages.

Why check, and not the Markdown job the siblings use

There is no Markdown job here. Run Markdown Lint is a step inside check, beside the Rust, Python, Biome and validation steps.

What decides it is the docs_only gate. changes counts docs/**, **/*.md, mkdocs.yml and .markdownlint* as documentation, and AGENTS.md matches **/*.md — so a pull request confined to a generated region is docs-only:

  • test-core and test-docker-e2e-matrix are gated on docs_only != 'true', so a step in either would be skipped on precisely the pull requests that can cause drift.
  • test-registrar-redteam-docs is gated the other way, on docs_only == 'true', so a step there would be skipped on every pull request that touches any code.

check is the only job with no if: at all.

Why the step is last, and guarded

      - uses: aicers/agent-instructions/check-drift@main
        if: ${{ !cancelled() && steps.checkout.outcome == 'success' }}

This matters more here than in any other consumer: check has roughly twenty steps ahead of the new one — cargo clippy, cargo doc, ruff, biome ci, five validation scripts, cargo audit — and a step's default success() would skip the drift check whenever any of them failed. As a separate job it reported regardless, and it should: a drifted block is drift whether or not clippy is also unhappy. !cancelled() rather than always() so a cancelled run stops; the checkout clause keeps a failed checkout from being reported as drift, since the action fails outright when it cannot read the pin file and that message names the pin for a problem that is not the pin's.

Branch protection

Nothing to edit, and this is the part CI cannot demonstrate. GET /repos/aicers/bootroot/branches/main/protection answers 404 "Branch not protected", so no status check is required on main and Instructions / check never was. Organization-level rulesets need admin:org to read and were not inspected — worth a maintainer's glance that no organization rule names the check this removes.

Verification

actionlint reports nothing new on the edited file. It does report three shellcheck findings — SC2129, SC2155, SC2034 — but all three are pre-existing: the same three appear when actionlint is run against main's copy of this file, at lines 188, 296 and 381 there and at 223, 331 and 416 here only because the drift step shifted the line numbers. They are left alone rather than folded into an unrelated change.

A drift run and its revert are posted below.

The check was a job calling `check-drift.yml` as a reusable workflow.
Actions is free on a public repository, so nothing was billed for it;
what it cost was a job -- a scheduling slot and a row in every run, and
this is the busiest consumer on the list -- for nine seconds of byte
comparison. As a step it runs inside a job that already exists, and the
fleet's other consumers now carry it the same way.

`check` hosts it because the step must not inherit the `docs_only`
gate. `changes` counts `**/*.md` as documentation and AGENTS.md is a
`.md` file, so a pull request that edits only a generated region is
docs-only: `test-core` and the Docker E2E matrix would skip the check
on exactly the changes that cause drift, and the redteam job, gated the
other way, would skip it on every change that touches code. `check`
carries no `if:` and already runs the Markdown lint. There is no
`Markdown` job here to use instead, as there is in every other
consumer.

The step goes last and carries a guard. A step's default `success()`
would skip it whenever any of the twenty steps above failed -- clippy,
ruff, biome, five validation scripts and cargo audit among them -- and
this job has more ways to fail before reaching it than any other in the
fleet. As a job it reported regardless, and it should.

The header of update-instructions.yml named `.agents/instructions.toml`,
which exists in no consumer; the pin is `.agent-instructions.toml`.

Closes #993
Temporary: proves the drift check still fails from its new home.
Reverted in the next commit.
Restores the marked block. The failing run it produced is linked from
the pull request.
@sehkone

sehkone commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Test plan results

A drifted block fails. Run 34420384401 (8db751b, one character inside shared:workflow in AGENTS.md) — Quality Check failed, and actions/jobs/<id> reports exactly one failed step in it: Run aicers/agent-instructions/check-drift@main. Every step ahead of it — clippy, rustdoc, ruff, biome, the Markdown lint, the five validation scripts, cargo audit — passed, so the failure is the drift check and nothing else. The log also shows pinned to 0.4.0; the latest release is 0.4.0: the pin-behind warning resolved the latest release rather than reporting that it could not, which are two log lines that look alike and mean opposite things.

One thing in that run is worth reading carefully, because it looks like evidence and is not. All three downstream jobs report skipped:

Unit & CLI Smoke              = skipped
Docker E2E (all scenarios)    = skipped
Registrar Redteam (docs-only) = skipped

That is not the change filter. Those jobs are needs: [check, changes], and check had just failed, so they were skipped for the dependency, not the gate. The tell is that Unit & CLI Smoke (gated on docs_only != 'true') and Registrar Redteam (gated on docs_only == 'true') are skipped together — the gate is mutually exclusive, so it can never skip both. Anyone reading this run as a docs-only demonstration would be reading it wrong.

The docs-only case rests on check carrying no if: at all, which is the whole reason it is the host job here.

The revert goes green. Run 34421788966 (def78c7) — Quality Check, Change Filter, Unit & CLI Smoke and all fifteen Docker E2E scenarios success; Registrar Redteam (docs-only) skipped, correctly, this branch not being docs-only. No job named Instructions / check appears in either run.

@sehkone
sehkone merged commit b0328a3 into main Sep 10, 2026
22 checks passed
@sehkone
sehkone deleted the sehkone/issue-993 branch September 10, 2026 02:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Run the instruction drift check as a step, not a job

1 participant