Repository navigation
fix(gateway): status readers follow the desktop's active lane (F20) - #5997
Conversation
s.space signs in as a SECONDARY gateway lane on the desktop (PRIMARY_LANE_DOMAINS stays ag2.space), so its bridge writes only state/gateway-status.<lane>.json, e.g. gateway-status.s_dspace.json. health-check, core-input-watch and services_status read only the unsuffixed state/gateway-status.json, so on an s.space-only install they had no opinion about the lane that is actually serving. gateway_serving gains active_lane() (the first line of $SUTANDO_APP_SUPPORT/channels/active-gateway-lane, the selector the desktop writes, when it is a valid instance slug other than all/primary) and status_path(state_dir) (that lane's suffixed file when it exists, else the unsuffixed one). The three readers resolve their default path through it; health-check keeps status_read_path's legacy workspace-root fallback for the unsuffixed file. No change to which lane is primary. Test: tests/gateway-status-active-lane.test.py (a workspace with only gateway-status.s_dspace.json and active lane s_dspace reads as serving in all three readers; no/all/primary/invalid selectors and a missing lane file fall back; the legacy single file still works). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yixuan-ag2
left a comment
There was a problem hiding this comment.
Changes requested. There is one blocker, a red required check caused by the new test file. Everything else below is non-blocking. Reviewed at head 563d0198, merge-base 9826add5.
Blocker
tests/hermetic-env-structural.test.py fails on the new file with tests/gateway-status-active-lane.test.py:26: misplaced-isolation and :36: unisolated. That failure is the red python standalone tests (2/9), and it fails the required tsc + tests (clean install).
- Reproduced at head:
FAILED (failures=2), and both failures name this file. - Control: the same suite at the merge-base without the file gives
Ran 27 tests ... OK. - Cause: lines 23-25 (
tempfile.mkdtemp()/.mkdir()) run beforeisolate_os_environ(...)at :26. That placement voids the isolation, so the loader at :36 counts as unisolated. - Fix: call the isolation first, then create the two dirs and set the env vars. That should clear both findings.
The other red jobs look unrelated. (6/9) is whatsapp-guided-connect, the known flake in #5784. (7/9) is ack-claim-lock-cleanup, which passes locally at head and imports none of the touched modules. A rerun should confirm.
Verified
- Discrimination of the new test:
- At head: PASS.
- Control 1, merge-base plus the test only: fails with
AttributeErroronstatus_path. - Control 2, merge-base plus head's
gateway_serving.pyonly (readers unwired): the 4 reader checks fail (health-check_gateway_servingand last-ok age, core-input-watch, services_status probe path). The reader assertions discriminate, not just the resolver ones. - Mutation,
exists()fallback dropped: both "missing lane file" checks fail.
- Existing suites at head, all rc 0:
- gateway-serving-shared-owner, health-check-gateway-bridge, gateway-connected-requires-last-ok, gateway-age-helpers-overflow-safe
- core-input-watch-gateway-probe, core-input-watch (195 tests)
- services-status-liveness-patterns, services-status
- health-check-gateway-lane-verdict (12), health-check-gateway-stale-lane (29), health-check-gateway-outage-duration (7)
- gateway-runtime-identity, health-snapshot (93)
review-checks.sh --diff: PASS.
- Bug exists on main, read-only on a live s.space-only desktop install. The install has selector
s_dspace, a freshgateway-status.s_dspace.jsonand a parked primarygateway-status.jsonabout 4.8h stale.- At the merge-base, health-check
_gateway_serving()returnsNoneand_gateway_status_stale_age_s()is about 17359s.check_gateway_bridgetherefore takes the stale-sidecar warn branch before it reaches_gateway_lane_verdicts, even though that function does reports_dspaceserving. core-input-watch_gateway_statusreturnsNone. - At head, both return
True. - The fixture has no primary file at all. With an absent primary, main already falls through to the lane verdicts, so the stale-primary shape is the one that actually warns on main. Worth adding as a case.
- At the merge-base, health-check
- No reader left behind. The other
gateway-statusreaders insrc/either glob every lane (health_snapshot.py,connections.py, health-check's lane helpers) or are per-instance bridge code.services_status.GATEWAY_STATUS_PATHis now unused insrc/; onlytests/services-status.test.py:389uses it.
Fallback semantics and worst case (non-blocking)
- Selector absent, empty,
all,primaryor invalid: the unsuffixed file is read, the same as main. - Selector names a lane whose file is missing: the unsuffixed file is read. On a desktop that file is the parked lane's leftover.
- A fresh, connected leftover (just after a lane switch) reads as serving until its freshness window runs out: 180s in health-check and services_status, 90s in core-input-watch. After that, health-check gives a stale warn and the other two have no opinion and fall back to pgrep.
- So this fails toward a short false-OK, or a short false not-serving if the leftover recorded
connected:false. It cannot turn a connected legacy record into "disconnected". - These reads match main, so this is not a regression.
- Divergence from the desktop's lane policy:
active_lane()reads the raw selector. The desktop's launcher guard (_effective_active_lane) and its own status readers (effective_active_gateway_lane, documented there as "Status readers use this, never the raw file") ignore the selector in three cases: it does not serve the signed-in homeserver, it has no launch key, or it relays via loopback. In those cases every lane runs.- In that state this PR moves the three readers off the primary onto the named lane. If that lane is not signed in, core-input-watch could pin
gateway-downwhere main read the serving primary. - The desktop writes the selector and the homeserver record together, with rollback, so this should be rare. It is still a third reading of a policy the desktop keeps in two parity-tested copies. Having the desktop publish the effective lane would remove it. That can be a follow-up.
Nits
src/gateway_serving.py:26: the comment calls this the bridge'sGATEWAY_INSTANCEshape, but the bridge's shape is[A-Za-z0-9_-]{1,32}(gateway_instance.INSTANCE_RE,src/connections.py:101). Lowercase-only matches what the desktop writes, so the behavior is fine; the comment is wrong.- Selector validation is barely pinned. With the regex and the
all/primaryexclusion both removed, only theS_DSPACEcheck fails locally, and only because macOS is case-insensitive. On a case-sensitive FS the fallback should hide it too. Assertinggs.active_lane() is Nonefor each rejected selector would pin it. (F20)in thegateway_serving.py:45docstring and theservices_status.py:284comment is a tracker reference (CLAUDE.md comment rule).- The "KNOWINGLY PRIMARY-ONLY" header at
services_status.py:278now contradicts its last line. - There is a stray
if True:at test line 81.
What would change the verdict: a green hermetic-env-structural. With that fixed, I'd approve.
hermetic-env-structural flagged tests/gateway-status-active-lane.test.py (misplaced-isolation, unisolated): its temp dirs were created before isolate_os_environ(), so tempfile read the outer environment first. The test now isolates first, as the other gateway tests do, then creates its fixture dirs and sets SUTANDO_APP_SUPPORT. Also drops plan references from comments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
review-checks (prose-cap) counted the 7-line module docstring and the run of `# noqa: E402` import comments as over-cap comment blocks. The docstring is two lines and the imports after isolate_os_environ() carry no comments, as in the other gateway tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Coverage Gate✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 94%. Diff CoverageDiff: origin/main...HEAD, staged and unstaged changes
Summary
|
yixuan-ag2
left a comment
There was a problem hiding this comment.
Approve. Reviewed at head 77aa083e. The blocker from my last review (at 563d0198) is fixed. The notes at the end are non-blocking.
Blocker fixed
isolate_os_environ()now runs at test line 10, before any project import. The temp dirs are created at lines 20-22, after it.- I ran the structural scanner's
violations()on the new test file:- At
563d0198::26 misplaced-isolation,:36 unisolated. - At
77aa083e:[].
- At
- The full
tests/hermetic-env-structural.test.pyat head givesRan 27 tests ... OK. - CI at head: 36 of 37 check runs succeed and
PowerShell + Windows runtimeis skipped. Everypython standalone tests (n/9)shard passes, and so doestsc + tests (clean install). The earlier(6/9)and(7/9)reds did not come back.
Other checks
- F20 references are gone. No added line in the diff against main contains
F20, andgit grep F20over the five touched files at head finds nothing. - The new test still discriminates. It passes at head. I ran two controls:
- Merge-base
9826add5src with the head test fails withAttributeError ... status_path. - Merge-base src plus only head's
gateway_serving.pyfails all four reader checks: health-check serving, health-check last-ok age, core-input-watch, and the services_status probe path.
- Merge-base
- Dropping
SUTANDO_WORKSPACE_DIRfrom the test leaks nothing. I ran it with an empty<repo>/workspace/statepresent, and it wrote nothing there. The readers getWSandSTATEassigned or passed explicitly. - Related suites pass at head:
- hermetic-workspace-guard, lint-hermetic-bridge-tests, lint-hermetic-discovery-floor, hermetic-env-sentinels
- gateway-serving-shared-owner, health-check-gateway-bridge, core-input-watch-gateway-probe, services-status
- health-check-gateway-lane-verdict, health-check-gateway-stale-lane
scripts/review-checks.sh --diffon the PR diff: PASS.
Still open from last review (non-blocking)
src/gateway_serving.py:26says the regex is the bridge'sGATEWAY_INSTANCEshape. The bridge's shape is[A-Za-z0-9_-]{1,32}.- The rejected selectors are pinned only through the path fallback. Asserting
gs.active_lane() is Nonefor each one would pin the regex itself. - The fixture still has no stale-primary case. That is the shape that actually warns on main.
- The
KNOWINGLY PRIMARY-ONLYheader atsrc/services_status.py:278contradicts its last line. - There is still an
if True:at test line 76. - The divergence between the raw selector and the desktop's
effective_active_gateway_laneremains a follow-up.
liususan091219
left a comment
There was a problem hiding this comment.
Approve. Reviewed at head 77aa083e. The bar is met by the expert route: @yixuan-ag2's approval at this head is substantive, and the required verification exists. They reproduced the bug read-only on a live s.space-only desktop install. On main, _gateway_serving() returned None against a 4.8h-stale primary; at head it returns True. They also ran controls showing the reader assertions discriminate, and confirmed the hermetic-env blocker is fixed.
Independently checked
- Tests use the real code.
tests/gateway-status-active-lane.test.pyimports the realgateway_servingand loads the three readers viaspec_from_file_location. It does not grep the source text or copy the logic into the test. It passes locally at head. - No reader is left behind. I searched
src/ scripts/ skills/ packages/forgateway-status. The single-file readerscore-input-watch.py:529,health-check.py:6975andservices_status.py:286now go throughgateway_serving.status_path. The rest either read every lane's file (health_snapshot.py:379,connections.py:1578,connect_bridge.py:148) or are code inside one bridge instance. - The lane policy has one owner. It lives in
gateway_serving.py:30-51. - Status. CI is green and
license/clapassed. There are no unresolved threads.
Non-blocking
gateway_serving.py:32:active_lane()reads only$SUTANDO_APP_SUPPORT.sutando_config.desktop_runtime_selection_pathalso falls back to the engine tree's parent directory, so jobs started without that variable quietly read only the primary file.gateway_serving.py:26: the comment calls this the bridge's instance shape. The bridge's shape is[A-Za-z0-9_-]{1,32}(connections.py:101).- Asserting
gs.active_lane() is Nonefor each rejected selector would pin the rule itself. There is also a leftoverif True:in the test. services_status.py:278: the "KNOWINGLY PRIMARY-ONLY" header contradicts line 284.
— Sutando (susans-MacBook-Pro)
main gained its own answer to "which gateway-status sidecar do the readers read": #5983 routed health-check, core-input-watch and services_status through connections.primary_sidecar_name (the primary connection row's suffixed file, the unsuffixed one without a primary row), and #5971 / #6008 reworked the neighbouring health-check text and the configured-gateway probe. This branch routed the same three readers through gateway_serving.status_path, the desktop's active-lane selector. Both are kept: the active lane wins when the selector names a valid lane whose file exists (an s.space-only desktop install, where s.space is a secondary lane and no row is primary), else the primary connection's sidecar as main resolves it. Conflicted files and how each was resolved: - src/gateway_serving.py (auto-merged, edited): status_path's fallback is now connections.primary_sidecar_name(state_dir) via a lazy import instead of the literal gateway-status.json, so one resolver carries both rules and no reader composes them privately. Without a primary row that name is the unsuffixed file, so the branch's fallback cases are unchanged. - src/core-input-watch.py: _gateway_status reads gateway_status_path(state_dir) (this branch); main's private _primary_sidecar_name helper is dropped because its logic now lives in the shared resolver; gateway_alive's docstring names both sources. - src/services_status.py: main's no-argument gateway_status_path(), which services-status.test.py pins, is kept and returns status_path(STATE_DIR); the import alias becomes _lane_status_path so the function no longer shadows it (the raw auto-merge had the def shadowing the import). Main's registry comment is kept with "active lane, else primary" wording; the probe calls gateway_status_path(). - src/health-check.py: all five default-path sites keep main's _gateway_primary_sidecar() (pinned by health-check-gateway-primary-connection .test.py); that helper now resolves the name through status_path and still passes it through status_read_path for the legacy workspace-root fallback. This branch's _gateway_status_path helper is removed as redundant. Tests run at this commit, all green: gateway-status-active-lane, gateway-serving-shared-owner, health-check-gateway-bridge, health-check-gateway-primary-connection, health-check-sspace-migration, health-check-gateway-lane-verdict, health-check-gateway-stale-lane, health-check-gateway-transient-outage, health-check-gateway-outage-duration, health-check-gateway-probe-unknown, health-check-notify-gateway, gateway-connected-requires-last-ok, gateway-age-helpers-overflow-safe, gateway-runtime-identity, core-input-watch-gateway-probe, core-input-watch, services-status-liveness-patterns, services-status, core-health-verdict, util-paths-runtime-identity-absent. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
sonichi
left a comment
There was a problem hiding this comment.
Code-owner approve at 05e333f (agent-assisted, Blue for Chi's seat).
- One owner for the lane choice.
gateway_serving.status_pathresolves it, andcore-input-watch,health-checkandservices_statusdelegate to it instead of each callingprimary_sidecar_name. That follows the shared-adapter-policy rule. - Safe selector.
active_lane()reads the first line of$SUTANDO_APP_SUPPORT/channels/active-gateway-laneand accepts only the bridge's instance shape (_LANE_REadmits no/and no..), excludingall/primary. A bad selector can't point the read outsidestate/. The lane file is preferred only when it exists; otherwise it's the primary sidecar as before, so nothing changes for installs without the selector. - health-check keeps
status_read_path's legacy root fallback for the chosen name. - Non-blocking:
active_lane()depends onSUTANDO_APP_SUPPORT. A process started outside the desktop's environment (a manualhealth-check.pyfrom a shell) falls back to the primary sidecar and reports the s.space lane as unknown again. Worth a line in the docstring, or a fallback to the app's default support dir. - Since Yixuan's and Susan's approvals, this head only merges main. CI was still running when I approved.
john-the-dev
left a comment
There was a problem hiding this comment.
Approve at exact head 05e333f37ffbc1ade656f444e3f18196aaf76399 (independent agent-assisted review). No blocking findings.
The shared gateway_serving.status_path owns the lane choice, and all three single-file readers delegate to it. This fits the shared-adapter-policy boundary. Existing freshness/reconnect decisions remain with their readers; health-check retains its legacy path fallback. No status schema or writer changes are introduced.
Independent validation in isolated temporary state:
- The active-lane integration test passes. Keeping the new resolver but restoring the three readers from base
369597667abe602b82d146c072351c1e46e60ef8fails four reader assertions, so this verifies the actual wiring rather than only the new helper's existence. - Additional real-reader scenarios: stale primary + serving active lane returns
(True, True, running)at head versus(None, None, offline)with base readers. A fresh healthy primary cannot mask a down active lane(False, False, offline)or malformed active record(None, None, offline). - Shared serving-contract, health-check gateway, core-input gateway-probe, services-status (36 tests), and hermetic structural (27 tests) suites pass. The earlier test-isolation blocker is resolved.
- The exact-tree review scanner passes.
Non-blocking limits remain as already discussed: selection depends on SUTANDO_APP_SUPPORT; missing active sidecars fall back to the primary, and the raw selector can differ from the desktop's effective selection. Worst case, a missing active sidecar can temporarily report the primary’s health; existing identity-blind process fallbacks also remain. This is retained fallback behavior, and the change does not start or stop gateway services. The regex comment should describe the desktop selector shape, not claim parity with the bridge's broader instance validator.
This review used synthetic sidecars and real reader code; no live service restart or round trip was performed. Listed checks pass/skip, but the structured readiness collector returned UNKNOWN, so this code approval is not a merge-readiness claim.
keweichen
left a comment
There was a problem hiding this comment.
CHANGES REQUESTED at 05e333f37ffbc1ade656f444e3f18196aaf76399.
Blocking: src/gateway_serving.py:26-50 introduces a private, unbounded lane matcher that drifts from the production GATEWAY_INSTANCE contract (src/connections.py:103-104 and packages/ag2-sparrow/ag2_sparrow/gateway_instance.py:26, both [A-Za-z0-9_-]{1,32}).
Exact repro on macOS with Python 3.13.5: write 256 lowercase a characters to $SUTANDO_APP_SUPPORT/channels/active-gateway-lane. At this head, active_lane() returns all 256 characters and _LANE_RE accepts 33 and 256 characters, while both production validators reject them. gateway_serving.status_path(), health-check._gateway_serving(), and services_status.service_registry() then raise OSError: [Errno 63] File name too long; core-input-watch._gateway_status() catches the exception and silently degrades to None. This defeats the stated invalid-selector fallback and can stop health/status output on one malformed desktop-owned state file. The green test covers traversal and uppercase but not the production length bound.
Please reuse or extract one dependency-light bounded instance validator, make invalid or unreadable selector values fall back without raising, and add behavioral controls for values over 32 characters, including a 256-character selector. That would change this verdict. The focused suites and CI otherwise pass, and the intended active-lane delegation is sound.
…ance bound Review at 05e333f found that active_lane() matched the selector against a private, unbounded pattern that drifted from the production GATEWAY_INSTANCE contract (connections.INSTANCE_RE and ag2_sparrow.gateway_instance.INSTANCE_RE, both [A-Za-z0-9_-]{1,32}). A 33- or 256-character selector was accepted as a lane, so one malformed desktop-owned state file could push a name no file system accepts into every status reader instead of falling back. Blocker: one validator, no copy. - src/gateway_serving.py: the private _LANE_RE and its comment are gone. active_lane() accepts the selector only when connections.INSTANCE_RE matches it whole (a lazy import, as the primary-sidecar fallback already is) and it is not all/primary. connections' regex is the one a connection row must pass to launch, so the reader and the launcher agree by construction; the sparrow package's copy serves the bridge process and stays where it is. - status_path(): the lane file's existence probe is wrapped so an OSError (a Python whose Path.exists re-raises ENAMETOOLONG, or an unreadable state dir) falls back to the primary connection's sidecar instead of propagating. - active_lane()'s docstring now says what the reviews asked: the bound it applies, and that a process started without SUTANDO_APP_SUPPORT reads the primary connection's sidecar. Non-blocking notes folded in: - The regex comment that misnamed the bridge's shape is removed with the regex. - tests/gateway-status-active-lane.test.py pins the rule itself: every rejected selector asserts active_lane() is None, so a case-sensitive file system can no longer hide a regression behind the path fallback. The uppercase case is dropped (uppercase is inside the production bound); dotted, spaced, 33- and 256-character selectors take its place. - New behavioral controls for the bound: with a serving primary file and a 33- or 256-character selector, status_path, health-check._gateway_serving, core-input-watch._gateway_status and services_status.service_registry all resolve the primary file and nothing raises. - New stale-primary case (the shape that actually warns on main): a 4.7h-stale primary beside a serving active lane reads as serving in health-check and core-input-watch. - The stray `if True:` block in the test is flattened. - F20 tracker references and the KNOWINGLY PRIMARY-ONLY header were already gone at 05e333f; nothing to change there. Evidence: the new test against the previous head's src (scratch worktree at 05e333f, test file copied in) fails three cases — 's.dspace', the 33- and the 256-character selector all reported as lanes; at this head it passes. Suites at this head, all rc 0: gateway-status-active-lane, gateway-serving-shared-owner, health-check-gateway-bridge, hermetic-env-structural, health-check-gateway-primary-connection, health-check-sspace-migration, core-input-watch-gateway-probe, core-input-watch, services-status, services-status-liveness-patterns, health-snapshot, gateway-runtime-identity, lint-hermetic-bridge-tests, lint-hermetic-discovery-floor, hermetic-workspace-guard. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
sonichi
left a comment
There was a problem hiding this comment.
Re-reviewed at ae0ee29 (agent-assisted, Blue for Chi's seat). Still approve.
- Lane rule. The selector is now checked against the bridge's own
connections.INSTANCE_RE(^[A-Za-z0-9_-]{1,32}$) instead of a private regex, so there's one owner for what a lane is. - Invalid selectors.
../etc, dotted names and anything over 32 characters are never a lane. - Uppercase. An uppercase selector now passes the shape check, but it only looks up
gateway-status.<lane>.json, which won't exist, and falls back. That's harmless. - No raise.
p.exists()is wrapped intry/except OSError, so an over-long path can't raise. - Tests. They now assert both
active_lane() is Noneand the fallback for each bad selector.
Merge once CI is green (3 pass / 15 pending when I looked).
john-the-dev
left a comment
There was a problem hiding this comment.
Re-reviewed exact head ae0ee29fa5aa0cf5e83cbfa919b44dd8c13d9b97. The overlong-selector failure raised by @keweichen is fixed; no remaining blocking code finding in this delta.
The previous private matcher admitted unbounded values. The helper now delegates to connections.INSTANCE_RE.fullmatch, enforcing the production 32-character bound, and treats an OSError from the file existence probe as fallback. The updated tests assert selector rejection directly and exercise all reader paths for 33- and 256-character values.
Independent evidence:
- Updated active-lane test passes at this head. Keeping the updated test but restoring
gateway_serving.pyfrom05e333f3fails with the reportedOSError: [Errno 63] File name too longon the 256-character selector. - Shared serving-contract suite and services-status suite (36 tests) pass.
- Three independent reader scenarios still pass: stale primary/healthy active, healthy primary/down active, and healthy primary/malformed active.
- Exact-tree review scanner passes.
My earlier review did not catch the missing selector length bound; this recheck reproduces that failure and verifies the fix. The separate changes-requested review still needs its reviewer’s update; this code recommendation does not clear their verdict. CI was still running, and the structured readiness receipt remains UNKNOWN. No live runtime/configuration changes or post-restart round trip were performed.
…no-app-support path The diff-coverage gate failed at ae0ee29 on src/gateway_serving.py lines 55-56, the `except OSError: pass` around the lane file's `exists()` in status_path(). No test reached it: the 33- and 256-character selector controls are rejected by the validator before the probe, and on the Python used locally Path.exists() swallows ENAMETOOLONG anyway. tests/gateway-status-active-lane.test.py gains two behavioral cases: - With the active lane's file present, pathlib.Path.exists is replaced, under try/finally, by a wrapper that raises OSError(63) for that one lane path and defers to the real method for every other path. status_path() then returns the primary sidecar without raising, health-check._gateway_serving and core-input-watch._gateway_status still read the primary file, and once the method is restored the resolver reads the lane file again. The lane file exists during the patch, so it is the except branch, not a missing file, that produces the fallback. - SUTANDO_APP_SUPPORT set to the empty string gives active_lane() None (the shell-run job case the docstring describes). Other suites exercised that line in CI; pinning it here keeps the gate independent of their environment. Measured with the gate's own invocation (coverage run --rcfile=.coveragerc on this test alone, then diff-cover --compare-branch=origin/main): src/gateway_serving.py 95.8% with line 36 missing before the second case, 100% (24/24 changed lines) after it. Suites at this head, all rc 0: gateway-status-active-lane, gateway-serving-shared-owner, health-check-gateway-bridge, hermetic-env-structural, health-check-gateway-primary-connection, core-input-watch-gateway-probe, services-status, lint-hermetic-bridge-tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
@cla-assistant check |
sonichi
left a comment
There was a problem hiding this comment.
Re-reviewed at 5b12d94 (agent-assisted, Blue for Chi's seat). The one new commit is test-only. It covers the shell-run path with no SUTANDO_APP_SUPPORT (no lane). It also covers the lane file's exists() raising: status_path, health-check and core-input-watch all fall back to the primary file, and the patch is restored in finally. Still approve; merge once CI is green.
john-the-dev
left a comment
There was a problem hiding this comment.
Approve at exact head 5b12d940d76c1eb611730947f2e53476d5295b69 after reviewing the test-only delta from ae0ee29. No new production changes or blocking findings.
The added checks exercise the existing empty-app-support and existence-probe-error branches. The Path.exists patch is limited to the active lane filename and restored in finally; the test then confirms normal lane selection resumes.
Independent isolated validation:
- Updated active-lane reader suite passes.
- Making the production existence-error handler re-raise makes the new check fail with
exists() raising propagated OSError(63, ...). - Removing the empty-root guard, with a valid selector under an isolated working directory, makes the new no-app-support assertion fail. This demonstrates it prevents an unintended relative-path selector read, rather than passing merely because no file exists.
- Exact-tree review scanner passes.
The prior production review and overlong-selector fix verification still apply. This refresh adds branch-test evidence; it does not claim a live round trip or green CI. CI remains in progress and the structured readiness collector returns UNKNOWN. No merge or runtime changes performed.
keweichen
left a comment
There was a problem hiding this comment.
Re-reviewed exact current head 5b12d940d76c1eb611730947f2e53476d5295b69. APPROVE; ready to merge. This replaces my prior changes-requested review because its blocker is fully fixed.
active_lane()now delegates toconnections.INSTANCE_RE.fullmatch, so the reader uses the production[A-Za-z0-9_-]{1,32}bound instead of a private unbounded matcher. The 33- and 256-character selectors are rejected before path construction.status_path()catchesOSErrorfrom the selected lane file probe and falls back to the primary sidecar. The5b12d940delta is test-only and directly forces that branch, restoresPath.existsinfinally, and pins the no-SUTANDO_APP_SUPPORTpath.- Exact-head local validation passed: active-lane integration, shared serving-owner, health-check primary-connection (22 tests), core-input gateway probe, services-status (36 tests), hermetic-env structural checks, and the cumulative review scanner. Keeping the current test against
05e333f3source fails with the original 256-characterENAMETOOLONG, so the regression control discriminates. - Current CI and CLA are green; Windows is intentionally skipped. Worst case for a malformed selector or unreadable lane probe is now the established primary-sidecar fallback, not a health/status crash. This is a read-only status-selection path, so no bridge restart witness is required.
liususan091219
left a comment
There was a problem hiding this comment.
Approve at 5b12d940. This replaces my earlier approval at 77aa083e, which the later commits made stale.
Bar met (expert route, and also ≥3 known approvals): at this exact head there are substantive approvals from Chi's seat (sonichi), @john-the-dev and @keweichen. @keweichen's approval replaces their own change request at 05e333f3, whose blocker (a private, unbounded lane matcher) is fixed: gateway_serving.active_lane() now validates the selector with the bridge's own connections.INSTANCE_RE. The reproduction @yixuan-ag2 did earlier still covers the underlying bug.
Checked in the delta since my last review
src/gateway_serving.py:active_lane()returns None for an unsetSUTANDO_APP_SUPPORT, an unreadable or non-UTF-8 file,all/primary, or a value outsideINSTANCE_RE;status_path()usesgateway-status.<lane>.jsononly when it exists, else the primary connection's sidecar. One owner for the lane choice;core-input-watch.py,health-check.pyandservices_status.pyall delegate to it.- The last commit is test-only (no-lane shell path and the
exists()error path). - No added
~/.sutando/SUTANDO_WORKSPACEliteral. CI green, mergeable CLEAN,license/clasuccess, no open threads.
Not verified: a live desktop round trip on an s.space-lane install after restart (named, not blocking).
— Sutando (susans-MacBook-Pro)
Priority
Level: medium
Reason: s.space-only desktop installs (rc.18 onward) get no gateway health verdict; read-side half of F20 in the ag2.space removal plan.
Closes
F20 (onboarding removal plan tracker), read side. s.space stays a secondary lane:
PRIMARY_LANE_DOMAINSin the desktop is unchanged.Summary
On the desktop, s.space signs in as a secondary gateway lane, so its bridge writes only
state/gateway-status.<lane>.json(for examplegateway-status.s_dspace.json).health-check,core-input-watchandservices_statusread only the unsuffixedstate/gateway-status.json, so on an s.space-only install they had no opinion about the lane that is actually serving.gateway_serving(the shared owner of this record) now resolves the desktop's active lane:active_lane()reads the first line of$SUTANDO_APP_SUPPORT/channels/active-gateway-lane(the selector the desktop writes; ignored unless it is a valid instance slug other thanall/primary), andstatus_path(state_dir)returns that lane's suffixed file when it exists, else the unsuffixed one. The three readers resolve their default path through it; health-check keepsstatus_read_path's legacy workspace-root fallback for the unsuffixed file.The "AG2 Space re-link recovery" text that tells the agent which status file to read lives in the desktop repo (
engine/AGENTS.md/engine/CLAUDE.mdof ag2-space/ag2space-cinny-desktop), not in this repo, so it is updated in the companion desktop PR ag2-space/ag2space-cinny-desktop#1005.Checklist
bassil@ag2.ai)upstream/main(the new test fails there, below)tests/gateway-status-active-lane.test.pygateway-status.s_dspace.jsonwithchannels/active-gateway-lane=s_dspace) is the one the rc.18 L2 journey run recorded (connect_op committed lane=s_dspace, gateway windowgateway-s_dspace).find tests -name '*.test.py')resolve_workspace/status_read_path; the app-support dir comes from$SUTANDO_APP_SUPPORT, aschannel_env_containmentandcloud_authread itBefore / after evidence
Before (src reverted to
upstream/main9826add, new test kept):After (this head):
The new test covers: a workspace with only
gateway-status.s_dspace.jsonand active lanes_dspacereads as serving in health-check (_gateway_serving,_gateway_last_ok_age_h), core-input-watch (_gateway_status) and services_status (registry probe path andprobe_gateway); selectors absent, empty,all,primary,../etcandS_DSPACEfall back togateway-status.json; an active lane whose file is missing falls back; the legacy single file still works.🤖 Generated with Claude Code