Skip to content

fix(gjc-runtime): fence continuation dispatch and repair the memory-guard split marker - #3604

Closed
twoimo wants to merge 10 commits into
Yeachan-Heo:devfrom
twoimo:feat/windows-psmux-authority-v4
Closed

fix(gjc-runtime): fence continuation dispatch and repair the memory-guard split marker#3604
twoimo wants to merge 10 commits into
Yeachan-Heo:devfrom
twoimo:feat/windows-psmux-authority-v4

Conversation

@twoimo

@twoimo twoimo commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Thank you for the detailed review on #3595. Both P1 blockers you identified are repaired in this successor, and I want to be explicit about the causality I found for each, since in both cases the defect was slightly different from what the failure name suggested.

P1 (1) — a public authority-changing operation dispatched no continuation argv

You were right that this was a real production gap, not a flaky test. The mechanism:

continueStalledGjcTeamWorkers writes the attempt reservation under the task-store lock, releases that lock, and only then revalidates continuation eligibility before send-keys. That leaves an unguarded window. pruneTeamWorkerGcRecord — a claim-releasing operation — could land inside it, delete the claim record, and the subsequent revalidation would correctly observe "no current claim" and journal the incident skipped with zero dispatches.

The repair is the fence-hold restructure the previous PR body deferred:

  • Revalidation plus send-keys now runs inside withGjcTeamMutationFence, the same fence every other public authority-changing operation already takes. Claim mutation and dispatch are now mutually exclusive.
  • The fence is released before the ACK wait. This matters: the ACK is published by a separate receiver process that must acquire the same cross-process fence, so holding it across the wait would deadmine the very ACK being awaited. Your existing test allows a spawned receiver CLI ACK while continuation waits outside the mutation fence pins that contract, and it stays green — I verified an earlier, coarser version of this fix that held the fence across the whole pass, and it broke exactly that test, which is what pointed me at the correct boundary.
  • pruneTeamWorkerGcRecord now also takes the fence, since it deletes claim records and rewrites claimed tasks.

The fence closure returns a typed outcome (skipped / dispatched / threw) so the post-fence code paths stay explicit rather than reading mutated outer state.

P1 (2) — replacement settled before pane-launch ACK

This one turned out to be a test-fixture regression introduced by this PR's own rewrite of createFakeTmuxBin, and I am glad you held the line on it, because the red was genuine.

The predecessor shell-based fake wrote a 3-field marker to tmux-last-split on every split-window. The rewritten TypeScript fake updated only countPath and dropped that marker write entirely. The memory-guard replacement test waits on that exact marker to know a successor pane was launched before it publishes the generation-bound worker-startup-ack. With the marker never written, the ACK never arrived, relaunchWorkerPaneForMemoryGuard hit its startup deadline, and the guard reported retrying — precisely the symptom in your CI log. Restoring the marker write fixes it.

I also fixed a separate structural bug in that same test that made it unrunnable on non-Linux hosts: it aborted the split-marker waiter and then awaited it before reaching its own process.platform !== "linux" early return, so the rejected waiter masked the advisory outcome. The platform branch now cancels and swallows the waiter first.

Verification

packages/coding-agent/test/gjc-runtime/team-runtime.test.ts on this exact head, rebased onto current dev:

116 pass / 1 fail   (was 114 pass / 3 fail before these repairs)

The three cases you flagged or that I repaired are individually green:

holds every public authority-changing operation behind the monitor dispatch fence   pass
allows a spawned receiver CLI ACK while continuation waits outside the mutation fence   pass
selects the hottest Linux worker, checkpoints it, and syncs config and manifest on replacement   pass

One honest caveat, stated plainly rather than buried: the single remaining local failure is requires exactly one current claim and rejects shutdown, draining, lease, and pane authority gaps, which exceeds its explicit 20s cap on my macOS host. It is not in your CI failure set (your run showed 115/2 with only the two P1s), it timed out identically on my very first baseline run before I made any edits, and its scenario list is byte-identical between dev and this head — so I believe it is host slowness from 13 sequential git-repo fixtures, not a logic regression. I did not touch it, and I would rather flag it than quietly claim a fully green shard.

Scope note

This branch carries no dependency on the reverted #3489. acquireManagedLockSync and ManagedStorageLockSync do not appear anywhere in it — the head commit explicitly enforces psmux authority migration exclusion without the managed sync lock, and persistGjcTmuxProviderAuthoritySync is defined within this branch's own tmux-provider-context.ts. I have also not opened any competing branch against the session-storage subsystem, which I understand is under #3596's sole ownership.

Rebased cleanly onto dev at dd26a0787. Happy to keep iterating if any of the above reasoning does not hold up under your independent check.

twoimo and others added 10 commits July 31, 2026 04:15
…-independent

Restacks the Windows psmux authority work onto current dev, dropping the
storage lineage dev already absorbed via squash, and repairs every
darwin-only failure in the slice.

Five separate platform sources decided psmux behavior independently; four
ignored the authority test seam entirely, so Windows-authority paths were
unreachable on POSIX and eleven tmux-sessions tests failed there:
- psmuxAuthorityEnvironments gated persisted-authority discovery on raw
  process.platform, aborting before any later guard could run.
- resolveGjcTmuxProviderContext and createGjcTmuxSession each recomputed
  the platform instead of consulting the seam.
