Skip to content

chore: rebuild welcome gutter successor - #9

Draft
twoimo wants to merge 9 commits into
mainfrom
fix/welcome-composer-gutter-current
Draft

chore: rebuild welcome gutter successor#9
twoimo wants to merge 9 commits into
mainfrom
fix/welcome-composer-gutter-current

Conversation

@twoimo

@twoimo twoimo commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Temporary internal PR used only to apply the final Yeachan-Heo#1862 gutter repair on current dev and run focused viewport tests. The one-shot trigger file is removed by the verified product commit.

Yeachan-Heo and others added 2 commits July 25, 2026 04:27
…ispose flake (Yeachan-Heo#3144)

* test(sdk): make teardown ordering witness production-observable

The Phase 2 rewrite of "session teardown drains admitted direct gate
resolution" was accepted with a narrowed claim because a mutation probe
survived: replacing `await rt.waitForGateResolutionQuiescence()` with
`void ...` still passed. The test fully mocked `resolveGate`, so the
delayed operation never touched the real terminal controller, and it
released the resolver after a single setImmediate while `stopSession()`
was independently awaiting the native `pushFrameAndWait(session_closed)`
barrier — resolution therefore completed before teardown reached
detachment even without the quiescence await.

Now the test calls through the original `registerGateTerminalController`
and the original `resolveGate` (wrapping the latter only with a deferred
pre-terminalization gate), makes `pushFrameAndWait(session_closed)` an
explicit test-controlled pre-drain barrier, asserts the controller is
still attached at the quiescence point, then observes real accepted
terminalization and gate continuation before detachment.

Mutation-proved: the void-await mutation now FAILS at the pre-detachment
assertion (controllerAttached false). Passes 10/10 unmutated.

* test(runtime): poll for the TERM marker in the concurrent-dispose redteam

Caught live by the stabilization soak: main-nontag rehearsal run
30149261910 failed on shard 11 with

  (fail) process-lifecycle adversarial owned-process invariants >
         double and concurrent dispose share one settled result and
         issue one terminating signal
  expect(received).toHaveLength(expected)  Expected: 1  Received: 0

The child's TERM trap appends its marker asynchronously
(`trap 'echo term >> $tmp; exit 0' TERM`), so under shard load
`awaitExit` can return before that write lands and the single-sample
read observes an empty file. The file already has a `waitForAsync`
helper for exactly this shape; the marker assertion just wasn't using
it.

Polls for the single terminating signal before asserting, preserving
the original invariant (exactly one `term` line — not "at least one").

Verified: 15/15 reruns, 4x parallel contention clean, whole file 9/9,
typecheck and biome clean.

This test was NOT in the 62-suspect audit shortlist: it did not fail or
retry during the mined two-week window, so it is a genuinely new
observation the soak surfaced.

---------

Co-authored-by: Yeachan-Heo <yeachan-heo@gajae.dev>
Yeachan-Heo and others added 7 commits July 25, 2026 17:22
`sh` runs a TERM trap only after the current foreground command returns.
With the child looping on `sleep 1` and `gracefulMs: 500`, dispose could
escalate to SIGKILL before the handler wrote its `term` marker, so the
one-terminating-signal assertion saw an empty file (observed in CI run
30149261910, coding-agent shard 11).

Shorten the loop interval to 0.05s and use the module's own
`DEFAULT_GRACEFUL_MS` (2000ms) so the trap has a deterministic window.
Under 18-worker CPU contention the old shape wins the race 8/20; the new
shape wins 20/20. All assertions are unchanged.

Also record the missing `## [Unreleased]` changelog entries for Yeachan-Heo#3109,
Yeachan-Heo#3127, and Yeachan-Heo#3131, found while auditing release scope after v0.11.9.
…turns (Yeachan-Heo#3147)

Lifecycle `session_shutdown` started `controller.stopCurrentSession(ctx)`
but discarded the promise, awaiting only `stopSession(id)`. Once startup
has settled the host is broker-visible and can accept `session.close`
while the startup handler's post-start `reconcileCurrentSession` is still
running. That reconciliation can mint a replacement notification-root
token; `ensureTelegramDaemon` then unregisters it asynchronously. Shutdown
could therefore return — and disposal exit — before that unregister's file
lock and atomic registry write settled, leaving a stale `sessions[id]` row
that the retained older token is correctly fenced from removing
(`unregisterNotificationRoot` rejects token mismatches by design).

Now shutdown snapshots `sessionStartPromises.has(id)` first and awaits the
settled controller stop after `stopSession` whenever startup was NOT
pending, so completed-start reconciliation and its replacement-token
cleanup are joined. The intentional nonblocking path is preserved exactly
where it matters: a genuinely pending startup entry (the `/notify on`
case) still leaves the controller stop fire-and-forget.

Surfaced by the flaky-CI stabilization soak. The regression test
"Telegram root release failure is retained and retried through lifecycle
shutdown" failed deterministically on darwin-arm64 at dev head while
Linux CI stayed green (run 30147146988, 34/34 shards) — a completion-
ordering divergence, not a `/var` canonicalization or native-addon issue:
both registry and notification-root paths are lexical `path.join` with no
realpath or case folding.

Verified on darwin-arm64: the previously-failing test now passes 10/10,
the whole sdk-host-wiring file is 72/72 (first fully green run of this
file on Darwin), and telegram daemon + btw-e2e are 463/463. The assertion
was kept intact rather than replaced with polling, which would have hidden
the lifecycle-return bug.

Co-authored-by: Yeachan-Heo <yeachan-heo@gajae.dev>
…eachan-Heo#3149)

Caught by the stabilization soak: main-nontag rehearsal run 30151115867
failed on shard 8 with

  (fail) session_start swallows startup plus owner-release failure
         without surfacing an extension error
  expect(surfaced).toEqual([])   received 1 surfaced error

The test used `mockRejectedValueOnce` on the SHARED prototypes
`NotificationServer.prototype.start` and `SessionSdkHost.prototype.stop`.
That is a one-shot global: a peer test scheduled concurrently in the same
shard can consume the single rejection first. When that happens this
test's own `start()` resolves, so `startSession` never reaches the
`stopSession` catch that sets `suppressExtensionError`
(src/sdk/bus/index.ts:4407-4420), and the startup error surfaces through
`session_start` instead of being swallowed.

Three separate tests in this file arm one-shot rejections on the same two
prototypes, so the interference is cross-test within a shard rather than
a missing restore — every one of them restores correctly.

Replaces both one-shots with per-test guarded implementations that reject
only on this test's first call and then delegate to the real method,
preserving the existing assertions (including
`expect(hostStop).toHaveBeenCalledTimes(2)`, which is what makes the
"first call rejects" semantics load-bearing).

Verified on darwin-arm64: 10/10 targeted reruns, 6x concurrent whole-file
runs with zero failures, whole file 72/72, typecheck and biome clean.

Co-authored-by: Yeachan-Heo <yeachan-heo@gajae.dev>
@github-actions
github-actions Bot force-pushed the fix/welcome-composer-gutter-current branch from 156ddd9 to 08ebde6 Compare July 25, 2026 11:01
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