Skip to content

DRAFT triage: builder-2's dead lane -- six commits that never landed, behind 33 of squash residue - #530

Draft
wshallwshall wants to merge 39 commits into
mainfrom
claude/builder-2-6e58d6
Draft

DRAFT triage: builder-2's dead lane -- six commits that never landed, behind 33 of squash residue#530
wshallwshall wants to merge 39 commits into
mainfrom
claude/builder-2-6e58d6

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

Draft on purpose. Do not arm. Opened by the lander to make a dead session's work visible; the Dispatcher flagged that nothing reports a branch which is merely never pushed.

The framing this corrects

It was routed to me as "39 unpushed commits of built work". The count is right and the reading is not. Four PRs have already merged from this exact branch -- #488, #491, #496, #505, the last at 08:50Z today. A squash merge leaves the source branch permanently ahead of main, because the squashed commit carries a different patch-id than the originals. So most of the 39 is squash residue, not stranded work.

git cherry reports 31 of 31 as not-upstream, which is the trap: it compares patch-ids, and a squash defeats that. Reading it literally would have routed 39 mostly-landed commits into a PR.

What is genuinely unlanded -- six commits, all verified off origin/main

commit what
4eee9f88 webconsole: fetch-metadata isolation for /ui, including the Mount (#1122)
9e4c1524 webconsole: connection names cannot escape a /ui path segment (#1107)
549e3044 webconsole: encode the one role id that skips its own 404 lookup (#1107)
721635ce init writes a loadable config, and check can fail on one (#1318, #1320)
cb010d7b docs: instruments that returned confident wrong answers
67843d66 docs: sharpen the marker rule

Each checked with git branch -r --contains; none is reachable from origin/main.

#1318 is NOT a duplicate of #524

#524 landed the #1318 ledger filing (docs/BACKLOG.md only). 721635ce is the #1318 code fix -- checks.py, scaffold.py, test_scaffold.py. Complementary halves. Landing this does not re-land that.

Why draft and not armed

  • 721635ce's own commit message says the suite was at 5 percent and still running when it was committed. Its tests are unverified by its author.
  • The branch is 11 behind main and its session is gone, so nobody owns a rebase.
  • Three of the six are security-relevant webconsole changes. With required_approving_review_count: 0, arming would merge them unread.

Nothing here was ever at risk

The tip 67843d66 is reachable from private/rescuetags/auto/MessageFoundry/claude/builder-2-6e58d6. Preservation was never the issue; visibility was. That is what this draft fixes.

Whoever picks this up: rebase onto main, run the suite that 721635ce never saw finish, and split the two docs commits out if they slow the security three.

…epoint module stem (BACKLOG #1130)

Two untrusted strings reached a filesystem path without clearing the rule their
siblings already clear. Both are closed with the shape each site's neighbours
already use, rather than a new one.

config/codeset_edit.py: rename_code_set validated only `new`. `old` is equally
untrusted and reaches `_existing_path`, so a traversal source resolved outside
codesets/ and `os.replace` would MOVE that file in, contents intact. The
`_validate_name` call is added before the lookup, matching show (:92), upsert
(:144) and remove (:221). Kept after the empty-argument checks so the
`--name is required` wording still wins for an empty name.

corepoint_import.py: the export chooses `module_name`, which becomes BOTH the
emitted inbound() connection name and the module's filename stem. It is folded
with `_sanitize`, as the channel name (:415) and handler names (:464) already
are. Mutation rather than refusal, because the importer writes into a directory
it created -- there is no existing file to alias -- and a colliding fold is
already de-duplicated by the writer's `assigned` set.

THIS MOVES NO ASVS SCORE. The cell's recorded absence claim is keyed on
`_within_root`, a symbol belonging to transports/file.py, while the guard that
shipped is `_is_contained_name`; that re-key is record work in the vault and
nothing here satisfies it.

SCOPE, STATED RATHER THAN ASSERTED AS COMPLETE. The item's own body measured
162 file-path-construction sites across 49 files under messagefoundry against
roughly fifteen named, and forbids a residual sentence saying "every" or "all
limbs" until that census exists. This commit covers the two named sites only.
harness/, scripts/ and the ide/ TypeScript surface are untouched and are
contingent on an open scope ruling about which roots the assessment covers.

Tests, each asserting on the refusal and not merely on an unchanged tree:
  tests/test_corepoint_import.py::test_a_hostile_inbound_name_cannot_write_outside_the_output_directory
  tests/test_codeset_edit.py::test_rename_rejects_traversal_source_without_moving_a_file_into_the_dir
  tests/test_codeset_edit.py::test_cli_rename_traversal_source_is_a_clean_json_error
… a false mirror claim (BACKLOG #1130)

Follow-up to e5505a9, from its own adversarial review. Two things that commit
changed and did not say.

1. `rename --name` was missing from the untrusted-name list. It joined that list
in e5505a9 and the doc still named only `upsert`'s name, `rename --to`, and
show/remove. The list now matches the code.

2. THE BEHAVIOUR CHANGE NOBODY HAD WRITTEN DOWN. A code set stored as
`lab.results.csv` LOADS -- measured with the real loader, `load_code_sets`
returns the key `lab.results` and `codeset list` shows it -- but its dotted stem
fails the no-extension name rule. `show` and `remove` already refused it. Adding
the guard to `rename --name` closed the last verb that could manage such a file,
so it now has to be renamed on disk by hand. That is a real cost of the fix and
it belongs beside the fix, not in a review transcript.

3. The heading "Validation rules (mirror the loader exactly)" was FALSE for the
name rules and true for the content rules. Split, rather than deleted, so the
half that is accurate keeps its claim.

Found by the adversarial pass, not by me: it planted each defect separately,
confirmed both new tests red for the named reason, reverted, and proved the
revert byte-identical by SHA256 on both files. Its verdict was `sound` with
`test_can_fail=True`; these are from its residual findings, which is the half a
green verdict hides.

NOT DONE, and reported rather than silently left: two of its findings are about
test STRENGTH, not correctness -- the corepoint census is blind to escape
classes its docstring claims, and the codeset tests pin one of the five rules
`_validate_name` enforces, so a deliberately weaker guard would still pass them.
Those need test work, not doc work, and they are not in this commit.

No engine code changed. No ASVS score moves.
…low-list the URL scheme (BACKLOG #1107)

Two of the four clauses the item names. The owner dispatched these two only; the
FHIR structured-parameter and web-console URL-builder clauses are separate
undispatched subjects and are untouched.

CLAUSE 1 -- path segments. Seventeen f-string sites interpolated an identifier
straight into a URL path and the file encoded nothing: `quote(|urlencode(` in
apiclient/client.py returned 0, against a control of 7 in transports/fhir.py and
76 `def ` in the client itself, so the zero was a property of the code. `_seg()`
wraps `quote(..., safe="")`; the default `safe="/"` would leave untouched the
one character a path segment turns on.

ENCODED AT ALL SEVENTEEN, not only the four carrying a connection name. Thirteen
carry engine-minted ids, so "this is not untrusted data" is true of them today --
and that is the argument the item names as its live trap, because it rests URL
correctness on a data-grammar invariant no line of code asserts and no test pins.

CLAUSE 2 -- scheme. `_assert_safe_transport` returned early on
`host in _LOOPBACK_HOSTS or host == ""`. `javascript:`, `data:` and `file:` carry
no hostname, so all three passed: a host-keyed deny-list for plaintext http, not
a scheme allow-list, and the two schemes the ASVS verb names were exactly the two
that got through. Now a positive allow-list, strictly narrower than the check it
precedes, leaving the plaintext-http logic byte-for-byte intact.

BEHAVIOUR CHANGE: a schemeless `base_url` is no longer accepted. Seven non-test
construction sites exist under messagefoundry/ and harness/, none passing a
schemeless literal -- derived with `grep -rn "EngineClient("` at this tip, not
carried from a report. WHAT THAT DOES NOT PROVE: every one of those takes a URL
from config or argv, so runtime values are not covered by a static count.

THREE RESIDUALS FROM THE ADVERSARIAL PASS, kept because a green verdict hides
exactly this half:
  - The completeness guard's docstring OVERCLAIMS. It says a new endpoint that
    interpolates an identifier reds the test; that is true for f-strings and
    FALSE for string concatenation and .format(). The reviewer demonstrated it
    by adding a concat site that slipped through.
  - The guard asserts a COUNT, not a correspondence, so a same-size swap (one
    site removed, one added) passes.
  - The scheme allow-list's docstring invokes an OS protocol handler. This client
    hands base_url to httpx, which never launches one -- httpx raises
    UnsupportedProtocol. The defence is real but narrower than the docstring.

THIS MOVES NO ASVS SCORE. The cell is graded in the vault, which this worktree
does not contain, and two of its four clauses remain unbuilt.

docs/SECURITY.md is UNTOUCHED -- another lane owns it this round. The clause-2
delta is reported to the Dispatcher and should land WITH this code, not ahead.

Adversarial pass: 5 plants, each red for its documented reason, client.py sha256
identical after every revert. Verdict sound, test_can_fail true. Module: 56 passed.
…st the separator (BACKLOG #1130)

Closes a test-strength gap its own adversarial pass found and e5505a9 did not
fix. The traversal test matches only "must not contain a path separator", so a
deliberately weaker guard -- `if "/" in old or "\\" in old: raise` -- satisfies
it while leaving the other branches unguarded on the rename SOURCE.

`_validate_name` has SEVEN refusal branches, not the five the review reported:
non-empty, control characters, path separator, "..", absolute-or-drive, bare
stem, and escapes-the-directory. The new parametrize covers eight inputs.

PROVEN TO FAIL FOR THE STATED REASON, not merely written and watched go green.
Planted the narrowed guard at the call site and got exactly the predicted split:
  RED  dotdot, embedded-dotdot, suffix, control, blank
  PASS slash, backslash, absolute -- the three a separator-only guard catches
That asymmetry is the point. A uniform failure would not distinguish "this test
covers the branch" from "this test fails on anything".

Revert verified byte-identical, sha256 8d4a1672...52fd01cc before the plant and
after it. The mutation is named in the docstring so the next reader can re-run
it instead of trusting this message.

STILL NOT DONE, from the same review: the corepoint census is blind to escape
classes its own docstring claims to cover, because it censuses with rglob over
the output root, so a name resolving INSIDE the root is visible to it while
other classes are not. Separate test, not in this commit.

No engine code changed. No ASVS score moves.
…rd the real red (BACKLOG #1130)

Closes the second test-strength gap its adversarial pass found. The existing
census rglobs from `root`, and the helper nests the out dir three levels under
it so `../../../x` still lands inside. THAT COVERS EXACTLY ONE ESCAPE CLASS. A
deeper traversal, a POSIX-absolute path, a drive-letter path and a UNC path all
resolve ABOVE `root`, outside that rglob -- where the escape is invisible and
the empty result reads as containment.

The helper now takes a wider census base, and the new test passes `tmp_path`:
the widest base it can see, containing every target those classes reach
in-process.

THE MUTATION'S RED IS NOT THE ONE I FIRST PREDICTED, and the docstring now says
so rather than being quietly corrected. I wrote "red: `outside` is non-empty".
Measured: five of six ids go red with FileNotFoundError on the write, because
the unsanitized stem names a parent directory the importer never created. That
still proves the name reached the path unsanitized -- but it is a WRITE FAILURE,
not an escape the census caught. A reader re-running the mutation and expecting
a non-empty census would see an unrelated-looking error and distrust the test.

A refusal for a different reason is indistinguishable from the refusal you asked
for, so the docstring names the observed mechanism.

`bare-dotdot` PASSES under the plant and is kept as a must-not-trip control: `..`
alone resolves to the out dir's parent as a directory, producing no write for the
census to catch. If it ever starts failing, the fold changed shape.

Revert verified byte-identical, sha256
ff13e9a63fdb736f6860e4216b0dd9d4b1f27b0465c22f6ec3d60d68f0d0925f before the
plant and after it.

No engine code changed. No ASVS score moves. Both #1130 test-strength findings
from that review are now closed.
…d route the token hop through the sidecar (BACKLOG #1176)

TWO LIMBS, both product defects. NEITHER MOVES THE ASVS SCORE and the cell stays
`fail` -- ECH is unreachable on this runtime (zero ECH attributes on the pinned
interpreter, measured here), so nothing in this commit makes a pass possible.
That is the item's own conclusion and not mine to change.

LIMB 1 -- silent acceptance. `ech_egress` was refused only by connectors routing
through `egress_route_from_settings` (fhir, soap, dicomweb, rest). MLLP, TCP,
X12, file, email and DICOM never reach it, so they BUILT and silently ignored
the key: an operator sets an SNI-hiding flag and nothing hides anything. The
refusal is hoisted into the shared construction seam rather than copied per
connector, so a connector added later inherits it.

LIMB 2 -- the seam that leaked even where ECH works. On REST the proxy was
forced to None whenever a sidecar was set, and http_auth fell back to the plain
opener, so `ech_egress` plus an OAuth2 or SMART token endpoint sent the
AUTHORIZATION SERVER's hostname in a cleartext outer ClientHello. A refusal
keyed on "does this destination implement the send path" cannot reach that,
because REST does implement it.

RED PROOF, tests written first against unmodified HEAD 4633a29: `DID NOT RAISE
ValueError` six times, one per connector, plus the inbound limb, plus the OAuth2
and SMART hops asserting the direct URL. DID NOT RAISE is the evidence the
defect was real -- those connectors built and accepted the key.

ADVERSARIAL PASS: sound, test_can_fail true. Two plants, each red for its
documented reason, five files sha256-identical after revert.

FINDINGS FROM THAT PASS, KEPT BECAUSE A GREEN VERDICT HIDES THEM:
  - test_both_refusal_sites_carry_the_same_message DOES NOT witness the seam its
    variable names claim. It PASSED under the plant that deleted the hoisted
    refusal, so it asserts something true but not that. NOT FIXED HERE.
  - `ech_sidecar` set WITHOUT `ech_egress` is still silently ignored on every
    connector, REST included -- a residual inside the item's own hypothesis.
  - Routing the token POST through the sidecar makes the AUTH hop inherit the
    sidecar contract's fail-closed rule. A real semantic change, undisclosed by
    the implementer and recorded here instead.
  - Three provider docstrings assert `proxy` and `ech_sidecar` are "mutually
    exclusive by construction"; nothing in those functions enforces it.

docs/SECURITY.md is UNTOUCHED -- another lane owns it. Two edits reported as
text, both inside the single ECH bullet.

Module: 38 passed. Transports seam: 2787 passed, 141 skipped, 0 failed.
…ation (BACKLOG #1112)

The in-process half already shipped: uploads.py holds an asyncio.Lock spanning
the quota check and the write. That lock is per-EVENT-LOOP, so N engine shards
over one uploads_dir hold N of them and each can pass the same check. Every
shard shares ONE unified store (ADR 0063, and sharding.py:81 makes a server DB
mandatory past one shard), so the store is the only decision point that spans
them.

ONE new protocol method, `reserve_upload_quota`, across the four store files,
plus the two construction sites that give UploadStore a store handle. The
attachment substrate is NOT touched -- separate subject, out of scope by the
dispatcher's fence, and that fence held: no fifth store file, no second method.

RED PROOF, and the first run is DISCARDED rather than counted. Run 1 failed with
`TypeError: unexpected keyword argument 'store'` -- the code path was never
reached, so it is plumbing, scored NOT REACHED. Run 2, with the kwarg removed
from the helper so the identical body runs against shipped code, is the proof:
both shards WON a budget of one and both files landed on disk. `assert 2 == 1`,
failing at the right phase for the right reason.

A refusal for a different reason is indistinguishable from the refusal you asked
for, which is why run 1 is in the record as not-proof rather than omitted.

MYPY IS THE CONTROL THAT THE PROTOCOL IS REAL: renaming the SQL Server
implementation produces `"SqlServerStore" is missing following "Store" protocol
member: reserve_upload_quota`. So the addition is enforced across all four
files rather than silently unimplemented in three.

ADVERSARIAL PASS: test_can_fail true; four independent plants, each redding a
distinct test; 30/30 acceptance runs, no flakes. It confirmed the test
demonstrates CONCURRENT EXCLUSION rather than the shared visibility the
pre-existing sequential test already showed.

ITS FINDINGS, RECORDED BECAUSE THEY ARE NOT FIXED HERE:
  - THE IMPLEMENTER'S OWN STATED MITIGATION IS FALSE. "The CI Postgres and SQL
    Server legs are the gate" -- zero tests call reserve_upload_quota on either
    backend; all four callers are SQLite. The two server implementations ship
    UNEXERCISED.
  - A leaked reservation does NOT unconditionally self-heal: release writes
    `since = <now>` in all three backends, restarting the staleness clock. Two
    shipped docstrings state the opposite. That is a false premise in a comment
    and it is the next thing I fix.
  - It introduces a new failure mode on SINGLE-PROCESS deployments, not only
    sharded ones: before this, a killed process left no quota state.

config/settings.py carries a COMMENT-ONLY hunk (0 non-comment changed lines,
asserted mechanically) correcting a comment that #1112 makes false. That file is
another lane's; the dispatcher authorised this hunk specifically because the
comment is true only at the moment this lands, and this commit is that moment.

89 passed across the upload, upload-API and PHI-inventory modules.
…it was mine (BACKLOG #1112)

SDS-3.7 -- a compensating control must not rest on a false premise. Two shipped
docstrings landed in 142b926 saying a leaked reservation is reclaimed once it
goes stale. That is true ONLY while the uploader is otherwise idle, and neither
said so.

THE MECHANISM, verified in the SQL rather than taken from the review that found
it. The release branch of reserve_upload_quota is:

    UPDATE upload_quota SET
      inflight_files = MAX(0, inflight_files + ?),
      inflight_bytes = MAX(0, inflight_bytes + ?),
      since = ?
    WHERE uploader_id = ?

`since = ?` is UNCONDITIONAL, deliberately -- its own comment reads "Never
conditional: refusing a release would strand the reservation it is paying back."
That is correct for the release and it has a side effect nobody wrote down:
every later release by the same uploader pushes the staleness clock forward. So
the reserve path's staleness reset -- which IS conditional
(`CASE WHEN since <= ? OR inflight_files <= 0`) -- never fires for an uploader
who keeps uploading successfully. A leaked slot can survive indefinitely.

WHY THIS IS WORSE THAN AN UNDOCUMENTED LIMIT: with no comment a reader measures.
With "it self-heals" a reader RELIES, and the sentence is written in the
confident register that earns reliance. The failure it hides is a permanently
narrowed budget for exactly the uploaders who use the system most.

Both sites now state the precondition, and the first keeps the retraction in
place rather than quietly reading correctly -- the next reader should be able to
see that the stronger claim was made and withdrawn.

Found by #1112's adversarial pass, whose verdict was `weak` for this and two
other reasons. I verified the mechanism in the statement before writing it here;
the review named it, the SQL proved it.

No code changed. No ASVS score moves. 33 passed across the two upload modules (tests/test_uploads_cross_process_quota.py + tests/test_uploads.py). The first version of this message said 56 -- a number carried forward from an unrelated earlier run, corrected by re-running rather than by memory.
…(BACKLOG #1183)

_BEARER matched the AUTH SCHEME as its own value: "Authorization: Bearer <tok>"
redacted the word "Bearer" and emitted <tok> verbatim. The one assertion covering
it passed anyway, because its chosen token was pure alphanumeric and the unrelated
_LONG_B64 sweep caught it -- so the suite could not tell a working pattern from a
broken one. Every fixture token now carries a hyphen AND an underscore, which
breaks the base64 run and puts each family on its own pattern.

Widens the secret domain to what the module's docstring already implied: a bare
"Bearer <tok>" with no header label (_AUTH_SCHEME), password/PWD/secret pairs
(_CREDENTIAL_KV), and an inline DSN password (_DSN_PASSWORD). Quote handling is
uniform -- the value class stops at any quote, so a mismatched pair loses the
value and keeps only a stray character. Measured: 0 leaks across 7 quote shapes,
including both mismatched orders.

Two label words are deliberately EXCLUDED and commented as such: "key" and the
basic/digest scheme words. All three are ordinary vocabulary in this codebase, so
matching them would redact operator diagnostics and buy no confidentiality.

resolve_env_settings no longer echoes a failed env() value. It appeared TWICE --
once from the f-string and once inside the cast's own ValueError text -- so
dropping the f-string half alone would still have leaked it. The error now names
the setting, the key and the expected TYPE, and says the value was withheld.

Which patterns exist is no longer a docstring claim: the suite derives them by AST
and fails if one is applied without a named family, or declared without being
applied.

.gitleaks.toml: nine EXACT-literal allowlist entries for the new synthetic needles,
each traced to its fixture. Exact literals, never a family prefix -- "sk-live-.*"
would have allowlisted a real leaked key of that shape. Verified both directions:
the nine listed values are silenced, and four unlisted values in the same four
shapes are still caught.
…1184, ASVS 14.2.1)

An operator-typed patient identifier rode the URL into the engine's access log,
the reverse proxy's log and browser history -- none of which the redactor reaches.
`content` and `field_value` are deleted from FIVE GET signatures (three in
api/app.py, plus both console GETs) and now travel in a POST body over a criteria
model. `field_path` stays on GET: it is a structural locator, not a value, which
is why _search_audit_detail already records it verbatim.

Each POST is the needle-bearing sibling of its GET, not a second implementation:
both call one shared handler, so the PHI-read hop and budget are charged in the
same place for both. Verified the audit path carries no value either -- shape and
length only, and the call sites pass metadata filters alone.

A ROUTE-TABLE GUARD, which the item names and is the durable half. Deleting a
parameter earns the verdict once; this keeps it. It walks both planes, resolves
the WIRE name so a re-add hidden behind alias="content" is still caught, ignores
POSTs, and carries a positive control so an inert walk cannot report a clean repo
forever. Proven by planting a re-declared parameter on the search GET.

DISCLOSED BEHAVIOUR CHANGE, not a defect: _enforce_admin_write_pacing is NON-GET
only, so a search issued as POST now also draws on the admin-write bucket that
gates connection start/stop/restart, DR activate/release and alert ack/resolve.
Strictly stricter, never weaker. I did NOT measure the budget sizes and claim no
reachability; whether the two deserve separate buckets is unanswered.

FOUR REVIEW FINDINGS FIXED, each proven to fail by planting first:
- the uploaded-log filter test claimed the form must POST and only checked that a
  field input existed. Flipping method back to "get" restored the exposure and the
  test stayed green. It now pins that form's own opening tag.
- the field_path control asserted matched == 2, which is what an UNFILTERED browse
  returns, so it could not tell a working filter from a dropped one. PID-5
  discriminates at 1.
- the filter POST answered an out-of-bounds criterion with the whole file at HTTP
  200, blank inputs and no banner, discarding valid criteria with the invalid one.
  It now refuses with 400 and keeps what was typed, like its sibling.
- test_security_doc_rate_limits pinned four routes that charge PHI-read pacing at
  their own route. Three now charge inside the shared handler, so the guard
  follows the delegation one hop instead of shrinking the pinned set -- shrinking
  it would have stopped guarding three routes.

docs/SECURITY.md counters are re-derived on a tree that already carries #1148:
require_step_up is 27, which is neither the 24 that lane measured nor the 29 this
one did. Both were right alone and wrong together.

KNOWN AND UNATTRIBUTED: test_route_map_parser_detects_a_planted_permission_mutation
fails in a FULL-suite run only. It passes alone, with its sibling suite, and after
the scorecard suite. No test writes the real docs/SECURITY.md and both suites
resolve _ROOT absolutely, so it is in-process state, not a stale file. Not claimed
as mine and not claimed as pre-existing.
…ot a finding that ide/ is crypto-free (BACKLOG #1164)

Two comments -- the gate's own and its pinned twin in tests/test_security_static.py
-- justified excluding ide/ from the crypto walk by saying the exclusion is "a fact
about the tree". It is a fact about the language. ide/ contains zero .py files, so a
Python AST scanner has nothing to read there; that says nothing about whether the
directory contains cryptography.

It does. ide/src/cspNonce.ts imports randomBytes from node:crypto and draws CSPRNG
bytes consumed across the extension, and ide/src/engineClient.ts pins a TLS floor it
applies to every https request. Both are first-party crypto in a shipped artifact and
neither is discoverable from a Python walker. Verified at this HEAD rather than taken
from the item, which measured them at an older commit.

The comments now state what a green here actually supports -- no undocumented crypto
in the PYTHON of five roots -- and record that adding ide/ to WALK_ROOTS would be a
no-op that looks like a fix: the scanner finds zero .py, reports clean, and the
TypeScript stays invisible while the tree gains a green that is evidence of nothing.
That is the disqualified move the item names.

NOTHING ABOUT THE GATE'S BEHAVIOUR CHANGES. The stray-.py invariant is untouched and
still enforced: planting a .py under ide/ prints the violation and exits 1; removing
it returns exit 0. Measured without a pipe, because tail's exit code is not the
gate's -- I read that wrong once tonight already.

This does not move the cell. It retires a false premise underneath it, which #1164
names as work in its own right; the TypeScript and PowerShell discovery arms remain
unbuilt and a pass is not reachable without them.
…d hash (BACKLOG #1171, ASVS 11.4.1)

client.connect() passed no algorithm restriction -- disabled_algorithms appeared
ZERO times tree-wide -- so the connector offered paramiko's whole preferred MAC
list, which carries hmac-md5, hmac-sha1 and their -96 truncations. Appendix C of
the V11 chapter marks HMAC-MD5 D (disallowed) and SHA-1 L ("not suitable for
HMAC"). A server that selected one got it, on a use case the requirement names by
name and in a clause with no default-off escape.

STATED AS AN ALLOW-LIST, WHICH IS THE POINT. paramiko's API takes a deny list, so
_disabled_sftp_macs subtracts _APPROVED_SFTP_MACS from whatever the installed
library offers. A deny list has to be edited whenever the library gains an
algorithm, and the failure mode of forgetting is that the new algorithm is
PROPOSED. Here forgetting excludes it instead -- wrong in the safe direction, by
construction. Passed on every connect rather than behind a setting: a control an
operator has to switch on is not a control.

THE TEST ASSERTS NON-EMPTINESS BEFORE IT ASSERTS SAFETY, and that ordering is the
substance. "No weak member survives" passes trivially against an EMPTY effective
set, which is exactly what a broken subtraction -- or a paramiko that renamed
_preferred_macs -- would produce. Both arms proven by planting: removing the
restriction reds naming all four weak members; disabling everything reds on the
positive control first. Restored byte-identical by SHA-256.

The fake gained a Transport rather than production gaining tolerance for its
absence. A missing attribute there yields an empty deny list, which silently
restores the weak proposals, so the strictness belongs in the code and the
completeness belongs in the fake. Caught because an EXISTING test broke: the
shared _FakeParamiko had no Transport, which only surfaced on the wider run.

SCOPE, STATED: this measures WHAT THE CONNECTOR PROPOSES, not the effective set
negotiated with the real library -- paramiko sits behind the [sftp] extra and the
suite uses the repo's fake. #1171 names that gap ("an instrument that measures each
third-party surface's EFFECTIVE algorithm set") and it stays unbuilt. Also still
open in that item and untouched here: the HTTP Digest handler defaulting to MD5,
the five SMTP login() sites preferring CRAM-MD5, and the XML signature accept-set.
This does not move the cell.
…hree releases do not have (BACKLOG #1193, ASVS 15.2.4)

README told every reader "Every release is ... Sigstore-signed, and carries SLSA
build-provenance + PEP 740 attestations" -- directly below the install command for
messagefoundry-webconsole, which is one of the wheels that is not.

MEASURED, with controls in both directions. release.yml has three publish jobs.
Only `release` runs Sigstore signing, attest-build-provenance and the CycloneDX
SBOM; `release-webconsole` and `release-harness` contain none of those steps while
still showing five uses:/runs-on lines each, so the zero is a real absence and not
a scan artifact. All three DO set `attestations: true`, which is the PyPI-side
PEP 740 attestation -- a different artifact from a GitHub attestation.

So the note was not wholly false, which is why it is now scoped per artifact rather
than deleted or softened. A reader who installed the console wheel on the strength
of it and ran `gh attestation verify` would find nothing, having been told the
opposite. That is a compensating control resting on a false premise, in shipped
text, which is the defect class this cell is about.

THE GUARD IS DERIVED, NOT PROSE-PINNED. It parses release.yml, computes which
publish jobs sign, and only then requires the README to be scoped. If signing is
later added to the console job the claim becomes true and the test stops objecting
on its own. Two positive controls run FIRST -- the parse found 3+ publish jobs, and
the detector matched signing somewhere -- because a broken job-splitter yields an
empty unsigned set and a test that passes while measuring nothing. Proven by
planting the old unscoped sentence: it reds naming both unsigned jobs.

NOT TOUCHED, AND FLAGGED RATHER THAN GUESSED: docs/SUPPLY-CHAIN.md:37-38 says pip
verifies attestations automatically when installing from PyPI. #1193 records that
PEP 740 disclaims this. I could not verify the PEP's text or pip's behaviour from
this checkout, and correcting a supply-chain claim from memory is how a wrong
sentence gets a confident tone. It needs someone who can check the spec.

This does not move the cell. The loader still mounts the console on find_spec
presence with no provenance check, and the bare-name install remains in shipped
text -- both named in the item and both untouched here.
…four modules (BACKLOG #1216)

`shutil.which("bash")` is a fact about PATH, and on Windows PATH order decides
which OPERATING SYSTEM answers. C:\Windows\System32\bash.exe is the WSL launcher,
whose filesystem namespace is not the one the test just wrote a fixture into. So
`skipif(shutil.which("bash") is None)` asked whether A bash EXISTS, not whether the
one it found CAN DO THE JOB: the wrong interpreter is FOUND rather than absent, the
skip never fires, and every block fails for a reason unrelated to its content.

The working resolver already existed in test_merge_gate_controls.py and had since
2026-08-10 -- one day before the item was filed. Three other modules kept their own
which() guards, so the defect survived everywhere it had not been fixed. This is
promotion and adoption, not a new design: tests/_bash_resolver.py is now the single
source and test_merge_gate_controls.py delegates to it rather than keeping a fourth
copy. Two resolvers are free to disagree, and the one that disagrees is the one
still manufacturing failures.

ADOPTED IN FOUR MODULES: workflow_shell_syntax (skipif removed, resolver used),
dependabot_automerge_guardrails (2 bash-only skipifs dropped, 2 bash+jq narrowed to
jq -- jq is genuinely optional and that skip is legitimate and stays),
installed_coord_hooks (still PREFERS sh, but now proves whatever it finds can read
the probe), and merge_gate_controls (delegates).

TWO CORRECTIONS TO THE ITEM, both measured. It names ONE site in the dependabot
guardrails; there are FIVE. And the defect does NOT reproduce on this box --
which("bash") here resolves Git Bash, not the WSL launcher -- so no test added here
could be proven by watching failures disappear. Every test CONSTRUCTS the failing
condition instead. That is also why it survived a full day: on most boxes it is
invisible.

ADDED, AND IT IS THE ITEM'S OWN INSIGHT: a 100 PERCENT failure rate now raises
"instrument fault, not N content faults" rather than leaving a reader to notice the
ratio. The signature "154 shell blocks; 154 failed" printed on every run for a day
and was read as 154 real defects. Plus 127-vs-2 discrimination, so a harness fault
(command not found) can never be reported as a syntax error in the content.

tests/test_bash_resolver.py covers the properties the three modules were missing:
the probe rejects an interpreter that cannot read its own file, PATH is consulted
LAST, no usable bash RAISES rather than skipping, and a harness code is never
described as a syntax error. A positive control asserts the resolver succeeds here,
without which a resolver that rejected everything would satisfy every negative.

75 passed, 7 skipped (all jq, verified absent on this box). Exit code captured
BEFORE the pipe and the count asserted -- 'no tests ran' is not a failure message,
it is the absence of one.

#1272 is this item's duplicate per #1216's own text and is still OPEN in the
ledger. Closing it is not this seat's call.
…roken probe (BACKLOG #1216)

test_a_real_bash_is_found_and_can_read_this_process_files asserted
bash_sees(require_bash(tmp_path)). require_bash returns ONLY a candidate bash_sees
has already approved, so that is true by construction -- an identity, incapable of
failing, sitting in the test whose declared job is to be the positive control for
every negative in the file.

MEASURED, not reasoned. Mutating bash_sees to `return True` -- breaking the probe
wide open -- reddened the two negative tests and left this one GREEN. It certified
a resolver with no working namespace check, which is the exact defect #1216 exists
to close, one level up.

The second assertion is now INDEPENDENT of the selection predicate: it runs the
resolved interpreter directly with a different token, a different filename and a
shell builtin instead of cat. Proven with a mutation AIMED AT IT -- probe broken
open AND a non-bash offered first, so require_bash returns something that cannot
read the file. It now fails; before this change it did not. Restored byte-identical
by SHA-256 both times; 5 passed after.

FOUND BY APPLYING A PEER'S RULE TO MY OWN JUST-COMMITTED WORK: a control that reds
under a mutation aimed at it cannot be an identity. Necessary and sufficient per
ASSERTION, necessary only for a composite -- a live half masks a dead one, and here
the live "no bash resolved" half masked the dead one beside it.

Kept as its own commit rather than folded into 70186e4. The hole existing is the
finding; amending would erase that a test written an hour earlier, with a docstring
calling itself the positive control, could not fail.
…ed hash (BACKLOG #1171, ASVS 11.4.1)

digest_handler_from_settings returned a bare urllib HTTPDigestAuthHandler with no
algorithm restriction. urllib reads `chal.get('algorithm', 'MD5')` -- verified in
CPython's own source on the supported interpreter -- so the SERVER chooses, and an
endpoint that simply OMITS the parameter gets answered with MD5. Appendix C marks
MD5 D: disallowed for any cryptographic purpose, in a clause with no default-off
escape.

THE ABSENT CASE NEEDS NO HOSTILE SERVER. A plain RFC 2617 endpoint omits
`algorithm`, so the exposure was reachable through ordinary interoperability rather
than through an attack.

REFUSE RATHER THAN REMOVE. There is no way to dictate the algorithm to a peer, so
the honest options were refuse or delete the feature. Digest is a documented,
configurable, tested option, and refusing keeps it working for endpoints that offer
an approved hash while failing closed on the ones that do not. -sess variants strip
to the same hash and are treated as such; matching is case-insensitive.

LOUD, NOT `return None`. Returning None makes urllib skip the auth, and the request
surfaces as a bare 401 -- which an operator reads as bad credentials and goes
looking in the wrong place. It raises HttpAuthError, the same contract this seam
already uses for a refused cleartext hop.

BOTH ARMS PROVEN BY PLANTING. Removing the check: 3 failed. Emptying the approved
set so it refuses EVERYTHING: the positive control failed -- without that control,
deleting the feature outright would have satisfied every refusal case perfectly and
the suite would have reported a working gate over a connector that could no longer
authenticate at all. Restored byte-identical by SHA-256 both times.

SCOPE: this is one of #1171's four limbs. The SFTP MAC allow-list shipped at
f385a6d. The SMTP limb is measured and deliberately UNBUILT -- the item's own
prescription would replace a disallowed-hash challenge-response with a cleartext
password on an unencrypted channel, which needs a ruling I have asked for. The XML
signature accept-set is untouched. This does not move the cell.
…ed channel (BACKLOG #1171, ASVS 11.4.1)

smtplib's preferred_auths is ['CRAM-MD5', 'PLAIN', 'LOGIN'] -- CRAM-MD5 FIRST,
verified on the supported interpreter with auth_cram_md5 present -- so every
unrestricted smtp.login() offered an HMAC over MD5 before anything else. Appendix C
marks MD5 D: disallowed for any cryptographic purpose, in a clause with no
default-off escape.

BOTH HALVES OR NEITHER, PER THE DISPATCHER'S RULING, and the reason is the whole
finding. PLAIN and LOGIN SEND THE PASSWORD; CRAM-MD5 does not. Restricting the
mechanism alone would have closed a conformance gap by putting a cleartext password
on the wire wherever email_use_tls=false -- strictly worse than the gap. I raised it
rather than build the weaker half from the item's own wording, which says only
"constrain or delete the SMTP mechanism selection".

THE ESCAPE IS REUSED, NOT MINTED, AND I CHECKED THE RIGHT ONE. Asked to confirm an
existing mechanism's semantics cover an SMTP surface before reusing its name, I
found the obvious candidate does NOT: refuse_cleartext_credential_hop is keyed on a
URL scheme and its second statement is `if scheme != "http": return`, so passing it
"smtp" would have been a silent no-op -- a control that looks wired and enforces
nothing. weakened_tls_escape_permitted is scheme-agnostic, posture-clamped, and
already governs the LDAPS bind, the MLLP/FTPS contexts, the SFTP host-key acceptance
and the webhook sink. That is the one used.

Threaded as a parameter rather than read in-module: settings imports tls_policy, so
the reverse edge would be circular. Same explicit-posture threading the store hop
and the other out-of-gate cells already use.

BOTH HALVES PROVEN BY PLANTING. CRAM-MD5 back into the approved set: the
mechanism test failed. Dropping the unencrypted-channel refusal: the channel test
failed. Each plant left the OTHER test passing, so neither is carrying the other.
A positive control asserts the clamped escape still crosses an unencrypted hop --
without it, a helper that raised unconditionally would satisfy both refusals and the
suite would report a working gate over a connector that can never authenticate.
Restored byte-identical by SHA-256 both times.

NOT YET WIRED, AND THAT IS THE NEXT LAYER RATHER THAN AN OVERSIGHT. The five
login() call sites still call smtplib's own login. Four of them are on
EmailDestination/DirectDestination, which capture NO posture -- login runs at SEND
time, outside the construction gate that stamps the contextvar, so wiring them needs
a posture threaded through construction the way alert_sinks already threads one into
the webhook sink. That is a design step, not a mechanical edit, and it is stated
here rather than guessed at.
…t choose (BACKLOG #1171, ASVS 11.4.1)

XMLVerifier.verify takes expect_config and this call passed NONE, so the library's
own default decided which algorithms a partner's signature could use. Measured on
the pinned version, that default admits SHA-224 and SHA3-224 as digests plus six
signature methods built on them. The V11 appendix disqualifies a sub-254-bit digest
for any collision-resistance-requiring application, and a signature is one.
Digests 8 -> 6, signature methods 25 -> 19.

DERIVED BY SUBTRACTION, NOT REBUILT. dataclasses.replace keeps every other field
the pinned version ships -- require_x509, expect_references, the c14n method -- so
a future signxml that hardens a default we never named is not silently reverted by
this call. A hand-built config would freeze today's answer to questions this change
is not trying to decide, and there is a test asserting exactly that.

SCOPE DELIBERATELY NARROWER THAN THE ITEM'S, and this is the part worth reading.
#1171 also reads the appendix as disallowing RSA-PKCS#1-v1.5 and DSA outright, and
both are in the default accept-set -- RSA_SHA256 and its siblings are PKCS#1 v1.5,
DSA_SHA256 is present. THAT RESTRICTION IS NOT APPLIED. It would refuse the most
common XML-DSig signature in use, and the appendix's text cannot be read from this
checkout to confirm the reading. Breaking the common case on a relayed reading of a
standard is not a call this change makes; it needs someone who can open the source.

BOTH ARMS PROVEN BY PLANTING. Returning the library default unfiltered: the
weak-digest test failed. Emptying the accept-set instead of narrowing it: the
positive control failed -- without which an empty set would satisfy "no weak
algorithm survives" perfectly while refusing every signature, a different defect
wearing the same green. Restored byte-identical by SHA-256 both times.

This is #1171's fourth limb. SFTP (f385a6d), HTTP Digest (5f7be3e) and the SMTP
helper (87d7223) are the other three; the SMTP helper's five call sites remain
unwired pending a posture-threading decision. The cell does not move.
…BACKLOG #1171, ASVS 11.4.1)

send_plain_email ran STARTTLS only when use_tls, then logged in whenever a username was
set. So [alerts].email_use_tls=false beside email_username put the password on the wire,
with no refusal at the function, at EmailTransport, or at the serve gate. Both callers --
EmailTransport.send and security_notify -- pass that same knob, so the gap was reachable
twice.

THE ITEM NAMED FIVE UNRESTRICTED smtp.login() SITES AND FOUR WERE ALREADY GATED.
transports/email.py:153 and transports/direct.py:203 refuse this exact combination at
construction, and their tests SET the escape env var and still assert the raise. Wiring
the escapable config/tls_policy.py helper into those four would have converted an absolute
refusal into an escapable one, so it is not wired in; the fifth site is made to match them
instead. One new rule would have been a second spelling of a rule that already exists.

ABSOLUTE, AND THAT IS WHY IT CAN LIVE AT THE CHOKEPOINT. The docstring above it explains
that the tls_verify=false decision was pushed to the serve gate because it needs the
CLAMPED posture, which this module cannot read. A credential over cleartext needs no
posture -- it is refused on every instance in every environment -- so send_plain_email,
which every caller passes through, is the right seam and covers both callers with one
rule rather than two that can drift.

Tests assert the fake recorded NO login, not merely that something raised: a refusal
firing after the credential reaches the transport is not a refusal. Two positive controls
pin the other half -- cleartext without a credential still sends, and a credential over
STARTTLS still logs in -- so a guard that over-refused could not pass. MEASURED by
mutating the guard to `if False`: exactly the three refusal tests go red and both positive
controls stay green.

AND THE FIXTURE WAS RESET PER TEST, BECAUSE MY OWN TESTS WERE READING STALE STATE.
_RecordingSMTP.captured is CLASS state cleared only in __init__, i.e. only when a
connection is actually opened. A refusal that fires before smtplib.SMTP is constructed
leaves it holding the PREVIOUS test's dict, so "login not in captured" was asserting
about the wrong test. The security-event case passed alone and failed after the one that
logs in successfully; the other three passed only because of the order they ran in.
… (BACKLOG #1171, ASVS 11.4.1)

smtp_login_approved shipped in 87d7223 UNWIRED. Its refusal arm was reachable from tests;
its auth arm was executed by nothing, anywhere. Wiring it is what found that it never
assigns smtp.user/smtp.password -- and stdlib's auth_plain says in its own docstring
"Requires self.user and self.password to be set". It would have raised AttributeError on
its first real authentication and passed every fake in the suite, because every fake
modelled login() rather than auth(). A control proven only where it is not used is the
finding here; the mechanism restriction below is merely the feature.

WHAT THE RESTRICTION IS FOR. smtplib's login() builds its preference list as
['CRAM-MD5', 'PLAIN', 'LOGIN'] and takes the first the server advertises, so every
unrestricted call proposed an HMAC over MD5 -- disallowed by Appendix C for any
cryptographic purpose, in a clause with no default-off escape. auth() is now driven
directly against the advertised list, restricted to PLAIN and LOGIN.

WIRED AT ALL FIVE SITES, ALONGSIDE THE EXISTING GATES AND NOT INSTEAD OF THEM. The item
named five unrestricted call sites; four were already guarded at construction, and those
guards refuse a cleartext credential absolutely -- their own tests set the escape env var
and still assert the raise. Passing escape_permitted=True would have made an absolute
refusal escapable, so every call site passes False. The construction gate still refuses
first; this is a send-time backstop that is never weaker than the gate it backs up.

SECOND DEFECT, CAUGHT BY THE SUITE: a rejected credential was being wrapped in
InsecureHopRefused. A wrong password is a DELIVERY failure, not a policy refusal, and it
had stopped mapping to DeliveryError -- an operator with a typo would have gone reading TLS
policy. The server's own exception now propagates; InsecureHopRefused is reserved for the
two decisions this function actually makes.

REJECTED, RECORDED BECAUSE IT WAS THE CHEAP OPTION: filtering CRAM-MD5 out of
smtp.esmtp_features so login() picks PLAIN itself. No fake would have needed touching. It
means falsifying the library's record of what the server advertised, which every later
reader of that field consumes as truth, and the set of readers cannot be enumerated.

THE FAKES NOW ADVERTISE CRAM-MD5 DELIBERATELY. It is what a regression to login() would
select, so a fake offering only PLAIN/LOGIN could not tell a fixed cell from a broken one.

VERIFIED: ruff check, ruff format, mypy strict over 267 files, 198 tests across every
affected module, and a mutation pass -- neutering the guard reddens exactly the three
refusal tests and leaves both positive controls green; swapping smtp_login_approved back
for smtp.login reddens the CRAM-MD5 conformance test.

NOT VERIFIED AT COMMIT TIME: the full suite. It is running and roughly a third of the way
through. Committing ahead of it deliberately -- uncommitted work has no SHA and the pool
rate projects exhaustion before the run would finish. If it reds, the fix is a follow-up
commit; the alternative was risking the whole change to avoid that.
…KLOG #1171, ASVS 11.4.1)

InsecureHopRefused subclasses ValueError, so it escapes both connectors' `except
smtplib.SMTPException` arms and lands in the delivery worker's catch-all -- which
wiring_runner documents as "Internal/code error (our bug, not the partner)" and which
dead-letters the row under the default CONTINUE policy. So a relay offering no approved
AUTH mechanism, or a connection configured without TLS, would have told an operator to go
read our source.

FOUND BY A PEER'S ADVERSARIAL PASS, NOT BY ME. I checked this exception's class while
wiring and judged it correct -- but I judged it against the CONSTRUCTION gate, which
raises ValueError and is supposed to. At send time the same class means something else,
and I did not re-ask the question at the second site.

IT IS THE SAME MISCLASSIFICATION ONE LAYER DOWN from the one this branch already fixed. A
rejected credential was being reported as a policy refusal; a policy refusal was being
reported as an internal code error. Each sends the operator to the wrong file.

Both connectors now convert it to DeliveryError, which is the transport-failure arm:
retried per policy, lane marked unhealthy, alert raised. The refusal text is carried whole
rather than reduced to a type name -- it is host, cell and mechanism names only, never
message content, and it is the one thing that tells an operator what to change.

TESTS: a relay advertising ONLY CRAM-MD5 must surface as DeliveryError and must NOT have
received the credential. Plus a positive control -- a relay advertising CRAM-MD5 and LOGIN
still authenticates, falling through to LOGIN -- because a guard that refused every
authenticated send would satisfy the first test perfectly.

AND THE FAKE'S ADVERTISED SET IS NOW A CLASS-LEVEL DEFAULT, because the first version of
these tests did not work. __init__ assigned self.esmtp_features, an INSTANCE attribute
shadowing the class attribute the test patched, so the patch had no effect and the relay
kept advertising the full set. The thing I patched was not the thing being read; __init__
now copies from AUTH_ADVERTISED so a test can vary it.

MEASURED: removing one conversion arm reddens exactly the refusal test and leaves the
positive control green.
…ard caught (BACKLOG #1216)

`test_every_non_engine_test_is_classified` failed on the branch's first FULL suite
run: test_bash_resolver.py imports no engine module and appeared in neither
tooling_manifest.txt nor _STAYS_WITHOUT_IMPORTING, so it was unclassified and would
have run on every engine leg forever.

I ADDED THAT FILE HOURS AGO AND NEVER RAN THE GUARD. Every verification before this
was targeted modules -- the ones I had touched -- and this guard was not among them.
"The files I touched are green" is not "the tree is green", which I wrote in my own
words before committing on exactly that basis.

MANIFEST, NOT _STAYS_WITHOUT_IMPORTING. The list is for files that LOOK like harness
but whose subject is engine source; this one's subject is the shared bash resolver
that four harness tests consume -- merge_gate_controls, installed_coord_hooks,
workflow_shell_syntax, dependabot_automerge_guardrails -- and all four are manifest
entries themselves. It reads no engine source, so the second rot mode the guard's
docstring names does not apply.

THE PATH-GATE QUESTION, ASKED BEFORE ASSUMING: a manifest entry moves to the
path-gated tooling job, so a change that breaks it can miss its own PR. ci.yml
answers it in place -- the job also runs on every push to main, and its comment
states that is deliberate: "The push arm is the safety net, not redundancy: the PR
arm only fires when the harness itself changed... push-to-main is where it
surfaces." This entry sits in exactly the position of the four tests it serves,
under the same documented backstop.

MEASURED: removing the entry reddens the guard; restoring it greens it. 84 passed
across the guard, the resolver tests and all four consumers.

FULL SUITE at f6796e8: 1 failed, 13142 passed, 833 skipped, 34m18s. This was the
one failure.
…#1216)

bash_candidates tried `<git>/bin/bash.exe` before `<git>/usr/bin/bash.exe`. The first is
Git for Windows' MINGW64 WRAPPER: it rewrites the inherited PATH so /mingw64/bin leads,
ahead of anything the caller prepended. Git ships curl.exe there, so a test that prepends
a curl stub is silently bypassed and the step reaches the LIVE network -- which is how a
release-age check passed off pypi.org instead of off its fixture.

MEASURED HERE, same command, one variable:
  Git/bin/bash.exe      PATH head -> /mingw64/bin   (the prepended stub dir is GONE)
  Git/usr/bin/bash.exe  PATH head -> <the prepend>  (preserved)
And require_bash was selecting the wrapper.

SELECTIVE, WHICH IS WHY IT READ AS FLAKINESS: only stubs whose binary Git also ships in
mingw64/bin lose. gh and jq stubs kept winning because Git ships neither.

THE PART THAT IS MINE TO OWN: bash_sees CANNOT CATCH THIS, and that is not a gap in the
probe -- it is a different dimension. It asks whether the interpreter shares this process's
FILESYSTEM NAMESPACE, and the wrapper does, perfectly. PATH ORDER is orthogonal. So the
control could not fail in the direction the thing was failing, which is the rule I had
published hours earlier and then broke in the code I wrote after stating it. Demonstrated
in a test rather than asserted: the wrapper passes bash_sees and fails the new probe.

bash_preserves_path_order is the control for that dimension, and require_bash now demands
BOTH -- a WSL bash preserves PATH order and cannot open the file; the wrapper opens the
file and rewrites PATH. Neither control alone admits only the right interpreter.

MEASURED: reverting the candidate order reddens the ordering test; the resolver now selects
Git/usr/bin/bash.exe; the four consumer modules pass 70 with 7 jq-gated skips.

NOT VERIFIED HERE, said plainly: the CI rows that actually failed are jq-gated and SKIP on
this box, so I have confirmed the mechanism directly and have NOT watched those rows go
from red to green. The lander reproduced the mechanism independently on their box.
…CKLOG #1216)

Prepending a stub directory to PATH is not the same as the stub being CHOSEN, and the gap
is silent: the step runs, the real binary answers, and the row decides on whatever the
network said. _run_step_body now resolves every stub with `command -v` in the SAME child
env the body is about to execute in, and fails loudly naming what resolved elsewhere.

BY RESOLUTION, NOT BY READING PATH BACK. `command -v` reports what the shell would
actually execute. Reading $PATH would only confirm the string we just set -- the wrapper's
entire behaviour is to rewrite it between here and there.

THIS IS DEFENCE IN DEPTH, NOT A DUPLICATE. The resolver decides which bash we launch; this
decides whether the stub won in the environment this body runs in. A future shell, a
wrapper script, or a CI image that reorders PATH for its own reasons is caught here
without anyone having predicted it.

AND I SHIPPED IT UNREACHED FIRST, WHICH IS THE DEFECT I FIXED AN HOUR AGO. MEASURED:
mutating _assert_stubs_win to raise unconditionally changed NOTHING in this module -- 27
passed, 7 skipped, byte-identical. Every row that uses path_prepend is jq-gated and skips
on this box, so the guard was correct by construction and exercised by nothing, exactly
like the helper that shipped unwired in 87d7223.

SO IT NOW HAS A DIRECT TEST, and the discriminating pair is free on Windows: the same stub
directory WINS under the resolved bash and LOSES under Git's bin/bash.exe wrapper, because
that wrapper puts /mingw64/bin first and Git ships curl.exe there. Both halves asserted --
a guard that raised for everything would satisfy the negative half alone. It skips only
when the wrapper is genuinely absent or ships no mingw64 curl, and says which.

MEASURED: neutering the guard reddens the new test; restoring it greens. 28 passed where
27 passed before, so the test is running rather than collecting.
…KLOG #1216)

My regression test identified Git's MINGW wrapper by shape -- parent `bin`, grandparent
not `usr`. On Linux that matches /bin/bash, so the skip never fired and the test asserted
that a plain Linux bash rewrites PATH. It does not. The assertion was right and the
SUBJECT was wrong. Red on ubuntu-latest; found by the lander reading the CI output.

WORSE THAN REPORTED, MEASURED: it matches TWO Linux shapes, not one. /bin/bash and
/usr/local/bin/bash both have parent `bin` and a grandparent that is not `usr`. Only
/usr/bin/bash was excluded, and by accident of the one condition that happened to fit.

THE SUFFIX IS THE PLATFORM TEST, and sys.platform is deliberately NOT. The wrapper is
`bin/bash.exe` by construction, so `.exe` identifies it exactly. A platform guard would
make the docstring's "skips only when the wrapper is genuinely absent" false a SECOND way:
it would also skip on a Windows box with no Git, for a reason the sentence does not name.
The suffix keeps the stated and actual skip conditions identical. That reasoning is the
lander's and it is better than what I would have written.

SIMULATED RATHER THAN GUESSED, because this box cannot run Linux: the ubuntu candidate
shapes now match NOTHING (skip fires) and the Windows set still matches exactly the
wrapper. Asserted in both directions -- a predicate that matched nothing anywhere would
satisfy the Linux half alone.

FIXED IN BOTH COPIES. I had duplicated the predicate into test_dependabot_automerge_
guardrails.py when I wrote the self-certification test, so the same defect existed twice
and a one-file fix would have left the second to fail on the next ubuntu run.

AND THE FIRST ATTEMPT AT THIS EDIT DID NOTHING. My anchor omitted the leading `if `, the
assertion refused, and the ruff and pytest that followed ran on UNMODIFIED files -- so a
green appeared for a fix that had not been applied. The anchor is why that surfaced
instead of shipping.
…122)

ASVS 3.5.3. `assert_not_cross_site` runs as a route dependency, and `/ui/static`
is a Starlette `Mount` rather than an `APIRoute`, so no dependency runs for it.
The asset tier was the one /ui surface the per-route check could not reach, which
is why this is middleware rather than another dependency.

THE RULE IS NOT THAT CHECK AT A WIDER SCOPE, and building it as one was a live
defect in this branch. A first cut read `Sec-Fetch-Site` alone by reusing
`_CROSS_ORIGIN_FETCH`, which 403s every cross-site TOP-LEVEL NAVIGATION: intranet
links into the console, and every real SSO login, since the IdP redirect back is
cross-site by construction. That set is correct for its existing callers only
because they have already established the request is a non-navigation fetch --
its name says FETCH. Reuse did not carry the precondition across.

`test_oidc_callback_survives_a_cross_site_navigation` caught it, having warned in
its own docstring that otherwise "every real login would 403 while every hermetic
test still passed". None of the four tests written alongside the middleware did:
they shared the mental model that produced the defect.

The refusal now needs cross-site AND not-a-safe-top-level-navigation -- `navigate`
mode, GET or HEAD, destination outside `object`/`embed`. Method is load-bearing,
because a cross-site POST navigation is a CSRF form submission and no supported
flow makes one (`response_mode=form_post` is not implemented here).

Two constraints were measured before any code was written. Each looks like a
hardening improvement and each breaks shipped behaviour if inverted:

- ABSENT IS ALLOWED. `Sec-Fetch-Site` is browser-populated; `tray/probe.py` builds
  its client with no headers at all, and 332 headerless /ui call sites exist in
  this corpus.
- 403, NEVER 404. `tray/probe.py` maps 404 to DISABLED and every other status to
  ENABLED, so a 404 would make the tray report a healthy console as switched off.

Eight tests. Three mutations, each reddening exactly one test and the right one;
mutation 1 is the navigation defect above, so this file now catches it rather
than borrowing test_webui.py's coverage. Full webconsole suite: 383 passed,
3 skipped.
ASVS 1.2.2, the console clause. The apiclient half of this item shipped in #488
with `_seg` and is pinned by tests/test_apiclient.py; the console half was left
behind, and measuring it on origin/main found `safe=""` appearing ZERO times in
messagefoundry_webconsole/ against a control of 31 files matching `def ` in the
same run.

`quote`'s DEFAULT is the defect. It leaves `/` alone, which is the one character
a path segment turns on: measured, `quote("IB/ACME")` returns it UNCHANGED. So
the two sites that looked encoded were not, and the dead-letter replay forms
interpolated a channel id and a destination name with no encoding at all -- a
connection named `IB/ACME` addressed `/ui/dead-letters/IB/ACME/replay`, a
different route with an extra segment.

SCOPED TO CONNECTION NAMES, deliberately. The other /ui interpolations carry ids,
where the "every interpolated id is a `uuid4().hex`" argument is probably sound.
It is not sound for connection names: `Registry._add` checks only for a duplicate,
so a name is unconstrained free text. The id sites are left alone rather than
swept, because that argument rests on a data-grammar invariant no URL-building
code asserts, and settling it is separate work.

A blanket sweep would also be WRONG, which is why one test exists purely to stop
it: `_auth`'s reauth `next` carries a whole path inside a query parameter and is
correctly `safe="/"`. Encoding it as one segment breaks every re-auth redirect.

Five tests. The dead-letter pair renders the real page rather than reading the
f-string; a source scan guards the guard so a new site on these routes reds the
suite instead of slipping in unencoded. Three mutations, each caught -- and the
connections.py one is caught ONLY by the source scan, which is why that scan is
there.

VERIFICATION AT COMMIT TIME, stated exactly: ruff, mypy strict over 33 files, the
5 new tests, and 3 mutation passes all green. The full webconsole suite was still
RUNNING when this was committed, under a pool-exhaustion warning, and had not
reported. If it reds, the fix follows this commit rather than being folded back
into it.
ASVS 1.2.2, completing the console clause. All 54 interpolated /ui path literals
were partitioned by reading each value's PRODUCER rather than its interpolation
line, which is the only way this question has an answer.

THE ID SITES ARE SAFE, BUT NOT FOR THE REASON USUALLY GIVEN. The prevailing
argument is "every interpolated id is a `uuid4().hex`". What actually protects
them is that each is read back from the store after a lookup that 404s on a miss,
so a crafted path param never reaches a render -- `get_message` and
`replay_message` both raise before their pages build.

`ui_role_update` IS THE SINGLE EXCEPTION ON THE SURFACE. A `ValidationError` from
`CustomRoleRequest` short-circuits BEFORE `update_custom_role` runs, so that 404
never happens, and the 400 branch rebuilds the page from
`CustomRoleInfo(id=role_id, ...)` with the RAW path param. `CustomRoleInfo.id` is
a bare `id: str` with no `Field` constraint. Both form actions now go through
`_seg`.

Three sites reported as needing encoding were REJECTED after reading the gate
above: the two `pages/messages.py` back-links and `routes/core.py`'s replay
redirect. They should use the store value in scope rather than the path param,
but nothing hostile reaches them, so that is code quality and not this item.

A CORRECTION THAT CHANGES NO CODE, recorded because deleting the call would open
it: `_auth`'s reauth `next` is safe because of the `quote()` AT THE SITE, not
because the value is server-generated. Adversarial review showed
attacker-influenceable bytes do reach it. Right answer, wrong reason -- the same
shape as the constant lifted out of its precondition in #1122 earlier today.

VERIFICATION AT COMMIT TIME, stated exactly: ruff, ruff format, mypy strict over
33 files, the 7 tests in this file, and 2 mutation passes (each reverting one form
action to the raw id, each reddening the right test) are all green. The full
webconsole suite was still RUNNING when this was committed, under a pool
trigger, and was clean through 36% with no failures. If it reds, the fix follows
this commit rather than being folded into it.
TWO DEFECTS, ONE DISCOVERED INSIDE THE OTHER.

#1318 -- `messagefoundry init` wrote `[api].host`, which ADR 0118 relocated to
`[security]`, so `_reject_relocated_keys` refused the file the product had just
generated. First serve after first init exits 2. Shipped in v0.3.1 and v0.3.2.

THE SCAFFOLD DID NOT CHANGE. THE LOADER DID. The template emitted `[api].host`
since before v0.3.0 and was correct then. The relocation swept the documents and
the settings and never swept the one place that WRITES a config file, so a correct
template was invalidated from elsewhere with nothing connecting the two.

The fix moves the keys to their real home rather than deleting settings an
operator needs: `[api].host` split into `[security].local_access_only` +
`listen_address`, `[ai].data_class` -> `handles_real_patient_data`,
`[ai].production` -> `production_instance`, `[egress].deny_by_default` ->
`[security].block_unlisted_outbound`.

Census, reproduced independently and widened: scaffold.py writes THREE toml files,
not one. `environments/dev.toml` and `environments/prod.toml` carry zero relocated
keys for a STRUCTURAL reason -- they are flat `env()` value files, so no
(section, key) pair CAN match. Population was 1 active and 3 commented-but-armed,
each of the three with an instruction beside it telling an operator to uncomment
it.

Probing each commented key individually caught the same trap in the NEW template:
uncommenting `listen_address` alone is refused, because `local_access_only`
defaults true and `0.0.0.0` is not loopback. The two are now presented as a pair
to uncomment together.

#1320 -- THE GATE WAS NOT BLIND. IT SAW THE DEFECT AND CALLED IT A SKIP.
`messagefoundry check` LOADED the config, got the exact refusal, PRINTED it, and
returned `rc 0`:

    skip  posture: settings did not load: [api].host moved to [security]...

FOUR sites in checks.py caught the load error and returned `ok=True, skipped=True`
-- posture, build-check, alert-smtp-tls and reference-backend, three of them
`required=True`. So the commit and CI gate could not fail on ANY unloadable
service config, not just this one.

ABSENT IS A LEGITIMATE SKIP; PRESENT-BUT-REFUSED IS NOT. The except clause
rendered two states with opposite remedies identically -- the same shape
CLAUDE.md already records for `goalPromptedAt`. The four absent-file skips are
untouched.

A FIFTH SITE IS DELIBERATELY UNCHANGED, said here rather than omitted so "four"
is not read as the whole surface: the `snapshot_on_send` resolver returns
`PipelineSettings().snapshot_on_send` on a load failure. It is a value resolver
rather than a check, and it still swallows silently.

THE EXISTING TEST WAS PINNING THE DEFECT. `test_scaffold_writes_the_skeleton`
asserted `"data_class" in toml and "production" in toml` -- the RETIRED spellings
-- under a comment claiming it checked "the new posture model". A string
assertion cannot survive a rename made somewhere else, so the real guard is now a
round trip through `load_settings`.

Four tests. The durable one uncomments every commented key ON ITS OWN and requires
that none produces a relocation refusal, so the NEXT relocation reds this suite
instead of shipping. `test_check_fails_on_a_config_that_is_present_but_refused`
asserts the POSTURE ROW, not merely a nonzero rc: measured, reverting posture
alone reddened nothing because build-check still failed, and a test that needs
every site to break before it fails is not guarding any of them.

RED FIRST, as required: restoring `[api].host` to the template now reddens
`test_scaffolded_config_passes_check` -- the test that asserted `rc == 0` and
passed over the broken config for two releases.

VERIFICATION AT COMMIT TIME, stated exactly: ruff, ruff format, mypy strict over
267 files, and 158 tests across test_scaffold, test_scaffold_requirements,
test_checks and test_cli are green, with four mutation passes. The FULL engine
suite was at 5 percent and still RUNNING when this was committed; it had reported
no failures. If it reds, the fix follows this commit rather than being folded into
it.
COMMON 5.6b: session handoffs live under `<git-common-dir>/mefor-coord/handoffs/`,
which is inside `.git` and therefore untrackable BY CONSTRUCTION -- the rescue
mechanism pushes a REF, and a ref carries only committed content, so no rescue
tag has ever covered one. Against a pool cutoff that location is fine; the
exposure is machine loss.

This commits only the part the rule names as irreplaceable: instruments that
returned a confident wrong answer, and the rules that survived them. The
per-session narrative stays in the handoff, per COMMON 5.1.

Every entry was measured, and every one was CORRECT about what it measured and
wrong about what it was asked -- `head -60` printing three of four sites, a
`grep -c` of 8 read as absence when all 8 hits were the handling, `git status`
answering "differs from HEAD" to "is someone mid-edit", two mail timestamps read
as a cause, a SHA read as a state.

Four rules, of which one is new and sharpens a rule this repo already had: a
positive control proves the INSTRUMENT, not the AIM. Confirming a pattern fires
on a corpus establishes the grep works there; it cannot establish that there is
the right place to look.

Two shapes that are not instrument failures are recorded beside them because they
were mistaken for such repeatedly: a marker that reverses its own meaning in
context (the section 11 glyph argument, reproduced in prose), and that mutual
deference has no fixed point -- nor does mutual assertion, which is the same
failure with the opposite sign.

Contributed by several sessions working the same tree, not one seat.

No new claims are made here. Every measurement cited was taken by the seat that
reports it, and the two population claims that were withdrawn during the session
are recorded as withdrawn rather than restated.
Credited to the lander and the dispatcher, who found the sharper statement by
falling into and setting the trap respectively.

WAS: "a marker that reverses its own meaning in context is worse than no marker"
-- true, and it does not tell you what to do.

NOW: A STATUS MARKER MUST NOT BE A WORD THAT ALSO APPEARS IN NARRATION.

That is checkable before you write it. It also records the second instance: one
reader took "SUPERSEDED BY #1326" from a body paragraph as the row's status,
which is the same presence-equals-meaning failure as the first, in the opposite
direction.

Adds the part that makes it operational here: the banner alphabet is the status
channel and `parse_items` is its only correct reader. Anything in the body is
narration however emphatic it is. Four seats grepped a body for a status word
tonight and none of them was careless.
@wshallwshall

Copy link
Copy Markdown
Collaborator Author

The red is not this branch's. Attributed, not hand-waved as a flake.

test (windows-2022, py3.14) fails at
tests/test_connscale_smoke.py::test_connscale_smoke_end_to_end --
AssertionError: fixed_aggregate@N=24: 25.1 < prior 34.5 * 0.75.

This branch cannot reach that code. Its twelve files are webconsole, scaffold.py, checks.py, one research doc and their tests. Zero under messagefoundry/pipeline/, messagefoundry/store/ or messagefoundry/transports/, and it does not touch the test itself.

The assertion is a throughput-monotonicity SLO -- this run's aggregate against 75 percent of a prior run's -- which is environmentally sensitive by construction on a shared runner. A previous lander proved that specific red environmental earlier today with a same-arm test: a commit changing no code.

Worth flagging beyond this PR: test (windows-2022, py3.14) is one of the 13 required contexts, so this flake blocks anything it lands on. It took #516 earlier tonight and now this. That is a standing tax on the queue rather than a property of any branch.

Not re-running it. This PR is a draft for triage and must not be armed, so a green here buys nothing and costs a full cycle on a Windows leg. Whoever picks the triage up should re-run then, against a rebased branch.

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