Skip to content

fix(checks): build-check validated the env file under --project-root then read values from cwd (BACKLOG #1062) - #242

Merged
wshallwshall merged 1 commit into
mainfrom
claude/check-project-root-anchor
Aug 6, 2026
Merged

fix(checks): build-check validated the env file under --project-root then read values from cwd (BACKLOG #1062)#242
wshallwshall merged 1 commit into
mainfrom
claude/check-project-root-anchor

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

Fixes BACKLOG #1062. 4 files, +118 -2. Closes the item's banner in the same commit.

THE DEFECT. messagefoundry check validated that <root>/<env_dir>/<env>.toml EXISTS
under the supplied --project-root, then dropped the root -- _check_build re-derived the
value anchor from Path.cwd(). So it verified the file under the supplied root and read the
VALUES from wherever the shell happened to be standing.

THE FIX IS SERVE'S OWN MECHANISM, not a new one. run_checks gains project_root, threaded
to _check_build and applied as an [environments].base_dir CLI override, so
load_settings' CLI > env > file precedence puts it above a file-set base_dir exactly as
serve already does (cli["environments"]["base_dir"] = args.project_root). The existing
resolve_values_base_dir call is UNCHANGED and becomes correct because base_dir is now
populated. Left unset it still falls back to the process directory, so check --config config is byte-identical.

SCOPE CONFIRMED BY READING, not assumed from the item: four call sites in checks.py load
settings that way, and only _check_build resolves environment VALUES. The other three are
unaffected.

⭐ THE WEAKNESS IN THE TEST EVIDENCE, STATED RATHER THAN LEFT TO BE FOUND. The tests' pre-fix
failure is only a TypeError -- run_checks() got an unexpected keyword argument 'project_root' -- which proves the API is NEW, not that the old behaviour was WRONG. That is
a weaker red than it looks. The defect was therefore reproduced BEHAVIOURALLY instead: both
files reverted, the same invocation run from a directory carrying its own
environments/prod.toml, and measured --

PRE-FIX, values read from  : <tmp>/W   (the process directory)
the root the gate validated: <tmp>/R
DEFECT REPRODUCED          : True

The reproduction proves the defect; the tests guard the fix.

THE TESTS ASSERT THE DIVERGENCE, which is the only shape that can fail: the process
directory holds its own value file with a DIFFERENT host. A test run from inside the root
passes WITH the bug present, because both anchors agree there -- the same masking that hid
the rule 3d defect on Windows, and the reason #1000's asymmetry rule requires a control that
can distinguish. A second test asserts the no-root fallback still anchors on cwd, so this is
a correction and not a widening.

VERIFIED: ruff format + check clean; mypy strict clean across 262 files; 122 tests green
across the anchoring, checks, gate-parity, offline-resolution and environments suites. Based
on 132dc68 (current main), merge-tree clean. Headings 142 on both sides -- a banner edit
adds none.

…then read from cwd (BACKLOG #1062)

`messagefoundry check --project-root R` anchored --config and --service-config
under R and HARD-FAILED when R/<env_dir>/<env>.toml was absent -- then dropped R.
run_checks took no root, so _check_build re-derived the value anchor from
Path.cwd(). The gate verified the file under the root the operator named and read
the VALUES from wherever the shell happened to be.

`serve` never had this. It writes the root into cli["environments"]["base_dir"]
before load_settings. The comment directly above the defect even claimed parity
-- "Resolve env() against the active environment the same way serve does" --
and serve's way IS that assignment, which was the missing step.

THE FIX IS SERVE'S MECHANISM, not a new one. run_checks gains `project_root`,
threaded to _check_build and applied as a [environments].base_dir CLI override,
so load_settings' CLI > env > file precedence puts it above a file-set base_dir
exactly as serve does. The existing resolve_values_base_dir call is unchanged and
becomes correct because base_dir is now populated; left unset it still falls back
to the process directory, so the documented `check --config config` invocation is
byte-identical.

Scope confirmed by reading, not assumed: four call sites in checks.py load
settings this way, and only _check_build resolves environment VALUES. The other
three are unaffected.

TESTS ASSERT THE DIVERGENCE, which is the only shape that can fail: the process
directory holds its OWN value file with a different host. A test run from inside
the root passes with the bug present, because both anchors agree there -- the
same masking that hid the rule 3d defect on Windows.

AND THE PRE-FIX BEHAVIOUR WAS REPRODUCED, not inferred. Reverting both files and
running the same invocation from a directory carrying its own environments/
prod.toml showed the values read from the PROCESS directory while the root was
the one validated. Stated because the test's own pre-fix failure is only a
TypeError -- the parameter did not exist -- which proves the API is new and NOT
that the old behaviour was wrong. The reproduction is what proves the defect.

Consequence, in the conditional per section 0: build-check is a required blocking
check for the ADR 0092 posture-keyed insecure-hop refusal, whose hosts and
schemes are env()-supplied, so a deploying site could have had that refusal
decided against a different environment's values than the one it named. Nothing
is deployed, so this is what a deploying site would have hit.

Verified: ruff format + check clean, mypy strict clean across 262 files, 122
tests green across the anchoring, checks, gate-parity, offline-resolution and
environments suites. Banner closed in the same commit; headings unchanged at 142
on both sides, since a banner edit adds none.
@wshallwshall
wshallwshall enabled auto-merge (squash) August 6, 2026 14:07
@wshallwshall
wshallwshall merged commit afdc5cb into main Aug 6, 2026
33 checks passed
@wshallwshall
wshallwshall deleted the claude/check-project-root-anchor branch August 6, 2026 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant