Skip to content

fix(pet): stabilize iTerm2 Gajae Pet placement, drag handling, and animation rendering - #4591

Merged
Yeachan-Heo merged 13 commits into
Yeachan-Heo:devfrom
snowykr:fix/iterm-pet-size-paste-center
Aug 16, 2026
Merged

fix(pet): stabilize iTerm2 Gajae Pet placement, drag handling, and animation rendering#4591
Yeachan-Heo merged 13 commits into
Yeachan-Heo:devfrom
snowykr:fix/iterm-pet-size-paste-center

Conversation

@snowykr

@snowykr snowykr commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

What

  • Render the iTerm2 pet in a geometry-derived centered three-row canvas instead of a fixed pixel box, preserving its two-row art footprint across font, Retina, and cell-size changes.
  • Preserve native iTerm image dragging while consuming only the named temporary Gajae Pet image path that iTerm bracket-pastes back into the composer.
  • Replace per-frame iTerm PNG replacement with meaningful-state animated GIF uploads protected by a raster lease. Generic TUI redraws exclude the leased rectangle, so normal output cannot erase or reload the pet.
  • Preserve per-skin animation registries, keep iTerm capability probing non-destructive, preserve the multiplexer restriction on forced Kitty pet graphics, and suppress iTerm pet writes while the TUI is temporarily stopped.

Why

In iTerm2, the pet was vertically misaligned and undersized at non-default geometry. Dragging it inserted /var/folders/.../iTerm2.…gajae-pet.* into the composer. During idle and work animation, per-frame direct writes raced with generic CSI 2K redraws, causing repeated flicker and transient background-color frames.

Review found six valid regressions in the first renderer port: probes could use shutdown-only input draining, forced Kitty could bypass multiplexer policy, stopped TUI sessions could reacquire raster leases, pre-start initialization could consume the iTerm capability warning deadline, non-crab skins could request crab-only frame names, and Bash/Python mode could bypass marked pet drag-path suppression.

Related issues/PRs: none found in the upstream open iTerm-pet issue/PR search.

Behavior and recovery

  • Sixel and Kitty retain their existing rendering paths.
  • Every skin uses its own base, idle, work-entry/exit, work, and burst registry for raster overlays and iTerm GIF records; Ouroboros no longer requests crab frame names.
  • iTerm GIF state changes only for skin, work/flex state, geometry, or placement changes; it does not reupload on every 80ms animation tick.
  • The lease is invalidated on resize, capability loss, manual viewport transitions, explicit replacement, and disposal; cleanup releases the exact protected rectangle so generic rendering resumes normally.
  • Probe input waits only for quiescence. It does not disable Kitty keyboard mode, disable modifyOtherKeys, or detach the active input handler. Shutdown still uses the existing destructive drain.
  • Forced Kitty pet graphics remain unavailable inside tmux, screen, and zellij. Forced Sixel and verified iTerm2 transport keep their existing behavior.
  • iTerm animation checks TUI lifecycle state before lease acquisition and again before async submission. Temporary stops therefore remain silent; normal writes resume after the TUI restarts.
  • The unavailable-capability deadline begins only after UI startup has initiated direct iTerm probing or managed polling, so initialization work cannot produce a premature warning.
  • Marked iTerm pet drag paths are consumed before Bash/Python literal-paste bypass; ordinary image paths remain literal in those modes.

Testing

  • bun --cwd=packages/tui run check
  • bun --cwd=packages/coding-agent run check
  • bun test packages/tui/test/gajae-pet.test.ts packages/tui/test/raster-lease.test.ts packages/coding-agent/test/gajae-pet-widget.test.ts packages/coding-agent/test/input-controller-keybindings.test.ts packages/tui/test/render-commit.test.ts packages/tui/test/mouse-sgr.test.ts
    • 188 passing tests, 1042 expectations.
  • Review-fix regressions:
    • bun test packages/tui/test/terminal-probe-reply-leak.test.ts packages/tui/test/raster-lease.test.ts packages/tui/test/gajae-pet.test.ts — 65 passing tests, 569 expectations.
    • bun test packages/coding-agent/test/modes/components/pet-capability.test.ts packages/coding-agent/test/gajae-pet-widget.test.ts — 68 passing tests, 220 expectations.
    • bun test packages/coding-agent/test/gajae-pet-widget.test.ts packages/coding-agent/test/interactive-mode-editor-component.test.ts packages/tui/test/raster-lease.test.ts packages/tui/test/terminal-probe-reply-leak.test.ts — 135 passing tests, 590 expectations.
    • bun test packages/coding-agent/test/modes/components/pet-capability.test.ts packages/tui/test/gajae-pet.test.ts packages/coding-agent/test/input-controller-keybindings.test.ts — 89 passing tests, 657 expectations.
    • bun test packages/coding-agent/test/gajae-pet-widget.test.ts packages/coding-agent/test/input-controller-keybindings.test.ts — 121 passing tests, 1208 expectations.
  • Direct iTerm2 dogfooding confirmed centered placement, native drag without composer path insertion, stable idle/work animation, Ouroboros skin rendering, and Bash/Python drag-path suppression.
  • Not manually verified: managed iTerm2 transport through tmux; focused transport/raster lifecycle coverage is included.

GJC verdict

gajae.pr-review-verdict.v1 merge-approved sha256:56669e9a7b741947635f495009c2f93bc72d67d47b7be8908832095759154832 reviewer:human reviewer-id:probepark evidence:exact-head-approval-2b75098e-maintained-adversarial-review

  • Target branch is dev
  • bun check passes (focused package checks were run; GitHub CI validates the final head)
  • Tested locally
  • CHANGELOG updated
  • Verdict above matches the exact PR head, not an earlier commit

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 10ded2afa0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tui/src/tui.ts Outdated
Comment thread packages/coding-agent/src/modes/components/pet-capability.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

await new Promise(resolve => setTimeout(resolve, Math.min(idleMs, timeLeft)));

P1 Badge Replace the timeout promise with Promise.withResolvers

The new capability-probe quiescence loop constructs a timeout promise directly, contrary to the repository contract requiring Promise.withResolvers() instead of new Promise(...); rewrite this wait using the mandated resolver pattern.

AGENTS.md reference: AGENTS.md:L119-L119

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/coding-agent/src/modes/components/gajae-pet-widget.ts
Comment thread packages/coding-agent/src/modes/interactive-mode.ts Outdated
@snowykr

