Commit Graph
4781 Commits
Author SHA1 Message Date
gensokyo-devandgensokyo-dev 982ea7fa9f test: drop a frozen bridge sentence and a pcap deadline override
Keep the bridge cursor-mismatch code check without copying its message, and
let the pcap relative-tool diagnostic use the shared child deadline, which it
does not test.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
gensokyo-devandgensokyo-dev 4ad1eb5beb fix(snapshot): locate entry diagnostics by collection and index
Each snapshot entry is validated alone, so its schema issue path started at
the entry. Prefix it with entries or workflow_entries and the entry index so
the reason names the entry to repair, and match the header case's issue
count without freezing the whole sentence.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
gensokyo-devandgensokyo-dev 612b330b3b test(native): keep the export entry-allowance case in its own group
Merging the native export capacity tests made one describe block exceed
the lint function-length limit.

Refs #7628

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
gensokyo-devandgensokyo-dev c52bf54ce3 test(snapshot): type the CLI detail comparison
Refs #7442

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
gensokyo-devandgensokyo-dev e6bcda9d45 test(snapshot): compare snapshot diagnostics without MCP session facts
MCP errors now also carry open-session facts (selection and target at
failure), so the CLI/MCP comparison checks that MCP keeps the CLI's
snapshot details rather than requiring identical objects.

Refs #7442

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
3ae21f9ff8 fix(evidence): name the noncanonical bundle sections
Any canonical-form mismatch reported "Evidence bundle manifests are not
canonical", so a bundle whose records were out of order, or whose
unknowns differed, blamed unchanged manifests. The error now names the
top-level sections that differ from canonical form, for example
"Evidence bundle sections are not canonical: records".

Fixes #7330

Based on #7358 by zzrs-xxxr, which split records from manifests; this
reports every differing section instead, so unknowns or several
sections are attributed correctly.

Co-Authored-By: zzrs-xxxr <225415@qq.com>
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
morlutoandgensokyo-dev 4104f88b62 fix(native-ui): advertise capture source requirement
Refs #7570

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
morlutoandgensokyo-dev 4d544c8434 test(snapshot): assert full validation diagnostics
Refs #7442

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
wunianze666-netizenandgensokyo-dev 213fcc8ce1 fix(snapshot): retain schema diagnostics in file failures
Refs morluto/rea#7442

Fixes #7442
Supersedes #7875

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
morlutoandgensokyo-dev 7471b07a90 test(native): verify export allowance recovery over MCP
Refs #7628

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
ayobamiseunandgensokyo-dev 8e5e23c21e fix(native): name the exhausted native export allowance by its units
The native export parser throws one BinaryResourceFailure for both its
262144-entry inventory ceiling and its 16 MiB retained-text byte budget, and
NativeExportsProvider mapped every such refusal to the fixed-count recovery.
A PE whose export names exhaust the byte budget therefore reported
reported_limits.maximum 16777216 next to "is a fixed count".

Tag each parser allowance at the producing boundary (NativeExportLimitFailure
with limit "rows" or "retained_text") and let the provider select count,
byte, or structural-decoding recovery from that tag instead of from message
text. The refusal code, non-retryable flag, message, and reported maximum are
unchanged.

Fixes #7628
Supersedes #7754

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
Zhaowei Guandgensokyo-dev 4447c2ea25 test(native): pin IDA and Kuna in the dispatch fallback acceptance test
The darwin-only acceptance test passes the caller's environment to the CLI
and points only Hopper, Ghidra and Java at missing paths. On macOS the
list_names fallback has four candidates (ghidra, hopper, ida, kuna), so a
REA_IDA_MCP_CONFIG or REA_KUNA_COMMAND set in the developer's shell still
reaches it: with REA_KUNA_COMMAND set, kuna is no longer rejected as
not_configured. Point both at missing paths, as
tests/acceptance/nativeDispatchSnapshot.test.ts does, so the test always
exercises the unavailable fallback.

Signed-off-by: Zhaowei Gu <83746079+zw-g@users.noreply.github.com>

Refs #7651

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
3a1db29166 fix(native): retain the dispatch reader failure when the symbol fallback fails
When the byte reader for inspect_native_dispatch_metadata refuses a target
(for example a 32-bit Mach-O) and the list_names symbol fallback then fails
or returns an invalid inventory, only the fallback's error reached the
caller. Retain the reader failure under diagnostics.binary_metadata_failure.

Fixes #7651.

