Files
orca/docs/audits
Jinwoo Hong 2a68ea9496 refactor(terminal): one commit module for terminal topology closes, with a boundary check (#25329)
* refactor(terminal): move the topology revision and leaf-lookup helpers into terminal-topology

advanceTerminalTopologyRevision, findTerminalTabIdForLeaf and
hasHostAuthoritativeTerminalMembership move verbatim from the renderer-save
membership rebase into persistence/terminal-topology, the home of the commit
boundary. Importers are repointed; no behavior change.

* test(terminal): test-only guard for topology writes outside the commit boundary

The three session sinks now publish through one commitWorkspaceSessionPartition
helper, which hands the prior and published partition to the topology write
guard. Production never arms the guard, so each sink pays one global lookup.

The unit suite arms it in report mode: a sink write that changes class-(a)
topology (membership, root, bindings, titles, incarnations, sleeping records,
remote session ids, tombstones, default-applied, revision) outside a commit
scope is attributed to its writer's file by stack, and fails the test unless
the writer is on the unrouted-writers allowlist that later routing PRs shrink.
Renderer saves and test seeding are exempt. Deep-freeze is available but stays
off suite-wide until the in-place writers return new sessions.

* refactor(terminal): add the topology commit module with bindLeaf, closeLeaf and closeTab

terminal-topology-commit.ts is the boundary for class-(a) terminal topology.
bindLeaf forwards to persistPtyBinding, whose write now runs in a commit
scope; the spawn commits and the relay reattach bind through it. closeLeaf and
closeTab wrap the existing close mutation in a commit scope and one
persistence.terminal-topology span (kind, outcome, refusal reason; no ids),
and the runtime close goes through them. Session output is unchanged: tests
compare it byte for byte with the old writers for local, ssh: and folder
workspaces.

* chore(terminal): drop an unused lint suppression from the topology write guard

* fix(terminal): attribute topology guard writers relative to the repo root

The guard read a frame's file through its last /src/ segment, so a test under
tests/ (folder-upgrade-identity-persistence.unit.test.ts) had no source frame
and failed as an unknown writer. Frames are now taken relative to the repo
root the setup passes in, tests/ counts as test seeding, Windows backslashes
are normalized before the node_modules skip, and nested src/ paths keep their
full path. The two runtime funnel files skip only their funnel function, so
another writer in them still shows. The R9 allowlist key names the file whose
frame actually writes. The class-(a) diff and the attribution move into their
own files; the stack limit is restored in a finally.

* refactor(terminal): drop bindLeaf until binding reaches a sink; add the boundary ratchet

bindLeaf and the commit scope inside persistPtyBinding changed nothing: the
binding write never reaches a session sink, and a scope inside the Store method
would have admitted every direct caller once it did. Both return in B1-4; the
spawn commits and the relay reattach call persistPtyBinding directly again.

The runtime close now calls one closeLeafOrTab entry, so its callbacks keep
their contextual types. A census test is the primary enforcement: only
persistence/terminal-topology and the callers it lists may call
persistPtyBinding, the session setters or the three sinks, and every
unrouted-writer allowlist entry must name an existing file.

* fix(test): resolve the topology guard's repo root without the global URL

Under happy-dom the global URL is not Node's, so fileURLToPath(new URL(...))
threw in the setup file and failed every happy-dom test file.

* refactor(terminal): drop the runtime topology write guard; the boundary ratchet enforces

The AST boundary ratchet is the enforcement for B1. The stack-attributed
runtime guard, its class-(a) diff, the unrouted-writer allowlist, the vitest
setup and the freeze option are removed; it saw two writers in the whole unit
suite and its real value starts only once binding reaches a sink (B1-4).

Also: one closeLeafOrTab wraps the close in the span (no per-kind copies or
narrowed types), the span has one finish like persistence.pty-binding, the
sink helper is publishWorkspaceSessionPartition (it publishes; the commit
boundary is the module), the ratchet drops the private publishSession row and
checks that every listed caller file exists, and the close comparison keeps
its two meaningful cases with span cases chosen by name.

* refactor(terminal): trim the B1-1 commit module and ratchet to what they enforce

- Point the acknowledged-tab-retirement audit fixture at the moved
  advanceTerminalTopologyRevision; its old import no longer resolved.
- Drop publishWorkspaceSessionPartition: it was the removed guard's
  interception point, so the three session sinks return to origin/main.
- One traced(kind, mutate) wrapper in the commit file replaces the span
  factory; closeLeafOrTab is one call.
- The ratchet walks src/main with the shared scanSourceTree, drops the
  loading-store-internal rows and the redundant file-exists test; exact-set
  equality already fails on a missing file.
- The commit test is a pure unit test of the span outcomes: no Store
  harness, electron mock or self-comparing close.

* refactor(terminal): census the runtime session controller's write and drop stage ids from comments

The controller's setter was named set, which the boundary census could not
list without matching every Map.set, so a new OrcaRuntime mixin could write
sessions through it unseen. Rename it setForWorktree and census it.
Comments now describe state instead of citing plan stage ids.

* refactor(terminal): census writer references and trace refusals by callback

- traced() takes refusalOf instead of assuming an Error refusal, and only
  mutate() sits in the try, so a span outcome of threw means the write threw.
- The boundary ratchet counts references, not just direct calls: non-null
  calls, bracket keys, aliases, destructures, .call/.bind and parenthesized
  callees all count; declared names and type positions do not.
- Census terminalSurfaceCloseMutation (boundary-only) and the partition sinks
  setLocalWorkspaceSession / setHostWorkspaceSession.

* test(terminal): count writer uses in extends clauses and instantiations, skip type-only imports and local declarations

The census skipped ExpressionWithTypeArguments as a type, which also holds
`extends f(x)` and `x<T>` value expressions. Type-only import/export
specifiers and declared names (variables, parameters, accessors, enum
members) no longer count as uses. The audit fixture is listed in the table
instead of a separate exemption.

* test(terminal): count quoted and assignment-pattern destructures of layout writers

* test(terminal): count every mention of a layout writer except its definition

Telling definitions from uses per syntax kind kept missing nested and
for-of destructures. Exempt only the writer's own function or class-member
definition; any other mention (including object-literal keys) counts, so the
census errs toward a loud false alarm rather than a silent miss. Quoted names
count only in member-name position.

* test(terminal): count every string literal naming a layout writer

Member-name positions missed wrapped keys like store[('name')] and
store['name' as const]. Counting every string literal outside types is
shorter and errs toward a loud false alarm.

* test(terminal): exempt only class members and functions as writer definitions

Object-literal methods and accessors were exempt while equivalent arrow
properties counted; all object-literal keys now count alike.

* test(terminal): parse files with unicode escapes in the writer census

A name spelled with a \u escape never appears verbatim, so the text
prefilter skipped it.

* test(terminal): parse any file with an escape in the writer census

\x, identity and line-continuation escapes also decode to a writer name
without it appearing verbatim.
2026-10-05 13:39:21 -04:00
..