Commit Graph
559 Commits
Author SHA1 Message Date
Neil 2d8f09d137 Merge remote-tracking branch 'origin/main' into nwparker/sta-3077-reattach-pane-cardinality 2026-08-12 20:24:09 -07:00
Neil 0ba2bed7ee fix(ssh): harden PTY reattach reliability 2026-08-12 20:23:51 -07:00
erish 1192f73f4d docs(i18n): sync Korean README with current content (#14124) 2026-08-12 17:32:36 -07:00
Neil 5dc1e01857 Merge remote-tracking branch 'origin/main' into nwparker/sta-3077-reattach-pane-cardinality 2026-08-12 03:48:31 -07:00
github-actions[bot] ad6f8011e2 Update README downloads badge 2026-08-12 09:33:52 +00:00
Neil e03728c2d0 Merge origin/main into STA-3077 (9 commits)
Two conflicts:
- .gitignore — both sides added doc negations; kept both.
- orchestration/db.ts — this branch moved `isEquivalentPaneKey` into
  shared/stable-pane-id (collapsing a duplicate), while main added
  `parseWorkerTerminalPriorOwnerIds` beside the old local copy. Kept the move
  and kept main's new helper, rather than resurrecting the duplicate.

43,250 pass. The 7 failures are the same environment-specific pre-existing ones
(osc8 snapshot round-trip, IME resumed-preedit) that fail on clean origin/main
in this environment.
2026-08-12 02:03:38 -07:00
Neil 9c091cf77e chore(perf): add renderer agent-status benchmark harness (#13905)
Splits run-idle-cpu-benchmark.mjs into a scale fixture, an in-page timing
probe, and process sampling, and records a measured origin/main baseline so
the agent-status batching slice has an auditable before.

The agent-status write workload is not included: it needs setAgentStatuses,
so it lands with the store slice.
2026-08-12 00:37:48 -07:00
Neil 26af3d8234 Merge origin/main into STA-3077 (287 commits)
Three conflicts, all resolved keeping both sides:

- persistence.ts — both sides added a field to persistPtyBinding's options.
  Kept `mayCreate` (ours) and `expectedSourceBinding` (main's).
- ipc/pty.ts — main added an `expectedSourceBinding` CAS and a
  `terminal_split_source_not_found` throw to the exact block our `bindPaneShell`
  producer replaced. Threaded `expectedSourceBinding` THROUGH the producer rather
  than dropping it, and kept main's refusal throw on `bound === false`. Dropping
  it would have silently disarmed a CAS main just added.
- pty-connection.test.ts — main added `startupCommandDelivery: 'shell-ready'` to
  a resume assertion this branch deletes outright (the resume no longer happens).
  Took ours; main's addition is moot in an inverted clause.

Also repairs a rename/rename divergence unrelated to STA-3077: both sides deleted
a different half of a duplicated xterm-bypass fixture, so the merge deleted both.
Restored main's `xterm-bypass-event-fixture.ts` and repointed three test imports
at it, adopting main's convention rather than this branch's older move.

Verified after merge: 43,179 tests pass. The 7 failures (osc8 snapshot
round-trip, IME resumed-preedit) are PRE-EXISTING on clean origin/main — checked
against a baseline worktree, not assumed. Typecheck clean on node and web.
2026-08-11 23:11:00 -07:00
Jinjing 97ebfd9b81 fix(cmd-j): improve keyboard selection visibility in light mode (#13855)
Use color-mix to blend selection highlight instead of flat accent, making the keyboard cursor visible on light popover surfaces in both light and dark modes. Fix race condition in selection handling where deferred query lag could break arrow navigation by always accepting cmdk's internal cursor updates.
2026-08-11 12:31:30 -07:00
Brennan Benson 915ee50cc2 fix(ui): rename Caffeinate Auto mode to Agent (#13751)
* fix(ui): rename caffeinate auto mode to agent

* fix(ui): center caffeinate radio indicator

* fix(ui): center caffeinate indicator within option
2026-08-10 23:50:41 -07:00
Brennan Benson 4dcb410982 docs: remove restored shell validation report (#13759) 2026-08-10 23:49:38 -07:00
Brennan Benson 7250fde148 fix(codex): repair hook trust before restored shells reconnect (#13745)
* fix(codex): reconcile hooks for restored shells

* docs: move restored shell validation report

* perf(codex): gate retained pane inventory
2026-08-10 23:47:32 -07:00
Neil 1979f5a3e3 refactor(linear): remove replaced team combobox (#13506) 2026-08-10 20:25:16 -07:00
github-actions[bot] 1c1107fc9b Update README downloads badge 2026-08-11 00:33:35 +00:00
Brennan Benson d50adec2d2 feat(ai-vault): isolate scanning from terminal workloads (#13411)
* feat(ai-vault): isolate scanning in service processes

* fix(ai-vault): retire idle service processes

* fix(ai-vault): discard unverified cache processes

* fix(ai-vault): clear relay sidecar cancel watchdog on acknowledgement

A cancelled relay call is settled before its 2s cancel watchdog is armed, so the acknowledgement path bailed out of settle() before clearing the timer. The watchdog then faulted a healthy sidecar two seconds after every aborted scan, killing whatever request had since become active.

* fix(ai-vault): clear the pending restart before scheduling another

recordFault overwrote this.timer, stranding a restart that dispose() could no longer cancel.

* refactor(ai-vault): drop the orphaned first-prompt IPC wrapper

session-first-user-prompt-handler.ts now owns this entry point and routes through the service; the copy left in the read module had no callers.

* fix(ai-vault): retry a faulted cold start before surfacing it

A slow first start surfaced a raw 'did not become ready' error to the caller even though the supervisor was already respawning. Requeue an unsent call once onto the scheduled respawn instead.

Also stop arming the cancellation watchdog for a call the child never received: no acknowledgement is coming, so it killed a healthy service and stalled the lane.

Invalidation bookkeeping and ready-waiter construction move to the state module to stay under the max-lines cap.

* fix(ai-vault): give relay title reads their own lane

Before this branch the relay read title files directly, concurrently with scans. Routing both through one sidecar lane put title resolution behind a list scan that may run up to 130s, so SSH tab titles could lag minutes behind.

Split cache and interactive lanes in both the relay client and the sidecar entry, mirroring the desktop service.

Also: clear the ready deadline on fault, so a sidecar that dies before ready cannot fault its healthy replacement five seconds later; retry an unsent call once across a respawn; and skip the cancellation watchdog for a call the sidecar never received.

Restart/circuit bookkeeping moves to its own module, mirroring the desktop policy, to stay under the max-lines cap.

* fix(ai-vault): degrade relay title resolution on sidecar failure

listSessions already returns a host issue when the sidecar is unavailable; titles propagated the raw RPC error instead. Return no titles so callers fall back to preview text, and keep cancellation propagating.

* fix(ai-vault): scrub the service child environment

The children are forked with a 384 MiB heap cap and no loader, but both
spawn sites handed them the full parent environment, so an exported
NODE_OPTIONS silently raised the cap or --require'd code into them.

Allowlist both, following the plugin worker. The desktop child keeps the
eleven agent-root overrides it resolves its own roots from; the relay
sidecar takes remoteHome and hostPlatform from its init message and so
needs none of them. Both children share one priority module while they
share this one.

* fix(ai-vault): soft-disable relay vault when the service is missing

A missing service threw out of the constructor, so a Vault wiring bug
would abort relay startup and take every PTY on the host with it. The
unsupported-platform branch three lines above already treats a Vault
failure as a soft disable; do the same here.

Threading the service through the two handlers instead of a field also
retires the definite-assignment assertion the throw was propping up.

* fix(ai-vault): drain consumed cache invalidations

invalidatedPaths was re-applied in every request's finally and never
drained, so once N paths had been invalidated every later request paid N
evictions for the life of the process; the 4096 cap only bounded how bad
that got.

The re-apply exists to cover a read that overlapped the invalidation, so
drain once nothing is executing. Clearing unconditionally would drop the
re-apply for a request still running on the other lane.

* fix(ai-vault): keep a busy child through slow invalidation acks

invalidate() reused the 5s ready budget as its acknowledgement deadline
and killed the child on expiry, so a delete issued during a large scan
could kill a healthy process mid-scan and burn a slot toward the restart
circuit.

Fault only when nothing is executing. Fork IPC ordering already puts the
invalidation ahead of any later request, so a busy child owes no ack
here, and the 130s/15s request deadlines still catch a wedged one.

The start-retry predicate moves to the state module to stay under the
line cap, matching the shape the relay client already uses.

* fix(ai-vault): report a failed local scan as a host issue

A local-scope scan let its error escape to the renderer, which paints it
over the session list. Service supervision now produces those errors, so
"AI Vault service restart circuit is open." replaced the list.

Route local scope through the degradation the all-hosts leg and every SSH
leg already use, so it lands as a retryable host issue row instead. Same
result shape either way, so no IPC or wire contract changes.

* test(ai-vault): cover the relay restart circuit transitions

The relay policy shipped without tests. Pin both circuit edges, the
aging-out case, the forced-refresh reopen the relay has and the desktop
does not, and the backoff schedule.

* fix(ai-vault): keep the OpenCode roots in the service child env

The scrubbed allowlist dropped XDG_DATA_HOME and OPENCODE_DB, which the child
reads to locate the OpenCode store and database. The pre-PR worker thread
inherited them, so a user who sets either lost every OpenCode session.

* test(ai-vault): anchor the service spawn env assertion
2026-08-10 15:52:59 -07:00
Neil 6cdd019367 docs(mobile): keep Android links on published release (#13565) 2026-08-10 02:22:35 -07:00
Neil bbcf89e221 chore(mobile): prepare 0.0.43 releases (#13549) 2026-08-10 01:49:33 -07:00
Jinjing 8331fe5995 docs: update GitHub star history chart (#13380)
Refresh README star history graphic to the latest ~40K stars curve (through August).
2026-08-09 15:17:11 -07:00
Neil c20eb15ecf docs(terminal): record the two defects found after the goalposts were met
RC2 survived S3 and was found by writing the E2E test, not by reading. The load
fold left the incarnation half of the pane fence behind and was found by
adversarial review. Both were invisible to unit oracles that had already gone
green, which is the useful part of the record.
2026-08-09 02:41:32 -07:00
Neil 460e68630d docs(terminal): record S3, S4, S5 and S8 proven, and G1 missed
All seven step goalposts are now proven. G1 (net-negative production) is NOT met
at +83 and is reported as a miss with a per-step breakdown rather than reframed.

Also records the near-miss G5 caught: S4's reader guard showed no mutation
response because the load fold had already erased the divergent state at boot.
It was correct and would have shipped unproven. An added clause pins it.
2026-08-09 01:51:45 -07:00
NeilandOrca d0abd32396 docs(terminal): add a resume prompt for a clean session
Points at the goalposts as the contract, names the three goalposts whose oracles
are already written and red, and carries the process rules that were learned the
expensive way — prove guards reachable, verify mutations land, commit per step,
and never let a subagent write production files in a shared worktree.

Co-authored-by: Orca <help@stably.ai>
2026-08-09 00:47:31 -07:00
NeilandOrca 66c1996061 docs(terminal): keep the full design detail in the repo
It only existed in an ephemeral job directory, so the plain-English explainer
had no durable source for its specifics — record shape, death rule, reattach
algorithm, migration order and the 25 oracles.

Co-authored-by: Orca <help@stably.ai>
2026-08-09 00:46:49 -07:00
NeilandOrca 1847812cd8 docs(terminal): record that the recovery grant is dead code, deleting a design step
The lease stores a relay-native pty id and the caller passes the app form, with
a raw equality comparison between them, so the 30s grant cannot fire for a real
SSH pane. The death rule that existed to referee it is deleted rather than
built, and the dead path itself becomes a removal.

Co-authored-by: Orca <help@stably.ai>
2026-08-09 00:45:25 -07:00
NeilandOrca 97dc07e163 docs(terminal): add tracked goalposts for the new design
Each goalpost is a behaviour with an oracle and the mutation that must redden
it, so 'proven' cannot be claimed from a green test. Records the anti-inert rule
as a first-class goalpost, since three guards in this program passed their tests
while sitting off the route production takes.

Co-authored-by: Orca <help@stably.ai>
2026-08-09 00:44:31 -07:00
NeilandOrca 686dc08334 docs(terminal): rewrite the design explainer in plain English
The first version assumed the reader knew the codebase. Reframed around two
bugs, two fixes and one decision, with the jargon replaced by pane / program /
note / helper and a five-word glossary for what could not be avoided.

Co-authored-by: Orca <help@stably.ai>
2026-08-09 00:21:59 -07:00
NeilandOrca 239934f682 docs(terminal): explain the landed design on its own terms
One leaf-keyed ownership record, orphans computed at connect, and replacement
shells only on positive proof — with the shipping order and the one product
trade the design asks the owner to accept.

Co-authored-by: Orca <help@stably.ai>
2026-08-09 00:14:33 -07:00
NeilandOrca b287830f02 docs(terminal): correct the report — the respawn proof gate guards a minority path
A final review traced every auto-respawn route. The primary one converts the
reattach failure into a boolean before any classifier sees it, so the shipped
proof gate never runs there. Records that two of the six shipped changes are
narrower than claimed, and why their tests could not have caught it.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 23:58:20 -07:00
NeilandOrca 5ea8f6c917 docs(terminal): report the design counsel's outcome and the live respawn bug it found
Three review rounds across two models replaced the two-record split with one
leaf-keyed record, deleted attach-time pane identity, and made orphans a
connect-time projection. Records that a shipped gesture still turns a healthy
remote shell into a duplicate agent resume, and that the renderer classifier in
that chain treats an error-message shape as proof of death.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 23:56:05 -07:00
NeilandOrca 2564f86c1a docs(terminal): add the design brief the review counsel works from
Separates verified code facts from unverified leads so reviewers attack the
design rather than a reconstruction of it, and records which simpler
alternatives were already refuted and why.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 21:57:49 -07:00
NeilandOrca 88bd43d51f docs(terminal): record the finalized two-plane architecture decision
Adopts the data-plane proposal and adds the control-plane track it does not
cover: re-key ownership by pane, split orphan inventory out, then delete the
compensating code. Records that the host-authority alternative was refuted and
that the shipped keystroke fence is inert on the reattach path.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 21:41:51 -07:00
NeilandOrca f9e8f81a45 docs(terminal): add a self-contained visual report of the design and its evidence
Pre-renders every diagram to inline SVG in both themes so the report opens
offline and stays sharp when zoomed. States the gate/journey score and the
retractions alongside the fixes, so the unproven half is as visible as the
proven half.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 20:15:17 -07:00
NeilandOrca c29f0e4d16 docs(terminal): add plain-language design and test overviews
Two reviewable documents with diagrams, written so someone with no prior
context can follow what breaks, why, and what changed.

The design overview explains the five things stacked behind one terminal
rectangle, the 2 -> 19 -> 20 report, the three root causes, and the rule
underneath all of them: unknown is not dead.

The test overview explains why a green test proves nothing on its own,
the four-step mutation proof we adopted, and — the part worth reviewing
hardest — an honest account of what could not be proven and why, including
the properties that are true by construction and therefore have no guard
to remove.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:53:35 -07:00
NeilandOrca 317b40414b docs(terminal): the fixture clause already holds where it matters
Checked what the build emits rather than reasoning from file paths. None
of the 32 test-only fixtures appears in `out/` — Rollup drops them because
no production entrypoint reaches them. On "compiles into the shipped
product", this clause holds today.

On the other reading it cannot be closed by moving files at all: both
production tsconfigs use bare `include` globs with no `exclude`, so a
`__tests__/` directory matches exactly like any other path, as does every
`*.test.ts` in the repo. Relocating 32 fixtures would remove nothing from
typecheck scope.

A sweep was started and stopped once this was verified, rather than
landing 32 moves across areas this program does not own for no gain. If
the intent is that typecheck scope should exclude test code, that is a
repo-wide tsconfig change with a different owner.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:50:20 -07:00
NeilandOrca dfe8b47507 refactor(terminal): finish a half-done fixture move and audit the rest
`xterm-bypass-event-fixture.ts` and `__fixtures__/xterm-bypass-event.ts`
were byte-identical apart from an import path. The `__fixtures__` copy had
zero importers and the live copy compiled as production — someone started
the move and left both. Dead copy deleted, live one moved, its three test
importers updated.

Audited the wider G6 clause by importer rather than filename: 32 test-only
files, roughly 3,300 LOC, currently compile as production; 4 of the 36
candidates have real production importers and are correctly placed. The
list is recorded in the goalposts.

Those 32 are almost all older than this program and outside the terminal
surface, so sweeping them belongs in its own change rather than inside a
terminal PR. The clause stays failing, with the remaining files named.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:47:18 -07:00
NeilandOrca 0037f7b3a4 docs(terminal): the input quarantine is load-bearing, not superseded
G6 lists "no superseded quarantine remains reachable" and this module was
assumed to be one. Disabling its single call site reproduces the hazard
it exists for — `cho hi; rm -rf x` reaching the shell — so deleting it
without a replacement re-opens command execution.

The replacement was costed by building it rather than estimated: +26
production LOC to thread the incarnation, ~+33 complete, and the
cross-remount state it needs outlives the destroyed pane so it becomes a
module about the size of the one deleted. Floor is roughly +140 to delete
88, and it would add a second identity comparison to a gate already
failing for having more than one.

The decisive part is that the route is not uniformly available: remote
runtime results carry no incarnation, old hosts cannot be made to publish
one, and mixed versions are the normal state. A paired client reads
unknown, which this program's own rule says is not proof — so either
every remote reattach surfaces unresolved, or a fallback is needed and
the only correct fallback is this module.

Whether to amend the clause or accept something weaker on remote hosts is
a user decision, so the clause verdict is left as failing rather than
quietly reclassified.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:40:48 -07:00
NeilandOrca a02fa82924 docs(terminal): reconcile G6 with the recorded decision and assess its clauses
G6's body still demanded strictly-negative production LOC after the user
relaxed it to minimise-and-justify, so the gate had two conflicting pass
conditions and no single truth value. Its body now points at that
decision.

Assessed the remaining clauses against the branch rather than assuming.
Two fail structurally: more than one identity comparison and mutation
admission path still exist, and `terminal-input-quarantine.ts` is still
reachable from two production files.

Records why the quarantine is not subsumed by the superseded-PTY fence,
which I had assumed and checked. The fence refuses writes aimed at a
stale ptyId; the quarantine guards the user's next keystrokes landing on
the successor under its current, correct id — a case the fence never
sees. Removing it needs the recovery path to surface a different shell as
unresolved, not a deletion.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:54 -07:00
NeilandOrca 5ed7114c6a docs(terminal): record why the duplicate-resume fix was not built
I recommended adding a typed end-reason so a user quit stops looking like
a resume candidate, then went to implement it and stopped.

`SleepingAgentSessionRecord` already carries three fields that each exist
to stop something resuming that should not have — `origin`,
`restoreOnTabOpenOnly`, and `automaticResumeBlockedBy` — each traceable
to its own incident, consulted at 22 non-test sites. A fourth predicate,
however well typed, is the fifth containment cycle.

The designs without this bug do not have a better flag; they resume only
on an explicit action, into a new terminal id, and make two agents in one
terminal unrepresentable in the schema. The first of those is a product
decision about whether automatic resume stays a feature, so it is the
user's call rather than mine.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:54 -07:00
NeilandOrca 6b6a3b666a test(ssh): add MaxSessions, lazy-discovery and paired-skew oracles
Three journeys attempted; none promoted, and the reasons are recorded in
the ledger rather than rounded up.

MaxSessions=1 against real OpenSSH, with the cap read back from `sshd -T`
rather than assumed, and remote pids read on the container two
independent ways that must agree, each carrying its kernel start time.
Two disjoint mutations discriminate — one reddens only the reconnect
clause, the other only the two restart clauses. But the disconnect clause
is a forward guard: four separate guard removals left it green, so
nothing shipped is load-bearing for it.

Lazy discovery samples sshd's own accept log and live session census
across a 22s window with the in-use host as a positive control. No
mutation reddens its third clause alone — the real cross-host lease
scoping is load-bearing, but removing it breaks the sibling host during
setup, so the failure carries no clause information.

The paired-runtime skew spec pairs two real processes at different
versions and refuses to run rather than degrade into a same-version
pairing that would look green and prove nothing.

No production code changes.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:54 -07:00
NeilandOrca d0f747b391 docs(terminal): correct the WSL provider-suite diagnosis
The WSL run blamed bash 5.3.9 for the unrelated
`local-pty-shell-ready` failure. macOS runs the same bash version and
passes 67/67, so the version is not the cause — the trigger is
environmental to that distro, and the underlying defect is that the spec
asserts an absolute count of OSC markers it does not own.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:54 -07:00
NeilandOrca 06cd858dc0 docs(terminal): promote Journey 2 to proven on macOS, Linux and physical WSL
The oracle runs on every environment the journey names, and is
clause-selective on all three: reverting three-valued `hasPty` reddens
only the unknown-not-dead clause, and widening the sole-provider fallback
reddens only the stale-generation clause.

Selectivity in WSL was established rather than assumed. The spec runs
serially, so a red first test reports the others as "did not run" — they
were re-run alone under the same mutation and stayed green.

Also records that an Orca WSL-mode terminal now starts on that host at
all, which it could not before: the distro had no provisioned default
Unix user, so every interactive launch blocked on first-run setup.

One diagnosis from the WSL run is corrected here rather than repeated:
the unrelated `local-pty-shell-ready` failure was attributed to bash
5.3.9, but macOS runs the same bash version and passes 67/67. The trigger
is environmental to that distro, and the underlying defect is that the
spec pins an absolute count of OSC markers it does not own.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:54 -07:00
NeilandOrca d0d942bb1c docs(terminal): record journey evidence that falls short of promotion
Four journeys now have discriminating oracles but none meets its full
stated scope, and each shortfall is named rather than rounded up.

Journey 2 is one WSL run from promotion. Journey 12's tests are
in-process, so they do not close the live-skew gap the original ledger
named. Journey 4's cross-host clause cannot be proven by mutation at all
— a mux is per target, so its dispose cannot cross hosts, and the
cross-host test stayed green under the mutation that reddens siblings.
Journey 13 measured one dimension of ten, on lifted predicates rather
than through real IPC.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:54 -07:00
NeilandOrca 4622f33453 docs(terminal): promote Journey 1 to proven on all three platforms
The oracle now runs natively on macOS, Linux and Windows, and its
discrimination was watched on each: a mutation reddens it, a restore
greens it. On Linux and Windows both mutations were run, and the second
reddens only the stale-operation test — so the journey's two clauses are
proved independently rather than jointly.

Windows is the new evidence. The PowerShell branches added blind at
ebffb85a848 executed correctly on their first run: `$PID` expanded to
real integers, which also proves the pane shell there is PowerShell-family
rather than Git Bash, and `Get-Process StartTime` returned kernel start
times 5.4s apart — so a recycled pid could not have passed as a survivor.

First journey promoted in this program. The other twelve are unchanged,
and the residual limit on "every stale exact operation" is recorded
rather than glossed.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:54 -07:00
NeilandOrca 27ad530ea6 docs(terminal): record the fence's real gap and what peer designs taught
Marks the client-constructed binding proposal as rejected with the three
false claims that sank it, and records what shipped instead.

States the shipped fence's actual limitation rather than leaving it
implied: it compares a binding, not an incarnation, so a respawn under a
reused ptyId passes. The obvious remedy is wrong here — the agent-create
id is deterministic by design so a replayed create stays idempotent, and
randomising it would trade this narrow gap for a duplicate-spawn bug.

Also records the ranked lessons from four comparable agent IDEs, chiefly
that a typed end-reason at end time is what stops a user quit from
looking like a resume candidate.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:54 -07:00
NeilandOrca 23fb083ea6 docs(terminal): propose one authoritative binding identity
Every defect this program has touched is the same defect: identity
compared with the wrong key, or not compared at all. Lease keyed without
the pane, reattach using a creating write, folder-workspace ids compared
with the instance suffix stripped, local mutating IPC carrying only an
id, a live shell classified as expired, liveness unable to say unknown.

Proposal: one branded binding type built from fields that already exist
and are already persisted, constructible only from an authoritative
source, carried by mutating operations, compared by one shared function.
Makes a wrong-key comparison a type error rather than the next incident.

Under adversarial review, including against the open issue corpus.
Not accepted.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:54 -07:00
NeilandOrca 29e1f0f889 docs(terminal): record the user decision relaxing G6
G6 becomes minimise-and-justify rather than strictly net-negative. The
deletion budget the plan assumed does not exist: an entrypoint-rooted
import graph found 51 of 53 candidate files reachable and instantiated
on live paths, leaving 263 deletable LOC against roughly +1,021 to
offset. Correctness may still not be traded for line count.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:53 -07:00
NeilandOrca 8ec025e4e4 test(ssh): census both durable session partitions on reconnect
Adds a second reconnect scenario and a helper that reads pane records
from the local partition as well as the ssh host partition. That split
matters: the reattach binding call passes no hostId, so a grafted pane
lands in the LOCAL partition and an oracle reading only the host
partition passes whether or not the guard is present.

Both tests remain forward guards. The second one was reported as
discriminating and did not reproduce: with `mayCreate: false` removed
from the call site and the app rebuilt, both still passed. Its induction
races `pty:kill` against a severed transport, so when the kill lands the
lease is cleaned up and there is nothing left to graft. The handoff
README is corrected to say so rather than claim a journey.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:53 -07:00
NeilandOrca 72f4384a2d docs(terminal): track the terminal-session correctness handoff package
The package was untracked under a gitignored `docs/**`, with the
un-ignore rules living only in an uncommitted .gitignore edit — a single
`git clean -xdf` would have destroyed the authoritative plan.

The 814-path construction snapshot is now pushed as
`nwparker/react185-authority-snapshot` too; it had no remote ref.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:53 -07:00
NeilandOrca 8480d8f26d docs(terminal): record that a guard must be pinned at its call site
A refusal that exists and is never passed is indistinguishable from no
refusal, and store-level tests cannot tell the difference — they call the
store directly. Learned from `mayCreate`, which was correct and had no
production caller for several commits.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:53 -07:00
NeilandOrca 0960a4c911 docs(terminal): record what makes a retention bound safe
Shortening a grace period is the wrong lever. Measuring process time and
gating reclamation on an independent observation are what make one safe,
and they are what deployed systems actually do.

Also records that lifecycle belongs in the attach reply rather than a
delivered event — that is what removes the need for a durable per-consumer
cursor to guarantee an exit is never lost.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:34 -07:00
NeilandOrca 9177022505 docs(terminal): record the terminal session behavior contract
Properties stated as observable behavior rather than mechanism, so an
oracle written against them survives a change of implementation.

Records the weaker, correct form of the timer rule — a timer may never
be the sole cause of a destructive action — because recovery budgets and
scratch-file age gates are correct code that an absolute ban would
condemn. Also notes which mechanisms are deliberately not required, so
each has to earn its place rather than arrive with an architecture.

Co-authored-by: Orca <help@stably.ai>
2026-08-08 19:11:07 -07:00