Skip to content

fix(scripts): anchor webconsole_seam_snapshot.py on its own repo, not on sys.path (BACKLOG #1439) - #826

Merged
wshallwshall merged 5 commits into
mainfrom
claude/vigilant-bhaskara-461a36
Sep 4, 2026
Merged

fix(scripts): anchor webconsole_seam_snapshot.py on its own repo, not on sys.path (BACKLOG #1439)#826
wshallwshall merged 5 commits into
mainfrom
claude/vigilant-bhaskara-461a36

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

Closes BACKLOG #1439.

scripts/webconsole_seam_snapshot.py derived the seam digest from whichever messagefoundry sys.path happened to offer, while reading _CONSOLE_DIR and _ENGINE_DIR out of the repository it lives in. Run from a git worktree with no .venv of its own -- the normal state for these sessions -- those are two different trees.

Python puts the SCRIPT's directory on sys.path[0], never the caller's cwd. So scripts/ led the path, no entry offered messagefoundry, and the import fell through to site-packages: the primary checkout's, via its path-based editable install (_editable_impl_messagefoundry.pth holds the primary root). That is also why an ordinary sys.path.insert(0, ...) is enough to beat it.

The success output is the part that misled

Measured 2026-09-03 in a worktree with no .venv: --write printed the primary tree's digest 93ba1f10b9dccfc8, rewrote messagefoundry/api/_ui_seam.py and the golden with that unchanged value, and reported success naming both files -- while tests/test_webconsole_seam_snapshot.py kept failing against 266cbfd342b22819, a digest the script had never computed.

The only loud word in the room was rewrote, so the repair on offer to a reader is that the gate is broken. The gate was right the whole time. This is the SDS-3.8 shape: an instrument answering a question adjacent to the one asked.

The measurement, as a controlled comparison

Four scripts run by path from one cwd, under one interpreter, with sys.path[0] set to each target's own directory. The only variable is whether the script anchors itself:

scripts/bench/stage_residency.py       -> <THIS WORKTREE>/messagefoundry/__init__.py
scripts/security/dast_auth_sweep.py    -> <THIS WORKTREE>/messagefoundry/__init__.py
scripts/tray/make_icons.py             -> <THIS WORKTREE>/messagefoundry/__init__.py
scripts/webconsole_seam_snapshot.py    -> <PRIMARY CHECKOUT>/messagefoundry/__init__.py

The first instrument tried was the wrong one, and the item records it so it is not tried again. A runpy.run_path probe from the repo root reported all four resolving correctly -- it could not have done otherwise, because runpy left cwd on sys.path and so never reproduced a by-path invocation. Checking that the instrument answers the asked question is the same discipline this PR is about, and it caught a false clean one step in.

The census bounds it at exactly one script

Every .py under scripts/ importing an in-repo top-level package (messagefoundry, messagefoundry_webconsole, harness, tee, ide), including indented and deferred imports:

Script Anchors on __file__ Runnable by path Verdict
bench/stage_residency.py yes, parents[2] yes safe
security/dast_auth_sweep.py yes, parents[2] yes (CI + by hand) safe
security/dast_target.py no no -- library, no __main__ not exposed
security/route_gates.py no no -- library, no __main__, says so not exposed
tray/make_icons.py yes, parents[2] yes safe
webconsole_seam_snapshot.py no yes, and the docs prescribe it fixed here

The two unanchored security modules are reached only as scripts.security.X, which already requires the root on sys.path; dast_auth_sweep.py inserts it before importing them and pytest supplies it, so they inherit a corrected path. scripts/security/crypto_inventory_check.py names those packages only in AST-matching string constants, not imports.

So three siblings already carried this fix and one was missed -- which is the strongest thing the census says. A per-script anchor has no list for an unanchored script to be absent from, so the omission was invisible by construction.

Two tests, because the obvious one is not enough

test_the_script_run_by_path_computes_the_same_digest runs the generator as a subprocess, by path, from a cwd that is not the repo root and with PYTHONPATH scrubbed. Both scrubs are load-bearing: under pytest the root is already on sys.path, so every in-process check in that file is blind to this defect by construction, and a subprocess inheriting either rescue would pass for a reason unrelated to the script.

That test cannot fail on a hosted runner -- one engine tree means an unanchored script is right by luck -- and measured here it did not fail without the fix either, because this box's two trees currently carry the same seam. A test whose environment cannot produce the failure is not evidence the failure is absent.

So test_the_script_prefers_its_own_repo_over_an_earlier_path_entry supplies the second tree itself: a decoy messagefoundry package on PYTHONPATH, which for a by-path invocation sits ahead of site-packages and behind an explicit sys.path.insert(0, repo_root). It reds if and only if the anchor is gone, on any machine, with no worktree and no second checkout needed.

Checks

Check Result
pytest tests/test_webconsole_seam_snapshot.py 8 passed
pytest seam + ledger files (6 files) 125 passed
Mutation check anchor deleted, decoy test reds naming the decoy import; anchor restored, 8 passed
ruff check, ruff format --check clean on both files
mypy 4 findings in the test file are pre-existing (identical at HEAD); tests/ and scripts/ are outside CI's mypy messagefoundry messagefoundry_webconsole scope
scripts/docs/backlog_status_check.py OK, 658 items, each declaring exactly one status
pre-commit all hooks passed

Not run: the full pytest suite, and any hosted-runner-only leg. Please read the CI legs after they report.

messagefoundry/api/_ui_seam.py and tests/golden/webconsole_seam.snapshot are deliberately untouched -- the contract did not move, only the instrument that measures it.

Severity

No engine effect, no PHI axis, and no deployment axis (CLAUDE.md section 0): scripts/ ships in no wheel and this tool touches no product surface. The cost is bounded to a developer's session -- wasted debugging, and a gate a reader would "fix" in the wrong direction. Nothing was mis-shipped, because CI runs on a hosted runner with exactly one engine tree, where the defect cannot express itself.

Adjacent, and deliberately not fixed here

docs/WEBCONSOLE-PACKAGE.md's seam-refresh procedure is stale in three steps left behind by #1220: it says to bump ENGINE_UI_SEAM by hand (1 to 2) when the value is a derived digest, it says to update curated lists in this script that #1220 retired, and its step 5 prescribes python scripts/webconsole_seam_snapshot.py > tests/golden/... -- the shell redirect this script's own docstring forbids, because PowerShell's > writes UTF-16LE with a BOM into a file the test reads as UTF-8. That is doc drift with its own cause and wants its own item; folding a documentation rewrite into a sys.path fix would make both harder to review.

🤖 Generated with Claude Code

… on sys.path (BACKLOG #1439)

The generator derived the seam digest from whichever `messagefoundry` sys.path
happened to offer, while reading _CONSOLE_DIR and _ENGINE_DIR out of the
repository it lives in. Run from a git worktree with no .venv of its own -- the
normal state for these sessions -- those are two different trees.

Python puts the SCRIPT's directory on sys.path[0], never the caller's cwd, so
`scripts/` led the path and the import fell through to site-packages: the
primary checkout's, via its path-based editable install
(_editable_impl_messagefoundry.pth holds the primary root).

The failure was silent and it pointed the wrong way. Measured 2026-09-03 in a
worktree with no .venv of its own: `--write` printed the primary tree's digest
93ba1f10b9dccfc8, rewrote both files with that unchanged value, and reported
success naming both, while tests/test_webconsole_seam_snapshot.py kept failing
against 266cbfd342b22819 -- a digest the script had never computed. The only
loud word was "rewrote", so the repair on offer was to a gate that was right.
This is the SDS-3.8 shape: an instrument answering an adjacent question.

The fix is the sys.path insert three sibling scripts already carry, above the
`from messagefoundry...` imports, citing the rule scripts/coord/alloc.ps1
states for `git` (#1060): anchor on the script, not on the caller.

Two tests, because the obvious one is not enough:

  test_the_script_run_by_path_computes_the_same_digest runs the generator as a
  subprocess, by path, from a cwd that is not the repo root and with PYTHONPATH
  scrubbed. Both scrubs are load-bearing: under pytest the root is already on
  sys.path, so every in-process check in that file is blind to this defect by
  construction.

  test_the_script_prefers_its_own_repo_over_an_earlier_path_entry supplies a
  second engine tree itself -- a decoy messagefoundry package on PYTHONPATH,
  which for a by-path invocation sits ahead of site-packages and behind an
  explicit sys.path.insert(0, repo_root). It reds if and only if the anchor is
  gone, on any machine. The first test cannot carry that weight: one tree on a
  hosted runner means an unanchored script is right by luck, and measured here
  it stayed green with the anchor deleted, because this box's two trees
  currently carry the same seam.

Census of scripts/ for the same class: six .py files import an in-repo
top-level package. Three already anchor on __file__ (bench/stage_residency.py,
security/dast_auth_sweep.py, tray/make_icons.py). Two are libraries with no
__main__ reached only as scripts.security.X, so they inherit a corrected path
and are not exposed (security/dast_target.py, security/route_gates.py). This
was the only one.

Verification: 8 passed in tests/test_webconsole_seam_snapshot.py. Mutation
check run rather than argued -- anchor deleted, the decoy test reds naming the
decoy import; anchor restored, 8 passed. ruff check and ruff format --check
clean on both files; the 4 mypy findings in the test file are pre-existing
(identical at HEAD) and tests/ is outside CI's mypy scope. The seam constant
and the golden are deliberately untouched: the contract did not move.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@wshallwshall wshallwshall added the reviewed A reviewer has read this. Removed automatically when new commits arrive. label Sep 3, 2026
…ved resolution

Three peer sessions raised the same methodological point independently: a script
can resolve to the correct tree today for a reason that has nothing to do with
the script, so a census keyed on "which tree did it resolve to" clears every
script whose cwd currently rescues it.

Verified here, one interpreter, one command, three working directories. From a
worktree root `python -c "import messagefoundry"` resolves to the WORKTREE,
because cwd is sys.path[0] and precedes the editable install. From the primary
root or a neutral cwd the same command resolves to the primary. A by-path script
resolves to the primary from everywhere.

That is this defect's mirror image: a by-path script reads the primary because
nothing anchors it, a bare interpreter reads the worktree because cwd happens to
win, and both are the same missing anchor. Only one of them looks wrong. The
fleet's recipe for a .venv-less worktree depends entirely on that rescue.

The census table was already keyed on the anchor rather than on resolution, but
the item did not say so, and a future reader re-running it could reasonably key
it the other way. It says so now.

Also re-derived the table by AST on the predicate "imports an in-repo package
AND is runnable by path AND puts nothing on sys.path". It agreed with the
reading exactly. Positive control, since a pattern that finds nothing anywhere
is indistinguishable from a clean tree: 53 .py files under scripts/, 28 deriving
a path from __file__, 6 importing an in-repo package. Control on the instrument
itself: against the parent commit the census flags webconsole_seam_snapshot.py
as EXPOSED and nothing else; against the fix it reports none.

Two further corrections to the record:

  The two library modules are structurally safe, not accidentally safe. The
  scripts.security.X import form cannot resolve unless the repo root is already
  importable, so the root supplying the module necessarily supplies its
  messagefoundry. One tree by construction, not by cwd.

  Added what the census could NOT see: its scope is scripts/**/*.py only, its
  runnability test is a __main__ guard which would misjudge a module invoked by
  path without one, and it says nothing about the wider class the peers named.

No code change. The fix, both tests and the seam value are untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot removed the reviewed A reviewer has read this. Removed automatically when new commits arrive. label Sep 4, 2026
@wshallwshall

Copy link
Copy Markdown
Collaborator Author

LANDER: applying reviewed under an explicit owner override. The owner has suspended the reviewer process and directed that this label be applied as needed to clear the gate.

SO READ THIS LABEL AS A BYPASS TOKEN, NOT A REVIEW. It records that the gate was cleared by instruction. It does not assert that anyone read this diff, and no later reader should infer one from it.

What I can say: this PR appeared in a read-only pre-verification sweep of all 59 non-draft open PRs (per-PR defect read, plus pairwise git merge-tree against every PR sharing a non-BACKLOG file), and no blocking defect was recorded against it. That sweep did not run any tests, and CI has been down for hours, so nothing here has a current green.

-- Lander

@wshallwshall wshallwshall added the reviewed A reviewer has read this. Removed automatically when new commits arrive. label Sep 4, 2026
@github-actions github-actions Bot added the ci-red A required check went red. Attribute it before retrying. label Sep 4, 2026
Both sides append a new section at the same point in docs/BACKLOG.md, under different
item numbers. Neither supersedes the other, so taking a side would drop a filed item
outright. Both blocks are kept, in the ascending item order the file already uses.

Verified with parse_items from scripts/docs/backlog_status_check.py rather than a
hand-rolled scan, and by an ADDED/LOST item-set difference against both parents, which
is empty. A count alone would not catch two blocks folding into one.
@github-actions github-actions Bot removed the reviewed A reviewer has read this. Removed automatically when new commits arrive. label Sep 4, 2026
@wshallwshall wshallwshall added the reviewed A reviewer has read this. Removed automatically when new commits arrive. label Sep 4, 2026
Both sides append a new section at the same point in docs/BACKLOG.md, under different
item numbers. Neither supersedes the other, so taking a side would drop a filed item
outright. Both blocks are kept, in the ascending item order the file already uses.

Verified with parse_items from scripts/docs/backlog_status_check.py rather than a
hand-rolled scan, and by an ADDED/LOST item-set difference against both parents, which
is empty. A count alone would not catch two blocks folding into one.
@github-actions github-actions Bot removed the reviewed A reviewer has read this. Removed automatically when new commits arrive. label Sep 4, 2026
Both sides append a new section at the same point in docs/BACKLOG.md, under different
item numbers. Neither supersedes the other, so taking a side would drop a filed item
outright. Both blocks are kept, in the ascending item order the file already uses.

Verified with parse_items from scripts/docs/backlog_status_check.py rather than a
hand-rolled scan, and by an ADDED/LOST item-set difference against both parents, which
is empty. A count alone would not catch two blocks folding into one.
@wshallwshall wshallwshall added the reviewed A reviewer has read this. Removed automatically when new commits arrive. label Sep 4, 2026
@wshallwshall
wshallwshall added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit bd9830f Sep 4, 2026
42 of 43 checks passed
@wshallwshall
wshallwshall deleted the claude/vigilant-bhaskara-461a36 branch September 4, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-red A required check went red. Attribute it before retrying. reviewed A reviewer has read this. Removed automatically when new commits arrive.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant