Skip to content

fix(harness): the two-box batch_ab rig could not start — restore the bind override and move its ports - #96

Merged
wshallwshall merged 6 commits into
mainfrom
harness-batch-ab-ports
Aug 3, 2026
Merged

fix(harness): the two-box batch_ab rig could not start — restore the bind override and move its ports#96
wshallwshall merged 6 commits into
mainfrom
harness-batch-ab-ports

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

Two independent defects, either of which alone prevents a batch_ab two-box run from starting. Both verified against origin/main before editing, with an adversarial second pass.

1. The off-loopback bind override reached only one of the two node types

_insecure_bind_args lived in shardcert.py and was called from ShardCertNode.start — but never from EngineNode.start, which is what the batch engine box uses.

A two-box rig exports MEFOR_INBOUND_BIND_HOST=0.0.0.0 so off-box senders can reach the inbound ports. That non-loopback plaintext-MLLP bind trips serve's off-loopback exposure gate (ADR 0002 §0), which refuses at start rather than warning. The engine box therefore could not come up at all.

The helper moves to failover.py — the module that owns EngineNode — and shardcert.py imports it. One definition instead of a verbatim duplicate of a security-adjacent gate that was already free to drift.

Behaviour is unchanged for every existing caller. The four other EngineNode call sites resolve to loopback-or-unset, so their argv stays byte-identical, and the failover two-node run sets no bind host at all. Verified directly:

loopback -> []          unset -> []          0.0.0.0 -> ['--allow-insecure-bind']
shardcert._insecure_bind_args is failover._insecure_bind_args -> True

2. batch_ab's inbound port band overlapped the sink port

base_port = 2600 spans [2600, 2600+count), and the single-box default --sink-port is 2700. The shape guard therefore fails loud from 101 connections on — not merely at 1024, as first reported. The band also straddles RDP 3389 on a Windows bench box.

Moved to 20000: the harness's own --inbound-base default (harness/__main__.py:576, multishard.py:275), clear of the two-box --sink-base 40000 band, and the value the bench operator was already hand-editing in to run the sibling fuse_ab profile.

The regression guard, also stranded

tests/test_bench_insecure_bind.py pins "non-loopback ⇒ flag present for both node types, loopback ⇒ byte-identical argv". Nothing on main pinned either shape — which is why defect 1 survived. It was written alongside the original fix and stranded with it. Structural only: it fakes create_subprocess_exec, so no engine spawns, no DB, no network.

Proven red-then-green by mutation, not assumed:

drop the EngineNode call site test_engine_node_adds_flag_for_nonloopback FAILS (1 failed, 5 passed)
restore 6 passed
file after restore byte-identical

Verification

ruff check and ruff format --check clean. 591 passed / 39 skipped across the bench, shardcert, failover and connscale suites. mypy does not cover harness/ — CI types messagefoundry + messagefoundry_webconsole only.

Known, deliberately out of scope

fuse_ab.toml, connscale.toml and pooled_ab.toml all pin the same base_port = 2600 and trip the same guard at counts ≥ 101. This PR fixes only batch_ab, matching the reported defect. Say the word and they can follow in one line each.

🤖 Generated with Claude Code

…bind override and move its ports

Two independent defects, either of which alone stops a batch_ab two-box run.

1. The off-loopback bind override reached only ONE of the two node types.
   `_insecure_bind_args` was defined on shardcert.py and called from
   ShardCertNode.start, but never from EngineNode.start — which the batch
   engine box uses. A two-box rig exports MEFOR_INBOUND_BIND_HOST=0.0.0.0 so
   off-box senders can reach the inbound ports; that non-loopback plaintext
   MLLP bind trips serve's off-loopback exposure gate (ADR 0002 §0), which
   REFUSES at start rather than warning. So the engine box could not come up.

   The helper moves to failover.py, the module that owns EngineNode, and
   shardcert.py imports it — one definition instead of a verbatim duplicate of
   a security-adjacent gate that was already free to drift. Behaviour is
   unchanged for every existing caller: the four other EngineNode call sites
   resolve to loopback-or-unset, so their argv stays byte-identical, and the
   failover two-node run sets no bind host at all.

2. batch_ab's inbound port band overlapped the sink port.
   base_port = 2600 spans [2600, 2600+count); the single-box default
   --sink-port is 2700, so the shape guard fails loud from 101 connections on,
   not merely at 1024. The band also straddles RDP 3389 on a Windows bench box.
   Moved to 20000 — the harness's own --inbound-base default, clear of the
   two-box --sink-base 40000 band, and the value the operator was already
   hand-editing in to run the sibling fuse_ab profile.

Also lifts tests/test_bench_insecure_bind.py, the structural guard that pins
"non-loopback => flag present for BOTH node types, loopback => byte-identical
argv". Nothing on main pinned either shape, which is why defect 1 survived. It
was written alongside the original fix and stranded with it.

