From f174981b070fa830ed27e2a8a5540d6fda1630c2 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Tue, 22 Sep 2026 08:57:09 -0400 Subject: [PATCH] fix: ml and web are one image, so they are one path set (4296) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run 7271. `test_every_copied_path_is_in_the_artifacts_path_set` — a guard I did not know this repo had — caught the merge's real consequence within minutes, and it is precisely #3202's class: ml: Dockerfile copies 'frontend/package.json' into the ml image, but no include in scripts/artifacts.sh covers it. web: Dockerfile copies 'requirements-ml.txt' into the web image, but no include covers it. Both follow from step 6 and neither was visible by reading my own diff. The merged Dockerfile carries the frontend-builder stage, so the ml image now copies frontend files; and the web image now installs the ML requirements. Left as they were, each artifact's derived version would have stopped moving when those files changed, and a pinned build would serve stale bytes — silently, since nothing fails when a version simply does not advance. The fix is structural rather than two more entries. `fabledcurator-ml` IS web's image now: same Dockerfile, same context, same bytes, published under a second name only because the operator's Swarm stack still references it. So ML_PATHS is WEB_PATHS by assignment, and `cmd_paths` gives ml the same deriver and extension append — the XPI is in those bytes too. Two lists describing one image is the duplication this milestone has been collapsing all day. It existed for about an hour and the guard found it first, which is the argument for the guard. I also predicted this failure would be the image build. It was not; the build passed and the unit lane failed. Worth noting because the prediction was confident and wrong, and reading the log took one call. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR --- scripts/artifacts.sh | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/scripts/artifacts.sh b/scripts/artifacts.sh index fcce132..6a3df2f 100755 --- a/scripts/artifacts.sh +++ b/scripts/artifacts.sh @@ -49,15 +49,22 @@ ROOT=$(git rev-parse --show-toplevel) # frontend/public/extension/ before the docker build), so an extension change # changes the web image. The extension's packaged set is appended in cmd_paths # rather than restated — one definition, per #2397. -WEB_PATHS='Dockerfile requirements.txt backend alembic alembic.ini entrypoint.sh frontend :(exclude)frontend/test :(exclude)frontend/test/**' +WEB_PATHS='Dockerfile requirements.txt requirements-ml.txt backend alembic alembic.ini entrypoint.sh frontend :(exclude)frontend/test :(exclude)frontend/test/**' -# ml (Dockerfile, context `.`) — no frontend, no extension. Note it copies -# BOTH requirements-ml.txt and requirements.txt. -# Dockerfile, not Dockerfile.ml: the images merged at milestone 422 step 6 -# and Dockerfile.ml is gone. A path set naming a deleted file silently -# stops contributing to the derived revision, which is what the reuse -# check and the version string both read (#3202's shape). -ML_PATHS='Dockerfile requirements-ml.txt requirements.txt backend alembic alembic.ini entrypoint.sh' +# ml — THE SAME IMAGE as web since milestone 422 step 6, built from the same +# Dockerfile with the same context and published under a second name only +# because the operator's Swarm stack still references it. +# +# So it is the same path set, by assignment rather than by a copy that would +# drift. This was two lists describing one image for about an hour, and the +# artifact-paths guard caught it immediately: the merged Dockerfile carries +# the frontend-builder stage, so the ml image copies `frontend/package.json`, +# and the old ml list did not cover it — its version would not have moved when +# the frontend changed, and a pinned build would serve stale bytes (#3202). +# +# `requirements-ml.txt` is in WEB_PATHS for the mirror-image reason: the web +# image now installs the ML requirements, so changing them changes it. +ML_PATHS="$WEB_PATHS" # agent (agent/Dockerfile, context `agent`) — copies requirements.txt and # fc_agent only. agent/README.md, agent/docker-compose.yml and agent/ruff.toml @@ -108,7 +115,9 @@ ext_paths() { cmd_paths() { case "$1" in web) echo "$WEB_PATHS $DERIVER $(ext_paths)" ;; - ml) echo "$ML_PATHS" ;; + # Identical to web, deliberately: it IS web's image. That includes the + # deriver and the bundled extension — the XPI is in these bytes too. + ml) echo "$ML_PATHS $DERIVER $(ext_paths)" ;; agent) echo "$AGENT_PATHS" ;; extension) ext_paths ;; *) usage ;;