Commit Graph
4758 Commits
Author SHA1 Message Date
gensokyo-devandgensokyo-dev 07e4b700ac test: let the deadline policy own test waits
Vitest's expect.poll and vi.waitFor stop after one second by default, a
host-speed guess that the project's deadline policy does not see. Under
hosted-CI load, kunaProjectConcurrency failed that way twice on unrelated
PRs. Another 256 waits relied on the default, and 97 passed literals
picked to fit some host.

Waits now take as long as their test may run, less the reporting reserve
that children already keep. Each Vitest project sets expect.poll's
default, and the new waitFor in tests/support/testWait.ts replaces
vi.waitFor. Fixture readiness waits (awaitAcknowledgement and the provider
process waits) default to the same deadline, and their host-speed literal
arguments are removed. One remainingTestDeadline owns the arithmetic for
both children and waits.

Ten waits whose short timeout was the real check now assert it directly
(Kuna version probe, Cutter discovery, doctor and Inspector cancellation,
apktool fixture teardown, JADX disconnect): settlement before the
production deadline that would otherwise also satisfy the wait, or a
deadline pinned beyond every test deadline.

The rea/project-wait-deadline lint rule rejects vi.waitFor and explicit
expect.poll timeouts. The testing guide and the CI tests audit skill now
document the policy.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 19:28:38 +08:00
gensokyo-devandgensokyo-dev 054fbc4c26 fix(cli): exit one-shot commands by draining the event loop
process.exit() shuts Node's platform down without tearing the V8
isolate down. When a concurrent Maglev or baseline compile job is
waiting for a collection at that moment, the main thread blocks forever
in NodePlatform::Shutdown joining it (captured with gdb on Node
24.22.1). A one-shot command that called Incur's exit then never
terminated: about 1 in 20 such exits after the lazy CLI loading change,
and rarely on main.

The exit callback now sets process.exitCode and lets the event loop
drain, so Node tears the isolate down in order. A handle that outlives a
one-second grace still gets the forced exit with the same status.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 19:28:31 +08:00
gensokyo-devandgensokyo-dev c169e8d56e test(native): assert universal slice refusals by their input issue
The MCP universal-slice refusal test also compared whole English
messages; it now checks the architecture input issue and only the
offered selector. The linker-options CLI test drops its per-call child
timeout, which the testing guide reserves for testing the deadline.

Refs #9792
Refs #8935

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 12:13:56 +01:00
gensokyo-devandgensokyo-dev 6a2d1dc066 test(native): own Mach-O test children and assert refusal facts
The chained-import CLI test ran xcrun through a bare execFile, outside
the test's process registry, so a stalled compiler or dyld_info could
outlive the test; it now uses the processes fixture. The slice
selection refusal tests compared whole English messages; they now check
the refusal kind, the offered selector, and that the selector works.

Refs #8723
Refs #9792

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 12:13:56 +01:00
Lewis Wongandgensokyo-dev 4bdd6d10c9 fix(native): name chained imports from the binary's import table
dyld_info prints chained-fixup rows by import ordinal, and a row with two
spaces after the ordinal has two readings. REA settled them against the
nm undefined-symbol list, which omits weak-def coalesced names, so such
imports got null or ambiguous names. inspect-macho now reads the
chained-fixup import table from the binary and matches rows by ordinal,
falling back to the symbol table with a recorded limitation when the
table is unusable.

Fixes #8723

Squashed from #8724 and rebased onto main.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 12:13:56 +01:00
gensokyo-devandgensokyo-dev ffc8318da3 test(apple): run the ARM32 IPS join through the shared CLI fixture
runFileCli was folded into cli.run by #10412.

Refs #10201

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 12:13:56 +01:00
ducanhnguyen223andgensokyo-dev 364432d6c9 fix(native): correct Mach-O command completeness
inspect-macho marked fully decoded LC_LINKER_OPTION, LC_NOTE and
LC_FILESET_ENTRY commands as undecoded. Each now counts as decoded only
when its payload is consumed exactly (the fileset string must start
right after the 32-byte header and end at cmdsize), and the note keeps
its full owner storage.

Fixes #8935

Squashed from #8937 and rebased onto main.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 12:13:56 +01:00
ShuaixinHuangandgensokyo-dev ff64ed5142 fix(apple): match IPS ARM32 joins by CPU subtype
Fixes #10201

Squashed from #10270 and rebased onto main; the ARM32 branch runs before
the ARM64 family matching from #9971.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 12:13:56 +01:00
2SinceJulyandgensokyo-dev d80339df1d fix(apple): agree known ARM64 subtypes with their report family in IPS joins
Fixes #9960

