# Test plan, coverage and verification record

## Verification record for 0.7.2

Local release preparation: **2026-10-28**, macOS, Node **26.12.0**, npm
**11.20.0**, **unmodified official Pi 1.4.0**, Claude Code CLI **2.1.311** and
tmux **3.8**. The candidate includes `9bb9b59` and released main; package and
Claude plugin manifests both advance **0.7.1 → 0.7.2**. This preparation changes
release metadata, documentation and packaging assertions, not runtime code.

The official Pi `docs/index.md`, `packages.md`, `extensions.md` and the linked
session-state example were read before preparation. `PI_SDK_PATH` explicitly
selected the official managed **1.4.0** `dist/index.js`, not a patched local core.
The SDK fixtures and CLI/terminal/subagent-process fixtures use that host.
Only synthetic local responses were used, with an isolated outer HOME, Pi/Claude
configuration directories, empty npm configuration files and a clean environment.
No credentials or private histories were inspected; no live installation,
activation, host selection, project resources or Pi release files were changed.

| Check | Actual result |
|---|---|
| `npm run test:all` | **1,387 Node tests and 61 native Claude plugin tests passed**, zero failures/skips; generated shared-copy parity and plugin validation passed |
| `node --test --test-concurrency=1 test/package.test.mjs test/pi-usage*.test.mjs` | **38 passed**, zero failures/skips: 4 package checks, 28 usage unit regressions and 6 real official-SDK cases |
| `npm pack --dry-run --json --ignore-scripts` | **50 files**, version **0.7.2**; includes `pi/usage.mjs`, `pi/subagents.mjs`, `pi/handoff.mjs` and the complete repository MIT license; excludes personal config/history, tests and generated host declarations |
| JavaScript/TypeScript and installer syntax | **37 `node --check` checks** and `bash -n install.sh` passed; package/plugin versions and the sole Pi entry point match |
| `node --test scripts/release-policy.test.mjs` (repository root) | **31 passed**, zero failures/skips |
| Local documentation check | **374 relative links/anchors** across 12 documents, **4 reciprocal README pairs**, and syntax of **58 shell blocks** passed; install/publication examples were not executed |
| Working-tree metadata precheck against `origin/main` | Next patch **0.7.1 → 0.7.2**, matching manifests and bilingual **2026-10-28** notes passed via the existing policy helpers; this is not committed-ref validation |
| Scope and whitespace | `git diff --check` passed; both root READMEs remain unchanged outside their Translator section; no runtime changes or absolute personal paths were added during preparation |

