The shims that dispatch a Run: run.yml, approve.yml and queue.yml, each held by its template and its hash (record 0157)

Merged
DO
2d ago
+437-39

This adds the three shims that dispatch a Run: run.yml, approve.yml and queue.yml. Each is held word for word by its template and by its hash. This is PR A of issue 196, "Workflows: the frozen entry shim, the poller, the Run's three jobs and the nudge, each held to its shape", as the Factory Builder ruled it on 2026-11-07. The poller's shim is PR B, which comes next.

Nothing goes live here. All three run only on workflow_dispatch, and nothing dispatches them yet, because the poller is not on main. Each if: refuses three things:

  • any dispatcher but the account 41898282, which the pass's own token runs as. Every workflow token here runs as that account too: what keeps a dispatch the pass's is that only the jobs pass, wake and lane are granted actions: write;
  • a re-run;
  • a Pause.

queue.yml is also never dispatched while config/controller.json names no queue App, under decision 0128.

The Pause. The repo's own FACTORY_PAUSED reads true, set by the Maintainer and read back, so nothing that merges here can start a Run.

Every file under .github/ here is a protected path, so each one is the Maintainer's to approve: approve.yml, queue.yml and run.yml.

The shims

FileCalls, at @factory/v0-nextTemplateHash in SHIMS
run.yml (sf-run)run-jobs.ymlRUN_SHIM, which already existede0888dab…
approve.yml (sf-approve)approve-job.ymlAPPROVE_SHIM, newde636e91…
queue.yml (sf-queue)queue-job.ymlQUEUE_SHIM, newf29c8cac…

What every shim has in common:

  • three required inputs: name, item and pass;
  • run-name: ${{ inputs.name }};
  • one job, lane, granted actions: write and contents: read. Its act's wake needs the write, and a called job gets no more than its caller grants (record 0115);
  • the concurrency group ${{ inputs.item }}, which cancels nothing;
  • secrets: inherit;
  • the same if:.

The called files are all in factory/v0.1.0, which is factory/v0-next at 499835d5. Each is held word for word, and main changes none of them.

What the check changes (tools/check_workflows.py)

  • LANE_SHIMS maps run.yml, approve.yml and queue.yml to their templates. lane_shim_problems replaces run_problems and holds each file to its template, its dispatcher being the account config/controller.json names.
  • A shim that dispatches a Run may name its run. Before, only run.yml could. The records port reads a Run by its run-name, so an act's Run named by its workflow alone would stay unidentified for ever, and under decision 0036 every admission would defer. Record 0128 also names each queue act by its run-name. No other shim may name its run.
  • CALLERS: no file but approve.yml calls approve-job.yml, and none but queue.yml calls queue-job.yml, just as none but run.yml calls run-jobs.yml.
  • The lane rule widens: a job that calls an act's job also holds its item's lane.
  • SHIMS holds the three hashes.

Records

  • 0157, the number the Factory Builder gave it: "An act's shim is the Run's but for its name and the file it calls". It widens record 0057's row "Its shape", where only run.yml names its run.
  • 0036's row waited on run.yml. It now names the Run's shim test, the act-shim test and the live-shim test, with their mutants. Its half on the pass's own run still waits on poller.yml, PR B.
  • 0055's row waited on approve.yml. It now names test_the_approving_act_holds_the_approvers_token_alone and test_the_shims_that_dispatch_a_run_stand_as_their_templates_and_their_hashes, with their mutants.

Checks

CheckResult
tests.test_tools.Workflows51 OK
make verify, on an export of 6e7d966, then of 41d66ab, with the record numbered 0157 in the export onlyPass, both
The 34 new and changed shim mutants, at 41d66ab34 of 34 killed
make verify, on an export of the numbered head c1b640e1659 tests OK (expected failures=8)

Reads

All four reads were of 6e7d966, before the fix round.

ReviewerHeadBlockingMajorMinorInfo
claude-review6e7d9660012
claude-security6e7d9660001
agy (Argon)6e7d9660620
astra, in Muse's place6e7d9660000

Muse was not run: it is out of quota until 2026-11-09T00:00Z.

FindingFromFixed in 41d66ab
The shims' if: admits the account every workflow's own token here runs as, not the pass's aloneclaude-securityThe record, the shims standard, the check and each shim's comment say what holds it: only pass, wake and lane are granted actions: write, and the act's step reads the item again. The hashes are recorded again
0036's re-pointed row names a test of run.yml alone, and no test of the live shimsclaude-review, agyIt names the act-shim and live-shim tests and their mutants. Its half on the pass's own run waits on poller.yml
The grant paragraph leaves out queue.yml and queue-job.ymlclaude-reviewNamed
An act's shim is never compared with the Run's beyond five linesagyIt is asserted equal to RUN_SHIM line for line, but for its name and the file it calls. New mutant: "an approval's name may be left out"
LANE_SHIMS is never asserted to hold the threeagyAsserted
No mutant puts back if name == "run.yml":agy"only run.yml is held to its shim"
No mutant for the queue act's re-run, its wake's write, its file, the approving act's lane, or APPROVE_JOB in CALLERSagyA mutant for each, all killed
No mutant disables the hash comparisonagyNo change: "a shim is held by nothing" already does, killed by test_a_shim_is_held_word_for_word
The record is unnumberedclaude-review, agy0157, in c1b640e

The three CodeQL notes, "unpinned tag", flag the Channel tag that a shim calls by design (decision 0006). The tag rulesets guard it (decisions 0115 and 0124). Each thread was answered and resolved. Whether to dismiss the alerts is the Maintainer's call.

🤖 Generated with Claude Code