Objective
Make scripts/dev/update_service_base_images.py rewrite and --verify every service
template the preflight declares, including nested ones, so that the guard currently blocking
nested service templates can be removed.
Context you need
service_templates() in src/cli/managers/base_image_preflight.py walks the template
directory recursively (PR #388, merged as fix/issue-383-nested-service-templates). Two
functions in the release rewriter still do not:
verify_base_tags — scripts/dev/update_service_base_images.py:322
update_base_tags — scripts/dev/update_service_base_images.py:382
Both iterate sorted(DOCKERFILES_DIR.glob("Dockerfile*")), which does not descend into
subdirectories. DOCKERFILES_DIR is defined at :13.
The consequence, if a nested service template existed: .github/workflows/test-and-build-tag.yml
would rewrite only the top-level templates and its three --verify invocations would all
pass, while the nested template still named whatever base reference it was committed with.
A release could ship one service built on a stale development base image with every check
green.
Why this is currently latent, and what is holding it. There are zero nested service
templates today — the only nested files, base-python-image/Dockerfile and
base-pytorch-image/Dockerfile, are excluded by NON_SERVICE_TEMPLATES. PR #388's review
closed the gap with a guard rather than the capability:
nested_service_templates() in src/cli/managers/base_image_preflight.py
test_no_service_template_is_nested_while_the_release_rewriter_is_top_level_only in
tests/unit/test_base_image_preflight.py
That test fails the moment a nested service template appears, naming the two call sites
above. So nothing can ship broken — but nothing can be nested either. This issue is the
capability the guard is standing in for.
Why it was deferred. Raised twice by automated review on PR #388
(#388) during the 2026-08-29 nightly review. Making
release automation recursive is a change to the rewriter, its own test module, and two CI
workflows; it does not belong in a PR whose subject was the preflight's traversal.
Constraints
- Branch from
origin/dev. Open the PR with
gh pr create --repo fasrc/archi --base dev. Never commit to dev directly.
origin is fasrc/archi. Do not target upstream (archi-physics/archi).
- No
Co-Authored-By and no session trailers on commits in this repo.
- TDD: failing test first, watch it fail, then the minimum code, then the gate.
bash scripts/gate.sh must pass before every commit. Never --no-verify.
- Do not touch
deploy/fasrc-dev/**.
- The rewriter is the only writer of service
FROM lines. Do not hand-edit a template.
--verify must keep failing on a run that matched no reference at all — a check that
reads nothing must not pass. See the --verify description in
docs/docs/developer_guide.md.
Plan
One PR. The rewriter change and the guard removal must land together, or the guard blocks
the very thing the change enables.
- Write a failing test in
tests/unit/test_update_service_base_images.py: a template
directory with a nested sub/Dockerfile-svc pinned to an old tag, asserting
update_base_tags rewrites it. Watch it fail.
- Write a failing test that
verify_base_tags exits non-zero when that nested template
names a reference other than the target, and zero when it matches.
- Replace both
DOCKERFILES_DIR.glob("Dockerfile*") calls with the recursive traversal.
Prefer importing service_templates() from the preflight over a second rglob, so the
rewriter and the preflight cannot disagree about the set; if the import direction is
wrong for a scripts/ tool, add a shared helper rather than duplicating the walk.
Check whether the "checked" counter and the "matched no reference" failure path still
behave when the set grows.
- Remove
test_no_service_template_is_nested_while_the_release_rewriter_is_top_level_only
and nested_service_templates(), and replace the guard with a test that a nested service
template is rewritten and verified end to end. Removing the guard without that
replacement is what this issue must not do.
- Update
docs/docs/developer_guide.md, in "Pointing the service templates at a base
image": the bullet stating the rewriter is not recursive, and the sentence naming the
guard test, both become wrong.
Commands
git fetch origin && git checkout -b fix/issue-<N>-recursive-rewriter origin/dev
# the two non-recursive call sites
grep -n 'DOCKERFILES_DIR.glob' scripts/dev/update_service_base_images.py
# the guard this issue retires
python -m pytest tests/unit/test_base_image_preflight.py -q -k nested
python -m pytest tests/unit/test_update_service_base_images.py -q
python -m pytest tests/unit/test_base_image_preflight.py -q
python -m pytest tests/unit/test_python_version_declaration.py -q
bash scripts/gate.sh
Acceptance criteria
Start here
Run grep -n 'DOCKERFILES_DIR.glob' scripts/dev/update_service_base_images.py and read both
functions around those lines. Then write the step-1 test and watch it fail before changing
the rewriter.
Objective
Make
scripts/dev/update_service_base_images.pyrewrite and--verifyevery servicetemplate the preflight declares, including nested ones, so that the guard currently blocking
nested service templates can be removed.
Context you need
service_templates()insrc/cli/managers/base_image_preflight.pywalks the templatedirectory recursively (PR #388, merged as
fix/issue-383-nested-service-templates). Twofunctions in the release rewriter still do not:
verify_base_tags—scripts/dev/update_service_base_images.py:322update_base_tags—scripts/dev/update_service_base_images.py:382Both iterate
sorted(DOCKERFILES_DIR.glob("Dockerfile*")), which does not descend intosubdirectories.
DOCKERFILES_DIRis defined at:13.The consequence, if a nested service template existed:
.github/workflows/test-and-build-tag.ymlwould rewrite only the top-level templates and its three
--verifyinvocations would allpass, while the nested template still named whatever base reference it was committed with.
A release could ship one service built on a stale development base image with every check
green.
Why this is currently latent, and what is holding it. There are zero nested service
templates today — the only nested files,
base-python-image/Dockerfileandbase-pytorch-image/Dockerfile, are excluded byNON_SERVICE_TEMPLATES. PR #388's reviewclosed the gap with a guard rather than the capability:
nested_service_templates()insrc/cli/managers/base_image_preflight.pytest_no_service_template_is_nested_while_the_release_rewriter_is_top_level_onlyintests/unit/test_base_image_preflight.pyThat test fails the moment a nested service template appears, naming the two call sites
above. So nothing can ship broken — but nothing can be nested either. This issue is the
capability the guard is standing in for.
Why it was deferred. Raised twice by automated review on PR #388
(#388) during the 2026-08-29 nightly review. Making
release automation recursive is a change to the rewriter, its own test module, and two CI
workflows; it does not belong in a PR whose subject was the preflight's traversal.
Constraints
origin/dev. Open the PR withgh pr create --repo fasrc/archi --base dev. Never commit todevdirectly.originisfasrc/archi. Do not targetupstream(archi-physics/archi).Co-Authored-Byand no session trailers on commits in this repo.bash scripts/gate.shmust pass before every commit. Never--no-verify.deploy/fasrc-dev/**.FROMlines. Do not hand-edit a template.--verifymust keep failing on a run that matched no reference at all — a check thatreads nothing must not pass. See the
--verifydescription indocs/docs/developer_guide.md.Plan
One PR. The rewriter change and the guard removal must land together, or the guard blocks
the very thing the change enables.
tests/unit/test_update_service_base_images.py: a templatedirectory with a nested
sub/Dockerfile-svcpinned to an old tag, assertingupdate_base_tagsrewrites it. Watch it fail.verify_base_tagsexits non-zero when that nested templatenames a reference other than the target, and zero when it matches.
DOCKERFILES_DIR.glob("Dockerfile*")calls with the recursive traversal.Prefer importing
service_templates()from the preflight over a secondrglob, so therewriter and the preflight cannot disagree about the set; if the import direction is
wrong for a
scripts/tool, add a shared helper rather than duplicating the walk.Check whether the "checked" counter and the "matched no reference" failure path still
behave when the set grows.
test_no_service_template_is_nested_while_the_release_rewriter_is_top_level_onlyand
nested_service_templates(), and replace the guard with a test that a nested servicetemplate is rewritten and verified end to end. Removing the guard without that
replacement is what this issue must not do.
docs/docs/developer_guide.md, in "Pointing the service templates at a baseimage": the bullet stating the rewriter is not recursive, and the sentence naming the
guard test, both become wrong.
Commands
Acceptance criteria
grep -n 'DOCKERFILES_DIR.glob' scripts/dev/update_service_base_images.pyreturnsnothing.
update_base_tagsandreported by
verify_base_tagswhen it disagrees with--tag.--verifystill exits non-zero on a run that matched no reference at all.nested_service_templates()and its guard test are gone, replaced by a test thatexercises a nested template through rewrite and verify.
bash scripts/gate.shexits 0 with patch coverage at or above 80%.docs/docs/developer_guide.mdno longer claims the rewriter is non-recursive.fasrc/archi:devand its body carries aCloses #<N>keyword — in thebody, not the title, or the issue is not linked.
Start here
Run
grep -n 'DOCKERFILES_DIR.glob' scripts/dev/update_service_base_images.pyand read bothfunctions around those lines. Then write the step-1 test and watch it fail before changing
the rewriter.