The real usage fixtures confirm that numeric receipts remain outside provider
context and Pi's native totals, survive reload/resume and whole-session tree
navigation, and seal unresolved attempts under their original owner before
replacement. Pi 1.4.0 does **not** save a native session file for a failed first
input: its receipts are buffered in memory until a later conversation message
flushes them. No usage sidecar or historical backfill is added. Missing usage,
late replies, cancellation and storage failures remain explicitly bounded by the
[receipt contract](../pi/README.md#auxiliary-usage-accounting).

The full run passed on its first attempt; the earlier development-only Claude
desktop timing failure below was not reproduced, and no timing fix is claimed.
The validator retains its non-gating **ICON_MISSING** warning and marketplace
installation advice; it does not establish catalog acceptance. An initial local
runner setup used the same empty file for npm's user/global config, which npm
refused; distinct empty files corrected that setup before any test suite ran.
No personal npm configuration was read. LSP diagnostics for the packaging test
are **syntax-only**, not a strict project typecheck.

Coverage was **not remeasured** for this candidate; the 2026-10-27 development
coverage below remains historical. Pi 1.3.0/older minimum hosts, the bundled
Claude desktop engine, physical-terminal/GUI acceptance, Linux, hosted-model
quality, real-provider billing and abrupt-process-loss behavior were not newly
verified. Local preparation does not prove GitHub/npm availability or update
an existing installation. The publishing parent must commit the candidate,
run `node scripts/release-policy.mjs --base origin/main --head HEAD --branch translator`,
then perform the documented post-merge/tag and publication checks. A validator
that reads committed refs cannot validate these uncommitted release files.

## Auxiliary usage development verification (2026-10-27)

Local checks on **2026-10-27**, macOS, Node **26.12.0**, Pi **1.4.0** and Claude
Code CLI **2.1.311**, using isolated settings/auth paths and synthetic localhost
providers only. No installation, activation, paid calls or personal transcripts.

- `node --test --test-concurrency=1 test/pi-usage*.test.mjs`: **34 tests passed**
  (28 unit regressions and 6 real SDK cases). Covers returned numeric usage before
  validation, refusal/fallback attribution, concurrent parts and sibling failure,
  cancellation/timeout with late replies, missing/invalid usage, no duplicate
  charges on cache/skips/repeated display events, stale context/branch/session
  ownership, write failures, subagent task/report previews and the eight-task
  parallel limit. Supported task preparation stays sequential.
- Final `npm run test:all`: **1,387 Node tests and 61 native Claude plugin tests
  passed, zero failures/skips**; generated shared-copy parity and plugin validation
  passed. The first full run had **one unchanged Claude desktop fixture failure**
  (mid-turn row still showed the original rather than the translated prompt).
  Its isolated two-test rerun, the complete rerun and the coverage run all passed;
  no Claude code/assertion/timeout was changed and no underlying timing fix is
  claimed. The validator retains its missing-icon warning and marketplace advice.
- `npm run test:coverage`: **1,387 passed**, zero failures/skips. Raw V8 coverage:
  **94.08% lines, 90.36% branches, 91.97% functions** overall;
  `pi/usage.mjs`: **99.22% lines, 93.33% branches, 95.83% functions**. The existing
  jiti attribution and entry-point/native-plugin instrumentation limits below
  still apply; this is not a billing-completeness or translation-quality score.
- The real SDK confirms the custom entries never enter provider context or native
  session totals; whole-session receipts survive disk resume, reload and tree
  navigation. Pending reload/tree/replacement work records one null receipt under
  the old owner and never appends late usage to a new owner. In-process reload
  retains no-session and first-input buffered entries. A failed first input does
  **not** create a native session file; later conversation delivery flushes its
  receipts. No new usage sidecar is created.

`bash -n install.sh`, `git diff --check`, and **171 local links/anchors** across
the eight changed documents (including two reciprocal README pairs) passed.
The new command examples are exercised by the focused tests above. LSP diagnostics
are syntax-only, not a strict TypeScript check. Physical-terminal, Linux,
older-host, real-provider billing and abrupt-process-loss behavior are not newly
verified. This is development evidence, not a release or activation record.

## Verification record for 0.7.1

Local release verification: **2026-10-23**, macOS, Node **26.12.0**, Pi **1.4.0**
and Claude Code CLI **2.1.309**. The installed CLI matches npm `latest` on that
date. `npm run test:all` passed **1,353 Node tests**, generated shared-copy parity,
plugin validation and **61 native Claude plugin tests**, with zero failures or
skips. The focused Pi **1.3.0** UI suite passed **8 tests**, zero skips; its full
suite was not rerun. Installer/JavaScript syntax, **31 release-policy tests**
and whitespace checks passed. The release PR records the committed-candidate
and post-merge policy gates and final archive verification.

The Claude validator retains its non-gating missing-icon warning and marketplace
installation advice; no marketplace acceptance is claimed. A read-only review
identified that clone-based installs must migrate their source-local data before
switching to an npm-layout package. Both upgrade guides now separate existing
npm installs from clone installs and explicitly preserve custom config paths.
Runtime identity with 0.7.0 was checked separately. LSP was syntax-only, not a
strict project typecheck. Coverage was not remeasured. No paid model calls or
personal history/configuration were used; physical-terminal input/painting,
Linux, older minimum hosts and new desktop-app/engine acceptance remain unverified.

The release preserves both the Pi dialog-stack regressions and the npm 12
pack-report compatibility fix. Runtime translation code, preferences and cache
contracts are unchanged. npm registry publication is not part of this release;
the GitHub Release supplies the packed package for stable local installation.

## Pi 1.4.0 compatibility verification (2026-10-22)

Local verification: **2026-10-22**, macOS **27.2**, Node **26.12.0**, Pi
**1.4.0**, Claude Code CLI **2.1.308**. This is a compatibility follow-up, not a
release, version bump or installed-plugin update. The existing runtime adapter
needs no Pi 1.4.0 patch or configuration migration. The release-time Pi 1.3.0
and desktop-engine evidence remains in the separate 0.7.0 record below.

From `Hooks/translator`:

```sh
export PI_SDK_PATH="$HOME/.pi/agent/install/releases/1.4.0/node_modules/@earendil-works/pi-coding-agent/dist/index.js"
npm run test:all
bash -n install.sh
node --check test/pi-widget-integration.test.mjs
```

The final full run passed **1,353 Node tests, 0 failures, 0 skips**, generated
shared-copy parity, plugin validation and **61 native Claude plugin tests**.
Shell and JavaScript syntax checks passed. The validator still reports its
non-gating missing-icon warning and marketplace-install advice; no directory
publication or policy acceptance is claimed. One intermediate full run timed
out in the existing Claude live-session hot-reload fixture. Its unchanged
isolated rerun and the final full rerun passed; no timeout or assertion was
relaxed, and no underlying timing fix is claimed.

The TUI integration now calls the actual host `showExtensionCustom` implementation
instead of reimplementing editor-slot restoration. Three new Pi 1.4.0 cases
exercise the native dialog stack: dismissal restores a selector below Translator;
tree navigation and SDK reload invalidate a hidden Translator panel without
stealing focus from a selector above it. They check retained draft text,
answerable dialogs and inert stale callbacks. These use an in-memory terminal,
a synthetic editor/status sink and the real SDK lifecycle. The retained-UI SDK
reload case does **not** simulate the full interactive `/reload`, which resets
all extension UI before reloading the session.

The focused `node --test test/pi-widget-integration.test.mjs` also passed with
explicit SDK paths for **Pi 1.4.0 (11 tests)** and **Pi 1.3.0 (8 tests)**, with
zero skips. The three stack-specific cases apply only to the host that implements
stacking. Pi 1.3.0's full suite was not rerun for this follow-up.

The full suite still checks real Pi loader/CLI/SDK input and output, private
history integration via the actual terminal fixture, cancellation, queue delivery,
resume/reload and official-example subagent processes. All model replies are
synthetic localhost fixtures; no paid calls or personal prompt/configuration
changes were used. This does not establish physical-terminal, Linux, hosted-model
quality or new desktop-app GUI acceptance. Coverage was not remeasured.

## Verification record for 0.7.0

Local release verification: **2026-10-18**, macOS **27.2**, Node **26.12.0**, Pi **1.3.0**,
Claude Code CLI **2.1.302**, tmux **3.8**. The installed desktop app is
**2.42304.0**, bundling Claude Code **2.1.299**; the CLI and desktop engine are
separate test targets. Pi 1.3.0 and Claude CLI 2.1.302 matched npm `latest` when
checked on this date. The user explicitly authorized the **0.7.0 milestone**,
GitHub/npm publication and local installation. Publication identifiers and final
artifact hashes belong in the release PR and GitHub Release.

Final release-snapshot `npm run test:all`: **1,350 Node tests passed, 0 failed,
0 skipped**, followed by successful plugin validation and **61 native Claude
plugin tests passed**. Generated shared-copy parity passed. This supersedes the
earlier candidates' 1,265/59 and 1,345/61 results. All model responses came from localhost
fixtures; no paid calls, private prompt histories or personal configuration
changes were used. Counts include unit, integration and actual-host tests, not
1,350 manual end-to-end experiences.

The follow-up covers one API-response retry, completed-output ownership,
English-to-Chinese task previews without changing English dispatch, explicit
Pi task handoffs, completed Claude child identities and scanner
`literal-whitelist-v9`. Intermediate runs exposed stale single-attempt fixtures
and a real over-translation regression for a Chinese answer ending in `OK`;
those were corrected without weakening fail-closed/context checks. The old
hanging sibling fixture was cancelled, and the initial attempt to launch the
Linux VM desktop binary was unusable; neither is counted as a pass.

The final desktop-engine run used the **native macOS 2.1.299 executable**, not
its Linux VM binary, first on an isolated temporary `PATH`:

```sh
node --test --test-concurrency=1 test/claude-{tasks,headless-output,cancellation,desktop,tui,live-session,integration,skill}.test.mjs
npm run test:claude
```

**29 focused tests passed, 0 failed/skipped**; plugin validation and **61 native
tests** also passed. These check the bundled engine, actual terminal execution
and desktop-shaped remote render requests, not the app's physical painting.
The CLI 2.1.302 executes the same tests in the complete suite.

A bounded release review found an unanchored Pi chain ownership ticket:
`{previous}` alone could match any unrelated direct child. The parent now blocks
placeholder-only chain tasks while Input is on, omits their ownership with Input
off, and children reject inherited wildcard tickets. All **56 targeted tests**
passed, including an actual custom JSON child with legacy wildcard metadata.
The final suites above ran from an exported committed source snapshot containing
that fix, not from the dirty mixed-tool checkout.

### New regression coverage

| Contract | Evidence |
|---|---|
| Independent common/Pi/Claude defaults, flat-file compatibility, partial layers, explicit follow versus inheritance, reset, malformed fields, environment lock and canonical locked writes | `defaults.test.mjs`, `control.test.mjs`, existing adapter settings tests |
| Atomic Pi single/parallel/chain task preparation, English Chinese-preview without rewriting submission, protected `{previous}`, failure/cancel, nested IDs, immutable tool results/chain handoffs, output cards and resume/reload | `pi-subagents.test.mjs`, `pi-subagents-host.test.mjs`, `pi-subagents-process.test.mjs`; real SDK fixtures, **4 actual official-example cases** (including Chinese chain reports) and **3 custom JSON-child cases**, with `pi-handoff.test.mjs` covering exact assignment matching |
| Claude agent launches, identifiable parent-to-child SendMessage, nested/peer exclusions, delivered reports, background notifications, selections before turn completion, pending-state concurrency and reload | `claude-tasks.test.mjs`, `claude-safety-regression.test.mjs`, native plugin tests on both CLI and bundled engines |
| Conservative input versus tolerant output paths/names, complete mixed-script paths, bounded answer preface but strict task instructions, already-Chinese sections absent from requests, exact restoration, malformed masks and long-answer batching | `output-filtering.test.mjs`, `task-display.test.mjs`, language/literal/core/bridge/Pi tests, generated scanner parity, existing corpus fixtures and **100 deterministic recombinations in both directions** |
| Task records cannot restore human prompt history/recall; multiple task/tool records do not collapse under an absent UUID on journal restore | `bridge.test.mjs`; two findings from read-only review fixed with regressions |

### Observed end-to-end behavior and earlier-release goals

| Scenario / prior goal | Observed result in the final host tests |
|---|---|
| 0.6.15–0.6.16: output and originals survive ordinary interruption and later work | Pi tmux Escape retains the completed output, later turns remain usable, and Ctrl+Alt+C explicitly cancels. Real RPC abort and print/JSON exit waiting pass. Claude selection/plan dialogs remain pending while translations finish; stream-json interruption retains native/Pi-backend output with no attached UI. |
| Retry recovery, without hidden extra Translator attempts | Actual Pi SDK and Claude HTTP fixtures recover from API, transport, empty/incomplete and deadline failures in both directions. Pi asserts exactly two HTTP requests; Claude asserts its two-completion budget, separately from host-internal HTTP retries. |
| New subagent contract without regressing 0.5.0 context isolation | Real official Pi single/parallel/chain children receive English tasks; English tasks remain exact and get Chinese previews. Reports, including Chinese `{previous}` results, stay original on the next model request. Real Claude foreground/background children, completed-child `SendMessage` and process restart pass. Display translations never enter the main model context. |
| 0.6.2, 0.6.6, 0.6.10, 0.6.13–0.6.16: history, redraw, settings and command menu | Existing real terminal/SDK and desktop-protocol tests pass for original recall/replay, mid-turn originals, restart/resume/reload, forks, settings/model isolation and `/translator` before a model turn. |
| 0.6.3, 0.6.9–0.6.10: no missed prose or redundant technical Chinese | Ambiguous `./old.ts改为new.ts` stays translatable; uppercase instructions are not acronyms. Existing Chinese units, mixed-script resources and an otherwise Chinese answer ending in `OK` skip unnecessary calls. Standalone `OK` and substantive English still translate. |
| 0.5.0–0.6.1, 0.6.7–0.6.8, 0.6.12, 0.6.14: safety and operational contracts | Full regressions retain fail-closed input, literal integrity, bounded concurrent long-answer parts, gateway bypass, no-persistence, locked installation/config writes, null default deadline and refusal fallback. |

The changelog from **0.5.0 through 0.6.16** was reviewed against current tests.
This is evidence for retained *current* contracts, not a promise to preserve
superseded behavior: 0.6.10 removed semantic audit/repair, later versions replaced
old history polling/hotkeys, and 0.7.0 intentionally broadens API retry and
replaces blanket child suppression. No unresolved regression was observed in
the final suite; it is not exhaustive external-environment acceptance.

### API research and acceptance boundaries

The current official [Mods interface guide](https://code.claude.com/docs/en/plugins/mods/interface),
[API guide](https://code.claude.com/docs/en/plugins/mods/api) and
[reference](https://code.claude.com/docs/en/plugins/mods/reference), together with
locally generated current declarations, were checked. The question dialog tree
must contain the native reference exactly once, with annotations above it;
plain Text avoids the bundled engine's rejection of Markdown at that site.
Permission prompts are not render sites. Agent input uses `agent.spawn` and
`session.send`; display output uses delivered `turn.complete` reports rather than
pre-review child steps. Optional task-event hooks are guarded when unavailable.
Pi support uses 1.3.0's documented mutable `tool_call` input and notification-only
`tool_execution_end`, plus non-context custom entries.

- The Pi SDK tests execute an offline contract fixture. Separately, the process
  tests launch the actual CLI and its **unmodified shipped subagent extension**
  for single, parallel and chain modes: real JSON children, isolated agent
  profiles, dummy credentials, offline startup and a localhost-only provider.
  They verify original chain handoffs and no duplicate child translation.
  Arbitrary third-party schemas/launchers and older subagent APIs are not covered.
- Claude real-child tests exercise Chinese and English task input, exact
  mixed-script filename delivery, Chinese `ToolUse` previews before/after resume,
  untouched tool results, foreground/background reports, and original human
  prompt history in both languages. They also send actual follow-up `SendMessage`
  tasks to completed children after restarting the process, not just routing-hook
  fixtures. Arbitrary tool schemas and remote/cloud sessions without public local
  lifecycle events remain outside these guarantees.
- No manual desktop/mobile/VS Code GUI acceptance, live-model linguistic quality
  or cost/latency measurement, minimum-version retest, or separate Linux run.
- LSP diagnostics for changed runtime modules are **syntax-only**, not a project
  typecheck. No standalone TypeScript compiler was available for this follow-up;
  the earlier entry-point check allowed existing implicit-any callbacks and is
  not a full strict typecheck. Runtime/native tests are the primary host evidence.
  `test:coverage` was not run for this candidate.
- Review was read-only and bounded; the two reproduced findings above are fixed.
  This is not a blanket clean/security review claim.

### Packaging and release gates

The final complete suite and the desktop-engine checks above were rerun after
all runtime/scanner fixes, with both manifests still at 0.7.0. Documentation was
then reconciled with the observed results.

| Check | Result |
|---|---|
| `npm pack --dry-run --json --ignore-scripts` | **49 files**, version **0.7.0**; includes `pi/subagents.mjs`, `pi/handoff.mjs` and generated runtime mirrors; no tests, personal config/history or generated host declarations |
| Local documentation check | Relative links/anchors across **15 documents** and **4 reciprocal README pairs** checked; all **18 startup-default command examples** executed successfully against a temporary config. The release PR records the final exported-tree link count. |
| `bash -n install.sh`, `git diff --check` | Passed, including the task-preview/path-protection follow-up |
| `node --test scripts/release-policy.test.mjs` from current `origin/main` | **31 passed**, no failures/skips |
| Authorized local milestone policy precheck against `origin/main` | **Passed** on the isolated committed Translator tree: **0.6.16 → 0.7.0**, matching package/plugin manifests and dated bilingual changelogs. This local dry run is not the required PR approval record. |
| Formal merge/tag gate | The publisher must read the exact authorized milestone comment from `yhyfhgs` on the PR, validate its current head, merge with a merge commit, then validate that merge against its first parent. Record those results in the PR/Release. |

The earlier uncommitted-tree validator failed with `found 0`; it was not a
release check pass. Release preparation now uses a temporary Git index and
committed Translator-only trees on the existing `translator` line, based on
released `origin/main`. The original `split-explorer` checkout, its index and
unrelated work are preserved; no second clone/worktree or stash is needed.
GitHub returned **403** for branch protection on the current private-repository
plan; local checks are not server-side protection. No Codex review was requested
(the organization suspension remains in force), and no human/self-approval
review is fabricated. The milestone comment records the user's explicit release
authorization, not a separate independent review.

## Verification record for 0.6.16

Local run: **2026-10-16**, macOS **27.2**, Node **26.8.2**, Pi **1.2.0**, Claude
Code **2.1.300**, tmux **3.7c**. `npm run test:all`: **1,073 Node tests passed,
0 failed, 0 skipped**, followed by **56 native Claude plugin tests passed,
0 failed**. Plugin validation and generated shared-copy parity passed. Every
model response came from a synthetic local fixture; no paid model calls or
private prompt history were used.

Against the desktop app's bundled Claude Code **2.1.295** (a `claude` first on
`PATH`), `claude-skill.test.mjs`, `claude-desktop.test.mjs`,
`claude-integration.test.mjs`, `claude-live-session.test.mjs`,
`claude-cancellation.test.mjs` and `claude-tui.test.mjs`: **10 passed**, so that
engine loads the mod with its new `classic.UserPromptSubmit` hook and
`$.command.list()` call, and its terminal gets the restored history. A local
documentation checker passed **318 relative links/anchors** across the
repository's 33 guides. `npm pack --dry-run` includes **47 files**, the new
`shared/recall.mjs` among them. Release-policy unit tests: **24 passed**;
`bash -n install.sh` and `git diff --check` passed. The validator's run against
`origin/main` is recorded in the release PR. `npm run test:coverage` was not run
for this patch.

0.6.16 translates Claude Code answers after their turn, by one copy of the mod
across a reload, lets Claude Code's prompt history keep the typed text while the
stored row is English and gives lines earlier releases saved in English their
originals back, removes the band hotkeys, restores drawings and settings across
switches, branches and forks, translates long native answers in parts with one
fallback round, and brings the same release, queue, history and fallback
behavior to Pi, Claude Code's Pi backend and the command-line tool (see the
changelog). Before release, an offline review of the change ran real-host
scenarios on Claude Code 2.1.300 and 2.1.295 and Pi 1.2.0 and confirmed 29
defects in the unreleased code; the fixes for them are covered by the suite
above. Four limits left after that review were then fixed and are covered too:
`claude-tui.test.mjs` seeds history lines an earlier release saved in English and
sees both originals on two Up presses in a row, and the new `pi-tui.test.mjs`
drives the real Pi terminal through `/tree` and `/fork`, which now put `请检查` in
the editor (the previous commit put `Please check.` there, and the test fails on
it). Known limits that remain:

- Claude Code: an Esc or rewind put-back may show its English for one frame; the
  engine fills the box before any hook runs, and the swap takes one read and one
  fill.
- Claude Code's history file gets an original only from its session's journal, so
  memory-only mode, and lines whose journal is gone, keep their English.
- Claude Code's Pi backend and the command-line tool ask the other family by
  name, and Pi picks the highest-sorting matching model ID at that provider,
  which may be another route or region than Pi's own fallback would choose.
- Pi: a translation that `/reload`, navigation or a session switch stops leaves a
  `cancelled` card rather than finishing.

Not checked by hand: the desktop app's own painting, physical key delivery and
the Claude mobile app or VS Code extension. Pi 1.0.4 and 1.1.0 and Claude Code
2.1.292 to 2.1.299, except 2.1.295 as stated above, were not retested.

## Verification record for 0.6.15

Local run: **2026-10-15**, macOS **27.2**, Node **26.8.2**, Pi **1.2.0**, Claude
Code **2.1.299**, tmux **3.7c**. `npm run test:all`: **1,024 Node tests passed,
0 failed, 0 skipped**, followed by **56 native Claude plugin tests passed,
0 failed**. Plugin validation and generated shared-copy parity passed. Every
model response came from a synthetic local fixture; no paid model calls or
private prompt history were used.

`npm run test:coverage` passed all **1,024 tests**, with no failures/skips:
**96.98% lines, 90.44% branches, 93.77% functions** for its configured file set.
A first coverage run of the same code also passed every test but reported far
fewer covered lines for two unchanged Pi files (`widget.mjs` 10.67%, branches
and functions unchanged); it did not recur, like the coverage report failure
noted for 0.6.13. A local documentation checker passed **243 relative
links/anchors** across the Translator and repository guides. `npm pack
--dry-run` includes **46 files**. Release-policy unit tests: **24 passed**;
`bash -n install.sh` and `git diff --check` passed.

0.6.15 draws the original of a prompt folded into a long turn in the desktop app
at once. Beyond the suite above:

- The failure was traced with the desktop harness on Claude Code **2.1.295**, the
  build the app bundles, and a fixture turn whose steps hold it for seconds. The
  engine's debug log showed the prompt translated only when it was folded in at
  the tool boundary, then the worker asking for its row's ids while the step ran
  and finding none; the original came under the app's id only after the turn
  ended. The app's renderer (Claude desktop 2.37337.1, read from its locally
  cached code) asks for a `UserMessage` row only once the prompt is no longer
  queued, under the id it sent it with, with `origin` `sdk`.
- The new tests fail without the change: two plugin cases and the new real-binary
  desktop case against 0.6.14's `register.js`. The case with the same English from
  two originals passes on both, as 0.6.14 drew no original under such an id.
- `claude-skill.test.mjs` and `claude-desktop.test.mjs`, including the new
  long-turn case, passed against Claude Code **2.1.295** through a `claude` first
  on `PATH`: **4 passed**.
- The new real Pi case (a steer delivered between two delayed tools gets its card
  while the second still runs) passes with Pi's code unchanged.

Not checked by hand: the desktop app's own painting of the restored original. Pi
1.0.4 and 1.1.0 and Claude Code 2.1.292 to 2.1.296 were not retested, except
2.1.295 as stated above.

## Verification record for 0.6.14

Local run: **2026-10-14**, macOS **27.2**, Node **26.8.2**, Pi **1.2.0**, Claude
Code **2.1.299**, tmux **3.7c**. `npm run test:all`: **1,022 Node tests passed,
0 failed, 0 skipped**, followed by **54 native Claude plugin tests passed,
0 failed**. Plugin validation and generated shared-copy parity passed. Every
model response in the suite came from a synthetic local fixture.

`npm run test:coverage` also passed all **1,022 tests**, with no failures/skips:
**95.66% lines, 90.37% branches, 93.77% functions** for its configured file set.
A local documentation checker passed **241 relative links/anchors** across the
Translator and repository guides. `npm pack --dry-run` includes **46 files**.
Release-policy unit tests: **24 passed**; `bash -n install.sh` and
`git diff --check` passed.

0.6.14 asks another model once for a native Claude Code reply cut short before
its end marker. Beyond the suite above:

- The failure was reproduced on the maintainer's machine before the change,
  outside the suite and with real model calls: an English prompt naming a few
  Chinese files, translated by Opus 5.5 at medium effort through
  `$.model.complete` on Claude Code 2.1.295, came back cut short in all 10
  attempts at the whole prompt (after 220 to 652 characters), with or without
  literal masking, the end-marker instruction or the untrusted-input sentence. A
  request holding only its one line with Chinese was cut short once in two
  attempts. Sonnet 5.5 completed the whole prompt 3 of 3 times, and Haiku 5.5
  once. Run through the main loop instead, one attempt stopped with stop reason
  `refusal` and the notice that Opus 5.5's safeguards had stopped the response.
  The prompt itself is not recorded.
- `$.model.complete` in Claude Code 2.1.295 and 2.1.299 returns a reply's text
  without its stop reason (read from the engine's code), so a stopped reply
  arrives as a normal answer and only the missing end marker reveals it.
- With the change, the same prompt on Claude Code 2.1.295: Opus 5.5 was cut short
  after 231 characters, Sonnet completed it, the prompt was sent, and the record
  named `claude-code/sonnet` with `fallbackFrom: claude-code/claude-opus-5-5`.
- The new tests fail without the change: eight bridge cases against 0.6.13's
  `bridge.mjs` and both new plugin cases against its `register.js`.
- `claude-skill.test.mjs` and `claude-desktop.test.mjs` passed against Claude
  Code **2.1.295**, the build the desktop app bundles, through a `claude` first on
  `PATH`: **3 passed**.

Not checked by hand: the desktop app's own display of a fallback translation. Pi
is unchanged; Pi 1.0.4 and 1.1.0 and Claude Code 2.1.292 to 2.1.296 were not
retested, except 2.1.295 as stated above.

## Verification record for 0.6.13

Local run: **2026-10-14**, macOS **27.2**, Node **26.8.2**, Pi **1.2.0**, Claude
Code **2.1.299**, tmux **3.7c**. `npm run test:all`: **1,015 Node tests passed,
0 failed, 0 skipped**, followed by **52 native Claude plugin tests passed,
0 failed**. Plugin validation and generated shared-copy parity passed. Every
model response came from a synthetic local fixture; no paid model calls or
private prompt history were used.

`npm run test:coverage` also passed all **1,015 tests**, with no failures/skips:
**96.41% lines, 90.39% branches, 93.73% functions** for its configured file set.
An earlier coverage run of the same code also passed every test but could not
report coverage: Node found one child process's coverage file empty. It did not
recur in two later full runs. A local documentation checker passed **233 relative
links/anchors** across the Translator and repository guides. `npm pack --dry-run`
includes **46 files**. Release-policy unit tests: **24 passed**; `bash -n
install.sh` and `git diff --check` passed.

0.6.13 lists `/translator` in the Claude Code desktop app's menu for a session
that has not started. Beyond the suite above:

- How the app builds that menu was read from its own code (Claude desktop
  2.37337.1): a short stream-json Claude Code run with `disableAllHooks`, whose
  `initialize` commands it caches per folder. A project session's process starts
  only with a first message, and its band or pane is drawn only for a running
  process. `claude-skill.test.mjs` replays the menu query.
- `claude-skill.test.mjs` and `claude-desktop.test.mjs` also passed against
  Claude Code **2.1.295**, the build the desktop app bundles, through a `claude`
  first on `PATH`.
- The new tests fail without the change: seven native cases against 0.6.12's
  `register.js`, both `claude-skill.test.mjs` cases without `SKILL.md`, and its
  real-binary case without the `session.start` try/catch or without the
  manifest's `"skills": []`. Before that field was added, a full run failed
  `claude-integration.test.mjs` (Pi backend) and `claude-tui.test.mjs`, where
  `--plugin-dir` read `SKILL.md` as `/translator:translator`.
- In a real terminal with the skills-folder install, `/translator status` typed
  mid-turn ran when the turn ended, answered by the mod, with no model request.
  With hooks disabled, the skill's text and arguments reach the model; under
  `--safe-mode` and `--bare` the skill is not loaded at all. No real model's reply
  was requested.
- An independent read-only review found no blocking defect. Its findings
  corrected the safe and bare mode claims, narrowed the registration catch and
  extended `claude-skill.test.mjs`; it also ran the new behavior on Claude Code
  2.1.293 and 2.1.295.

Not checked by hand: the desktop app's own painting of the menu, the settings
pane and the button row. Pi 1.0.4 and 1.1.0 and Claude Code 2.1.292 to 2.1.296
were not retested, except as stated above.

## Verification record for 0.6.12

Local run: **2026-10-14**, macOS, Node **26.8.2**, Pi **1.2.0**, Claude Code
**2.1.299**, tmux **3.7c**. `npm run test:all`: **1,013 Node tests passed,
0 failed, 0 skipped**, followed by **48 native Claude plugin tests passed,
0 failed**. Plugin validation and generated shared-copy parity passed. Every
model response came from a synthetic local fixture; no paid model calls or
private prompt history were used.

`npm run test:coverage` also passed all **1,013 tests**, with no failures/skips:
**96.27% lines, 90.42% branches, 93.73% functions** for its configured file set,
with the same instrumentation limits as 0.6.10. A local documentation checker
passed **235 relative links/anchors** across the Translator and repository
guides. `npm pack --dry-run` includes **45 files**. Release-policy unit tests:
**24 passed**; `bash -n install.sh` and `git diff --check` passed.

0.6.12 makes no translation time limit the default (`timeoutMs: null`) and
otherwise removes dead code and duplication. Beyond the suite above:

- The literal scanner's offline inspection helpers were deleted, not changed.
  `splitLiterals`, `proseText` and both language gates gave identical results
  before and after on **49,532 texts**: the hard-prompt corpus, every string in
  the scanner and gate tests, and random recombinations of them.
  `LITERAL_RULES_VERSION` and the cache contract are unchanged.
- The test of the deleted helper was removed and its two cache-integrity
  assertions kept, so the suite has one test fewer than with the time-limit
  change alone; that change added a test for the null default.
- The Claude plugin test for the prompt-hint status now requires that the mod
  never touches its pinned status line; the call that cleared it at session
  start was a no-op.
- The real-host Claude integration test now runs its native backend without
  `timeoutMs`, so the default request with no deadline and a continuation
  without expiry passed on Claude Code 2.1.299. Its Pi backend and the other
  host tests keep explicit limits.

No host behavior was checked by hand for this patch beyond the real-host
fixtures in the suite. Pi 1.0.4 and 1.1.0 and Claude Code 2.1.292 to 2.1.296
were not retested.

## Verification record for 0.6.10

Local run: **2026-10-12**, macOS, Node **26.8.2**, Pi **1.1.0**, Claude Code
**2.1.296**, with tmux available. `npm run test:all`: **1,013 Node tests passed,
0 failed, 0 skipped**, followed by **48 native Claude plugin tests passed,
0 failed**. Plugin validation and generated shared-copy parity passed. Every
model response came from a synthetic local fixture; no paid model calls or
private prompt history were used.

`npm run test:coverage` also passed all **1,013 tests**, with no failures/skips:
**96.31% lines, 90.04% branches, 93.22% functions** for its configured CLI,
shared/Pi `.mjs`, bridge and script file set. These totals do not instrument the
native Mods `.js` or Pi's TypeScript entry point. A local documentation checker
passed **207 relative links/anchors** and all four reciprocal README language
pairs.

Verified offline regression coverage:

- Both input and output use the same preflight literal spans for language
  decisions and request masking. Certain bare Chinese filenames, paths and
  literal-like identifiers/settings/mentions must reach the translator as
  placeholders and restore with exact source bytes and occurrence counts.
- Literal-only input/output skips before translation calls and logs, including
  bare `报告.md`, `./文档/报告.md`, `用户表_v2`, `LABEL=中文` and `@张三`.
  Ambiguous prose remains model-visible, quoted Chinese prose takes the input
  translation path, plain `OK` output translates, and the existing Chinese
  technical-label skips remain intact. Verify this through Pi, both Claude
  backends and the generated in-plugin scanner.
- Each fresh input translation or output part needing translation gets one
  completion, without an AI audit or repair call. Completed/nonempty fixture responses with remaining
  Chinese input or no Han in output must not be rejected for language, including
  native Claude and joined long answers. Literal content must never cause a
  language rejection. This acceptance is not evidence of semantic quality.
- Structural failures still block input and leave original answers intact:
  incomplete/empty responses, missing/duplicate native terminal markers,
  missing/damaged/duplicate placeholders and reordered protected input blocks.
  Long-part failure stops sibling work; cancellation prevents late publication.
- Cache keys remain exact source plus scanner/prompt contract; the bumped
  contract isolates old versioned entries. Nonempty current and unversioned
  candidates receive source-literal byte/count checks only, not order, language
  or semantic checks. Literal-changing entries cause a fresh translation.
- Successful Pi originals remain journal/cache-only until real user-message
  delivery, then appear once after the user row. Exercise delayed-tool steer and
  follow-up queues, duplicate prompts/events, exact images, ambiguous originals,
  bypassed input, discard, navigation/reload, immediate failures and persistent
  ordering. No display entry may inject context or request another turn.
- Claude's real TUI is driven through tmux: Up/Down restore Chinese across
  multiple history entries, plugin reload, resume and a new session. Unchanged
  recall replays the saved English with no translation call even with automatic
  cache off; editing the source makes one fresh call. Typed English that happens
  to match a translation is untouched. Main requests contain no display originals.
  Pure matching tests cover ambiguity, intervening edits, bounds and replay
  selection; bridge tests cover older contracts, no-persistence mode, Input off,
  cancellation, current language suffixes and source-literal integrity.
- Generated shared-copy parity and both adapters' failure/cancellation and
  display/context boundaries pass. Claude's already synchronized prompt-row
  display remains unchanged; only Pi's early original-card timing changed.

The [documented reproduction commands](#reproduce) remain the release check.
`node --test scripts/release-policy.test.mjs` passed **24 tests**; `bash -n
Hooks/translator/install.sh` and `git diff --check` passed. Candidate/merged
release-policy validation and archive integrity are recorded in the release PR.

### Evidence boundaries

The Pi queue fixture uses a real SDK delayed tool while steer/follow-up prompts
wait, checking event and persisted row/card ordering, images, duplicate events,
discard, reload and ambiguous originals. Pi exposes no queue-item correlation
ID: conflicting sources/bypasses or host-normalized content omit a card rather
than guessing. Older cards are not retroactively moved.

The Claude terminal fixture exercises native arrow navigation, source editing,
replay, typing, mid-turn messages and reload/resume against the real 2.1.296 TUI.
The composer API has no history-navigation event: restoration uses a short local
poll only while a terminal draft and known originals exist, without replacing
arrow bindings. Missing/ambiguous original records remain unchanged. The test
verifies programmatically delivered tmux keys, not a person's terminal setup.

LSP diagnostics were **tree-sitter syntax only**, not TypeScript type checking.
The Node and native plugin suites provide runtime/host evidence. No live-model
quality evaluation, minimum-host-version retest, or manual
mobile/desktop visual acceptance is claimed for this release.

## Verification record for 0.6.9

Local run: **2026-10-11**, macOS, Node **26.10.0**, Pi **1.1.0**, Claude Code
**2.1.296**. `npm run test:all`: **860 Node tests passed, 0 failed, 1 skipped**
(the real Claude terminal test: tmux is not installed), plus **47 native Claude
tests passed**. Plugin validation and generated shared-copy parity passed.

- The initial focused regressions reproduced **12 failures** with the previous
  output-name rules. The new `language-gates.test.mjs` passes **51 tests** for
  Chinese artifact/storage summaries, exact technical labels, real English
  counterexamples, input bypass, unchanged literal protection and long near-matches.
- The real Pi SDK and real Claude binary (both backends) run with **input and
  output enabled**: the synthetic English input is sent unchanged with no suffix,
  Chinese deliveries make no translation request, and input/output journals stay
  byte-identical. Adding an English sentence still translates. Claude's native
  tests also cover the skipped-answer fallback with `OK`, `Mermaid` and `Token`
  as the final text row.
- `npm run test:coverage`: **860 passed, 0 failed, 1 skipped**; raw V8 coverage
  **93.65% lines, 90.06% branches, 93.26% functions**, with the jiti attribution
  caveat described below.
- Release-policy unit tests: **24 passed**. `bash -n install.sh`,
  `git diff --check`, and **207 local documentation links/anchors** across the
  Translator and repository guides passed. `npm pack --dry-run` includes **44
  files**, matching package/plugin versions, and no tests or private runtime data.

These are local checks, not CI or hosted-model quality measurements. No paid
model calls were made. Unknown plain-word names remain conservative translation
triggers; this patch adds a bounded, case-sensitive output exception, not a
universal language classifier. Input cache contracts and literal masking are
unchanged. GitHub publication does not imply npm registry publication or an
upgrade of an already installed/running extension.

## Verification record for 0.6.8

Local run: **2026-10-11**, macOS, Node **26.10.0**, Pi **1.1.0**, Claude Code
**2.1.296**. `npm run test:all`: **809 Node tests passed, 0 failed, 1 skipped**
(the tmux-dependent Claude terminal test), plus **47 native Claude tests passed**;
plugin validation and shared-copy parity passed. The new real-Pi gateway bypass
regression fails on the previous entry point (`Prompt NOT sent`) and passes with
the process opt-out. Release-policy unit tests: **24 passed**. `bash -n install.sh`,
`git diff --check` and `npm pack --dry-run` passed (44 files, no runtime data).
These are local release checks, not live service or phone acceptance. This
GitHub release does not include npm registry publication. No paid calls or real
WeChat messages were used.

## Verification record for 0.6.7

Local run: **2026-10-10**, macOS (Darwin), Node **26.8.2**, Pi **1.1.0** and Claude
Code **2.1.296**.

| Check | Result |
|---|---|
| `npm run test:all` | **809 Node tests + 47 native Claude tests passed, 0 failed, 0 skipped**; plugin validation and shared-copy parity passed. This includes the real Pi SDK integration, now with a long answer whose parts reach the local model at the same time, and the real `claude` binary with both backends |
| `npm run test:coverage` | 809 passed; raw V8 report: **96.58% lines, 89.91% branches, 93.26% functions** |
| Regression evidence | On 0.6.6 a local model whose response time grows with its text cannot translate a 6,669-character answer within a 400 ms `timeoutMs` (`Translation timed out`, one request); 0.6.7 translates it in four parts at once. Both Pi status checks in `pi-output.test.mjs` fail on 0.6.6: the status line stayed on |
| Real answers | The two answers that timed out in local Pi journals (7,035 and 21,543 characters) cut into 4 and 11 parts of at most 2,000 characters, and each joins back byte for byte |
| `node --test scripts/release-policy.test.mjs` | 24 passed |
| `bash -n install.sh`, `git diff --check` | Passed |
| Packed npm tarball | `npm pack --dry-run`: 44 files with matching 0.6.7 manifests; no tests, config or history |
| Documentation | Local links and anchors in the Translator documents (141 links) and the repository's root documents (70 links) checked |

Only the shared core, the Pi output handler, their tests and the documents changed;
Claude Code's native backend still translates an answer in one request. The Pi
tests were not repeated on Pi 1.0.4, nor the Claude host tests on Claude Code
2.1.292. How fast a hosted model translates parts was not measured, and
`npm run eval:live` was **not** run because it makes paid calls.

All model responses in automated tests are **local fixtures**, not hosted model
calls. The real installed host binaries/SDK are used, but their model requests go
to local HTTP servers. Fixtures supply dummy keys; tests do not parse or copy
credential files.

### Earlier records

| Date | State | Hosts | Result |
|---|---|---|---|
| 2026-10-10 | 0.6.6 release (drawing in the Claude Code desktop app) | Pi 1.1.0, Claude Code 2.1.296 (whole suite also on 2.1.293, desktop test on 2.1.294) | 797 Node + 47 native tests passed, none skipped; raw coverage 85.3% lines, 89.8% branches, 93.2% functions; the packed tarball held 44 files and installed into a temporary prefix and home |
| 2026-10-10 | 0.6.5 release (the `pi-package` npm keyword only) | Recorded in [PR #21](https://github.com/Orbit-Labs-AI/orbit-tools/pull/21) | 795 Node + 39 native tests passed, none skipped; the packed tarball held 44 files with matching 0.6.5 manifests |
| 2026-10-09 | 0.6.4 release | Pi 1.1.0 (Pi tests also on 1.0.4), Claude Code 2.1.295 | 795 Node + 39 native tests passed; raw coverage 90.6% lines, 89.6% branches, 93.0% functions; a real Pi 1.1.0 terminal showed single-language Markdown cards; the packed npm tarball installed into a temporary prefix and home |
| 2026-10-09 | 0.6.3 release | Pi 1.1.0, Claude Code 2.1.295 | 788 Node + 39 native tests passed; raw coverage 84.3% lines (V8 jiti attribution), 89.8% branches, 92.7% functions; 45 of the 788 updated tests failed on 0.6.2; both language checks stayed under about 80 ms on 100,000-character inputs; the packed npm tarball installed into a temporary prefix and home |
| 2026-10-09 | 0.6.2 release | Pi 1.1.0 (Pi tests also on 1.0.4), Claude Code 2.1.294 (Claude tests also on 2.1.292) | 763 Node + 39 native tests passed; raw coverage 83.6% lines, 89.7% branches, 92.4% functions (V8 merged the jiti-loaded Pi panel poorly; 93.9% lines without the two new real-host files); the packed npm tarball installed into a temporary prefix and home |
| 2026-10-09 | 0.6.1 release | Pi 1.1.0 (Pi tests also on 1.0.4), Claude Code 2.1.294 (Claude tests also on 2.1.292) | 751 Node + 34 native tests passed; coverage 90.05% lines, 89.69% branches, 92.58% functions; the packed npm tarball installed into a temporary prefix and home |
| 2026-10-08 | 0.6.0 release (then `prompt-translate`) | Pi 1.0.4, Claude Code 2.1.292 | 546 Node + 30 native tests passed; coverage 92.20% lines, 88.68% branches, 93.16% functions; the packed npm tarball installed into a temporary prefix and home |
| 2026-10-08 | 0.6.1 in development: unified settings, compact Pi panel, literal whitelist | Pi 1.1.0 (Pi tests also on 1.0.4), Claude Code 2.1.294 | 594 + 33, then 604 + 34, then 751 + 34 tests passed |

Review findings from those runs were reproduced with failing tests before they
were fixed. The Pi panel's shifted `?`, a search that reset a pinned model and
an offset in mouse edits are examples, as is invalidating Pi translations before
a branch change and withdrawing Claude cancellation before publication.

## Reproduce

From `Hooks/translator/`:

```sh
npm test
npm run test:claude
npm run test:all
npm run test:coverage
npm run check:shared
node --test --test-concurrency=1 test/package.test.mjs test/pi-usage*.test.mjs
npm pack --dry-run --json --ignore-scripts
bash -n install.sh
```

For an official Pi 1.4.0 run, explicitly set the SDK before these commands:

```sh
export PI_SDK_PATH="$HOME/.pi/agent/install/releases/1.4.0/node_modules/@earendil-works/pi-coding-agent/dist/index.js"
```

Use an equivalent path for a different official installation. Do not redirect
this verification to a patched host. The release run also isolates the outer
HOME and Pi/Claude directories and uses **distinct empty files** for
`npm_config_userconfig` and `npm_config_globalconfig`, with a temporary npm cache
and offline npm mode; no `.npmrc` or live credentials are needed. Set the SDK
path before replacing HOME. The host tests supply their own localhost providers
and fixture keys; never run `eval:live` as a release check.

There are no npm runtime dependencies to install. Tests use temporary directories,
HOME/config overrides, local HTTP servers and fake executables. Claude may
regenerate its ignored `.claude-plugin/types/` declarations. Tests do not alter
personal translation config/history or register hooks in the real user's home.
Read-only smoke checks additionally confirm that existing local registrations
need no changes; package installation checks use isolated temporary homes.

Pi integration normally discovers its managed SDK; set `PI_SDK_PATH` to the
installed `dist/index.js` if needed. Claude integration requires `claude` on PATH.
Missing hosts cause **explicit skips** in the Node suite: a skipped host must not
be claimed as tested. `test:claude` / `test:all` need Claude actually installed.

`KEEP_TRANSLATION_TEST=1 node --test test/claude-integration.test.mjs` keeps the
temporary transcripts/debug logs for investigation. They contain only fixture
prompts/responses but should still not be committed.

## Coverage scope and interpretation

The coverage command includes:

- `shared/*.mjs`;
- `pi/*.mjs`;
- `claude-code/bridge.mjs`;
- `scripts/*.mjs`;
- public `cli.mjs`, `control.mjs`, `import-history.mjs`.

The reload-safe Pi entry now evaluates local implementations through jiti. V8's
path-based report combines native modules and jiti-transformed scripts with the
same filenames; these are raw script metrics, not fully source-mapped coverage.
Individual percentages can also fluctuate between runs (notably the widget), so
they are not directly comparable to the earlier native-import-only percentages.
The live-upgrade TUI test additionally runs a temporary copy of the source, which
is outside the original-path coverage scope.

Selected raw V8 line figures from the 0.6.1 run:

| Module | Lines |
|---|---:|
| Shared translation core | 97.26% |
| Shared literal scanner | 93.55% |
| Shared feature names, defaults and parsing | 91.53% |
| Shared file-update helpers | 100% |
| Shared history | 96.97% |
| Pi history / trace / subprocess backend / model selection | 100% each |
| Pi input handler / output handler | 96.51% / 97.17% |
| Pi settings panel | 98.73% |
| Pi controls / toggle widget (jiti raw-script caveat above) | 68.16% / 8.22% |
| Claude Node worker | 98.99% |
| Public CLI / history importer | 100% each |
| Public config command | 92.31% |
| Installer implementation | 88.15% |

The Pi controls and widget line figures are raw-script numbers affected by the
jiti caveat above; the same run reports 77.00% and 100% of their branches.

Coverage is **not** a percentage of all host behavior. Pi's TypeScript entry and
Claude's sandboxed JS are validated by real-host/native tests outside these V8
module metrics. Installer CLI tests execute a copied checkout in a temporary
home, so those executions are not attributed to the original source path's
coverage. The numbers are neither a translation-quality score nor proof of
exhaustively testing all external API / operating-system failures.

## Functional test matrix

This matrix is a **behavior contract and test-location map**, not a separate
verification result. The current results are in the 0.7.2 record above; earlier
version records remain historical. The new regression table above extends this
baseline for host defaults, subagents, selections and tolerant output filtering.

### Shared translation and data protection

| Behavior / failure | Test locations |
|---|---|
| Chinese input and mixed answers are translated; entirely English input, entirely Chinese output, empty text and extension input bypass | `core.test.mjs` |
| Literal-only input and output, including certain bare Chinese filenames, paths, identifiers/settings/mentions, skip without translation calls or logs; English input that only names Chinese resources passes through; quoted Chinese prose remains translatable | `core.test.mjs`, `bridge.test.mjs`, `literals.test.mjs`, `literal-edge-cases.test.mjs`, adapter/native tests and real-host fixtures |
| Whitelist only: a masked span never swallows prose (glued URLs and paths, closing tags, `和/或`, a lone slash after Chinese as in `项目/Orbit`); uncertain spans go to the translator | `literals.test.mjs`, `literal-edge-cases.test.mjs`, `hard-prompts.test.mjs` |
| At most two completion attempts per input/output part: API/transport failures, empty/incomplete responses and timeouts retry once; Pi retries the same model except for refusal fallback, native Claude uses its alternate family. Refusal fallback and native token-cap correction share the budget. Cancellation, local config/storage and literal-restoration faults do not retry. Claude's internal HTTP retries are separate. No AI audit, semantic recheck or literal-repair call; completed/nonempty fixture echoes or output without Han are accepted if structural safety holds | `retry.test.mjs`, `pi-retry-host.test.mjs`, `claude-tasks.test.mjs`, core/bridge/backend/output-parts tests and native Claude tests |
| Hard corpus: Chinese mixed with URLs, paths, code, formulas, symbols, JSON, HTML, tables, quoted UI strings and an injection, through every host path | `hard-prompts.test.mjs` (all cases), `corpus-hosts.test.mjs` (real Pi SDK and `claude` CLI sample) |
| Preflight skips output with no English prose to translate, including literal-only output; English prose, including slash-joined words and `_emphasis_`, still translates; acronyms, camelCase names, flags, versions, dotfiles, mentions, links/paths and exact technical labels (`Markdown`, `Mermaid`, `Downloads`, `Token`, `Tokens`) do not make an otherwise Chinese answer English; plain `OK`, `Mermaid` or `Token` output still translates; literal-only input bypasses even unreadable config | `core.test.mjs`, `literals.test.mjs`, `language-gates.test.mjs`, `pi-input.test.mjs`, `pi-output.test.mjs`, `bridge.test.mjs`, native Claude tests and both real-host integrations |
| Synthetic Chinese artifact deliveries and storage summaries, including Markdown links/tables, labels, CRLF, long text and punctuation, skip with no completion/config/history/trace/UI work; exact spelling only, no arbitrary Titlecase or Chinese-ratio shortcut; appended English still translates; both modes enabled in real Pi and both Claude backend fixtures, with unchanged English input, unchanged Chinese output and byte-identical journals; Claude's last-row fallback does not retry a skipped answer whose last row alone reads as English | `language-gates.test.mjs`, `fixtures/language-gates.mjs`, adapter tests, native Claude tests and both real-host integrations |
| Input preflight leaves ambiguous prose visible: Han glued to Latin letters or tied to names by `：` or `、`, ambiguous Han-ending links/paths, sentence-length mentions, Chinese email local parts and slash/`#`-joined prose are not swallowed as one literal; conservative matching and long-input performance remain regression requirements | `literals.test.mjs`, `hard-prompts.test.mjs`, `language-gates.test.mjs` |
| Each direction's detection and masking use exact non-overlapping spans; output adds protected Chinese units and the documented tolerant resource/name spans; byte-for-byte restoration of certain code, filenames (including bare Chinese), paths, Markdown targets, math and literal-like identifiers/settings/mentions; uncertain content stays model-visible | `literals.test.mjs`, `literal-edge-cases.test.mjs`, `bridge.test.mjs`, adapter and real-host fixtures |
| Fullwidth path brackets, apostrophes/angle destinations, next-line reference titles, deeper indented code, quoted prose and custom/self-closing HTML tags | `literal-edge-cases.test.mjs` |
| Claude uses the same scanner without forbidden cross-plugin imports; stale generated mirrors fail tests | `layout.test.mjs`, `npm run check:shared`, native Claude tests |
| Spelling/grammar/typo and unambiguous-fragment correction policy; no rephrasing/guessing | Prompt-contract assertions in `core.test.mjs` |
| Exact code, fences, inline code, CRLF, URLs and paths; same-line triple-backtick spans do not consume later prose | `core.test.mjs`, `cli.test.mjs`, `bridge.test.mjs`, `audit-regressions.test.mjs` |
| Missing, duplicated or damaged literal markers, reordered code blocks, identical repeated literals, Unicode/escape mutations and fabricated markers; inline literals may move for English word order | `core.test.mjs`, `literals.test.mjs`, `bridge.test.mjs` |
| Empty, incomplete, tool-only or failed responses are rejected; remaining Chinese in input or missing Han in output alone is not a failure | `core.test.mjs`, `bridge.test.mjs` |
| Deadlines and cancellation, including providers ignoring AbortSignal | `core.test.mjs`, `pi-output.test.mjs`, `output-parts.test.mjs`, `backend.test.mjs` |
| Long answers use up to four concurrent parts, each needing translation gets at most two attempts with a fresh configured deadline; short answers keep one untrimmed request; cuts preserve blocks, lines, masked literals, spacing and indentation; preflight skips source parts with no English prose, including literal-only parts; final response/placeholder failure stops siblings and names the part without repair; cancellation stops all parts; joined output has restoration safety but no language/semantic requirement or audit call; usage adds up; Claude Code's native path prepares the same parts as separate requests and names a failing part | `output-parts.test.mjs`, `pi-output.test.mjs`, `bridge.test.mjs`, real Pi SDK and Claude Pi-backend fixtures |
| Config types/ranges/model pairs/backend selection/thinking levels | `core.test.mjs`, `bridge.test.mjs` |
| Translator follows the current session or a branch/session/file pin; independent thinking applies to both directions; explicit follow clears file pins; a missing model blocks uncached input | `bridge.test.mjs`, `claude-code/tests/translation.test.ts`, `pi-input.test.mjs`, `pi-output.test.mjs`, `backend.test.mjs`, both real-host integrations |
| No source execution, tools, main history or hidden thinking in translation | Core assertions, backend tests and both real-host integrations |
| Display control-character sanitizing; complete bilingual text in previews, only the Chinese in Pi's cards | `preview.test.mjs`, `pi-card.test.mjs`, real Pi renderer assertions |

The correction-policy assertions prove that the intended prompt is supplied.
They deliberately do **not** pretend that mocked completions demonstrate a real
model's ability to detect every linguistic mistake.

### Cache, controls, persistence and CLI

| Behavior / failure | Test locations |
|---|---|
| Exact whitespace-sensitive matching and rule-version isolation; model/thinking/backend choice remains outside the cache key | `features.test.mjs`, `shared/history.mjs` cache contract |
| Cross-store refresh, no double-counted usage, suffix applied after reuse | `features.test.mjs`, `bridge.test.mjs` |
| Failed/empty/obsolete entries rejected; equality to source alone is not a rejection reason; torn and non-record JSON recovery | `control.test.mjs`, `audit-regressions.test.mjs`, `features.test.mjs` |
| Legacy import idempotence and original session bytes unchanged | `features.test.mjs`, `entrypoints.test.mjs` |
| Nonempty current and unversioned cache candidates receive only source-literal byte/count integrity checks, not order, language or semantic rechecks; a literal-changing result triggers fresh translation and an accepted replacement is reusable; the bumped scanner/prompt contract isolates old versioned entries | `literals.test.mjs`, `literal-edge-cases.test.mjs`, `features.test.mjs`, `bridge.test.mjs` |
| Pi active branch, abandoned branch exclusion, no-session memory | `features.test.mjs`, Pi integration |
| Every global mode: on/off/toggle/default/status, unknown fields/permissions | `control.test.mjs` |
| Concurrent control processes retain independent changes and toggle order, including config symlink aliases | `control.test.mjs`, `file-update.test.mjs` |
| Invalid global command arguments do not modify config | `control.test.mjs` |
| Language suffix, input/output independence, branch/session settings restoration, new-session defaults and unchanged shared config | Both real-host integrations, `features.test.mjs`, `pi-controls.test.mjs`, `claude-safety-regression.test.mjs` |
| Only `/translator` registered; no-argument native five-option panel; strict status/help, switch, model and thinking subcommands; no cancellation command | `pi-controls.test.mjs`, `pi-widget-integration.test.mjs`, `claude-safety-regression.test.mjs`, native Claude tests |
| Write-ahead original before request, durable failed first input | `trace.test.mjs`, real Pi integration, `bridge.test.mjs` |
| New journal/cache file permissions and no-persistence behavior | `trace.test.mjs`, `features.test.mjs`, `bridge.test.mjs` |
| CLI help, JSON/plain output separation, exit codes, no fallback text | `cli.test.mjs`, `entrypoints.test.mjs` |
| Public modules import from stdin/eval without executing main or resolving `-` as a file | `audit-regressions.test.mjs` |
| Source through stdin, all isolation flags, model override, no shell evaluation; real CLI wire excludes synthetic global APPEND_SYSTEM and ancestor instructions | `backend.test.mjs`, `fixtures/fake-pi.mjs` |
| Subprocess stderr privacy, invalid protocol, oversized output, process abort | `backend.test.mjs` |

The audit also tightened `translator status`: stray arguments now fail
instead of being silently accepted.

### Pi-specific functionality

- Auxiliary usage (`pi-usage.test.mjs`, `pi-usage-host.test.mjs`): one numeric-only
  `orbit.translator.usage.v1` custom receipt per invoked completion, captured
  before validation/fallback; whole-session request-ID deduplication; no native
  totals/context changes or legacy backfill; retries, concurrent parts, previews,
  unknown usage, cancellation, stale ownership and persistence warnings. Real
  official Pi 1.4.0 tests cover reload/resume/tree/replacement, failed-first-input
  buffering, later flush and no-session memory. Malformed and over-eight-task
  subagent batches are rejected before preview calls; eight valid tasks still
  prepare sequentially.
- Process-local automation bypass (`pi-gateway.test.mjs`): real JSON/print CLI,
  synthetic task/cron/peer prompts and a localhost provider that rejects any
  translation. Enabled saved branch overrides, unavailable translation pins and
  malformed config cannot block the original task or delay its English answer.
  Another extension still runs; settings and prior branch records remain intact;
  no translation journal/cache is created. With the bypass absent or `0`, the
  same gateway-looking input is still translated and fails closed. This checks
  the Translator contract, not the live WeChat service or phone delivery.
- Real SDK loads **the installer-generated registration**, following its stable
  source symlink, with no duplicate extension.
- Input replaces only submitted text, preserves images and queue behavior, and
  leaves original text exclusively in non-context records/previews.
- TUI cards show only what the transcript lacks, collapsed or expanded: the
  original after a delivered translated user row (never the English sent), the Chinese
  after an answer (never the English answer), and the reason plus the original
  for a blocked prompt. Rendered with Pi's own components and Markdown theme,
  links, code and lists are drawn as Markdown and every line keeps the card
  background (`pi-card.test.mjs`, `pi-integration.test.mjs`). RPC shows the
  bilingual notification; headless output goes to stderr.
- A three-switch bar mounts below the editor only in TUI mode. Real raw SGR
  press/release/drag sequences run through Pi's fullscreen renderer and actual
  chat-dock layout with an in-memory terminal. A completed click appends exactly
  one mode entry, keeps editor focus/draft, and does not call a model or add
  model-visible messages. The bar stays fixed while the transcript scrolls.
- The only registered slash command is `/translator`. No arguments open the
  native five-option `SettingsList` in TUI mode; RPC/JSON/print report status/help
  without mounting terminal components. Direct input/instruction/output controls,
  model follow/pin and all five thinking values are validated before persistence.
- Panel/command/shortcut/click state stays synchronized without remounting the
  existing bar. Ctrl+Alt+T/L/Y retain their toggles, including inside the panel.
  Native components are exercised for model search, selection, focus/IME cursor,
  requested/effective thinking feedback, compact/default versus contextual-help
  layouts, CSI-u question marks, search selection stability, prompt mouse editing,
  Unicode, theme changes, narrow widths and dismissal without draft mutation.
  The panel mounts through the host's actual custom-dialog implementation for raw
  mouse/resize checks. Pi 1.4.0 cases preserve another native dialog below/above
  Translator across dismissal or SDK lifecycle invalidation; full TUI reload's
  own global UI reset is a separate host behavior. Real tree navigation, reload and disk resume
  restore branch settings; navigation closes the panel and rejects stale callbacks.
- Branch state uses field patches over file defaults, including atomic model
  pairs and explicit null/null follow. Malformed persisted patches are ignored;
  invalid direct arguments and unavailable new pins leave state unchanged.
  Persisted unavailable pins are reported rather than silently replaced.
- Live-upgrade regression: first load a legacy controller via native `.mjs`
  imports, replace the installed source in a temporary checkout, then `/reload`
  in the same SDK process. The new bar must mount despite the old native ESM
  cache. A second source edit/reload must update the widget, controller and nested
  shared defaults. This covers changing code during a running session, rather
  than merely reloading already-current code.
  Tests cover narrow widths/resizing, regular-mode keyboard hints, theme
  invalidation, modified/right clicks, dragging, padding and stale hit targets.
- Environment-disabled input is visibly locked while other modes work. Config
  errors disable stale controls; persistence failures do not optimistically
  change their state. Global defaults refresh at input/agent boundaries. Disposal
  and session replacement invalidate old widgets and callbacks. RPC retains text
  status; JSON/print install no widget.
- Translator model/thinking overrides never change the main model, its thinking
  or shared config. The real SDK inspects isolated input/output requests using a
  pinned reasoning model, requested `max` clamped to `xhigh`, a non-reasoning pin
  with no reasoning option, and follow with `low`; the main request retains its
  own model/level. Pins survive disk resume. Provider, storage, UI, config and
  session-switch failures cannot fail open to the original Chinese prompt.
- Default-follow checks use the session's current model, read per request, at
  translator `thinkingLevel`. The real SDK fixture uses a reasoning-capable model with
  the session at `max`: translation requests carry `reasoning_effort: "medium"`
  while main requests keep the session's own level (`max`, clamped to `high` for
  that model). Unit tests cover clamping and models without reasoning.
- Steering and follow-up input sent while Pi works is translated but has no
  successful display entry or notification while queued. `pi-queue-regression.test.mjs`
  uses a real SDK delayed tool to check both queue modes, once-per-user-row event
  and persistent ordering, cache reuse, discard, reload and ambiguous originals.
  `pi-input.test.mjs` covers exact image bytes, duplicate events, Input off and
  extension bypasses, session/branch invalidation, immediate failures and
  post-delivery display errors. Another real SDK regression steers input during
  a running turn to check cancellation. A steer delivered between two delayed
  tools gets its card while the second still runs, the behavior Claude Code's
  desktop drawing now matches for a prompt folded into a long turn.
- Complete output only: no tool chatter, hidden thinking, non-assistant messages,
  aborted/unfinished turns, duplicate delivery or original-answer mutation; an
  answer cut at the output limit (`length`) is translated.
- In the TUI, the `turn_end` handler holds the turn only until the person
  interrupts (`ctx.signal`) or queues more (`hasPendingMessages`); the translation
  goes on, and its card, landing after a later row, names its answer
  (`answerStart`), a failed one too. RPC releases the wait without cancellation;
  print/JSON waits through ordinary aborts before exiting. Navigation and reload
  record a `cancelled` card before invalidating. An unreadable configuration warns
  once and adds no card. Only matching explicitly parent-owned tasks defer
  input/final-report display (`pi-output.test.mjs`, `pi-card.test.mjs`,
  `pi-lifecycle-e2e.test.mjs`, `pi-handoff.test.mjs`).
- A translated prompt waiting in Pi's queue shows its original above the editor
  until delivery. A run that stops while a queued prompt is translated gives the
  prompt back to the TUI editor, after the draft there, while Ctrl+Alt+C gives
  nothing back. Esc or Alt+Up puts the queue back as the originals and clears the
  queue line. The skill and template lines `pi.getCommands()` lists and matched
  parent-prepared child tasks go out as typed, while a path, an unknown skill, an extension command's
  name and a `!` line sent with Alt+Enter are translated (`pi-input.test.mjs`).
- Pi's editor history maps refilled English rows, and a switched-to session's
  file, back to exact, unambiguous originals during a refill only (English typed
  and submitted stays as typed), with one wrapper per process
  (`pi-history.test.mjs`). `/tree` and `/fork` to a translated prompt put its
  original in the editor, once and only in place of exactly its English
  (`pi-history.test.mjs`, and through the real Pi terminal in `pi-tui.test.mjs`). After a refusal, the fallback is the newest model of
  the other family by the version after the family word
  (`claude-sonnet-4-5-20250929` over `claude-sonnet-4-20250514`), and a long answer
  whose parts fell back in part names the fallback model whichever part finished
  last (`pi-input.test.mjs`, `pi-output.test.mjs`). Real terminal history tests
  exercise refills; real CLI subprocess tests exercise hash-only task handoffs,
  original Chinese chain results and unmatched custom-launched JSON sessions
  (`pi-subagents-process.test.mjs`). A parent PID alone suppresses nothing.
- 0.6.10 long-answer checks: each part needing translation gets one
  isolated completion (now with one allowed API retry), with one record for the answer and a cleared status when
  it ends (`pi-output.test.mjs`, `pi-integration.test.mjs`). Parts reach the local
  fixture concurrently, cut between paragraphs/lines and preserve masked
  literals. Placeholder failure stops siblings without a repair call; neither
  parts nor joined output receive a language or semantic check.
- 0.6.10 preflight checks: the shared scanner excludes the same certain
  literal spans from language decisions and masks them in both directions'
  requests. Literal-only input/output and answers with no English prose to
  translate skip before config, branch history, model, trace or UI work. Real
  SDK fixtures must cover bare Chinese filenames, paths, identifiers/settings/
  mentions, quoted Chinese prose, ambiguous prose and plain `OK` output, plus
  exact filename/link/code restoration. Chinese resources or hidden thinking
  must not suppress translation of English prose. Accepted literal content must
  never trigger a post-translation language rejection.
- Output failure, missing model, cancellation, session switch or UI/storage loss
  retains the already-completed main answer.
- Actual provider requests are inspected before and **after reopening the native
  session file**: neither the original Chinese prose from translated prompts nor
  display-only output notices leak into subsequent model context. Literal bytes
  deliberately remain unchanged, including Han in filenames and paths.
- Failed **first** input is journaled even when Pi never creates a native session.
- Real SDK pending-input tests suspend a local translator response, then reload,
  dispose or replace the runtime. No main request or late entry reaches the new
  context. Getter exceptions in cleanup cannot escape the input handler.
- The registered **Ctrl+Alt+C** shortcut cancels actual translation HTTP requests
  for input, output and both simultaneously. Cancelled input never reaches the
  main model; cancelled output retains the completed answer; subsequent
  translations remain usable. The panel also routes the shortcut while focused.
  Signal-ignoring providers cannot publish late successes. Output cleanup covers
  tree/session identity changes, throwing getters, reload and disposal without
  stale entries, notifications or status clears.
- A separate ordinary `session.abort()` regression, originally named for Pi
  1.0.4, documents the idle-input abort gap rather than successful cancellation.
  Focused evidence is on the installed 1.1.0 SDK; neither test establishes
  physical Escape-key behavior.

Files: `pi-input.test.mjs`, `pi-output.test.mjs`, `pi-history.test.mjs`, `pi-integration.test.mjs`,
`pi-controls.test.mjs`, `pi-settings.test.mjs`, `pi-widget-integration.test.mjs`, `features.test.mjs`,
`trace.test.mjs`, `pi-lifecycle-regression.test.mjs`, `pi-queue-regression.test.mjs`.

### Claude-specific functionality

Two real-host end-to-end paths are tested:

1. `claudeBackend: "pi"`: real Claude main requests use an HTTP fixture, while an
   isolated fake Pi executable verifies the legacy translation subprocess flags.
2. `claudeBackend: "native"`: **Pi is explicitly unavailable**. The actual Claude
   completion API makes isolated HTTP requests and the plugin is discovered
   through the **installer-created user skills link**, not `--plugin-dir`.

Both verify prompt translation with the stored row in English, native system-notice persistence, unchanged main
answers, independently enabled output, cache reuse after resume, failure blocking,
slash-command behavior and absence of bilingual notices from main wire context.
Native requests contain one source message and no tools/history. Default-follow
checks use the session's `--model`, `output_config.effort: "medium"` and a
32000-token cap; the fake Pi executable checks `--thinking medium` by default.
The added real-host settings path pins a different translator and `high` thinking
for both input and output, verifies the actual native requests / Pi worker traces,
then restores follow. It checks unchanged main-model requests and config bytes,
settings restored on resume, and file defaults in a new session.

Worker tests additionally cover session-model selection, file and session pins,
all five thinking overrides in both directions, explicit follow clearing a file
pin, native versus Pi-default follow semantics, malformed session settings and
environment-lock precedence. They also cover fail-closed input without any model,
a restart message for a mod loaded before the update, the original translator
recorded on cache reuse, preparation/finalization, JSON-round-tripped literal
state, per-request terminal markers, missing/duplicate markers, native
API failure/cancellation, obsolete continuations, wrong sessions, no-persist,
cache-off and global-disable precedence. Each request gets one native completion,
and `finish` returns a result or failure, or once a fallback round for the parts
whose replies were cut short or could not be given, never a third round, a
language audit or a repair call. A long answer's parts are separate requests,
joined in order, and a failing part is named. Literal-only cache integrity must
accept nonempty candidates regardless of language. `rows` follows forks: a
branch's `forkedFrom`, a copy's session ID and a parent's `continued-in` record
name the parent; a fork that names none is tied to the session whose journal
linked a row it draws, never to a session written since; memory-only mode still
names a parent; a branch of a branch follows its lineage, nearest ancestor first,
and a copy of a copy names the copy; an ancestor's journal that cannot be read and
a project file that cannot be stat'ed lend nothing without failing the session's
own rows; an invalid workspace name reads nothing. It returns 500 rows at
most to a mod of 0.6.15 and the newest translated prompts as `recall`; a
continuation of the 0.6.15 worker still finishes, as one part.

Claude-native test-kit cases cover malformed/truncated/nonzero worker output,
unknown statuses, six fail-closed input paths, session-model forwarding (including
a switch and an unavailable model), unified command validation, per-session
settings, memory-only settings, subagent/interruption bypass, the native five-option
Pane and its alias/full-ID input, all three button actions, which carry no
hotkeys, native completion dispatch, and output failure with an unavailable UI.
The pane, commands and band are exercised together for immediate synchronization,
model follow and invalid-input rejection without any main-model turn. The Cancel
button, shown only while a translation runs (key 4 in the pane), is tested on
native input and output completions, rejecting late replies
without invoking finish or publishing a translation. Output warnings are
best-effort: even a failed toast cannot break or retry the completed answer.

The 0.6.10 checks exercise preflight with output explicitly enabled:
answers without English prose to translate, including literal-only answers, skip
before model/mode reads or worker startup; mixed prose and plain `OK` translate.
Both backends and the native test kit must cover literal-only input/output with
no translation call/log, quoted Chinese input, ambiguous prose and skipped
`turn.step` answers marked handled even if `turn.complete` carries only a last
row that alone reads as English (`OK`). Both real backend fixtures must inspect
masked requests and exact restoration for certain bare Chinese filenames,
paths, identifiers/settings/mentions, Markdown targets and code in both
directions. Completed/nonempty replies are not language-checked after restoring
those bytes; structural failures still block input or leave the answer intact.

Real HTTP fixtures also return multiple text blocks: their stored message UUIDs
are checked against display-state keys. Native
and Node regression fixtures prove that repeated last rows cannot associate one
answer's whole translation with another, and synthetic fallback uses no text key.

Settings controls are tested with deterministic overlapping panel, command and
button operations in persistent and memory-only modes, failed config/store/
refresh calls, environment locks, invalid syntax and session switches. Per-session
queues retain independent changes, unblock after failure, and never replace the
active session's status or settings through a stale pane/band callback. Evidence:
`claude-safety-regression.test.mjs`, native test kit and real integration.

Cancellation Node regressions cover input/output during worker preparation,
native completion and finish, plus host dispatch signals and session refresh.
They check signal propagation, closed worker streams, discarded late results, no
submitted input or drawn/appended output, and no fallback retry. Where a surface
draws the session, an answer is translated after its turn: the step returns at
once, an interrupt leaves the translation, Cancel stops it, and a reload resumes
it. The mod has no `turn.abort` hook. `claude-cancellation.test.mjs` retains the
legacy direct `-p` dispatch-abort fixture for both backends: a test mod invokes
`$.turn.abort` during the completion itself; no late output is published and the
native HTTP exchange or optional Pi child closes. Separately,
`claude-headless-output.test.mjs` proves the session-owned runner versus a
step-owned timer and explicit Cancel. `claude-tasks.test.mjs` uses actual
stream-json interruption, **without `ui_attach`**, and verifies durable finished
output and normal exit notices for both backends. These are host dispatch tests,
not physical key routing or real-host input-stage Escape acceptance. No custom
global Ctrl binding is installed.

Claude Code's own prompt history is tested end to end. `claude-tui.test.mjs`
checks on the real TUI that its `history.jsonl` holds only the typed text, for
prompts typed, recalled, edited, folded into a turn and queued; that Up/Down show
the Chinese; that an unchanged recalled prompt replays its saved translation with
the cache off while typed English is left alone; and that a resumed and a new
session map older English entries to their originals; and that lines an earlier
release saved in English get their originals in the file at the next start, so
two Up presses in a row show both. Node regressions check
that the typed text enters while the stored row the model reads is English: a
whole block or whole lines, a row the engine joined from two queued prompts
(originals drawn in the order sent), a prompt a hook below changed (around a
path, or entirely when Chinese prose would remain), context blocks the desktop
app adds kept as they are, a toast when no row took the English, and command
lines, meta prompts and other origins passed through as typed. Answer steps are
covered too: a cut at the output cap translated with its continuation, or alone
when the turn ends on it; a refused step skipped; each answer of a turn a Stop
hook continues translated. A fork that names no parent asks once about the rows
it draws without a drawing, then draws the found session's rows and takes its
settings, while an ordinary session never asks. Further cases: a prompt with an
image typed mid-turn goes down in English (a document's does not), a stored row
that kept no rewrite gets the warning and no drawing, a session is loaded again
after a session end empties `$.state`, only lines `$.command.list()` lists pass as
commands, an interrupt hands a queued prompt back down while Cancel drops it,
settings `UserPromptSubmit` hooks read the English, an SDK prompt spread over
text blocks is stored as one English block, a session change lets an answer's
translation finish, and memory-only settings come along to a branch
(`claude-safety-regression.test.mjs`). The worker's history pass changes only a
line that is exactly the English of a prompt its session translated within ten
minutes, keeps every other line byte for byte, waits while Claude Code holds
`history.jsonl.lock`, leaves no copy behind, reads each line once and does
nothing in memory-only mode; an answer's lease makes a second worker wait while
the first lives, take it once that one is gone, and answer `done` after a
translated result (`bridge.test.mjs`). The mod skips an answer whose lease says
`done` and releases its lease once it has translated one
(`claude-safety-regression.test.mjs`).

The native test kit's `$.session.append` stub gap was documented on 2.1.292;
real-binary HTTP integration tests cover actual notices and resume instead.

Display rows are tested end to end. The kit tests that the prompt-hint status
and the original drawn under a prompt row come from mod state, never from the
stored row. The real-binary integration checks that the journal's `linkedRow`
for a translated prompt matches the transcript's stored prompt row, and that
`--resume` in a new process draws both the original prompts and the Chinese
answers again.

The desktop app's drawing is tested at three levels. Worker tests read a fixture
transcript under a temporary `CLAUDE_CONFIG_DIR`: an answer's text row gets both
`<message id>-t<n>` counts (its message's text blocks and all its blocks, a row
written twice counted once, an assistant line without a message skipped), a
`queued_command` row its `source_uuid`, a prompt row and any other attachment no
id, and a row the transcript does not hold yet is reported missing. Of two copies
of a transcript the newer is read, also through a linked workspace. Native kit
cases mount rows on the `desktop` surface: a row showing the original is handed
the prompt as sent, while a terminal row, a row already showing the English and a
longer prompt are left alone; an answer under the app's id is drawn only under
the block showing exactly the linked row's text, not under one whose text is part
of it; the first remote draw asks for the journal's rows once and the terminal
never asks; rows restored with a remote surface attached get its ids then, with
no second pass; a failed pass is tried again by a draw 30 seconds later; with a
remote surface attached, a folded prompt and an answer are asked for as soon as
they are linked, and again after 500 ms while the transcript does not hold the
row, while a prompt stored as its own row is not asked for. A folded prompt is
drawn by its English, or from the app's Chinese copy, under an id not known yet;
it keeps its original through eight asks that find no row, gets its id when a
step ends, and from then on another row with the same English gets none; the
same English folded from two originals draws none.
`claude-desktop.test.mjs` runs the real binary in the SDK's stream-json form,
attaches as `desktop` and asks for rows with the props and ids the app uses, live
and after `--resume`, a folded prompt drawn both from the English and from the
app's Chinese copy. A second case folds a prompt into a turn whose next step runs
for eight seconds and requires its original under the app's id before the turn
ends; 0.6.14 drew it only after. On 0.6.5 it fails at its first check: the app's prompt row
comes back as the Chinese.

The desktop app's slash menu for a session that has not started is tested at two
levels. Native kit cases refuse the mod's `$.command.register`, as Claude Code
does where `SKILL.md` is the user's own `/translator`, and check on the terminal
and `desktop` surfaces that `session.start` still loads the session's settings
(the band shows them) and restores rows; any other registration failure goes to
the debug log while settings still load; a failing `/translator` is answered by
the mod's `.catch`, never by the run beneath it that stands for the skill.
`claude-skill.test.mjs` links a copy into a skills folder and asks the real binary
for its commands with `disableAllHooks`, as the app does: `/translator` is listed
once, with `COMMAND_SPEC`'s name, description and hint. Sent as a new session's
first message over stream-json with the `desktop` surface attached, `/translator`
opens the settings pane and the band shows the file's settings; a setting changes
the band; neither calls a model; the debug log names no skipped hook of the mod;
and an ordinary prompt's request lists another user skill but not `/translator`.
Loaded with `--plugin-dir`, the same copy lists only the mod's own `/translator`,
which answers without a model request. A Node check keeps `SKILL.md`'s frontmatter equal
to `COMMAND_SPEC` and the manifest's `"skills": []` in place: without it,
`--plugin-dir` reads `SKILL.md` as `/translator:translator`, aliased `/translator`,
and `claude-tui.test.mjs`'s `/translator status` reaches the model.

Prompts sent mid-turn and reloads of the mod are tested at three levels. Node
regressions load two copies of the module against one fake host, the way Claude
Code reloads it: a step in the old copy and `turn.complete` in the new one
translate an answer once and draw it under the reply row the new copy stored; a
copy replaced before it translated still gets its answer drawn; a reply row of an
earlier turn is never used; waiting originals and memory-only settings survive.
They also cover a waiting translation matched only on whole lines, suppression
of an unlinked/queued UI row even when its text matches, and a dropped prompt
being forgotten. Success notices wait for the stored row too; failure notices
remain immediate. Native kit
cases raise the real `queued_command` row by doors `delivery` and `attachment`,
and a prompt row stored after its turn began. `claude-live-session.test.mjs` and
`claude-tui.test.mjs` run the real binary, as described in the verification
record.

### Installer and directory structure

| Scenario | Expected result / test |
|---|---|
| Neither host present | Nonzero error; no config, registration, directory or backup writes |
| Only Pi / only Claude present | Automatic selection; other agent's directories remain absent |
| Both present | Interactive Pi/Claude/both/cancel choices; non-TTY requires explicit choice |
| `--agent pi`, `claude`, `both`; `--yes` | Selected available hosts only; missing requested host fails |
| Old/unknown versions; invalid flags/choices | Reject before applying changes |
| Managed executable paths and non-executable files | Proper discovery; shell-visible broken install is not silently bypassed |
| Help / dry run | No installation writes, including personal config and backups |
| Paths containing spaces / unrelated CWD | Node and shell entry points work |
| Custom config dirs and alternate/shared Claude skills | Honor CLI/env locations; preserve directory symlinks |
| Existing symlinked Pi settings | Update target, keep link, retain unrelated settings |
| Existing config/history/extensions | Preserve content/preferences; backup exact old settings bytes |
| Reinstall | No duplicate entries, no extra backups or operations |
| Legacy Pi root paths | Migrate/deduplicate to `pi/index.ts`; no root hook shim |
| Conflicting files/directories/dangling links | Preflight abort; neither selected host partially installed |
| Invalid settings/config schemas | Error without replacing original bytes |
| Late filesystem exception | Roll back this run's links/files/settings/empty directories |
| Settings changed after planning or while preparing temp/backup files | Late baseline comparison rejects detected changes instead of overwriting them |
| Pi settings lock compatibility, including symlinked settings | Real Pi 1.0.4 FileSettingsStorage interoperability with deterministic process barriers |
| Rollback fails due to an external write | Preserve external content and the original backup; report its exact recovery path |
| Missing Node | Shell entry reports dependency error, no modifications |
| Layout invariants | Pi/Claude sibling directories, one Pi manifest entry, valid relative imports, clone config stays put |
| npm packaging | Stable external data directory, npx-cache refusal, every runtime helper/scanner included, no personal data/tests/generated host declarations |

Files: `install.test.mjs`, `layout.test.mjs`, `package.test.mjs`, plus both real-host integrations.

## Explicitly not claimed as verified

- Real hosted-model translation quality, typo detection, ambiguous intent or
  guaranteed resistance to all prompt injection. Those require curated live
  evaluations and may incur model charges. Latency and cost of particular session
  models at a given `thinkingLevel` are likewise not measured.
- A complete Markdown/HTML/shell parser or infallible classification of arbitrary
  unwrapped strings. Shared preflight masks only documented, certain syntax and
  names for input; output adds the documented tolerant names/resources, protected
  Chinese units and bounded preface skip. Instructions for unmasked text are not
  a byte-integrity proof. Use backticks for exact strings. Language decisions
  run only before translation; doubtful input remains eligible, while output
  deliberately favors avoiding repeat translation. There is no post-translation language/semantic
  gate or AI audit/repair, including for native Claude, long parts and joined
  output. Completed/nonempty responses with valid literal restoration may still
  contain untranslated or inaccurate prose. Literal content cannot trigger
  language rejection, and cached text receives source-literal byte/count checks
  only, not a language or semantic recheck.
- How fast a hosted model translates a part of a long answer, how many requests
  a provider serves at once before limiting them, and whether two parts word a
  term the same way. The parts are tested with a local model whose response time
  grows with its text; the timings that motivated them come from local journals
  of real translations, not from a measured benchmark.
- Production provider credentials, billing, real network outages, organizational
  policies, or every future Pi/Claude release. No paid calls were made here.
- Physical keyboard/mouse/terminal-specific input reporting, all visual layouts,
  every terminal emulator, or hidden UI bands. Pi's fullscreen renderer/layout
  and raw SGR mouse routing are tested programmatically through an in-memory
  terminal, and the Claude Code and Pi terminal tests send keys to the real TUIs
  through tmux; neither is a hardware or visual compatibility check on every
  terminal.
- Linux execution on a separate machine, the exact minimum Node version, Windows
  installation (explicitly unsupported), or arbitrary network filesystem behavior.
- Power loss, forced process kill halfway through installation, every possible
  disk/permission failure, or a globally coordinated multi-installer transaction.
  Rollback is tested for caught application/filesystem errors. Locks coordinate
  cooperating processes only: no OS CAS against arbitrary editors, no guarantee
  for unknown symlink aliases or Pi's first-create read-before-lock race. Crashed
  owners can leave locks requiring manual inspection.
- Reliable ordinary Pi SDK/RPC abort during idle input preprocessing, or physical
  Escape-key cancellation in either host. Pi cancellation evidence invokes the
  registered Ctrl+Alt+C handler against the real SDK and routes programmatic keys
  through its native panel. Claude evidence includes native button actions for
  input/output, legacy direct-dispatch abort, and stream-json interruption with
  output retained in both backends, not a guarantee that a physical key reaches
  an abort in every UI/dispatch phase. Automated tmux key injection is also tested;
  it is not a manual physical-key acceptance run.
  Bare Pi SDK disposal blocks late writes but may leave the translator running
  until completion/timeout.
- Claude's special synthetic no-provider-step transcript flush edge case. Normal
  headless output waits for native notices, but exiting the process can end
  unfinished work. A copy with no initialized session-start output runner retains
  the safe legacy waiting-step path. Stream-json may still defer the next prompt
  until an outstanding side completion finishes; persistence is not an unlimited
  background scheduler.
- The Claude Code desktop app's own painting and its exact naming of rows. The
  tests send the asks the app sends, with the props and ids seen in its logs and
  transcripts on 2.1.293, and check what the session answers. How the app paints
  that answer, which of the two counts of `n` in `<message id>-t<n>` it uses (both
  are covered), and the Claude mobile app and VS Code extension, which also draw
  remotely, are not tested.

These boundaries matter: broad offline coverage and real host loading are useful
regression evidence, not a promise that every external environment is identical.