Squashed from #9971 and rebased onto main.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 12:13:56 +01:00
chordpliandgensokyo-dev d5dd0b6fd9 fix(native): select same-family universal slices by subtype name
Fixes #9792

Squashed from #9793 and rebased onto main.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 12:13:56 +01:00
Avocadoandgensokyo-dev 0c083b7271 fix(browser): qualify target listing coverage by transport admission
Fixes #7687
Supersedes #7746

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 12:13:50 +01:00
morlutoandClaude Opus 5.5 2343e56925 test: model open -W in the UI lane and drop the pyc wall-clock bound
The native UI verifier now removes its fixture root only after `open -W`
exits on its own (67ce671d66). The lane test's `open` stand-in stayed
alive until the test released it, so every case waited out the grace
period, retained the root and failed on the Apple E2E job. The stand-in
now exits when its fixture exits, as `open -W` does.

The shared-descendant pyc case bounded wall-clock time at 5 s; it runs in
~0.5 s locally but 4-7 s on loaded CI shards and failed scheduled runs.
The retained-heap bound and exact object counts already detect the
per-wrapper copy regression, so the host-speed assertion is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 05:36:39 -05:00
gensokyo-devandClaude Opus 5.5 9e24b36fa6 test(cli): assert the retained entropy observation at its current location
Since #8444 the completed observation is projected once at the composed
error's details.partial_observation rather than inside primary_error,
so restore #7319's retained-Evidence assertion there.

Refs #7279

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 10:21:04 +01:00
gensokyo-devandClaude Opus 5.5 abd64498b7 test(cli): scope the entropy cleanup regression to the provider id
Main now reports a descriptor close failure as unreadable_output without
the completed partial observation, so the retained-Evidence assertion
from the original branch no longer describes current behavior. Keep the
provider-id regression this fix owns; the dropped partial observation is
tracked separately.

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 10:21:04 +01:00
6c8be105a2 test(cli): assert retained entropy Evidence beside the cleanup diagnostic
Check the partial observation returned with the close-failure diagnostic: provider id, file digest and byte count.

Refs #7279

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 10:21:04 +01:00
bac8a115cd fix(cli): report the byte-entropy provider id in cleanup diagnostics
inspect-byte-entropy passed a hard-coded "rea-byte-entropy" to the shared command finalizer, so a failed descriptor close named a provider that differs from the Evidence provider ("byte-entropy"). Use BYTE_ENTROPY_PROVIDER.id so both come from one owner.

Fixes #7279

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 10:21:04 +01:00
1b10caed25 test(conformance): guard Malayalam heading links against joiner loss
Extend readmeConsistency.test.ts with a Malayalam case mirroring the
existing Persian one, addressing the P2 review finding on #6383. The
update heading's U+200C is written as an explicit escape so an editor
cannot strip it invisibly; verified the test fails when the joiner is
removed from README_ml.md.

npm run test:local -- tests/conformance/readmeConsistency.test.ts
passes 10/10.

Supersedes #6383 (follow-up to #6731)

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 10:21:04 +01:00
0b21b6ce03 fix(skill): keep recovery client IDs aligned with the setup registry
The registration repair guide named 24 of the 29 client IDs that
`rea setup --client` accepts. Add codebuddy, cline, kimi, workbuddy and
lm_studio, and make the documentation-facts check require every setup
client ID in that list so a new client cannot be omitted again.

Regenerate the tracked Cursor plugin copy of the reference.