Carries forward e7884a704 (by @morluto, on @dreinst's #7701).

Signed-off-by: Zhaowei Gu <83746079+zw-g@users.noreply.github.com>

Supersedes #8036

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
fd97d0926c fix(hopper): type the Apple Events authorization denial
A hardened-runtime client without the Apple Events automation
entitlement got generic Automation advice, and the projection kept
retryable: true, so agents retried from the same unauthorized client.
The denial is now a typed launch rejection whose message and recovery
name the entitlement and the Automation setting, with retry turned off.

Fixes #9879

Squashed from #9916 and rebased onto main.

Co-Authored-By: morluto <76467478+morluto@users.noreply.github.com>
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
1052326311andgensokyo-dev eee8c5fa97 fix(session): compare entrypoint file identity
Fixes #8844
Supersedes #8849

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
morlutoandgensokyo-dev 90fd58d373 test(pcap): verify relative tool diagnostics through MCP
Refs #7569

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
Harshit Sharmaandgensokyo-dev 1327c386da fix(pcap): keep selected relative executable paths in error facts (#7569)
Fixes #7569
Supersedes #7577

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:06:23 +01:00
gensokyo-devandgensokyo-dev 9d3eb083dd fix(python): pin only the 3.14 and 3.15 magics checked locally
Keep magic 3627 (CPython 3.14.6) and 3666 (CPython 3.15.0b3), whose code
layout and inline-cache tables match those interpreters' opcode tables and
whose decoded code objects match marshal.loads across a stdlib sample. Leave
the 3.15.0a8 magic 3663 unpinned, since no 3.15.0a8 interpreter was available
to check it, and regenerate the 3.15 slice fixture with 3.15.0b3.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 13:37:55 +01:00
Abhishek Sharma 1d08253ff2 feat(python): decode CPython 3.14 and 3.15 pyc code objects
Decode marshal slice constants from magic 3608 onward and pin the 3.11 code
layout and inline-cache tables for the 3.14 and 3.15 magics.

Refs #8206
2026-11-14 13:37:55 +01:00
gensokyo-devandgensokyo-dev 5451d3cb2d test(cli): compare each registrar in a fresh lazy process
The in-process equivalence checks share every module that the complete
CLI already imported, so they cannot detect a registrar that relies on
another module's import-time setup. For each registrar, a fresh rea
process now runs one of its commands with an unknown option, taking the
lazy path. The test compares the rejection document and status with the
complete CLI's.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 20:25:11 +08:00
gensokyo-devandgensokyo-dev b990eb496d perf(cli): load only the registrar a one-shot command needs
Every rea process imported and registered all 52 command registrars
(about 1,900 modules) before running any command. A one-shot command
now loads only the registrar that defines its top-level command.

- src/cli/cliCommandRegistrations.ts holds every registrar in one table,
  grouped by tool family, with its module loader and its call. The
  complete createCli registers the table with static imports, and the
  types check that each entry matches its module.
- src/cli/cliShell.ts owns what does not depend on which commands are
  registered: identity, globals, argument policy, completions and the
  registration builtins. The full and lazy paths share it.
- scripts/record-cli-commands.mjs records, after tsc, which registrar
  defines each top-level command, from the same compiled table. The
  bundle keeps the record as an external dist module.
- cli/cliLoader.ts selects the recorded registrar when a known top-level
  command runs. It loads the complete CLI for help, --schema, --llms,
  completions, builtins, unknown names ("did you mean"), or a missing
  record.
- Process-owned CLI resources register their closers when their module
  loads (cli/cliResources.ts), so the shell no longer imports
  JavaScript-analysis and guidance owners just to close them.

Measured on one pinned core (median of 5 warm runs, base main vs this
change):
- guidance: 1.95 s / 253 MB → 0.42 s / 101 MB
- list-cutter-sessions: 1.97 s → 0.49 s
- inspect-go-binary: 1.95 s → 0.95 s
- setup --dry-run: 2.40 s → 1.88 s
- analyze: 2.39 s → 1.78 s
- --help: 1.99 s → 2.04 s (complete CLI, as before)

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 20:25:11 +08:00
gensokyo-devandgensokyo-dev 55c9e8e546 test: close the wait-deadline review gaps
- doctorCancellation measured from the signal, after the probe had
  started, so a probe stopped by its own deadline could still pass. The
  fake probe now records its start, and the check runs from there.
- rea/project-wait-deadline missed literal computed `timeout` keys and
  spread options. It now reports both, as well as computed keys it
  cannot read.
- The testing guide notes that a long-running test's expect.poll keeps
  its project budget, while waitFor follows the tag's deadline.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 19:28:38 +08:00
gensokyo-devandgensokyo-dev 4baf1b6193 test: keep the wait helper free of config-only context types
Stryker's TypeScript checker compiles the test support without
vitest.config.ts, so inject("testDeadlineScale") had type never there.
Outside a test the wait helper now uses the unscaled source deadline,
which no project's hook deadline is shorter than.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 19:28:38 +08:00
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