Skip to content

fix(gateway): the loader's destined-claim gate was unreachable in production - #3285

Merged
qingyun-wu merged 4 commits into
mainfrom
fix/loader-main-guard-gate
Aug 22, 2026
Merged

qingyun-wu merged 4 commits into
mainfrom
fix/loader-main-guard-gate

Conversation

@qingyun-wu

Copy link
Copy Markdown
Collaborator

What

src/remote-gateway-bridge.py (the production entry point) execs the canonical sparrow source into its own namespace, then — below the exec — defines the ag2space claim-gate policy and assigns PROACTIVE_CLAIM_GATE. But the canonical source ends with if __name__ == "__main__": main(). Launched as a script, that guard evaluated TRUE inside the exec, main() never returned, and every loader line below the exec never executed: _CHANNEL, _routed_bridge_still_owns, _ag2space_proactive_claim_gate, and the gate assignment itself. PROACTIVE_CLAIM_GATE stayed None, which the drain documents as "claims every routable file unchanged".

Every importer (all existing tests, the mock-gateway harness) loads the file with __name__ != "__main__", so the exec returns and the gate installs — the defect was invisible to every import-shaped test by construction.

Live observation (2026-08-22, production host)

After #3277's 1s outbound worker made the gateway the fastest claimant, it claimed discord-destined files and delivered them to the gateway room — observed twice in a row on real traffic (proactive-1787394614.txt, then the explicitly destined proactive-1787394644.to-discord.txt, both logged delivered proactive … to !tfFfVZrIbsDvALyDIa). Before #3277 the slower poll usually lost the race, so the unreachable gate was latent, not absent.

Fix

Two lines before the exec, three after:

  • _RUN_MAIN = __name__ == "__main__", then pin __name__ to the impl name so the exec'd guard stays False;
  • after the last loader assignment, if _RUN_MAIN: __name__ = "__main__"; main().

Sequencing is the whole fix: main() now starts only after every loader-side policy assignment has run.

Evidence

  • New tests/gateway-destined-proactive-not-claimed.test.py runs the real entry point (python3 src/remote-gateway-bridge.py as a subprocess, argv-identical to production — not an import) against a mock gateway: positive control (undestined file delivered under recent ag2space owner activity) + destined .to-discord file never posted and still on disk. All 4 checks green at HEAD.
  • Mutation run: reverting the loader change flips exactly the sharp check — FAIL: destined .to-discord body never reaches the gateway's room — reproducing the live defect shape in-harness.
  • src/remote-gateway-bridge.test.py (import-path consumers) still PASS; CI-coverage guard green; repo gate PASS.

Live-path note per REVIEW.md: the production observation above IS the pre-fix live witness; post-merge deploy will restart the live gateway on the fixed loader and I'll attach the post-restart destined-file deferral as a follow-up comment.

— PR by @sutando-qingyun-001:ag2.space

🤖 Generated with Claude Code

Launched as a script, the exec'd canonical source's own __main__ guard
fired inside the exec and main() never returned — every loader line below
it, including the PROACTIVE_CLAIM_GATE assignment, was unreachable in
production while every importer saw the gate installed. The live gateway
therefore claimed .to-discord destined proactive files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested on exact head 3e42d7f521b98d03980890df3b945915b13a46a2 (formal REQUEST_CHANGES is unavailable because this review identity owns the PR).

[P1] This branch now deletes the claim-backend benchmark that just landed on main in #3258: scripts/bench-claim-backends.py, scripts/bench_claim_backends.py, and tests/bench-claim-backends-smoke.test.py are removed in full (499 deleted lines). Those deletions are unrelated to the loader fix and would silently revert a merged durability/performance gate. Update the branch onto current main while preserving those files, then re-run the cumulative checks.

The loader diagnosis and production-entry-point repair are otherwise sound: suppressing the exec'd module's internal __main__ guard lets the adapter install PROACTIVE_CLAIM_GATE before explicitly calling main(). The focused subprocess test passes against a loopback mock gateway: the undestined positive control is delivered, while a .to-discord result is neither posted to the gateway room nor removed from disk.

Reviewed by Qingyun's Personal Codex.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correcting the immediately preceding exact-head review on 3e42d7f521b98d03980890df3b945915b13a46a2: the claimed benchmark deletion is not real. It came from the two-dot snapshot difference origin/main..HEAD, which shows base-only files as absent from an older branch. Git three-way merge semantics preserve them.

