* fix(apple): stop counting single-object NIB connectors as ownerless
The NIBArchive projection counts every object whose class name contains
"Connector" and has no resolvable NSSource as an ownerless connection
(#2484). AppKit also archives connector classes that annotate one object
and never encode NSSource: tooltips (NSIBHelpConnector), accessibility
attributes (NSNibAXAttributeConnector) and user-defined runtime
attributes (NSIBUserDefinedRuntimeAttributesConnector). Each of them
marked the document's connection coverage partial with
connection_unresolved, raised connection_count, set graph.truncated and
added the "declare no resolvable owner" limitation, although no
connection was dropped. The keyed-archive producer does not treat these
classes as connections.
Skip AppKit connectors that encode no NSSource key. A connector whose
NSSource is encoded but nil or malformed, and every UIKit runtime
connection, is still counted as ownerless.
Across 3924 NIBArchive files shipped with macOS system apps and Xcode,
688 reported this false partial coverage (for example Preview's
PaperKitWindowController.nib, whose only other connector is one outlet);
none do after the change.
* test(apple): keep unusable AppKit connector sources counted as ownerless
Only an absent NSSource marks a single-object annotation. Cover an AppKit outlet connector whose NSSource is nil or a non-reference value, which must still be reported as an unresolved, omitted connection.
* test(apple): split unusable connector source cases into their own block
Keep the connector describe block within the max-lines-per-function limit.
* fix(apple): keep unknown NIB connectors unresolved
* fix(apple): preserve unknown AppKit NIB connectors
* fix(apple): share annotation connector semantics
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* fix(website): keep header navigation on one row and grid it on phones
The header grid gave the navigation only the space left beside the brand
caption and social controls, so six links wrapped onto a second line in
Greek, German, Spanish, Portuguese, Vietnamese and English at common
desktop widths.
- Size the navigation to its content and let the brand column absorb the
remaining width; a container query hides the caption only when it no
longer fits, replacing the fixed 851-1250px breakpoint.
- Move the navigation to its own row at 1100px and below, before one
line can no longer hold it in any edition.
- On phones, lay the six links out as two rows of three content-sized
columns, with a tighter gap below 340px.
* fix(website): wrap mobile navigation at narrow widths
---------
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* fix(hermes): detach aliased server tables before editing
Copy aliased parent mappings before setting or removing a Hermes
registration. Resolve references against the original document and remove
anchors from the private copies to preserve existing sources and consumers,
including shadowed anchor names.
Cover YAML 1.1 and 1.2 setup, refresh, doctor readiness, repeated setup and
uninstall, along with comments, exact backups and invalid-alias refusal.
Fixes#3320
* test(setup): isolate Hermes flows from host Hopper detection
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* fix(website): align the language picker chevron
Replace the font-dependent down glyph with a fixed-size SVG mask that
inherits the button color and aligns within its existing flex layout.
Rotate the chevron when the native details menu is open.
Refs #3804
* fix(website): preserve the chevron in forced colors
The shared HAR/qlog JSON parser checked depth before pushing a container
using `frames.length > 64`, which admitted a 65th container. Over-deep
scalars were only rejected because literal values ran the same check, so
an empty array or object at level 65 passed while `[0]` at the same level
failed.
Admit a container only while its own level (root = 1) stays within the
budget and drop the literal-value check, so empty and scalar-bearing
containers share one boundary.
Fixes#3960
Co-authored-by: Anurag Singh <211784907+Anurag-5ingh@users.noreply.github.com>
* fix(providers): report provider-selection diagnostics when the selected provider refuses its profile
An explicitly selected provider that refuses the target during profile resolution now yields the target_unsupported selection error with its candidates and rejections, keeping the refusal's message and, when present, its specific recovery.
Fixes#3930
* fix(providers): keep refusal evidence and cleanup ownership on selection failures
A refusal with unconfirmed cleanup is returned unchanged; otherwise the selection failure carries the refusal's captured output, diagnostics, partial observation and cause. Registry tests cover the copied recovery and the cleanup guard.
* fix(captures): scope HAR HTTP interpretation to entries
* test(snapshot): preserve Evidence-scoped native workflow unknowns
* test(process): assert owned snapshot cancellation outcomes
* test(process): preserve snapshot race causes and owned cleanup
* test(cli): scope runtime cleanup provider injection
CLI boundary tests share a module graph. The cleanup fixture's module mock
cannot replace a provider factory already captured by the real command
registration, leaving its close count at zero after another test imports it.
Inject a typed provider factory into the registration instance, defaulting
to the existing production factory. Both commands still create their own
provider at execution time. Remove the redundant runtime context wrapper
and the global mock state. Keep the real child CLI parity case and verify
that neither command completion nor JSON output precedes awaited cleanup;
retain the close-count, cleanup error and completed Evidence assertions.
The unchanged test passes alone but fails with the command module preloaded.
Both modes pass after the correction. Seven related files pass 28 tests;
check and docs:check also pass on Node24.18.0/npm11.16.0 macOS arm64.
* test(artifacts): use valid ASAR bytes for signature control
* test(mcp): isolate snapshot filesystem fault injection
* test(process): distinguish unacquired resources in failure progress
* chore: restore main's copies of files the merge hook reformatted
The pre-commit oxlint --fix touched two files this PR does not change; keep them identical to main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(artifacts): reconcile ARM32 Mach-O slice names with subtypes
* test(snapshot): preserve Evidence-scoped native workflow unknowns
* test(process): assert owned snapshot cancellation outcomes
* test(process): preserve snapshot race causes and owned cleanup
* test(cli): scope runtime cleanup provider injection
CLI boundary tests share a module graph. The cleanup fixture's module mock
cannot replace a provider factory already captured by the real command
registration, leaving its close count at zero after another test imports it.
Inject a typed provider factory into the registration instance, defaulting
to the existing production factory. Both commands still create their own
provider at execution time. Remove the redundant runtime context wrapper
and the global mock state. Keep the real child CLI parity case and verify
that neither command completion nor JSON output precedes awaited cleanup;
retain the close-count, cleanup error and completed Evidence assertions.
The unchanged test passes alone but fails with the command module preloaded.
Both modes pass after the correction. Seven related files pass 28 tests;
check and docs:check also pass on Node24.18.0/npm11.16.0 macOS arm64.
* test(artifacts): use valid ASAR bytes for signature control
* test(mcp): isolate snapshot filesystem fault injection
* test(process): distinguish unacquired resources in failure progress
---------
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The semantic collectors admitted Node timers, fetch, WebSocket, JSON.parse
and global coercions from the identifier spelling alone, so a source that
replaced those globals (`setTimeout = () => null`) still produced resolved
built-in effects. Require the existing isUnmodifiedGlobalReference guard,
which the static analyzer already uses for fetch and WebSocket, before
admitting these effects.
The guard treats any visible possible write as a replacement without
evaluating order or reachability, so computed global writes and guarded
polyfills also remove the resolved effect. The workflow docs now say so.
Fixes#3367
* fix(setup): tell MSIX Claude Desktop's process view from any private roaming file
Some profiles keep writing the real %APPDATA%\Claude, so the MSIX package never gets a private LocalCache\Roaming\Claude folder and setup and uninstall refused Claude Desktop for good. Compare that folder first and, when it has no file, a bounded set of files from the package's other private roaming folders, which the package redirects in the same way. A package with no private roaming file still refuses, now with the manual edit as the remedy.
Fixes#4278
* fix(setup): address review findings on the MSIX roaming probe
Name the configuration setup would write, and both package files for uninstall, in the unknown-view advice. Bound the roaming walk by 1024 inspected entries and a queue no longer than the directories it can still list. Count ENOENT at the AppData path as a direct view only while the private file still exists.
* fix(setup): bound the roaming walk's directory listings by its entry budget
Read at most the names the walk's budget has left, with opendirSync in production, so a crowded package-private folder is never enumerated whole before the budget applies.
* fix(setup): recheck the private probe before accepting a mismatched target
A private file deleted between the two lookups no longer shadows its AppData path, so a redirected view can reach an ordinary copy there. Count a different target, like ENOENT, as a direct view only while the private file still exists.
* fix(setup): repeat the AppData lookup before trusting a rechecked probe
A private file deleted and recreated between the lookups passed the existence recheck on stale evidence. Look the AppData path up again after the recheck: a match with the private target is a redirected view, an agreeing non-match counts as direct, and a probe whose lookups disagree decides nothing.
* fix: bound Claude roaming folder probes
* fix(setup): charge enumerated Claude folder names
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* fix(hopper): shut down the Linux demo helper orderly so its X lock is removed
Teardown SIGKILLed the helper's process group, so run_launch's finally
never ran stop_process on Xvfb and each session orphaned its
/tmp/.X<n>-lock. Later startups count orphans as address_collision.
Ask the helper to stop first: closing its stdin trips OwnerLifetime,
and its finally stops Hopper and Xvfb with SIGTERM, which removes the
lock. Anything still alive after the grace period is reaped by the
token-verified group cleanup.
Fixes#4921
Co-authored-by: Saim Shafique <128062316+sx4im@users.noreply.github.com>
* fix(hopper): notice signal-exited demo helpers during orderly shutdown
A helper that already died from a signal keeps exitCode null, so the
orderly-shutdown path treated it as still running and sat out the full
grace period before the group cleanup. Check signalCode alongside
exitCode in both the cleanup guard and the exit wait, and add a
regression test for an already signal-exited helper.
Also hardens the ignored-shutdown test: it now proves the fixture
started (waits for its lock), asserts the helper was reaped by the
group SIGKILL, and bounds the wait by the configured 500 ms grace
instead of a vacuous 30 s ceiling.
The one-shot output-route check read the first word as an abbreviation,
so `p 1`, `s` and `d 1` matched python, source and define, and `t 1`
matched tty, and were refused before GDB started. GDB resolves its
built-in aliases first, so resolve the four that shadow those prefixes
(print, step, delete, thread) in one helper both checks share.
Fixes#5098
* fix(javascript): match unchanged module exports across application versions
An export binding's identity is keyed within its whole application root (container_sha256), so it changed whenever any sibling file changed, and no match tier paired export nodes: every export of an unchanged file was reported removed and added, and its module fell to unknown. The exact module-source-digest tier now also pairs an export binding by its own file's bytes (identity artifact_sha256) and exported name, as it already pairs a source module by source_sha256. Exports of edited files stay unmatched.
* style(javascript): format the version-matching export test
---------
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
* fix(hopper): bound bridge request frames like the Cutter bridge
The Hopper bridge read each request with an unbounded readline, so a peer
that never wrote a newline kept Hopper allocating for as long as it kept
writing. The Cutter bridge already refuses a frame above MAX_FRAME_BYTES;
the Hopper bridge had no request ceiling at all.
Read the request line with a 16 MiB bound and refuse a line that fills the
limit without a terminator, using the existing invalid_request diagnostic
before closing the connection. Such a line cannot be answered or
resynchronized, because its remainder would be read as the next request.
The response direction is unchanged, so large decompilation results still
flow, and every dispatchable method carries small bounded parameters.
The stalled-line case keeps its connection open instead of half-closing
it, so the test pins the exact ceiling: a bound one byte larger waits for
a byte the peer never sends and fails instead of passing quietly.
Validation: npm run check:fast (typecheck, lint) passed uncached, and the
new boundary test passed against the real bridge over socket pairs at the
frame limit and above it. Removing only the read bound makes the same test
fail, and widening the bound to one byte past the limit fails it too. The
Hopper boundary and bridge-packaging suites report the same 17 failing
files and 126 failing tests before and after this change on this Windows
host; those failures are POSIX-only probes (Unix socket directories,
os.statvfs) unrelated to this diff. Native macOS Hopper and real-client
traffic were not run.
* fix(bridges): exclude line delimiter from payload limit
* test(hopper): cover partial request at EOF
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* fix(wasm): refuse unretained summary delivery before provider work
inspect_wasm_artifact with detail summary ran the WASM provider before
checking whether the server retains Evidence. On a fixed server without
session retention a successful provider run was followed by
capability_unavailable, and a failing provider run replaced that
diagnostic with the provider error, hiding the guidance to select
detail complete.
Check retention right after input validation, as the EVM, pcap and
network-capture handlers do, so the refusal precedes any provider work
and keeps its typed diagnostic. Complete delivery is unchanged. The
fixed-server boundary test now injects a vi.fn provider and asserts it
stays idle for refused summaries while complete delivery still runs it.
Fixes#5100
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(wasm): validate caller paths before the summary retention preflight
Review follow-up for #5274. The retention preflight ran ahead of the
semantic input checks, so a relative path, glue path or candidate path
with detail summary on a fixed server returned capability_unavailable
instead of its field-specific invalid_request.
WasmArtifactService now exposes that parse-and-absolute-path step as
validate(); inspect() uses the same method, so the checks stay at the
owning boundary, and the MCP handler validates before deciding on
retention. The composition descriptor and server typing expose validate.
The fixed-server boundary test sends relative path and glue_paths with
detail summary and asserts invalid_request with both issue paths and no
provider call; it failed on 418a66d7d with capability_unavailable.
docs/mcp-contracts.md now claims the before-analysis refusal only for
inspect_artifact, inspect_wasm_artifact, inspect_macho and
decode_interface_builder, which enforce it; inspect_binary_layout and
analyze_javascript_application still analyze first and are left to a
separate change.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* refactor(wasm): inspect validated artifact input without reparsing it
Follow-up for #5274, matching the review outcome on #5288. The MCP
handler validated the request and then called inspect(), which ran the
same validator again, so every call parsed and cloned its input twice.
WasmArtifactService now separates the raw entry point from execution:
inspect() validates raw input once and delegates to the new
inspectValidated(), which runs the inspection for input that validate()
already admitted. The handler validates, runs the retention preflight,
then calls inspectValidated(); the composition descriptor and server
typing expose it. The CLI keeps the raw inspect() entry point; behavior
and diagnostics are unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(wasm): keep cancellation ahead of raw artifact input validation
Follow-up for #5274, matching the review outcome on #5288. Separating
validate() from inspectValidated() moved the raw inspect() entry point's
validation ahead of its abort check, so a direct call with an
already-aborted signal and malformed input returned AnalysisInputError
instead of the AnalysisCancelledError the pre-change service gave.
The raw entry point now checks the signal before validating, as before,
while inspectValidated() keeps its own guard for the MCP handler path.
A unit test pins the precedence with no provider call.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
rea mcp doctor rendered json with Formatter.format, which always indents, so
redirected --json, --full-output and validation errors stayed indented. The
dispatcher now passes the stdout TTY state and the doctor renders json and
jsonl through jsonOutputParts like the other commands.
Fixes#5435
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
* fix(hopper): keep an observed launcher exit over a later cancellation
`#connect` projected every interrupted wait as `#startupInterruption`
without consulting the launcher the owner already held, so a launcher that
exited 3 before the caller aborted was reported as `HopperCancelledError`.
An exit or signal the owned process already holds is now projected as the
physical startup failure first; a cancellation observed before any exit is
unchanged, and successful exits keep their previous behaviour.
Fixes#5429
* fix(hopper): arbitrate startup exits through process owner
---------
Co-authored-by: XiaoBinGan <XiaoBinGan@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
A safetensors length prefix such as 640 encodes as 80 02 00 ..., which the
provider read as a pickle PROTO marker and refused before the safetensors
parser ran. The parser that owns the length prefix now makes the call: a
pickle-like prefix is safetensors only when byte 8 is '{' and the declared
length is within the safetensors header ceiling. Real pickles stay refused
and are never deserialized.
Fixes#5895
* fix(artifacts): refuse thin Mach-O extraction before inventory
Extraction admitted both Mach-O formats and fell through to the universal
slice reader for a thin file, which yields a slices/<arch> entry that the
root-only inventory never recorded. The writer then reported an integrity
mismatch and recommended an integrity policy retry that cannot succeed.
Only mach-o-universal has an extraction reader, matching the inventory
reader selection. A thin Mach-O now receives the typed unsupported-target
refusal before any inventory or output work.
Refs #6098
* test(artifacts): cover the thin Mach-O extraction refusal
A thin arm64e Mach-O image is refused with the typed unsupported-target
error and no output directory is created.
Refs #6098
* fix(artifacts): keep embedded packs behind a thin Mach-O extractable
Refusing every thin Mach-O before inventory also refused PyInstaller
onefile executables and embedded Godot packs whose bootloader is a thin
Mach-O. Inventory classifies those roots as pyinstaller or godot-pck, and
createReader selects that reader before the Mach-O fallback, so they were
extractable on the parent revision.
A thin Mach-O now takes the same embedded-pack probe as ELF and PE: a
detected pack proceeds to inventory, and a plain image keeps the
unsupported-target refusal.
Refs #6098
* test(artifacts): cover a thin Mach-O PyInstaller onefile extraction
Refs #6098
* fix(native): keep truncated empty class-import scans partial
An explicitly present empty objc_class_import_addresses list with
objc_class_import_addresses_complete false reported binding coverage as
not_requested with a null reason, discarding the caller's declaration that
the candidate scan was truncated. Only an omitted list, or an empty list
declared complete, is not a request; a truncated empty scan now reports
partial coverage with the existing record-budget reason and zero examined
and decoded addresses.
Refs #6086
* test(native): cover empty truncated class-import candidate scans
Refs #6086
Model inspection turned every coded host error into a selected-target
refusal, so EMFILE at open or EIO at header read on a valid model came
back as target_unavailable with advice to check the path. Callers were
told to reselect a model that was fine.
Only codes that describe the selected path (missing, not a directory,
symlink, name too long, non-regular, changed) keep that refusal, as the
Go provider already does. Other host errors reach the existing
execution_failure fallback with the path and reason.
Fixes#6004
associateWasmGlue tested every glue string literal with
/\.wasm(?:[?#].*)?$/iu. Each .wasm? or .wasm# occurrence scanned to the
next line terminator and failed at $, so a multi-line literal with many
such occurrences took quadratic time on the event loop (about 2.6 s for
192 KB, within the 8 MiB glue limit). Because . cannot cross a line
terminator and $ only matches at the end, test the literal's last line
only; results are unchanged.
* fix(doctor): read each client's own activation key
Doctor treated both `disabled: true` and `enabled: false` as a disabled
registration for every client except Grok. The installed clients each
read only one of them: Codex and Kimi Code honor `enabled` and still
load a server marked `disabled`, while Devin honors `disabled` and
still loads a server marked `enabled: false`. Doctor reported those
loaded registrations as stale, so `rea doctor --client codex` failed
with config_drift and setup offered to "repair" a working entry.
Skip the key each of these clients ignores. Other clients keep the
existing rule.
* fix(tests): resolve the Devin config root per platform in activation cases
The client activation cases wrote Devin's mcp_config.json under the
XDG root on every platform, but devinDirectory resolves APPDATA (or
~/AppData/Roaming) on Windows. Follow the client's per-platform root
so the cases exercise the file doctor actually reads there.
Keep ordinary UTF-8 digests and secret redaction, separate malformed UTF-16 preimages, and record storage fingerprint provenance. Compare legacy hashed snapshots as unknown without treating encoding metadata as a target change. Cover producer boundaries and real owned-browser CLI/MCP workflows.
BREAKING CHANGE: legacy hashed browser storage remains readable but comparisons return unknown until both sides are recaptured.
Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com>
* refactor(process): share paired prlimit ceiling ownership
Route firmware, Wakaru, and pcap paired ceilings through
LinuxResourceLimits without changing their byte or CPU budgets.
Keep EVMole's soft-only limits as a dated architecture-guard exception.
Co-authored-by: morluto <morluto@users.noreply.github.com>
* test(pcap): admit capture limits without host prlimit
The ceiling test only checks the shared argument prefix, so it uses a
temporary executable instead of /usr/bin/prlimit.
Co-authored-by: morluto <morluto@users.noreply.github.com>
* style(pcap): format the capture ceiling test
oxfmt rejected the object literal wrapping in PcapNativeBound.test.ts.
Co-authored-by: morluto <morluto@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
* fix(windows): report missing native launch controls as unavailable capabilities
An admitted Windows native artifact may predate the optional protocol stdin
or Job Object memory limit controls. WindowsOwnedProcess correctly refused
such launches, but threw a plain Error, so providers projected the refusal
as execution_failure with generic `rea doctor` advice and left the package
remedy in nested diagnostics.
Introduce RuntimeControlUnavailableError as the shared owner of expected
host-control refusals. PrivateRuntimeRootUnavailableError and the new
WindowsNativeControlUnavailableError (which records the missing control)
extend it, and the six provider adapters that hand-built the same
AnalysisCapabilityUnavailableError now project any subtype through one
helper. The refusal still precedes every native launch.
Fixes#6050
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(errors): drop capability imports superseded by the shared helper
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Map RegularFileChangedError from reader admission to an integrity failure instead of the generic I/O and archive-open diagnostic. This preserves the changed-source condition and the existing stable-artifact recovery guidance.