feat: the agent derives its version instead of being asked to remember it (4386)
CI and images / lint (push) Successful in 3s
CI and images / extension-version (push) Successful in 2s
CI and images / frontend-build (push) Successful in 19s
extension / lint (push) Successful in 21s
CI and images / backend-lint-and-test (push) Successful in 33s
CI and images / integration (push) Successful in 2m25s
CI and images / sign-extension (push) Successful in 4s
CI and images / build-web (push) Successful in 2m1s
CI and images / smoke-web (push) Successful in 1m2s
CI and images / build-agent (push) Successful in 10m7s
CI and images / promote (push) Skipped

Operator: the agent's build string could not identify the agent. VERSION was a
literal in app.py an author was meant to bump, and nobody did — the September
image printed the same "2026-07-17.1" as the July one, so the one surface
meant to answer "did my pull work?" answered the same either way.

Nothing new was needed. scripts/artifacts.sh has derived a version per
artifact since milestone 313, and build-agent has been computing the agent's
on every run and printing it to the log. The image just never carried it.

Three values, never folded together (rule 149):

  FC_VERSION   YYYY.MM.DD.HHMM from the COMMIT its shipped files last changed
               in — identical on dev and main for the same source, which is
               what makes "am I running production's code?" answerable.
  FC_CHANNEL   a sibling field, never a suffix inside the name.
  FC_REVISION  the 12-char sha; the same string as the fc.revision LABEL, so
               the image and the registry cannot disagree about which commit
               this is.

The page SHOWS the version and COMPARES the revision. Those were one value
before, which is how a version acquires a second job and then cannot be
changed without breaking the reload banner. An unstamped local build reads
`unknown` and compares `local` — absent rather than empty, one spelling of
"cannot say".

scripts/artifacts.sh joins the AGENT path set in the same commit, and it had
to: a version has no backstop. A revision that is computed differently stops
matching the published label and forces a rebuild, so it self-corrects; a
version is compared against nothing, so a change to cmd_version alone would
leave the agent publishing the old format with nothing to contradict it. That
is #3202's finding, and the agent was rightly exempt only while it had no
version of its own. tests/test_artifact_paths.py pins it.

Also corrects two build.yml comments claiming agent/ had not changed since
2026-07-17. Both were already false — it changed 2026-09-23 — and one of them
is the stated rationale for the force_build escape hatch. Rewritten without
dates: how long an artifact has been quiet is a `git log` question, and its
answer in a comment is wrong the next time anyone commits (lesson #4383).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
This commit is contained in:
2026-09-24 07:07:31 -04:00
co-authored by Claude Opus 5
parent 970d17f98a
commit e1cd629196
7 changed files with 325 additions and 37 deletions
+22 -9
View File
@@ -55,20 +55,33 @@ WEB_PATHS='Dockerfile requirements.txt requirements-ml.txt backend alembic alemb
# fc_agent only. agent/README.md, agent/docker-compose.yml and agent/ruff.toml
# live in the directory but never reach the image, so they must not re-version
# it: this is deliberately NOT `agent/`.
#
# $DERIVER is appended in cmd_paths, not here, so the two artifacts that stamp
# a version pick it up the same way rather than one of them carrying it in its
# literal — see DERIVER below for why it belongs to both.
AGENT_PATHS='agent/Dockerfile agent/requirements.txt agent/fc_agent'
# This file. It is copied into no image and it is still part of what the web
# image is built from, because it DECIDES the FC_VERSION baked into that image
# (#3202). Same finding as #3156 about packaging.sh, one level up.
#
# Why web and nothing else. Every artifact stamps `fc.revision`, but only web
# also stamps a version (build.yml line ~488 feeds `version web` to the
# FC_VERSION build arg; the agent asks for `revision` alone, and the
# extension takes its version from packaging.sh). For a revision-only artifact
# this file needs no entry: any change to how the revision is COMPUTED changes
# the derived value, which then disagrees with the label on the published image
# and forces a rebuild. That mechanism is self-correcting because it compares
# against a string stamped into a real artifact.
# Web AND agent, since 2026-09-24. Both stamp a VERSION into the image they
# build (FC_VERSION), and the extension takes its version from packaging.sh.
#
# The agent was deliberately absent from this list until then, and the reason
# it was correct to be absent is the reason it no longer is: it asked this
# script for `revision` alone. For a revision-only artifact this file needs no
# entry — any change to how the revision is COMPUTED changes the derived value,
# which then disagrees with the label on the published image and forces a
# rebuild. That mechanism is self-correcting because it compares against a
# string stamped into a real artifact.
#
# Giving the agent a self-reported version took that backstop away from it, so
# it inherits web's exposure and has to be listed here in the SAME change that
# introduced the version. Left out, the failure is the one recorded below: a
# change to `cmd_version` alone would move no revision, the reuse check would
# hit, the build would be skipped, and the published agent would go on
# reporting the old format — with nothing anywhere to disagree with it.
#
# The version is compared against nothing, so it has no such backstop. Before
# this entry, a change to cmd_version alone left every artifact's revision
@@ -100,7 +113,7 @@ ext_paths() {
cmd_paths() {
case "$1" in
web) echo "$WEB_PATHS $DERIVER $(ext_paths)" ;;
agent) echo "$AGENT_PATHS" ;;
agent) echo "$AGENT_PATHS $DERIVER" ;;
extension) ext_paths ;;
*) usage ;;
esac