snowykr commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

Validated both review findings against the live input/protocol paths and applied only the behavior-preserving fixes in 53b6b7aaad9783a842944a8cb032f847acad58f2.

  • P1: Added drainPendingInput() for capability probes. Unlike shutdown-only drainInput(), it neither emits Kitty/modifyOtherKeys disable sequences nor replaces the active input handler. TUI.drainPetProbeInput() now uses only that safe API and deliberately no-ops for terminal implementations that do not provide it. Regression coverage proves enhanced keyboard mode remains enabled and ordinary input still reaches the focused component.
  • P2: Restored the multiplexer guard specifically for Kitty in getPetPixelProtocol(). Forced Kitty is unavailable under tmux, screen, and zellij; forced Sixel and verified iTerm remain unchanged. Coverage exercises all three multiplexer markers.

Validation:

  • bun --cwd=packages/tui run check
  • bun --cwd=packages/coding-agent run check
  • bun test packages/tui/test/terminal-probe-reply-leak.test.ts packages/tui/test/raster-lease.test.ts packages/tui/test/gajae-pet.test.ts — 65 pass
  • bun test packages/coding-agent/test/modes/components/pet-capability.test.ts packages/coding-agent/test/gajae-pet-widget.test.ts — 68 pass

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Maintained review/salvage is now active on this PR at exact head 53b6b7aa (base d332b789, branch fix/iterm-pet-size-paste-center). Two review findings are under verification before a verdict: (1) stale-head P1 — iTerm capability probing previously used shutdown-oriented drainInput() in packages/tui/src/tui.ts, risking Kitty keyboard protocol / modifyOtherKeys disablement and startup input loss; the head commit claims to fix this, and input-protocol preservation (including when the pet setting is off) is being re-proven deterministically. (2) current-head P2 — packages/coding-agent/src/modes/components/pet-capability.ts:52 may allow forced Kitty graphics under tmux/screen/zellij, emitting raw Kitty escapes where the prior guard allowed only explicitly forced Sixel and managed iTerm; multiplexer gating is being checked against the terminal-capabilities contract. Focused tests, multiplexer env matrices, and CI on the exact head will follow; fixes (if needed) will land on this branch with maintainer modification.


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

@snowykr

snowykr commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

Validated both lifecycle findings against the stop/start and initialization order before changing them. Applied the narrow fixes in 2d30ea2256fea31882ae71b3afce0a2e61bb448c.

  • P1: TUI.isRunning exposes only lifecycle state. The iTerm widget checks it before every tick and inside the asynchronous freshness predicate after acquisition, so a stop between those points invalidates/revokes rather than writes GIF bytes. terminalAvailable semantics, Sixel/Kitty behavior, and normal restart behavior are unchanged. A delayed-acquisition regression proves no lease submission while stopped and resumption when running again.
  • P2: The configured-pet warning registration now occurs after ui.start() and direct probe/managed polling initiation. Immediate non-probe warnings keep their existing behavior and wording. The fake-timer integration test proves pre-start todo loading cannot consume the deadline, while the full post-probe deadline still yields the same unavailable message.

Validation:

  • bun --cwd=packages/tui run check
  • bun --cwd=packages/coding-agent run check
  • bun test packages/coding-agent/test/gajae-pet-widget.test.ts packages/coding-agent/test/interactive-mode-editor-component.test.ts packages/tui/test/raster-lease.test.ts packages/tui/test/terminal-probe-reply-leak.test.ts — 135 pass
  • bun test packages/coding-agent/test/modes/components/pet-capability.test.ts packages/tui/test/gajae-pet.test.ts packages/coding-agent/test/input-controller-keybindings.test.ts — 89 pass

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2d30ea2251

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/coding-agent/src/modes/components/gajae-pet-widget.ts Outdated
Comment thread packages/coding-agent/src/modes/controllers/input-controller.ts
@snowykr

snowykr commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

Validated both newly detected findings against current source and direct dogfooding before applying 1f76721d2a13f4742e00ca265e4e548020226708.

  • P1: The widget had indeed regressed from skin-owned animation metadata to literal crab frame names. Initial frame selection, idle/work loops, work transitions, bursts/work bursts, and iTerm GIF timelines now all derive from PET_SKINS[mode]. Ouroboros can therefore emit only its authored idle/transition/work/heart frames across Sixel, Kitty, and iTerm. Geometry, lease, transport, and UI text were unchanged.
  • P2: The marked pet-path guard now precedes the Bash/Python early return. Only .gajae-pet.gif and compatibility .png artifacts are consumed; ordinary image paths retain existing literal shell/Python paste behavior.

Validation:

  • bun --cwd=packages/coding-agent run check
  • bun test packages/coding-agent/test/gajae-pet-widget.test.ts packages/coding-agent/test/input-controller-keybindings.test.ts — 121 pass, 1208 expectations.
  • Direct iTerm2 dogfooding approved for Ouroboros rendering and Bash/Python drag-path suppression.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1f76721d2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/coding-agent/src/modes/components/iterm-pet-transport.ts
Comment thread packages/tui/src/terminal.ts Outdated
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Fix-forward applied at exact head 23a5e38d (fast-forward atop 1f76721d, base d332b789, maintainer modification per maintainer_can_modify): packages/tui/src/terminal.ts drainPendingInput now builds its quiescence wait with Promise.withResolvers() instead of the contract-forbidden new Promise(...) executor — the last PR-introduced violation of that rule; the pre-existing drainInput occurrence is untouched base code. Verified: bun test packages/tui/test/terminal-probe-reply-leak.test.ts (14 pass), full focused battery (245 pass / 2072 expectations across 9 files incl. ouroboros frame-set, bash/python drag suppression, stop/resume, multiplexer matrix), bun --cwd=packages/tui run check and bun --cwd=packages/coding-agent run check green, bounded drain timing re-proven, state-writer fast gate clean. Canonical full-index diff digest for d332b789...23a5e38d is sha256:d239ff27b705d17e7313cb58421e3d95f067cdf10feebb55c1a3b2e7024ec65a. Adversarial review of all seven findings (probe drain, multiplexer Kitty, stopped-TUI writes, capability deadline ordering, skin frame registry, shell drag ordering, withResolvers contract) continues against this exact head; verdict follows once replacement CI settles.


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