Fixes #7640

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
2026-11-14 10:21:04 +01:00
morluto b138f52e12 refactor: prune internal seams and preserve configuration ownership (#10430)
* refactor: prune internal seams and preserve configuration ownership

* fix(setup): keep YAML aliases and inherited registration settings on refresh

- Reuse an unchanged YAML collection only while every alias in it still
  resolves after the edit; otherwise rebuild it so the alias becomes its
  value. A sibling anchor replaced or moved in the same refresh no longer
  leaves an unresolved alias in Hermes or Goose output.
- Keep a connection selector whose existing value already chooses stdio
  (for example an inherited Hermes transport: stdio) instead of superseding
  it; remote selectors are still replaced. Explicit stdio selectors are
  therefore no longer rewritten (Kimi expectation updated).
- When the whole Hermes registration is inherited through a merge,
  override it with that merge and write only the changed fields.

* fix(setup): edit registration fields whose YAML key is an alias

A key written as an alias to a string scalar (for example *field with
field: &field env) was treated as a source-only key, so any change to
its value was refused in Hermes and Goose. Resolve such keys to their
string, keep the aliased spelling while its anchor survives the edit,
and write the plain key when the same edit rebuilds the anchor.

* test(process): bound cleanup-boundary waits and always release gates

Both cancellation tests awaited a bare started promise and released
their gates only on success, so a missed boundary waited for the outer
deadline and could leave cleanup parked. Use awaitAcknowledgement and
release and join the cleanup in finally.

* fix(setup): repair only aliases an edit leaves unresolved

Replace the pre-edit alias survival guess, which expanded aliases to
sibling anchors that survive unchanged, with an exact repair: record
what each alias in the replaced value resolves to, write the edit, then
replace only aliases left without a preceding anchor by that value.

- Keep an aliased key's source position, so its anchor stays ahead of
  later aliases.
- Treat an aliased key as its string only when no other key in the map
  resolves to the same string; an ambiguous one stays guarded.
- Override an inherited Hermes registration only when the requested
  entry keeps every inherited field, so an explicit replacement can drop
  remote selectors.

* fix(setup): repair aliases whose binding an edit changes, keeping comments

An alias whose own anchor was removed could resolve to an earlier anchor
with the same name and silently change value. Record the node each alias
resolved to and repair any alias that no longer resolves to that node.
Carry the alias's comments and spacing onto the repaired value.

* test(setup): split the Hermes anchor and merge refresh cases

* fix(setup): refuse removing an overridden key its merge would restore

The merge-retention check skipped keys the mapping also overrides, so
dropping such a key (for example a remote url during an explicit stdio
replacement) let the retained merge restore it. Check removal before
skipping overridden keys, so the edit is refused with the existing
message instead of producing a registration that cannot be applied.
2026-11-14 04:08:37 -05:00
67ce671d66 fix(verifiers): release fixture roots only after their owners finish (#10491)
* fix(verifiers): release fixture roots only after their owners finish

Several real-tool verifiers released a fixture root even when a cleanup
that still used it had failed, so a surviving MCP server, session or
descendant kept running against a deleted directory and the failure
looked like ordinary cleanup.

- scripts/lib/verifier-cleanup.mjs: new releaseAfter(dependents,
  release, retained) task. It attempts every dependent cleanup and
  releases the resource only if all succeed; otherwise it keeps the
  resource and reports it with the dependent failures.
- verify-signal-recording: the root is released after the MCP client
  and transport close, and the host-limits read moves before the root
  is acquired so its failure cannot leak the root.
- verify-recorded-crash: sessions, the sentinel and the process-lineage
  check must all succeed before the root is released. A transport close
  failure after a failed connect keeps the connect failure.
- verify-wasm-artifact: both roots are released after the MCP client
  and transport close; the residue check and the two directories stay
  independent after that.
- verify-native-ui: `open -W` exits only after the app exits, so the
  root is removed only once that exit is observed; otherwise it is
  retained and reported.

Fixes #8561
Fixes #8562
Fixes #8564
Refs #8559

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(verifiers): treat a signaled open as unproven fixture exit

A terminated `open` launcher does not prove the LaunchServices app it
started has exited, so only an `open -W` that exits on its own counts.
Also clear the grace timer so a prompt exit does not hold the process
for the rest of the grace period.

Refs #8559

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(verifiers): model open -W exiting after the fixture is signaled

The native UI verifier now removes its root only after `open -W` exits
on its own. The authored recording's `open` never exited when the
fixture was signaled and had no signalCode, so the complete case waited
out the grace period and kept the root. The double now behaves like
`open -W`: it exits once the fixture process is signaled.

Refs #8559

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 09:56:18 +01:00
8e2853dd7b fix(ghidra): prevent loader relocations from constant folding (#10483)
* fix(ghidra): prevent loader relocations from constant folding

Ghidra imports loader relocation destinations with their file bytes. A
destination in a non-writable loaded block can be populated by the
runtime loader instead, for example a copy-relocated musl `stderr` in
RELRO, so the decompiler folded the on-disk zero and showed
`fprintf(NULL, ...)`.

Before initializing the decompiler, mark each loaded, initialized,
non-writable block containing a relocation destination volatile in the
ephemeral database, and add a limitation to procedures that read such a
destination. ELF shared objects and PIEs are included; relocatable
objects and core files are not. Permissions and source FileBytes are
unchanged.

Sessions without database-mutation authority (Windows P0 and existing
caller projects) record the limitation but do not mark blocks volatile,
and the analysis profile versions the two behaviors separately.

A real Linux Ghidra ELF probe runs in the Ghidra bridge CI lane.

Fixes #4177

Squashed from #4198 and rebased onto main. Rebase adaptations: the
read-only gate, the verifier renamed to verify-ghidra-loader-relocation
so it stays out of the manual-verifier glob, docs moved to
docs/testing-lanes.md and docs/providers/ghidra-provider.md.

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ghidra): await installation inspection in the relocation verifier

inspectGhidraInstallation became async on main after #4198's base, so the
verifier asserted on a pending Promise. Also wait for provider output to
close before reading it, like the sibling Ghidra verifiers, and keep the
workflow reference inside the docs site.

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ghidra): address relocation review findings

- Version the existing-project analysis profile with the read-only
  relocation semantics, since those sessions now add the limitation.
- Inspect the installation through the verifier helper that owns the
  file-reader resources.
- Build the fixture with -fPIC: with -fPIE, GCC 12 emits a copy
  relocation of rea_external_value into writable .bss, so the probe's
  GOT check could not hold.
- Run probe cleanup through withVerifierCleanup so a cleanup failure
  does not hide the probe failure, and keep the workspace when owned
  Ghidra cleanup is incomplete.
- Link the workflow with a full GitHub URL, per the docs guide.

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ghidra): reach the external data through the GOT with a weak extern