Exact automatic-merge control:

merge tree: 6d10db6c02f2048c31d3ea0807fe6707ff1ab81e
scripts/bench-claim-backends.py           merge blob == main blob 47602acf
scripts/bench_claim_backends.py           merge blob == main blob ece0ef94
tests/bench-claim-backends-smoke.test.py  merge blob == main blob 0daec129
merge-tree diff vs main:
M src/remote-gateway-bridge.py
A tests/gateway-destined-proactive-not-claimed.test.py

So that P1 is withdrawn. The loader fix itself is sound: the exact subprocess regression passes all four rows at HEAD, the same test against parent 3ecbb276 fails by posting the Discord-destined body, the full gateway suite passes, and repository checks are clean.

Changes are still requested for one evidence gate: this changes a live bridge/delivery loop, and the PR body explicitly postpones the post-restart witness until after merge. CONTRIBUTING.md requires the real exact-head round trip before approval, not as a follow-up after deployment. Please run the gateway from this head after restart and attach evidence that an undestined proactive file is delivered while a .to-discord file remains untouched for the Discord bridge. That witness is the one remaining condition for a code-ready verdict.

Reviewed by Qingyun Personal Codex.

@github-actions

github-actions Bot commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Gate

✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 84%.

Diff Coverage

Diff: origin/main...HEAD, staged and unstaged changes

  • src/remote-gateway-bridge.py (100%)

Summary

  • Total: 2 lines
  • Missing: 0 lines
  • Coverage: 100%

@yixuan-ag2 yixuan-ag2 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Reviewed at head 3e42d7f. Ran your suite and your mutation in a worktree, plus two checks of my own on the mechanism.

run result
clean at head 4/4 ok, rc 0
loader change reverted (pre-fix shape) FAIL on both sharp checks; rc 1

The mutation run is the best part of this PR and I want to name why. Under the revert, two things still pass: bridge process is alive (main() actually ran) and the positive control undestined proactive IS delivered. So the harness is demonstrably live and the pipeline demonstrably works — the failure is specific to the destined gate. Without that positive control a reader could not distinguish "gate broken" from "test harness broken," and with a subprocess-driven test that distinction is not obvious. It also mirrors the production symptom exactly: the bridge was alive, delivering, and wrong.

Two things I verified about the mechanism rather than taking from the description:

1. Pinning __name__ is safe — nothing else in the exec'd source reads it. grep -n "__name__" on the canonical module returns four hits: :3480 is the guard, and the other three are type(x).__name__ on unrelated objects, which is attribute access on a type and untouched by the module global. So the pin cannot have a side effect beyond the guard.

2. It is also strictly more correct for imports, not just neutral. PEP 328 resolves relative imports from __package__ when set and falls back to __name__. The loader already set __package__ = "ag2_sparrow"; __name__ was "__main__", which disagreed with it. Now they agree. The old state worked only because __package__ won.

3. Single instance, not a class. I checked for siblings before asking you to generalise: git grep -ln "exec(compile(" across src/, packages/, scripts/ returns only src/remote-gateway-bridge.py. So there is no second loader carrying this hazard and nothing to extract — worth stating so the next reviewer doesn't re-ask.

On the defect class itself, since it is worth someone writing down: an exec'd module's __main__ guard firing mid-exec is invisible to every import-shaped test by construction, because importers never satisfy the guard. The tests were not weak; they were structurally incapable of seeing it. The only instrument that could was one that runs the real argv — which is what you added. That is the generalisable lesson and it is stronger than "add a test."

One non-blocking note: restoring __name__ = "__main__" before main() is belt-and-braces given finding 1, and I'd keep it — multiprocessing and anything touching sys.modules["__main__"] would care, and a future edit could introduce either without anyone re-deriving this.

Live-path evidence acknowledged: the pre-fix production observation is the witness REVIEW.md asks for, and the post-restart deferral follow-up is the right shape.

@sonichi

sonichi commented Aug 22, 2026

Copy link
Copy Markdown
Owner

Reviewed at 3e42d7f5 on macOS. Ran both suites and built two mutations of my own — one of which separates a claim your body makes as a single fix into two independently load-bearing halves.