@snowykr

snowykr commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

Validated the repeated managed-topology report and applied the focused fix in 676000d8efce58132863b0ffdfecd08c92ef1c93, rebased on maintainer commit 23a5e38dfb6393d2dc4f212f2b015cfaeada63f9.

  • Repeated revoke(reason) calls now return without epoch advancement or availability emission only when the transport is already unavailable for that exact reason. This prevents the 250ms managed poll from repeatedly suspending/rerendering the pet.
  • Changed reasons still emit a new unavailable transition. A valid managed recovery still emits available, and a later revocation emits again. The focused transport regression covers all of those transitions.
  • The Promise.withResolvers() finding was already applied by the maintainer in 23a5e38d; its terminal-only change was retained during rebase.

Validation:

  • bun --cwd=packages/coding-agent run check
  • bun --cwd=packages/tui run check
  • bun test packages/coding-agent/test/modes/components/iterm-pet-transport.test.ts packages/tui/test/terminal-probe-reply-leak.test.ts packages/coding-agent/test/gajae-pet-widget.test.ts packages/coding-agent/test/input-controller-keybindings.test.ts — 138 pass, 1236 expectations.

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

MERGE_READY at exact head 676000d8 (base d332b789, digest sha256:03163b59ac2d12d8643c3973f51415e3cca7c4c55fa835bd97cfbcd63912c949).