Building the whole fixture with -fPIC also turned the reader's direct
load of rea_loader_pointer into a GOT-relaxed lea plus two loads, and
the real-Ghidra lane then reported that the baseline no longer folds
the file constant. Keep -fPIE and declare only rea_external_value weak:
GCC and clang must reach a possibly absent symbol through a GOT entry,
which lands as R_X86_64_GLOB_DAT inside GNU_RELRO (checked with readelf
for GCC 12.2 and clang/lld).

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ghidra): protect only relocations Ghidra could not apply

Running the real-Ghidra lane locally (Ghidra 12.1.4, JDK 21, GCC 12)
showed that marking every non-writable block that holds a relocation
volatile also marks full-RELRO .got. External calls then decompile as
opaque pointer calls (`(*pcVar2)(param_1)` through
`PTR_rea_external_...` instead of `rea_external(param_1)`), which the
probe's GOT-stability check caught.

Ghidra applies GLOB_DAT and RELATIVE relocations, so those bytes already
hold the loader's value and folding them is correct. The #4177 case is
a copy relocation, which Ghidra imports as UNSUPPORTED with the file's
zero in place. Protect only destinations whose relocation status is not
APPLIED or APPLIED_OTHER.

The fixture now reads a provider `const` object that the linker copies
into .data.rel.ro, the #4177 shape. The probe requires that relocation
to be unapplied and requires applied GOT relocations to stay
non-volatile. Ghidra 12.1.4 already shows this x86-64 read by name
without protection, so the probe records whether the baseline folds and
compares before and after only when it does. The profile value becomes
volatile-unapplied-relocation-blocks-v1.

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Arslan Basharat <arslanbasharat414@gmail.com>
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 09:32:48 +01:00
0fa03afe7d fix(reconstruction): reject inconsistent artifact comparisons (#10484)
verify_reconstruction trusted a structural-artifact comparison's
aggregate status without checking its rows, so a caller-imported
comparison that said "unchanged" while retaining a changed row passed
the claim.

- The artifact comparison schema rejects an "unchanged" aggregate that
  retains change rows, non-zero change counts or integrity mismatches.
- Comparison Evidence validates integrity-mismatch citations like
  row citations.
- Verification replays compareArtifacts over the cited source
  inventories for definite outcomes, rejects manifest identities that
  disagree with the sources, and withholds a pass when the sources do
  not corroborate the claimed status.

Fixes #7304

Squashed from #7308 (including morluto's corroboration commits) and
rebased onto main.

Co-authored-by: ducanhnguyen223 <ducanhnguyen223@github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 08:48:17 +01:00
672e214963 fix(process)!: retain observed cleanup intervention (#10481)
* fix(process)!: retain observed cleanup intervention

Process capture derived `settlement.cleanup_outcome` from the settlement
state alone in three places, so a quiesced capture reported
`not_required` even after final owned-process cleanup SIGKILLed a live
token-owned descendant. `cleanupOwnedProcessGroup` already knew it had
signaled, but the capture lifecycle discarded that fact.

Carry the observed signal out of ownership cleanup (also on failure
paths), accumulate it across cleanup retries as true/false/unknown, and
derive settlement and the intervention residual unknown once in
`processCaptureCleanupFacts` for full, partial and harness results.

BREAKING CHANGE: a quiesced settlement may now report
`cleanup_outcome: "cleaned"`. Saved captures stay valid; readers that
accepted only `not_required` for quiesced settlements must accept
`cleaned`.

Fixes #8032

Squashed from #8043 and rebased onto main (adapted to the resource-scope
release API and the inspection-failure cleanup path).

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(process): keep a sent signal when cleanup throws an inspection failure

Final ownership verification can rethrow a typed inspection failure (for
example cancellation) after SIGKILL succeeded. The capture owner then
recorded the attempt as unknown and dropped the intervention fact.

cleanupOwnedProcessGroup now marks a failure it throws after signaling,
without changing the failure's identity, and the capture owner keeps
the signal for such failures.

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: wunianze666-netizen <wunianze666@gmail.com>
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 08:48:13 +01:00
1569186aa6 test: clear file-owned MCP schema validation after each file
The testing guide requires file-owned createMcpSchemaValidation instances
to be cleared in afterAll, so shared workers do not retain schema strings
and compiled validators after a file finishes.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 15:01:36 +08:00
6452c86cdc test: use shared lazy validation for stdio MCP clients
Stdio clients that list tools and then call one used the SDK's default
eager validator. Give each such file one shared lazy validation.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 15:01:36 +08:00
06e6401750 test: compile MCP output validators on first use
After tools/list, the MCP SDK client builds an output validator for every
listed tool before its first tools/call. Against REA's 255-tool catalog
that compiled for about 15 seconds of CPU (5 seconds for
inspect_analysis_view alone), whichever tool a test called. The shared
test validation now snapshots each schema when asked and compiles it when
it first validates.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 15:01:36 +08:00
morluto b3657c557f perf(tests): run plain CLI commands on the file's shared process (#10412)
* refactor(cli): run one-shot commands through one shared owner

scripts/rea.mjs and the test CLI session worker each implemented the
one-shot command sequence: argument normalization, output admission,
configuration errors, version documents, the Amp scope refusal, and
result delivery. The worker copy had drifted: it never rendered
formatted version reports or the Amp refusal.

Move the sequence into src/cliOneShot.ts. Both callers supply a host
for their streams, exit and resource ownership. The launcher keeps
closed-pipe status, worker shutdown and process exit; the session
worker captures output per command.

The cliOutput bundle entry is superseded and removed. The dispatcher
test now replaces only the command runtime, so output admission runs
production code. A new parity test compares the file session with a
fresh process.

* test(cli): bundle the one-shot entry in the runtime bundle fixture

* perf(tests): run plain CLI commands on the file's shared process

`cli.run` in `cliTest` files started a fresh compiled CLI for every
command, about 0.45s and 260 MB each before any work. The file-scoped
session already existed, but callers had to opt in through
`runFileCli` or `sharedCliRun`, so 1,300 call sites still spawned.

Make the fixture choose: `cli.run` uses the shared process when it
reports the command exactly as a fresh process would, and starts one
otherwise (stdin, explicit deadlines, MCP and MCP doctor routing,
NODE_OPTIONS, or enabled logging). `freshProcess: true` keeps
explicit process-level checks. The session worker now presents the
launcher's entrypoint, so registration identity matches a fresh
process.

`runFileCli` and `sharedCliRun` are superseded and removed; their
call sites use `cli.run`, and paired fresh/shared checks use
`freshProcess`.

* style(tests): format the managed member summary call site

* test(cli): give each shared-session command an empty stdin

The shared CLI process reads session frames from its own stdin. A
command that reads stdin without supplied input, such as
`compare-wasm-interfaces - --json`, read that pipe and hung, where a
fresh process receives EOF. The worker now presents an ended, empty
stdin to each command and restores its own afterwards.

The parity test compared the routed path with itself after cli.run
began choosing the session. It now compares a fresh process with the
session directly, including the `mcp add` refusal and a command
reading absent stdin.

* test(cli): keep default command logging and launcher checks on fresh processes

environment-serve and environment-build log at info when REA_LOG_LEVEL is
unset, through a logger that writes to stderr directly. The shared CLI
process captures only process.stderr writes, so routed runs lost those
lines. Declare the default-logging commands beside the command names,
have infoLogger read that set, and start a fresh process for them.

The log-level precedence checks compared the shared process with itself
after routing; their fresh side now passes freshProcess: true.
2026-11-14 14:28:08 +08:00
6938696256 test: keep child deadlines that rule out fallback exits
These fixtures' descendants exit on their own after 20 or 30 seconds. The
shorter explicit deadline is what proves cleanup stopped them first.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 13:21:13 +08:00
ee1450b5d4 test: bound every test process registry by the running test
Registries created directly with createTestProcessRegistry did not get the
processTest default, so a hung child there had no deadline once its explicit
value was removed; Vitest's test timeout does not settle the awaited run, so
shutdown was never reached. Derive the default in the registry from the
running test instead, using the real clock.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 13:21:13 +08:00
84990f9777 test: remove fixed child-process deadlines from tests
239 literal timeoutMs values on cli.run and processes.run only restated
a guess at host speed. On hosted runners with four workers, cliOutput's
3-second value failed while the command still produced correct output.
Deadlines that are the behavior under test, derived from an operation
budget, or that bound a regression's wait remain.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 13:21:13 +08:00
077bebe49b test: bound fixture child processes by the remaining test deadline
A processTest child run without its own timeoutMs now gets the test's
remaining deadline less a reporting reserve (at least 5 seconds, or a
tenth of the deadline). A hung child still fails with its captured output,
without a fixed per-call value that assumes a host's speed.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 13:21:13 +08:00
gensokyo-devandClaude Opus 5.5 5b158b2d1d fix(setup): stop treating Antigravity's .gemini storage as Gemini CLI
Antigravity keeps its MCP configuration and skills under
~/.gemini/config, and Gemini CLI was detected by the bare ~/.gemini
directory. Setting up Antigravity therefore made broad setup select
Gemini CLI and write a registration nobody asked for.

Gemini CLI's applicability now follows its own evidence: settings.json,
its trusted-folder records (honoring GEMINI_CLI_TRUSTED_FOLDERS_PATH) and
its .gemini/tmp session state, through the existing discoveryPaths
owner that detection, status and uninstall share. Selecting
--client gemini_cli explicitly is unchanged.

Fixes #7567

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 05:36:32 +01:00
gensokyo-devandClaude Opus 5.5 899f77d840 fix(setup): say when an authentic Hopper package mismatches its feed hash
Linux Hopper setup verifies the package signature, then compares the
launcher it contains with the release feed's main_exe_hash. When only
that comparison failed, as with the Fedora 6.6.1 RPM, setup reported the
generic integrity failure and advised retrying or updating REA, which
cannot change a stale vendor feed.

The publisher now reports that case as launcher_digest_mismatch. Setup
keeps the public integrity_mismatch code and the refusal, and its
recovery says the download is authentic, the feed describes another
launcher build, and the mismatch needs a corrected feed. The two digests
stay in the detail. Every other integrity failure keeps its recovery.

Fixes #8138

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 05:36:32 +01:00
gensokyo-devandClaude Opus 5.5 aa848cf1d2 fix(android): name the JADX JAR download and checksum in recovery
Doctor and configuration failures for a missing or rejected
REA_JADX_MCP_JAR asked for a "caller-supplied" jadx-headless-mcp 0.7.1
JAR without saying where it comes from. The upstream project has since
renamed its packages, so users concluded the JAR could not be obtained.

The shared JADX JAR remediation now names the audited release URL and
SHA-256 that REA already pins. The two capability-availability texts
point to rea doctor --provider jadx for them instead of repeating the
release data outside its owner.

Fixes #7768

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 05:36:32 +01:00
gensokyo-devandClaude Opus 5.5 82f3bfb6e8 fix(doctor): refuse an empty --target like other target inputs
`rea doctor --target ""` passed an empty string to target readiness,
which checked the working directory instead of reporting that no app
was named. Use the shared local path schema, so an empty target fails
validation before any readiness check, as every other target does.

Fixes #7561

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 05:36:32 +01:00
gensokyo-devandClaude Opus 5.5 5557d9b3eb fix(doctor): count installations by resolved launcher directory
On Windows `where rea` lists the npm shims rea, rea.cmd and rea.ps1 that
one global install writes side by side, and doctor counted each listed
path as an installation, so a single install reported "multiple".

The system host now pairs each PATH launcher with the installation it
resolves to: its directory after symlink resolution, compared
case-insensitively on Windows. Doctor counts distinct installations and
still lists every launcher path. Two POSIX launchers that resolve to one
package also count once; an unresolvable launcher keeps its own path.

Fixes #8122

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 05:36:32 +01:00
3a13b088e6 fix(mcp): explain skipped doctor identity probes
Report identity as unobserved when the selected tool families omit
binary_session, preserving the current health decision and exit status.
Cover scoped production sessions and retain validation of successful
and schema-invalid probe responses.

Fixes #7438

[gensokyo-dev: rebased onto main; report "identity not observed" for every
request-flow reply that was not received (skipped scope, transport failure or
rejected output schema), not only skipped scopes; corrected the replayed
inventory-mode expectation, whose identity is observed and matches.]

Fixes #7438

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 05:36:32 +01:00
gensokyo-devandClaude Opus 5.5 d412569293 fix(mcp-doctor): close only through the owned transport
The owned transport is the doctor's only cleanup owner, and Client.close
only delegates to it. Close it once and report its typed failure instead
of aggregating a duplicate SDK close and clearing that failure after a
retry. Pass the filtered caller environment as the launcher's base only,
and name the doctor operation in transport cleanup errors.

Cover a descendant in its own session, which only the token sweep can
reach, and a late cleanup denial that keeps the startup diagnostic and
reports the retained run. Bound the fixture descendant's lifetime so a
failing run cannot leak it. Document the doctor's process-family
ownership and output bound.

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 05:36:23 +01:00
2SinceJuly 1b019a73d8 fix(mcp-doctor): keep the diagnostic child target-free and its stderr visible
The owned launcher merges its host base back under the explicit overrides, so
filtering only the override object let an ambient HOPPER_TARGET_* variable
through and the diagnostic child could open the target the doctor promises to
skip. Pass the filtered environment as the host base too, and opt into the
token sweep so a detached descendant that outlives the launcher is still
reached by cleanup.

Also keep the stderr observer attached until bounded cleanup and draining have
settled, report the typed cleanup resources instead of a generic close_binary
message, and drop a first-attempt cleanup failure once the owned retry proves
release.
2026-11-14 05:36:23 +01:00
2SinceJuly d427fc05c3 fix(mcp-doctor): stop the owned process family on every exit path
runProductionMcpDoctor wrapped the SDK StdioClientTransport and only joined its
close calls. That transport clears its direct-child reference, ends stdin and
signals only that child, so a server whose launcher exited after forking an
inherited-stdio descendant left the descendant running while the doctor
reported nothing but the transport deadline.

Run the doctor on OwnedMcpStdioTransport, which spawns the child in a dedicated
POSIX process group or Windows job and verifies group and token-owned
descendant cleanup before close returns. Feed stderr through a sink on
acquisition so diagnostics survive an early child exit and still honor the
64 KiB retained prefix and full byte count.

Covered by a fixture that forks a descendant, exits, and is timed out: the
regression asserts the descendant is gone after the doctor returns.
2026-11-14 05:36:23 +01:00
gensokyo-devandClaude Opus 5.5 7db1b61790 fix(ghidra): qualify inferred no-return cuts by the excluded fall-through
A terminal call on one branch does not end the whole function: other
branches can still reach the following code, which stays in the body and
pseudocode. The function-level limitation now says Ghidra excluded that
call's fall-through and that code reachable only through it is missing
if the callee returns. It is listed before the call-specific facts so a
bounded composed summary that admits limitations in order keeps it.

The body-coverage tests now check the counted records and their reasons
instead of freezing the explanatory sentence.

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 05:34:39 +01:00
gensokyo-devandClaude Opus 5.5 ad2111fac6 fix(ghidra): say when an inferred no-return call cuts a function
Ghidra's Non-Returning Functions - Discovered analyzer can mark a
returning callee, such as an imported operator new thunk, as
non-returning. Ghidra then clears the fallthrough at each call site and
refits the callers, so a constructor decompiles to a few plausible
lines. REA reported the terminal call with the FunctionManager flag but
did not say the result omits the code after it or how to compare.

That analyzer bookmarks each entry it marks with the Analysis comment
"Non-Returning Function Found" (Ghidra 11.0 through 12.1.4 source); the
name-list Known analyzer writes "Non-Returning Function Identified".
When a terminal call's callee, or the function a thunk forwards to,
carries the Discovered bookmark, the call's limitation now says the
flag was inferred, and the function adds one address-free limitation
that its body and pseudocode end at that call and omit the following
code if the callee returns, with the REA_GHIDRA_DISABLED_ANALYZERS
comparison. A flag without the bookmark keeps an unknown origin.

The function-boundary observation contract moves to v5 so snapshots
from earlier bridges are not replayed without the qualification.

Refs #10365

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 05:34:39 +01:00
gensokyo-devandClaude Opus 5.5 9d082b144a fix(decompile): derive the body-coverage caveat from retained records
decompile_all set its top-level "procedure_info is unavailable" caveat
from a per-call flag that only a newly attempted function could raise.
A resume that skipped every verified file, or selected nothing, rewrote
the manifest and decompile-index.json with an empty limitations list
while every retained record still reported an unavailable body.

The export owner now answers from the records it retains: the summary
counts every non-external record whose body is unavailable, including
records an earlier run wrote, and repeats the reason when all share one.
The per-call flag is removed. A fresh export whose procedure_info works
but leaves some bodies unavailable now reports that caveat too; a later
attempt that records a known body clears it.

Fixes #8351

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 05:34:39 +01:00
gensokyo-devandClaude Opus 5.5 e8c838e8bd test(mcp): keep the surrogate path handler check within its deadline
The handler regression added in #10390 listed tools under the full input
profile and matched every advertised schema before calling each tool. It
took about 14 s locally and passed the 30 s deadline on loaded CI runners.
advertisedPatternsVMode.test.ts already proves that no input or output
profile advertises the surrogate escape, so this case now checks only the
runtime refusal through the default-profile handlers, as the
relative-path case does.

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 05:06:09 +01:00
gensokyo-devandClaude Opus 5.5 809f0d69db fix(mcp): report HTTP listener recovery from its parsed failure
rea mcp --http printed "Choose another --port or stop the process using
it" for every listener failure except a missing host allowlist, so a host
lookup failure, an unassigned address or a permission denial got port
advice. StreamableHttpListenError already classifies the native failure
and owns a recovery for each kind.

Startup now always prints that owned recovery behind one shared lead-in,
and the fixed message is removed. The native cause text stays out of
stderr as before.

Fixes #7346

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 04:57:03 +01:00
RRFZ 3986bf9f8e test(artifacts): fit the buffered member case to Windows stream buffers
The shared reader lifecycle case waits for a member's writable side to
finish before it reads anything. That only happens while the member fits
below the readable high-water mark. Node's default is 16 KiB on Windows and
64 KiB elsewhere, and the ESP flash fixture's first member is a 32 KiB
bootloader region, so both cases hung until the 30 s timeout on Windows.

Pick the first nonempty member that fits the default buffer and assert
that it fits the opened stream before waiting.
2026-11-14 04:50:06 +01:00
morluto bddbd80afb refactor(cli): run one-shot commands through one shared owner (#10405)
* refactor(cli): run one-shot commands through one shared owner

scripts/rea.mjs and the test CLI session worker each implemented the
one-shot command sequence: argument normalization, output admission,
configuration errors, version documents, the Amp scope refusal, and
result delivery. The worker copy had drifted: it never rendered
formatted version reports or the Amp refusal.

Move the sequence into src/cliOneShot.ts. Both callers supply a host
for their streams, exit and resource ownership. The launcher keeps
closed-pipe status, worker shutdown and process exit; the session
worker captures output per command.

The cliOutput bundle entry is superseded and removed. The dispatcher
test now replaces only the command runtime, so output admission runs
production code. A new parity test compares the file session with a
fresh process.

* test(cli): bundle the one-shot entry in the runtime bundle fixture
2026-11-14 11:48:19 +08:00
morluto f1e250ee76 test(dmg): stop hard-coding host speed in attachment recovery (#10431)
The test bounded each hdiutil and CLI step with its own 10-30 s process
deadline. On hosted macOS runners the test takes 32-45 s (about three
times a local run), and the busy-image CLI call, which verifies the whole
image before its attach is refused, exceeded 30 s and was killed in two
recent main runs. Remove those per-step deadlines; the project deadline
still bounds a hang.
2026-11-14 11:47:59 +08:00
morluto 71712f27f7 test(mcp): open stdio clients through the shared local-tools fixture (#10411)
* test(mcp): open stdio clients through the shared local-tools fixture

Twelve files built their own StdioClientTransport and Client for
`rea mcp`. Those clients never listed tools, so the SDK did not
validate structured results against advertised output schemas, and each
file repeated transport setup, stderr draining and close ordering.

Use connectLocalToolsMcp, which lists tools, validates replies,
silences logging, leaves deep providers unconfigured and owns cleanup.
Per-test clients register test-scoped cleanup; module-level clients
collect cleanup for afterAll.

* test(mcp): keep failed file-scoped MCP cleanup owned

The three module-level clients removed every cleanup callback before
running them, so a failed cleanup lost its only handle. Add
fileLocalToolsMcp, which connects before the file's tests and removes
each cleanup only after it succeeds, and use it in those files.
2026-11-14 11:47:55 +08:00
gensokyo-devandClaude Opus 5.5 ee339e5db5 fix(debuggers): keep stopped Rizin and GDB sessions explainable
After REA stopped a Rizin debug session (frame or output limit, deadline,
cancellation, protocol failure) or the debugger exited, later commands
returned a generic invalid_request and status gave no reason. GDB
collapsed the same states the same way.

Both managers now keep a published session addressable until
close_*_session releases it, record the first reason it stopped, report
it as closed_reason in session status, and refuse later commands with
capability_unavailable naming that reason and the close-then-restart
recovery. An ID the server never returned, or that close released, still
fails as invalid_request with its own recovery. Retained stopped
sessions keep their slot, which the capacity recovery now says. A GDB
command deadline now records its reason like cancellation already did.

Fixes #8024

Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 04:42:35 +01:00