Found in the cold review of #2337 (WM-1104), polish item 13. Not blocking that PR; filed separately per the discovered-work rule.
Problem
config/repos.yaml runtime_roles[].paths is documented — in docs/ship.md, in config/repos.example.yaml, and now enforced as required by loadShipConfig — as mirroring the publisher workflow's own on.push.paths. That is not a convention but the definition of stale the check uses: the publisher rebuilds when those paths move, so a pin older than a change to one of them is stale by the publisher's own rule.
Mirroring means the two copies can drift, and nothing notices. legalease's case_agent role lists four paths; .github/workflows/case-agent-image.yml has its own on.push.paths. If someone adds a fifth path to the workflow, the publisher starts rebuilding on it while ship-preflight keeps reporting the pin fresh — a silent false PASS on the release gate, in the one direction that matters.
The WM-1104 fix round made a missing paths a hard config error, which closes the "check that can never fail" hole. It does nothing about the "check that reads the wrong definition of stale" hole.
Two options
- Read the workflow file (preferred): drop
paths from the config for roles whose publisher lives in the same repo and parse on.push.paths out of .github/workflows/<publisher_workflow> at the tip being shipped. Single source of truth, drift impossible. Cost: the module has to read a file out of the target checkout (git show <tip>:.github/workflows/<wf>, which is the honest read — the paths that applied at that commit) and parse YAML. It also has to decide what to do when the workflow has no on.push.paths at all (answer: FAIL, same as today's missing-paths case).
- Drift-check: keep
paths in config but add a check that compares it against the workflow's on.push.paths and FAILs pre-flight when they differ. Cheaper, keeps the config self-documenting, but keeps the duplication.
Option 1 also removes a required config key from every repo that adopts ship-preflight, which is the larger win.
Acceptance
- Adding a path to the publisher workflow without touching
config/repos.yaml changes the runtime-pin:<role> verdict (option 1) or FAILs pre-flight with a drift message naming both lists (option 2).
- A publisher workflow with no
on.push.paths is a FAIL with a clear message, not a SKIP or a PASS.
docs/ship.md and config/repos.example.yaml stop telling operators to hand-mirror a list, or say explicitly that the drift check exists.
Owned paths
orchestrator/ship.mjs
orchestrator/ship.test.mjs
config/repos.example.yaml
docs/ship.md
Found in the cold review of #2337 (WM-1104), polish item 13. Not blocking that PR; filed separately per the discovered-work rule.
Problem
config/repos.yamlruntime_roles[].pathsis documented — indocs/ship.md, inconfig/repos.example.yaml, and now enforced as required byloadShipConfig— as mirroring the publisher workflow's ownon.push.paths. That is not a convention but the definition of stale the check uses: the publisher rebuilds when those paths move, so a pin older than a change to one of them is stale by the publisher's own rule.Mirroring means the two copies can drift, and nothing notices. legalease's
case_agentrole lists four paths;.github/workflows/case-agent-image.ymlhas its ownon.push.paths. If someone adds a fifth path to the workflow, the publisher starts rebuilding on it whileship-preflightkeeps reporting the pin fresh — a silent false PASS on the release gate, in the one direction that matters.The WM-1104 fix round made a missing
pathsa hard config error, which closes the "check that can never fail" hole. It does nothing about the "check that reads the wrong definition of stale" hole.Two options
pathsfrom the config for roles whose publisher lives in the same repo and parseon.push.pathsout of.github/workflows/<publisher_workflow>at the tip being shipped. Single source of truth, drift impossible. Cost: the module has to read a file out of the target checkout (git show <tip>:.github/workflows/<wf>, which is the honest read — the paths that applied at that commit) and parse YAML. It also has to decide what to do when the workflow has noon.push.pathsat all (answer: FAIL, same as today's missing-pathscase).pathsin config but add a check that compares it against the workflow'son.push.pathsand FAILs pre-flight when they differ. Cheaper, keeps the config self-documenting, but keeps the duplication.Option 1 also removes a required config key from every repo that adopts
ship-preflight, which is the larger win.Acceptance
config/repos.yamlchanges theruntime-pin:<role>verdict (option 1) or FAILs pre-flight with a drift message naming both lists (option 2).on.push.pathsis a FAIL with a clear message, not a SKIP or a PASS.docs/ship.mdandconfig/repos.example.yamlstop telling operators to hand-mirror a list, or say explicitly that the drift check exists.Owned paths
orchestrator/ship.mjsorchestrator/ship.test.mjsconfig/repos.example.yamldocs/ship.md