Adversarial review conclusions on this exact head — all seven tracked findings proven fixed, plus the two late contributor commits verified:

  1. Destructive probe drain (P1, fixed at 53b6b7a)drainPendingInput waits on stdin quiescence only: no \x1b[<u, no \x1b[>4;0m, no #inputHandler detach, input-handler restore not needed (never detached); drainPetProbeInput prefers it and only falls back to the shutdown drain when absent. terminal-probe-reply-leak.test.ts proves keyboard modes survive and startup keys still arrive, including with the pet setting off.
  2. Multiplexer forced-Kitty (P2, fixed at 53b6b7a)getPetPixelProtocol() gates Kitty on !isUnderTerminalMultiplexer(); re-proved live across tmux (TMUX/TMUX_PANE), screen (STY, TERM=screen*/TERM=tmux*), zellij (ZELLIJ) with PI_FORCE_IMAGE_PROTOCOL=kitty → null every time, while forced Sixel and the separately verified managed-iTerm transport remain allowed, and plain Kitty outside multiplexers still resolves.
  3. Stopped-TUI animation writes (P1, fixed at 2d30ea2)TUI.isRunning gates the iTerm tick entry, and the async current() freshness predicate re-checks isRunning before every acquire/submit/write, closing the delayed-acquisition race during external-editor stop; restart resumes submissions. Widget test covers stopped → no output/invalidated lease → resumed → GIF written.
  4. Capability deadline ordering (P2, fixed at 2d30ea2) — the settle timer is now registered after ui.start() and after probe/poll initiation; the fake-timer regression proves pre-start initialization cannot consume the deadline and the full window remains post-probe.
  5. Skin frame registry (P1, fixed at 1f76721) — crab-only IDLE_LOOP/WORK_LOOP hard-coding replaced by PET_SKINS[mode] metadata (baseFrame, idle/work, workEnter/workExit transitions, bursts, workBursts); #itermTimeline mirrors it for GIFs and the semantic key includes the timeline signature. Re-proved at runtime: red/blue/ouroboros idle, work, transitions, and burst timelines resolve only to real frames for Kitty/Sixel pixel maps and iTerm GIF builds — no undefined frame, no Unknown ouroboros GIF frame.
  6. Bash/Python drag-path ordering (P2, fixed at 1f76721) — the marked .gajae-pet.(png|gif) drag check now runs before the shell-mode bypass, so the pet artifact never lands in !/$ composers, while ordinary image paths still fall through to literal/attachment handling (covered for both modes and both extensions).
  7. Promise.withResolvers contract (fixed-forward by maintainer at 23a5e38) — the PR-added drainPendingInput quiescence wait now uses Promise.withResolvers(); no PR-introduced new Promise(...) remains (the pre-existing drainInput occurrence is untouched base code). Bounded drain timing re-proven.

Late delta 23a5e38d..676000d8 (one-line revoke() idempotence guard + 97-line managed-topology test) verified against all revoke side effects: identical-reason re-revoke short-circuits without epoch churn or duplicate listener events; changed-reason and available-state revocations still take the full path; pending-probe resolution and dispose idempotence unaffected; recovery then re-revocation emits exactly the expected transition sequence.

Evidence on this head: focused battery 248 pass / 2080 expectations across 10 files (including the new iterm-pet-transport.test.ts), bun --cwd=packages/tui run check and bun --cwd=packages/coding-agent run check green, state-writer fast gate clean, GIF cache bounds (≤32 entries / 8 MiB with eviction) re-proven, capability-reply parser boundaries re-proven (F → complete, malformed → invalid, incomplete → undefined).

The PR contract bootstrap red is the expected external gate: it intentionally fails while the verdict is needs-human and then requires an authenticated exact-head APPROVED review from a write-capable non-author reviewer. Requesting @probepark (primary) and @HaD0Yun (backup) for the exact-head approval; on approval the single verdict line will be promoted to merge-approved with this same digest and the PR merged to dev.


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

@Yeachan-Heo

Copy link
Copy Markdown
Owner

@probepark @HaD0Yun — requesting an exact-head review approval on 676000d8 (PR #4591). The maintained MERGE_READY review above documents the adversarial verification of all seven findings on this exact head (focused battery 248 pass / 2080 expectations across 10 files; both package checks green; canonical digest sha256:03163b59ac2d12d8643c3973f51415e3cca7c4c55fa835bd97cfbcd63912c949 bound in the PR body). The PR contract bootstrap check stays intentionally red until this verdict is promoted to merge-approved, which requires one authenticated APPROVED review from a write-capable non-author reviewer at this exact head. An approval here unblocks promotion + dev merge; any blocker you find will be fixed forward on the same branch.


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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 676000d8ef

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -1537,6 +1569,11 @@ export class InteractiveMode implements InteractiveModeContext {
this.#petUnavailableWarningDisposer = undefined;
this.petWidget?.dispose();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Await pet cleanup before restoring the terminal

When InteractiveMode.stop() is invoked directly rather than through shutdown(), dispose() only schedules the Kitty/Sixel erase on the Promise-backed raster queue, while this method proceeds synchronously to ui.stop(). The erase can therefore be written after raw mode and cursor state have already been restored, interleaving escape sequences with the parent shell prompt; ensure pet cleanup is completed or synchronously registered and flushed before stopping the TUI.

Useful? React with 👍 / 👎.

if (start < 0) {
const suffixLength = Math.min(marker.length - 1, combined.length - offset);
const candidate = combined.slice(combined.length - suffixLength);
const keep = candidate.length > 1 && marker.startsWith(candidate) ? candidate : "";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve one-byte capability prefixes

When an iTerm capability reply is split immediately after its initial ESC byte, this candidate.length > 1 condition forwards that ESC to the editor and forgets it, so the following ]1337;Capabilities=... chunk is never recognized and the probe incorrectly times out. Input streams may split at any byte boundary, so retain a one-byte marker prefix with a safe probe-end flush path and cover that fragmentation boundary.

AGENTS.md reference: AGENTS.md:L158-L158

Useful? React with 👍 / 👎.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Fix-forward at new exact head 6d69a334 (fast-forward atop 676000d8, base d332b789): the exact-head Codex fragmentation finding is accepted and fixed. Root cause ran deeper than reported — the fragment filter retained only a single fixed-length suffix candidate, so any split that left a shorter valid marker prefix at the chunk end (not just after the lone ESC byte) forwarded that prefix to the editor and the capability reply was never recognized; the probe then timed out spuriously. Fix: scan suffix lengths descending and retain the longest marker prefix (consumeCapabilityInput in packages/coding-agent/src/modes/components/iterm-pet-transport.ts). Regression matrix added: split after ESC, after 2 marker bytes, after a long prefix, marker alone in first chunk, and before the ST terminator (ST frame asserted verbatim), plus passthrough/interleaving cases proving unrelated input, Kitty keys, OSC 11, and DA1 replies still pass through untouched. The second exact-head finding (stop-ordering of pet cleanup) was investigated and classified non-blocking for current callers: every production path (main.ts, /exitshutdown()) awaits petWidget.disposeAsync() — which flushes the raster/cleanup writes — before ui.stop(), dispose() is idempotent, and the only direct mode.stop() caller (main.ts:819) runs in the finally after await mode.shutdown(). Verified on this head: focused battery 255 pass / 2102 expectations across 10 files (transport 10 pass), bun --cwd=packages/coding-agent run check and bun --cwd=packages/tui run check green. Canonical digest updated to sha256:303151e9b525c318883e1719e3d7abe77ad0efc1d7f19a6f56328b1558ce6bbf; the needs-human bootstrap gate continues to await the independent exact-head approval from @probepark / @HaD0Yun (now bound to 6d69a334).


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

snowykr and others added 10 commits August 15, 2026 19:56
iTerm2 now uses a geometry-derived three-row canvas with transparent insets instead of a fixed pixel box.\n\nThe iTerm anchor no longer receives the shared one-row safety lift, so the padded canvas aligns to the composer boundary.\n\nLore-id: iterm-pet-center\nConfidence: high\nScope-risk: narrow\nReversibility: simple\nTested: bun test packages/coding-agent/test/gajae-pet-widget.test.ts packages/tui/test/gajae-pet.test.ts
iTerm turns a native inline-image drag into a bracketed temporary-file path paste.\n\nTag pet frames with a dedicated filename and consume only that decoded path in the composer, keeping native dragging and ordinary image-path pastes intact.\n\nLore-id: iterm-pet-drag-paste\nConfidence: high\nScope-risk: narrow\nReversibility: simple\nTested: bun test packages/coding-agent/test/input-controller-keybindings.test.ts packages/coding-agent/test/gajae-pet-widget.test.ts packages/tui/test/gajae-pet.test.ts\nTested: bun --cwd=packages/coding-agent run check\nTested: bun --cwd=packages/tui run check
iTerm replaces inline images at a stable cell block.\n\nErasing that block before each animation frame exposed the composer background while the next PNG loaded. Keep explicit erasure for Sixel and lifecycle cleanup only.\n\nLore-id: iterm-pet-animation-flash\nConfidence: medium\nScope-risk: narrow\nReversibility: simple\nTested: bun test packages/coding-agent/test/gajae-pet-widget.test.ts packages/coding-agent/test/input-controller-keybindings.test.ts packages/tui/test/gajae-pet.test.ts\nTested: bun --cwd=packages/coding-agent run check
Per-frame PNG writes raced with full-line TUI erases, exposing transient background frames and repeatedly reloading the pet.\n\nUse a lease-protected iTerm GIF record that changes only with meaningful pet state, preserving the normal renderer outside its exact cell rectangle.\n\nLore-id: iterm-pet-stable-rendering\nConfidence: high\nScope-risk: focused\nReversibility: isolated-renderer\nTested: bun --cwd=packages/tui run check\nTested: bun --cwd=packages/coding-agent run check\nTested: bun test packages/tui/test/gajae-pet.test.ts packages/tui/test/raster-lease.test.ts packages/coding-agent/test/gajae-pet-widget.test.ts packages/coding-agent/test/input-controller-keybindings.test.ts\nTested: bun test packages/tui/test/render-commit.test.ts packages/tui/test/mouse-sgr.test.ts
Capability probes used the shutdown drain and allowed forced Kitty graphics through multiplexers.\n\nProbe input now waits non-destructively, while pet capability selection keeps raw Kitty unavailable under tmux, screen, and zellij.\n\nLore-id: iterm-pet-probe-guard\nConfidence: high\nScope-risk: narrow\nReversibility: simple\nTested: bun --cwd=packages/tui run check\nTested: bun --cwd=packages/coding-agent run check\nTested: bun test packages/tui/test/terminal-probe-reply-leak.test.ts packages/tui/test/raster-lease.test.ts packages/tui/test/gajae-pet.test.ts\nTested: bun test packages/coding-agent/test/modes/components/pet-capability.test.ts packages/coding-agent/test/gajae-pet-widget.test.ts
Stopped TUI sessions could reacquire an iTerm raster lease and start the capability warning before its probe began.\n\nGate asynchronous animation submission on TUI lifecycle state and start the warning deadline only after UI startup initiates probing.\n\nLore-id: iterm-pet-lifecycle-guard\nConfidence: high\nScope-risk: narrow\nReversibility: simple\nTested: bun --cwd=packages/tui run check\nTested: bun --cwd=packages/coding-agent run check\nTested: bun test packages/coding-agent/test/gajae-pet-widget.test.ts packages/coding-agent/test/interactive-mode-editor-component.test.ts packages/tui/test/raster-lease.test.ts packages/tui/test/terminal-probe-reply-leak.test.ts\nTested: bun test packages/coding-agent/test/modes/components/pet-capability.test.ts packages/tui/test/gajae-pet.test.ts packages/coding-agent/test/input-controller-keybindings.test.ts
The stable iTerm port reused crab frame names for every skin and bypassed marked drag-path suppression in shell modes.\n\nUse each skin's authored animation registry end to end, and consume only marked pet paths before the Bash/Python literal-paste bypass.\n\nLore-id: pet-skin-and-shell-drag-guard\nConfidence: high\nScope-risk: narrow\nReversibility: simple\nTested: bun --cwd=packages/coding-agent run check\nTested: bun test packages/coding-agent/test/gajae-pet-widget.test.ts packages/coding-agent/test/input-controller-keybindings.test.ts
The new drainPendingInput quiescence loop constructed its sleep promise
with new Promise(...), violating the repository contract that mandates
Promise.withResolvers(); the executor only wired a setTimeout resolve.

Lore-id: pr4591-withresolvers
Confidence: high
Scope-risk: narrow
Reversibility: simple
Tested: bun test packages/tui/test/terminal-probe-reply-leak.test.ts; bun --cwd=packages/tui run check
An ineligible managed tmux topology was revoked on every polling interval, repeatedly forcing pet lifecycle work.\n\nSuppress duplicate unavailable transitions for the same reason while preserving changed-reason and recovery transitions.\n\nLore-id: iterm-pet-topology-idempotence\nConfidence: high\nScope-risk: narrow\nReversibility: simple\nTested: bun --cwd=packages/coding-agent run check\nTested: bun --cwd=packages/tui run check\nTested: bun test packages/coding-agent/test/modes/components/iterm-pet-transport.test.ts packages/tui/test/terminal-probe-reply-leak.test.ts packages/coding-agent/test/gajae-pet-widget.test.ts packages/coding-agent/test/input-controller-keybindings.test.ts
The capability-input fragment filter kept only one fixed-length suffix
candidate, so a reply split after the ESC byte (or any shorter valid
marker prefix inside a longer chunk) forwarded the prefix to the editor
and the reply was never recognized; the probe then timed out. Scan
suffix lengths descending and retain the longest marker prefix.

Lore-id: pr4591-marker-fragments
Constraint: must not weaken the ST-terminator regression or leak capability replies to the editor
Confidence: high
Scope-risk: narrow
Reversibility: simple
Tested: bun test packages/coding-agent/test/modes/components/iterm-pet-transport.test.ts (10 pass incl. 5 split matrices)
Tested: focused battery 255 pass / 2102 expectations across 10 files; both package checks green
@Yeachan-Heo
Yeachan-Heo force-pushed the fix/iterm-pet-size-paste-center branch from 6d69a33 to e5abb7d Compare August 15, 2026 19:58
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Rebased onto current dev (91a35114, "fix(ai): release Codex websocket on consumer abort #4556" — disjoint packages/ai paths, zero conflicts): PR head is now e5abb7da32, all 10 commits preserved 1:1 (range-diff = for every commit, including both maintainer fix-forwards cb3a0fab withResolvers and e5abb7da marker-prefix retention). Validation on the rebased exact head: focused battery 255 pass / 2102 expectations across 10 files; bun --cwd=packages/tui run check, bun --cwd=packages/coding-agent run check, bun --cwd=packages/ai run check (new dev neighbor) all exit 0; state-writer fast gate clean. Canonical full-index digest for 91a35114...e5abb7da is unchanged at sha256:303151e9b525c318883e1719e3d7abe77ad0efc1d7f19a6f56328b1558ce6bbf (the PR diff content is identical; only the base moved), so the verdict line remains digest-correct. Replacement exact-head CI is starting; the needs-human bootstrap gate continues to await the independent approval from @probepark / @HaD0Yun bound to e5abb7da.


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

submitTerminalOutput invoked queued shouldWrite() freshness predicates
outside the terminal-operation guard at both the early gate and the
post-barrier gate. A throwing predicate rejected the raster-queue
promise; the iTerm pet submits fire-and-forget, so that rejection
surfaced as an unhandled rejection and production fatal handling
escalates it to process exit. afterPrefix already degraded via
try/catch, so wrap both predicate callsites the same way and return the
typed failed ack instead of rejecting.

Lore-id: pr4591-shouldwrite-guard
Constraint: false must still classify stale-token and true must still write; unrelated terminal errors must keep failing normally
Confidence: high
Scope-risk: narrow
Reversibility: simple
Tested: bun test packages/tui/test/raster-lease.test.ts (33 pass incl. throwing/false/true predicate matrix)
Tested: focused battery 256 pass / 2108 expectations across 10 files; tui + coding-agent checks green
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Fix-forward at new exact head 87bc0580 (base 91a35114): the boundary red-team lane found a crash-class defect — submitTerminalOutput invoked queued shouldWrite() freshness predicates outside the terminal-operation guard at both the early gate and the post-barrier gate, so a throwing predicate rejected the raster-queue promise; the iTerm pet submits fire-and-forget (void #submitIterm), which escalates through production unhandledRejection fatal handling into process exit. Fixed by wrapping both predicate callsites in try/catch that degrade to the existing typed failed ack, matching the already-guarded afterPrefix path. Regression added in packages/tui/test/raster-lease.test.ts: a throwing predicate must resolve status:"failed" for both queued-output and leased multipart paths with zero terminal writes and no rejection; false still classifies stale-token and true still writes — all three cases asserted in one deterministic test (33 pass in that file). Verified on this head: focused battery 256 pass / 2108 expectations across 10 files; bun --cwd=packages/tui run check and bun --cwd=packages/coding-agent run check green; dev unchanged so no rebase was needed. Canonical digest updated to sha256:f9a034ea26cff56626403d0212d2a084834349b19f7c954380f01170478b6127; replacement CI is starting, and the needs-human bootstrap gate still awaits the independent exact-head approval from @probepark / @HaD0Yun bound to 87bc0580.


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

Direct InteractiveMode.stop() queued pet erase through the raster queue
and then restored the terminal synchronously; the deferred queue body
still wrote after restoration, interleaving escapes with the parent
shell. A multipart body that already began could also resume after an
await (flush/afterPrefix) or a user predicate that stops the terminal
and then throws/returns false, emitting suffix, abort, and cursor
restoration bytes post-stop.

Capture a raster lifecycle epoch in #enqueueRaster and increment it as
the first action of stop(). Queue bodies receive an isCurrentLifecycle()
predicate, evaluated at entry and re-checked after every await and
before every later terminal write; stale bodies settle with their typed
failed/stale-token/rejected/retained results. abortBarrier refuses to
write once stale, so no abort or cursor-restoration bytes escape. A
retained cleanup payload is delivered by the next start(), which keeps
stop() synchronous and preserves the stop-flush cleanup window.

Also gate the pet-unavailable warning suffix on an observed iTerm
transport reason so non-iTerm terminals stop rendering '(unknown)'.

Lore-id: pr4591-raster-stop-epoch
Constraint: stop() stays synchronous; synchronous stop cleanup before restoration must keep working; false must still classify stale-token and true must still write
Confidence: high
Scope-risk: narrow
Reversibility: simple
Tested: bun test packages/tui/test/raster-lease.test.ts (34 pass incl. gated-ingress, paused-at-await, predicate-stop, restart-delivery matrix)
Tested: focused battery 259 pass / 2142 expectations across 10 files; tui + coding-agent checks green
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Fix-forward at new exact head 189a227b (base 91a35114): two cohort findings fixed at the root.

(1) Post-stop raster writes (direct-stop finding, confirmed ACTIVE by reproduction). Direct InteractiveMode.stop() queued the pet erase on the raster queue and then restored the terminal synchronously — the deferred queue body still wrote after restoration, interleaving escapes with the parent shell. Worse, a multipart body that had already begun could resume after flush()/afterPrefix() awaits, and a user shouldWrite predicate could itself call stop() before throwing/returning false — both emitting suffix/abort/cursor-restoration bytes post-stop. Fix: #enqueueRaster now captures a raster lifecycle epoch (incremented as the first action of stop()) and hands each body an isCurrentLifecycle() predicate — evaluated at entry, re-checked after every await, and before every later terminal write. Stale bodies settle with their typed failed/stale-token/rejected/retained results; abortBarrier refuses to write when stale; a retained cleanup payload is delivered by the next start(). stop() stays synchronous and its own pre-restoration cleanup window is untouched (no #stoppingFlush revalidation bypass — captured-epoch equality only). Deterministic regression in raster-lease.test.ts covers all four shapes: gated-ingress (multipart parked in afterPrefix blocks the queue), started-body-paused-at-await when stop occurs, predicate-that-stops-then-throws, and restart delivery + new-lifecycle write — zero suffix/abort/GIF/cursor bytes after stop, exact typed acks, no rejections, pending convergence (34 pass).

(2) (unknown) warning suffix (architecture-lane P2, confirmed by reproduction). getItermPetUnavailableReason() ?? "unknown" rendered (unknown) on every non-iTerm terminal (VS Code/alacritty/base) for both the startup settle warning and /pet refusals. Fix: a #petUnavailableStatusText() helper appends the parenthetical only when this instance has an iTerm transport and a concrete observed reason; probe timing and retry behavior unchanged. Focused regressions added: plain-warning-no-suffix on non-iTerm startup + refusal, concrete (probe-timeout) suffix on iTerm, plus no-(unknown) assertions folded into the existing deadline and shared-policy tests (file now 32 pass).

Verified on this head: focused battery 259 pass / 2142 expectations across 10 files; bun --cwd=packages/tui run check and bun --cwd=packages/coding-agent run check green; dev unchanged. Canonical digest updated to sha256:b10462db16ac4d5b65b36ea26b235ddcc83b07500a46c2560a3aac58d8100622. Replacement CI is starting; the needs-human bootstrap gate still awaits the independent exact-head approval from @probepark / @HaD0Yun bound to 189a227b.


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

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Exact head 189a227b finalized — digest updated and one consolidated fix-forward summary:

  • Direct-stop raster lifecycle race fixed: #enqueueRaster captures a raster lifecycle epoch (incremented as the first action of stop()); all six queue callsites (acquireRasterLease, submitTerminalOutput, invalidateRasterLease, notifyTerminalLifecycle, queueTerminalCleanup, render-buffer write) are typed, captured-epoch gated. Async boundaries re-check after flush() and afterPrefix() awaits, and immediately before every later terminal write; shouldWrite predicates that stop the terminal and then throw/return false can no longer emit abort or cursor-restoration bytes (abortBarrier refuses when stale). stop() stays synchronous; its own pre-restoration cleanup window is untouched.
  • Warning (unknown) fix: the parenthetical iTerm reason renders only when this instance has an iTerm transport with a concrete observed reason; non-iTerm startup warnings and /pet refusals show the plain warning. Probe timing and retry behavior unchanged.
  • Verification on this head: raster-lease.test.ts 34/34 (153 expectations, including gated-ingress, paused-at-await, predicate-stop, restart-delivery matrix); focused 10-file battery 259/259 (2142 expectations); bun --cwd=packages/tui run check and bun --cwd=packages/coding-agent run check green; base dev (91a35114) contained, worktree clean.
  • Verdict line: needs-human preserved with reviewer metadata; canonical digest now sha256:b10462db16ac4d5b65b36ea26b235ddcc83b07500a46c2560a3aac58d8100622 (the earlier f9a034… was stale).
  • CI: run 31908742989 was cancelled and superseded by same-head Dev CI 31908772467 (in progress) — that run is authoritative. Exact-head independent approval from @probepark / @HaD0Yun remains required before the verdict can be promoted to merge-approved.


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

The shared pet-policy test asserted the multiplexer warning variant,
which only renders when TMUX/STY/ZELLIJ are set; CI runs without them
and failed on the base variant. Assert the variant-agnostic prefix.

Lore-id: pr4591-warning-env-agnostic
Confidence: high
Scope-risk: narrow
Reversibility: simple
Tested: bun test packages/coding-agent/test/interactive-mode-editor-component.test.ts under both host-TMUX and TMUX-less env (32 pass each)
Tested: focused battery 259 pass / 2142 expectations across 10 files
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@Yeachan-Heo

Copy link
Copy Markdown
Owner

New exact head 2b75098e (one-line test-only fix-forward atop 189a227b, base 91a35114): the shared pet-policy warning assertion had baked in the multiplexer warning variant, which only renders under TMUX/STY/ZELLIJ — CI runs without them and failed on the base variant. The assertion now matches the variant-agnostic prefix, verified locally under both host-TMUX and TMUX-less environments (32 pass each; full focused battery 259 pass / 2142 expectations across 10 files; coding-agent check green). Canonical digest updated to sha256:56669e9a7b741947635f495009c2f93bc72d67d47b7be8908832095759154832. Note on run 31908772467: its earlier reds were (a) the expected needs-human bootstrap gate and (b) an infra cancellation — the native-build job's Install system deps apt-get step was cancelled mid-fetch (~29 min in, external cancel), and evidence producer/aggregate then failed closed on the missing artifact; that is not a product failure. Replacement CI on 2b75098e is starting; exact-head independent approval from @probepark / @HaD0Yun remains required before verdict promotion.


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

@Yeachan-Heo

Copy link
Copy Markdown
Owner

CI terminal classification for exact head 2b75098e (run 31910918162): all technical jobs green — affected plan, native-build, every affected-path test/check shard (including raster-lease, pet-widget, transport, interactive-mode, terminal-probe, both package checks, ts-build, cli-smoke), evidence producer, aggregate, GJC state gates, and virtual integration validation. The single red is PR contract bootstrap with the expected Verdict needs-human intentionally blocks merge — the digest is current (no stale-digest diagnostic), so this is purely the external approval gate.

Adversarial review is complete on this head: seven tracked findings plus six review-lane findings all proven fixed (probe drain, multiplexer Kitty matrix, stopped-TUI async writes, capability deadline ordering, ouroboros skin registry, bash/python drag ordering, withResolvers contract, revoke idempotence, marker-prefix fragmentation, shouldWrite throw classification, raster stop-epoch lifecycle incl. flush/afterPrefix/predicate-stop boundaries, (unknown) warning suffix, warning env-agnosticism).

Remaining blocker is exactly one: an effective APPROVED review at 2b75098e from a write-authorized non-author reviewer (@probepark primary, @HaD0Yun backup). On approval I will promote the single verdict line to merge-approved with the same digest sha256:56669e9a7b741947635f495009c2f93bc72d67d47b7be8908832095759154832, let bootstrap go green, and merge to dev.


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

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Classification of the five current-head Codex comments (re-anchored re-posts of findings already fixed on this branch, verified against exact head 2b75098e): (1) multiplexer forced-Kitty guard — fixed at 23a8a688, live matrix re-proven (TMUX/STY/ZELLIJ/TERM=screen*/tmux* forced kitty → null; forced Sixel and managed iTerm retained) with pet-capability.test.ts coverage; (2) iTerm animation writes while TUI stopped — fixed at 2e84ccb2 (TUI.isRunning tick gate + async freshness predicate, delayed-acquisition regression); (3) drag check before Bash/Python bypass — fixed at c3ee4b00 with both-mode/both-extension regression; (4) repeated topology revocation — fixed at 67de737b (revoke idempotence guard + transition-matrix test); (5) pet cleanup after terminal restoration on direct stop — fixed at 189a227b (raster lifecycle epoch; gated-ingress, paused-at-await, predicate-stop, restart-delivery regressions in raster-lease.test.ts 34/34). No new findings on this head. CI 31910918162: all technical jobs green; only the needs-human approval gate remains — @probepark @HaD0Yun, an APPROVED review at 2b75098e unblocks the merge-approved promotion.


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

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

MERGE_READY (owner classification, exact head 2b75098e, base 91a35114, digest sha256:56669e9a7b741947635f495009c2f93bc72d67d47b7be8908832095759154832 — independently recomputed from the GitHub head and matching the body verdict line).

Superseded-run classification: run 31910887721's single red (job 95075028753) was the interactive-mode-editor-component.test.ts shared-policy assertion, which had baked in the multiplexer warning variant while CI runs TMUX-less; fixed by 2b75098e (variant-agnostic prefix, verified under both host-TMUX and TMUX-less env). After rerun, 31910887721 is also all-green except bootstrap — not treated as final or as product failure. Authoritative replacement 31910918162 is terminal: every technical job green (affected plan, native-build, all test/check shards including the previously failing one, evidence producer, aggregate, state gates, virtual integration). The only red on both same-head runs is PR contract bootstrap = the intentional needs-human gate, with a current digest (no stale-digest diagnostic).

Review comments: every inline comment anchored at 2b75098e is a re-anchor of a comment originally filed at an older head (53b6b7a / 2d30ea2 / 1f76721 / 676000d); each was previously reproduced/classified and carries a fix + deterministic regression on this branch (multiplexer guard, stopped-TUI writes, drag ordering, revoke idempotence, stop-cleanup epoch, plus the later fragmentation/shouldWrite/warning fixes). No new findings exist at this head.

Outstanding blocker — exactly one: an effective APPROVED review at 2b75098e from a write-authorized non-author reviewer. @probepark (primary) and @HaD0Yun (backup) are formally requested and unresponded. Per the repository contract the verdict cannot be self-approved; on approval I will promote the single verdict line to merge-approved with this same digest, let bootstrap go green, and squash-merge to dev immediately.


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

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

Approve at 2b75098e7

fix(pet): stabilize iTerm2 Gajae Pet placement, drag handling plus a raster-lease boundary. 19
files, +5416 - the largest thing in this batch.

Differential

# base 91a35114d, with this head's test files applied
 82 pass  41 fail

# head 2b75098e7
 240 pass  0 fail
$ bun --cwd=packages/tui run check           -> exit 0
$ bun --cwd=packages/coding-agent run check  -> exit 0

41 failures on base against a suite that is fully green at head. For a change this size that ratio is
what I want to see - the tests are not decoration around a refactor, they are the specification of
new behaviour.

The raster-lease boundary is the substantive part

The pet placement is the visible feature; the lease lifecycle is where the risk lives. These
assertions cover it properly:

  • retains failed cleanup, blocks reacquire, then reports retry counts - a failed cleanup must not
    silently free the lease. Blocking reacquire until recovery is what stops two owners drawing into
    the same cells.
  • retries two cleanup records independently and releases only recovered dependent FIFO work -
    independent retry with dependent release. Coupling them would let one stuck record hold everything
    behind it, or worse, release work whose dependency never recovered.
  • erases raster leases before entering the manual history viewport and before revealing a manual transcript anchor - inline images that survive a viewport change are the classic terminal-graphics
    artifact, and they persist across redraws.
  • never writes queued raster work after stop and resumes cleanly after start - the stop/start
    boundary, where queued work escaping after teardown corrupts whatever the terminal shows next.

The head commit is the right instinct

"make warning assertion independent of multiplexer env" - a TUI test that behaves differently under
tmux than in a bare terminal is one that fails for whoever happens to run it in the wrong place. Same
class as the platform-bound test I blocked in #4539, caught here without prompting.

merge-approved.

Reviewed by @probepark - method: fresh-worktree run and both package typechecks at the exact head, separate clean base worktree with the head's test files to prove the differential.

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

Verdict promoted to merge-approved with the exact-head digest sha256:56669e9a7b741947635f495009c2f93bc72d67d47b7be8908832095759154832, backed by @probepark's APPROVED review at 2b75098e (write-authorized, non-author). The 04:03 PR-contract run raced the body edit against the approval event; this comment re-triggers the exact-head contract validation.


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

@Yeachan-Heo
Yeachan-Heo merged commit 05d33c9 into Yeachan-Heo:dev Aug 16, 2026
120 of 151 checks passed
@Yeachan-Heo

Copy link
Copy Markdown
Owner

Post-merge reconciliation complete. PR #4591 was squash-merged to dev as 05d33c998 after @probepark's exact-head APPROVED review at 2b75098e (write-authorized, non-author), verdict promoted to merge-approved with the same digest sha256:56669e9a7b741947635f495009c2f93bc72d67d47b7be8908832095759154832, PR contract + Dev CI bootstrap green at merge time.

The merge surfaced three latent regressions that the PR's affected-path matrix had never executed, each reproduced, root-caused, fixed, and verified on dev (fix-forward commits, bun run build green): (1) 60a92c7b resize byte-stream drift — the pet change wrote a cell-size query and a duplicated synchronized-output begin on every resize, drifting three render goldens byte-exactly while leaving rendered content identical; the query is now scoped to sessions that actually held a raster lease and the inner frame begin to the lease-preserving repaint branch. (2) e1849e67 post-render overlay framing — the pet overlay hardcoded the 2026 begin/end pair, violating the synchronized-output-disabled compatibility contract; it now routes through the setting-aware wrap. (3) 52dad458 ownerless composer remount — the pet ownership gate made remountComposer a no-op when the pet was never activated, so the command palette closed without remounting the editor (an interaction with dev's backfilled pet rework that only the merged tree exposed); an ownerless container now remounts the plain editor while a registered owner still wins.

Final state: dev head 52dad458, Dev CI 31933875101 fully green across every job, local bun run build exit 0, TUI suite 1176 pass / 0 fail, focused pet battery green, both package checks green. Lane retired; no issue closure (none linked).


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

Yeachan-Heo pushed a commit that referenced this pull request Aug 16, 2026
…rk failure

When moveTreeNoReplace fails during managed fork artifact publication (e.g. an identity_mismatch at the rename boundary), the failed move has already disposed of the staging tree. The catch-block cleanup then calls removeTreeExpected on a staging directory that no longer exists, and the native exact-remove reports not_found — which the wrapper treated as an independently real cleanup failure, superseding the primary error with "Failed to clean up managed fork artifacts: not_found" (primary preserved only as cause).

Per the precedent set by f6fb5c8, only a cleanup failure that can leave a live artifact behind may supersede the primary. A not_found on the staging tree is the strongest possible evidence that nothing is left to clean: the tree is absent. Treat it like cleanup_pending — authorized cleanup — so the primary failure reaches the caller verbatim.

Reconstructed onto dev 52dad45 (first fully green canonical dev after the shared-shard repair: #4597 apply-patch marker fix, #4591/#4604 pet-tui cluster, #4595, #4598, #4600). Identical one-line delta as originally submitted at 87bee51.

Lore-id: fork-staging-notfound-masking
Constraint: only cleanup failures that can leave a live artifact behind may supersede the primary error
Confidence: high
Scope-risk: narrow
Reversibility: trivial
Tested: discrimination suite green at reconstructed head with red control on one-line revert; session-manager/ 307 pass / 0 fail; transition-seam + session-import 55/0; biome + tsc clean
Not-tested: Windows exact-remove codes
Supersedes: 3059adb
Yeachan-Heo added a commit that referenced this pull request Aug 16, 2026
…rk failure (#4588)

When moveTreeNoReplace fails during managed fork artifact publication (e.g. an identity_mismatch at the rename boundary), the failed move has already disposed of the staging tree. The catch-block cleanup then calls removeTreeExpected on a staging directory that no longer exists, and the native exact-remove reports not_found — which the wrapper treated as an independently real cleanup failure, superseding the primary error with "Failed to clean up managed fork artifacts: not_found" (primary preserved only as cause).

Per the precedent set by f6fb5c8, only a cleanup failure that can leave a live artifact behind may supersede the primary. A not_found on the staging tree is the strongest possible evidence that nothing is left to clean: the tree is absent. Treat it like cleanup_pending — authorized cleanup — so the primary failure reaches the caller verbatim.

Reconstructed onto dev 52dad45 (first fully green canonical dev after the shared-shard repair: #4597 apply-patch marker fix, #4591/#4604 pet-tui cluster, #4595, #4598, #4600). Identical one-line delta as originally submitted at 87bee51.

Lore-id: fork-staging-notfound-masking
Constraint: only cleanup failures that can leave a live artifact behind may supersede the primary error
Confidence: high
Scope-risk: narrow
Reversibility: trivial
Tested: discrimination suite green at reconstructed head with red control on one-line revert; session-manager/ 307 pass / 0 fail; transition-seam + session-import 55/0; biome + tsc clean
Not-tested: Windows exact-remove codes
Supersedes: 3059adb

Co-authored-by: Yeachan Heo <yeachan.heo@gmail.com>
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.

3 participants