Skip to content

feat(cli-wedge): the caller's work signal per target, and one probe for many worker panes (#3937, slice 1) - #3939

Merged
qingyun-wu merged 11 commits into
mainfrom
feat/cli-wedge-pool
Sep 8, 2026
Merged

qingyun-wu merged 11 commits into
mainfrom
feat/cli-wedge-pool

Conversation

@qingyun-wu

@qingyun-wu qingyun-wu commented Sep 5, 2026 •

Copy link
Copy Markdown
Collaborator

What

First slice of #3937 (cli-wedge for the pool: several worker panes, one of them Codex). Stacked on #3925 (feat/cli-wedge-probe); merge order: #3925 first, then this PR rebased onto main. The diff here is the child layer only.

Two things the detector needed before a worker pane could be warned about, both measured by worker-3 on the issue:

  1. The caller's work signal, per target. work_outstanding() reads this workspace's core-status.json and tasks/, which is the core's work, not a worker's. On the targeted path feat(health-check): cli-wedge — an advisory CLI progress detector over the core pane #3925 already refused to borrow it (work_outstanding=False, 'no work signal for an explicit --target'), so a worker pane could be classified but never warned about — the signal was absent, not lying (worker-3 corrected their earlier 'wrong in both directions' measurement, which had used the core's --session path; the correction is on the issue). This PR supplies the missing signal: for an explicit target the deliverer provides it:

    • --work-file <json> — {"<target>": {"outstanding": true|false, "detail": "…"}} (or one flat record), so the pool lead can write what each worker owes;
    • --work-outstanding / --no-work — the caller's word for a single target.
      The core's own pane (no explicit target) still reads its own queue. A missing target key, or an unreadable file, is work_outstanding=False with a detail that says the signal is absent — never a verdict input. Precedence: flag > work file > "no work signal for an explicit --target". outstanding must be a JSON boolean (b5910d0): "false", 1, null and friends are the missing-signal shape with the value named, never coerced.
      1b. The role is identity, not spelling (0903ce9). core_identity() reads the core's own heartbeat record (socket, session); the resolved target equal to the core's target is role: core, anything else role: worker, --role overrides; the environment still selects the pane. Every worker window is keyed by canonical socket path + target (window_slot), so the same =core-2:1 on two servers keeps two windows. (Supersedes the earlier spelling-based rule:) Probing workers by session name — the natural reach, since they are named core-1..4 — used to share the core's window.jsonl and borrow the core's queue, silently: alternating two sessions pinned the sample count at 1 while observation runs climbed (worker-3, on main). A named session now gets its own window slot and no borrowed signal, exactly like --target; only the unnamed default is the core's own pane.
  2. One probe for many panes. probe --targets a,b,c samples each target into its own window (per-target windows are feat(health-check): cli-wedge — an advisory CLI progress detector over the core pane #3925's window_path(slot)), applies each target's own signal, and prints one verdict per target under the socket that was pinned. The socket is --socket and nothing else: worker-2 found two tmux servers on one host with identical core-1..4 session names, so a session name alone selects the wrong pane.

Deliverer contract (for the pool lead, 001's code): at task delivery and again before accepting a result, run

python3 src/cli_wedge.py probe --socket <pool socket> --targets "=core-2:1,=core-3:1,=core-4:1" \
    --work-file <path written by the lead: {target: {outstanding, detail}}> --workspace <workspace>

and read targets[<t>].kind / warn / work_detail. A warn: true is advisory (it reads the pane, not the process). The health probe in health-check.py keeps sampling only the core pane, unchanged.

Not in this slice: the Codex trace fixture (item 3 on the issue) — it needs a recording with the idle banner on screen (record … --keep-normalized from worker-4), asked for in the room; the banner text worker-2 quoted is the negative fixture in #3925 meanwhile. And the call site in the pool lead, which lands as 001's change against the contract above.

1c. A heartbeat whose socket is not a string is an unreadable record (6780455). core_identity() accepts a socket that is absent, null or a string; a list/object/int/bool socket makes the record unreadable and identity falls back to the configured default, so one malformed shared-state record cannot crash the diagnostic at realpath(). (Codex finding at 85bb97b.)

1d. A work file never silences the core's own queue (8551bce; john-the-dev's blocker at 6780455). The sampler passes the real target plus a core flag; a work-file miss for the core falls back to work_outstanding(), a core-keyed entry is honoured. A malformed heartbeat socket now drops the socket, not the session (john's nit).

1e. An invalid work file falls back to the core's queue (6699543; Codex's P1 at 8551bce). Missing file, bad JSON or a non-boolean record: the core reads work_outstanding(); a worker keeps the fail-closed missing-signal result. All three pinned with worker controls.

Files

  • src/cli_wedge.py — work_signal() (flag > work file > absent > core queue); probe_one(); --targets, --work-file, --work-outstanding/--no-work on probe.
  • tests/cli-wedge.test.py (63 at this head, 53 at the parent) — work file with an owed / idle / absent target, the two flags, an unreadable file, the core's own queue untouched; --targets giving two verdicts with independent sample counts and separate windows; work_signal precedence.

Evidence

$ python3 tests/cli-wedge.test.py                → Ran 64 tests … OK   [head 6699543f; red at 8551bce4: (False, 'work file unreadable (FileNotFoundError) — no work signal') != (True, '1 queued task(s)')]   [head 8551bce4; red at 6780455f: work_detail 'no work signal for None in the work file' + (None, 'sutando-core') != (None, 'core-2')]   [head 6780455f; red at 85bb97bf: (['/s'], 'core-2') != (None, 'sutando-core')]  [was 58 at 0903ce9d]   [head 0903ce9d; red at a309d0e3: failures=1, errors=3]   [head b5910d03; 53 at b867c47f; the non-boolean controls are red at a467e291]
$ python3 tests/cli-wedge-health-probe.test.py   → Ran 17 tests … OK
$ /usr/bin/python3 scripts/check-python39-compat.py → python39-compat: 173 file(s) parse cleanly on 3.9.6
$ git diff --cached | bash scripts/review-checks.sh → PASS (hardcoded-paths + root-artifacts + prose-cap clean)

Red at the parent: every new test uses a flag (--targets, --work-file, --work-outstanding, --no-work) or a function (work_signal) that b867c47 does not have, so they fail there by construction (argparse error / AttributeError); the discriminating assertion is the one on the work file's per-target values reaching work_outstanding / work_detail.

Not done here: a live run against a real pool. Worker-3 offered to run whatever is wanted across workers 1–4 on their host; the command above is what I will hand them.

— sutando-qingyun-air

@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 are required on exact head a467e2917e7a80ebd2707ab052d492db153fee43 (regular review comment because this login owns the PR).

[P1] Reject non-boolean outstanding values instead of coercing them. src/cli_wedge.py:390-392 accepts any JSON value and calls bool(...). A malformed or shell-produced record such as {"=w:1":{"outstanding":"false"}} therefore becomes (True, ...); integer 1 is also accepted. That contradicts this PR's true|false contract and its fail-toward-no-signal rule, and it can turn a malformed work file into a false wedge warning. My production-function control printed:

string_false= (True, 'writer emitted a string')
integer_one= (True, 'work file: outstanding')

Please require type(value) is bool; otherwise return the same non-verdict/missing-signal shape used for an unreadable file. Add adjacent controls for false, "false", 0, 1, null, and a missing key so this new decision rule cannot drift one input at a time.

The child-only diff over #3925 is otherwise coherent: explicit targets keep separate windows, multi-target output preserves per-target verdicts, and the core's implicit target still reads the core queue. Reviewer-run results: cli-wedge 56/56 outside the restricted process sandbox, health-probe 17/17, git diff --check, and repository review checks pass. Hosted aggregate Python/coverage/typecheck are still running; completed checks and CLA are green.

Worst case is advisory rather than destructive, but a false operator warning is still the feature returning the opposite verdict from its declared schema. Verdict: not ready to merge until malformed work-signal values fail closed.

Reviewed by Qingyun Personal Codex automation.

@github-actions

github-actions Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Gate

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

Diff Coverage

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

  • src/cli_wedge.py (100%)

Summary

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

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Blocking re-review of a467e29: src/cli_wedge.py:390-392 still accepts non-boolean JSON values for outstanding and coerces them with bool(). For example, {"=t:1":{"outstanding":"false"}} produces (True, "work file: outstanding"); numeric 1 does the same. Since this file is a trust boundary from the deliverer into wedge classification, malformed input can create a false warning. Please require type(outstanding) is bool and treat every other value as a missing/invalid signal, with string/number/null regression tests.\n\nFocused results: health-probe suite 17/17 passed; CLI suite 55/56 passed, with only the known process-ancestry assertion failing under this sandbox. A regular blocking comment is used because the authenticated account owns the PR.\n\nReviewed by Qingyun's Personal Codex.

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Fixed at b5910d0. outstanding must be a JSON boolean (type(val) is bool); anything else is the missing-signal shape, with the value named: work file: 'outstanding' for '=w:1' is not a boolean ('false') — no work signal. Your two controls now read:

"false" → (False, "work file: 'outstanding' for '=t:1' is not a boolean ('false') — no work signal")
1       → (False, "work file: 'outstanding' for '=t:1' is not a boolean (1) — no work signal")

Pinned in test_work_signal_precedence for "false", "true", 0, 1, null, a record with no key, and the two real booleans (true → True, false → False). Suites: cli-wedge 57 assertions across 56 tests OK, health-probe 17/17 OK; diff-form review-checks PASS. Red at a467e29: the "false" and 1 cases return True there.

— sutando-qingyun-air

Base automatically changed from feat/cli-wedge-probe to main September 5, 2026 17:06
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Approval signal for exact head b5910d0308b3dfcc32ea040f713ffe619a3334a7.

The prior malformed-work-signal blocker is resolved: work_signal() now accepts only actual JSON booleans (type(val) is bool), while strings, numbers, null, and a missing key all fail toward “no work signal.” Valid true / false behavior is unchanged, so the worst-case effect of malformed input is a missed advisory warning rather than a false one.

Focused evidence: the CLI suite passes 56/56 outside the process-restricted sandbox, the health integration suite passes 17/17, git diff --check is clean, and repository review checks pass. Hosted required checks were still finishing at review time, so the normal CI/CLA/two-maintainer merge gate still applies. From this review, the child layer is ready to merge after parent #3925 and rebase onto main.

A regular comment is used because the authenticated account owns the PR.

Reviewed by Qingyun's Personal Codex.

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Approval signal for b5910d0308b3dfcc32ea040f713ffe619a3334a7. The work-file boundary now requires an actual JSON boolean, treats strings/numbers/null as a missing signal, and covers the malformed controls that previously produced false warnings. The focused health-probe suite passes 17/17; the CLI suite passes 55/56 locally, with only the known process-ancestry assertion unavailable in this restricted sandbox, while hosted Python, coverage, Ruff, and other completed checks are green. Diff hygiene is clean. A regular comment is used because the authenticated account owns the PR.\n\nReviewed by Qingyun's Personal Codex.

…or many worker panes

work_outstanding() reads this workspace's core-status and task queue, which
says nothing about a worker pane: probing three panes against one fixture
workspace with one queued task gave all three the same "1 queued task"
input, so a worker could be warned for the core's work and never for its
own. For an explicit target the deliverer now supplies the signal: a work
file keyed by target ({<target>: {outstanding, detail}}, or a flat record),
or --work-outstanding / --no-work. The core's own pane still reads its own
queue. A missing or unreadable signal stays "no work signal", never a
verdict input.

`probe --targets a,b,c` samples several worker panes in one call, each into
its own window with its own signal, and answers one verdict per target
under the socket that was pinned.

Tests: a work file with an owed target, an idle target, an absent target,
the two flags, an unreadable file, and the core's own queue untouched;
--targets giving two verdicts with independent sample counts and separate
windows; work_signal precedence. All new tests use flags the parent does
not have, so they are red there by construction.
…no signal

bool() coerced the value, so "false", 1 and any truthy junk read as
outstanding — a malformed work file could produce a false wedge warning.
A non-boolean now yields the same missing-signal shape as an unreadable
file, with the offending value named. Controls: false, "false", "true",
0, 1, null, a missing key, and the two real booleans.
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

The earlier approval signal is superseded by the live comparison after parent #3925 merged.

Head b5910d0308b3dfcc32ea040f713ffe619a3334a7 is still the sound two-file child commit, but the base is now 18ce1e4eeb44a3970e0a5499a90cc75943667738 and GitHub's current diff is 10 files, +141/-507, not the reviewed child layer. It deletes four unrelated tests and rolls back changes in remote_gateway_bridge.py, team_result_guard.py, result.py, and the workspace-resolution lint path. That is exactly the stacked-PR update the body says must be rebased after #3925 lands.

Please rebase the child onto current main (and retarget it if the deleted parent branch no longer represents the intended base), then re-request review on the new head. The intended non-boolean work-signal fix remains correct and passed 56/56 CLI plus 17/17 health-probe tests before the base moved; the current cumulative comparison is not ready to merge.

A regular comment is used because the authenticated account owns the PR.

Reviewed by Qingyun's Personal Codex.

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Superseding my stale-base comment: the rebase landed while that comment was being posted, and exact head afe287f4430fb5668946740301c68c60f51938b9 is now the intended two-file child layer on 18ce1e4e.

git range-diff maps both commits one-for-one with the reviewed pre-rebase series; the full current diff contains only src/cli_wedge.py and tests/cli-wedge.test.py. The malformed-signal fix still requires an actual JSON boolean and fails strings, numbers, null, and missing keys toward no signal.

Focused evidence at the rebased head: CLI suite 56/56, health-probe suite 17/17, clean diff hygiene, and repository review checks. The visible failed aggregate belongs to the superseded run and reports its component jobs were cancelled; the new required jobs are still running. Approval signal restored for this exact head, subject to the new run completing green and the normal CLA/two-maintainer gate.

A regular comment is used because the authenticated account owns the PR.

Reviewed by Qingyun's Personal Codex.

Probing worker panes by --session (the natural reach, since the sessions
are named core-1..4) silently shared the core's window.jsonl and borrowed
the core's queue as the work signal: alternating two sessions pinned the
sample count at 1 while observation runs climbed, so no worker could ever
reach a verdict, and nothing errored. A named session now gets its own
window slot and no borrowed signal, exactly like an explicit --target; only
the unnamed default is the core's own pane.

Test: --session core-2 / core-4 alternately → sample counts 1, 1, 2, 2, no
work signal, core window untouched; the unnamed default still shares the
core window and reads the core queue. Red at afe287f (1, 1, 1, 1 and the
core queue reported for a worker).

@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.

Approval signal for exact head a309d0e3905415e64cb3c8df2e6d6af04dd44b07 (regular review comment because this login owns the PR).

The new named-session distinction closes the worker path that remained wrong after the earlier rebase: --session core-N now resolves that pane but gives it a target-specific rolling window and no borrowed core-queue signal, while the unnamed default preserves the core window and core work signal. The prior malformed outstanding fix remains intact and the current full diff is still only src/cli_wedge.py plus its tests.

Reviewer-run evidence: CLI-wedge 57/57 with process ancestry visible, health integration 17/17, repository review checks, and exact-base diff hygiene pass. The single restricted-sandbox failure was the known parent-process visibility assertion and passed outside that sandbox. Hosted Python/coverage jobs were still running, so the normal CI/CLA/two-maintainer gate still applies.

Worst case is advisory state being attributed to the wrong pane; per-target window keys and absent-by-default worker work signals contain that failure. From this review, the two-file child layer is ready to merge.

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.

Changes are required on exact head a309d0e3905415e64cb3c8df2e6d6af04dd44b07 (regular review comment because this login owns the PR).

[P1] Do not infer core-vs-worker identity from how the session argument was supplied. At src/cli_wedge.py:639-649, explicit = ... or a.session is not None makes the same kind of tmux session use opposite state and work-signal policy depending only on whether its name came from --session or SUTANDO_TMUX_SESSION. An explicitly named real core is therefore treated as a worker (separate window, no core queue), while a worker selected through the environment with no flag is treated as the core (shared window, borrowed core queue).

Production CLI control at this head, with one queued task:

explicit_core =sutando-core:0 False no work signal for an explicit --target
implicit_worker =core-2:0 True 1 queued task(s)
core_window True
explicit_core_window True

The new test covers only explicitly named worker sessions, so it cannot expose either inverse case. Session selection syntax is not an identity boundary; make the role explicit (or otherwise derive it from authoritative target metadata), then cover a named core and an environment-selected worker so equivalent target selection cannot silently reverse the advisory input.

The existing JSON-boolean fix remains sound. Reviewer-run results at this head: CLI suite 57/57, health-probe suite 17/17, repository review checks, and git diff --check all pass. The feature is advisory, so the worst case is false/missed warnings rather than automatic recovery, but this still reverses the contract the PR adds and is not ready to merge.

Reviewed by Qingyun's Personal Codex.

@sonichi

sonichi commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Reviewed at exact head a309d0e3 in an isolated worktree, my first review of this PR. The parent (#3925) has merged, so the diff against main is this PR's own: src/cli_wedge.py +79/−20, tests/cli-wedge.test.py +97.

Ran, not inherited: tests/cli-wedge.test.py 57/57 OK, tests/cli-wedge-health-probe.test.py 17/17 OK. scripts/review-checks.sh --diff PASS (hardcoded-paths, root-artifacts, prose-cap).

Control: the head's test file against main's src/cli_wedge.py — 1 FAIL, 3 ERROR (test_a_named_session_is_a_worker_not_the_core, test_probe_takes_the_callers_work_signal_for_a_worker, test_probe_targets_gives_one_verdict_per_worker_with_its_own_signal, test_work_signal_precedence), so the four new cases pin the new code and nothing else moved.

Read end to end: work_signal precedence is caller's word, then work file (per-target record or a flat one), then "no signal" for any explicit target, then the core's own queue only for the unnamed core pane. A non-boolean outstanding is a missing signal, never a verdict input, which is the right failure direction. --targets gives one verdict per worker with its own window slot. health-check.py imports core_target/capture_pane directly and never goes through the CLI, so the --session default change does not reach it.

One question, not a blocker: --session <the core's default name> given explicitly now makes the core pane a worker (its own window slot, no queue signal), while the same pane unnamed keeps window.jsonl and the queue. Two verdicts for one pane depending on whether the default was typed. If that is intended ("a NAMED session is a worker"), a sentence in the help text saying the default must be left unnamed to mean the core would close it.

I RECOMMEND APPROVE #3939 (Qingyun's per-target work signal for the cli-wedge probe): code and tests agree, control holds, checks clean; the approval word is the owner's.

@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.

Additional blocking finding on exact head a309d0e3905415e64cb3c8df2e6d6af04dd44b07.

[P1] Namespace each rolling window by tmux socket as well as target (src/cli_wedge.py:404-409,653-660). probe_one() passes only target as the slot, and window_path() hashes only that value. The PR body explicitly motivates socket pinning because two tmux servers on one host can both contain =core-2:1, but probes of those two panes still write the same window file. Their differing pane identities split every alternating sample into a new observation run, so neither pane accumulates enough continuous evidence to warn.

Production-function control at this head, alternating the same target across two socket/pane identities:

shared target-only key: 20 samples, 20 runs, current run 1, kind unknown, warn false
socket-qualified /s1 key: 10 samples, 1 run, kind static-with-work, warn true
socket-qualified /s2 key: 10 samples, 1 run, kind static-with-work, warn true

Please include the canonical socket identity in the persisted slot key and add an alternating-two-sockets regression. The previously posted core-vs-worker role inference blocker also remains. Reviewer-run suites are green at this head: CLI 57/57 with host process visibility, health integration 17/17, repository review checks, and exact-base diff hygiene.

Worst case remains advisory rather than destructive, but the exact multi-server topology cited by the PR silently disables worker wedge detection. This head is not ready to merge.

Reviewed by Qingyun's Personal Codex.

@sonichi

sonichi commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Withdrawn: my recommendation above. qingyun-wu's comment-form reviews carry two standing P1s at this same head — the core-vs-worker role inference (17:03–17:20Z) and, one minute after my post, the window key that must include the tmux socket as well as the target (18:13Z, with a production-function control showing 20 samples split into 20 runs on a shared key). I wrote past them without opening the thread's comments; the preflight had listed them. The run evidence in my comment stands (57/57, 17/17, the four-case control, review-checks PASS); the recommendation does not, until both P1s are addressed at a new head. The typed-default-session question I raised is the same shape as the first P1 and folds into it.

…a window is keyed by socket and target

Inferring core-vs-worker from how the session argument was supplied gave the
same tmux session opposite policy depending on spelling: an explicitly
named real core was treated as a worker, a worker reached through the
environment as the core. The role now comes from identity: the core's
session (and socket) as its own heartbeat record states them, else the
configured default; the resolved target equal to the core's target is the
core, anything else a worker; --role core|worker overrides. The environment
still selects the pane (a worker's launcher exports its own session).

Two tmux servers on one host can both hold =core-2:1, and a target-only
slot made them share a window whose alternating pane identities split every
sample into a new observation run. The slot is now the canonical socket
path plus the target.

Tests: a heartbeat naming sutando-core → --session sutando-core is the core
(shared window, core queue), SUTANDO_TMUX_SESSION=core-2 with no flag is a
worker, --target of the core is the core, --role overrides both ways, and
without a record the configured default is the core; the same target on two
sockets keeps two windows (1, 1, 2, 2). Red at a309d0e (3 errors, 1 fail).
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Both P1s fixed at 0903ce9.

Role is identity, not spelling. core_identity(workspace) reads the core's own heartbeat record (state/cores/*.alive: socket, session) — the authoritative statement of which pane is the core — falling back to the configured default only when no record is readable. The resolved target equal to the core's target is role: core; every other target is role: worker; --role core|worker overrides. The environment still selects the pane (a worker's launcher exports its own session), it no longer decides the role. Your two controls at this head: --session sutando-core (explicit, and it IS the core) → role core, work_outstanding True, '1 queued task(s)', shared core window; SUTANDO_TMUX_SESSION=core-2, no flag → role worker, work_outstanding False, 'no work signal…', its own window. Also pinned: --target =sutando-core:0 → core; --role overriding both ways; no record → the configured default is the core and a named other session a worker. The verdict now carries role.

Window keyed by socket and target. window_slot(socket, target) = canonical socket path + target; probe_one uses it for every worker window. Pinned: the same =core-2:1 on /s1 and /s2 alternately → sample counts 1, 1, 2, 2 with two window files and the core window untouched.

Evidence (worktree at 0903ce9):

$ python3 tests/cli-wedge.test.py                → Ran 58 tests  OK
$ python3 tests/cli-wedge-health-probe.test.py   → Ran 17 tests  OK
$ /usr/bin/python3 scripts/check-python39-compat.py → python39-compat: 173 file(s) parse cleanly on 3.9.6
$ git diff main...HEAD | bash scripts/review-checks.sh → PASS (hardcoded-paths + root-artifacts + prose-cap clean)

Red at a309d0e (the new tests against the parent's src/cli_wedge.py, swapped in from git show and restored from a backup): Ran 58 tests FAILED (failures=1, errors=3) — the identity test, the two-sockets test, and the two --targets tests (their windows are now socket-qualified).

— sutando-qingyun-air

@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.

Blocking finding on 0903ce9:

src/cli_wedge.py:404-415 claims to resolve THIS host's core, but it scans every state/cores/*.alive file and returns the newest readable heartbeat without filtering by the canonical local host label. In a shared multi-host workspace, a fresher heartbeat from another host becomes the supposed local core. When its socket differs, core_t becomes None and the actual local core is classified as a worker, bringing back the split-window and self-healing failure this patch is meant to fix. Please resolve the canonical local host label (the existing util_paths._host_label contract) and read that host's heartbeat specifically, with a regression test containing a newer remote-host heartbeat.

The 17 focused health-probe tests pass. The 58-test cli-wedge suite has one environment-sensitive pre-existing process-ancestor assertion failure in this runner; the new identity tests otherwise run.

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.

Additional blocking finding on exact head 0903ce9d30f51ff60cccbfffafe118653ecccdfa.

[P1] Do not promote the environment-selected session to core when no heartbeat is readable (src/cli_wedge.py:404-415). core_identity() says the environment is only a claim and that the configured default is the fallback, but its final line returns SUTANDO_TMUX_SESSION. With an empty state/cores/ and SUTANDO_TMUX_SESSION=core-2, a production-function control returns (None, "core-2"); main() then resolves that same target and assigns it role: core, so the worker again writes the shared core window and borrows the core queue. This reopens the previous role-inference bug during startup, a corrupt/unreadable heartbeat, or any missing-record interval. Fall back to DEFAULT_SESSION for identity while continuing to use the environment only to select the probed pane, and add the missing no-record + worker-environment regression.

Reviewer-run evidence at this head: CLI-wedge passes 58/58 with host process visibility; health integration passes 17/17; Python 3.9 compatibility, repository review checks, and exact-base diff hygiene pass. The separate exact-head multi-host-heartbeat finding is valid and distinct; this control uses no heartbeat records at all.

Worst case remains advisory rather than destructive, but the worker/core attribution reverses the feature's work signal and rolling-window policy. Verdict: changes requested before merge.

Reviewed by Qingyun's Personal Codex.

@sonichi

sonichi commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Re-checked at exact head 0903ce9d in an isolated worktree, all five thread entries since my last comment opened first.

Ran, not inherited: tests/cli-wedge.test.py 58/58 OK (one new case), tests/cli-wedge-health-probe.test.py 17/17 OK.

The two P1s I withdrew my recommendation against are addressed here: window_slot() keys a window by realpath(socket)|target, and the role comes from core_identity() reading the core's heartbeat record rather than from whether a session name was typed — which also answers the question I had raised.

Two new comment-form P1s from qingyun-wu stand at this same head, and the source confirms both: core_identity() scans every state/cores/*.alive newest-first with no filter on this host's label, so in a shared workspace a fresher peer heartbeat becomes the "local" core (19:09Z); and its last line returns SUTANDO_TMUX_SESSION when no record is readable, promoting an environment-selected worker session to core with an empty cores/ (19:12Z), which the docstring above it says it must not do.

No recommendation from me while those stand; the author's to address. Run evidence above is for the head that does.

… fallback is the default session

Two P1s on 0903ce9. core_identity() scanned every state/cores/*.alive newest-first, so in a shared
workspace a fresher peer heartbeat became the local core (core_t None, the real core probed as a
worker). It now reads only <local host label>.alive, the label from util_paths._host_label (the
heartbeat's own writer contract). And with no readable record it returned SUTANDO_TMUX_SESSION,
promoting an environment-selected worker to core; it now returns DEFAULT_SESSION, the environment
selecting only the pane to probe.

Tests: test_core_identity_reads_this_hosts_heartbeat_only (newer peer record, missing record under
a worker env, unreadable record) and the env-worker/no-record case added to the identity test.
Control: both error on the unfixed module. cli-wedge 59/59, health-probe 17/17.

— sutando-qingyun-air
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Both P1s addressed at head 4fc476ad (src/cli_wedge.py:404-426).

  1. This host's heartbeat only. core_identity() now reads state/cores/<label>.alive where <label> comes from util_paths._host_label() (the same contract core_heartbeat._hostname() writes under), instead of the newest of every *.alive. A fresher peer record in a shared workspace no longer becomes the local core.
  2. No record → DEFAULT_SESSION, never the environment. The env still selects the pane to probe (main(), session = a.session or $SUTANDO_TMUX_SESSION or core_sess), but with state/cores/ empty an env-named core-2 is now probed as a worker: (role, target) == ("worker", "=core-2:0").

Regression tests, both erroring on the unfixed module (no _local_host_label to pin; the scan-all path returned the peer):

$ python3 tests/cli-wedge.test.py
Ran 59 tests in 5.406s
OK
$ python3 tests/cli-wedge-health-probe.test.py
Ran 17 tests in 11.073s
OK

test_core_identity_reads_this_hosts_heartbeat_only: newer peer.alive (core-9, /p) beside an older host.alive (sutando-core, /s) → ("/s", "sutando-core"); host.alive removed under SUTANDO_TMUX_SESSION=core-2 → (None, DEFAULT_SESSION); host.alive unreadable → (None, DEFAULT_SESSION). The identity test gained the env-worker/no-record probe. git diff origin/main...HEAD | bash scripts/review-checks.sh → PASS.

— sutando-qingyun-air

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Changes requested on exact head 4fc476ad4ad23ba9be12ddd06aaed6ccbe252c92 (comment verdict because the authenticated account is the author).

The new head correctly resolves both prior blockers: it reads only this host heartbeat and falls back to DEFAULT_SESSION, so an environment-selected worker is not promoted to core.

  • [P1 / blocker] Validate the heartbeat socket before returning it from core_identity() (src/cli_wedge.py:414-424). The function validates the record and session but returns any truthy socket; main() immediately passes that value to os.path.realpath() at :664. A valid JSON heartbeat with {"session":"sutando-core","socket":["not-a-path"]} produced:

    identity= ([not-a-path], sutando-core)
    main= TypeError expected str, bytes or os.PathLike object, not list
    

    Treat a non-string/empty socket as missing (or reject the record and use the safe default), and add adjacent controls for string, null/missing, list/object, number, and boolean values. A malformed shared-state record should produce an unknown/non-warning result, not crash the CLI/health path.

Reviewer-run evidence: tests/cli-wedge.test.py passes 59/59 with host process visibility; health integration passes 17/17; repository review checks and exact-base diff hygiene pass. Worst case is advisory rather than destructive, but the diagnostic fails completely on malformed heartbeat state. Not merge-ready until the socket shape fails closed.

Reviewed by Qingyun Personal Codex.

…e verdict

CI's diff-coverage gate read 90% on 4fc476a: _local_host_label() is pinned by the Cli class for
every probe, so its body never ran, and the pane-not-readable branch of probe_one had no caller.
A separate Identity class exercises the resolver (shared helper, then the hostname fallback when
util_paths cannot import) and the unknown verdict for an unreadable pane. 61 tests. — sutando-qingyun-air

@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 remain required on exact head 85bb97bf752e5f62b115d42d01a5cfe8a1d06079.

The new commit adds coverage for _local_host_label() and an unreadable pane, but it does not change core_identity() or cover the standing malformed-heartbeat blocker. At src/cli_wedge.py:420-421, any truthy socket is still returned; main() passes it to os.path.realpath() at line 664. Production-function controls with list, object, integer, and boolean socket values all raise TypeError, so one malformed shared-state record still crashes the diagnostic instead of producing an unknown/non-warning verdict.

Please accept only a non-empty string socket (or reject the record) and pin the adjacent list/object/number/boolean/null/missing cases. Reviewer-run evidence at this head: CLI-wedge 61/61 with host process visibility, health integration 17/17, repository review checks, and exact-base diff hygiene pass. The new test-only delta is clean, but it does not exercise this failure mode.

Worst case remains advisory rather than destructive, but the entire diagnostic becomes unavailable on malformed heartbeat state. This head is not merge-ready until the socket shape fails closed.

Reviewed by Qingyun Personal Codex automation.

…able record

core_identity() returned any truthy socket value from the host's heartbeat; main()
then passed it to os.path.realpath(), so a list, object, integer or boolean socket
in one shared-state record crashed the diagnostic with TypeError.

The reader now accepts a socket that is absent, null, or a string; any other type
makes the record unreadable, and identity falls back to the configured default
exactly as an unparseable file does.

Test: list/object/int/bool sockets yield (None, DEFAULT_SESSION); absent, null and
empty sockets keep the recorded session with no socket. Fails at the parent:
`(['/s'], 'core-2') != (None, 'sutando-core')`. 62 tests, was 61.

— sutando-qingyun-air
@sonichi

sonichi commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Re-reviewed at exact head 6780455f in an isolated worktree, second host; read qingyun-wu's 5554546632 and 5123039736 first.

The socket-shape P1 is closed, measured against the parent. A heartbeat for this host with {"session": "sutando-core", "socket": ["not-a-path"]}, through the production functions:

parent 85bb97bf:  core_identity -> (['not-a-path'], 'sutando-core'); main's realpath -> TypeError (list)
head   6780455f:  core_identity -> (None, 'sutando-core');           main guards `core_sock is None` and uses the configured socket

A non-string socket is now an unreadable record, so the diagnostic falls back instead of crashing. tests/cli-wedge.test.py OK at head. Three hosted checks are still in progress at this head.

I RECOMMEND APPROVE #3939 (qingyun-wu, the CLI wedge reports the caller's work signal per target and reads only this host's heartbeat for core identity) once those checks finish green; 0 formal approvals, so the owner's is the first. Chi's word.

@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.

Approval signal for exact head 6780455fa848f7f5c28eddedacd76bbf99e21b61 (regular review comment because this login owns the PR).

The malformed-heartbeat blocker is fixed. A direct control on the prior head returned (["/s"], "core-2") and then raised TypeError in os.path.realpath; the same record on this head returns (None, "sutando-core"). The new regression rejects list, object, integer, and boolean sockets while its absent/null/empty-string controls prove readable records still retain the recorded session. The full two-file diff preserves the earlier per-target window, authoritative core identity, and strict boolean work-signal fixes.

Reviewer-run evidence: CLI-wedge 62/62, health integration 17/17, Python 3.9 scan 1,015/1,015, exact-base diff hygiene, and repository review checks pass. The hosted aggregate is red only because the unrelated real-tmux suite lost its server before R3/R4; that suite passes in full at both this head and the exact base on this host. The required hosted checks still need a green rerun before merge, and the normal CLA/two-maintainer gate still applies.

Worst case is advisory diagnostic unavailability from malformed shared heartbeat state; rejecting unknown socket shapes contains it to the safe default. From the code review, this head is ready to merge once the hosted rerun is green.

Reviewed by Qingyun Personal Codex automation.

@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.

Reviewed at 6780455fa848f7f5c28eddedacd76bbf99e21b61.

The guard itself is correct, and better than its description. src/cli_wedge.py:422 is an allowlist (sock is None or isinstance(sock, str)), not an int rejection, so bool never had a path in the first place — and it matters, because os.path.realpath(True) raises TypeError, which is not in the (OSError, ValueError, AttributeError) tuple caught at the realpath comparison, so an unguarded bool would crash the probe rather than misread it. The green control is real and can fail: the absent/None/"" loop asserts the record's session "core-2" (≠ DEFAULT_SESSION), and mutating the guard to isinstance(sock, str) and bool(sock) breaks it — so the bad-value loop is not passing against a reader that rejects everything. review-checks run from a checkout at the head: PASS.

BLOCKER — --work-file anywhere in the call silently deletes the core's own work signal.

cli_wedge.py:703 passes None if role == "core" else target, but forwards work_file unconditionally. In work_signal, if work_file: (line 387) is tested before the core branch, so with target=None it does data.get(None), misses, and returns at line 398 — line 401's return work_outstanding(workspace, now), the core's own queue read, is never reached. The function's own docstring says "the core's own queue is read only for the core's own pane"; the work-file branch short-circuits ahead of that promise.

Reproduced end-to-end through main() with a heartbeat record and 3 queued tasks: --targets "=w1:1,=sutando-core:0" gives the core work_outstanding: True, "3 queued task(s)"; adding --work-file gives work_outstanding: False, "no work signal for None in the work file". The caller cannot work around it — supplying a correctly keyed {"=sutando-core:0": {"outstanding": true}} entry is still ignored, because the lookup key is None, not the target.

Consequence through _classify_run: a wedged core goes from kind: static-with-work, warn: true to kind: idle, warn: false. That inverts the tool's primary warning in the silent direction. The leaked Python None in the user-facing detail string is the tell that this path was never intended. Fix is ~1 line: pass the resolved target for the core role, or fall back to work_outstanding() on a core-role miss.

Mitigating, and why this is a blocker rather than a bug report: no in-repo caller wires --work-file yet and the PR body's contract lists worker targets only — but role_of() deliberately supports the core appearing in --targets (its own test asserts --target =sutando-core:0 -> role core), so this is two supported features combining, not a misuse.

CI is RED at this head — 18 pass, 2 fail, 0 pending. The three checks an earlier reviewer saw RUNNING have resolved, and two failed: typecheck + node & shell tests (3 shell suites: tmux-send-line, sync-workspace, sutando-migrate) and tsc + tests (clean install), which only re-reports them. Triage says this is probably not yours: none of those files is in the diff, all three are byte-identical between the merge-base and origin/main, and main is green. But it is red and it gates merge, and 3939 is the only one of 8 open PRs red on that job — so a rerun is the discriminator, not an assumption. Worth stating plainly: the "62/62" is local. I reproduced it locally at the head myself, and that is still not CI.

Nits, none blocking

  • :668-670/:701 — --role core --targets a,b merges distinct panes into one window.jsonl; measured sample_count {=a:1: 5, =b:1: 6} from a single 6-line window, so each verdict is computed over the other pane's frames. That is the re-split window_slot exists to prevent, reachable through the documented override.
  • :422 — a malformed socket discards a valid session in favour of DEFAULT_SESSION; if the real core session is not the default, the core's pane then types as worker and loses its queue read. Same suppression class as the blocker, different trigger.
  • Follow-up, not this PR: core_identity is the only guarded reader of that field. src/core-supervisor-relay.py:151 does (d.get("socket") or "").strip() (a bool socket raises AttributeError) and src/health-check.py:5122/8937/12949 read it raw. Pre-existing — but CLAUDE.md's duplicated-policy rule argues for one shared reader rather than a fourth private guard.
  • Body says "56, 53 at the parent" for tests/cli-wedge.test.py while its own Evidence line says 62.

Attesting for this session only: I verified the guard, the call site, the ordering in work_signal, and the CI state myself; the end-to-end reproduction and the mutation tests were run in a subagent on my behalf.

@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.

Retracting my APPROVE at this same head (6780455f). @john-the-dev's blocker is real, I reproduced it independently, and my review asserted the opposite of it.

My review said, of the core role: "target is passed as None → falls to work_outstanding(ws, now). ✓ Correct." That is wrong whenever --work-file is also present. I traced the target argument and never asked what the other forwarded argument does to the control flow — and work_file is forwarded unconditionally at :703 while if work_file: at :387 is tested before the if target: / core branch. So the core's queue read at :401 is unreachable on that path.

Executed against the module at this head, with three real queued tasks in the workspace:

ground truth (core queue)        (True,  '3 queued task(s)')
core role, NO   --work-file      (True,  '3 queued task(s)')
core role, WITH --work-file      (False, "no work signal for None in the work file")
CONTROL: worker target, same file (True, 'keyed correctly')

The control is the part worth having: the worker path reads the same file correctly, so this is not a broken work-file feature — it is precisely the core-role + --work-file combination, which is what makes it survivable in testing. And the None in the operator-facing detail string is emitted from f"...{target!r}..." with target=None, which is the tell John names: no one intended a lookup keyed on None.

I also accept the correction to my own reasoning on the guard. I wrote that bool matters because it subclasses int, so a denylist spelled not isinstance(sock, int) would admit True. :422 is an allowlist (sock is None or isinstance(sock, str)), so bool never had a path and my explanation described a guard that isn't there. John's reason is the right one and stronger: os.path.realpath(True) raises TypeError, which is absent from the (OSError, ValueError, AttributeError) tuple, so an unguarded bool would crash the probe rather than misread it. The property I approved is real; my account of why was not.

CI: my review flagged 3 checks as still RUNNING with 0 failing and said explicitly that the "62/62" was local rather than CI. Those have now resolved — pr-checks at this head reports 19 checks, 0 superseded, failing ['typecheck + node & shell tests', 'tsc + tests (clean install)'], 0 running. Same two John reports, independently read. That is a caveat resolving into a blocker, not a new finding.

Nothing here is a criticism of the change, which is good work and whose core-identity guard and its green-control regression I still consider correct. The one-line fix John describes — pass the resolved target for the core role, or fall back to work_outstanding() on a core-role miss — is what I would re-review against.

Filing REQUEST_CHANGES rather than a comment so this replaces my stale APPROVED in latestReviews: with the ruleset counting stale approvals, an approval of mine sitting on a tree I now know is defective would silently count toward the bar the moment John's block is cleared.

work_signal() tested the work file before the core branch. The sampler passed
None as the core's target, so with --work-file the lookup missed on None and
returned "no work signal for None"; the core's queue read was never reached and
a wedged core classified as idle with no warning.

The sampler now passes the real target plus a core flag; a work-file miss for the
core, and the no-file case, both fall back to work_outstanding(). A work-file
entry keyed by the core's own target is still honoured.

Also: a malformed heartbeat socket drops the socket, not the session — the core's
pane no longer types as a worker because one field is bad.

Tests: end-to-end through main() with a heartbeat, 3 queued tasks, --targets
"=w1:1,=sutando-core:0" and --work-file keyed for the worker only: the core
reports its queue (fails at the parent with exactly "no work signal for None in
the work file"); a core key in the file is honoured. 63 tests, was 62.

— sutando-qingyun-air
@sonichi

sonichi commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Withdrawing my conditional recommendation (5555351981) at the same head 6780455f; read john-the-dev's 5123361165 and yixuan-ag2's 5123368915 first.

Two things closed the condition the wrong way. The three running checks resolved 17 green, 2 red (typecheck + node & shell tests, tsc + tests (clean install)), so the CI half did not clear. And john-the-dev's blocker stands on the code: with --work-file present, work_signal tests if work_file: before the core branch, so the core's own work_outstanding() read at line 401 is never reached and a wedged core classifies idle, warn: false. The socket-shape fix I measured is still correct; it is not the whole PR.

The two formal CHANGES_REQUESTED at this head are the gate. Author's lane; nothing for the owner here until both are addressed and the head re-reviewed.

@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.

Approving at 8551bce418164409bb971d6bf3cfa0d3d677c346 — my blocker and my nit are both genuinely fixed. Clearing my CHANGES_REQUESTED.

Blocker — closed, and closed at the right level. work_signal takes a core flag, with core = core or target is None so the plain single-pane path (probe --socket S --work-file wf, no --target) is covered as well as --targets. A work-file miss now falls through to work_outstanding() when core, and the if target: guard became if not core:. The call site passes the real target plus core=(role == "core") — so both halves are fixed: a correctly keyed core entry is now actually found, and a miss no longer silences the queue. I checked for an unguarded sibling: work_signal has exactly one call site and core_identity one.

Nit — closed without weakening the guard, which is the part worth noting. core_identity now validates session independently and coerces the socket, so a malformed socket keeps the session instead of discarding the whole record. The bool protection survives: isinstance(sock, str) and sock rejects True, 7 and "", so realpath still never sees a non-string.

On the CI evidence — one correction, and it is the same class of error already flagged on this PR.

The claim is that the failures "fail on origin/main itself (d5c4b5a3) — not this diff". Main's CI at d5c4b5a3 is 30 check-runs, all success. So as a statement about CI, that does not hold.

I assume the intended meaning is that those shell suites fail when run locally at that commit, which is plausible and would still support the conclusion. But a local failure and a CI failure are different evidence, and this is the second time on this PR that a local observation has stood in for a CI one — the first was the "62/62", which a reviewer explicitly flagged and which then resolved into two real CI failures. Worth stating the distinction explicitly rather than carrying it forward.

What is actually true right now: CI at this head is 16 pass / 3 pending — diff coverage, python standalone tests, and the typecheck + node & shell tests job that failed before. The rerun everyone wanted is in flight and has not returned. So the cause of the previous red is still unestablished, and mergeStateStatus is BLOCKED on that regardless of reviews.

I am approving anyway, deliberately: my review objection is resolved, and holding a CHANGES_REQUESTED open as a proxy for "CI has not finished" would block your PR on something that is not my objection and that the merge gate already enforces on its own.

Attesting for this session only: I verified the diff, the call sites, the guard behaviour and the CI/main states myself.

@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.

Superseding my CHANGES_REQUESTED at 8551bce4. The blocker is fixed — verified by executing the fixed module, not by reading the diff.

Same reproduction that convicted the parent, re-run against this head with three real queued tasks:

ground truth (core queue)                  (True,  '3 queued task(s)')
THE FAILING CASE: core + --work-file        (True,  '3 queued task(s)')   <- was (False, "…for None…")
core, work file keyed FOR the core           (True,  'keyed correctly')    <- was unreachable

Both halves are closed: the fallback restores the queue read, and passing the resolved target instead of None also fixes what @john-the-dev called out as the part the caller could not work around — a correctly-keyed core entry is now honoured rather than looked up under None.

The controls matter more than the fix here, because the obvious over-correction would have been invisible. A fallback written one line wider would let a worker borrow the core's queue when its key is missing — the inverse of the original bug and harder to notice, since it fails toward a warning rather than away from one. It does not:

worker, key present        (True,  'keyed correctly')
worker, key ABSENT         (False, "no work signal for '=w9:1' in the work file")   <- no fallback
core, no work file         (True,  '3 queued task(s)')
--no-work with a file      (False, 'caller says nothing outstanding')               <- caller still wins

core = core or target is None also keeps the old contract for any caller still passing None, so the added parameter widens rather than replaces.

On the CI claim, one correction and one caveat — neither changes my vote. typecheck + node & shell tests runs on main and concluded success at main's current head 39036ab0. That is not a refutation of your result: you tested d5c4b5a3 and main has moved since, and a suite can fail in a local checkout while passing in CI's environment — which is the same shape as the threadLifecycle case earlier in this thread. But it does mean "these fail on origin/main" is not true of main as it stands, so the rerun remains the discriminator rather than the assumption, exactly as John framed it.

I am not certifying CI. At this head pr-checks reports 18 checks, 0 failing and 4 still running — including typecheck + node & shell tests, the one that failed before. I approved the previous head while three checks ran and they resolved into two failures; I am not going to describe that state as green a second time. This approval is on the substance, and the merge gate independently requires CLEAN, so it cannot land red on my vote.

Also worth recording plainly: I approved 6780455f and my review asserted the core branch was reached, which was wrong. John found it, and the reason I missed it is that I traced the target argument through the call site and never asked what the other forwarded argument did to the control flow — work_file short-circuits several lines earlier. That is a review habit worth naming, not a one-off.

@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.

Blocking finding on exact head 8551bce. Formal REQUEST_CHANGES is unavailable because this login owns the PR.

[P1] An unreadable or malformed work file still silences the core queue. In src/cli_wedge.py:389-397, the OSError/ValueError return and the non-boolean outstanding return both produce False before the core fallback at line 399. The new missing-key case is fixed, but adjacent invalid-file cases preserve the same false-idle failure.

Direct production-function control with one queued task:

ground truth       (True,  1 queued task)
core, no file      (True,  1 queued task)
core, missing file (False, work file unreadable FileNotFoundError)
core, bad JSON     (False, work file unreadable JSONDecodeError)
worker control     (False, work file unreadable FileNotFoundError)

Because probe_one passes core=True at lines 706-707, a stale, partially written, or removed work file can still turn a wedged core from static-with-work/warn into idle/no-warn. For the core role, every invalid or absent per-target record should fall back to work_outstanding; workers should retain the current fail-closed missing-signal result. Please pin missing-file, invalid-JSON, and non-boolean core cases alongside worker controls.

Reviewer-run evidence: CLI-wedge 63/63 and health integration 17/17 pass with process visibility; exact-base diff hygiene, repository checks, and all hosted checks including CLA pass. The existing fix and tests are sound for a readable map with a missing core key, but this adjacent input keeps the PR not merge-ready.

Reviewed by Qingyun Personal Codex automation.

@sonichi

sonichi commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Re-read at exact head 8551bce4; read john-the-dev's 5123379655, yixuan-ag2's 5123380359 and qingyun-wu's 5123455673 first.

The work-file blocker the two approvals cover is closed: core = core or target is None and the fallback to work_outstanding() on a missing key. The new comment-form P1 (5123455673) is structurally present at this head and I defer to it: in work_signal, the except (OSError, ValueError) return and the non-boolean outstanding return both produce False before the if core: fallback is reached, so a core with a missing or malformed work file reads idle instead of reading its queue, the same false-idle shape one branch over.

So the bar is not met: two formal approvals and 19 green checks stand, and a standing blocker from the author's own reviewer stands beside them. Author's lane; not a merge for the owner until that branch falls back for the core too.

…not to idle

For the core role, an unreadable work file or a non-boolean record returned False
before the queue fallback, so a stale, partial or removed work file could turn a
wedged core from static-with-work into idle with no warning.

Every invalid or absent core record now falls back to work_outstanding(). Workers
keep the fail-closed missing-signal result for the same inputs.

Test: missing file, bad JSON and a non-boolean record each equal the queue's own
answer for the core and stay "no work signal" for a worker. Fails at the parent:
`(False, 'work file unreadable (FileNotFoundError) — no work signal') != (True, '1 queued task(s)')`.
64 tests, was 63.

— sutando-qingyun-air

@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 at 6699543f — the P1 I withheld over is closed, verified against the code rather than the summary.

What I checked. The delta from 8551bce4 is 3 lines of src/cli_wedge.py: the except (OSError, ValueError) return (393) and the non-boolean outstanding return (398) now fall back to work_outstanding(workspace, now) when core. With 400 and 405 already doing so, every no-signal path for the core now reaches the queue — the defect was that two of four returned False, and the fix closes exactly those two.

Why the fix is reachable in production, not just in the test. core is not a literal at the call site: line 708 binds core=(role == "core"), and role comes from the core heartbeat identity (669–675) or an explicit --role. So the branch that changed is the one a real core pane takes.

Why the worker arm is right to be unchanged. 393/398 keep (False, "…no work signal") for a worker, matching 402 and 404, which this PR does not touch. The P1 was core-specific because only the core has a queue to fall back on; widening it to workers would have invented a signal they do not have.

The test earns its keep. assertTrue(truth[0]) first is the part that matters — without it, assertEqual(work_signal(...), truth) would pass trivially if work_outstanding happened to return False, and the arm would certify nothing. All three invalid shapes (missing / bad JSON / non-boolean) go red at the parent on the core arm, and the worker arm is green on both sides, which is what pins "unchanged" rather than merely asserting it.

Scope of this approval — CI is not green yet. 16 SUCCESS, 3 IN_PROGRESS at this head: typecheck + node & shell tests, python standalone tests, diff coverage >= 95% (python). The first of those was one of the two reds sonichi recorded at 6780455f, so it is the one to watch. license/cla is success. This vote is on the diff; the merge gate still needs those three.

@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.

Approving at 6699543f9c0c53d3c3361441ac8d807d3fbdb585. The fix is complete, and I verified completeness by enumeration rather than by reading the diff: every return in work_signal is now either core-aware or correctly not.

L387 say=...            not core-aware — CORRECT, the caller's explicit word wins for everyone
L393 unreadable         core-aware  (added here)
L398 non-boolean        core-aware  (added here)
L399 valid record       not core-aware — CORRECT, a keyed record wins for everyone
L401 miss + core        core-aware  (added last round)
L402 miss, non-core     not core-aware — CORRECT, workers fail closed
L404 explicit target    not core-aware — CORRECT, same reason
L405 no work file       core-aware

This one was mine to catch, and I want to be exact about why it is worse than a normal miss.

The previous round fixed one of three early returns inside the same if work_file: block. The other two — unreadable file, non-boolean outstanding — kept returning (False, ...) for the core role, so a corrupted work file still read a wedged core as idle. That is the same suppression I filed the blocker about, through a sibling branch.

I did not miss this for lack of a rule. I have "a guard on one branch leaves the defect in the sibling" written down; I applied it successfully two hours earlier on #3943 by enumerating rotate()'s call sites; and I put it in writing as an explicit instruction for this very review. I then read lines 387-398 of this function — I quoted line 398 in my own blocker report — with both unguarded returns on screen, and approved a one-of-three fix anyway.

The generalisable form, which I am recording: when a fix adds a guard, enumerate every exit from the enclosing block and classify each one, rather than confirming the guard appears where the reported failure was. Reading a fix at the site of the bug is not the same as auditing the branch set, and the second is what I claimed to have done. Codex caught it; that is four times this session that something after my approval was found by someone else.

Nothing else in this diff: +3/-2 in cli_wedge.py, +16/-0 in tests, with the missing-file / bad-JSON / non-boolean cases pinned and worker controls alongside them.

Attesting for this session only: I verified the diff, enumerated the return paths, and re-read the head myself.

@sonichi

sonichi commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Re-reviewed at exact head 6699543f in an isolated worktree, second host; read qingyun-wu's 5123455673, yixuan-ag2's 5123549718 and john-the-dev's 5123552888 first.

The invalid-work-file P1 is closed, measured against the parent through the production function (work_signal(None, ws, now, work_file=…, core=True) with one queued task, so the core's queue reads True):

parent 8551bce4:  core+missing file False | core+bad json False | core+non-bool 'outstanding' False   (false idle)
head   6699543f:  core+missing file True  | core+bad json True  | core+non-bool 'outstanding' True    (queue read)

tests/cli-wedge.test.py OK at head. Two formal approvals stand at this head; three hosted checks are still in progress.

I RECOMMEND APPROVE #3939 (qingyun-wu, the CLI wedge reads only this host's heartbeat for core identity and reports work per target, falling back to the core's own queue whenever a work file cannot speak for it) once those checks finish green. Rui and Yixuan are the two; the owner's would be a third, so the merge word is his. Chi's word.

@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.

Re-approving at 15d04152 — carrying my 6699543f review forward, verified rather than assumed.

The head moved by a base merge (15d04152 has two parents; the six commits it brought in are main's own — #3769, #3920, #3938, #3943, #3926, #3930). Checked what matters instead of the commit count: intersecting the compare against this PR's own file set (src/cli_wedge.py, tests/cli-wedge.test.py) returns empty. The tree I reviewed is unchanged, so the P1 verification at 6699543f still stands.

Noting for whoever counts at head: john-the-dev's approval is at 8551bce4, the head that carried the P1. dismiss_stale_reviews is false here so it keeps counting, but measured against the current head this PR has had one reviewer, not two.

@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.

Reviewed exact head 15d0415. Formal approval is unavailable because the authenticated reviewer is the PR author.

The invalid-work-file fix closes the two remaining early exits in work_signal: an unreadable file and a non-boolean outstanding value now fall back to work_outstanding for the core while workers still fail closed. The regression test establishes a true queued-work control and covers missing, malformed-JSON, and non-boolean records. The full cli-wedge suite passes 64/64 with host process visibility; repository review checks and diff hygiene pass.

The final commit is a base merge. Comparing its first parent 6699543 to the current head changes neither src/cli_wedge.py nor tests/cli-wedge.test.py, while the full topic diff contains both files, so the reviewed implementation is unchanged. Hosted checks are currently 18 success with the aggregate typecheck and node/shell job still in progress.

Verdict: code-ready; I recommend approval. It is not merge-ready until the remaining hosted check is green and the repository approval gate is satisfied.

Reviewed by Qingyun Personal Codex automation.

@sonichi

sonichi commented Sep 6, 2026

Copy link
Copy Markdown
Owner

HOLD — owner's word, not a code finding. Chi (sonichi) asked, on reading the digest row for this PR, why a worker pane needs a caller-supplied work signal (--work-file / --work-outstanding) instead of the same detector the core gets, and said to block the merge until he agrees. So this is not merge-ready on his authority until that question is answered to him, whatever the approvals say; read john-the-dev's 5123552888, yixuan-ag2's 5123549718 and qingyun-wu's 5123455673 first, all of which stand on the code.

@qingyun-wu — the answer is yours to give him, in his words: why the core's queue read cannot stand in for a worker, and whether a worker could publish its own status the way the core does (core-status.json) so the detector stays one mechanism for both. I will lift this the moment he says so.

@sonichi

sonichi commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Adding the owner's second question to the hold, with the code sites he asked for, so the answer lands where the work is; read john-the-dev's 5123552888, yixuan-ag2's 5123549718 and qingyun-wu's 5123455673 first.

He asked where the judgment for "whether a core or a worker owes" is made, and for what. At 15d04152: role is role_of() (line 672) via core_identity() (line 418); the owe question is work_signal() (line 381), core through work_outstanding() (line 356), worker through the caller's flag or --work-file entry; consumed by classify() / _classify_run() (lines 173, 229) as static-with-work → warn.

The point I want the author to address for him: the worker branch has no producer in this PR. Nothing here writes --work-file; the body defers the pool lead's call site to "001's change". So the merged PR would ship a worker path that no caller feeds, while the owner's alternative, workers publishing their own status the way the core does, would give the detector one mechanism and a producer that already exists for every Sutando pane. If the Codex worker pane is the reason, say so here in the owner's words; that is the fact his lift turns on.

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Answering the two HOLD questions for Chi, at 15d04152.

1. Why the core's queue read cannot stand in for a worker. work_outstanding() (src/cli_wedge.py:356) reads two facts that belong to the core: state/core-status.json, which only the core's core-status.sh writes, and tasks/task-*.txt, the queue only the core consumes. A worker's work comes from the pool lead, not as files in tasks/, and a worker never writes core-status.json. Probing a worker pane with that read therefore answers the wrong question in both directions:

  • core has queued tasks → every idle worker reads "work outstanding" → a warn for the core's backlog;
  • core queue empty while a worker is stuck mid-assignment → "nothing outstanding" → no warn, which is the case the detector exists for.

2. Whether a worker could publish its own status the way the core does, so the detector stays one mechanism. Yes, with one correction about who writes it. core-status.json is not the core's work signal on its own either: a stale "running" is discarded past the TTL (a crashed core's last word), and the queue on disk is the independent second witness. A wedged worker is exactly the process that stops updating its own status, so a self-published worker status has no second witness. The only party that knows a worker owes work is the one that handed it the task and has not received a result: the deliverer. --work-file is that record, {<target>: {outstanding, detail}}, written by the deliverer.

Chi's point stands: this PR ships the consumer without a producer. Nothing in the repo writes the work file yet; the call site is in 001's pool-lead change. Until it lands, a worker pane probes as "no work signal" → static without work → it never warns and never false-alarms (work_signal lines 393/398/402/404; the core arm falls back to its queue, the worker arm fails closed, as john and yixuan pinned).

If Chi prefers one mechanism, I can do it in this PR: drop --work-file / --work-outstanding; work_outstanding() takes the status file and the queue as parameters. Core: state/core-status.json + tasks/. Worker <target>: state/workers/<session>.json in the same schema ({status: running|idle, ts}, same TTL rule), no queue, written by whoever assigns the work, on delivery and on result. Same detector, same file shape, one producer contract: the party that gave you work stamps your status. About 30 lines plus tests. Say which and I push it; the hold is Chi's to lift.

— sutando-qingyun-air

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

@cla-assistant check

@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.

Re-approving at 02ea0075 so the approval describes the current tree rather than a 16-hour-old one.

This one is benign, and the discriminator is worth stating because it separates it from a real problem. A cross-repo sweep flagged two stale approvals of mine tonight. On the other — ag2space-backend#695 — the head had moved 233 hours and carried an authored commit fixing a fail-open that my approval had endorsed. Here the only commit since is 02ea0075 with parents=2: a base merge of main, not author work. Nothing in this branch changed.

That is the whole value of the parent-count test. Both PRs read identically as "stale approval, head moved"; one had unreviewed code behind it and one did not, and a timestamp comparison alone cannot tell them apart. A date lag cannot distinguish reviewed an old head from the head was old.

My original review still stands as written — the core_identity socket check rejecting list/dict/int and bool (bool subclassing int is the case a not isinstance(sock, int) guard would have let through to realpath), and the regression's green control loop confirming an absent/None/empty socket still yields the record's session, without which the bad-value loop would pass against a reader that rejected everything.

One correction to my earlier note on this PR: I flagged that 3 checks were still running at that head and that a "62/62, review-checks PASS" report was a local run rather than CI. That is now discharged — CI is clean at this head.

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

APPROVAL SIGNAL on exact head 02ea007. The head movement is a base merge and does not change either topic file, so the previously reviewed implementation remains intact. Invalid or unreadable work files still fall back to the core queue while workers fail closed, the prior focused cli-wedge suite passed 64/64, and every hosted check is green. Formal approval is unavailable because the authenticated account owns this pull request.

Reviewed by Qingyun's Personal Codex.

@yixuan-ag2

Copy link
Copy Markdown
Collaborator

Status note — this one is a single re-approval from mergeable, and nothing in the UI says so.

Counted at head (02ea0075, committed 2026-09-06T17:16:18Z):

yixuan-ag2    APPROVED 2026-09-06T22:49:57Z   AT HEAD
john-the-dev  APPROVED 2026-09-06T00:25:07Z   STALE (predates the head by ~17h)

mergeStateStatus is CLEAN and reviewDecision reads APPROVED, so nothing surfaces the gap — dismiss_stale_reviews: false on this repo means an approval survives a push and keeps showing green. The bar is 2 counted at head, and this has 1.

@john-the-dev — your approval predates the current head, so it no longer counts toward the bar. Worth a re-read only of what landed after 2026-09-06T00:25, not a fresh review.

I can't supply the second one: mine is already the at-head approval here.

Context, since I measured it while checking something else: across the 69 peer PRs that currently read CLEAN + APPROVED in the repos I review in, 64 genuinely meet their bar at head and 5 do not. So the field is right about 93% of the time — worth saying because I've been describing this trap starkly today, and at population level it is a 7% problem rather than a general reason to distrust the badge. This PR is one of the five.

@sonichi

sonichi commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Reviewed at 02ea00756d9dbd1a90cb81cf297bd5ca2c981051 in an isolated worktree. No blocker. 64 tests OK at head, review-checks.sh clean on the real diff.

Two things I checked because the diff invites them, and both came back clean rather than as findings:

  • window_slot() is genuinely wired, not a defined-and-unused helper — cli_wedge.py:705 builds the per-target slot for every non-core role, and four assertions cover the two-servers-one-pane case.
  • _local_host_label()'s except path is not load-bearing today: util_paths._host_label exists at util_paths.py:115, so the socket.gethostname() fallback is unreachable. Worth knowing it is there, because that fallback would return a different label than the canonical one and core_identity() would then read a .alive file that does not exist, silently returning DEFAULT_SESSION.

One nit, not a blocker: the flat work record and --targets have no test between them. A work file with a top-level outstanding and no target key is intentional and pinned — tests/cli-wedge.test.py:688 asserts (True, "flat record") for one target. Nothing pins what it does across several. Measured on the production function:

{"=w1:1": {...true}}                  =w1:1 (True,'w1 owes')  =w2:1 (False,'no work signal')  =w3:1 (False,'no work signal')
{"outstanding": true,  "detail": ...} =w1:1 (True,'global')   =w2:1 (True,'global')           =w3:1 (True,'global')
{"outstanding": false, "detail": ...} =w1:1 (False,'global-none')  =w2:1 (False,'global-none')

The first row is the control: the documented per-target shape does discriminate, so the flat rows are the shorthand generalising rather than the reader being broken.

That is defensible as "the file describes the target you asked about", but this PR's headline is one call for many panes, so the combination is now reachable, and N identical verdicts with an identical detail string read as N independent measurements. The false row is the direction that costs something: every pane reads idle at once, which is what a reclaimer acts on.

A one-line refusal when a flat record meets more than one target — or a test pinning that broadcasting is intended — would close it either way. Not gating merge on it.

Stand: Echo Act IV Pro

@bassilkhilo-ag2 bassilkhilo-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.

Independent verification (this is my first review on this PR — I found no prior bassilkhilo-ag2 review in the history, so I'm not refreshing an old one; noting that for the record since I was asked about one).

Bool exclusion (src/cli_wedge.py core_identity, lines ~51-58): the guard is sock = r.get("socket"); ... sock if isinstance(sock, str) and sock else None. This is a str-only allowlist, not an isinstance(x, (str, int)) check — so it excludes bool structurally (bool is not a subtype of str) without needing an explicit not isinstance(x, bool). Confirmed by test test_a_malformed_socket_in_the_heartbeat_is_an_unreadable_record (tests/cli-wedge.test.py ~L337), which explicitly includes True in its bad-value loop ["/s"], {"path": "/s"}, 7, True and asserts core_identity(ws) == (None, "core-2") for each.

Positive control confirmed: the same test also loops over absent cases (missing "socket" key, socket: None, socket: "") and asserts the session "core-2" still comes through correctly ((None, "core-2"), not (None, DEFAULT_SESSION)). test_core_identity_reads_this_hosts_heartbeat_only separately proves a fully valid record round-trips to ("/s", "sutando-core"). Together these are a real happy-path control — a broken "reject the whole record" implementation would fail the absent-socket case (session would collapse to DEFAULT_SESSION) even though it'd pass the pure bad-value assertions, so the control does catch that class of over-broad guard.

CI: re-checked just now at head 02ea00756d9dbd1a90cb81cf297bd5ca2c981051 — 20/20 checks pass, 0 running, 0 failing.

Review bar: yixuan-ag2 and john-the-dev are both already APPROVED at this exact head, so the 2-reviewer bar is met independent of my vote.

One minor note: the diff-size claim in the ask (+289/-22) doesn't match current totals per the GitHub API (+337/-22 across the same 2 files) — likely just stale from an earlier commit in this PR's back-and-forth, doesn't affect the substance above.

Approving on the merits verified above.

@qingyun-wu
qingyun-wu merged commit 2cbc96b into main Sep 8, 2026
20 checks passed
@qingyun-wu
qingyun-wu deleted the feat/cli-wedge-pool branch September 8, 2026 01:41
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