Verified: ruff check + ruff format clean; 591 passed / 39 skipped across the
bench, shardcert, failover and connscale suites; the new guard proven
red-then-green by mutation (drop the EngineNode call site ->
test_engine_node_adds_flag_for_nonloopback fails; restore -> 6 pass). mypy does
not cover harness/ (CI types messagefoundry + messagefoundry_webconsole only).

Note: fuse_ab.toml, connscale.toml and pooled_ab.toml pin the same base_port =
2600 and trip the same guard. Left alone deliberately — out of scope here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
wshallwshall added a commit that referenced this pull request Jul 31, 2026
…o trackers (#95)

Three independent doc defects, all verified against origin/main before editing.

1. BACKLOG #96 carried three pieces of capacity guidance that ADR 0074's
   2026-07-14 validity re-check found measured-false and unsafe, and the
   correction never landed - it sat only on a stranded commit (bd969b0e) in a
   ref namespace whose remote no longer exists. B14 named four downstream
   artifacts; three were amended on 2026-07-14 and #96 was the last one left,
   so ADR 0074 required-change 13 still read as fully outstanding.

   Corrected here: the sum-across-interfaces aggregate rule (measured-false,
   ~11x over-report - 16 lanes summed predicts 960/s against a measured 87/s);
   the "only trustworthy success gate", which over-reports 3-5.5x on its own and
   needs the non-filling companion #96 already named; and the poller-zero
   remedy, which is circular. Adds the BUILD GATED (measurement layer) advisory
   and marks the quick-win/Difficulty-5 score as no longer trustworthy.
   ADR 0074 B14 and required-change 13 are closed out to match.

   Deviations from bd969b0e, each deliberate: it used a leading ⛔ banner, which
   the backlog checker reads as a CLOSED status against #96's OPEN 🔢 banner and
   fails "item contradicts itself" - ⚠️ is the sanctioned non-status advisory
   glyph, per the ⛔+⚠️ stacking that landed in #92. Its "11 open decisions"
   count is unsourced (the Amendment lists 14) and is dropped rather than
   restated. Two of its quotations were inexact and are corrected against the
   file. "zero code" became "only TOML comments, zero code" - the grep returns
   10 TOML hits and no Python.

2. BACKLOG #171, #131 and #127 carried only their pre-build demand-gate banners
   although work has shipped. Each gets an AMENDED banner rather than a close,
   because verification refuted a full close in all three cases: #171's API half
   is built but its console half is dead code (app.js registers the handlers, no
   page emits the attributes, and the /ui routes do not exist); #131 built the
   connection flag and the Flagged-only filter, but "every configuration object"
   is a scope fork ADR 0007 ratified deliberately; #127 built Basic, and Digest
   for http destinations, while NTLM and Windows are refused at construction and
   recorded as deferred - so this is explicitly not four-scheme parity.

3. The demand-gate tracker claimed 11 lanes "Not started" that have all shipped.
   Each lane was verified individually by ADR status plus code symbol; the
   public history is snapshot-collapsed across the 2026-07-17..24 window, so no
   merge SHA can be cited for any of them.

Verified: tests/test_backlog_status_check.py (15 passed, including
test_the_real_backlog_satisfies_the_invariant, which the ⛔ form fails), plus
the ledger, runbook, doc-ref and doc-drift suites - 100 passed, 89 skipped.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
wshallwshall added a commit that referenced this pull request Aug 2, 2026
…ide (#144)

Follow-on to #143, which merged before this measurement existed. Adds the one
argument #340 was missing, and it is a better one than the cycle-count case.

Measured 2026-08-02T13:41Z, re-derived here rather than relayed:

  open=14  armed=9  armed_and_inert=6  armed_and_CLEAN=0
  #142 BEHIND  #139 BEHIND  #128 BEHIND  #101 BEHIND  #96 BEHIND  #71 DIRTY

Two-thirds of the armed PRs in this repo cannot land, and NOT ONE armed PR was
CLEAN. #71 is armed and DIRTY, so it can never land at all.

Why this belongs in the item: everything else in #340 is an efficiency argument,
and an efficiency argument has a "then be patient" answer. This one does not.
Every session here reads autoMergeRequest != null as "this will land" -- I said
exactly that about my own PR an hour before measuring this -- when for six of nine
it means "this waits until a human runs gh pr update-branch", with nothing
reporting the difference. That is the ADR 0158 defect class (a green signal that
means nothing) caught live rather than in retrospect.

ADR 0158 is referenced by number, not linked: it is not on main yet.

The measurement came out of the sandbox-codec session's queue claim, checked by the
announce-hook session, and the connection to 0158's class is sandbox-codec's. Both
routed it to me rather than writing it, since #340 is claimed here. Re-derived
independently before writing; their figures and mine agree exactly.
@wshallwshall
wshallwshall merged commit c5ae1f8 into main Aug 3, 2026
32 checks passed
@wshallwshall
wshallwshall deleted the harness-batch-ab-ports branch August 3, 2026 16:43
wshallwshall added a commit that referenced this pull request Aug 3, 2026
…est plan (#161)

* fix(docs): unbreak main — six new mirror-prose hits from the master test plan

main has been red since 4ea1501 (#60): test_cutover_slug_rot's prose ratchet
reports 58 hits against a ceiling of 54, so every PR current with main inherits
the failure, #160 among them.

Measured rather than inferred. The last green commit 8e6e7fa sits at 52 hits
with 2 slots of slack, so #60 added SIX — not the four the ceiling arithmetic
(58-54) suggests. c5ae1f8 (#96) added none. All six are in the new
master-test-plan chapters, and the assertion's "New or changed" list is a tail
slice of the sorted hits, not a diff, so it does not attribute them.

Five are false positives in senses unrelated to the retired public mirror:
three are `the mirror` matching INSIDE "the mirrorED" (the webview functions
mirroring stepsModel, ADR 0108), one is a pip wheelhouse for air-gapped
installs, and one is a detector spec quoting the very string it wants deleted —
the _SELF collision this module already documents, reappearing in another file.

The sixth is genuine rot. "the mirror-only gap" reincarnates the nightly cost
lever that f7a337a (#25) swept from three files, leaving an explicit "do not
re-add it" marker; windows-service-smoke is gated
`github.repository == 'MEFORORG/MessageFoundry'`, i.e. ON this repo, which is
the source. The line now names that gate instead of a mirror.

The regex is deliberately NOT changed. Adding \b to `the mirror` clears three of
the six and measures safe on the baseline (52 -> 52, suppressing only the three
"mirrorED" lines) — but `private repo` -> "reporting" was a CROSS-LEMMA
collision where \b removed noise, whereas `the mirror` -> "the mirrored" is the
same lemma inflected, where it removes signal. Measured: \b blinds all five of
"the mirrored workflows are rewritten by publish.ps1 on every push", "the
mirroring job publishes ... nightly", "the mirrors are refreshed from the source
repo each night", "CI runs on the source; the mirrored copy is read-only" and
"the mirrored README is regenerated at release time", none of which carries a
retrospective trigger — while both KEEP-class probes (the "mirror image of"
idiom, a historical narrative) survive it untouched. Those verb forms are how a
present-tense mirror claim actually gets written, so the boundary would
preferentially blind the FIX class. Filing down the detector to clear a red is
the failure this module exists to name.

_PROSE_CEILING is unchanged at 54. The count returns to 52 — exactly the
last-green posture — so no credit was earned to spend, and lowering it to 52
would set slack 0 as a new, stricter policy unrelated to this fix.

Verified: 4/4 in the module; 52 hits / ceiling 54 / slack 2 across 1503 files;
ruff check + ruff format clean; full suite 10222 passed, 836 skipped, with the
sole failure test_gate_installed_parity — a local-machine test that skips on CI,
whose installed-vs-source delta is one redacted comment line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore(ide): bump fast-uri/brace-expansion/undici to patched versions (3 HIGH advisories)

npm-audit (a required gate) began failing on every fresh CI run: three HIGH
advisories now cover the ide/ transitive deps at their locked versions —
fast-uri (GHSA-7p8r-x3mc-p8w7), undici (GHSA-8xcm-r25x-g524 + siblings), and
brace-expansion (GHSA-rgw5-rvv9-x895). Each has an in-major patched release, so
this is a lockfile bump, not a breaking major:
  fast-uri         ^3.1.3 -> ^3.1.5
  brace-expansion  ^5.0.8 -> ^5.0.9
  undici           (new override) ^7.29.0
npm audit --package-lock-only now reports 0 vulnerabilities. Folded onto the
main-unblock branch so one merge clears both the slug-rot test-red and npm-audit
(the two were a circular deadlock).

* fix(deps): bump cryptography 49->50 + pyOpenSSL 26.3->26.4 (CVE-2026-69247)

pip-audit (a required gate) flagged cryptography 49.0.0 for CVE-2026-69247 — a
PKCS7 decrypt Bleichenbacher/timing oracle, fixed in 50.0.0. The straight bump
was gated by pyOpenSSL's cryptography<50 cap; pyOpenSSL 26.4.0 lifts it to <51,
so both move together, within the existing pyproject floors (cryptography>=48.0.1,
pyOpenSSL>=26.3.0) — no constraint change. Re-locked via uv and re-exported all
six DEP-1 artifacts (uv lock --check clean); the stale pyproject <50 note -> <51.

The advisory's primary oracle is already mitigated on the OpenSSL 3.2+ wheels
this project ships; this clears the flagged version regardless.

Third and final red gate on the main-unblock branch: with the slug-rot ratchet
and the ide npm advisories, one merge now clears all three.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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