fix(ssh): pinned expanded pending preview commit-unstable
The SSH renderer previously declared 'provisionalPendingPreview: "collapsed"', which only opted the COLLAPSED pending shape out of the transcript's stable-prefix ratchet. Once the user expanded an in-flight SSH preview (ctrl+o) and the framed block outgrew the viewport, the pending rows became ratchet-eligible and committed to native scrollback before the result render inserted the 'Output' section. The settled render then re-anchored the frame, producing two distinct stranded shapes in history: - a stale 'pending SSH: [host]' header pinned above the final '<- SSH: [host]' frame (header variant), and - the pending bottom border row reused in-place as the new 'Output' separator, with a fresh '...' footer pushed below it (footer variant). Flip 'provisionalPendingPreview' to 'true' so every pending shape — collapsed or expanded — is treated as provisional and stays out of native scrollback until the result render commits a settled frame. The 'collapsed'-only opt-out remains correct for renderers (bash, eval) whose expanded pending preview is top-anchored and survives the result render without re-anchoring. Added two contract tests asserting expanded pending SSH is commit-unstable and that bash/eval expanded pending preview is still commit-stable — keeping the opt-in renderer-scoped. Fixes #3714
This commit is contained in:
@@ -46,6 +46,34 @@ describe("ssh tool block commit stability", () => {
|
||||
expect(component.isTranscriptBlockCommitStable()).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps the collapsed pending SSH preview commit-unstable until a result arrives", () => {
|
||||
// Issue #3714: with `provisionalPendingPreview: true` the pending call
|
||||
// preview is commit-unstable regardless of expansion, so neither the
|
||||
// `⏳ SSH: [host]` header nor the framed bottom border can leak into
|
||||
// native scrollback before the result render inserts `Output`.
|
||||
const component = makeSshComponent();
|
||||
component.setArgsComplete();
|
||||
|
||||
expect(component.isTranscriptBlockFinalized()).toBe(false);
|
||||
expect(component.isTranscriptBlockCommitStable()).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps the expanded pending SSH preview commit-unstable until a result arrives", () => {
|
||||
// Issue #3714: the previous `"collapsed"` opt-out left expanded pending
|
||||
// rows commit-stable. Once the box outgrew the viewport the stale
|
||||
// `⏳ SSH: [host]` header and the pending `╰──╯` footer reached native
|
||||
// scrollback, then the final result re-anchored the frame and stranded
|
||||
// the pending rows above (header variant) or reused the footer row in
|
||||
// place as `├── Output ──┤` (footer variant). Expanded MUST also be
|
||||
// commit-unstable until the result render replaces the pending shape.
|
||||
const component = makeSshComponent();
|
||||
component.setExpanded(true);
|
||||
component.setArgsComplete();
|
||||
|
||||
expect(component.isTranscriptBlockFinalized()).toBe(false);
|
||||
expect(component.isTranscriptBlockCommitStable()).toBe(false);
|
||||
});
|
||||
|
||||
it("flips commit-stable as soon as the SSH result settles", () => {
|
||||
const component = makeSshComponent();
|
||||
component.updateResult(partialResult("connecting…"), true);
|
||||
@@ -65,4 +93,18 @@ describe("ssh tool block commit stability", () => {
|
||||
expect(component.isTranscriptBlockFinalized()).toBe(false);
|
||||
expect(component.isTranscriptBlockCommitStable()).toBe(true);
|
||||
});
|
||||
|
||||
it("does not opt other foreground tools out of expanded pending-preview commits", () => {
|
||||
// Sanity: bash/eval still use `provisionalPendingPreview: "collapsed"`,
|
||||
// so once expanded their pending preview is commit-stable. The SSH
|
||||
// `true` opt-in MUST remain renderer-scoped — flipping the default
|
||||
// here would block long top-anchored streams (e.g. a task call's
|
||||
// context/assignment markdown) from reaching native scrollback.
|
||||
const component = new ToolExecutionComponent("bash", { command: "ls" }, {}, undefined, uiStub);
|
||||
component.setExpanded(true);
|
||||
component.setArgsComplete();
|
||||
|
||||
expect(component.isTranscriptBlockFinalized()).toBe(false);
|
||||
expect(component.isTranscriptBlockCommitStable()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user