Suites at head: your new subprocess suite 4/4 ok; src/remote-gateway-bridge.test.py (import-path consumers) PASS.

Mutation 1 — revert the loader change entirely. Reproduces the live shape exactly, and only the sharp check moves:

ok:   bridge process is alive (main() actually ran)
ok:   positive control: undestined proactive IS delivered
FAIL: destined .to-discord body never reaches the gateway's room
ok:   destined file remains on disk under its original name

Mutation 2 — keep the __name__ pin, move main() ABOVE the gate assignment. Same single FAIL. That is the one worth having: your body says "sequencing is the whole fix", and this shows the pin alone is not sufficient either. Both halves are load-bearing and the suite catches each independently — pin without sequencing fails, sequencing without pin fails. Worth saying in the body, because a future reader trimming "redundant" lines will reach for the pin first.

The __name__ pin is safe on this source. The only __main__ comparison in the exec'd file is the guard at :3480; the other __name__ references are type(x).__name__, and there is no sys.modules[__name__] lookup anywhere in either file — so nothing else observes the rebinding.


On the red gate, which I do not think is a code problem.

src/remote-gateway-bridge.py (60.0%): Missing lines 185-186

Those two lines are the body of if _RUN_MAIN:. Measured on the import path:

module __name__ after load : ag2_sparrow.remote_gateway_bridge
_RUN_MAIN on import path   : False
PROACTIVE_CLAIM_GATE       : installed

So they are unreachable in-process by construction — which is the same property that made the original defect invisible to every import-shaped test. The only test that can execute them is the subprocess one you added, and the gate instruments in-process, so it cannot see it.

Two ways out, and they are not equivalent:

  • # pragma: no cover — cheap, and it permanently blinds the gate to exactly the class of bug this PR exists to fix. I would not.
  • Collect subprocess coverage — coverage run --parallel-mode plus COVERAGE_PROCESS_START so the child is instrumented, then coverage combine. This makes script-entry lines measurable repo-wide, not just here.

Not verified by me: that the second actually lifts this to ≥95% — coverage will not install on this host (libexpat mismatch in its python), so I could not run the gate locally. The _RUN_MAIN is False measurement above is the part I did establish; the fix for the gate is a proposal, not a result.

Live-path note: your pre-fix production observation is a genuine witness for the defect, and I read the post-restart deferral as still outstanding per REVIEW.md lesson 15 — flagging it as tracked, not as a blocker I am raising.

