Repository navigation
fix(health-check): probe the interpreter each bridge RUNS on, not the one that would launch it - #3597
Conversation
… one that would launch it The secret-scanner probe reported `ok — detect-secrets present in all 3 bridge interpreter(s)` while the ag2.space gateway bridge ran on the app's bundled python, which has no `detect_secrets`. Inbound text was scanned with the 17 provider detectors and the entropy checks off, and health-check printed green over it. Issue #3580. Before / after on a live host, gateway bridge pid 76550 up, same machine: origin/main OK | detect-secrets present in all 3 bridge interpreter(s) this branch WARN | DEGRADED in 1/4 bridge interpreter(s) | (…/engine/runtime/python/bin/python3): detect-secrets | missing, so its provider detectors and entropy checks | are off … The population was never the bug — the question was. Adding the gateway to `_VAULT_SCANNER_BRIDGES` alone does NOT fix this: `_bridge_interpreter` returns `sys.executable` for a bridge with no required import, and that interpreter HAS the module, so the probe would print `present in all 4` and read as MORE authoritative while the gap persisted. So the probe now asks the process table which interpreter each bridge is actually running on, and falls back to `_bridge_interpreter` when the bridge is down. Three things the naive implementation gets wrong, each pinned by a control in the test — I hit all three while writing it: - the bundled interpreter's path contains spaces, so shlex/argv[0] truncates it mid-path ("/Users/x/Application") - the script arg carries a `src/` prefix, so cutting at the bare script name leaves a trailing slash and an empty basename - the probing shell's own argv contains the script name — the self-match that `_proc_argv`'s docstring in this same module already warns about Both probe polarities are pinned too, so neither verdict is free: stubbing every bridge onto an interpreter that has the module asserts `ok`, and onto one that does not asserts `warn`. Scope: this makes the probe honest. It does not install anything. The durable half — vendoring detect-secrets into the bundle or pointing the bridge at an interpreter outside the engine tree — is a separate call, and the tree is replaced on app update (this interpreter's mtime is today's update), which is why the 2026-08-18 manual install did not survive. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…l bridge name CI's lint-hermetic-bridge-tests caught this test reading the developer's real per-user config: health-check resolves workspace/channel config at import, so `exec_module` did it against the host. Now `CLAUDE_CONFIG_DIR` points at a temp dir set at module level before the load, which is the documented fix. The gate also fired on its own prefilter, `(discord|slack|telegram)-bridge\.py`. This test does not import a bridge — the string appeared as test DATA in the "a bridge that is not running yields None" control, where the name is arbitrary: any script absent from the ps output exercises that path. Renamed it to `not-running-bridge.py`, which is a more accurate description of what the control actually asserts. The isolation above is kept regardless, so the test is hermetic whether or not the prefilter would have fired. Not touched: SUTANDO_WORKSPACE, which I had also set — it is no longer honored as of v0.8 and only emits a deprecation warning, so setting it isolated nothing. Local: lint-hermetic-bridge-tests ok (112 scanned, 46 grandfathered, 1 mitigated). Test still discriminates — non-zero against origin/main's health-check.py, 0 against this branch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Coverage Gate✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 87%. Diff CoverageDiff: origin/main...HEAD, staged and unstaged changes
Summary
|
qingyun-wu
left a comment
There was a problem hiding this comment.
Changes requested at exact head 9cfd2784932acd221d7bb916f51d3ce93493c862.
[P1] src/health-check.py:678-700 loses the argv boundary when the script argument path also contains spaces. The new parser finds the last space before the script basename; for a supported row such as:
/Users/x/Application Support/app/runtime/python/bin/python3 /Users/x/My Sutando/src/remote-gateway-bridge.py
that space is inside the script path, so _live_bridge_interpreter() returns None. The caller then falls back to _bridge_interpreter() and recreates the exact would-launch false green this PR is meant to remove. Exact-head composed control:
result: {status: ok, detail: detect-secrets present in all 1 bridge interpreter(s)}
probed: [/healthy/python3]
live parsed: None
The running degraded interpreter was never probed. The added test covers spaces in the interpreter path but keeps the script argument as src/remote-gateway-bridge.py, so it cannot detect this case. Please recover the executable from the matched PID (or otherwise use an argv-boundary-preserving source) and add a polarity control with spaces in both paths; the composed check must warn and probe the live interpreter.
Worst-case disruption is a security-relevant standing false green for any checkout or bundled script path containing spaces: provider and entropy detectors can remain off while health-check reports healthy. The 19 focused scanner/process assertions, Python compilation, cumulative diff hygiene, repository review checks, all hosted CI, diff coverage, and CLA pass, so this is the sole blocker I found.
Not ready to merge until the live interpreter is measured for spaced script paths.
…slicing argv Addresses qingyun-wu's P1 on #3597, which is correct and reproduces exactly. Round 1 sliced argv at the last space before the script name. When the SCRIPT path also contains spaces, that boundary falls inside the script path: argv: /Users/x/App Support/rt/python/bin/python3 /Users/x/My Sutando/src/remote-gateway-bridge.py round1 parse -> '/Users/x/App Support/rt/python/bin/python3 /Users/x/My' basename 'My' -> rejected -> None -> caller falls back to _bridge_interpreter() -> FALSE GREEN So the defect this PR exists to remove came straight back for any checkout or bundled script path containing spaces. No argv boundary is recoverable when both the interpreter and the script path may contain spaces, so argv is no longer parsed at all: `_proc_executable(pid)` reads the executable via `ps -o comm=`, which is a single field, so a spaced path survives it. The PID comes from ps field 0, which is unambiguous. `/bin/ps -o comm= -p` is already the idiom used elsewhere in this module. Verified on this host, real ps and real PID: ps -o comm= -p 76550 /Users/…/space.ag2.app/engine/runtime/python/bin/python3 probe -> WARN | DEGRADED in 1/4 bridge interpreter(s) (…/runtime/python/bin/python3) Test additions, per the review: - spaces in BOTH interpreter and script paths resolve to the interpreter - the COMPOSED check driven through _ps_snapshot + the parser (not a stub of the helper) on that same spaced-both row must WARN and must NAME the live interpreter it probed - the same row with a healthy interpreter must be ok, so the warn is not free - executable unresolvable -> None `exe_of` is injectable so synthetic PIDs resolve in tests, matching the existing injectable-probe convention in this module (_proxy_config_dir_from_process). Local gates: review-checks PASS (hardcoded-paths + root-artifacts + prose-cap), lint-hermetic-bridge-tests ok, test passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Confirmed and fixed at Reproduced your case against the round-1 parser before changing anything: The fix takes your first suggestionNo argv boundary is recoverable when both the interpreter and the script path may contain spaces — any delimiter I pick is ambiguous. So argv is no longer parsed at all. On this host, real ps and real PID: The polarity control you asked forAdded, and deliberately not a stub of
One note on the earlier round's coverage, since you flagged it: my round-1 test varied only the interpreter path and kept the script arg as
Local gates before push: |
TustinOC
left a comment
There was a problem hiding this comment.
Reviewed at 9cfd2784. The thesis is right and I reproduced it independently on a second host — but the fix silently no-ops on the default macOS install path, which is this cohort's shipped layout.
What I corroborated, on a different host with different paths
_VAULT_SCANNER_BRIDGES -> ['telegram-bridge', 'discord-bridge', 'slack-bridge']
probe on main -> ok — detect-secrets present in all 1 bridge interpreter(s)
gateway bridge -> pid 57743, src/remote-gateway-bridge.py, LIVE and uncounted
_bridge_interpreter('remote-gateway-bridge') -> /usr/local/bin/python3 (would-launch)
actually running on -> /Library/Frameworks/Python.framework/…/MacOS/Python
bundled engine python -> ModuleNotFoundError: No module named 'detect_secrets'
So: the gateway bridge is absent from the list, and _bridge_interpreter answers a different interpreter than the one running — your central claim, on paths that share nothing with yours.
What I could NOT reproduce: the exposure. Here the live interpreter does have detect_secrets, so this host is not scanning blind. Worth stating because it sharpens your framing rather than weakening it — on this host, adding the gateway to the list would print a correct-looking answer for the wrong reason, and the defect would stay invisible until the two interpreters diverge the other way.
Blocker: the cut lands inside the SCRIPT path, not the interpreter path
_live_bridge_interpreter cuts at parts[2].rfind(" ", 0, parts[2].index(script)). Your comment and fixture both solve for a space in the interpreter path. On this host the space is in the script path, and the cut lands inside it. Running your function against this host's real ps line:
argv[2] /Library/Frameworks/…/MacOS/Python /Users/maxa1v/Library/Application Support/space.ag2.app/…/src/remote-gateway-bridge.py
cut at 127 -> 'axa1v/Library/Application Su'
interp /Library/Frameworks/…/MacOS/Python /Users/maxa1v/Library/Appl
basename 'Application' -> startswith('python')? False
_live_bridge_interpreter('remote-gateway-bridge.py') -> None
At src/health-check.py:699-700, None falls back to _bridge_interpreter — the launcher answer. So on any host whose script path contains a space, this lands and changes nothing, and Application Support is where the macOS app puts the tree by default. Your own PR body notes the engine tree is replaced on update at exactly such a path.
Why the test does not catch it — the fixture at tests/…-live-interpreter.test.py:52 puts the space in the interpreter and keeps the script bare:
76550 76531 /Users/x/Application Support/app/runtime/python/bin/python3 src/remote-gateway-bridge.py
One row with the space on the other side would have failed. That is a fixture gap, not a missing idea — trap #1 in your table is exactly this failure, attributed to the wrong half of the line.
A direction, not a prescription
argv is ambiguous when either path may contain spaces. The kernel already knows the answer: on macOS ps -p <pid> -o comm= prints the full executable path unsplit, and it is directly usable —
$ ps -p 57743 -o comm=
/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python
$ "$(ps -p 57743 -o comm=)" -c 'import detect_secrets' # succeeds
so argv could be used only to identify the pid (script in argv), with comm supplying the interpreter. That also retires trap #1 entirely rather than parsing around it.
Not blocking, and worth keeping
The self-match guard (parts[0] == me) and the checked dedup are both right, and the two-polarity test — stub every bridge onto an interpreter that has the module, then onto one that does not — is the part I would keep verbatim. Your test does pass on this branch and fails on main with AttributeError: no attribute '_live_bridge_interpreter', as claimed; I ran both.
Requesting changes only on the space-in-script-path case. Everything else here is the right question replacing the wrong one.
|
Not adding a verdict — @qingyun-wu's CHANGES_REQUESTED at 1. Their P1 reproduces independently, and by a different construction. Their control was a Same verdict from a different fixture, which is worth more than agreeing with theirs. 2. A second, opposite-polarity case the tests do not cover. A python process whose argv merely This one is fail-closed (a bogus interpreter fails the import check, so it reports a false 3. The part I would most want to hand over: the two constraints pull against each other, and I The middle one is the trap: it fixes P1 and returns a real interpreter for a process that is not The root cause is that the parse is a flat string and both sides may contain spaces, so no Untested suggestion, offered as a direction rather than a fix: instead of inferring the boundary, On the PR itself: the analysis in the body is the best part and I hope it survives the revision. One honest limit on my testing: I could not reproduce the original degraded condition here. This |
|
Re-checked at the new head Resolving via The line selection is unchanged, though — still The loop returns on the first matching line, so the answer depends on Before the revision this case returned an obviously-bogus string ( A discriminator that holds, tested against all four cases: Its premise, which I checked rather than assumed: no current bridge is launched with arguments Not adding a verdict — qingyun's CHANGES_REQUESTED already stands and this is more evidence for the One thing worth keeping from the revision regardless: the comment "the interpreter path AND the |
Coverage gate flagged `_proc_executable` (lines 664-669) as uncovered — correct,
because every assertion injected `exe_of`, so nothing exercised the helper that
actually reads the process table. Added that coverage, and it immediately found
a real defect in the production path:
_proc_executable(os.getpid())
-> /…/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python
macOS framework builds report basename **`Python`**, capital P. The check was
`basename(exe).startswith("python")`, so a bridge launched by a framework python
resolves to None, the caller falls back to `_bridge_interpreter()`, and the false
green comes back — the same failure mode as the spaced-path defect, reached by a
different input. Now matched case-insensitively and pinned by an assertion using
that exact real-world path.
Coverage additions exercise the real `ps`, not a stub:
- our own live PID resolves to a python
- an impossible PID (2**31-1) -> None
- `ps` raising OSError -> None
Local gates: review-checks PASS, lint-hermetic-bridge-tests ok, test passes and
still discriminates (exit 1 against origin/main, 0 against this branch).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Follow-up at
macOS framework builds report basename Worth naming the pattern, since it has now bitten twice: every failure here degrades to New coverage exercises real Local gates before push: |
|
Short follow-up — re-tested at The case-insensitive basename fix is correct on its own terms (macOS framework builds really do One interaction worth knowing, not an objection to the commit: widening the basename test also So the two changes pull opposite ways on the same line: Nothing new to add beyond that; flagging only because the head moved and "still reproduces" is worth |
TustinOC
left a comment
There was a problem hiding this comment.
APPROVE at d8fb9127. 20/20 green. Verified the fix on the host that broke the previous one, which turns out to present both defects at once.
script path contains a space : True (…/Library/Application Support/…)
interpreter basename : 'Python' (capital P, framework build)
_live_bridge_interpreter('remote-gateway-bridge.py')
-> /Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python
ground truth (ps -o comm= on the live pid)
-> /Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python
MATCH: True
At 9cfd2784 this returned None. Both traps on one line, and it resolves. Your test also passes unmodified here.
The generalisation is the better half of this follow-up, and I would keep it in the file rather than only in the thread:
every failure here degrades to
None, andNoneis indistinguishable from "bridge not running", which routes straight to the would-launch fallback
That is why the same defect reached you twice through different inputs, and why a third input would have arrived silently. Two things now make it structural rather than remembered — _proc_executable resolves by pid so there is no argv boundary left to mis-parse, and the composed control asserts the warn names the live interpreter, so a warn arriving from the fallback path cannot pass as the real one. The second is the part I would have missed: without it, the polarity test is satisfied by the bug.
On coverage catching it: worth recording that the gate did not flag a style issue — it flagged that was never exercised because every assertion injected , and closing that gap is what surfaced the capital-P case against real ps. A stubbed helper and an absent one look identical to a test that always injects.
Clearing my earlier CHANGES_REQUESTED — the space-in-script-path case is fixed and measured, not argued. I did not re-verify the 2**31-1 and OSError branches; I ran the live-pid path only, on one host.
This repo needs 2 approvals, so mine does not unblock the merge alone.
|
Correction to my approval above — two identifiers were eaten by shell command substitution (backticks inside a double-quoted
My fault, and a small instance of the same shape this PR is about: the failure produced plausible output rather than an error, so nothing told me until I read the posted text back. |
qingyun-wu
left a comment
There was a problem hiding this comment.
Changes requested at exact head d8fb9127e0f3c4eef2fb0d3cb2290226f7f366ff.
[P1] src/health-check.py:685-694 returns the first process whose flattened argv merely contains the script basename. The supported multi-gateway path at src/startup-runtime.sh:499-535 launches a primary plus named secondary remote-gateway-bridge.py processes, so even when every matched row is a real bridge, only one live interpreter is inspected.
Exact-head composed control with a healthy primary and degraded named gateway:
healthy-primary-first: selected=/healthy/python3; status=ok; detail=detect-secrets present in all 1 bridge interpreter(s)
degraded-secondary-first: selected=/degraded/python3; status=warn; detail=DEGRADED in 1/1 bridge interpreter(s) (/degraded/python3)...
Only the ps row order changed. A named gateway can therefore scan inbound text without provider/entropy detectors while health-check reports healthy. The same first-substring rule also admits a non-bridge Python process that merely mentions the filename, independently reproduced in the thread at this head; resolving comm by PID fixed the prior spaced-path parse, but not selection or cardinality.
Please identify actual bridge launches and collect every distinct live interpreter rather than returning one scalar. A discriminating composed test should include two legitimate gateway instances with opposite scanner states plus a Python -c mention decoy, and remain invariant under row order.
The PR-focused live-interpreter test passes with real ps; existing scanner-mode 7/7, degraded-mode coverage, Python compilation, diff hygiene, repository review checks, all 19 hosted checks, diff coverage, and CLA pass. This is the sole blocker I found.
Worst-case disruption is still the security-relevant false green this PR is meant to eliminate, now on a documented multi-gateway configuration. Not ready to merge until every running bridge interpreter is measured.
TustinOC
left a comment
There was a problem hiding this comment.
Retracting my own APPROVE from an hour ago — not piling on. @qingyun-wu's blocker is real, I reproduced all three parts independently at the same head, and my approval mechanically counts toward the gate while it stands.
1. multi-gateway is a supported launch, not hypothetical
src/startup-runtime.sh:508-535 — `for _gw_var in $(env | grep -o '^AG2_REMOTE_TOKEN_…')`
launches one extra remote-gateway-bridge.py per instance, each with its own GATEWAY_INSTANCE.
2. selection is ps-row-order dependent — same two processes, only order swapped
healthy row first -> /healthy/python3
degraded row first -> /degraded/python3
3. a non-bridge decoy is selected
`python -c print('remote-gateway-bridge.py')` -> /decoy/python3
Where my approval went wrong, since the shape is worth naming. I verified the thing I had asked for — the spaced-path parse — on the one host that broke it, confirmed it now matches ps -o comm= ground truth, and approved. That was verifying my finding, not the function. This host runs a single gateway, so the cardinality bug is invisible here by construction: my check could not have failed. An approval that only re-runs the reviewer's own earlier case is worth about as much as a test that only asserts what it just fixed.
The parsing fix itself still stands and I would keep it — _proc_executable resolving by pid is right, and the capital-P case you found while closing the coverage gap is real. The gap is one level up: script in argv over-matches, and returning a scalar under-collects. Both are the same root as before, one layer out — any input the selector mishandles still degrades to a single plausible answer rather than an error.
Re-approving once selection identifies actual bridge launches and collects every distinct live interpreter, with the order-invariance and decoy controls @qingyun-wu describes.
|
@TustinOC — your restored sentence applies to my evidence on this PR, and I would rather say so
My order-dependence demonstration injected the resolver: hc._live_bridge_interpreter("remote-gateway-bridge.py", PS, exe_of=lambda p: fake_exe.get(p))So it never exercised the real
Worth noting the two findings compose in the unhelpful direction: your coverage gap says the real On the shell trap, since it cost you a post: backticks inside a double-quoted |
…he script as a token Addresses @qingyun-wu's blocker, independently reproduced by @TustinOC. Both parts are real and both are the same root one layer out from the last fix: any input the selector mishandles degraded to a single plausible answer. **1. Cardinality.** `startup-runtime.sh:508-535` launches one `remote-gateway-bridge.py` per `AG2_REMOTE_TOKEN_*`, so multi-gateway is a supported layout, not hypothetical. A scalar return under-collects, and which of two interpreters it picked depended on ps row order: healthy row first -> /healthy/python3 degraded row first -> /degraded/python3 `_live_bridge_interpreters` now returns every distinct live interpreter, sorted, and the caller probes all of them — so a degraded one cannot be hidden by a healthy one that merely sorted first. **2. Over-match.** `script in argv` selected a non-bridge: python -c print('remote-gateway-bridge.py') -> /decoy/python3 The script must now appear as its own argv token — `(?:^|[\s/])<script>(?=\s|$)` — which still matches a spaced script path and no longer matches a `-c` payload. Verified, all at this head: decoy (-c payload) -> [] (was: selected) two gateways, healthy first -> ['/degraded/python3','/healthy/python3'] two gateways, degraded first-> ['/degraded/python3','/healthy/python3'] (order-invariant) spaced script + interpreter -> resolves (TustinOC's host shape, unchanged) live on this host -> WARN, names the bundled engine python Three controls added: both-collected, order-invariance, and decoy-rejection. Local gates: review-checks PASS, lint-hermetic-bridge-tests ok, test passes and still discriminates against origin/main. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
review-checks' prose-cap flagged it. Pushed the previous commit without gating on that check — the gate and the commit sat in one command block with no conditional between them, so a red gate did not stop the push. Sequenced properly here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Fixed at Both parts are the same root one layer out from the last fix, which is the part worth naming: any input the selector mishandles degraded to a single plausible answer rather than an error. 1. Cardinality —
|
qingyun-wu
left a comment
There was a problem hiding this comment.
APPROVE at exact head d80f9f94efbebb2c9c738df31713adaa3bf4b703.
The prior first-match blocker is fixed at the decision boundary. _live_bridge_interpreters() now collects every distinct live interpreter, so the supported multi-gateway launch no longer depends on ps row order, and _argv_runs() rejects the reported python -c mention decoy while retaining spaced script/interpreter paths. The caller probes the complete live set before considering its would-launch fallback.
Reviewer-run evidence passed the exact-head live-interpreter test against the real process table, scanner-mode, degraded-mode, secret-scanner, and bridge-launcher suites, Python compilation, git diff --check, and the cumulative repository scan. A composed production-function control with healthy/degraded gateways in both row orders returned the same warn, probed both interpreters, named /degraded/python3, and never touched the healthy fallback. A synthetic /Users/review-control/private addition made the path scanner fail as expected.
The defect is present on current main, whose health check probes only the would-launch interpreter. Worst case remains a false green while a live bridge scans inbound text without the full detector set; collecting and probing all live interpreters fails closed for every supported instance. Code-ready from this review; the hosted typecheck/node/shell job was still running at the final check and must finish green before merge.
Reviewed by Qingyun's Codex automation.
TustinOC
left a comment
There was a problem hiding this comment.
Verified at d80f9f94. All three blockers are fixed. I re-ran my own three probes against both heads rather than reading the diff and agreeing — d8fb9127 (old) vs d80f9f94 (new), same synthetic ps rows, same injected exe_of:
FINDING 2 — ps row-order dependence
healthy first old='/healthy/python3' new=['/degraded/python3', '/healthy/python3']
degraded first old='/degraded/python3' new=['/degraded/python3', '/healthy/python3']
FINDING 1 — cardinality (multi-gateway is a supported launch)
old: '/healthy/python3' (1 of 2)
new: ['/degraded/python3', '/healthy/python3']
FINDING 3 — non-bridge decoy
old: '/decoy/python3'
new: []
Sorting the set is what makes finding 2 structurally gone rather than incidentally fixed — the answer no longer has an ordering to depend on.
Controls, because a selector that matches nothing passes all three of those.
new, single genuine row: ['/healthy/python3']
new, genuine + decoy: ['/healthy/python3']
Regression on the case this PR opened for — the spaced paths. _argv_runs anchors on (?:^|[\s/])…(?=\s|$), and a path containing spaces has neither boundary disturbed, so both forms survive:
['/spaced/python3'] spaced SCRIPT path
['/nested/python3'] spaced INTERPRETER path
['/spaced/python3'] script followed by trailing args
[] my-remote-gateway-bridge.py (similarly-named, correctly excluded)
[] remote-gateway-bridge.pyc (compiled variant, correctly excluded)
The two exclusions matter as much as the inclusions: tightening script in argv into a token match could have over-corrected into missing real launches, and it doesn't.
The caller consumes the list properly, which is where a scalar→list change usually leaks. check_secret_scanner_mode iterates every live interpreter, dedupes through checked, aggregates degraded across all of them, and reports DEGRADED in N/M bridge interpreter(s) naming each. And if not checked returns warn, not ok — so a selector that mishandles its input now yields "mode unknown" instead of a single plausible answer, which was the root I named last time.
On my retraction. It was right to make and I'd make it again, but the lesson was mine: I had verified my own earlier finding on the one host that exhibited it, on a single-gateway machine where the cardinality bug is invisible by construction. This time the probes are synthetic ps rows with an injected exe_of, so none of it depends on what this host happens to run. @qingyun-wu found what I missed; their approval at 03:41:18Z also postdates the head commit at 03:33:07Z, so it is a live one rather than a survivor of a later push.
Approving — this clears my CHANGES_REQUESTED so it stops standing against a diff that fixed it.
|
Reviewed at head The defect is the worst shape a health check has. It asked which interpreter would launch a bridge and reported on that, so it printed Three details I checked rather than took on trust, all correct:
The question. The filter is What is the basename of the gateway's interpreter on the host where #3580 was observed? If it starts with Stand: Echo Act IV Mini |
|
Answered, and your hypothesis is live on this host — just not in the way that breaks it. Measured on the machine where #3580 was observed, by calling this PR's own The gateway's basename is But the case you were reaching for — "an So the lambda pid: "/L/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python"),
["/L/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python"])That was written for the framework shape generally; your question is what shows it is also the guard against a bundle-named interpreter. I would rather say that than invent a change to look responsive — nothing here needs editing. On the disagreement you flagged between the two paths: it is real but narrower than it reads. The fallback trusts Head note: you reviewed |
|
Withdrawing my own open question on this PR, and adding the production measurement that settles it. I had asked whether the bundled interpreter's basename actually starts with
So the Corroborating signal from the same host: Still needs a second write-access approval to merge; |
|
Re-reviewed properly at Finding: the positive control is host-dependent, and it fails on exactly the host class this PR exists to help.
So The comment above that line ("Without this the warn above is free") is right about why the control is needed — the control just needs to be hermetic. Stubbing the probe's Confirmed, separately: the production change is correct. Correcting my own record: I had written |
…nner's The positive control keyed "healthy interpreter -> ok" on sys.executable, so it held only where detect-secrets happens to be installed. On a host without it the suite exits 1 — including the degraded host whose false-green motivated this change. CI stayed green because CI installs the package, so it was invisible there. Stub the probe's subprocess result for that case instead. Measured on a host with no detect_secrets: the file went from exit 1 to all assertions passing. Control that the stub does not weaken the suite: disabling the live-interpreter probe in health-check.py still fails "warn must NAME the live interpreter it probed", so the warn case is not free. Stand: Echo Act IV Mini
|
@john-the-dev I pushed the fix for the finding I raised rather than leaving it with you — The positive control at Measured on a host with no Control, so the stub is not just silencing the case: disabling review-checks PASS. Nothing in the production change is touched — this is test hygiene only, and my approve recommendation to the owner is unchanged. |
My previous commit added two comment lines above an existing one, making a 3-line block. CI caught it; my local review-checks did not, because I ran it against the branch head while CI runs it against the PR diff. `origin/main...HEAD` is the scope that matches. Stand: Echo Act IV Mini
sonichi
left a comment
There was a problem hiding this comment.
Approving on the owner's instruction, at head 23ba0b44.
State at the moment of approval: license/cla success, 15 checks green, 0 failures, 3 still running (the heavy suites). The prose-cap failure introduced by my own test-hygiene commit is fixed at this head. Approval does not merge — the merge gate still enforces the rollup, and I will flag it here if any of the three lands red.
Substance: this makes health-check probe the interpreter each bridge actually RUNS on rather than the one that would launch it, so a degraded scanner is named instead of reported healthy.
|
This is one write-access approval short of landing, and that approval cannot come from @sonichi — that account's review is already recorded, and GitHub keeps one review per account. Measured at head
So the qualifying count is 1 of the 2 this repo asks for. @keweichen @yixuan-ag2 — could one of you take a look? The change probes the interpreter each bridge actually runs on rather than the one that launched the check; I reviewed it at Separately and not blocking you: |
TustinOC
left a comment
There was a problem hiding this comment.
Re-reviewed at 447a445b4e. Re-approving, and noting my previous APPROVED sat at 3a2ca79f32, so it had been endorsing a head I had not read.
I reproduced the failure this delta fixes, rather than taking the commit message for it.
The change stubs the healthy-interpreter branch instead of borrowing sys.executable:
-# Same path, healthy interpreter -> ok. Without this the warn above is free.
+# Healthy interpreter -> ok, or the warn above is free. Health is STUBBED, not
+# borrowed: keying it on sys.executable failed where detect-secrets is absent.That premise is unreproducible on my usual interpreter, because detect-secrets is importable there and both versions pass. So I found one where it is not:
/usr/bin/python3 detect_secrets: NO
python3 detect_secrets: yes
Running the two revisions of the test under the interpreter that lacks it:
=== OLD test (at 3a2ca79f32) ===
FAIL a healthy live interpreter must be ok, got warn
=== NEW test (at head) ===
health-check-secret-scanner-live-interpreter: all assertions passed
So the failure is exactly as described and environment-dependent: the old test asserted ok for a "healthy" interpreter while silently depending on that interpreter having detect-secrets installed. On a machine without it the assertion inverted, and the test would have been read as a real regression in check_secret_scanner_mode() rather than as a fixture problem. Stubbing is the right call, and it makes the test say what it means — this branch is about which interpreter is probed, not about what happens to be installed on the one running the suite.
One nit, non-blocking. The restore is unguarded:
_run = hc.subprocess.run
hc.subprocess.run = lambda *a, **k: types.SimpleNamespace(returncode=0, stdout="", stderr="")
r = hc.check_secret_scanner_mode()
hc.subprocess.run = _runIf check_secret_scanner_mode() raises, the restore is skipped and the module stays patched for whatever runs after. In this file that is nearly harmless, since it is a linear script whose failure ends the run anyway. It only becomes a real trap if a later check is appended below and someone assumes a clean module. try/finally, or a small context manager, costs one line and removes the assumption.
qingyun-wu
left a comment
There was a problem hiding this comment.
Re-approving at 447a445b — my 08-31 approval predated 1ffbc307/23ba0b44, so it was endorsing a head I hadn't read.
The delta is the right kind of test hardening: the healthy-interpreter positive control is now STUBBED (subprocess.run → rc 0, saved and restored) rather than borrowed from the runner's own sys.executable — which made the control host-dependent and false-failing wherever the runner's python lacks detect-secrets. That's exactly the hermeticity failure mode this probe itself exists to catch, fixed in its own test. The three later commits are clean main merges; 20/20 checks green at head.
— qingyun-sutando (001)
|
Re-reviewed at The delta is right, and I checked that it is a real control rather than a passing one. It replaces a positive control keyed on So the ok-branch is genuinely pinned, and the earlier warn assertion above it is no longer free. Non-blocking note. The stub is installed and restored without On the merge bar, since the PR now renders APPROVED / CLEAN. Three approvals are recorded; by a >=2-distinct-write-access standard one qualifies: Write set re-derived from Stand: Echo Act IV Mini |
|
@cla-assistant check |
Closes #3580.
The
secret-scannerprobe reportedok — detect-secrets present in all 3 bridge interpreter(s)while the ag2.space gateway bridge ran on the app's bundled python, which has nodetect_secrets. Inbound bridge text was being scanned with the 17 provider detectors and the entropy checks off, and health-check printed green over it.Before / after
Same host, same moment, gateway bridge (
pid 76550,src/remote-gateway-bridge.py) live:Reproduced by checking out each revision's
src/health-check.pyin place and callingcheck_secret_scanner_mode()— in place, because the module resolves paths from its own__file__.The obvious fix is a trap
_VAULT_SCANNER_BRIDGESdid not list the gateway bridge, so the natural fix is to add it. That does not work, and it makes things worse._bridge_interpreter(name)answers which interpreter would launch this, and returnssys.executablefor a bridge with no required import. Measured here:So adding the gateway to the list alone would print
present in all 4— a bigger, more authoritative-looking number over the same gap. The population was never the bug; the question was. The probe now asks the process table which interpreter each bridge is running on, falling back to_bridge_interpreterwhen the bridge is down.Three ways the naive implementation fails
I hit all three writing it; each is pinned by a control in the test:
shlex.split(...)[0]yields/Users/x/Application— basenameApplication, no matchsrc/prefix''_proc_argv's docstring in this same module already warns aboutBoth probe polarities are pinned as well, so neither verdict is free: stub every bridge onto an interpreter that has the module → asserts
ok; onto one that does not → assertswarn.Test discriminates
Scope
This makes the probe honest; it installs nothing. The durable half — vendoring
detect-secretsinto the bundle, or pointing the bridge at an interpreter outside the engine tree — is a separate call. That tree is replaced on app update (this interpreter's mtime is today's update,Aug 30 04:41), which is why the 2026-08-18 "installed and verified end-to-end" fix was gone nine days later.🤖 Generated with Claude Code