Raised while reviewing #15721, which correctly collapsed three shell copies of the SLM-frontend build-and-publish into one. That was the right move, and it leaves a residual worth naming.
Two canonical implementations, one contract
The publish contract — build with build:slm, abort hard on failure, build into a fresh directory, prove a non-empty index.html, flip through .current.next with mv -T — now exists twice:
| implementation |
consumers |
autobot-slm-backend/ansible/roles/_shared/tasks/build_publish_slm_frontend.yml |
the Ansible entry points (#15557) |
autobot-infrastructure/autobot-slm-frontend/templates/build-publish-slm-frontend.sh |
bootstrap-slm.sh, sync-frontend.sh (#15721) |
Two languages, so this is not fixable by deleting one — a shell script on a freshly bootstrapped node cannot invoke an Ansible task. The duplication is structural.
Why it needs more than "both are tested"
Each side has a guard asserting the same five properties, and both pass today. But they are parallel assertion lists, not a shared definition. If the idiom changes — say a future issue replaces mv -T with something better, as #15610 replaced the two-directory-rename shape — the Ansible guard is updated with it, and the shell guard keeps asserting the old contract and keeps passing.
That is the same failure mode #15557 fixed at the level below: four entry points each individually correct, with nothing holding them to one another. Collapsing to two implementations makes it much less likely, not impossible, and the property nobody checks is the agreement itself.
The stakes are unchanged from the outages that produced this family: vite empties its output directory before writing, so a publisher that drifts back to building into the served directory takes the site down, and a failed build behind an ungated publish leaves it down (#15430, #15462, #15557, #15610).
Acceptance criteria
Related
Raised while reviewing #15721, which correctly collapsed three shell copies of the SLM-frontend build-and-publish into one. That was the right move, and it leaves a residual worth naming.
Two canonical implementations, one contract
The publish contract — build with
build:slm, abort hard on failure, build into a fresh directory, prove a non-emptyindex.html, flip through.current.nextwithmv -T— now exists twice:autobot-slm-backend/ansible/roles/_shared/tasks/build_publish_slm_frontend.ymlautobot-infrastructure/autobot-slm-frontend/templates/build-publish-slm-frontend.shbootstrap-slm.sh,sync-frontend.sh(#15721)Two languages, so this is not fixable by deleting one — a shell script on a freshly bootstrapped node cannot invoke an Ansible task. The duplication is structural.
Why it needs more than "both are tested"
Each side has a guard asserting the same five properties, and both pass today. But they are parallel assertion lists, not a shared definition. If the idiom changes — say a future issue replaces
mv -Twith something better, as #15610 replaced the two-directory-rename shape — the Ansible guard is updated with it, and the shell guard keeps asserting the old contract and keeps passing.That is the same failure mode #15557 fixed at the level below: four entry points each individually correct, with nothing holding them to one another. Collapsing to two implementations makes it much less likely, not impossible, and the property nobody checks is the agreement itself.
The stakes are unchanged from the outages that produced this family: vite empties its output directory before writing, so a publisher that drifts back to building into the served directory takes the site down, and a failed build behind an ungated publish leaves it down (#15430, #15462, #15557, #15610).
Acceptance criteria
Related