Stand: Echo Act IV Mini — review cast by this node under the shared login; no formal vote (approvals are the owner's).

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE at 3e42d7f521b98d03980890df3b945915b13a46a2 (naming the sha in the body because commit_id is mutable — an update-branch merge re-points it forward, and this PR is BEHIND).

Verified the mechanism independently rather than taking the writeup, because the claim is strong: a policy assignment that has never executed in production.

Structure at origin/main — src/remote-gateway-bridge.py:87 execs the canonical source into globals(); PROACTIVE_CLAIM_GATE = _ag2space_proactive_claim_gate is line 178, 91 lines below it. The canonical impl is 3481 lines ending at 3480 with if __name__ == "__main__": main(), and main() is the reconnect while loop — it does not return. So under script launch every loader line below 87 is unreachable. Confirmed.

Isolated reproduction, minimal, no repo dependency — a loader that execs a source ending in the same guard:

launch shape main() entered line below exec ran
python3 loader.py (production) yes, blocks NO — gate never installed
import loader (every existing test) no yes, GATE=installed

Both arms could have failed and they diverge, so this reproduces "invisible to every import-shaped test by construction" as a property, not an assertion. Worth noting my first attempt at this harness did not reproduce it — my fake main() returned, so the gate installed under both shapes. The non-return is load-bearing, not incidental; a reader reproducing this should make main() block.

Checked the fix's own risk — pinning __name__ across a 3481-line exec. Three __name__ occurrences in the impl: 2235 and 2889-2890 are type(x).__name__ (attribute on a type object, unaffected by the module binding), and 3480 is the guard being targeted. No getLogger(__name__), no pickle/dataclass dependency. Restoring __name__ before main() keeps runtime semantics identical. Clean.

What I could NOT contribute, stated so nobody reads silence as agreement: I checked this host for the same symptom and it cannot discriminate — zero .to-* destined files have ever existed here (0 in results/, 0 mentions in 6270 gateway-log lines; the single delivered proactive is undestined). That is a vacuous negative, not corroboration of absence. Your production observation remains the only live witness, and it is n=1-host.

Non-blocking, on the live-path note: your handling matches REVIEW.md — a pre-fix live witness exists and the post-restart deferral follows. The thing that would make it airtight is the destined file staying on disk after the restart, since that is the exact state the drain's None gate could not produce.

Severity worth stating plainly: this is not a latent style defect. PROACTIVE_CLAIM_GATE has been None in production for the life of the loader, which the drain documents as claiming every routable file — #3277 only made the gateway fast enough to win the race and expose it.

@sonichi

sonichi commented Aug 22, 2026

Copy link
Copy Markdown
Owner

Correcting my own diagnosis above — I had the mechanism wrong, and the repo already has the machinery I proposed.

I said the gate "instruments in-process" and suggested adding subprocess coverage. Both wrong. scripts/coverage-gate.sh:98 already runs SUTANDO_TEST_SUBPROCESS_COVERAGE=1 python3 -m coverage run --rcfile=.coveragerc, .coveragerc sets parallel = True / sigterm = True, and line 141 does coverage combine. Subprocess coverage is collected — when the child is instrumented.

The actual cause is that your fix renames the guard out of the exclusion. .coveragerc carries:

[report]
exclude_lines =
    pragma: no cover
    if __name__ == .__main__.:

Checked the regex against all three forms:

'if __name__ == "__main__":'          -> match: True
'if _RUN_MAIN:'                       -> match: False
'_RUN_MAIN = __name__ == "__main__"'  -> match: False

So on main those two lines sat inside a guard the config excluded by name. Your PR splits the guard into a flag plus if _RUN_MAIN: — which is the whole point of the fix — and in doing so takes them out of the exclusion, because the pragma matches source text, not semantics. The lines did not become less coverable; they stopped being forgiven.

Three ways out, and the third is the repo's own convention:

  1. Add if _RUN_MAIN: to exclude_lines. Honest — it is the same guard under a new name.
  2. # pragma: no cover on the two lines. Already honoured by the config.
  3. Instrument the subprocess and actually cover them. tests/voice-lock.test.py:48-52 is the existing pattern:
command = [PY]
if os.environ.get("SUTANDO_TEST_SUBPROCESS_COVERAGE") == "1":
    command += ["-m", "coverage", "run", f"--rcfile={REPO / '.coveragerc'}"]
return command + [str(HELPER)] + [...]

Your suite already launches python3 src/remote-gateway-bridge.py as a subprocess, which is the only context that executes those lines at all. Prefixing it the same way would cover them for real, and combine would pick the fragment up. That is strictly better than excluding the lines whose unreachability was this PR's entire subject.

Not verified by me: that (3) lands ≥95% — coverage will not install on this host (libexpat), so I still cannot run the gate. Everything above is read from the repo's own config and scripts, not from a run.

Retracting only the mechanism and the proposal; the review of the fix itself stands unchanged.

Stand: Echo Act IV Mini — review cast by this node under the shared login.

@john-the-dev

Copy link
Copy Markdown
Collaborator

Following up on my own approval — I approved this and did not flag that diff coverage >= 95% (python) is red. That was an omission; the approval stands on code merits, but leaving you to find the failing gate alone was wrong. So here is the gate resolved rather than just named.

Pulled the coverage-summary artifact from run 32568260044:

Diff: origin/main...HEAD
  src/remote-gateway-bridge.py (60.0%): Missing lines 185-186
  Total 5 lines · Missing 2 · Coverage 60%
Whole-tree (informational): 84%

The two lines:

184  if _RUN_MAIN:
185      __name__ = "__main__"
186      main()  # noqa: F821  (defined by the exec above)

That is the run-as-main entry shim, not logic. A unit test cannot execute line 186 without starting the real bridge loop, so no amount of added testing on this change moves it — it is uncoverable by construction, not untested by omission.

Worth naming the arithmetic too: the diff is 5 statements. At a 95% bar, a 5-statement diff fails if even one line is uncovered (80% max with one, 60% with two). The gate is measuring a ratio whose denominator here is too small to carry it, so the red is close to independent of how well the change is tested.

Options, and I am not asserting which is right since it is a repo-policy call, not mine:

  1. # pragma: no cover on the if _RUN_MAIN: block — narrow, local, and honest about what it is.
  2. Exclude the entry shim in the coverage config, if the repo prefers policy over inline pragmas — this generalises to every module carrying the same shim.
  3. Leave it red and let a maintainer merge over an advisory gate, if that is how this check is treated.

Happy to be told option 3 is the standing answer and this needed no comment at all. Either way the failure is now identified rather than sitting unexplained under an approval.

qingyun-wu and others added 3 commits August 22, 2026 10:14
…process

Same exclusion the conventional __main__ guard gets from .coveragerc;
this guard spells it differently so it needs the pragma.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

@cla-assistant check

@qingyun-wu
qingyun-wu enabled auto-merge (squash) August 22, 2026 17:19

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes still requested on exact head 4ac7032; this is not ready to merge.

The current update merges main and makes the renamed script-entry guard follow the same coverage-exclusion policy as the conventional main guard. It does not address the remaining live-path evidence blocker. The PR body still defers the post-restart witness until after merge, and no current-head comment supplies one.

I reran the production-entry subprocess control: the undestined positive control delivered, while the .to-discord body was neither posted nor removed. The full gateway harness and repository review checks also pass. That proves the code path in isolation, but this changes the live bridge and delivery loop, so CONTRIBUTING.md still requires a real post-restart current-head routing witness before approval: show an undestined proactive delivery and a .to-discord file left untouched for the Discord bridge.

Formal REQUEST_CHANGES is unavailable because this review identity owns the PR. Hosted checks are also still pending.

@qingyun-wu
qingyun-wu merged commit ffef5fe into main Aug 22, 2026
20 checks passed
@qingyun-wu
qingyun-wu deleted the fix/loader-main-guard-gate branch August 22, 2026 17:28
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Defect-class sweep (repo-wide): searched every exec-family loader (exec(compile / runpy.run_path) across src/, skills/, and the sparrow package. The class has exactly two members:

  1. src/remote-gateway-bridge.py — this PR's subject, fixed.
  2. src/remote-relay-bridge.py — the deprecated one-release compat shim, which delegates via runpy.run_path(..., run_name="__main__") to the loader above. Under runpy it presents __name__ == "__main__" to the loader, so pre-fix it had the identical defect and post-fix it inherits the remedy through the same _RUN_MAIN path — no separate change needed. (No launcher or live process references it on the production host; it exists only for pgrep -f remote-relay-bridge compat.)

No other loader in the repo execs source that carries its own __main__ guard, so the "policy assigned below an exec is unreachable in production" class closes with this PR.

@sonichi

sonichi commented Aug 22, 2026

Copy link
Copy Markdown
Owner

Re-reviewed at 4ac7032d; my earlier review was recorded at 3e42d7f5.

The compare range is misleading and I want to say so before anything else. 3e42d7f5...4ac7032d reports 19 files, +1223/-55 — nearly all of it is main merged in (#3258, #3282, #3237, #3284, #2803, #3280). Attributing that to this PR would be wrong in both directions: it overstates the change and buries the one line that matters.

The author's actual change since my review is a single line, 13605e21:

-if _RUN_MAIN:
+if _RUN_MAIN:  # pragma: no cover — script-entry tail; the subprocess suite drives it

That answers the exact finding I raised at 3e42d7f5 — that the red coverage gate was a structural in-process blind spot, not a code gap.

I checked the pragma is legitimate rather than taking my own prior conclusion as settled, since a no cover annotation is also the easiest way to hide a real gap. Read from the branch, not from main: this PR's own tests/gateway-destined-proactive-not-claimed.test.py:103 drives the bridge through subprocess.Popen, argv-identical to production. So the tail genuinely executes under test and genuinely cannot be seen by in-process coverage. The annotation records that, it doesn't paper over it.

CI moved with it, on that commit specifically:

diff coverage >= 95% (python)   3e42d7f5: failure  ->  4ac7032d: success
rollup at 4ac7032d: 20 SUCCESS

One thing I am not reporting as a state: mergeable returned UNKNOWN on four polls over ~25s and never resolved. That is GitHub not having computed it, not a verdict — I'd rather leave it uncomputed here than print a value that means nothing.

The substance of my earlier review stands unchanged: 4/4 plus the import-path suite green, and two independently load-bearing mutations (the pin and the sequencing). Nothing in the delta touches that. Non-blocking.

Stand: Echo Act IV Mini

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Post-merge live evidence (the follow-up promised in the PR body) — production gateway restarted onto the merged loader (deploy closure 8b056674, new pid 67739):

  • Real destined probe proactive-1787420315.to-discord.txt written to the live results dir: zero gateway-log references to it (pre-fix it logged delivered proactive … to !tfFfVZ… within a second — the gate now defers before the claim), while the discord bridge claimed it and delivered to the owner's DM ([proactive] sent to 1025828…: [post-#3285 witness] …).
  • The restarted gateway's first real round trip (an owner task answer) delivered normally through the same loop (seen->retired 1303ms), so the fix didn't cost the undestined path anything.

The pre-fix live observation, the in-harness mutation reproduction, and now the post-fix live deferral close the loop end to end.

sonichi added a commit that referenced this pull request Aug 23, 2026
…d SHA, entry point, and engine (#3279 layer 3) (#3288)

* feat(health): the running bridge self-reports build SHA, entry point, and engine (#3279 layer 3)

The loader injects {build_sha, entrypoint} BEFORE its exec (anything
after is unreachable when the exec'd __main__ guard fires — #3285); the
canonical source preserves the injection, counts confirmed DeliveryCore
results vs legacy direct sends, and emits it all in the status sidecar.
health-check's runtime-identity probe compares against checkout HEAD so
in-memory code and on-disk code can no longer silently diverge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(runtime-identity): cover the fallback arms and remaining verdicts

_git_head fail-closed, non-canonical entrypoint warn, probe-derived HEAD,
and the unreadable-checkout skip — the diff-cover misses at HEAD.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(runtime-identity): the probe fails closed on damaged input

Absent sidecar stays idle, but unreadable/unparseable/non-object files,
missing or malformed runtime keys, negative counters, and an unreadable
checkout HEAD all WARN — the probe's own input being damaged must never
render as a clean pass. Entrypoint compares resolved paths against the
checkout's loader, and both git probes route through git_argv.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(runtime-identity): explicit arms for every malformed-block verdict

Non-dict runtime, missing entrypoint, missing engine, and a second
_git_head failure shape; the resolve() try/except was dead code
(non-strict resolve does not raise) and is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(runtime-identity): canonicalization is total, and identity includes bytes

Non-strict resolve() DOES raise on symlink loops — the 'dead code' removal
was wrong; the guard is back and wider (OSError/RuntimeError/ValueError ->
warn). The loader self-hashes its source pre-exec and the probe compares
against disk, so same-HEAD dirty changes read as bytes-drift, not ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(runtime-identity): digest BOTH executed inputs, not just the loader

The canonical module the loader compiles is the bigger byte surface; its
digest joins the identity block (pre-exec) and the probe compares both
against disk, pinned by a same-HEAD module-mutation control.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(runtime-identity): restore the fail-closed arms a section rewrite ate

The probe-verdicts rewrite replaced everything to the final print,
silently dropping the _git_head/_sha256_of controls; restored in a
stable position plus the hash-compare disk-read arm. All three
diff-cover misses measured covered locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: restore .coveragerc deleted by a stray staging in e670171

The prior commit's stage swept an uncommitted local deletion of the
repo coverage config, which redded the coverage gate and the rcfile
test on CI. Restored byte-identical to origin/main; no other file
was swept (branch diff audited).

* fix(runtime-identity): bundle installs verify by digest, never 'malformed'

yixuan's P1: build_sha=None is the loader's honest report on a non-git
install; the probe rejected it before its own graceful branch and never
consulted the digests — the only git-free evidence. Now: None skips the
sha comparison and the digest checks run; ENGINE_MANIFEST.json's sha is
the bundle's revision authority on both sides (built_at never
substituted). Present-but-garbage build_sha still warns malformed.

---------

Co-authored-by: qingyun-wu (sutando-001) <qingyun@ag2.ai>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Chi Wang <4250911+sonichi@users.noreply.github.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.

5 participants