Fixed a couple sneaky bugs with prompts.

This commit is contained in:
Storm Dragon committed 2026-10-28 23:27:05 -04:00
1 parent c842a1e3db
commit 21cecd1e71
3 files changed
+55 -6

No files matched your search

+4 -2
View File
@@ -259,9 +259,11 @@ func (a *application) observe_override(next screen.Snapshot) (applicationoverrid
} }
frame := selected.adapter.Observe(next) frame := selected.adapter.Observe(next)
freshShell := selected.shell.Observe(next) freshShell := selected.shell.Observe(next)
if !frame.Recognized { // A fresh shell prompt ends the override even if an adapter still recognizes
// stale application content or its bridge has not reported closure yet.
selected.exitPending = selected.exitPending || freshShell
if !frame.Recognized || selected.exitPending {
selected.recognized = false selected.recognized = false
selected.exitPending = selected.exitPending || freshShell
if a.overrideCurrent == selected { if a.overrideCurrent == selected {
if !a.restore_override(func() {}) { if !a.restore_override(func() {}) {
return applicationoverrides.Frame{}, false return applicationoverrides.Frame{}, false
@@ -5,6 +5,7 @@ package app
import ( import (
"errors" "errors"
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
@@ -149,6 +150,45 @@ func TestExitWaitsForPendingSpeechChangeAndRestoresBeforeShellOutput(t *testing.
} }
} }
func TestCodexShellExitWithoutBridgeClosure(t *testing.T) {
for _, pendingSpeech := range []bool{false, true} {
t.Run(fmt.Sprint(pendingSpeech), func(t *testing.T) {
a, voice, _ := selection_application(t, true)
a.prepareSpeech = func(config.Speech) (speech.Transport, error) { return &preparedVoice{}, nil }
composer := override_screen(1, "› hello")
composer.X = 7
override_read(t, a, composer)
a.select_override("codex")
override_read(t, a, composer)
baseline := a.overrideCurrent.underlying.settings.live
if pendingSpeech {
if response := request_selection(t, a, "SET speech#voice=Temporary;speech#module=fixture"); response != "" {
t.Fatal(response)
}
}
shell := override_screen(1, "[Username@host directory] $")
copy(shell.Line(20), []rune("› previous composer"))
shell.X = len([]rune(shell.Text(23))) + 1
override_read(t, a, shell)
if pendingSpeech {
finish_selection(t, a)
override_read(t, a, shell)
}
if a.overrideCurrent != nil || a.overrides[1] != nil || !reflect.DeepEqual(a.settings.live, baseline) {
t.Fatal("shell retained Codex settings without a bridge close event")
}
next := shell
next.X--
start := len(voice.messages)
a.engine.Note_input(true, false, false)
override_read(t, a, next)
if len(voice.messages) == start {
t.Fatal("shell arrow navigation remained silent")
}
})
}
}
func override_read(t *testing.T, a *application, snapshot screen.Snapshot) { func override_read(t *testing.T, a *application, snapshot screen.Snapshot) {
t.Helper() t.Helper()
a.source.(*fakeSource).snapshot = snapshot a.source.(*fakeSource).snapshot = snapshot
+11 -4
View File
@@ -21,6 +21,9 @@ def fixture_shell():
data = os.read(0, 4096) data = os.read(0, 4096)
if not data: if not data:
return return
if data == b"\x04":
os.write(1, b"\x1b[24;1H\x1b[2K[Username@host directory] $ ")
continue
os.write(1, data) os.write(1, data)
@@ -128,13 +131,17 @@ def main():
bridge.write_status(statusPath, "done") bridge.write_status(statusPath, "done")
time.sleep(0.4) time.sleep(0.4)
action("Insert+0", "Codex finished") action("Insert+0", "Codex finished")
action("Insert+a", "Application Codex") # Leave old Codex/composer text visible and keep the bridge alive:
action("d", "Default") # the fresh shell prompt itself must restore ordinary behavior.
action("Return", "Default settings restored") action("ctrl+d", "Default settings restored")
keyboard.key("h", "e", "l", "l", "o")
action("Left", "o")
action("Left", "l")
action("Right", "o")
keyboard.key("Insert+q") keyboard.key("Insert+q")
wait_for(lambda: (root / "finished").exists(), "reader exit") wait_for(lambda: (root / "finished").exists(), "reader exit")
assert not server.errors, server.errors assert not server.errors, server.errors
print("PASS: real private PTY/override/socket bridge, composer Left/Right feedback, baseline history, completed messages only, one announcement, long replies, replay and status") print("PASS: real private PTY/override/socket bridge, composer Left/Right feedback, baseline history, completed messages only, one announcement, long replies, replay, status, shell exit without bridge closure and restored shell arrows")
except BaseException: except BaseException:
print("Recent private speech:", server.events[-15:], file=sys.stderr) print("Recent private speech:", server.events[-15:], file=sys.stderr)
if (root / "status.json").exists(): if (root / "status.json").exists():