- teamProviderAuthority read process.platform directly; GjcTeamConfig now
  carries the platform so startGjcTeam's value reaches it.

Continuation dispatch had two real defects: the ack-loop deadline used
raw Date.now() while every other timestamp in the file routes through the
seamed clock, and the reservation site threw where the revalidation site
records a skipped outcome, so one worker's short lease aborted an entire
monitor pass.

tmux-sessions: 35/35 on darwin (was 24/35). team-runtime: 115/117, with
zero psmux-only failures - both remaining names also fail at dev, and
this slice fixes three that dev fails.
psmux rewrote the darwin lifecycle spawn/cleanup path (completeNonLinuxLifecycleSpawn,
cleanupDirectLifecycleAttempt), which regressed POSIX: dev is 60 pass / 0 fail on
notifications-lifecycle-control-runtime, psmux was 51 / 9. psmux is a Windows-only
provider and POSIX uses tmux, so the darwin path must stay dev's.

Reverting the file to dev keeps every psmux suite green (tmux-sessions 35/0,
psmux-detect 39/0, launch-tmux 137/0, tmux-gc 9/0) and preserves the win32 lifecycle
content, which dev already carries from the earlier merged psmux-authority slices.
…g continuation revalidation"

This reverts commit ebc3ca196801cd6b50b726c51d22ed3ba704e9ce.
…ng continuation revalidation"

This reverts commit 56f68822097d04d335126c7542f19e9dd720748e.
… the managed sync lock

The Windows psmux authority publish previously acquired `acquireManagedLockSync`
from managed-session storage. Dev removed that symbol when it reverted Yeachan-Heo#3489, so
the import broke on merge and the whole slice failed at module load.

Reviewers asked for the sync lock to return only alongside a real consumer and a
shared async/sync release contract, which belongs to the resident storage branch
rather than here. So this slice now enforces the property it actually needs on its
own: a retained lock descriptor for the exact authority name blocks publication
unless its owner process is definitely gone. Expiry alone is not permission,
because a paused writer with an expired lease is still live and stealing the name
would let two writers publish one generation-scoped authority.

`publishManagedFileNoReplaceSync` already supplies create-without-clobber
exclusion for the payload itself, so no lease helper is required.
…arker

Repairs the two blockers the reviewer identified on the previous head.

1. A public authority-changing operation dispatched no continuation argv.
   The continuation reservation is written under the task lock, that lock is
   released, and only then does dispatch revalidate eligibility. A concurrent
   claim-releasing operation could land in that window, so revalidation observed
   no current claim and the incident was journalled skipped with nothing
   dispatched. Revalidation plus send-keys now runs inside the same team
   mutation fence every other public authority-changing operation takes, and the
   fence is released before the ACK wait because the ACK is published by a
   separate receiver process that must acquire the same cross-process fence.
   Worker GC prune now takes that fence too, since it deletes claim records.

2. Replacement settled before pane-launch ACK. The fake tmux rewrite dropped the
   three-field tmux-last-split marker its shell predecessor published on every
   split. The memory-guard replacement test waits on that marker before
   publishing the generation-bound startup ACK, so the ACK never arrived,
   relaunch hit its startup deadline, and the guard reported retrying. The
   marker is restored. The same test also aborted its marker waiter before its
   own advisory-host early return, which masked the advisory outcome on
   non-Linux hosts; the ordering is fixed.

@Yeachan-Heo Yeachan-Heo left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

REQUEST_CHANGES — terminal hostile review of exact head 890bd21fdfc348aa1d9b745bb3865126af824130 against exact current-dev base dd26a0787260b4c54b6298b2f46b4908b2b1ed1a.

The two predecessor #3595 blockers are materially repaired in this successor. Exact-head CI proves team-runtime.test.ts green (job 90979894483), and source review confirms continuation revalidation + send-keys are held under withGjcTeamMutationFence, the fence is released before the receiver ACK wait, pruneTeamWorkerGcRecord takes the same fence, the receiver ACK re-enters through task mutation, and the fake tmux split marker is restored. I found no dependency on the reverted synchronous managed lock and no exact-file overlap with the sole #3596 branch.

P1 — the repaired exact head does not typecheck. Exact-head run 30574170803, required job 90979894452, fails packages/coding-agent check at src/gjc-runtime/team-runtime.ts:4534: readonly (string | undefined)[] is passed to a readonly string[] dispatch seam. The new frozen send-keys argv embeds worker.pane_id, whose type remains optional even after runtime eligibility validation. CI log SHA-256: 10b95e99ff89e21b5890ec6e6401d1bbe4e12baf60a6764c5a8c439c53e80a99.

Bounded repair: capture/narrow a non-empty pane id inside the fenced revalidation result before building argv (or return the typed skipped outcome), then use that proven string for both send operations. Keep the existing fence release before ACK and rerun a fresh exact-head package check plus team-runtime shard.

Exact-head CI is terminal with no pending checks: the team-runtime shard passed, but package check, evidence producer, and aggregate are red. Red CI is independently merge-blocking. Closing this external PR under doctrine; no external branch mutation, workflow rerun/cancellation, or duplicate internal mutation lane was created.


[repo owner's gaebal-gajae (clawdbot) 🦞]

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.

2 participants