* test: assert absolute-path facts without frozen sentences
Sibling path tests now require the field path, invalid_format, and regex
expectation, and they record that each negative case ran. NUL rejection
follows the argument that contains the byte instead of searching for the
word NUL.
* test: retain actionable NUL path diagnostics
---------
Co-authored-by: Cursor Agent <[email protected]>
The help renderer appends "(default: X)" whenever the option field declares a
schema default, so descriptions that also spell the default out print it twice:
--block-size <value> Power-of-two block size from 256 to 65536 bytes
(default 4096) (default: 4096)
Remove the hand-written default from the eleven options that own a schema
default across inspect-byte-entropy, inspect-devicetree,
inspect-packet-capture, inspect-packet-fields, inspect-signal-decoders and
decode-signal-recording, matching the rule in cliDecimalField's contract that
the domain field owns bounds, defaults and help.
Options without a schema default keep their prose: --match_limit, --line_limit,
--limit and --packet_limit are nullable or optional, so the renderer emits no
annotation and the description is the only place the effective value appears.
Add a boundary test asserting each such option states its default exactly once,
so a restated default cannot come back unnoticed.
Expose the existing opt-in Ghidra checkpoint lifecycle in tool descriptions and summary capabilities. Distinguish confirmed initial analysis from restored Programs, preserving the checkpoint path and digest through shared CLI/MCP results.
Remove prose-driven checks and their scaffolding; retain canonical effects and lifecycle/source-fact coverage. Validated with 445 focused tests, 13 real Ghidra CLI/MCP scenarios and three isolated local Codex CLI tasks covering disabled setup, saving edits and fresh-agent restoration.
* fix(cli): advertise domain constraints for selector and path options
Android class, method, and mapping fields, ADB setting keys, and browser
endpoints used bare strings in --schema. They now reuse the domain fields,
so empty selectors, relative mapping paths, and malformed setting keys
fail in the advertised schema.
* fix(cli): keep selector limits when argument parsing rejects them
Empty browser and Electron selectors now fail in the CLI parser, so their
diagnostics are VALIDATION_ERROR with the one-character minimum. ADB
setting keys stay on the selector repair path, which restores maximum 256
and the identifier pattern.
* test(cli): assert empty-selector codes without frozen prose
Empty browser and Electron selectors still fail as VALIDATION_ERROR
too_small on the named field. The tests no longer copy Zod's sentence.
* test(cli): compare endpoint diagnostics with the domain schema
Browser and Inspector tests take custom issue text from the schema that
owns it, and empty capture comparison must name both missing inspection
fields.
* fix(cli): remove duplicate merged schema import
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(mcp): advertise absolute paths for tools that reject relative inputs
The domain schemas accepted relative paths, then handlers rejected them
with isAbsolute. Those inputs now use absoluteLocalPathStringSchema, and
the duplicate handler checks are gone.
* fix(javascript): own recovery path admission in the schema
Relative recovery paths now fail in absoluteLocalPathStringSchema, including
the additional_paths index. Drop the duplicate handler check, and make the
CLI, MCP, and tools/list tests assert that schema fact.
* test: assert absolute-path facts instead of error prose
Recovery and web-script tests keep the field, format code, and regex
expectation. They no longer freeze the schema's explanatory sentence.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(javascript): keep timer cancellation within Node's handle family
Node keeps timeouts and immediates in disjoint handle families. clearTimeout
and clearInterval are aliases that act only on a Timeout from setTimeout or
setInterval, while clearImmediate acts only on an Immediate from setImmediate.
Crossing the families returns without doing anything, so the scheduled
callback still runs.
REA linked a cancellation whenever the cleared binding traced back to any timer
schedule, so clearImmediate(setTimeout(work, 10)) reported a complete
cancellation and the graph emitted a resolved cancels-timer relation. It also
flagged no uncertainty for it.
Require the cancel and the schedule to share a handle family before linking
them. A cross-family clear now keeps linkedTimerId null and resolution
unresolved, and the graph reports the cancellation as a candidate with an
ambiguous-target unknown instead of a resolved fact.
Verified against Node v24.22.1 over all nine schedule/cancel pairs: setTimeout
and setInterval with clearTimeout or clearInterval cancel, and setImmediate
with clearImmediate cancels; the other five combinations leave the callback
running. Node's own lib/timers.js shows why: clearTimeout requires
timer?._onTimeout, clearImmediate requires immediate?._onImmediate and returns
early otherwise, and clearInterval delegates to clearTimeout.
* chore: retrigger title and readiness checks after the title was corrected
* fix(javascript): say cross-family timer clearing is runtime-dependent
Review feedback from @morluto: the previous wording claimed clearing across the
timer handle families "returns without doing anything, so the timer keeps
running". That does not hold on every supported Node version. Measured on Node
24.22.1, all three cross-family pairs fire:
| scheduled with | cancelled with | Node 24.22.1 |
| --- | --- | --- |
| `setTimeout` | `clearImmediate` | fires |
| `setImmediate` | `clearTimeout` | fires |
| `setImmediate` | `clearInterval` | fires |
`clearImmediate` cancelling a `Timeout` on Node 22 is the case the old claim
missed. Within a family the clear always cancels, which is why the analysis
links only same-family pairs; across families the outcome depends on the
runtime, which is exactly why the analysis reports `unresolved` rather than
`complete` in either direction.
The behavior and the tests are unchanged. The `timerHandleFamily` doc comment,
the linked-origin comment, and the test comment now state the runtime
dependence and why it justifies an unresolved result.
Add opt-in REA_GHIDRA_CHECKPOINT_DIR for owned Ghidra Program persistence through the shared CLI/MCP provider. Save completed initial analysis and metadata changes on normal close; verify and restore the last successful packed Program without auto-analysis.
Preserve generation identity, exclusive ownership, typed cancellation and cleanup retry. Refresh checkpoint-dependent profiles after retirement, including failed-switch restoration, while retaining caller selection provenance. Keep source executables and caller projects unchanged.
Validated with 720 focused tests (3 platform skips), 13 real Ghidra CLI/MCP scenarios, static/docs checks and CI. Review findings addressed. No version bump or release publication.
Refs #8165.
* feat(desktop): read XML with a plain DOCTYPE, and check an element's whole text
- Both readers accept a DOCTYPE with only a name and an optional PUBLIC
or SYSTEM identifier, record it as the view's doctype, and never fetch
the DTD or expand entities beyond XML's five; an internal subset, an
external entity and a parameter entity stay refused alike.
- text_equals and text_contains compare the concatenated text of the
element (or text node or attribute) a pointer reaches, as XPath
string() does, with optional normalize_whitespace; both evaluators
agree, and a text check on a truncated element is undecided.
- Whitespace-only text between elements is dropped only when the element
holds no other text, so text split into runs keeps its spaces.
- The draft suggests text_equals for an element whose text changed.
So an AbiWord .abw reads as XML, and its paragraph's text is checked
whole however AbiWord splits it into <c> runs, as Writer's spans are.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): draft whole-text checks on the paragraph, read only text as text, and skip asctime stamps
- A changed value is read at both ends, so text that became a run still
marks its paragraph, and the drafted text_equals moves up out of mixed
content to the nearest ancestor named by an attribute, such as
AbiWord's p by its xid.
- Text checks accept only a text node or an attribute value; element
names and doctype fields fail, in both evaluators.
- isTimestamp also recognises asctime dates (Www Mmm dd hh:mm:ss yyyy).
- XML files are drafted with the keyed diff the archive entries use, so
no suggestion reaches an element by position; the shared logic moves to
appTaskDraftNamed.ts.
- Text-check tests assert facts, not whole sentences.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep checks on a changed attribute inside a paragraph whose text check replaces its runs'
A drafted whole-text check on a paragraph hid every change inside it, so a
link whose label and href both changed drafted only the paragraph's text and
the new destination went unchecked. The text check now covers text and runs
added or removed whole; a changed name or attribute of an element both
versions hold keeps its own check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read an attribute called name or attributes as an attribute when the draft keeps its check
The owner of `.../attributes/name` was taken as `.../attributes`, so a
changed `name` attribute inside a paragraph with a text check was dropped
again. The owner is now read by segment, attributes first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): draft text checks from text in runs added whole, and name a text element by its own id before its container
Text typed into a run the diff lists whole was never read, so the draft kept
only a presence check on the run, which wrong text also passed. Such text now
marks the nearest element both ends hold, or, when none can take the check,
the added element's own text. The climb also names an element by an
identifier of its own (id, xml:id, xid) before a larger container, so a
headline under a long layer keeps a whole-text check that survives re-split
runs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): expect the whole-text check where #8988's pairing test meets text checks
On this branch a changed element's text is drafted as `text_equals` on the
element, so the identity pairing test now expects that check, under the
same `[@role=body]` key.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep checks for elements without text beside a text edit, and normalize DOCTYPE system id line endings
A review found two gaps.
- A paragraph's whole-text check covered every element added or removed
inside it, including ones holding no text: an image frame, a line break.
A solver who typed the text but never inserted the image passed. Such an
element now keeps its own presence or absence check.
- The Python reader kept a DOCTYPE system literal's CRLF where TypeScript
normalizes it to LF. Both now report LF.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): import textSyntaxFormat once in the state format tests
main's mixed-case JSON suffix fix and this branch both added the import,
and the merge kept both.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Fixes#8838.
Admit all symbolic query entry points through the existing resolver budget, preserve cache-hit depth and restore entered frames in finally. Retain independent facts with partial source coverage instead of losing entire scripts to recursive spreads.
Validated 273 focused tests, check:fast/docs:check, real CLI/stdio-MCP projections and complete parent reads, independent public-source oracle, and unchanged full results for the official Electron control and Bitwarden selected preload.
Full technical CI 42308068679 passes, including all eight shards and required aggregates. Greptile reviews this head at 5/5; no completed Codex review of this head is available. The Author readiness-only exception is explicitly authorized; no human-review attestation is added.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Fixes#7540.
Retain every raw dryRun literal at the shared inert token binder before Incur boolean coercion; preserve parser-owned values, original execution, defaults and variadics. Return an actionable typed PARSE_ERROR before setup or environment publication.
Validated 40 focused tests, independent nine-call compiled-CLI before/after configuration controls, check:fast and docs:check.
Full technical CI 42302162266 passes, including all eight shards and required aggregates. Greptile reviews this head at 5/5; no completed Codex review of this head is available. The Author readiness-only exception is explicitly authorized; no human-review attestation is added.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Fixes#7375. Fixes#7608.
Share argument consumption and agent facts between admission and scope checks; reject invalid registrations before settings, backups or runner startup. Preserve missing-value priority, explicit redaction, discovery and valid registration controls.
Validated 33 focused tests (one platform skip), current-main RED and independent compiled-CLI before/after workflows; check:fast passes.
Full technical CI 42297438912 passes, including all eight shards and required aggregates. Greptile reviews this head at 5/5; no completed Codex review is available and the bot reports exhausted quota. The original author readiness declarations remain verbatim; no new human-review attestation is added.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Compare the preceding valid TypeDef coordinate independently for FieldList and MethodList, retaining readable members while leaving affected ownership unknown. Keep the scalar producer and shared owner lookup authoritative across member inspection, metadata inventory and native boundary workflows.
Validated with 577 managed/workflow tests, seven malformed/working ordering vectors, and independent dnfile/pefile raw metadata from a pinned public SystemEvents assembly. Four deliberately changed metadata bytes reproduce both decreases across an invalid row. Six CLI calls and ten stdio MCP tool calls before/after preserve all 458 fields and 195 methods and raw signatures/CIL hashes, keep ordinary results identical across three operations, and retain unresolved filter candidates rather than false absence. Alternative #8868 passes the expanded 206 inspector/ownership controls; converge on this generic implementation.
The original three unchecked human declarations are preserved. The explicitly authorized readiness-only exception applies after technical CI and review; no human-review attestation is added.
Fixes#8861
Full technical CI 42293217445 passes, including all eight shards and required aggregates. Greptile reviews this head at 5/5; Codex cannot complete because the repository review quota is exhausted. The Author readiness-only exception is explicitly authorized; no human-review attestation is added.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Classify a native method-93 decoder window refusal once at the ZIP owner as limit/memory, retaining the fixed 128 MiB cap, native cause and existing corruption/CRC/size/cancellation handling. Derive recovery labels from the cap and share one independent frame fixture.
Validated with 43 reader/translationtests,three actual CLI/stdio MCP cases, a pinned public GLFW payload rewrapped in independently verified ZIP controls, six equivalent CLI calls before/after, static checks and documentation checks. All input bytes remain unchanged, normal extraction matches exactly, and failed outputs are absent. ELF/GPU component refusal-policy gaps remain separate unverified full-format workflows.
The original three unchecked human declarations are preserved. The explicitly authorized readiness-only exception applies after technical CI and review; no human-review attestation is added.
Fixes#8623
Full technical CI 42279406899 passes, including all eight shards and required aggregates. Greptile reviews this head at 5/5; Codex cannot complete because the repository review quota is exhausted. The Author readiness-only exception is explicitly authorized; no human-review attestation is added.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Keep independent Objective-C class, metaclass, category and protocol records when an adopted-protocol list is unreadable. The shared list reader retains readable prefixes through tagged Result failures; consumers preserve partial/unknown semantics with unchanged limits. Advance the source-owned reader commitment to v3 and reject stale v2 snapshot bindings.
Validated with 232 focused tests, current-main and original-proposal RED checks, named provider-result/schema/identity controls, and a verified public GLFW Mach-O paired with independent LLVM/Python byte evidence. Ordinary binary results remain identical; a deliberately mutated count preserves ten rows instead of eight. The final head passes the real macOS CI lane, with twelve CLI tests including both new protocol-count cases over CLI and stdio MCP. Snapshot binding and same-version replay checks pass.
The original unchecked human declarations are preserved. The explicit maintainer readiness exception applies after technical CI and automated review, without adding a human-review attestation.
Fixes#8945
Full technical CI 42263548929 passes, including all eight shards and required aggregates. The Author readiness-only exception is explicitly authorized; no human-review attestation is added.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
* fix(desktop): read the PPA key add-apt-repository writes into Signed-By
On Ubuntu 24.04, add-apt-repository puts the PPA's armored key inside the
deb822 Signed-By field, and REA refused such repositories, so PPAs added the
standard way could not be exported. REA now reads that key by deb822 rules,
verifies the release with it through the same private gpgv keyring as key
files, as the entry's only candidate (as apt does), retains it as armored and
exports it as apt-keys/<name>.asc. A held key that does not verify is refused
with a reason that names no URL.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): check the held-key refusal's facts, not its sentence
The PPA refusal tests now assert that the reason names the sources file
and the release file and no URL, and that export carries that reason,
instead of comparing whole sentences.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* test(process): align symlink fixture with filesystem checkpoints
* test(frida): assert the CLI spawn Evidence contract
* test(mcp): use an unsupported analysis-view parent
* test(browser): separate source-location line bounds from parity
* fix(dotnet): reject invalid UTF-8 metadata version strings
* test(dotnet): preserve valid multibyte metadata version text
* fix(desktop): recognise JSON scalar files with mixed-case suffixes
* test(setup): use the current registration command in CodeBuddy fixture
* test(cli): split doctor help and timing boundary groups
* style(test): format split MCP doctor boundary groups
Resolve exported SQLite metadata through built-in PRAGMAs on the private connection. Preserve application tables/views, source encoding, nullable rows and the existing resource limits.
Validated with 93 focused tests, current-main RED controls, and actual CLI exports of the public Chinook dataset with native/shipped-reader parity and unchanged source hashes. No live GUI, container or installed agentenv qualification is claimed.
Full technical CI 42247722923 (all eight shards and final aggregate) passed; Greptile review scored 5/5 on the merged head. The original human readiness declarations remain unfilled; the explicitly authorized maintainer readiness exception applies without adding a human-review attestation.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Keep full schemas and canonical analysis unchanged while loading the cross-domain view through existing named discovery. Qualify real recent-tool workflows with token ledgers, latency, CPU, family RSS and retained-follow-up controls.
BREAKING CHANGE: The opt-in hot-cold catalog no longer preloads inspect_analysis_view. Discover it before calling it, batch with its producer when needed, or use the unchanged default full profile.
Preserve per-side capacity diagnostics in shared managed member comparisons and apply repeatable CLI type selection to both source inspections. Keep partial and historical unknown semantics intact, with one typed diagnostic projection and side schema.
Validated with 159 focused tests, final-main CLI/MCP boundary checks, schema validation and official public WPF 8.0.32/8.0.33 assemblies. Full source Evidence remains available through the retained-record MCP workflow. No target execution or budget increase.
Fixes#9021
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
* fix(javascript): apply package type scope to application sources
Application analysis parsed .js sources without their package scope, so an unbound require in a type-module package became a resolved CommonJS dependency. The parent now observes each source's nearest same-container package.json, stopping at node_modules, and the worker parses with that package type as reference imports already do.
Fixes#7323
* fix(javascript): keep package type unknown behind unavailable manifests
The package-type lookup indexed only readable files, so a nearer
package.json whose bytes were unavailable was skipped and an ancestor's
"type" applied to sources it does not own. Index unavailable manifests
by container and directory too, and stop the walk with "unknown" when
one is the nearest owner, as the Next app-context lookup already does.
* fix(javascript): keep Electron preloads outside module package scope
Electron's preload loader ignores a package's "type": "module" and gives
a .js preload `require`; ESM preloads need the .mjs suffix. Applying the
package type to every .js source marked such a preload as an ES module,
so its `require("electron")` edges and IPC pairing disappeared.
A source that requires an Electron module now keeps the context REA
inferred before package scope was applied. Node-loaded sources in a
module scope still drop unbound `require`.
* fix(javascript): admit Electron script loaders only on renderer evidence
The previous commit dropped a module package type whenever a source
required an Electron module. A Node-loaded ES module that requires
Electron, or one whose Electron call goes through a local `require`,
then regained the false require edges the package scope removes.
The parser applies the package type unconditionally again. The worker
re-reads a package-module script that requires an Electron module once
outside the package scope. The source keeps that context only when the
static pass there directly observes a contextBridge exposure or a
renderer IPC operation, which only Electron's renderer-side loaders
provide.
* fix(javascript): check Electron renderer evidence without a second static pass
b54845a6b ran the full static pass on an unscoped parse of every
package-module script that requires Electron, then discarded it when no
renderer operation turned up, so such main-process sources paid for two
full passes.
A candidate must now also name contextBridge or ipcRenderer, which any
renderer evidence needs. The evidence check runs only the static pass's
binding and Electron identity preamble with its exposure and IPC
descriptors. The full static pass runs once, on the chosen parse.
* fix(javascript): confirm Electron renderer evidence with the static pass
The focused evidence check from 4a86bca9b also counted renderer calls in
bundler factory defaults that the bundler convention never runs, which
the static pass skips. Such a default switched a type-module file to
script context, so its unrelated unbound requires became imports again.
The focused check now only screens. It follows the static pass's own
traversal, so a negative result still rules evidence out. A positive
result is confirmed by the static pass's direct findings on the unscoped
parse, and those findings become the file's facts when they hold.
* fix(javascript): confirm renderer evidence before wrapper substitution
f71236400 confirmed a screened source from the static pass's final
contextBridge and IPC findings, without wrapper substitutions. The
wrapper pass replaces a directly observed exposure inside a helper with
a substituted finding, so such a preload lost its imports again.
Before its wrapper pass, the static pass now records whether its source
traversal directly observed a contextBridge exposure or a renderer IPC
operation, and observedElectronRendererCall exposes that. The traversal
still skips bundler factory defaults that never run, and the reported
findings are unchanged.
* fix(javascript): resolve declared preload loaders before source analysis
* fix(javascript): leave preload discovery failures to source analysis
A source that failed during preload discovery threw out of the whole
application analysis, so one oversized source or a per-source timeout in a
module package returned no Evidence. Skip a source that exceeds its worker
limits and end discovery on any other failure except cancellation; source
analysis then meets the same source, records its failure and keeps partial
Evidence as it does without discovery.
* fix(javascript): report preload discovery gaps in Evidence limitations
When preload discovery skipped a source or stopped early, source analysis
could still read that source, so the result looked complete while a .js
preload declared there kept its package scope. Return each discovery gap
with the discovered paths and add it to the analysis limitations, which
both complete and partial Evidence carry.
* feat(process): add a non-executing dry run to capture_process_scenario
Preparation prompts asked an agent to prepare a scenario without running
it but could only suggest the tool that launches the target. Add
dry_run: true so the CLI and MCP resolve the executable, working
directory, observation paths and admission checks, then return planned
Evidence under the rea.process-scenario-plan predicate. Planned Evidence
is not capture Evidence and comparison tools refuse it as a capture.
The preparation prompt now suggests the dry-run call, and the scenario
identity commitments exclude the operation-only field.
Fixes#8398
* fix(process): redact planned sensitive input and share admission
Address review findings on the dry-run contract:
- Redact sensitive input events in planned Evidence and preserve only
their byte length.
- Recheck cancellation after the last awaited admission step.
- Run the sensitive-matcher and filesystem-root admission checks that a
capture performs before launch.
- Assert the prompt advertises the dry_run field without freezing
explanatory wording.
* test(process): hold the final admission step for cancellation
Add a preparation host seam so the regression can cancel while the last
filesystem admission call is pending, then assert the returned
AnalysisCancelledError retains the caller's reason.
* refactor(process): remove the unused plan alias
Keep the plan schema as the single exported contract and satisfy the
unused-export check.
* fix(process): report admission-time aborts as planning cancellation
`prepareProcessScenarioPlan` returned the admission error verbatim, so an
abort that landed while `admitFilesystemObservationRoots` awaited its
`lstat` surfaced as a cancelled `ProcessCaptureError` with
`operation: process_capture` even though no process ran. Convert that
result through the same `cancelled()` projection the neighbouring checks
use, and pin it with a test.
* fix(contracts): keep the process result invariants on the dry-run union
The output schema for `capture_process_scenario` became a union of the
capture and plan shapes, and the union no longer carried the description
that advertised the cross-field invariants to MCP clients — the boundary
test reads `normalized_result.description`. Attach a description to the
union that names both variants and keeps the capture invariants verbatim.
* test(process): exercise the post-admission cancellation check
The previous regression aborted immediately after starting preparation and
released the held admission afterwards. Preparation awaits runtime path
resolution first, so that abort was always observed by the pre-admission
check; deleting the post-admission "if (aborted()) return cancelled();"
still left the test green.
Signal from the admitRoots seam when admission is actually entered, abort
only after that point, then let admission resolve ok. Preparation now has
to reach the check between a successful admission and the ok result, and
the assertion pins both the AnalysisCancelledError tag and the caller's
reason as its cause.
Verified red-green: removing only the post-admission check fails this test
(expected true to be false at prepared.ok) while the other two pass, and
restoring it passes all three.
* test(process): gate the admission seam on acknowledgement
Race the admission acknowledgement against planning's own settlement and a
bounded deadline, and release and join planning in `finally`, so a planning
result that arrives before admission fails the test instead of stalling it
until the suite timeout.
* fix(contracts): hold plans to the scenario refinements and redact inputs
`processScenarioPlanSchema` described the resolved dry-run response but
dropped every cross-field check the input contract applies, so a plan
could advertise out-of-order events, an event past `timeout_ms`, or a
terminal beyond the renderer cell budget while claiming it had been
validated. Extract those checks into `scenarioRefinements` and refine the
plan's scenario with them.
The plan's input event also carried `sensitive` and an optional
`input_bytes`, which accepted a sensitive event that still shipped its
data. Model the public and sensitive forms as separate exact variants:
public input forbids `input_bytes`, sensitive input requires an empty
`data` plus its byte length.
---------
Co-authored-by: 2SinceJuly <[email protected]>
* feat(rizin): select the decoder and map address for one-shot commands
execute_rizin_command and rea execute-rizin-command accept optional
asm_arch, asm_bits, asm_cpu and base_address (CLI --asm-arch,
--asm-bits, --asm-cpu and --base-address). REA passes them to Rizin
before the artifact path as -a, -b, -e asm.cpu= and -m. Rizin 0.9.1
refuses -e asm.arch= and -e asm.bits= at startup, while -a and -b work
in 0.7.4 and 0.9.1. Rizin applies -e settings after -a and -b.
base_address is the address of file offset 0, as in REA's raw-binary
inputs.
REA checks only the spelling, because Rizin owns the arch and CPU
vocabulary and its builds differ. The Homebrew build of Rizin 0.9.1
has no xtensa or riscv plugin; the official static Linux build of
0.7.4 has both. Evidence records each input and the complete argument
list. When any input is set, Evidence also states that REA does not
confirm the decoder or map that Rizin used: Rizin uses another value
for one it cannot apply and exits 0. Rizin 0.9.1 keeps its default and
reports the value on stderr, and Rizin 0.7.4 turned -b 8 for arm into
16 without a message. Calls without these inputs keep their argument
list and Evidence.
The real-provider gate tests/boundary/providers/RizinDecoderInputs.test.ts
checks the effect of each flag through MCP with Rizin's own e asm.*,
omlj and byte reads. It skips when no Rizin is on PATH. It passes with
Rizin 0.9.1 (Homebrew, macOS arm64) and 0.7.4 (official static build,
Linux x86_64).
* docs(rizin): map multi-segment ESP32 images with om
An ESP32-family app or bootloader image holds several segments, each
loaded at its own address, so one base_address cannot map it. The
recipe removes the default map with om-* and adds one om map per
segment, using the segment offsets from the image format or from
esptool image-info. It does not depend on an ESP image reader in REA.
The offsets were checked against esptool 4.12.0 image_info on a
synthetic two-segment ESP32 image. Rizin 0.9.1 and 0.7.4 mapped its
segments at their load addresses. Rizin 0.7.4, whose static Linux
build has the xtensa plugin, decoded the IRAM segment as nop.n, nop.n,
ret.n and nop.n.
* fix(rizin): read the file at base_address when a loader would claim it
On Rizin 0.9.1, base_address could read 0xff at the mapped address with
exit 0. -m maps the file, but Rizin still runs its format loaders, and
a loose one can claim a raw image. Rizin 0.9.1's AVR loader matches
when bytes 1 and 3 look like rjmp and turns io.va off, so reads use
file offsets. The BIOS loader, in 0.7.4 and 0.9.1, matches a 64 KiB
file with a jmp 16 bytes from its end and maps it at another address.
An acceptance run on a raw ESP32-S3 segment hit the first case.
With base_address, REA now passes -n together with -m. Rizin then skips
the loaders and maps the whole file at the address: rizin.c calls
rz_io_map_new at the -m address and leaves io.va at its default. The
input therefore means a raw image, and Rizin loads no sections, symbols
or entry point, even from an ELF file. Evidence states this in its own
limitation, and the decoder limitation no longer covers the map.
The real-provider gate adds an AVR-like and a BIOS-like image and reads
below the base, at it, at the end of the image and past it. Without -n
it fails on Rizin 0.9.1. It passes with Rizin 0.9.1 (Homebrew, macOS
arm64) and 0.7.4 (official static build, Linux x86_64). The ESP om
recipe now starts with e io.va=true, because reads use the maps only
while io.va is on.
* fix(rizin): advertise name patterns that compile in v mode
The asm_arch and asm_cpu pattern ended its character class with an
unescaped '-', which JavaScript's v (unicodeSets) mode rejects, so
clients compiling advertised patterns in that mode could not use
execute_rizin_command's schema. Escaping it keeps the same names valid
in every mode, as main's own patterns write it.
* fix(rizin): state what the decoder and map inputs leave unconfirmed
The base_address limitation reported a map REA never observes and left
out that -n keeps Rizin's default arch, bits and byte order. The asm_*
limitation implied Rizin replaces a bad CPU, but e asm.cpu echoes any
value. Compact schemas drop property descriptions, so the tool
description now carries the raw-loading warning. The real-provider
gate checks that asm_cpu changes a decode, and a base_address-only call.
* docs(rizin): record the gate's full run on Rizin 0.7.4
* test(rizin): follow the test guide in the decoder input tests
Focused tests resolve execute_rizin_command from its contract family,
the harness owns the temporary directory before setup can fail, and
guidance is compared with its owner, not with sentences from it.
* feat(domain): guide agents from neighboring-tool argument mistakes (#2146)
* feat(mcp): guide agents from argument-name mistakes in MCP tools (#2146)
* fix(domain): suppress argument replacement when expected field is present (#2146)
Suppress argument replacement guidance when the expected field is already present in the call input, preventing invalid suggestions such as advising procedure when procedure was already provided. Defrost explanatory prose assertions in tests and bind test cases to production toolContract schemas.
* test(domain): use named contract families and decouple guidance prose in tests (#2146)
Import named contract families (OFFICIAL_TOOL_CONTRACTS, APPLICATION_TOOL_CONTRACTS, ENHANCED_TOOL_CONTRACTS) instead of the full tool catalog per docs/testing.md. Replace hardcoded explanatory message and remediation strings with dynamic references to the owning guidance constants.
* test(domain): restore independent replacement field assertions (#2146)
Keep exact checks for expected replacement field names ('procedure', 'pattern', 'address', 'query') independent of the owning guidance helper per docs/testing.md, while retaining dynamic validation for message and remediation prose.
Apply declaring-type selection before member-row admission, recovering only referenced unselected names. Reuse known cores and skip recovery for unscoped inspection. Add valid/malformed selected and unselected generic call regressions.
251 focused tests and check:fast pass. Public CLI scale reproduction and independent metadata oracle confirm complete selected members; public CoreLib/Roslyn/WPF facts remain identical. Real stdio MCP summary and paging retain complete source coverage and provenance.
Original proposal by sx4im. The maintainer explicitly authorized integration and waived the human-declaration gate; unchecked author declarations are preserved without attesting human review.
Fixes#8980.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Attribute REA capture-limit failures at the shared Rizin/objdump execution boundary. Preserve raw signals, output prefixes, unknown completion and incomplete-cleanup facts; report a termination request without claiming confirmed exit.
134 focused tests, check:fast and actual Rizin CLI controls over an owned ELF and /usr/bin/ls pass. Original proposal by cpruijsen.
Fixes#9031.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Preserve original SQLite TEXT bytes and decode them using the database encoding in exported Python readers. Extend the real encoding matrix to leading FEFF and quoted column names.
45 focused tests, native public CLI and full export/helper parity checks, formatting and check:fast pass. Retains the original correction by ShuaixinHuang from #9074.
Fixes#9004.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Apply the two-file repair proposed in #9109 to unblock main CI. Preserve every original test assertion and use the current registration command owner.
Co-authored-by: gensokyo-dev <[email protected]>
Use whole-row multiset identity for incomplete/NULL declared keys and share it with task drafting. Count actual projected predicates across all rows so timestamp omission and duplicate multiplicities yield satisfiable checks.
Fixes#8997.
Validation:106 focused tests; nativeSQLite public CLI before/after cases and generatedPython evaluator agreement.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
* feat(cli): expose caller-selected reference-source path exclusions
Let import-reference-source accept a JSON array of Git-ignore rules and
pass them to the existing importer before the source tree is opened.
Fixes#2443
* docs(cli): use an effective reference exclusion negation
* test(cli): expect entry_kind on reference-source exclusions
Main records entry_kind on every historical source exclusion. The caller
exclusion assertions still compared objects without that field, so the
pruning test failed after merging main.
Co-authored-by: Yunzhou Li <[email protected]>
* test(cli): record caller *.tmp over project ignore
Caller exclusions are mandatory and override project rules, so project.tmp
matched by *.tmp is caller-excluded. Align the pruning assertion with that
result from the merged importer.
---------
Co-authored-by: morluto <[email protected]>
Co-authored-by: REA Maintenance <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Yunzhou Li <[email protected]>
* fix(cli): honor the selected format for unknown mcp subcommands
Incur 0.6.1 renders its unknown mcp subcommand rejection as TOON
regardless of --format or --json. Report the same COMMAND_NOT_FOUND
envelope in the explicitly selected format before dispatching to
Incur, share the pinned mcp subcommand table with completions, and
redact explicitly declared sensitive values from the diagnostic.
Fixes#7691
* fix(cli): correct only the subcommand in the mcp suggestion
The suggested command replaced every token equal to the unknown
subcommand, including option values such as --command. Record each
command token's argument position in the shared builtin split and
replace only the subcommand position.
* fix(cli): leave earlier Incur builtins and caller output flags intact
Incur serves --llms, --llms-full and its update check before the mcp
subcommand branch, so defer those invocations to Incur as well. Build
the suggested command from the caller's arguments rather than the
arguments adapted for Incur, so a delimited format and its columns
remain in the suggestion.
* fix(cli): ignore output values spelled like earlier Incur builtins
The earlier-builtin check matched raw tokens, so an output flag value
such as --columns --llms or --filter-output --llms deferred the
rejection to Incur's TOON path. Check the arguments Incur receives and
skip positions its builtin pass consumes as output values.
* fix(cli): omit mcp suggestions that would not replay caller input
Joining the corrected tokens with spaces re-tokenizes a value with
whitespace or shell metacharacters when the suggestion is copied, and
quoting differs between POSIX shells, cmd and PowerShell. Offer the
corrected command only when every token is a plain shell word and no
declared sensitive value was redacted; the message still names the
suggested subcommand.
* fix(cli): preserve safe MCP diagnostic output
* fix(cli): omit mcp suggestions with words zsh expands
zsh expands a word that starts with = to an executable path by
default, so a copied suggestion such as --command =node would replace
the caller's literal value. Treat =-leading words as non-replayable.
* fix(cli): render mcp diagnostics through the document renderer
Markdown output went through Incur's formatter, so caller text such as
*oops* rendered as emphasis instead of literal text. Use REA's document
renderer for every format, and redact declared sensitive literals from
the fixed call-to-action strings as well as the message.
* test(cli): keep the mcp correction on the subcommand after timing globals
---------
Co-authored-by: morluto <[email protected]>
Document existing compact output advertisements for full catalogs and explicit hot/cold refresh under the FastMCP1MiB SSE limit. Preserve canonical server validation and spell out the remaining Unsloth and large-result verification gaps.
Refs #9093; related #5861,#7155.
Validation: four real FastMCP HTTP profile trials; docs:check and formatting.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Exclude only proven separate values from Webpack loader alias walks; preserve unsupported, projected and unresolved origins as unknown and retain the real alias-state bound.
Refs #7253. The full Bitwarden precision regression remains open.
Validation: 336 focused tests and check:fast passed; real Bitwarden preload identity retained; full main/preload completed with disclosed partial coverage.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
* feat(ghidra): admit DOS MZ targets on Windows P0
Windows Ghidra P0 refused dos-mz although the MzLoader import, 16-bit
real-mode language attestation and load-image evidence are host-neutral.
Admit dos-mz in inspectWindowsP0TargetSupport and run verify:ghidra:dos on
Windows: inherit the system environment there, expect annotation and naming
edits to be refused (P0 has no mutation authority), and skip verifier
lineage like the 6502 lane. Explicit COM stays refused on Windows; its
verifier is built around annotation edits.
Refs #7121
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(ghidra): check naming refusal in the read-only DOS lane
With mutable: false the address-boundary helper skipped every naming
request, so the Windows report overstated its coverage. Assert that
set_address_name and set_addresses_names are refused with
capability_unavailable and leave the entry name unchanged, and declare
the optional mutable argument in the .d.mts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(ghidra): unwrap failed profile Result before asserting
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* fix(ghidra): expect an unchanged DOS entry on Windows P0 verification
Windows P0 refuses edits, so verifyGhidraAddressBoundaries skips the
rename-and-restore and the entry keeps its imported symbol source. The
post-check still expected user_defined, which only follows a rename.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Register rea in the global ~/.codebuddy/mcp.json through a new codebuddy
entry in SUPPORTED_CLIENT_DEFINITIONS (json dialect, ~/.codebuddy marker,
~/.codebuddy/skills skill directory). Document the client and cover
registration and uninstall with boundary tests. Loading, the bundled
skill, and real REA tool calls were verified in the CodeBuddy client.
* feat(python): qualify a recognized 3.14/3.15 pyc header
Python 3.15 is the default `python3` on current Homebrew, but its pyc was rejected with
"Pyc magic 3666 is outside the pinned CPython 3.7–3.13 table" — a message that says
nothing about the file and reads as if the version were unknown. Issue #8206.
Two facts were wrong.
**3.15 was not in the identity table at all,** so `pythonBytecodeIdentity` reported it as
unknown even though the table already pins 3.14 (3627). 3666 is now pinned as 3.15.
**A known identity without a pinned code layout shared one refusal with an unknown
magic.** The two are different outcomes: every 3.7+ pyc carries the same packed 16-byte
header, so a recognized newer magic can still be qualified even when no marshal layout is
pinned for it. The parser now separates them:
magic 3666 (3.15) -> CPython 3.15 header is recognized (magic 3666, 16-byte header,
flags 0x0, timestamp-based), but no marshal code layout is
pinned for that version, so its code objects cannot be decoded.
Standalone marshal decoding is pinned for CPython 3.7–3.13.
magic 1 -> Pyc magic 1 is not a pinned CPython identity, so its header
cannot be qualified; ...
The recognized case reports the observed version, the header shape, the PEP 552 flags and
whether the pyc is hash-based or timestamp-based, which is what an agent needs to decide
that a `.jsc`-style text-recovery attempt is pointless and that the file is a 3.15 pyc
rather than a corrupt one. It stays a typed `unsupported` failure: no pinned layout means
no decoded code object, so there is no partial success.
I did not guess a 3.14/3.15 marshal layout. The 3.11+ `TYPE_CODE` order is pinned through
3.13; asserting the same order holds for 3.14 or 3.15 without a real interpreter to
verify against would be an unsupported claim, which is why this change qualifies the
header instead of decoding it.
Docs and the tool description now say marshal decoding is pinned for 3.7–3.13 while the
identity table also recognizes 3.14 and 3.15.
Three regressions fail on the base commit.
* chore: retrigger title and readiness checks after the title was corrected
* test(python): pin the qualified header facts to the packed fields
Review finding. Every new case used `pythonPycHeader(magic)` with its default zero flags
and never asserted the reported flags or header mode, so a refusal that hardcoded
`flags 0x0, timestamp-based` would have passed all of them.
The cases now build the header from explicit fields and assert the facts that field
produces: `flags 0x0/timestamp-based` and `flags 0x1` or `0x3` with a PEP 552 source hash
for hash-based. A separate case parses the same magic in both modes and requires each
refusal to name its own mode and not the other.
Verified by hardcoding `flags 0x0` in the refusal: the two hash-based cases fail.
* fix(javascript): report empty Electron coverage instead of silent zeroes
`analyze_javascript_application` reported `browser_windows: 0` and
`ipc.operations: 0` with only generic partial/unknown flags, so a bundle that
imports `electron` but whose Electron facts were not recovered read exactly like
an application with no IPC at all. Issue #8201 is that false negative on a real
Rolldown bundle.
Two additions, both scoped to "Electron is referenced but nothing was recovered":
**A machine-readable indicator.** `electron_summary` gains
`electron_semantics: "recovered" | "empty"`. It is absent when no analyzed source
links an Electron module, so its presence distinguishes "not an Electron
application" from "Electron coverage is empty". It follows the existing
`protocol_registrations.exactOptional()` precedent, so previously captured
evidence stays valid.
**A graph limitation.** The limitation list already carried a note when at least
one Electron fact was recovered but said nothing when zero were. It now states
that zero IPC operations and zero BrowserWindows mean empty coverage rather than
absence.
`electronModuleReferenced` is the single owner of the module test. It reuses
`ELECTRON_MODULE`, which matches exactly the supported entry points (`electron`,
`electron/common`, `/main`, `/renderer`, `/utility`), so `electron-helper` does
not count, and it ignores type-only links because importing Electron types
promises no boundary fact.
Five tests cover recovered, empty, not-referenced, type-only, and supported
subpath versus unrelated package. The empty, recovered and type-only cases fail
on unmodified `main`.
* chore: retrigger title and readiness checks after the title was corrected
* fix(javascript): report Electron coverage from retained facts
Four review findings on the coverage indicator.
**A rejected application projection hid the reference.** A file whose expanded
relationships exceed the result budget is replaced by a placeholder with no
module links before the summary runs, while `graphLimitations` reads the raw
analysis. The same response could then warn in prose and omit the field. The
summary now takes the retained analysis for coverage only, and both outputs
read one `electronSemanticsState`, so they cannot disagree.
**A skipped semantic analysis hid the reference too.** `electronModuleReferenced`
read only `semantic.ir.moduleLinks`, which is absent when semantic analysis is
skipped for a budget and drops `import("electron")` or `module.require("electron")`
forms. It now falls back to the retained `javascript.references`, which the static
producer records for those inputs, while still excluding `type-import` links when
the richer owner is available.
`electronBoundaryFactCount` is now the single owner of the fact total, replacing a
duplicate inline reduce in the graph builder.
**Omission no longer claims absence.** The schema comment said a missing
`electron_semantics` means the application does not use Electron. The predicate
only establishes that no retained fact names one, and unread sources, failed
analysis, and dynamic module names can hide its use, so the contract now says
unknown.
Two regressions cover a rejected projection and a skipped semantic analysis; both
fail on the previous commit.
* fix(javascript): count a dynamic Electron import as coverage
Review finding: `electronModuleReferenced` returned as soon as module links
existed, so a literal dynamic import was ignored. Verified against the two fact
owners:
import("electron") -> references: [dynamic-import electron]
moduleLinks: []
require("electron") -> references: [require electron]
moduleLinks: [require electron]
An application that only loads Electron through `import("electron")` therefore
got neither `electron_semantics: "empty"` nor the empty-coverage warning, and the
zero counts stayed unexplained.
The two owners now answer their own half instead of one short-circuiting the
other. Module links answer a runtime static import or require and keep excluding
`type-import`; the retained reference is read for every kind except
`static-import`, which is exactly the type-only risk. A dynamic specifier is
still never counted by either owner, so this remains an observed-reference claim
rather than an absence claim.
The regression uses `import("electron")` and fails on the previous commit.
* fix(javascript): read only module-loading Electron references
Two review findings, both verified against the static reference producer.
**A retained runtime static import was skipped.** The excluded kind was meant to
protect the type-only distinction, but the static analyzer records a whole
`import type { IpcMain } from "electron"` declaration as no reference at all:
import type { IpcMain } from "electron" -> references: []
import { app } from "electron" -> references: [static-import electron]
require("electron") -> references: [require electron]
import("electron") -> references: [dynamic-import electron]
new Worker(new URL("electron", m)) -> references: [worker electron]
When semantic analysis is skipped for a budget the module links are absent, so
that exclusion dropped a real runtime ESM import and left the zero counts
unexplained.
**A worker URL path is not an Electron module.** `new Worker(new
URL("electron", import.meta.url))` records a `worker` reference whose specifier is
a path relative to the importing source, so counting it reported an ordinary
worker file as `electron_semantics: "empty"`.
`ELECTRON_REFERENCE_KINDS` is now an explicit allowlist of the kinds that load a
module by package specifier — `static-import`, `dynamic-import` and `require` —
so `worker` and `service-worker` URL paths are excluded. Module links are still
read first for the type-only distinction.
Three regressions cover a skipped-semantics ESM import, a skipped-semantics whole
type-only import, and a worker URL path; two of them fail on the previous commit.
* fix(javascript): own Electron applicability in the static producer
Review finding. A `worker` reference records a URL relative to the importing
source, so `new Worker(new URL("electron", import.meta.url))` names a local file
and is not an Electron module. The boundary owner already read only module-loading
reference kinds, but `finalizeStaticAnalysis` still treated every matching static
reference as `analysis_coverage.applicability.electron: "observed"`, which
`javascriptStaticSourceCoverageSummary` publishes. One response could therefore
say Electron was observed in its coverage view while the Electron summary omitted
`electron_semantics`.
`ELECTRON_REFERENCE_KINDS` now lives next to `ELECTRON_MODULE` in the domain owner
and is consumed by both the static producer and the Electron boundary owner, so the
two outputs cannot disagree. A worker URL path is `unknown` for both, while a
`require("electron")` is `observed` for both.
The regression asserts the domain applicability fact and the boundary summary agree
for a worker URL path; it fails when the producer stops filtering by kind.
* fix(javascript): require a loaded Electron module for applicability
Review finding. A CommonJS `require.resolve("electron")` records a `require`
reference with `operation: "resolve"`: it locates the module without loading it,
so no Electron boundary fact is expected. Both predicates dropped the operation,
so such a source reported `electron_semantics: "empty"` and the missing-coverage
warning even though Electron was never loaded.
Measured on the static producer:
require.resolve("electron") -> references: [require resolve electron]
applicability.electron: unknown
require("electron") -> references: [require load electron]
applicability.electron: observed
import("electron") -> references: [dynamic-import load electron]
applicability.electron: observed
`electronModuleReferenced` and `finalizeStaticAnalysis` now both require
`operation === "load"`, so the boundary summary and the coverage view stay aligned
and reachability-only references no longer claim missing coverage.
The regression asserts a resolve-only source produces neither the field nor the
warning, and it fails when the operation check is removed.
* fix(javascript): classify Electron loads in the static fact owner
Review finding, and the correct home for the previous two rounds of the same
complaint. A Webpack/Rspack factory may name a local module `"electron"`, and the
static producer then records a loaded `require` reference whose specifier is that
bundle module key. Treating the key as a package specifier reported
`electron_semantics: "empty"` plus the missing-coverage warning for a bundle that
never loads Electron.
Three changes, all in the domain owner, so no consumer re-derives the question:
- `JavaScriptModuleRangeIndex.hasKey` answers whether a specifier is a recovered
bundle module key. A `require` whose specifier is such a key loads a module
inside the artifact.
- `electronObserved` in `finalizeStaticAnalysis` now requires a loaded module-load
reference whose specifier is not a bundle key. The published
`analysis_coverage.applicability.electron` therefore owns every exclusion:
`type-import`, `require.resolve`, `worker`/`service-worker` URL paths, and
bundle-local keys.
- `electronModuleReferenced` consumes that owned fact instead of re-deriving it,
keeping the boundary summary and the coverage view from disagreeing. Historical
evidence without the field still falls back to the retained references.
Measured:
bundle-local module ID "electron" -> applicability unknown
require("electron") -> applicability observed
webpack external n(45840) -> applicability observed
The regression covers both the local key and the real external in the same
factory shape; it fails without the bundle-key exclusion.
* fix(javascript): identify a bundle module by its loader, not its key
Both review findings were right, and they show why the previous key-set approach
was wrong: bundle-key membership is a property of the file, but whether a
specifier names a module inside the artifact is a property of the reference.
`JavaScriptStaticReference` now records the loader that produced it, and
`electronObserved` excludes `factory-loader` references instead of every specifier
that happens to match a recovered module key. Measured on Node 24.22.1:
local module keyed "electron" -> unknown (factory-loader)
external 45840 = require("electron") -> observed (commonjs-loader)
factory keyed "electron" that loads the pkg -> observed (commonjs-loader)
The third case is the regression: a bundle whose factory is *named* `"electron"`
but whose body does `e.exports = require("electron")` is a real Electron load, and
the old key-set check suppressed it. The real load keeps its `commonjs-loader`
identity even when the same specifier also names a factory, so one response can
rely on the loader while the other relies on the key without either lying.
`JavaScriptModuleRangeIndex.hasKey` is removed; it answered the wrong question.
One regression covers all three shapes and fails when the `factory-loader`
exclusion is dropped.
* fix(javascript): carry the reference loader across the worker boundary
The worker schema is strict, so the new `loader` field broke the worker handshake:
`unrecognized_keys` on every `references` entry. That surfaced in CI as both a
worker-schema test failure and a package-baseline failure reporting
`worker_failure_reason: "io"` for a one-file Electron app.
`referenceShape` now accepts the same three loader identities as the domain type, so
the fact survives the worker boundary instead of collapsing every analysis to an
execution failure.
Verified: `JavaScriptAnalysisWorkerSchemas.test.ts` 10 passed, and the full
domain/application/javascript suite is 5484 passed | 10 skipped. The remaining
`SourceMapCommand` and `DarwinHelperCache` findings are present on unmodified
`main` and are not from this change.
* fix(javascript): remove the superseded key set and forward a deferred loader
Two review findings.
**The `keys` set is dead.** `JavaScriptModuleRangeIndex.keys` is left over from the
abandoned key-membership approach: the loader-identity refactor removed `hasKey` and
the write in `add`, so nothing reads it. Removed.
**The deferred require path dropped the loader identity.** `requireCallSpecifier`
returns a `loader` for `commonjs-loader`, `factory-loader` and `create-require`, and the
direct `addReference` call forwards it, but `finishModuleRequireCalls` did not. A
`createRequire` loader saved under a member literally named `require` therefore lost
its identity:
import { createRequire } from "node:module";
const holder = { require: createRequire(import.meta.url) };
holder.require("x");
before: require "x" with loader undefined
after: require "x" with loader "create-require"
Three regressions cover it; one fails without the change.
I did **not** change the inline-type-only import case. `isTypeOnlyImportDeclaration`
deliberately keeps a declaration whose bindings are all inline type-only, because
`hasUncertainTypeOnlyModuleSideEffect` reports that decision as
`type-only-module-side-effect` uncertainty: whether TypeScript emits the import depends
on the consumer's compiler options. Narrowing it to erased broke
`javascriptStaticCoverage.test.ts` and `javascriptSemanticAnalysis.require_links.test.ts`,
which assert exactly that contract. If Electron applicability should ignore that
reference, the decision belongs where applicability is derived, not in the shared
declaration predicate — say the word and I will do it there instead.
* fix(javascript): keep a commonjs-loader reference out of the bundle module index
Review finding. `finalizeLocatedFindings` attaches `module_runtime` to every finding
that sits inside a factory module range, whoever produced it. `resolveReference` then
sent every reference carrying `module_runtime` through the bundle-module lookup, so a
real CommonJS load inside a factory body resolved to the local module key that shared
its name:
"electron": (e, t, n) => { e.exports = require("electron") }
fact: specifier "electron", loader "commonjs-loader", module_key "electron"
before: resolved through the bundle module index to `#module:electron`
after: resolves as an external package specifier
Only a `factory-loader` specifier names a recovered bundle module key, which is what
`loader` already records after the loader-identity change. The module lookup is now
gated on it, and an absent `loader` keeps the previous behaviour for older findings.
* fix(javascript): emit no fs acquisition for a rejected literal mode
`fs.open`, `fs.openSync` and `fs/promises.open` validate the `mode` in argument 2 before
touching the filesystem, so a mode Node rejects throws synchronously and no descriptor
exists. The guard read only the flag, so a rejected mode kept its acquisition and any
linked release.
Measured on Node 24.22.1:
fs.openSync(p, "r", -1) -> THREW ERR_OUT_OF_RANGE
fs.openSync(p, "r", 1.5) -> THREW ERR_OUT_OF_RANGE
fs.openSync(p, "r", NaN) -> THREW ERR_OUT_OF_RANGE
fs.openSync(p, "r", 1e21) -> THREW ERR_OUT_OF_RANGE
fs.openSync(p, "r", true) -> THREW ERR_INVALID_ARG_TYPE
fs.openSync(p, "r", 0n) -> THREW ERR_INVALID_ARG_TYPE
fs.openSync(p, "r", {}) -> THREW ERR_INVALID_ARG_TYPE
fs.openSync(p, "r", "8") -> THREW ERR_INVALID_ARG_VALUE
fs.openSync(p, "r", "") -> THREW ERR_INVALID_ARG_VALUE
fs.openSync(p, "r", "0o644") -> THREW ERR_INVALID_ARG_VALUE
--- accepted ---
fs.openSync(p, "r", 0o644) -> ok
fs.openSync(p, "r", 2147483647) -> ok
fs.openSync(p, "r", -0) -> ok
fs.openSync(p, "r", "0644") -> ok
fs.openSync(p, "r", null) -> ok
fs.openSync(p, "r", void 0) -> ok
The guard decides only exactly-readable literals. A numeric mode is validated as an
unsigned 32-bit integer; a string mode must be a canonical octal digit string, padded
leading zeros allowed, and its value must stay inside uint32; `null`, `undefined` and
`void <expr>` default the mode and are kept, as is a computed value.
Three boundaries are deliberate:
- Values in `[2^31, 2^32-1]` **abort the Node process** rather than throwing
(`Assertion failed: args[2]->IsInt32()`), so they stay undecided instead of being
claimed as a synchronous throw.
- A spread before the mode slot shifts the runtime position.
- A unary chain is resolved through its innermost readable operand, and a nested
boolean or string result is left undecided rather than applied as a number.
Regressions cover both directions. Full suite here: 5466 passed | 7 skipped.
* fix(javascript): remove the superseded string mode check
Review finding. `rejectsOpenModeLiteral` builds `octal` from a string literal or a
no-expression template and returns inside that branch for every value it matched. A
`StringLiteral` therefore never reaches the later `isStringLiteral` check, which cannot
run and is a second copy of the canonical-octal rule to maintain.
* test(javascript): cover the spread and boundary paths of the mode guard
Review feedback from @greptile-apps: the new mode tests skipped both paths the
guard deliberately leaves undecided. Two spread cases are added:
- `openSync(...args, "r", "0600")`, where a spread in the flag slot shifts the
runtime position of the mode;
- `openSync(p, "r", ...modes)`, where a spread occupies the mode slot itself.
Both must keep the acquisition, because neither establishes the mode value. The
boundary cases were already present: `2147483647` and `2147483648` are keep
cases (`2147483648` aborts the process rather than throwing) and `4294967296` is
the reject case, alongside the octal `37777777777` keep and `40000000000` reject
pair.
127 cases pass in this file.
pull-adb-file and push-adb-file declared device-path as any non-empty string. The CLI schema advertised a relative path as valid, and the service then refused it under the MCP field name. Reuse the shared device_path fields, and keep the regex constraint in CLI field errors as list-adb-directory does.
* fix(evidence): bound malformed file field diagnostics
`readEvidenceFileSource` projected every validator issue, and the generic error
delivery applied the MCP response budget afterwards. A file with many invalid
fields therefore produced a transport `resource_constraint` that replaced the
typed file error, so the caller lost the selected path entirely. Issue #8453
reports exactly that: 20,000 invalid values still returned a direct
`invalid_request` with the path and every field issue, while 125,000 returned
`resource_constraint` with neither.
`projectInputIssuesWithinBudget` already existed for this but had no production
caller. The reader now projects within a 64 KiB allowance and, when the whole list
does not fit, keeps the largest leading group that does and appends one explicit
omission issue naming how many of how many were dropped. The typed file error and
its `path` therefore survive the response boundary, and the diagnostics stay
actionable and explicitly incomplete instead of hiding the real failure.
Two regressions cover the bound and the fits-in-budget case. With the unbounded
projection restored, the bound test and its supporting fixtures fail.
* chore: retrigger title and readiness checks after the title was corrected
* fix(evidence): bound file diagnostics in one pass and cover the MCP boundary
Review findings on the first version.
**The prefix was not maximal.** `boundedContentIssues` halved the list and returned
the first attempt that fit, so a file just over the allowance lost roughly half its
diagnostics. Prefix selection now lives in the shared projector
(`projectInputIssuesBounded`), which walks the issues once and stops exactly at the
allowance, keeping the largest fitting prefix.
**A compound issue could still lose its location.** One `unrecognized_keys` issue
naming hundreds of keys expands into hundreds of field diagnostics and can exceed
the allowance on its own. The projector now retains a diagnostic at that issue's
path instead of returning only the omission notice.
**The regression never crossed the MCP boundary.** The issue is about the delivered
MCP error, so `evidenceFileSourceMcp.test.ts` sends the 125,000-value fixture
through the production `inspect_analysis_view` tool and asserts the delivered error
stays `invalid_request`, keeps `details.path`, keeps bounded `content_issues`, and
fits the response budget. Both regressions fail without the bound.
* test(evidence): move the MCP delivery regression to the adapters project
Review finding. `scripts/lib/test-projects.mjs` assigns `src/application/**` to the
`services` project, so the delivery regression sat in the wrong project even though
it exercises the production MCP tool registration. Moving it to `src/server/` puts
it in `adapters`, which runs the other MCP boundary tests and plans the same way.
* fix(web-capture): keep mitmproxy request targets and status codes in previews
Capture previews selected HAR/SAZ headline names (url, status) only, so a
native mitmproxy record lost its request target (scheme, host, port,
authority, path) and its status_code while keeping the reason phrase, and
the matching numeric sidecars were filtered out. Select those mitmproxy
headline fields too; headers and payloads stay omitted.
* fix(comparison): recheck historical calls-only changes (#2223)
* fix(comparison): preserve incomplete function call evidence
Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.
Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.
* fix(comparison): recheck historical calls-only changes
---------
Co-authored-by: morluto <[email protected]>
* fix(ci): isolate Linux Hopper process fixture inventory
* fix(android): make DEX descriptor pattern portable
* style: format comparison and IDA sources
* fix(web-capture): preview mitmproxy targets only as safe scalars
Select the mitmproxy target and status fields only for mitmproxy
records, only when they are scalars, and the path only in origin form.
An absolute, network-path or otherwise ambiguous path spelling can carry
userinfo that the native bridge's redaction does not recognise, and a
nested value could copy an arbitrary record subtree into the preview;
both stay in complete record pages. Other formats keep their existing
headline fields.
* fix(web-capture): keep the asterisk-form OPTIONS target in mitmproxy previews
* fix(web-capture): exclude ambiguous and oversized mitmproxy targets from previews
Origin-form checks alone accepted slash-backslash and slash-control
spellings such as /\user:password@host/a, which WHATWG resolves as an
authority with credentials and the recorded transport-URL interpretation
already treats as ambiguous credentials. Preview a mitmproxy path only
when parseRecordedTransportUrl finds no credentials, and omit mitmproxy
headline strings longer than 8 KiB so one record cannot exceed the reply
budget; both stay in complete record pages.
* fix(web-capture): keep userinfo-bearing mitmproxy target fields out of previews
Native redaction covers only request path and authority, so a malformed
state such as host "user:[email protected]" passed through the
preview. Omit every non-path mitmproxy headline string that contains a
userinfo marker (@); valid schemes, hosts and ports never do, and the
value stays in complete record pages.
* fix(web-capture): admit mitmproxy headline values only in their well-formed shapes
A per-string length cap still let a malformed capture fill every target
slot with escape-heavy text, so one default preview page could expand to
about 18 MiB of JSON. Preview each mitmproxy field only in its own shape:
a short scheme token, host/authority without controls, whitespace or
userinfo markers, integer port and status code, and a path of at most
2 KiB without control characters. That bounds every record's headline to
about 4 KiB serialized, even for an escape-heavy path.
* fix(web-capture): bind mitmproxy headlines to their message side and integer type
Select mitmproxy target fields only from the request and status_code
only from the response, so an unknown scalar extension such as
response.path or request.status_code is not promoted to a headline.
Admit port and status_code only when the record's numeric sidecar
reports the producer's integer type; a native float such as 443.0 is
otherwise indistinguishable from 443 after JSON parsing.
* fix(web-capture): require one canonical integer sidecar for mitmproxy numeric headlines
An inline parent could pair request.port 443 with an integer sidecar
whose literal is unbounded or does not represent the value, or with
duplicate sidecars, and the selected-pointer filter would copy them into
the preview. Admit port and status_code only with exactly one integer
sidecar whose literal is the value's canonical decimal, which also
bounds the copied literal to a few characters.
* fix(web-capture): add mitmproxy headlines only for HTTP flows
The mitmproxy container format does not make every retained flow HTTP; the bridge keeps non-HTTP and unknown flow states with their extensions. Add the mitmproxy target and status candidates only when the record's own type is "http".
* fix(web-capture): require consistent numeric sidecars for every mitmproxy headline
Selecting a field also selects its numeric sidecars, so a string target such as request.path with an unrelated multi-megabyte sidecar copied that literal into the preview. Admit integer slots only with exactly one canonical integer sidecar and string slots only with no sidecar.
* fix(web-capture): fail closed on any userinfo marker in previewed mitmproxy paths
URL parsing can fail before recognising userinfo: /\user:[email protected]:65536/a has an invalid port, so the parser reported no credentials and the path was previewed with them. Every userinfo needs "@", so preview a path only when it contains none and does not start with // or /\; such paths stay on complete pages. The parser check is no longer needed.
* fix(web-capture): never promote a redacted pointer to a mitmproxy headline
Inline or imported Evidence can carry a value at a pointer that its own redactions mark as sensitive. Skip any mitmproxy headline whose pointer has a redaction entry, so a preview never re-emits a value the record says was redacted.
* fix(web-capture): check integer headline types before comparing sidecar literals
The sidecar comparison called String(value) before the numeric shape check, so an inline object such as {"toString": null} at request.port threw a TypeError out of the view instead of being omitted. Require a number before canonicalizing it.
* fix(web-capture): honor ancestor value redactions before selecting mitmproxy headlines
A value redaction at /request or the record root excludes its whole subtree, but only exact-pointer matches were honored, so contradictory inline Evidence could still preview /request/host or /request/path. Treat a value redaction as covering every descendant pointer; a property-name redaction still only identifies the parent of an omitted key and hides no sibling values.
* fix(web-capture): treat redacted byte descriptors as exclusions for mitmproxy headlines
Producers mark excluded bytes with a redacted binary descriptor as well as a redaction entry, and the record schema accepts either facet alone. Skip a mitmproxy headline when a redacted descriptor covers its pointer, so contradictory Evidence that keeps the value cannot preview it.
* fix(web-capture): refuse a redacted flow type as the mitmproxy HTTP discriminator
Contradictory Evidence can keep type "http" while marking /type redacted. Apply the same redaction check to /type before treating the flow as HTTP, so no mitmproxy headline is added when the flow kind is unavailable.
* fix(web-capture): refuse mitmproxy headlines under contradictory numeric sidecars
A numeric sidecar records that the producer held a number at that pointer. Treat the flow as HTTP only when /type has no sidecar of its own, and promote a mitmproxy headline only when no ancestor (the record root, /request or /response) carries one.
* fix(web-capture): require a textual flow type before adding mitmproxy headlines
mitmproxy's flow state carries type as a string. When the native value was bytes, the bridge shows the UTF-8 display text "http" next to a retained binary descriptor, so the producer did not supply the discriminator mitmproxy defines. Treat the flow as HTTP only when /type has no numeric sidecar and no binary descriptor at or above it.
* fix(web-capture): refuse mitmproxy headlines under byte-valued ancestors
The mitmproxy bridge emits either a byte descriptor for a value or children for a dictionary, never both. Treat a byte descriptor on an ancestor like a numeric sidecar, and refuse a byte descriptor on an integer headline, while keeping the exact descriptor of byte-backed string fields such as the request path.
* fix(web-capture): check child facets and byte provenance of mitmproxy headlines
A facet beneath a scalar coordinate says the producer held a container there, so refuse the flow type or a headline field with a numeric sidecar, byte descriptor or redaction below it. A string headline may carry at most one byte descriptor, and only retained mitmproxy producer bytes whose content is the value's UTF-8; HAR content bytes or mismatched content keep it out.
* fix(web-capture): verify the byte count and digest of byte-backed headlines
The record schema checks a descriptor's length and digest shapes, not their relation to its content. Require a byte-backed string headline's descriptor to match the value's UTF-8 in content, byte count and SHA-256 before promoting it.
* fix(web-capture): reject byte-backed headlines without a UTF-8 form
Buffer.from replaces an unpaired surrogate with U+FFFD bytes, so a descriptor for those bytes matched a string no producer could have decoded. Require the value to round-trip through UTF-8 before comparing it with its descriptor, as CaptureRedaction already does.
* fix(web-capture): index mitmproxy facets once and refuse non-UTF-8 targets
Reject host, authority and path text containing an unpaired surrogate whether or not a byte descriptor backs it, since no producer could have reported such text. Build a per-record index of facet pointers (sidecars and descriptors by pointer, scalar pointers, container ancestors, withheld values and omitted names) once, only for mitmproxy HTTP records, so each headline check is a few set lookups instead of several passes over every facet array.
* fix(web-capture): bound the mitmproxy facet index to headline coordinates
Facet pointers have no length or depth bound, and recording every ancestor kept one set entry per prefix (a million for a 1 MB pointer). Record only the fixed headline coordinates (/type and the request/response target and status fields) that have a facet beneath them, examining prefixes no longer than those coordinates.
* fix(web-capture): index only facets at headline coordinates and their ancestors
Grouping every numeric sidecar and byte descriptor allocated an entry per facet even though only /type, the request/response headline fields and their ancestors are queried. Keep facets only at those pointers (at most two per pointer, which already decides every check), and let other facets merely mark the headline coordinates they lie beneath, using prefix comparisons that derive no strings.
* test(web-capture): pin headline omissions and controls beside many unrelated facets
The bounded facet index must not change what a mitmproxy preview withholds or keeps. Beside 20,000 unrelated sidecars, a redacted host and a path with a facet beneath it stay withheld while the scheme, port and status remain, with only their own sidecars retained. The test passes against both the current main projection and this change.
* test(web-capture): count long set additions in the million-character pointer tests
The two million-character pointer tests had no time or size bound, so on
Node 22 and 24 they passed against the old ancestor index too, and only
Node 26 caught a regression by running out of memory. Count the strings
longer than 1,000 characters the preview adds to sets instead: only the
pointer and strings cut from it are that long, the old index added
999,000 and 499,507 of them, and the new index adds none.
---------
Co-authored-by: Lucien <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
inspect-python-pyc and inspect-erlang-beam took their max-* limits through Number(), so 0x10 meant 16 and 1e2 meant 100. Admit decimal text only through cliDecimalField, which now keeps the shared field's description for help.
* fix(native): keep inspect_macho usable when dyld_info refuses the slice
dyld_info exits 1 with 'unknown platform' for Mach-O files without a
platform load command and with 'old arm64e bind opcodes not supported'
for pre-ABI arm64e binaries. inspect_macho then failed outright with the
generic execution_failure remediation although nm, otool and the load
table still describe the slice. Treat these two refusals like the
missing-UUID refusal: imports are reported unavailable, exports keep the
nm observations, and the dyld_info diagnostic is recorded as a limitation.
* test(native): cover dyld refusal fallback and limitations
* fix(decompile): keep the output failure streak across external functions
An external function writes no file, but the streak counter treated its outcome as a success and reset to zero. With external functions interleaved between unwritable local functions, decompile_all never reached the documented five-failure stop. The streak now ignores external outcomes.
Fixes#7374
* style(decompile): restore the repository formatter output
oxfmt --check rejected decompileAll.ts after a local prettier pass reflowed an unrelated type alias. Restore it so the diff is only the streak change.
* ci: retrigger after artifact-download ECONNRESET
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
---------
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
* test(process): align symlink fixture with filesystem checkpoints
* test(frida): assert the CLI spawn Evidence contract
* test(mcp): use an unsupported analysis-view parent
* test(browser): separate source-location line bounds from parity
* test(desktop): make Python output fixtures portable
* test(process): align symlink fixture with filesystem checkpoints
* test(frida): assert the CLI spawn Evidence contract
* test(mcp): use an unsupported analysis-view parent
* test(browser): separate source-location line bounds from parity
* fix(cli): stop completion after the option terminator
inspect-desktop-application took --max_entries and --max_probe_bytes through Number(), so 0x1 examined one entry and 1e1 ten. Admit decimal text only and keep the shared fields' bounds and defaults.
inspect-gpu-code-objects declared --expected_sha256 as any string. The CLI schema accepted an upper-case digest, as PowerShell's Get-FileHash prints it, and the command then refused it as invalid_request. Reuse the shared lowercase SHA-256 field so argument parsing refuses it with the CLI field name.
* test(cutter): synchronize peer descriptor close before cancellation
* test(process): align symlink fixture with filesystem checkpoints
* test(frida): assert the CLI spawn Evidence contract
* test(mcp): use an unsupported analysis-view parent
* test(browser): separate source-location line bounds from parity
* feat(harmony): disclose the unread pkgContextInfo.json declaration
A package that ships pkgContextInfo.json shows the entry in
unclassified_components, yet nothing said its isSO declarations were
skipped: bridge_candidates came only from the libs/<abi>/lib<name>.so
path rule. State that gap in limitations when the entry is inventoried,
so a path-convention hypothesis is not read as a package declaration.
The declaration-parsing half of #4489 needs a decision about who owns
reading inventoried file content; this projection receives identity and
digests only, and the shared inventory carrier guard rejects other
Evidence. No content is parsed here.
* test(harmony): assert the disclosure by its names, not its sentence
docs/testing.md requires asserting structured facts instead of freezing an
explanatory sentence, and says a replaced prose assertion must still pass on a
harmless wording change while failing when the fact is lost. The first version of
this test compared the whole limitation string verbatim, so any rewording of the
disclosure would have failed CI.
The assertions now require exactly one limitation naming pkgContextInfo.json,
carrying bridge_candidates, isSO, and the not-read/not-narrowed fact, and keep the
structural claim that the same package without the declaration file yields
identical bridge candidates and no such limitation.
* fix(desktop): launch spaced REA archives and Electron apps
Link the unpacked REA entrypoint to a fixed path so a top folder with
spaces is not split by the generated rea launcher. When a build request
marks the app as Electron, rewrite its installed desktop command with
the export path's launch policy before gtk-launch.
* fix(desktop): escape rewritten Exec values for desktop files
Command quoting alone left literal backslashes one escape short of the
desktop-entry value layer, so gio rejected the rewritten entry. Escape
the command with the same rules parseDesktopEntry undoes before writing
the Exec line.
---------
Co-authored-by: Cursor Agent <[email protected]>
State the existing discovery page and exact-name bounds using the validation constants. Isolate account apps and plugins in catalog trials by default, with an explicit opt-in and one shared feature policy for config and receipts.
* feat(javascript): compare static endpoints with HAR captures
* test(javascript): preserve original HAR fixture bytes during formatting
* test(javascript): load the byte-preserved HAR fixture
* feat(desktop): keep an agentenv computer up and drive it through REA's MCP tools
- A Python helper keeps one agentenv computer up per build with
run_bundle(keep=True) and reports its address, MCP URL, control token
and sandbox id; it tears the computer down when stdin closes.
- AgentEnvComputer owns the helper, waits for readiness, holds one MCP
session to the computer's computer_* tools, records through the
computer-recording routes, and removes the sandbox's containers by
label when agentenv cannot confirm teardown. The token never leaves it.
- start_build_computer starts a computer from a request (archive package,
apt repository or .deb, opened on demand with REA in /opt/rea) or from
a bundle folder; act_on_build_computer forwards screenshot, click,
drag, scroll, type, key and wait and returns the driver's images
unchanged; close_build_computer tears it down.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): remove a build computer's container by name when agentenv cannot confirm teardown
agentenv's local provider runs an ordinary computer as agent-<sandbox id>
without the agentenv.sandbox label, so the label-only fallback found
nothing and reported a running computer as removed. The fallback now
lists containers by exact name and by label. The real test holds its
recording open long enough for a first frame, checks the container by
name, and runs rea inside the computer; the guide and tool descriptions
say the app opens from the Applications menu and that a screenshot after
opening a menu should come from a wait.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): own failed build-computer starts, keep ids past oversized screenshots, bound recordings
- A start is owned from the helper's spawn. One that fails and whose
teardown is unconfirmed stays held under a computer_id close can retry;
closeAll refuses new starts, cancels and joins starts in progress, then
closes every held computer.
- When the first screenshot is too large, the error carries REA's result
with the computer_id, and start_build_computer takes screenshot region,
format and quality.
- Recordings are refused before reading when the manifest's total exceeds
the allowance, then fetched one segment at a time, each read bounded and
checked against its size and sha256.
- The helper keeps the tail of its output (a new tail retention mode in
ProviderProcessSupervisor), so a build's own error is not crowded out.
- A helper that reported no computer exits on its own before SIGTERM, so
no phantom owner is kept.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): bound a build computer's action log
Every action kept its full arguments for the computer's whole life, so a long
session grew REA's memory without limit. The log now keeps the latest 256
actions within 256 KiB, cuts each string to 1 KiB and each list to 64 items
(naming what it cut), and keeps the total and the last applied index of each
kind of action, so rules over the whole session hold after entries drop.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
RFC 5280 4.1 defines an Extension as extnID, an optional critical
BOOLEAN and an extnValue OCTET STRING. Section 4.2 says a certificate
MUST NOT include more than one instance of an extension.
The decoder only checked that extensions were a SEQUENCE. Any entry,
and any repeat, was accepted.
adb prints the no-permissions state as "no permissions[ (reason)]; see [URL]"
and omits the reason when it finds no udev problem to name, for example on a
Linux host without a plugdev group. The state matcher required whitespace after
"no permissions", so that line counted as unrecognized and list_adb_devices
refused the whole listing, hiding every other attached device.
Accept ";" after this one state. Other states still need a whitespace or
end-of-line boundary.
* feat(desktop): capture a Linux desktop application in use
Add `capture-desktop-application`, a CLI-only, Linux and X11 capture that
launches an installed desktop application, watches it while it is used and
writes capture Evidence plus SHA-256 addressed content:
- inventory: desktop entry, launch command, executable, package and its
apt sources (or snap, flatpak, AppImage), Electron markers and candidate
state directories;
- session: the launched process tree, client windows from xprop with the
first application window time and a window match, and files held open;
- state: a stat walk of the home directory before launch, at checkpoints
read from stdin, at stop and after exit, with created, modified, deleted
and retyped paths and the content of changed files and state directories.
It refuses non-Linux hosts, missing DISPLAY or xprop, a non-empty output
folder and an application whose window is already open, and signals only
processes it started.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): own launched processes, confirm walk coverage and keep cancelled captures
- Launch through the owned provider path with a run token; release with
SIGTERM, then the token-verified group cleanup, after every session.
Processes without the token are never signalled; each records whether it
carries it, and the session records whether cleanup was verified.
- Report creations and deletions only where a walk listed the nearest
existing directory; count the rest as unconfirmed.
- Retain a file only when it is still the walked version, through owned
no-follow reads; charge the budget only for new content; create files
exclusively in a private output folder owned by the caller.
- Walk content roots outside the home directory, compare paths in their
physical spelling, and run executables from the caller's directory.
- Refuse when the window list cannot be read, re-evaluate window ownership
each sample, handle one queued request per sample with a bounded stdin
queue, and write the capture with end reason `cancelled` on interruption.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep owned cleanup failures and fresh digests
- Allow a capture cancelled before any requested checkpoint to hold only the
baseline.
- Retry an incomplete stop once; if cleanup is still unverified, write the
capture and fail with the run token and process group as cleanup
resources.
- Classify path changes after a walk (ENOTDIR and other selected-file
errors) as changed_since_walk instead of failing the capture.
- Treat a non-directory ancestor as proof that nothing existed beneath it.
- Report a before digest only for the file version it was read from.
- Derive stderr_truncated from stderr's own byte counts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* wip(desktop): state view over a capture folder
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep failed file-handle closes owned for retry
Content reads pass retainFailedClose, so a handle whose close fails stays
owned by the content store; the capture retries those closes after it is
written and fails with the output folder as the cleanup resource if one
still cannot be closed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): read a captured application's state
Add inspect-desktop-application-state, a CLI command that reads a capture
folder into the application's state: every retained or changed file with
its role and reasons, a typed JSON view of each version (SQLite through the
shared worker, dconf through a private dconf dump, JSON, XML, key files,
text) and the kinds of change between versions (lines, values, keys, rows).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): report process and handle cleanup failures together
When both the launched processes and a retained file's handle remain
unresolved after the capture is written, one cleanup error now lists both
reasons and both resource sets.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): export a captured application as an agentenv computer app
Add export-desktop-application, which writes an agentenv bundle with one
computer_app environment from a capture folder and its state view: the
apt package, a boot launch matched on the captured window class with a
window ready check, owned paths from the state roles, a seed of the
baseline files, data hooks that call agentenv's add/get/reset helpers
with a typed JSON view on get, a skill, a README and a deploy smoke task.
The desktop CLI commands now load their services lazily.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep state views faithful to the capture
- Read key files, XML attributes, dconf groups and SQLite tables into
dictionaries without inherited properties, so captured keys such as
__proto__ stay data.
- Version databases with their write-ahead log as of each checkpoint, and
stage only digest-verified bytes for the SQLite and dconf readers.
- Detect the format per version, report format changes, keep JSON null and
empty containers as values, keep XML text and preserved whitespace, and
mark truncated views (including XML depth) and comparisons of them.
- Number versions and changes by checkpoint index, decide baseline
presence from the captured type, and keep tagged SQLite integers, text
bytes and blobs distinct.
- Carry the capture's limitations and omitted content forward, match state
directories by their home path, index changes once per capture, keep
failed handle closes owned, and finish the CLI through the shared owner.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep coverage, logs and handles honest in the state view
- Read coverage flags only from formats whose readers write them, so JSON
fields named truncated stay data.
- Fail a SQLite version whose write-ahead log existed but was not retained
or does not match its digest, instead of reading the database alone.
- Close retained content on every exit, including cancellation, and report
a close that still fails together with the primary failure.
- Finish the CLI with the canonical operation name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): export only what the computer can install and seed
- Refuse packages not from the Ubuntu 24.04 archive, since the computer
installs by name from that archive.
- Start the launch command in the captured working directory.
- Respell the home path only in files the state view read as text.
- Report files that were not retained instead of dropping them silently.
- Load captured dconf keys for apps that own no files.
- Keep home-level files as owned paths; rename reserved Python class names.
- Mark truncated SQLite and text views in the class's get view, and read
file types the way the state view does.
- Release the output tree through a resource scope, and read seeds through
an owner closed on every exit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep staged folders owned, symlinked state roots and XML values
- Stage database bytes in a private runtime root released through a
resource scope, so a failed removal keeps its owner and is reported with
the read's outcome.
- Report cleanup facts and owners from desktop failures instead of
wrapping them as a plain adapter error.
- Record the state directories as walked, in their physical spelling, and
match roles against both spellings, so files in a symlinked state
directory are not mistaken for selected documents.
- Keep whitespace-only XML leaf values; drop only formatting between
elements.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read XML in the exported app the way the state view does
Keep whitespace-only leaf values and xml:space="preserve" text in the
generated get view; drop only formatting between elements.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): release retained handles with their owners, read older captures
- Release retained-content handles whose close failed through a resource
scope on every exit; a release that fails again reports the exact handle
owners. A read that finished keeps its bytes when only its close failed.
- Accept captures written before state directories were recorded, and say
in the state view's limitations that symlinked state directories may be
misclassed for them.
- Compute each XML element's whitespace context once instead of scanning
siblings for every text run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): boot without the working folder, bound XML, accept mirrors
- Try the captured working directory only when an argument is relative,
and start the app even when that folder is missing.
- Cut the generated XML view at REA's node and depth limits, and keep one
unreadable file from failing data/get.
- Accept Ubuntu 24.04 mirrors, not only the canonical hosts; PPAs and
other repositories are still refused.
- Read seeds through the retained-content owner's new signature.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): state view review round
- Stage a retained -journal beside its database so the SQLite worker
accepts an inactive journal and refuses a hot one; a -wal or -journal
that existed but was not retained (including one omitted at the
baseline) makes the version unreadable.
- Keep tables that read when another fails, and let cancellation and
unfinished cleanup propagate instead of becoming view errors.
- Read text as a key file only when every line parses as one.
- Rank a file named in a window title or selected by the caller as a
document even when the application holds it open; match titles on
file-name boundaries; leave the display name out of dconf matching.
- Bound JSON and key file views (4 MiB, 500 levels, exact numbers) and
report too-large, missing and altered retained content separately.
- Add no version or change while a database stays absent.
- Read staged databases in place instead of copying them again.
- Give Evidence the same limitations as the result.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): export review round
- Load captured dconf keys without D-Bus: install dconf-cli, compile the
existing and captured key files into the user database, and warn on
failure instead of stopping the server.
- Start relative-argument launches in the captured working directory,
the home included, since the framework launches from the app folder;
respell the home itself and option values under it.
- Read views in a thread, skip files over 64 MiB, align the generated
readers with the state view's, and close SQLite connections.
- Never own the home or a shared base folder; map a symlinked state
directory's files back to their home spelling.
- Report omitted files and unretained opened folders.
- Name the module so it cannot shadow another, and keep the class off
the module's own names; derive names the bundle check accepts.
- Write seeds as they are read, and remove an unpublished bundle on
failure.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): capture review round
- Ask the launch process group and token carriers to exit before
killing them, on errors too; report incomplete cleanup with its facts.
- Count descendants of the launch as started by the capture even when
their environment cannot be read; treat a reused pid as a new process.
- Follow symlinked desktop entries; name a flatpak's state after its
application id; match flatpak windows by name only.
- Stop on end of input; refuse empty, duplicate and reserved checkpoint
names; require whitespace after the checkpoint keyword.
- Stay on each walk root's device, and never walk or retain `/` or a
directory containing the home; say so in the limitations.
- Refuse a window manager that does not publish _NET_CLIENT_LIST.
- Remove retained content when a capture fails before it is written.
- Skip reading a file larger than the remaining content budget.
- Record apt's archive origin for each package source.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read an unset client list as no windows when supported
xfwm4 leaves _NET_CLIENT_LIST unset while it manages no windows, so the
capture refused an ordinary desktop with nothing open. Read
_NET_SUPPORTED with the list: an unset list means no windows when the
window manager supports it, and the capture is refused only when it
does not. `xprop`'s "no such atom" output now reads as unset too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): accept packages by apt's archive origin
Accept a package source when apt reports its origin as Ubuntu for a
noble suite, so mirrors count and PPAs or other repositories that merely
have "ubuntu" in their URL do not. Captures made before origins were
recorded fall back to Canonical's own archive hosts. Seed reads report
why retained content could not be used.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): capture and export apps from vendor apt repositories
A closed-source app installed from its vendor's apt repository, such as
Typora, can now be captured and exported as a computer app.
- The capture records each non-Ubuntu repository listing the installed
version (uri, suite, components from apt's sources files, one-line or
deb822) and the key that signs it: the signed-by key, or the trusted
key that gpgv shows verifies the repository's signed index. The key
is retained with the capture.
- The capture records the packages owning the libraries the executable
loads (DT_NEEDED with RUNPATH and the linker cache, through REA's ELF
readers), since a vendor package may not depend on them.
- The export declares a verified vendor repository as an AptSource with
its key, installs a vendor package's library packages, and adds
--no-sandbox to an Electron app's launch, since a container denies
Chromium's sandbox.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): report a locked database at once in data/get
A database the running app holds locked made each data/get wait sqlite's
five-second busy timeout before reporting it. Record the Typora run in an
agentenv computer in the verification.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(desktop): detect key files without splitting the whole text
Read lines one at a time and stop at the first that a key file view
would drop, instead of splitting a file of up to 64 MiB into arrays
before its format is known.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep symlinked homes and state folders under the home in exports
- Respell a path under its state folder's logical spelling only when that
spelling is under the captured home, so a symlinked HOME keeps its
files; pair walked and logical state folders only when the capture
lists the same number, and match the innermost folder first.
- When two files map to one home path, keep the one present before the
capture and name the other in the limitations.
- Read key files in the generated data/get with the state view's rule,
checked against it by running the helper under python3.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): end captures on zombies and keep cleanup owners
- A zombie or dead process no longer keeps the session open.
- Remove only the partial capture file this run created.
- Keep the exact owners of handles and processes whose cleanup failed on
the returned error, so a retry can release them; test the published
capture's cleanup-failure branch with mixed outcomes and retries.
- Release the application once even when the exit checkpoint fails.
- A cancellation before the capture is written removes what it retained.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): vendor package review round
- Match apt sources through `apt-get indextargets`, which covers flat `./`
and `/` suites and gives each release file and sources entry.
- Accept a key whose signature verifies even when the release carries
another signature without a key here; keep only the verifying key from
a keyring holding several (OpenPGP packet splitter).
- Install the package from its repository's release (`name/<release>`),
declare one source per URI and suite, support only the capture host's
architecture, and refuse Ubuntu Pro repositories.
- List only library packages the computer can install; walk libraries only
for vendor installs, with RPATH inherited as ld.so does, normalized
paths, and DT_NEEDED bounded while reading.
- Put --no-sandbox after the program an env wrapper launches; keep the
gpgv folder's owner when its removal fails; respect --max-file-bytes for
keys; wait 0.25 s on a busy SQLite database in data/get.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): keep the vendor spec's gpgv command on one line
A code span broken across lines left `<binary keyring>` for VitePress to
read as an unclosed HTML tag, so the docs build failed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): add a validated task to exported desktop applications
Each capture can now yield one agentenv task in its exported bundle. An
agent writes the task (a prompt plus declarative checks over the app's
data/get views); REA validates it and stays model-free.
- `describe-desktop-application-task` summarises a capture's net change
per file (role, format, views before and after) and drafts checks.
- Checks: file presence, lines present/absent/counted, JSON-pointer values,
SQLite rows and counts. REA keeps a task only if a check fails on the
seed and every check passes on the captured end state; a file that was
not retained at the baseline counts as absent, as the computer starts.
- `export-desktop-application --task` writes tasks/NAME.json (deploy,
agent, skills, recording, prompt, verifier), a compiled verify.py that
reads data/get, and an eval. A boundary test runs the verifier under
python3 and checks it agrees with REA's evaluator on every check kind.
- Docs, spec and skill guidance, with the solve-rate recipe.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): keep the seed test in its own block
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): record the task verifier driven in an agentenv computer
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): record the Typora task run in an agentenv computer
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep task checks bounded, complete and attributable
- line_count counts lines holding a substring (contains) instead of
matching a regular expression, which could stall the verifier on
agent-written text and read differently in Python.
- value_equals of a whole object or array is undecided on a truncated
view.
- The draft keeps a database whose rows changed under an unchanged hash,
and a file whose truncated views match while it changed.
- A task file that cannot be read keeps the reader's detail, and a failed
cleanup keeps the read failure as its primary.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): leave a file with one version out of the task draft
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): update the task verification's test counts
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): record the task solve rate with Claude Code and Sonnet 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): capture and export apps installed from a downloaded .deb
capture-desktop-application --package-file reads the file's control fields
with dpkg-deb and records its SHA-256, size and dependencies when it holds the
installed package; the file itself is never kept. The library walk now also
runs for local installs.
export-desktop-application --package-url declares agentenv's
Download(url, sha256) for that file, its dependencies and library packages as
apt packages, and a run script that installs it with dpkg.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): seed app-named dotfiles and respell UTF-16 seeds
The capture now treats dotfiles in the home named after the application
(.varicad-cfg.ini, .notesrc) as state roots, so a file the app only reads
during the capture is still seeded. The exported seeding respells the
captured home in UTF-16 text files with a byte-order mark, keeping the mark.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): hold readiness until the app's own window shows
An exported app is ready only once a window of its class that the taskbar
lists is shown, so a splash screen of the class, which skips the taskbar, no
longer counts as the app being up.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): record VariCAD captured, exported and run in an agentenv computer
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep exported views strict JSON for infinite and inexact numbers
The exported data/get spells infinite and negative-zero SQLite reals as REA's
SQLite reader does ("Infinity", "-Infinity", "-0"), and refuses a JSON file
holding a number that reading would change (1e999, integers past 2^53, -0,
rounded fractions) with the state view's own error, instead of a view with
an infinity in it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep package-file facts tied to one file and URLs unechoed
- A --package-url refusal never repeats the URL, and credentials are checked
before the scheme.
- The package file's identity must hold between dpkg-deb's read and the hash.
- A failed close keeps its cleanup owner even when the capture is cancelled.
- Readiness also ignores windows typed as splash screens.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): record database logs and keep state changes distinct
- A selected path that is not a directory brings its -wal and -journal as
content roots, so the capture records whether they exist and retains them.
- The state view treats a file as absent only under recorded content roots
and refuses a database whose log presence is unknown.
- JSON changes list containers too, so an array that becomes an object with
the same members is a change.
- Key file entries escape the key as a JSON pointer segment, so [a/b] c and
[a] b/c stay apart.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep watching through unread samples and late workers
- An unreadable window list is unknown, not zero windows, and is named in
the limitations.
- Before ending the session, live run-token carriers no parent link reached
are tracked; an unreadable process table leaves the exit unknown.
- An env prefix's assignments find the program and name its XDG folders.
- flatpak run options that take a value are skipped with it, -- is honoured
and a /arch/branch suffix is dropped from the id.
- State directories include the entry's full id.
- Path options refuse empty values; roots count toward the entry limit.
- A capture file whose close failed stays owned and is retried on discard.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): bound apt sources, keep older captures and cleanup owners
- Sources files parse into one stanza per line or deb822 paragraph with its
URIs and suites kept as lists, so no URI x suite product is built.
- Repository architectures and release are optional, so captures written
before they were recorded parse; the export treats them as unrestricted
and unknown.
- Library sources are matched against every index of a declared
repository's URI, suite and components.
- Two failures side by side keep both causes and both cleanup owners, and a
host file whose close fails reports a lease on its handle.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): match escaped key file entries and suggest containers through members
- The draft's key file lookup uses the state view's escaped `group/key`, so a
key holding `/` or `~` still gets its suggestion.
- JSON and XML changes now list containers too; the draft suggests an added
or removed container through its listed members, and a container only
when none of its members is listed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read relative PATH entries from the launch folder
A program, or an empty or relative PATH entry, is resolved from the folder the
program starts in, as the launch does, so `env PATH=. app` with the entry's
Path still finds the app.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(desktop): compare JSON views on one shared path
JSON and XML changes are found by walking both trees with one shared pointer
path, building a pointer only for a listed value, so memory follows depth
rather than every container's full path. A pointer over 4096 characters is
counted but not listed. A 256-level document with 4000-character keys went
from about 251 MiB of heap to under 1 MiB.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep JSON Lines as text in the exported reader
The exported reader parses the whole text before an inexact number makes it
JSON with an error, so JSON Lines with an integer above 2^53 stay text as in
REA. Over-long integer spellings are unsafe without converting them. The
guide no longer claims large integers keep their value.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep the state-path help free of dash-led words
The CLI alias check reads `-wal` and `-journal` in help text as short flags,
so the help names the write-ahead log and rollback journal instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): never record or export apt source credentials
- Source URIs lose any user name and password as they are parsed, the way apt
splits them, and so do index-target URIs and descriptions and the sources
apt-cache policy lists, so matching works whether or not apt prints them.
- A repository whose entry held credentials is marked
`uri_credentials_removed`, and the export refuses it, or an older capture
whose URI still holds them, with a reason that names no URL.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): look programs up without normalizing their paths
Program paths and PATH entries are joined rather than resolved, so `..` after
a symbolic link means what it means to the launch. The lookup moves to its own
module.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): note inexact JSON numbers with a flag, not a list
The exported reader only needs to know whether any number was inexact, so it
keeps a boolean instead of every rejected spelling.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): refuse older captures that still hold apt credentials
A capture written before credentials were removed counted the credentialed
repository's libraries as installable, so the export refuses it whole and asks
for a new capture rather than declaring packages no source offers. Recorded
policy sources are redacted in export messages too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): keep the credential fixture on an example host
A login on a real host reads as a secret to the commit scanner, so the older
capture's fixture uses apt.example.com like the other credential tests.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): tidy looked-up paths and search relative entries only for env
Empty and `.` segments are dropped from a looked-up program path, which keeps
`/snap/bin/` entries recognisable as snaps, while `..` is still left for the
kernel. Relative and empty PATH entries are searched only in the PATH an `env`
prefix sets, which travels with the exported launch; the host's own PATH does
not.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep excluded and unwalked database logs unknown
The capture records the paths every walk left out, and the state view treats
a log as absent only under content roots the baseline walked completely and
outside those exclusions, so an excluded or unvisited log leaves the database
unread rather than read without it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): judge JSON numbers after duplicate keys settle
The exported reader marks an inexact number instead of flagging it during the
parse, then looks for marks in the parsed value, so a number a later duplicate
key overwrote no longer refuses the file, as in REA.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): redact a repository's policy source when it names it
An older capture whose policy source matched no sources entry kept that text
whole, and the export named the repository by it; the export now names it
without any user name or password.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): never signal the launch group on unknown membership
When the current read cannot observe a carrier's process group, the carrier
is signalled on its own instead of letting an older process-table row grant
a group-wide SIGTERM.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): look programs up in the launch's order and refuse host-relative finds
The lookup searches PATH as the launch does, an empty entry meaning the
launch folder, so inspection and launch name the same program. A program found
only through a relative or empty entry of the host's own PATH is refused,
since the exported computer would not have it; an env prefix's PATH travels
with the command.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): count unchecked paths and keep logs behind them unknown
Each walk counts the paths it could not stat and the directories on another
device it did not enter, and the checkpoint records it as `unchecked_paths`.
The state view treats a log as absent only when the baseline left nothing
unchecked, so a log whose stat failed stays unknown.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(desktop): scan parsed JSON with one iterator per level
The check for marked numbers walks the parsed value with an iterator per
nesting level instead of copying each container's children; a million-entry
array went from about 15.7 MiB to 8.1 MiB of peak traced memory.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): start a relative program from its captured folder
An exported launch whose program path is relative, such as `./app` from an
entry's Path, now changes to the captured working directory first, as it
already did for relative arguments.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): read zipped documents and name XML elements in task checks
- A file that starts as a zip archive gets a `zip` view listing its entries in
archive order, each with its own format and view, within limits on entries,
bytes and expansion; the exported Python reader matches it.
- A pointer segment applied to an array may be a selector, `[KEY=VALUE]` or
`[@KEY=VALUE]` for XML attributes, matching exactly one member, so checks
name elements instead of positions that shift between saves and versions.
- `value_equals` takes an optional numeric `tolerance`, comparing numbers and
numeric strings.
- Changes inside zip views are listed by selector, and the task draft suggests
name-addressed checks.
FreeCAD's `.FCStd` is the first case: a hole cut into a plate is verified from
the zipped `Document.xml` on files from both FreeCAD 0.21 and 1.0.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): record the desktop entry's package and app profiles in any case
- A launch from a desktop entry records the package that owns the entry, so
the export installs the app the entry starts (Writer, not only
libreoffice-common); the program's own package is kept as
executable_package when it differs.
- State folders match the app's names ignoring letter case, as direct
children of the XDG folders and the home, so GIMP's ~/.config/GIMP profile
is seeded and reset; roles follow the same rule.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): read namespaced XML alike in REA and the exported verifier
The exported Python reader now names XML elements and attributes as the
document spells them (svg, text:p, sodipodi:docname, xml:space), with
namespace declarations kept as attributes, which is what REA's view already
produced. Export therefore accepts checks into Inkscape SVGs, LibreOffice
content.xml and namespaced XML inside zips, and the draft suggests them.
expat replaces ElementTree: one pass with namespace processing refuses what
REA's parser refuses, and a streaming pass builds the view within the same
node and depth limits. Text splits at CDATA, comments and processing
instructions as REA's DOM splits it. Fixtures are real Inkscape 1.2.2 and
LibreOffice 24.2 saves.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): install the program's own package and keep unknown owners unknown
- When the desktop entry's package does not own the program it runs, the
export installs the program's package too. The capture records why it
cannot (executable_package_reason), and the export then refuses.
- The export owns a state folder in the letter case the app wrote it, as
the state roles already did, so data/get and data/reset cover a profile
folder the app created during the capture.
- A dpkg-query that fails or exits 2 or higher leaves the package unknown
instead of reading as "no owner".
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep selector checks undecided on partial views and pair members one to one
- A selector check is undecided while an array it searched may be missing
members: a cut zip listing, or a truncated XML or entry view. Both
evaluators agree.
- Diffs pair the members of two versions one to one, so two new elements
can no longer both match the same old one and hide an addition.
- The task draft descends into JSON values and readable entries added
whole, and never suggests an element by its position, last step included.
- The export's namespace refusal also checks XML entries inside the value a
pointer names, such as the whole listing.
- The exported reader keeps REA's 500-level JSON depth limit, so one deep
entry no longer hides the rest of the archive, and treats an entry whose
data runs past its declared size as damaged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): check approved packages against the repositories the export keeps
The capture approved library packages and the program's package against
every repository with a verified key, but the export can still leave a
repository out: its key dropped by the content budget, an architecture
restriction, or a conflicting key. Each repository now records the packages
only it supplies (`supplies`). The export drops stranded library packages
with a limitation, and refuses when the program's own package is stranded.
Captures without `supplies` behave as before.
The install limitations move to appExportInstallLimitations.ts to keep
appExportInstall.ts under the line limit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): compare type changes in place, keep empty values and unread entries honest
- When an array becomes another type, its members are compared with what
their pointers name there, so values that are still present no longer
look removed.
- The draft checks an added empty array or object by equality.
- The exported reader's JSON depth check keeps one iterator per level and
copies no wide list, back to the memory peak before the check.
- An entry whose view could not be read is no `null` value: the draft
suggests no check on it, and a check into its view fails in both
evaluators with the read error.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): decide entry view checks only on entries with a view, and walk JSON depth by level
- A check into an archive entry's view needs an entry read in a format
with a view. A binary or unread entry's `view` of `null` no longer
passes a `null` or absence check, in either evaluator, and the draft
suggests no absence inside an entry that ends without a view.
- REA's JSON depth check keeps one iterator per level instead of queuing a
pair per member, so 800,000 nulls need 18 MiB instead of more than 64.
The helper came with the state view; its Python twin was fixed here.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): refuse an export whose desktop entry runs a program no package owns
When a package owns the desktop entry but no package owns the program it
runs, such as one in ~/.local/bin first on PATH, the capture keeps the
entry's package and records why the program cannot be installed. Export
refuses instead of installing a package that does not bring that program.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): bound zip entry reads, export metadata checks, and diff by the evaluator's pointer rules
- The exported reader inflates a zip entry at most 64 KiB at a time and
stops a byte past its declared size, so a forged small size no longer
holds the whole stream in memory (77.5 MiB to 0.1 MiB on a 32 KiB bomb).
- The namespace refusal applies only to checks that depend on an entry's
XML, so checks on an entry's name, size or digest, or on its absence,
export.
- Diffs find a member's counterpart by the evaluator's pointer rules, so a
selector step across a type change no longer reports a present value
removed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read at most 1,000 zip directory records, and index arrays once per diff
- Both zip readers now read the central directory a record at a time,
keep the first 1,000 file records and count the rest, then hand the
library an archive whose directory holds only those. Directories are
told apart as zip.js does, so the Python reader stops listing
attribute-marked directories as files. 600,000 entries now peak at
1.3 MiB in Python (was 293.6 MiB) and need 24 MiB of heap in REA.
- The exported Python zip reader moves to appExportPythonZip.ts.
- Diffs look members up through an index built once per compared array,
so 20,000 selector keys against 20,000 members take 31 ms, not 5.4 s.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): pick seed and end by checkpoint, and keep the capture host out of bundles
- Export, task export and the task draft take --seed-checkpoint and
--end-checkpoint, so one capture can make the seed in the app and then
the edit; the defaults stay the baseline and the last version.
- Capture reads apt's lists folder and trusted keys from apt-config, so
a user-space apt-get update under APT_CONFIG is found, and starts the
app without AGENTENV_* variables.
- Capture records the host name and user. Export rewrites user@host, the
host name as a whole name and the captured home in text, UTF-16 and
dconf seeds, ships SEED_HOME=/home/user instead of the capture user's
home, lists scrubbed and unscrubbable seeds, and refuses checks that
name the host.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): check tasks against the shipped seed and scrub JSON and XML by value
- Task validation reads the seed as the bundle ships it, after the scrub,
so a task whose end value the scrub writes into the seed is refused.
- JSON strings and XML text, attribute values, comments and CDATA are
scrubbed as their decoded values and rewritten only when they change,
so syntax stays valid and escaped identities are found.
- One leftover search covers every shipped file's path and contents,
generated module and dconf keys included; the strict whole-name rule
is kept for replacing only.
- A host named localhost is never replaced, but user@localhost is.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): resolve a record's selector keys together, and follow Zip64 locators without sentinels
- The diff resolves all of a record's selector-shaped keys in one lookup:
conditions are normalized per field, selectors grouped by field set,
and only the values asked for are kept, so retained memory grows with
the keys, not members times field sets (68 MB to 5.5 MB, 76 MB to 4.9 MB
on the reported cases).
- Both zip readers follow a Zip64 locator just before the end record
whatever the classic record holds, as zipfile does, so valid Zip64
archives with ordinary classic values read again. The exported
module's synthetic tail carries a real Zip64 end record, so bytes that
only look like a locator no longer make zipfile refuse it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): scrub JSON values only, with a scanner whose stack does not grow with string length
JSON seeds are walked one string at a time, finding each closing quote
with indexOf and a backslash count instead of a backtracking regex, so a
16,000,000-character string no longer overflows the stack. Member names
are never rewritten, so two keys cannot collide; a name holding the host
leaves the file in unscrubbed_files.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): scrub dconf and UTF-16 seeds by format, search check values for the host, and gate checks on the selected ends
- dconf: the host scrub ran over the whole key file, so on a host named
`true` `wrap=true` became an invalid `wrap=localhost`. Only decoded GVariant
string values are scrubbed now; keys, groups, bytestrings, dictionary keys
and object paths ship as captured and are reported.
- Task identity: the host search read the serialized check, whose field
names (`file`, `kind`) and default ids matched hosts of those names. It now
reads the check's string values only.
- Value checks: the text-file refusal read every recorded version; it now
reads only the shipped seed and the end.
- UTF-16 seeds: the plain scrubber renamed JSON keys and XML names; the
decoded text's format now picks the scrubber, keeping the byte order mark.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): pair members by identity first, build selectors lazily, and refuse oversized and deep text before building a view
- Pairing: an element with an identifier (name, id, xid, *:id) pairs only by
it, so an unchanged id still names an edited element instead of a removal
and an addition, and two objects of one type are never paired as a rename.
Selectors keep the tag apart from each attribute condition and spell only
the chosen one, so a long tag with many attributes no longer multiplies.
- Size: REA's detection and the exported reader decide JSON with a scanner
that builds no value, and refuse a view over the parsed-view limit before
building it, in files and zip entries alike.
- Deep text: the exported reader confirms the whole text is JSON before
reporting a nesting depth error, as REA reads it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): count a selector's matches over every sibling while identified elements choose only their identifiers
Identity-first pairing also dropped an identified sibling's other
attributes from the counts that prove a selector names one member, so a
shared attribute such as `color` or `fill` could be chosen though it matched
two siblings: the diff reported present elements removed and drafts lost
their checks. Counting now covers every attribute of every sibling; only the
choice is limited to an identified element's tag and identifiers.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): tell a UTF-16 seed's format without building it, and scrub XML without one piece per token
Choosing the scrubber for a decoded UTF-16 seed ran the full state
detection, which built the whole XML document (or JSON value), so a large
seed within the capture limit could exhaust memory at export. A syntax pass
now decides the format (the JSON token scan, and the XML parser's events
keeping only the open elements), and the XML scrubber copies out only the
spans it changes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(filesystem): settle a Windows ChangeTime before recording a baseline
Windows advances a file's ChangeTime shortly after it is written while
security components attach catalog and file-hash extended attributes. A
baseline captured during that advance disagrees with the next observation,
so an untouched file was reported as changed: "Root artifact changed before
inventory" and "Selected input changed while being read".
Dropping ctime on Windows is not an option because it is the only field
that catches a same-size rewrite whose modification time was restored.
Instead, keep ctime at full strength and stop capturing it mid-flight: on
win32 a ChangeTime still inside the drift window is observed again until two
consecutive observations agree, and the settled value becomes the baseline.
Applied where a baseline is captured and later compared: openRegularFile's
admitted metadata and the inventory root snapshot. The exhaustive process
capture walk is left alone, since settling there would delay every freshly
written file in the tree.
Refs #8975
Co-Authored-By: Claude Sonnet 5 <[email protected]>
* fix(filesystem): wait out the drift window instead of a repeated value
Settling on two matching observations was not enough. Two reads taken before
Windows performs its delayed ChangeTime update agree with each other and
disagree with every later one, so an unchanged file still failed a later
check: the reported drift lands 0.3-1.0 s after the write, well past a pair
of reads 25 ms apart.
Decide on the observation's age in the drift window instead, and keep
observing until the ChangeTime itself has aged out. Both the first read and
the empty-observation path now go through the same window check.
The window-exit test could not fail before: its clock was frozen, so the
window never closed and the exit branch never ran. Advance the clock while
sleeping and cover a match that precedes a delayed update.
Refs #8975
Co-Authored-By: Claude Sonnet 5 <[email protected]>
* fix(filesystem): configure Windows ChangeTime settle limits
REA_WINDOWS_CHANGE_TIME_DRIFT_MS, REA_WINDOWS_CHANGE_TIME_SETTLE_POLL_MS, and REA_WINDOWS_CHANGE_TIME_SETTLE_MAX_POLLS keep today's 2000 ms, 25 ms, and 40-poll defaults. Invalid values name the setting, current value, source, and range, and say to restart REA.
* fix(filesystem): settle on the selected environment and stop when cancelled
`settleChangeTimeBaseline` resolved its limits from `process.env`, but the
inventory already carries the environment its owner selected. A Windows
runtime with explicit settle settings therefore ran on ambient values or
the defaults, and an invalid ambient value could fail a runtime whose own
configuration was valid. `scanCanonical` now passes `options.environment`
through.
The wait loop also never observed the owning operation's signal, so a
cancelled inventory could keep polling for the whole drift window — up to
a minute at the configured maximum — and closing the artifact scope waited
on it. The loop now checks the signal before each poll and after the
sleep, and both call sites pass theirs.
Refs morluto/rea#8979
* fix(filesystem): keep the caller's typed error on cancellation
Checking the signal was not enough: `throwIfAborted` threw the raw
`signal.reason`, which `openRegularFile` passed through unchanged. Its
callers recognise cancellation through `RegularFileOperationCancelled`, so
a cancelled admission surfaced as an `ArtifactReaderFailure` with reason
`io` instead of a cancellation.
Let the seam supply the error a caller matches on, and have
`openRegularFile` pass its own cancellation type.
Refs morluto/rea#8979
* fix(filesystem): keep inventory cancellation typed during ChangeTime settle
scanCanonical passed the abort signal into settleChangeTimeBaseline without a cancellationError, so an aborted inventory threw the raw AbortError and analyze reported execution_failure. Inventory now throws ArtifactReaderFailure reason cancelled, which callers already translate as cancellation.
* fix(filesystem): skip ChangeTime settling for private PYZ images
External admission still waits out a fresh Windows ChangeTime. A private image REA just wrote is recorded as written, so a compressed PYZ spool does not add about a second per image.
---------
Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: Claude Sonnet 5 <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Add per-connection discovery that loads canonical named tools on demand while preserving the default full catalog. Bound discovery replies before changing visibility, preserve family exclusions and Evidence workflows, and reset visibility on HTTP reconnect.
Verified with focused protocol tests and real Codex CLI artifact investigations; token costs remain task-dependent, so the profile is opt-in.
* feat(artifacts): select extraction entries and output destination
Refs morluto/rea#3490
* fix(artifacts): omit absent selection from execution parameters
* fix(artifacts): protect source directories and verify public selection
* fix(artifacts): keep optional selection inputs JSON-safe
* fix(artifacts): separate reader ownership from selected destinations
Validate complete reader inventory independently from collisions among selected output entries. Use occurrence-parent ownership instead of filename prefixes so independently owned PyInstaller entries remain selectable while nested-reader members remain refused. Validate advertised MCP schemas through the shared acceptance helper and add real PyInstaller/ASAR and excluded-entry regressions.
* fix(artifacts): advertise absolute extraction destinations
Reuse the shared host-absolute local path schema instead of an opaque refinement so SDK-converted JSON Schema preserves the runtime constraint. Extend the advertised-path and MCP-handler regression matrix for extract_artifact.output_root.
* fix(contracts): advertise unique extraction occurrence IDs
---------
Co-authored-by: gensokyo-dev <[email protected]>
* refactor(mcp): share official tool evidence registration
Route official tools through registerEvidenceTools and remove the duplicate
execution, progress, and Evidence delivery pipeline.
Preserve relationship coverage in the shared path and reuse the existing
source Evidence recording helper.
Validation: typecheck and 29 focused MCP tests passed.
Real native providers were not exercised.
* fix(mcp): support retained pseudocode summaries and paging
Allow procedure_pseudo_code to return an opt-in summary linked to its
retained complete Evidence. Extend inspect_analysis_view to page the
original pseudocode without repeating provider analysis.
Preserve default complete output and exclude delivery detail from
provider parameters and source Evidence identity.
Add regression coverage for large results, Unicode, null and empty
output, missing retention, transport-overflow recovery, and CLI paging.
Validation: 178 focused tests, build, check, docs:check, and test discovery
passed. Real stdio simulation reconstructed 98,000 code units exactly.
Real Ghidra, iTunes, and Claude Code verification remains unavailable.
Fixes#5032
* test(mcp): scope pseudocode contract lookup to official tools
* feat(desktop): capture and export apps installed from a downloaded .deb (#8954)
* feat(desktop): capture a Linux desktop application in use
Add `capture-desktop-application`, a CLI-only, Linux and X11 capture that
launches an installed desktop application, watches it while it is used and
writes capture Evidence plus SHA-256 addressed content:
- inventory: desktop entry, launch command, executable, package and its
apt sources (or snap, flatpak, AppImage), Electron markers and candidate
state directories;
- session: the launched process tree, client windows from xprop with the
first application window time and a window match, and files held open;
- state: a stat walk of the home directory before launch, at checkpoints
read from stdin, at stop and after exit, with created, modified, deleted
and retyped paths and the content of changed files and state directories.
It refuses non-Linux hosts, missing DISPLAY or xprop, a non-empty output
folder and an application whose window is already open, and signals only
processes it started.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): own launched processes, confirm walk coverage and keep cancelled captures
- Launch through the owned provider path with a run token; release with
SIGTERM, then the token-verified group cleanup, after every session.
Processes without the token are never signalled; each records whether it
carries it, and the session records whether cleanup was verified.
- Report creations and deletions only where a walk listed the nearest
existing directory; count the rest as unconfirmed.
- Retain a file only when it is still the walked version, through owned
no-follow reads; charge the budget only for new content; create files
exclusively in a private output folder owned by the caller.
- Walk content roots outside the home directory, compare paths in their
physical spelling, and run executables from the caller's directory.
- Refuse when the window list cannot be read, re-evaluate window ownership
each sample, handle one queued request per sample with a bounded stdin
queue, and write the capture with end reason `cancelled` on interruption.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep owned cleanup failures and fresh digests
- Allow a capture cancelled before any requested checkpoint to hold only the
baseline.
- Retry an incomplete stop once; if cleanup is still unverified, write the
capture and fail with the run token and process group as cleanup
resources.
- Classify path changes after a walk (ENOTDIR and other selected-file
errors) as changed_since_walk instead of failing the capture.
- Treat a non-directory ancestor as proof that nothing existed beneath it.
- Report a before digest only for the file version it was read from.
- Derive stderr_truncated from stderr's own byte counts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* wip(desktop): state view over a capture folder
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep failed file-handle closes owned for retry
Content reads pass retainFailedClose, so a handle whose close fails stays
owned by the content store; the capture retries those closes after it is
written and fails with the output folder as the cleanup resource if one
still cannot be closed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): read a captured application's state
Add inspect-desktop-application-state, a CLI command that reads a capture
folder into the application's state: every retained or changed file with
its role and reasons, a typed JSON view of each version (SQLite through the
shared worker, dconf through a private dconf dump, JSON, XML, key files,
text) and the kinds of change between versions (lines, values, keys, rows).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): report process and handle cleanup failures together
When both the launched processes and a retained file's handle remain
unresolved after the capture is written, one cleanup error now lists both
reasons and both resource sets.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): export a captured application as an agentenv computer app
Add export-desktop-application, which writes an agentenv bundle with one
computer_app environment from a capture folder and its state view: the
apt package, a boot launch matched on the captured window class with a
window ready check, owned paths from the state roles, a seed of the
baseline files, data hooks that call agentenv's add/get/reset helpers
with a typed JSON view on get, a skill, a README and a deploy smoke task.
The desktop CLI commands now load their services lazily.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep state views faithful to the capture
- Read key files, XML attributes, dconf groups and SQLite tables into
dictionaries without inherited properties, so captured keys such as
__proto__ stay data.
- Version databases with their write-ahead log as of each checkpoint, and
stage only digest-verified bytes for the SQLite and dconf readers.
- Detect the format per version, report format changes, keep JSON null and
empty containers as values, keep XML text and preserved whitespace, and
mark truncated views (including XML depth) and comparisons of them.
- Number versions and changes by checkpoint index, decide baseline
presence from the captured type, and keep tagged SQLite integers, text
bytes and blobs distinct.
- Carry the capture's limitations and omitted content forward, match state
directories by their home path, index changes once per capture, keep
failed handle closes owned, and finish the CLI through the shared owner.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep coverage, logs and handles honest in the state view
- Read coverage flags only from formats whose readers write them, so JSON
fields named truncated stay data.
- Fail a SQLite version whose write-ahead log existed but was not retained
or does not match its digest, instead of reading the database alone.
- Close retained content on every exit, including cancellation, and report
a close that still fails together with the primary failure.
- Finish the CLI with the canonical operation name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): export only what the computer can install and seed
- Refuse packages not from the Ubuntu 24.04 archive, since the computer
installs by name from that archive.
- Start the launch command in the captured working directory.
- Respell the home path only in files the state view read as text.
- Report files that were not retained instead of dropping them silently.
- Load captured dconf keys for apps that own no files.
- Keep home-level files as owned paths; rename reserved Python class names.
- Mark truncated SQLite and text views in the class's get view, and read
file types the way the state view does.
- Release the output tree through a resource scope, and read seeds through
an owner closed on every exit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep staged folders owned, symlinked state roots and XML values
- Stage database bytes in a private runtime root released through a
resource scope, so a failed removal keeps its owner and is reported with
the read's outcome.
- Report cleanup facts and owners from desktop failures instead of
wrapping them as a plain adapter error.
- Record the state directories as walked, in their physical spelling, and
match roles against both spellings, so files in a symlinked state
directory are not mistaken for selected documents.
- Keep whitespace-only XML leaf values; drop only formatting between
elements.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read XML in the exported app the way the state view does
Keep whitespace-only leaf values and xml:space="preserve" text in the
generated get view; drop only formatting between elements.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): release retained handles with their owners, read older captures
- Release retained-content handles whose close failed through a resource
scope on every exit; a release that fails again reports the exact handle
owners. A read that finished keeps its bytes when only its close failed.
- Accept captures written before state directories were recorded, and say
in the state view's limitations that symlinked state directories may be
misclassed for them.
- Compute each XML element's whitespace context once instead of scanning
siblings for every text run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): boot without the working folder, bound XML, accept mirrors
- Try the captured working directory only when an argument is relative,
and start the app even when that folder is missing.
- Cut the generated XML view at REA's node and depth limits, and keep one
unreadable file from failing data/get.
- Accept Ubuntu 24.04 mirrors, not only the canonical hosts; PPAs and
other repositories are still refused.
- Read seeds through the retained-content owner's new signature.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): state view review round
- Stage a retained -journal beside its database so the SQLite worker
accepts an inactive journal and refuses a hot one; a -wal or -journal
that existed but was not retained (including one omitted at the
baseline) makes the version unreadable.
- Keep tables that read when another fails, and let cancellation and
unfinished cleanup propagate instead of becoming view errors.
- Read text as a key file only when every line parses as one.
- Rank a file named in a window title or selected by the caller as a
document even when the application holds it open; match titles on
file-name boundaries; leave the display name out of dconf matching.
- Bound JSON and key file views (4 MiB, 500 levels, exact numbers) and
report too-large, missing and altered retained content separately.
- Add no version or change while a database stays absent.
- Read staged databases in place instead of copying them again.
- Give Evidence the same limitations as the result.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): export review round
- Load captured dconf keys without D-Bus: install dconf-cli, compile the
existing and captured key files into the user database, and warn on
failure instead of stopping the server.
- Start relative-argument launches in the captured working directory,
the home included, since the framework launches from the app folder;
respell the home itself and option values under it.
- Read views in a thread, skip files over 64 MiB, align the generated
readers with the state view's, and close SQLite connections.
- Never own the home or a shared base folder; map a symlinked state
directory's files back to their home spelling.
- Report omitted files and unretained opened folders.
- Name the module so it cannot shadow another, and keep the class off
the module's own names; derive names the bundle check accepts.
- Write seeds as they are read, and remove an unpublished bundle on
failure.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): capture review round
- Ask the launch process group and token carriers to exit before
killing them, on errors too; report incomplete cleanup with its facts.
- Count descendants of the launch as started by the capture even when
their environment cannot be read; treat a reused pid as a new process.
- Follow symlinked desktop entries; name a flatpak's state after its
application id; match flatpak windows by name only.
- Stop on end of input; refuse empty, duplicate and reserved checkpoint
names; require whitespace after the checkpoint keyword.
- Stay on each walk root's device, and never walk or retain `/` or a
directory containing the home; say so in the limitations.
- Refuse a window manager that does not publish _NET_CLIENT_LIST.
- Remove retained content when a capture fails before it is written.
- Skip reading a file larger than the remaining content budget.
- Record apt's archive origin for each package source.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read an unset client list as no windows when supported
xfwm4 leaves _NET_CLIENT_LIST unset while it manages no windows, so the
capture refused an ordinary desktop with nothing open. Read
_NET_SUPPORTED with the list: an unset list means no windows when the
window manager supports it, and the capture is refused only when it
does not. `xprop`'s "no such atom" output now reads as unset too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): accept packages by apt's archive origin
Accept a package source when apt reports its origin as Ubuntu for a
noble suite, so mirrors count and PPAs or other repositories that merely
have "ubuntu" in their URL do not. Captures made before origins were
recorded fall back to Canonical's own archive hosts. Seed reads report
why retained content could not be used.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): capture and export apps from vendor apt repositories
A closed-source app installed from its vendor's apt repository, such as
Typora, can now be captured and exported as a computer app.
- The capture records each non-Ubuntu repository listing the installed
version (uri, suite, components from apt's sources files, one-line or
deb822) and the key that signs it: the signed-by key, or the trusted
key that gpgv shows verifies the repository's signed index. The key
is retained with the capture.
- The capture records the packages owning the libraries the executable
loads (DT_NEEDED with RUNPATH and the linker cache, through REA's ELF
readers), since a vendor package may not depend on them.
- The export declares a verified vendor repository as an AptSource with
its key, installs a vendor package's library packages, and adds
--no-sandbox to an Electron app's launch, since a container denies
Chromium's sandbox.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): report a locked database at once in data/get
A database the running app holds locked made each data/get wait sqlite's
five-second busy timeout before reporting it. Record the Typora run in an
agentenv computer in the verification.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(desktop): detect key files without splitting the whole text
Read lines one at a time and stop at the first that a key file view
would drop, instead of splitting a file of up to 64 MiB into arrays
before its format is known.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep symlinked homes and state folders under the home in exports
- Respell a path under its state folder's logical spelling only when that
spelling is under the captured home, so a symlinked HOME keeps its
files; pair walked and logical state folders only when the capture
lists the same number, and match the innermost folder first.
- When two files map to one home path, keep the one present before the
capture and name the other in the limitations.
- Read key files in the generated data/get with the state view's rule,
checked against it by running the helper under python3.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): end captures on zombies and keep cleanup owners
- A zombie or dead process no longer keeps the session open.
- Remove only the partial capture file this run created.
- Keep the exact owners of handles and processes whose cleanup failed on
the returned error, so a retry can release them; test the published
capture's cleanup-failure branch with mixed outcomes and retries.
- Release the application once even when the exit checkpoint fails.
- A cancellation before the capture is written removes what it retained.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): vendor package review round
- Match apt sources through `apt-get indextargets`, which covers flat `./`
and `/` suites and gives each release file and sources entry.
- Accept a key whose signature verifies even when the release carries
another signature without a key here; keep only the verifying key from
a keyring holding several (OpenPGP packet splitter).
- Install the package from its repository's release (`name/<release>`),
declare one source per URI and suite, support only the capture host's
architecture, and refuse Ubuntu Pro repositories.
- List only library packages the computer can install; walk libraries only
for vendor installs, with RPATH inherited as ld.so does, normalized
paths, and DT_NEEDED bounded while reading.
- Put --no-sandbox after the program an env wrapper launches; keep the
gpgv folder's owner when its removal fails; respect --max-file-bytes for
keys; wait 0.25 s on a busy SQLite database in data/get.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): keep the vendor spec's gpgv command on one line
A code span broken across lines left `<binary keyring>` for VitePress to
read as an unclosed HTML tag, so the docs build failed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): add a validated task to exported desktop applications
Each capture can now yield one agentenv task in its exported bundle. An
agent writes the task (a prompt plus declarative checks over the app's
data/get views); REA validates it and stays model-free.
- `describe-desktop-application-task` summarises a capture's net change
per file (role, format, views before and after) and drafts checks.
- Checks: file presence, lines present/absent/counted, JSON-pointer values,
SQLite rows and counts. REA keeps a task only if a check fails on the
seed and every check passes on the captured end state; a file that was
not retained at the baseline counts as absent, as the computer starts.
- `export-desktop-application --task` writes tasks/NAME.json (deploy,
agent, skills, recording, prompt, verifier), a compiled verify.py that
reads data/get, and an eval. A boundary test runs the verifier under
python3 and checks it agrees with REA's evaluator on every check kind.
- Docs, spec and skill guidance, with the solve-rate recipe.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): keep the seed test in its own block
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): record the task verifier driven in an agentenv computer
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): record the Typora task run in an agentenv computer
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep task checks bounded, complete and attributable
- line_count counts lines holding a substring (contains) instead of
matching a regular expression, which could stall the verifier on
agent-written text and read differently in Python.
- value_equals of a whole object or array is undecided on a truncated
view.
- The draft keeps a database whose rows changed under an unchanged hash,
and a file whose truncated views match while it changed.
- A task file that cannot be read keeps the reader's detail, and a failed
cleanup keeps the read failure as its primary.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): leave a file with one version out of the task draft
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): update the task verification's test counts
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): record the task solve rate with Claude Code and Sonnet 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): capture and export apps installed from a downloaded .deb
capture-desktop-application --package-file reads the file's control fields
with dpkg-deb and records its SHA-256, size and dependencies when it holds the
installed package; the file itself is never kept. The library walk now also
runs for local installs.
export-desktop-application --package-url declares agentenv's
Download(url, sha256) for that file, its dependencies and library packages as
apt packages, and a run script that installs it with dpkg.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): seed app-named dotfiles and respell UTF-16 seeds
The capture now treats dotfiles in the home named after the application
(.varicad-cfg.ini, .notesrc) as state roots, so a file the app only reads
during the capture is still seeded. The exported seeding respells the
captured home in UTF-16 text files with a byte-order mark, keeping the mark.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): hold readiness until the app's own window shows
An exported app is ready only once a window of its class that the taskbar
lists is shown, so a splash screen of the class, which skips the taskbar, no
longer counts as the app being up.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): record VariCAD captured, exported and run in an agentenv computer
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep exported views strict JSON for infinite and inexact numbers
The exported data/get spells infinite and negative-zero SQLite reals as REA's
SQLite reader does ("Infinity", "-Infinity", "-0"), and refuses a JSON file
holding a number that reading would change (1e999, integers past 2^53, -0,
rounded fractions) with the state view's own error, instead of a view with
an infinity in it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep package-file facts tied to one file and URLs unechoed
- A --package-url refusal never repeats the URL, and credentials are checked
before the scheme.
- The package file's identity must hold between dpkg-deb's read and the hash.
- A failed close keeps its cleanup owner even when the capture is cancelled.
- Readiness also ignores windows typed as splash screens.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): record database logs and keep state changes distinct
- A selected path that is not a directory brings its -wal and -journal as
content roots, so the capture records whether they exist and retains them.
- The state view treats a file as absent only under recorded content roots
and refuses a database whose log presence is unknown.
- JSON changes list containers too, so an array that becomes an object with
the same members is a change.
- Key file entries escape the key as a JSON pointer segment, so [a/b] c and
[a] b/c stay apart.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep watching through unread samples and late workers
- An unreadable window list is unknown, not zero windows, and is named in
the limitations.
- Before ending the session, live run-token carriers no parent link reached
are tracked; an unreadable process table leaves the exit unknown.
- An env prefix's assignments find the program and name its XDG folders.
- flatpak run options that take a value are skipped with it, -- is honoured
and a /arch/branch suffix is dropped from the id.
- State directories include the entry's full id.
- Path options refuse empty values; roots count toward the entry limit.
- A capture file whose close failed stays owned and is retried on discard.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): bound apt sources, keep older captures and cleanup owners
- Sources files parse into one stanza per line or deb822 paragraph with its
URIs and suites kept as lists, so no URI x suite product is built.
- Repository architectures and release are optional, so captures written
before they were recorded parse; the export treats them as unrestricted
and unknown.
- Library sources are matched against every index of a declared
repository's URI, suite and components.
- Two failures side by side keep both causes and both cleanup owners, and a
host file whose close fails reports a lease on its handle.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): match escaped key file entries and suggest containers through members
- The draft's key file lookup uses the state view's escaped `group/key`, so a
key holding `/` or `~` still gets its suggestion.
- JSON and XML changes now list containers too; the draft suggests an added
or removed container through its listed members, and a container only
when none of its members is listed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read relative PATH entries from the launch folder
A program, or an empty or relative PATH entry, is resolved from the folder the
program starts in, as the launch does, so `env PATH=. app` with the entry's
Path still finds the app.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(desktop): compare JSON views on one shared path
JSON and XML changes are found by walking both trees with one shared pointer
path, building a pointer only for a listed value, so memory follows depth
rather than every container's full path. A pointer over 4096 characters is
counted but not listed. A 256-level document with 4000-character keys went
from about 251 MiB of heap to under 1 MiB.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep JSON Lines as text in the exported reader
The exported reader parses the whole text before an inexact number makes it
JSON with an error, so JSON Lines with an integer above 2^53 stay text as in
REA. Over-long integer spellings are unsafe without converting them. The
guide no longer claims large integers keep their value.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep the state-path help free of dash-led words
The CLI alias check reads `-wal` and `-journal` in help text as short flags,
so the help names the write-ahead log and rollback journal instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): never record or export apt source credentials
- Source URIs lose any user name and password as they are parsed, the way apt
splits them, and so do index-target URIs and descriptions and the sources
apt-cache policy lists, so matching works whether or not apt prints them.
- A repository whose entry held credentials is marked
`uri_credentials_removed`, and the export refuses it, or an older capture
whose URI still holds them, with a reason that names no URL.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): look programs up without normalizing their paths
Program paths and PATH entries are joined rather than resolved, so `..` after
a symbolic link means what it means to the launch. The lookup moves to its own
module.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): note inexact JSON numbers with a flag, not a list
The exported reader only needs to know whether any number was inexact, so it
keeps a boolean instead of every rejected spelling.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): refuse older captures that still hold apt credentials
A capture written before credentials were removed counted the credentialed
repository's libraries as installable, so the export refuses it whole and asks
for a new capture rather than declaring packages no source offers. Recorded
policy sources are redacted in export messages too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): keep the credential fixture on an example host
A login on a real host reads as a secret to the commit scanner, so the older
capture's fixture uses apt.example.com like the other credential tests.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): tidy looked-up paths and search relative entries only for env
Empty and `.` segments are dropped from a looked-up program path, which keeps
`/snap/bin/` entries recognisable as snaps, while `..` is still left for the
kernel. Relative and empty PATH entries are searched only in the PATH an `env`
prefix sets, which travels with the exported launch; the host's own PATH does
not.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep excluded and unwalked database logs unknown
The capture records the paths every walk left out, and the state view treats
a log as absent only under content roots the baseline walked completely and
outside those exclusions, so an excluded or unvisited log leaves the database
unread rather than read without it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): judge JSON numbers after duplicate keys settle
The exported reader marks an inexact number instead of flagging it during the
parse, then looks for marks in the parsed value, so a number a later duplicate
key overwrote no longer refuses the file, as in REA.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): redact a repository's policy source when it names it
An older capture whose policy source matched no sources entry kept that text
whole, and the export named the repository by it; the export now names it
without any user name or password.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): never signal the launch group on unknown membership
When the current read cannot observe a carrier's process group, the carrier
is signalled on its own instead of letting an older process-table row grant
a group-wide SIGTERM.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): look programs up in the launch's order and refuse host-relative finds
The lookup searches PATH as the launch does, an empty entry meaning the
launch folder, so inspection and launch name the same program. A program found
only through a relative or empty entry of the host's own PATH is refused,
since the exported computer would not have it; an env prefix's PATH travels
with the command.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): count unchecked paths and keep logs behind them unknown
Each walk counts the paths it could not stat and the directories on another
device it did not enter, and the checkpoint records it as `unchecked_paths`.
The state view treats a log as absent only when the baseline left nothing
unchecked, so a log whose stat failed stays unknown.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(desktop): scan parsed JSON with one iterator per level
The check for marked numbers walks the parsed value with an iterator per
nesting level instead of copying each container's children; a million-entry
array went from about 15.7 MiB to 8.1 MiB of peak traced memory.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): start a relative program from its captured folder
An exported launch whose program path is relative, such as `./app` from an
entry's Path, now changes to the captured working directory first, as it
already did for relative arguments.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(electron): reject non-directory scenario roots before launch (#8967)
* fix(contracts): align managed example MethodDef scope (#8966)
* docs(debugger): document configurable session limits (#8963)
* docs(debugger): document configurable session limits
* docs(debugger): clarify GDB control command deadlines
* docs(debugger): qualify configurable GDB output budget
* docs(debugger): clarify one-shot CLI settings scope
* docs(debugger): preserve repository line endings
* docs(testing): explain npm launcher selection in PowerShell (#8962)
* docs(mcp): explain PowerShell launch environment scope (#8961)
* docs(mcp): explain PowerShell launch environment scope
* docs(mcp): qualify saved setup selections and OpenCode environment
* fix(native): declare temporary filesystem writes for inspect_macho (#8959)
* fix(environment): retain ID ordering with missing clocks (#8957)
* fix(environment): retain ID ordering with missing clocks
* fix(environment): compare known creation clocks across gaps
---------
Co-authored-by: hhang <[email protected]>
* fix(gpu): distinguish ELF string scan limits from corruption (#8955)
Co-authored-by: hhang <[email protected]>
* fix(setup): replace the fixed Hopper download deadline with a stall timeout (#7150)
* fix(setup): replace the fixed Hopper download deadline with a stall timeout
Hopper package downloads aborted after a fixed 120 s, which was also the
largest deadline the download owner accepted. The current 32 MB Linux
package needs about 132 s at the reporter's sustained 245 KB/s, so setup
could never finish on that connection, and the timeout surfaced only as
Node's generic abort message.
Abort a download when no response bytes arrive for 30 s, or when it
outlives a 15-minute overall bound, with a tagged
HopperDownloadTimeoutError that names the URL, the limit reached and the
bytes received. Linux and macOS setup already relay a thrown download
error's message; the Linux publisher now keeps it for release metadata
and public key fetches instead of a generic verification failure.
Metadata and key fetches keep their 10 s bound.
Refs #7077
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(setup): keep the macOS release feed on the metadata limit
The stall-timeout change left the macOS release feed fetch on the
package download bounds. That fetch could then wait up to 15 minutes
instead of the 120 s it had, while the installation guide says release
metadata keeps a 10 s limit. Only the Linux publisher owned that limit.
Move the 10 s metadata limit into HopperDownload.ts beside the package
bounds. The Linux feed and key fetches use it, and the macOS installer
now passes it for its feed fetch through the installer host.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(cli): parse environment-serve --port and --host like rea mcp --http (#7092)
* fix(cli): parse environment-serve --port and --host like rea mcp --http
environment-serve declared --port as a number, so incur coerced the flag text with Number(): an empty --port= bound an ephemeral port instead of failing, and 0x1F90 or 1e3 were reinterpreted. --host accepted whitespace. docs/cli.md says these flags resolve like rea mcp --http, which accepts only decimal port text and a non-empty trimmed host. The flags now use the same rules and are refused before binding.
* refactor(cli): reuse the shared MCP listener port and host schemas
* fix(adb): separate unresolvable paths and host capacity in adb spawn failures (#6188)
* fix(adb): name unexecutable adb paths in readiness refusals
When REA_ADB_PATH or the PATH-resolved adb is a directory or a file the
host refuses to execute, the version probe fails at spawn with EACCES or
EPERM. That fell through to the generic "did not report a usable version"
refusal, which named neither the path nor the cause.
Classify those spawn errors as binary_not_executable (provider rejection
executable_missing), name the selected path and errno, and give
remediation for the environment, PATH and host-policy cases. The generic
fallback now names the selected path too.
Fixes#5689
* fix(process): admit synchronous spawn errors as captured failures
Node throws spawn errors other than EACCES, EAGAIN, EMFILE, ENFILE and
ENOENT synchronously from execFile. execFileBytes called execFile inside
its Promise executor, so ENOTDIR or EPERM became a rejection without
capture fields and execFileOutputFailure returned undefined. Consumers
that classify by failure code, such as the adb version probe, could not
see the code, which left the adb EPERM branch unreachable.
Catch synchronous spawn errno errors at the capture boundary and attach
the same empty capture an asynchronous spawn failure carries. Argument
validation errors stay unadmitted.
The adb version probe now reports an ENOTDIR selected path, such as a
REA_ADB_PATH whose parent is a regular file, as a missing binary with
the filesystem error code instead of the generic version refusal.
* fix(process): expose spawn failures from the capture owner
execFileBytes knows when a command never started, but
ExecFileOutputFailure did not say so, so consumers reread raw syscall
and code fields and disagreed. Mark both the synchronous throw and an
asynchronous `spawn <path>` error with `spawnFailed: true`, and set
`path` on the synchronous error to match the asynchronous shape.
Argument validation errors stay unadmitted.
Consumers now use the owner field:
- inspectAdbClient never reports a spawn errno as `exit_code`. ENOENT,
ENOTDIR, ELOOP and ENAMETOOLONG are `binary_missing`; EACCES and EPERM
stay `binary_not_executable`; any other spawn errno, such as EINVAL,
is `binary_not_executable` with a path-naming "could not be started"
message. Each records `filesystem_error` and `path_source`. EACCES
remediation also names parent directory search permission, and EPERM
remediation no longer names host policy as the only cause.
- DarwinProcessRunTokenReader tags every not-started compiler as
`spawn_failed`, not only ENOENT. EACCES, EPERM and ENOTDIR were
previously `failed`.
- NativeDmgArtifactReader uses one not-started rule for both
`processNotStarted` (previously `spawn ` only, so synchronous errors
were missed) and the ENOENT unavailable/io split (previously any
`spawn` prefix). Its fake hosts now reject with real execFileOutput
spawn failures.
* fix(process): classify spawn failures consistently across consumers
Admit synchronous and asynchronous spawn errnos through one helper that
requires an errno and a `spawn` or `spawn <path>` syscall. The
synchronous shape now also gains `spawnargs`, so both shapes match.
Use the owner's `spawnFailed` fact in the remaining consumers:
- inspectAdbClient reports exhausted host capacity (EAGAIN, EMFILE,
ENFILE, ENOMEM) as a host failure to retry and no longer tells the
caller to change REA_ADB_PATH. Path-resolution errnos come from a
predicate the filesystem owner exports.
- runAdbCapture says adb could not be started instead of claiming an
exit code for a process that never ran.
- The Darwin compiler probe words every spawn_failed outcome as a start
failure, not a compilation failure.
- Asset catalog inspection treats any assetutil that never started as
unavailable, not only ENOENT.
* fix(process): report host spawn capacity as a retryable constraint
Spawn errnos that report exhausted host capacity (EAGAIN, EMFILE, ENFILE,
ENOMEM) were classified by adb readiness alone. assetutil reported them as
an unavailable tool, which marked inspect_asset_catalog unavailable in the
session, and adb commands reported them as protocol failures.
Classify them once in the process capture owner and report a retryable
capacity constraint with recovery advice from assetutil and adb commands.
* docs(adb): describe host capacity failures from device operations
* fix(adb): report exhausted host spawn capacity as runtime unavailability
A host that cannot start adb for lack of process, memory or descriptor
capacity is not a configuration problem. Give it its own failure code and
map it to the runtime_unavailable provider rejection instead of
not_configured.
* fix(adb): report probe-time host capacity as a retryable constraint
A device operation whose adb version probe could not start for lack of host
capacity was refused as capability_unavailable without retryable. It now
reports the same retryable capacity constraint as a later adb command.
* fix(adb): keep the selected adb path in probe-time capacity errors
The retryable capacity constraint raised at the version probe dropped the
selected_binary and path_source diagnostics the refusal used to carry.
* refactor(adb): keep the spawn-failure fact to adb consumers
Restore the DMG reader, Darwin compiler probe and assetutil to main. The
capture owner still marks a never-started command, including the spawn
errors Node throws synchronously, and only the adb readiness probe and
adb commands read that fact.
* test(adb): assert spawn-failure facts instead of refusal wording
The new adb tests compared complete refusal sentences and banned an old
phrase. They now check the errno, selected path, path source and the
owner's spawn-failure marker, so wording edits do not break them.
* fix(adb): own one host-capacity remediation and cover the spawn route
The host-capacity constraint and the configuration failure each carried their own remediation sentence. AdbConfiguration now owns the text and both use it, so the constraint's remediation.action equals diagnostics.remediation.
The synchronous spawn-throw test now runs on Node's execFile route and on the owned spawn route adb uses (stopSignal). The provider test covers inspect_adb_client under EMFILE next to list_adb_devices, and the docs say which operations report the retryable capacity constraint and which report runtime_unavailable.
* fix(process): report EMFILE and ENFILE spawn failures without stdio
After an EMFILE or ENFILE spawn failure Node creates no stdio, so the
owned stop-signal route threw a TypeError while reading the streams and
the later spawn error went unhandled. Attach the error listener first and
capture absent streams as empty, so adb reports retryable host capacity.
* fix(adb): keep a completed push when host capacity stops its digest check
A capacity failure starting sha256sum came back as a retryable error even
though adb push had already written the file. Repeating the request with
overwrite=false is then refused because the destination exists.
Return the receipt with an unavailable device digest and name the capacity
failure in its limitations, so the caller is told the transfer is unverified
rather than advised to retry.
* docs(adb): list host capacity as an unverified push case
---------
Co-authored-by: t <t@t>
* test(gdb): expect configurable stream truncation diagnostics (#8965)
* test(gdb): expect configurable stream truncation diagnostics
* build(gdb): include prerequisite duplicate-import repair from #8884
* fix(javascript): resolve module-level Electron options in functions (#8960)
* fix(javascript): resolve module-level Electron options in functions
* fix(javascript): limit module option ancestry walk
* fix(javascript): preserve uncertain captured argument ordering
* fix(javascript): retain hoisted activation uncertainty
* fix(javascript): retain function ownership through loops
* fix(javascript): account for classic global activations
* fix(javascript): bind first activations to lexical identities
* fix(javascript): retain uncertain global activation timing
* fix(javascript): isolate receiver facts and share computed reads
* fix(javascript): retain projected and opaque global activations
* fix(javascript): refresh activation bounds as opaque effects grow
* fix(ci): format website watch directories without changing configuration
* fix(javascript): share source reads across opaque-effect updates
* fix(javascript): retain constructed-code global activation bounds
* fix(javascript): share runtime activation and receiver facts
* fix(javascript): reuse pattern reads and function positions
* fix(javascript): isolate copied paths and preserve runtime reads
* style(javascript): format migrated reference imports
* fix(javascript): normalize constructor activation and receiver effects
* fix(javascript): retain aliased receiver timing and lexical global this
* fix(javascript): preserve effect-free receiver identity across activations
* fix(javascript): retain outer activation and prototype effects
* fix(javascript): index implicit reads without deep path rescans
* fix(javascript): retain aliased prototype and parameter read uncertainty
* fix(javascript): index reflected reads through intrinsic invocation facts
* fix(javascript): retain unresolved global activation reads
* chore: refresh PR branch after review fix publication
* fix(javascript): retain projected receiver reads and cache method facts
* fix(javascript): retain inherited and implicit activation reads
* fix(javascript): retain caller-owned this uncertainty
* fix(javascript): retain reflected reads with unresolved spread arguments
* fix(javascript): preserve enclosing activation for decorators
* fix(javascript): track intrinsic literal prototype effects
* fix(javascript): retain catch activation from implicit reads
* perf(javascript): reuse effective keys for projected reads
* fix(javascript): retain possible caller-owned ambient effects
* fix(uninstall): preserve caller-selected Kuna caches during purge (#7921)
* fix(uninstall): keep a managed cache namespace whole when it holds a caller-selected Kuna cache
purge --purge-data reported a REA_KUNA_CACHE_DIR selection below the default Kuna cache as retained, then removed it together with its parent namespace. A namespace that contains the selection is now retained and reported, so the retained report is true.
Fixes#7344
* fix(uninstall): also keep a namespace equal to, or reached by symlink traversal from, the selected Kuna cache
Compare the namespace and the selection with the same normalization, treat an equal path as contained, and resolve existing paths before planning so a symbolic link followed by .. is judged by where it leads.
* fix(uninstall): judge a selection with parent traversal where the filesystem resolves it
Collapsing .. lexically contradicts the existing rule that parent traversal after a symbolic link leads elsewhere, and broke the retained-through-symlink-traversal test. Compare spellings without .. directly and resolve existing paths for the rest, so the symlink-parent case still purges the default cache while a selection that resolves into a managed namespace is retained.
* fix(uninstall): keep a namespace whole when the selection's location is unknown
A realpath failure other than ENOENT/ENOTDIR (for example EACCES under an
inaccessible parent) no longer counts as "absent". Each namespace that may
contain the caller-selected Kuna cache is retained instead of purged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(changed-behavior): withhold equality for unknowns and contradictions (#7624)
find_changed_behavior derived behavior_status from each process
comparison's aggregate status alone. An unchanged comparison whose
first_divergence was unknown (residual unknowns prevent proving no
divergence) and a consistent contradiction comparison both fell
through to observed_unchanged, with no unresolved finding and no
changed-behavior registry unknown.
An unchanged comparison with unknown divergence now counts as unknown
and adds one comparison-level unresolved first_divergence finding
carrying its reason, as artifact comparisons already do for coverage.
A contradiction status makes the aggregate unknown, and contradiction
dimensions are classified and counted as contradictions instead of
unresolved branches. Complete unchanged comparisons stay
observed_unchanged and changed comparisons with unknown localization
stay observed_changed.
Fixes#7384Fixes#7383
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(process): reject contradictory filesystem capture facts (#7631)
* fix(process): reject captures whose top-level filesystem facts contradict their checkpoints
A process capture records its filesystem state twice: files_before,
files_after and filesystem_effects at the top level, and the initial
and final filesystem_checkpoints. Validation never reconciled the two,
and comparison reads only the checkpoints. A caller-authored capture
recording a created file at the top level while its final checkpoint
was empty was therefore accepted and compared as unchanged.
Semantic validation now requires the initial checkpoint to match
files_before with no effects, and the final checkpoint to match
files_after and filesystem_effects. The native producer builds both
representations from the same snapshots, so produced captures are
unaffected; consistent created-file captures still compare as changed.
Fixes#7371
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(process): cover file-list checkpoint mismatches independently of effects
Every rejected case in the checkpoint-consistency test also disagreed on
effects, so removing both file-list comparisons left it passing. Add
initial and final checkpoint cases whose file lists alone disagree while
effects and coverage accounting stay valid.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(process): report deadlines expiring during final cleanup (#7635)
* fix(process): report a deadline that elapses during final cleanup as a timeout
runOwnedCommand checked its absolute command deadline before awaiting
process cleanup, but on the ordinary successful path it consulted only
the caller's cancellation signal afterwards. A command whose deadline
expired while successful cleanup was still awaited therefore resolved
as success.
After cleanup, an elapsed deadline now throws OwnedCommandFailure with
reason "timeout" and the completed snapshot, as docs/tool-design.md
requires a final signal check after the last awaited finalization step.
Existing primary failures, caller cancellation and incomplete-cleanup
classification keep their precedence.
Fixes#7369
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(process): drive the cleanup-deadline regression with controlled time
The regression test gave output collection 100 ms of real time before
the deadline. On a busy worker the deadline could expire during
collection instead, taking a different failure path and failing the
test even with the fix. Use fake timers, wait until cleanup has
started, then advance past the deadline and release cleanup, so the
ordering no longer depends on worker speed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(enhanced): bound batch decompile input size (#5937)
* fix(artifacts): handle early ASAR extraction cancellation (#5672)
* fix(artifacts): own source errors during output setup
* fix(artifacts): join silent source destruction before returning
* fix(artifacts): retain source cleanup failures with primary errors
* fix(artifacts): join source finalization and classify secondary failures
* fix(firmware): separate source cleanup from output rollback
* fix(artifacts): retain live source cleanup dependencies
* fix(artifacts): preserve source failure completion and semantics
* fix(artifacts): retain scope cleanup owners and typed failures
* fix(artifacts): keep cleanup classification and admission recovery
When the operation completed and only scope cleanup failed, keep the cleanup failure's own reason instead of reporting io, matching main (fixes artifactProvider.cleanupRetry). When a typed primary is rebuilt with pending scope cleanup, carry its userMessage and ASAR header admission so callers keep the configured ceiling and setting.
* fix(artifacts): translate plain file-close failures through the reader path
The outer file-cleanup branch rebuilt every ArtifactFileCleanupFailure as a
ProviderAdapterError, so dyld reads lost their io, unavailable, format and
cancelled reasons and their exact file-cleanup cause. Compose separately only
when the failed primary owns its own translation (a domain error, source
failure or scope cleanup failure); raw and reader primaries are already
projected by the file failure with its exact lease.
* fix(artifacts): bound source destruction by cancellation and keep wrapper admission
After cancellation, SafeOutputTree.write could wait indefinitely for a source
whose destruction did not settle, both in the destroy join and in the
iterator's wait for close. Both waits now end at cancellation plus a fixed 1 s
grace, modeled on the process-output closure grace. A destruction that
outlives it is reported as incomplete source cleanup under an exact lease that
releases only once closure is observed without a late error.
ArtifactResourceCleanupFailure and SafeOutputSourceFailure now keep the
primary's admission, matching withCleanup.
* fix(artifacts): keep one settled wait while observing a stuck source
observeClosure raced the same pending close promise against a new timer every
10 ms, so a source whose destruction never settled accumulated one reaction per
poll for as long as it stayed open. Each poll now waits on its own promise that
the close listener or the unref'd timer settles, leaving one pending wait and
one close listener. The lifecycle test also asserts that a late close releases
the retained owner without an explicit retry.
* fix(artifacts): settle pending reads before cancellation
* test(artifacts): observe the consumed ZIP iterator on cancellation
* test(artifacts): validate projected cleanup resources before sorting
* test(mcp): reserve parent capacity for timeout recovery
* fix(artifacts): contain ASAR cancellation before output consumption
* test(artifacts): hash ASAR cancellation fixture bytes
* fix(decompile): retain inventory limitations and selection (#8466)
* fix(decompile): retain inventory limitations with the latest selection
The manifest and decompile index dropped procedure-inventory
limitations, so a directory separated from its Evidence response could
not explain why functions were selected or omitted. Keep those
limitations on the manifest and in a latest_run index record that also
names the filter and counts that produced them. Older runs remain
represented only by their retained function records.
Fixes#8355
* test(decompile): surface export errors and check function caveats
Address review findings on the inventory-limitation regression:
- Throw the typed export error instead of returning from a failed Result.
- Assert historical function records keep their own pseudocode caveats
and do not receive the latest inventory warnings.
* fix(pyinstaller): leave PE carrier binary members unmarked (#6730)
The Windows bootloader sets no execute bit on extracted ARCHIVE_ITEM_BINARY
members (the S_IXUSR fchmod in pyi_archive.c is #ifndef WIN32). Mark type b
members executable only when the carrier dialect is posix.
* fix(jeb): reconnect after stale MCP sessions (#3754)
* fix(jeb): reconnect after stale MCP sessions
* fix(jeb): preserve failures for overlapping MCP requests
* fix(jeb): make cleanup operations explicit and stabilize timeout fixtures
* fix(setup): flag unresolved migrated Claude Desktop storage (#8971)
* fix(setup): flag unresolved migrated Claude Desktop storage
* fix(setup): preserve Claude MSIX process view when config is unresolved
* fix(enhanced): restore batch input record limit
* docs(tool-design): audit agent workflow guidance (#8439)
* docs(tool-design): audit agent workflow guidance
* docs(tool-design): check deployed guidance versions
* fix(cli): align ADB directory and package query schemas (#8974)
* fix(cli): align ADB directory and package query schemas
* fix(cli): preserve ADB selector correction details
* fix(devicetree): reject duplicate node paths in Evidence (#8970)
* fix(pe): classify valid DOS targets as unsupported (#7530)
fix(pe): classify confirmed DOS targets as unsupported
Signed-off-by: Rudy Celekli <[email protected]>
* fix(native): reject nonzero reserved ELF dynamic symbols (#7528)
fix(native): validate the reserved ELF dynamic symbol
Signed-off-by: Rudy Celekli <[email protected]>
* fix(evidence): traverse large source link sets safely (#8457)
* fix(verification): use inspected Ghidra JDK (#8440)
* fix(mcp): clarify retained dossier handoff in crash prompt (#8437)
* fix(evidence): clarify unsupported view recovery (#8412)
* fix(evidence): clarify unsupported view recovery
* fix(evidence): explain sensitive capture view refusal
* test(ghidra): check regex recovery at its owning boundary (#8578)
* test(ghidra): check structured snapshot profile mismatch (#8536)
* test(ghidra): check retained historical snapshot evidence (#8381)
* test(ghidra): check retained historical snapshot evidence
* test(ghidra): select historical observations by address
* test(cli): cover Frida argument admission and spawn ordering (#3356)
* fix(cli): reject spawn arguments in Frida attach mode
* test(cli): remove redundant Frida recorder assignments
---------
Co-authored-by: Kaoru0822 <[email protected]>
* fix(ghidra): report restricted-host temp write denial on Windows (#8889)
* fix(ghidra): report restricted-host temp write denial on Windows
Add a Windows doctor check that creates and deletes a file under os.tmpdir(), and classify a denied java_home.save in Ghidra startup output with its path, Win32 error 5, and TMP/TEMP remediation. The probe covers REA's own process only; a host that restricts only child processes is reported from the child's output at startup.
Fixes#7075
* fix(ghidra): probe the session root and retain failed probe cleanup
The Windows doctor temp_directory probe now writes under ghidraSessionRoot(), the same root Ghidra session runtimes use, instead of os.tmpdir(), which can differ when the inherited temp path has a dot-prefixed element. A probe file whose removal fails is retained on the doctor resource owner so doctor shutdown retries the removal; the result still reports its path.
* fix(cli): normalize exit status on worker cleanup failure (#8049) (#8050)
* fix(cli): normalize exit status on worker cleanup failure (#8049)
* test(cli): verify SDK output status and finalization rejection on exit
* ci: retrigger checks
---------
Co-authored-by: Sarthak Pandey <[email protected]>
* fix(gdb): keep inferior-tty verification for read-only queries (#5539)
* fix(gdb): keep TTY verification across read-only inferior-tty inspection
The console command path cleared `inferiorTtyVerified` before dispatching
any command whose text merely mentioned `inferior-tty`, so the read-only
`show inferior-tty` query permanently revoked a persistent session's
verified output sink; no in-session path ever restored the flag, and a
later close of a live session failed detach verification with
capability_unavailable.
Exempt the exact read-only query forms (`show inferior-tty` and the MI
`-inferior-tty-show`) from the output-mutation predicate; every other
command mentioning the TTY setting keeps revoking, so mutating commands
still fail closed and detach keeps re-reading the live TTY before
detaching. Cover the query forms, composite commands, and the
inspect-then-close detach path with regression tests.
Fixes#5099
* test(gdb): classify show inferior-tty in the one-shot table and rename the TTY classification table
- Add a 'show inferior-tty' row to the 'classifies one-shot GDB
commands' table: the read-only query no longer invalidates the detach
proof, a user-visible one-shot behavior change.
- Rename the 'classifies read-only inferior TTY queries as non-mutating'
table to 'classifies inferior TTY commands' since two of its rows are
mutation cases.
- Assert via the existing recorded-commands array that the inspected
'show inferior-tty' text is actually dispatched, and add an
execute-path assertion that the MI '-inferior-tty-show' form leaves
the verified-sink flag intact (defensive exemption: REA's own MI calls
do not route through this gate).
---------
Co-authored-by: akushonkamen <[email protected]>
* fix(browser): report an out-of-range generated position as a selection fault (#7133)
trace_web_source_location reported a well-formed generated_position past
the end of the selected script as invalid_format on generated_position,
with no bounds, so callers were told their input was malformed and not
which coordinate to change. Report out_of_range on the line or column
that is past the end, with its valid bounds, as export_web_scripts and
inspect_web_network_capture now do for their selections.
The position scan now returns the bound it ran past, so the offset and
the reported limit come from one pass and cannot disagree on line
terminators.
Fixes#7132
* fix(captures): classify unresolvable capture paths as capture_path input (#7814)
* fix(captures): classify unresolvable capture paths as capture_path input
Fixes#7636
* test(captures): cover unresolvable descriptor paths
---------
Co-authored-by: Abelo9996 <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(apple): preserve exact integer precision with post-table plist objects (#8910)
* fix(javascript): resolve electron findings through aliases of interop results (#8914)
* fix(javascript): resolve electron findings through aliases of interop results
* fix(javascript): resolve interop aliases iteratively and check target mutations
* fix(javascript): retain possible writes through uncertain aliases and guard positive reads
* perf(javascript): cache alias change status across semantic interop lookups
* fix(javascript): transient Windows private-runtime source-transfer (#8769)
* fix(javascript): transient Windows private-runtime source-transfer
Parent source-transfer into the Windows private runtime root failed with
EPERM on path-based exclusive create. Write through a native transient
private-runtime path that closes the handle after flush so per-job rm
still works, and yield around the sync write so abort deadlines can fire.
* fix(javascript): yield timer phase before Windows source-transfer write
* fix(il2cpp): reject module image references outside retained inventory (#8956)
* fix(ida): admit Docker host-gateway names as local MCP hosts (#8932)
Containerized REA reaches an IDA MCP server on the container host through
host.docker.internal (Docker, Podman compatibility) or
host.containers.internal (Podman). The HTTP registration refinement only
admitted localhost, 127.0.0.1 and [::1], so doctor reported the valid
gateway registration as invalid_configuration.
Admit both gateway names in the registration's local-host set. The
refinement still requires http or https, no URL credentials, and refuses
every other host.
Fixes#8925
* fix(mcp): report target after failed open (#8613)
* fix(mcp): report target after failed open
* test(session): cover failed open receipt ownership
* test(session): verify restored target receipt
* test(mcp): retain target on snapshot mismatch
* test(mcp): retain oversized open failure receipts
* test(cli): avoid continuing session receipt in one-shot errors
* fix(mcp): report provider after failed switch
* test(ghidra): verify failed open preserves annotations
* test(mcp): join queued receipt request before cleanup
* test(session): assert open failure receipts in lifecycle suites
* test(session): keep open-failure receipt checks within function limits
Move the provider-collision case and snapshot fixture setup out of the
functions that grew when both failure receipts were asserted.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(prompts): include existing static artifact investigation routes (#8972)
* fix(prompts): include existing static artifact investigation routes
* fix(prompts): preserve workflows in existing family scopes
* fix(prompts): require readers for Apple resource routes
* fix(rizin): keep debug targets off the debugger command stream (#8647)
* fix(rizin): keep debug targets off the debugger command stream
A Rizin debug target inherited Rizin's stdin, which is REA's command
stream. Once resumed, a target that reads stdin consumed REA's commands,
so close returned cleanup_incomplete and the one-shot CLI hung.
Route the target's stdin to the caller-selected TTY alongside stdout and
stderr. Before detaching, refuse a target whose stdin is still the command
stream, so a Rizin or backend that ignores the key gets a specific,
retained failure instead of a resumed target and a timeout.
Fixes#8642
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(rizin): keep the stdin test launcher's descriptors under dash
The launcher gated its target with `cat "$2" >/dev/null`. dash applies a
simple command's redirection to the shell itself until the command returns,
so on Ubuntu the waiting shell's fd 1 was /dev/null, and that shell is the
target REA reads in /proc. All 8 Linux-gated tests then failed the existing
output-descriptor check. macOS /bin/sh is bash, which redirects only in the
forked child, so this did not show locally.
Gate without any redirection. Production code is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(rizin): reject unmodeled commands and own cleanup at acquisition
Address review of the stdin regression tests.
The launcher accepted any unknown line, so the marker that followed could
complete it, and assertions only checked that commands were present. An
added `dc` or a repeated `olj` still passed. It now records an unknown line
and exits, and the tests pin the exact command sequence.
Cleanup hung off module state assigned after setup finished, so a failed
setup left its directory and terminal child behind. Each resource now
registers its own finish hook when acquired, using createTestTempDirectory.
Success checks throw the typed error instead of asserting `.ok`, and the
refusal probe no longer sits inside a loop and conditional.
Production code is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(ci): align tests with current evidence contracts
* feat(native): report binary hardening for ELF, PE and Mach-O (#8524)
* feat(native): report binary hardening for ELF, PE and Mach-O
Documents inspect_binary_hardening and its CLI equivalent, including
per-property states, supporting evidence, read budgets, and one report
per Mach-O slice. Explains that missing protection imports remain
unknown, signature presence does not establish validity, and hardened
runtime requires inspect_signature. Adds hardening links throughout the
documentation. The supplied diff also removes the advanced CodeQL
workflow and configuration, along with snapshot-binding mismatch
guidance.
* fix(native): validate declared payloads and dynamic tables before hardening rows
Run the shared range checks for ELF, read imports from the dynamic tables
rather than section metadata, and keep an unresolved symbol count apart from
an absent table. Check every Mach-O command before building rows and retain
only the offsets the rows need.
Make the verifier's readelf output wide, check the emitted format before
parsing Mach-O, and place the sources before the libraries in the link.
* fix(native): share Mach-O segment admission with hardening reads
A segment with a section count its cmdsize cannot hold passed the hardening
reader and could enable restrict_segment. Move the size, section count and
file range checks to a shared admission that readMachoImage and the hardening
reader both use, and pin the ELF load-size case with a regression test.
* fix(hardening): reject Mach-O section payloads past the slice and empty ELF interpreters
Segment admission never checked each section's payload range, so a
file-backed section past the slice still produced rows. Decode sections with
the section header owner and mark the slice malformed when a payload leaves
it; zero-fill and empty sections own no file bytes and are exempt.
admitInterp accepted a NUL first byte in a longer extent, which counted an
empty interpreter as PIE evidence. It now requires a nonempty path.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KuWHGvzJkrhCqjtEHUwqyH
* fix(native): read CFG instrumentation from decoded GuardFlags
Use the IMAGE_GUARD_CF_INSTRUMENTED flag that guardFlagRecord already
decodes instead of masking the raw GuardFlags word a second time.
* fix(native): reject ELF table spans a second PT_LOAD remaps
The hardening reader mapped DT_STRTAB, DT_SYMTAB and hash tables through
the one PT_LOAD containing the table's first address. A second PT_LOAD
starting inside the table could map the rest of it from other file bytes
and the reader still reported parsed import evidence.
Cap each mapping at the next PT_LOAD that starts inside it, reject a
requested range that crosses that boundary, and stop hash reads at the
same limit, matching the conflicting-mapping refusal in readElfSymbols.
* fix(native): dedupe ELF note tables only on shared boundaries
The hardening reader dropped any note table whose bytes another table
already covered. A covering SHT_NOTE can hold a non-GNU note whose
descriptor is itself a complete GNU property note, while PT_NOTE names
that inner note directly. Walking only the outer stream missed the GNU
marker and reported CET disabled.
Drop an alias only when it starts and ends on boundaries of admitted
tables, which are note boundaries of the walked streams. Any other
covered alias leaves the feature rows unknown, as a partial overlap
already does.
* fix(native): admit every Mach-O symbol record after a canary match
The Mach-O stack protector scan returned as soon as it found an
undefined ___stack_chk_fail, so a later undefined symbol whose n_strx
lay outside the string table was never checked. The same damaged table
parsed or failed as malformed depending only on record order.
Keep the match and finish the bounded record walk. Records after the
match are only checked against the string table bounds, so their names
are not read or charged to the scan budget. The ELF reader already
admits its whole symbol table before matching names.
* fix(native): accept ELF load aliases with the same file translation
The hardening reader treated every PT_LOAD that starts inside a table,
or that also contains the table's address, as a conflicting mapping.
A load with the same address-to-file translation names the same file
bytes, so tables it overlaps still have one interpretation, and the
canonical ElfLoadMap already merges such spans.
Treat equivalent loads as one interpretation. A load with a different
translation, or the zero-filled tail of an equivalent load, still ends
the usable range, so a remapped table is still rejected.
* fix(native): keep same-span ELF note tables with different alignments unknown
A section and a program header that share endpoints were treated as one
note stream even when their effective note alignments differed. Walking the
same bytes at 4 and 8 can frame different notes, so dropping the second table
could hide a GNU property word and report CET or BTI as disabled.
Require matching effective alignment at both endpoints, and no alignment
change inside the covered span, before treating a table as an alias.
Otherwise mark the feature rows unknown. The fixture's .note.gnu.property
section now declares the class's note alignment, as toolchains emit it.
* refactor(native): read LC_SYMTAB through the shared fixed-word decoder
Move the Mach-O fixed uint32 command layouts out of MachoCommandNames
into domain/apple/machoLoadCommands so there is one owner of those
offsets. MachoCommandNames and the hardening reader now both decode
through it; the hardening reader keeps its own bounds and import policy.
* fix(native): skip Mach-O common definitions in canary imports
A common definition sets N_UNDF|N_EXT like an import but stores its
allocation size in n_value. An object holding only
`int __stack_chk_fail;` therefore reported stack_protector: enabled.
Require a zero n_value before an undefined external counts as an
import, for both nlist widths.
* refactor(native): read linkedit data commands through the shared fixed-word decoder
readMachoLinkeditDataCommand reproduced the dataoff/datasize offsets that
FIXED_WORD_FIELDS already owns. Take the command and decode through
decodeMachoFixedWordFields, as readMachoSymtabCommand does, keeping the
truncation and endianness behavior.
* fix(native): skip empty SHT_NOTE sections in ELF hardening
A zero-sized SHT_NOTE owns no bytes, so the section directory marks it
not file-backed and the hardening reader rejected the whole image as
malformed, while readElfImage accepted the same bytes. Skip empty note
sections before the file-backing check; they have nothing to walk.
* fix(scripts): run hardening verifier commands through the shared owner
The binary hardening verifier started its probes and compiler drivers with
raw spawnSync, which bypassed the src/process/ supervision that AGENTS.md
requires. Every launch now goes through createVerifierCommands, and the
temporary root is removed only after the owner closes cleanly.
* fix(scripts): clean up hardening verifier commands on Ctrl-C
SIGINT or SIGTERM ended Node before withVerifierCleanup ran, so detached
compiler trees and the temporary root could outlive the verifier. The
verifier now turns those signals into an abort that every owned command
receives, so cleanup closes the owner and removes the root before exit.
* fix(scripts): fail the hardening verifier when a signal lands outside a command
Only the owned commands read the interruption signal, so a SIGINT or
SIGTERM during the final cleanup or between commands was recorded and
then dropped, and the verifier exited 0. After cleanup settles, the
verifier now throws the interruption reason. A failure the operation or
its cleanup already raised still leaves first and is not replaced.
* fix(native): fail Mach-O slices with a malformed encryption command
The hardening reader turned every malformed status from the shared
encryption reader into an unknown `encrypted` row, so a short
LC_ENCRYPTION_INFO_64 or an encrypted range past the slice still left the
slice parsed with a full report.
Only two conflicting encryption commands stay an unknown row now. Any other
encryption defect fails the slice like other structural command failures.
* fix(native): ignore STB_LOCAL undefined ELF symbols as imports
A local undefined __stack_chk_fail or __memcpy_chk is never resolved
against another object, so it does not establish an external import.
Skip STB_LOCAL bindings when reading protector signs, and give the
fixture's positive symbols STB_GLOBAL binding.
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
---------
Signed-off-by: Rudy Celekli <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Edgar Arakelyan <[email protected]>
Co-authored-by: ShuaixinHuang <[email protected]>
Co-authored-by: highmachs <[email protected]>
Co-authored-by: hhang <[email protected]>
Co-authored-by: hhang <[email protected]>
Co-authored-by: Paritosh Lahre <[email protected]>
Co-authored-by: Adolanium <[email protected]>
Co-authored-by: 김준호 <[email protected]>
Co-authored-by: t <t@t>
Co-authored-by: Hasan Zayni <[email protected]>
Co-authored-by: Salman Almutairi <[email protected]>
Co-authored-by: Lucien <[email protected]>
Co-authored-by: RRFZ <[email protected]>
Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: Wade <[email protected]>
Co-authored-by: Martin Klein <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Rudy Mizrahi Celekli <[email protected]>
Co-authored-by: xin zhuang <[email protected]>
Co-authored-by: Beqa <[email protected]>
Co-authored-by: NoThing <[email protected]>
Co-authored-by: Sarthak Pandey <[email protected]>
Co-authored-by: akushonkamen <[email protected]>
Co-authored-by: akushonkamen <[email protected]>
Co-authored-by: amyanger <[email protected]>
Co-authored-by: Abel Yagubyan <[email protected]>
Co-authored-by: Abelo9996 <[email protected]>
Co-authored-by: Jiabin Yin <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mohamed Ayoub Eleuch <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
* fix(cli): reject project-scoped Amp registration
Incur's mcp add parses --no-global but its Amp writer always writes the
global settings file, so a project-scoped Amp request exited 0 reporting
success while changing global config. Fail closed in the entrypoint before
Incur dispatches: project-scoped Amp registration now exits 1 with a
diagnostic and leaves global settings untouched. Global Amp registration
is unchanged.
Fixes#7535
* fix(cli): exempt discovery requests from the Amp project-scope guard
Discovery requests are not registrations: mcp add --no-global with a
discovery flag (--help, -h, --llms, --llms-full, --version, --schema) now
passes through to normal handling instead of exiting 1 with the scope
error, matching the discovery set mcp add itself exempts. Real
project-scoped Amp registrations are still rejected before Incur runs.
Also restores the executable bit on scripts/rea.mjs, and adds launcher
dispatch tests proving the guard rejects a real registration before the
CLI runtime loads and passes discovery requests through.
* fix(cli): share the mcp add discovery classification between guard and routing
The Amp project-scope guard kept its own copy of the mcp add discovery
flags, which could drift from the discovery routing in cli.ts.
classifyMcpAddDiscovery in cliBuiltinArguments.ts is now the single
owner: the guard and the cli.ts mcp add special-casing both consume it,
with bypass/version/schema precedence matching the routing. No behavior
change; covered by classification unit tests and the existing mcp add
discovery serve test.
* fix(cli): emit structured output for Amp project-scope refusal
The Amp project-scope guard wrote a plain message to stderr and left
stdout empty when the caller asked for a structured document (json,
jsonl, yaml), contradicting the failure framing documented for the
output argument. Structured formats now render the refusal through the
same pre-execution error renderer the neighboring output-argument
branch uses, with code AMP_PROJECT_SCOPE_UNSUPPORTED on stdout; every
other format keeps the byte-identical plain stderr message. The guard
still exits 1 before dist/cli-runtime/cli.js is imported.
Co-authored-by: Saim Shafique <[email protected]>
* test(cli): cover Amp project-scope guard through the launcher
The guard tests only called isProjectScopedAmpRegistration, so they
still passed with its call in scripts/rea.mjs removed. Add a launcher
test with an isolated home and working directory that checks the exit
code, the diagnostic, the unchanged global settings, and the absence
of project files for both explicit Amp and default-agent requests.
Co-authored-by: Saim Shafique <[email protected]>
* test(cli): assert Amp project-scope refusal by error code
The --no-global refusal tests locked the full English sentence. Structured output now asserts AMP_PROJECT_SCOPE_UNSUPPORTED and a message field.
* fix(cli): share normalized mcp add agent selection with the scope guard
The project-scope guard scanned raw arguments and only recognized the spaced --agent form, while dispatch expanded --agent= equals forms first: --agent=claude-code was wrongly refused as default Amp, and --agent claude-code --agent=amp slipped Amp past --no-global. The equals expansion and agent scan now live once in cliBuiltinArguments.ts and both callers consume them, so guard and dispatch read the same selection.
* ci(environment): pin the real agentenv lane to agentenv-framework 0.9.1431
The real-agentenv lane, its identity check and the docs pinned
agentenv-framework 0.9.1334, 97 releases behind the current 0.9.1431.
Recorded verification notes keep the versions they ran with; the website
catalogs change only the placeholder comment that shows the version.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): describe 0.9.1431's pulled infra images; keep the site on the release
0.9.1431 pulls its gateway and service-db images from ghcr.io by the
digests its release records and builds one only when that pull fails,
so the lane docs, the export guide and the verifier's comments now say
so, and the mirror step fetches only the bases those builds name. The
public site describes the released package, which still runs 0.9.1334,
so its guide and evidence page keep that version until a release ships
the new pin.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(discovery): advertise attached IDA navigation effects
The canonical tool audit now includes the UI and network effects of the attached IDA legacy profile for the four navigation operations. Session inventory still starts from the provider-neutral evidence base and composes the selected provider descriptors, so headless and native sessions do not inherit attached UI state.
Adds SDK/session boundary coverage for all four operations and preserves attached, headless, and native distinctions.
* fix(discovery): cover IDA provider effects across tools and workflows
* test(discovery): isolate IDA metadata from host tool probes
* test(discovery): compare live identity with session effect bases
* fix(discovery): audit complete IDA provider effect union
* fix(discovery): exclude unreachable provider workflow effects
* fix(discovery): propagate IDA effects through composed tools
* fix(artifacts): deduplicate keyed archive path diagnostics
* test(artifacts): use real bundle in provider binding fixture
* test(mcp): align overview inventory fixtures with row contracts
* fix(javascript): align Electron path and owner identities
Refresh archive close and function CLI fixtures for current admission and Result contracts.
* test: align boundary fixtures with runtime prerequisites
* test(javascript): preserve unknown Electron IPC ownership in fixture
* fix: audit IDA network effects during lifecycle cleanup
* test: align close lifecycle hint with network audit
* style(website): format merged Wrangler watch paths
* fix(discovery): retain retry and cleanup effects
* style(website): drop unrelated Wrangler formatting
The watch_dir reflow is whitespace only. The IDA navigation-effect fix stays.
---------
Co-authored-by: dhruvac29 <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(signals): report a failed sigrok version probe as a configuration error
* fix(signals): attribute a failed version probe to the limited probe
* fix(signals): name the limiter as a possible cause of a failed version probe
The version probe runs under the resource limiter, so a nonzero status
can come from prlimit as well as sigrok-cli. Say so in the configuration
refusal instead of blaming sigrok-cli alone, and cover a limiter that
refuses to start the tool for both decode and discovery.
* test(signals): assert limiter probe failures by typed fields
The limiter-refused recording test locked English probe wording. Assert the capability-unavailable code, provider, operation, and captured limiter output instead.
* fix(signals): record an unattributed version-probe status owner
A nonzero resource-limited version probe now reports version_probe_status_owner limiter-or-tool. The limiter-refusal test requires that field, so the old sigrok-cli-only failure no longer passes.
---------
Co-authored-by: Abelo9996 <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(native): report binary hardening for ELF, PE and Mach-O
Documents inspect_binary_hardening and its CLI equivalent, including
per-property states, supporting evidence, read budgets, and one report
per Mach-O slice. Explains that missing protection imports remain
unknown, signature presence does not establish validity, and hardened
runtime requires inspect_signature. Adds hardening links throughout the
documentation. The supplied diff also removes the advanced CodeQL
workflow and configuration, along with snapshot-binding mismatch
guidance.
* fix(native): validate declared payloads and dynamic tables before hardening rows
Run the shared range checks for ELF, read imports from the dynamic tables
rather than section metadata, and keep an unresolved symbol count apart from
an absent table. Check every Mach-O command before building rows and retain
only the offsets the rows need.
Make the verifier's readelf output wide, check the emitted format before
parsing Mach-O, and place the sources before the libraries in the link.
* fix(native): share Mach-O segment admission with hardening reads
A segment with a section count its cmdsize cannot hold passed the hardening
reader and could enable restrict_segment. Move the size, section count and
file range checks to a shared admission that readMachoImage and the hardening
reader both use, and pin the ELF load-size case with a regression test.
* fix(hardening): reject Mach-O section payloads past the slice and empty ELF interpreters
Segment admission never checked each section's payload range, so a
file-backed section past the slice still produced rows. Decode sections with
the section header owner and mark the slice malformed when a payload leaves
it; zero-fill and empty sections own no file bytes and are exempt.
admitInterp accepted a NUL first byte in a longer extent, which counted an
empty interpreter as PIE evidence. It now requires a nonempty path.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KuWHGvzJkrhCqjtEHUwqyH
* fix(native): read CFG instrumentation from decoded GuardFlags
Use the IMAGE_GUARD_CF_INSTRUMENTED flag that guardFlagRecord already
decodes instead of masking the raw GuardFlags word a second time.
* fix(native): reject ELF table spans a second PT_LOAD remaps
The hardening reader mapped DT_STRTAB, DT_SYMTAB and hash tables through
the one PT_LOAD containing the table's first address. A second PT_LOAD
starting inside the table could map the rest of it from other file bytes
and the reader still reported parsed import evidence.
Cap each mapping at the next PT_LOAD that starts inside it, reject a
requested range that crosses that boundary, and stop hash reads at the
same limit, matching the conflicting-mapping refusal in readElfSymbols.
* fix(native): dedupe ELF note tables only on shared boundaries
The hardening reader dropped any note table whose bytes another table
already covered. A covering SHT_NOTE can hold a non-GNU note whose
descriptor is itself a complete GNU property note, while PT_NOTE names
that inner note directly. Walking only the outer stream missed the GNU
marker and reported CET disabled.
Drop an alias only when it starts and ends on boundaries of admitted
tables, which are note boundaries of the walked streams. Any other
covered alias leaves the feature rows unknown, as a partial overlap
already does.
* fix(native): admit every Mach-O symbol record after a canary match
The Mach-O stack protector scan returned as soon as it found an
undefined ___stack_chk_fail, so a later undefined symbol whose n_strx
lay outside the string table was never checked. The same damaged table
parsed or failed as malformed depending only on record order.
Keep the match and finish the bounded record walk. Records after the
match are only checked against the string table bounds, so their names
are not read or charged to the scan budget. The ELF reader already
admits its whole symbol table before matching names.
* fix(native): accept ELF load aliases with the same file translation
The hardening reader treated every PT_LOAD that starts inside a table,
or that also contains the table's address, as a conflicting mapping.
A load with the same address-to-file translation names the same file
bytes, so tables it overlaps still have one interpretation, and the
canonical ElfLoadMap already merges such spans.
Treat equivalent loads as one interpretation. A load with a different
translation, or the zero-filled tail of an equivalent load, still ends
the usable range, so a remapped table is still rejected.
* fix(native): keep same-span ELF note tables with different alignments unknown
A section and a program header that share endpoints were treated as one
note stream even when their effective note alignments differed. Walking the
same bytes at 4 and 8 can frame different notes, so dropping the second table
could hide a GNU property word and report CET or BTI as disabled.
Require matching effective alignment at both endpoints, and no alignment
change inside the covered span, before treating a table as an alias.
Otherwise mark the feature rows unknown. The fixture's .note.gnu.property
section now declares the class's note alignment, as toolchains emit it.
* refactor(native): read LC_SYMTAB through the shared fixed-word decoder
Move the Mach-O fixed uint32 command layouts out of MachoCommandNames
into domain/apple/machoLoadCommands so there is one owner of those
offsets. MachoCommandNames and the hardening reader now both decode
through it; the hardening reader keeps its own bounds and import policy.
* fix(native): skip Mach-O common definitions in canary imports
A common definition sets N_UNDF|N_EXT like an import but stores its
allocation size in n_value. An object holding only
`int __stack_chk_fail;` therefore reported stack_protector: enabled.
Require a zero n_value before an undefined external counts as an
import, for both nlist widths.
* refactor(native): read linkedit data commands through the shared fixed-word decoder
readMachoLinkeditDataCommand reproduced the dataoff/datasize offsets that
FIXED_WORD_FIELDS already owns. Take the command and decode through
decodeMachoFixedWordFields, as readMachoSymtabCommand does, keeping the
truncation and endianness behavior.
* fix(native): skip empty SHT_NOTE sections in ELF hardening
A zero-sized SHT_NOTE owns no bytes, so the section directory marks it
not file-backed and the hardening reader rejected the whole image as
malformed, while readElfImage accepted the same bytes. Skip empty note
sections before the file-backing check; they have nothing to walk.
* fix(scripts): run hardening verifier commands through the shared owner
The binary hardening verifier started its probes and compiler drivers with
raw spawnSync, which bypassed the src/process/ supervision that AGENTS.md
requires. Every launch now goes through createVerifierCommands, and the
temporary root is removed only after the owner closes cleanly.
* fix(scripts): clean up hardening verifier commands on Ctrl-C
SIGINT or SIGTERM ended Node before withVerifierCleanup ran, so detached
compiler trees and the temporary root could outlive the verifier. The
verifier now turns those signals into an abort that every owned command
receives, so cleanup closes the owner and removes the root before exit.
* fix(scripts): fail the hardening verifier when a signal lands outside a command
Only the owned commands read the interruption signal, so a SIGINT or
SIGTERM during the final cleanup or between commands was recorded and
then dropped, and the verifier exited 0. After cleanup settles, the
verifier now throws the interruption reason. A failure the operation or
its cleanup already raised still leaves first and is not replaced.
* fix(native): fail Mach-O slices with a malformed encryption command
The hardening reader turned every malformed status from the shared
encryption reader into an unknown `encrypted` row, so a short
LC_ENCRYPTION_INFO_64 or an encrypted range past the slice still left the
slice parsed with a full report.
Only two conflicting encryption commands stay an unknown row now. Any other
encryption defect fails the slice like other structural command failures.
* fix(native): ignore STB_LOCAL undefined ELF symbols as imports
A local undefined __stack_chk_fail or __memcpy_chk is never resolved
against another object, so it does not establish an external import.
Skip STB_LOCAL bindings when reading protector signs, and give the
fixture's positive symbols STB_GLOBAL binding.
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(rizin): keep debug targets off the debugger command stream
A Rizin debug target inherited Rizin's stdin, which is REA's command
stream. Once resumed, a target that reads stdin consumed REA's commands,
so close returned cleanup_incomplete and the one-shot CLI hung.
Route the target's stdin to the caller-selected TTY alongside stdout and
stderr. Before detaching, refuse a target whose stdin is still the command
stream, so a Rizin or backend that ignores the key gets a specific,
retained failure instead of a resumed target and a timeout.
Fixes#8642
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(rizin): keep the stdin test launcher's descriptors under dash
The launcher gated its target with `cat "$2" >/dev/null`. dash applies a
simple command's redirection to the shell itself until the command returns,
so on Ubuntu the waiting shell's fd 1 was /dev/null, and that shell is the
target REA reads in /proc. All 8 Linux-gated tests then failed the existing
output-descriptor check. macOS /bin/sh is bash, which redirects only in the
forked child, so this did not show locally.
Gate without any redirection. Production code is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(rizin): reject unmodeled commands and own cleanup at acquisition
Address review of the stdin regression tests.
The launcher accepted any unknown line, so the marker that followed could
complete it, and assertions only checked that commands were present. An
added `dc` or a repeated `olj` still passed. It now records an unknown line
and exits, and the tests pin the exact command sequence.
Cleanup hung off module state assigned after setup finished, so a failed
setup left its directory and terminal child behind. Each resource now
registers its own finish hook when acquired, using createTestTempDirectory.
Success checks throw the typed error instead of asserting `.ok`, and the
refusal probe no longer sits inside a loop and conditional.
Production code is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(prompts): include existing static artifact investigation routes
* fix(prompts): preserve workflows in existing family scopes
* fix(prompts): require readers for Apple resource routes
* fix(mcp): report target after failed open
* test(session): cover failed open receipt ownership
* test(session): verify restored target receipt
* test(mcp): retain target on snapshot mismatch
* test(mcp): retain oversized open failure receipts
* test(cli): avoid continuing session receipt in one-shot errors
* fix(mcp): report provider after failed switch
* test(ghidra): verify failed open preserves annotations
* test(mcp): join queued receipt request before cleanup
* test(session): assert open failure receipts in lifecycle suites
* test(session): keep open-failure receipt checks within function limits
Move the provider-collision case and snapshot fixture setup out of the
functions that grew when both failure receipts were asserted.
---------
Co-authored-by: Cursor Agent <[email protected]>
Containerized REA reaches an IDA MCP server on the container host through
host.docker.internal (Docker, Podman compatibility) or
host.containers.internal (Podman). The HTTP registration refinement only
admitted localhost, 127.0.0.1 and [::1], so doctor reported the valid
gateway registration as invalid_configuration.
Admit both gateway names in the registration's local-host set. The
refinement still requires http or https, no URL credentials, and refuses
every other host.
Fixes#8925
* fix(javascript): transient Windows private-runtime source-transfer
Parent source-transfer into the Windows private runtime root failed with
EPERM on path-based exclusive create. Write through a native transient
private-runtime path that closes the handle after flush so per-job rm
still works, and yield around the sync write so abort deadlines can fire.
* fix(javascript): yield timer phase before Windows source-transfer write
* fix(javascript): resolve electron findings through aliases of interop results
* fix(javascript): resolve interop aliases iteratively and check target mutations
* fix(javascript): retain possible writes through uncertain aliases and guard positive reads
* perf(javascript): cache alias change status across semantic interop lookups
trace_web_source_location reported a well-formed generated_position past
the end of the selected script as invalid_format on generated_position,
with no bounds, so callers were told their input was malformed and not
which coordinate to change. Report out_of_range on the line or column
that is past the end, with its valid bounds, as export_web_scripts and
inspect_web_network_capture now do for their selections.
The position scan now returns the bound it ran past, so the offset and
the reported limit come from one pass and cannot disagree on line
terminators.
Fixes#7132
* fix(gdb): keep TTY verification across read-only inferior-tty inspection
The console command path cleared `inferiorTtyVerified` before dispatching
any command whose text merely mentioned `inferior-tty`, so the read-only
`show inferior-tty` query permanently revoked a persistent session's
verified output sink; no in-session path ever restored the flag, and a
later close of a live session failed detach verification with
capability_unavailable.
Exempt the exact read-only query forms (`show inferior-tty` and the MI
`-inferior-tty-show`) from the output-mutation predicate; every other
command mentioning the TTY setting keeps revoking, so mutating commands
still fail closed and detach keeps re-reading the live TTY before
detaching. Cover the query forms, composite commands, and the
inspect-then-close detach path with regression tests.
Fixes#5099
* test(gdb): classify show inferior-tty in the one-shot table and rename the TTY classification table
- Add a 'show inferior-tty' row to the 'classifies one-shot GDB
commands' table: the read-only query no longer invalidates the detach
proof, a user-visible one-shot behavior change.
- Rename the 'classifies read-only inferior TTY queries as non-mutating'
table to 'classifies inferior TTY commands' since two of its rows are
mutation cases.
- Assert via the existing recorded-commands array that the inspected
'show inferior-tty' text is actually dispatched, and add an
execute-path assertion that the MI '-inferior-tty-show' form leaves
the verified-sink flag intact (defensive exemption: REA's own MI calls
do not route through this gate).
---------
Co-authored-by: akushonkamen <[email protected]>
* fix(ghidra): report restricted-host temp write denial on Windows
Add a Windows doctor check that creates and deletes a file under os.tmpdir(), and classify a denied java_home.save in Ghidra startup output with its path, Win32 error 5, and TMP/TEMP remediation. The probe covers REA's own process only; a host that restricts only child processes is reported from the child's output at startup.
Fixes#7075
* fix(ghidra): probe the session root and retain failed probe cleanup
The Windows doctor temp_directory probe now writes under ghidraSessionRoot(), the same root Ghidra session runtimes use, instead of os.tmpdir(), which can differ when the inherited temp path has a dot-prefixed element. A probe file whose removal fails is retained on the doctor resource owner so doctor shutdown retries the removal; the result still reports its path.
* fix(jeb): reconnect after stale MCP sessions
* fix(jeb): preserve failures for overlapping MCP requests
* fix(jeb): make cleanup operations explicit and stabilize timeout fixtures
The Windows bootloader sets no execute bit on extracted ARCHIVE_ITEM_BINARY
members (the S_IXUSR fchmod in pyi_archive.c is #ifndef WIN32). Mark type b
members executable only when the carrier dialect is posix.
* fix(decompile): retain inventory limitations with the latest selection
The manifest and decompile index dropped procedure-inventory
limitations, so a directory separated from its Evidence response could
not explain why functions were selected or omitted. Keep those
limitations on the manifest and in a latest_run index record that also
names the filter and counts that produced them. Older runs remain
represented only by their retained function records.
Fixes#8355
* test(decompile): surface export errors and check function caveats
Address review findings on the inventory-limitation regression:
- Throw the typed export error instead of returning from a failed Result.
- Assert historical function records keep their own pseudocode caveats
and do not receive the latest inventory warnings.
* fix(artifacts): own source errors during output setup
* fix(artifacts): join silent source destruction before returning
* fix(artifacts): retain source cleanup failures with primary errors
* fix(artifacts): join source finalization and classify secondary failures
* fix(firmware): separate source cleanup from output rollback
* fix(artifacts): retain live source cleanup dependencies
* fix(artifacts): preserve source failure completion and semantics
* fix(artifacts): retain scope cleanup owners and typed failures
* fix(artifacts): keep cleanup classification and admission recovery
When the operation completed and only scope cleanup failed, keep the cleanup failure's own reason instead of reporting io, matching main (fixes artifactProvider.cleanupRetry). When a typed primary is rebuilt with pending scope cleanup, carry its userMessage and ASAR header admission so callers keep the configured ceiling and setting.
* fix(artifacts): translate plain file-close failures through the reader path
The outer file-cleanup branch rebuilt every ArtifactFileCleanupFailure as a
ProviderAdapterError, so dyld reads lost their io, unavailable, format and
cancelled reasons and their exact file-cleanup cause. Compose separately only
when the failed primary owns its own translation (a domain error, source
failure or scope cleanup failure); raw and reader primaries are already
projected by the file failure with its exact lease.
* fix(artifacts): bound source destruction by cancellation and keep wrapper admission
After cancellation, SafeOutputTree.write could wait indefinitely for a source
whose destruction did not settle, both in the destroy join and in the
iterator's wait for close. Both waits now end at cancellation plus a fixed 1 s
grace, modeled on the process-output closure grace. A destruction that
outlives it is reported as incomplete source cleanup under an exact lease that
releases only once closure is observed without a late error.
ArtifactResourceCleanupFailure and SafeOutputSourceFailure now keep the
primary's admission, matching withCleanup.
* fix(artifacts): keep one settled wait while observing a stuck source
observeClosure raced the same pending close promise against a new timer every
10 ms, so a source whose destruction never settled accumulated one reaction per
poll for as long as it stayed open. Each poll now waits on its own promise that
the close listener or the unref'd timer settles, leaving one pending wait and
one close listener. The lifecycle test also asserts that a late close releases
the retained owner without an explicit retry.
* fix(artifacts): settle pending reads before cancellation
* test(artifacts): observe the consumed ZIP iterator on cancellation
* test(artifacts): validate projected cleanup resources before sorting
* test(mcp): reserve parent capacity for timeout recovery
* fix(artifacts): contain ASAR cancellation before output consumption
* test(artifacts): hash ASAR cancellation fixture bytes
* fix(process): report a deadline that elapses during final cleanup as a timeout
runOwnedCommand checked its absolute command deadline before awaiting
process cleanup, but on the ordinary successful path it consulted only
the caller's cancellation signal afterwards. A command whose deadline
expired while successful cleanup was still awaited therefore resolved
as success.
After cleanup, an elapsed deadline now throws OwnedCommandFailure with
reason "timeout" and the completed snapshot, as docs/tool-design.md
requires a final signal check after the last awaited finalization step.
Existing primary failures, caller cancellation and incomplete-cleanup
classification keep their precedence.
Fixes#7369
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(process): drive the cleanup-deadline regression with controlled time
The regression test gave output collection 100 ms of real time before
the deadline. On a busy worker the deadline could expire during
collection instead, taking a different failure path and failing the
test even with the fix. Use fake timers, wait until cleanup has
started, then advance past the deadline and release cleanup, so the
ordering no longer depends on worker speed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(process): reject captures whose top-level filesystem facts contradict their checkpoints
A process capture records its filesystem state twice: files_before,
files_after and filesystem_effects at the top level, and the initial
and final filesystem_checkpoints. Validation never reconciled the two,
and comparison reads only the checkpoints. A caller-authored capture
recording a created file at the top level while its final checkpoint
was empty was therefore accepted and compared as unchanged.
Semantic validation now requires the initial checkpoint to match
files_before with no effects, and the final checkpoint to match
files_after and filesystem_effects. The native producer builds both
representations from the same snapshots, so produced captures are
unaffected; consistent created-file captures still compare as changed.
Fixes#7371
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(process): cover file-list checkpoint mismatches independently of effects
Every rejected case in the checkpoint-consistency test also disagreed on
effects, so removing both file-list comparisons left it passing. Add
initial and final checkpoint cases whose file lists alone disagree while
effects and coverage accounting stay valid.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
find_changed_behavior derived behavior_status from each process
comparison's aggregate status alone. An unchanged comparison whose
first_divergence was unknown (residual unknowns prevent proving no
divergence) and a consistent contradiction comparison both fell
through to observed_unchanged, with no unresolved finding and no
changed-behavior registry unknown.
An unchanged comparison with unknown divergence now counts as unknown
and adds one comparison-level unresolved first_divergence finding
carrying its reason, as artifact comparisons already do for coverage.
A contradiction status makes the aggregate unknown, and contradiction
dimensions are classified and counted as contradictions instead of
unresolved branches. Complete unchanged comparisons stay
observed_unchanged and changed comparisons with unknown localization
stay observed_changed.
Fixes#7384Fixes#7383
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(uninstall): keep a managed cache namespace whole when it holds a caller-selected Kuna cache
purge --purge-data reported a REA_KUNA_CACHE_DIR selection below the default Kuna cache as retained, then removed it together with its parent namespace. A namespace that contains the selection is now retained and reported, so the retained report is true.
Fixes#7344
* fix(uninstall): also keep a namespace equal to, or reached by symlink traversal from, the selected Kuna cache
Compare the namespace and the selection with the same normalization, treat an equal path as contained, and resolve existing paths before planning so a symbolic link followed by .. is judged by where it leads.
* fix(uninstall): judge a selection with parent traversal where the filesystem resolves it
Collapsing .. lexically contradicts the existing rule that parent traversal after a symbolic link leads elsewhere, and broke the retained-through-symlink-traversal test. Compare spellings without .. directly and resolve existing paths for the rest, so the symlink-parent case still purges the default cache while a selection that resolves into a managed namespace is retained.
* fix(uninstall): keep a namespace whole when the selection's location is unknown
A realpath failure other than ENOENT/ENOTDIR (for example EACCES under an
inaccessible parent) no longer counts as "absent". Each namespace that may
contain the caller-selected Kuna cache is retained instead of purged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(adb): name unexecutable adb paths in readiness refusals
When REA_ADB_PATH or the PATH-resolved adb is a directory or a file the
host refuses to execute, the version probe fails at spawn with EACCES or
EPERM. That fell through to the generic "did not report a usable version"
refusal, which named neither the path nor the cause.
Classify those spawn errors as binary_not_executable (provider rejection
executable_missing), name the selected path and errno, and give
remediation for the environment, PATH and host-policy cases. The generic
fallback now names the selected path too.
Fixes#5689
* fix(process): admit synchronous spawn errors as captured failures
Node throws spawn errors other than EACCES, EAGAIN, EMFILE, ENFILE and
ENOENT synchronously from execFile. execFileBytes called execFile inside
its Promise executor, so ENOTDIR or EPERM became a rejection without
capture fields and execFileOutputFailure returned undefined. Consumers
that classify by failure code, such as the adb version probe, could not
see the code, which left the adb EPERM branch unreachable.
Catch synchronous spawn errno errors at the capture boundary and attach
the same empty capture an asynchronous spawn failure carries. Argument
validation errors stay unadmitted.
The adb version probe now reports an ENOTDIR selected path, such as a
REA_ADB_PATH whose parent is a regular file, as a missing binary with
the filesystem error code instead of the generic version refusal.
* fix(process): expose spawn failures from the capture owner
execFileBytes knows when a command never started, but
ExecFileOutputFailure did not say so, so consumers reread raw syscall
and code fields and disagreed. Mark both the synchronous throw and an
asynchronous `spawn <path>` error with `spawnFailed: true`, and set
`path` on the synchronous error to match the asynchronous shape.
Argument validation errors stay unadmitted.
Consumers now use the owner field:
- inspectAdbClient never reports a spawn errno as `exit_code`. ENOENT,
ENOTDIR, ELOOP and ENAMETOOLONG are `binary_missing`; EACCES and EPERM
stay `binary_not_executable`; any other spawn errno, such as EINVAL,
is `binary_not_executable` with a path-naming "could not be started"
message. Each records `filesystem_error` and `path_source`. EACCES
remediation also names parent directory search permission, and EPERM
remediation no longer names host policy as the only cause.
- DarwinProcessRunTokenReader tags every not-started compiler as
`spawn_failed`, not only ENOENT. EACCES, EPERM and ENOTDIR were
previously `failed`.
- NativeDmgArtifactReader uses one not-started rule for both
`processNotStarted` (previously `spawn ` only, so synchronous errors
were missed) and the ENOENT unavailable/io split (previously any
`spawn` prefix). Its fake hosts now reject with real execFileOutput
spawn failures.
* fix(process): classify spawn failures consistently across consumers
Admit synchronous and asynchronous spawn errnos through one helper that
requires an errno and a `spawn` or `spawn <path>` syscall. The
synchronous shape now also gains `spawnargs`, so both shapes match.
Use the owner's `spawnFailed` fact in the remaining consumers:
- inspectAdbClient reports exhausted host capacity (EAGAIN, EMFILE,
ENFILE, ENOMEM) as a host failure to retry and no longer tells the
caller to change REA_ADB_PATH. Path-resolution errnos come from a
predicate the filesystem owner exports.
- runAdbCapture says adb could not be started instead of claiming an
exit code for a process that never ran.
- The Darwin compiler probe words every spawn_failed outcome as a start
failure, not a compilation failure.
- Asset catalog inspection treats any assetutil that never started as
unavailable, not only ENOENT.
* fix(process): report host spawn capacity as a retryable constraint
Spawn errnos that report exhausted host capacity (EAGAIN, EMFILE, ENFILE,
ENOMEM) were classified by adb readiness alone. assetutil reported them as
an unavailable tool, which marked inspect_asset_catalog unavailable in the
session, and adb commands reported them as protocol failures.
Classify them once in the process capture owner and report a retryable
capacity constraint with recovery advice from assetutil and adb commands.
* docs(adb): describe host capacity failures from device operations
* fix(adb): report exhausted host spawn capacity as runtime unavailability
A host that cannot start adb for lack of process, memory or descriptor
capacity is not a configuration problem. Give it its own failure code and
map it to the runtime_unavailable provider rejection instead of
not_configured.
* fix(adb): report probe-time host capacity as a retryable constraint
A device operation whose adb version probe could not start for lack of host
capacity was refused as capability_unavailable without retryable. It now
reports the same retryable capacity constraint as a later adb command.
* fix(adb): keep the selected adb path in probe-time capacity errors
The retryable capacity constraint raised at the version probe dropped the
selected_binary and path_source diagnostics the refusal used to carry.
* refactor(adb): keep the spawn-failure fact to adb consumers
Restore the DMG reader, Darwin compiler probe and assetutil to main. The
capture owner still marks a never-started command, including the spawn
errors Node throws synchronously, and only the adb readiness probe and
adb commands read that fact.
* test(adb): assert spawn-failure facts instead of refusal wording
The new adb tests compared complete refusal sentences and banned an old
phrase. They now check the errno, selected path, path source and the
owner's spawn-failure marker, so wording edits do not break them.
* fix(adb): own one host-capacity remediation and cover the spawn route
The host-capacity constraint and the configuration failure each carried their own remediation sentence. AdbConfiguration now owns the text and both use it, so the constraint's remediation.action equals diagnostics.remediation.
The synchronous spawn-throw test now runs on Node's execFile route and on the owned spawn route adb uses (stopSignal). The provider test covers inspect_adb_client under EMFILE next to list_adb_devices, and the docs say which operations report the retryable capacity constraint and which report runtime_unavailable.
* fix(process): report EMFILE and ENFILE spawn failures without stdio
After an EMFILE or ENFILE spawn failure Node creates no stdio, so the
owned stop-signal route threw a TypeError while reading the streams and
the later spawn error went unhandled. Attach the error listener first and
capture absent streams as empty, so adb reports retryable host capacity.
* fix(adb): keep a completed push when host capacity stops its digest check
A capacity failure starting sha256sum came back as a retryable error even
though adb push had already written the file. Repeating the request with
overwrite=false is then refused because the destination exists.
Return the receipt with an unavailable device digest and name the capacity
failure in its limitations, so the caller is told the transfer is unverified
rather than advised to retry.
* docs(adb): list host capacity as an unverified push case
---------
Co-authored-by: t <t@t>
* fix(cli): parse environment-serve --port and --host like rea mcp --http
environment-serve declared --port as a number, so incur coerced the flag text with Number(): an empty --port= bound an ephemeral port instead of failing, and 0x1F90 or 1e3 were reinterpreted. --host accepted whitespace. docs/cli.md says these flags resolve like rea mcp --http, which accepts only decimal port text and a non-empty trimmed host. The flags now use the same rules and are refused before binding.
* refactor(cli): reuse the shared MCP listener port and host schemas
* fix(setup): replace the fixed Hopper download deadline with a stall timeout
Hopper package downloads aborted after a fixed 120 s, which was also the
largest deadline the download owner accepted. The current 32 MB Linux
package needs about 132 s at the reporter's sustained 245 KB/s, so setup
could never finish on that connection, and the timeout surfaced only as
Node's generic abort message.
Abort a download when no response bytes arrive for 30 s, or when it
outlives a 15-minute overall bound, with a tagged
HopperDownloadTimeoutError that names the URL, the limit reached and the
bytes received. Linux and macOS setup already relay a thrown download
error's message; the Linux publisher now keeps it for release metadata
and public key fetches instead of a generic verification failure.
Metadata and key fetches keep their 10 s bound.
Refs #7077
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(setup): keep the macOS release feed on the metadata limit
The stall-timeout change left the macOS release feed fetch on the
package download bounds. That fetch could then wait up to 15 minutes
instead of the 120 s it had, while the installation guide says release
metadata keeps a 10 s limit. Only the Linux publisher owned that limit.
Move the 10 s metadata limit into HopperDownload.ts beside the package
bounds. The Linux feed and key fetches use it, and the macOS installer
now passes it for its feed fetch through the installer host.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(desktop): capture a Linux desktop application in use
Add `capture-desktop-application`, a CLI-only, Linux and X11 capture that
launches an installed desktop application, watches it while it is used and
writes capture Evidence plus SHA-256 addressed content:
- inventory: desktop entry, launch command, executable, package and its
apt sources (or snap, flatpak, AppImage), Electron markers and candidate
state directories;
- session: the launched process tree, client windows from xprop with the
first application window time and a window match, and files held open;
- state: a stat walk of the home directory before launch, at checkpoints
read from stdin, at stop and after exit, with created, modified, deleted
and retyped paths and the content of changed files and state directories.
It refuses non-Linux hosts, missing DISPLAY or xprop, a non-empty output
folder and an application whose window is already open, and signals only
processes it started.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): own launched processes, confirm walk coverage and keep cancelled captures
- Launch through the owned provider path with a run token; release with
SIGTERM, then the token-verified group cleanup, after every session.
Processes without the token are never signalled; each records whether it
carries it, and the session records whether cleanup was verified.
- Report creations and deletions only where a walk listed the nearest
existing directory; count the rest as unconfirmed.
- Retain a file only when it is still the walked version, through owned
no-follow reads; charge the budget only for new content; create files
exclusively in a private output folder owned by the caller.
- Walk content roots outside the home directory, compare paths in their
physical spelling, and run executables from the caller's directory.
- Refuse when the window list cannot be read, re-evaluate window ownership
each sample, handle one queued request per sample with a bounded stdin
queue, and write the capture with end reason `cancelled` on interruption.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep owned cleanup failures and fresh digests
- Allow a capture cancelled before any requested checkpoint to hold only the
baseline.
- Retry an incomplete stop once; if cleanup is still unverified, write the
capture and fail with the run token and process group as cleanup
resources.
- Classify path changes after a walk (ENOTDIR and other selected-file
errors) as changed_since_walk instead of failing the capture.
- Treat a non-directory ancestor as proof that nothing existed beneath it.
- Report a before digest only for the file version it was read from.
- Derive stderr_truncated from stderr's own byte counts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* wip(desktop): state view over a capture folder
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep failed file-handle closes owned for retry
Content reads pass retainFailedClose, so a handle whose close fails stays
owned by the content store; the capture retries those closes after it is
written and fails with the output folder as the cleanup resource if one
still cannot be closed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): read a captured application's state
Add inspect-desktop-application-state, a CLI command that reads a capture
folder into the application's state: every retained or changed file with
its role and reasons, a typed JSON view of each version (SQLite through the
shared worker, dconf through a private dconf dump, JSON, XML, key files,
text) and the kinds of change between versions (lines, values, keys, rows).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): report process and handle cleanup failures together
When both the launched processes and a retained file's handle remain
unresolved after the capture is written, one cleanup error now lists both
reasons and both resource sets.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): export a captured application as an agentenv computer app
Add export-desktop-application, which writes an agentenv bundle with one
computer_app environment from a capture folder and its state view: the
apt package, a boot launch matched on the captured window class with a
window ready check, owned paths from the state roles, a seed of the
baseline files, data hooks that call agentenv's add/get/reset helpers
with a typed JSON view on get, a skill, a README and a deploy smoke task.
The desktop CLI commands now load their services lazily.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep state views faithful to the capture
- Read key files, XML attributes, dconf groups and SQLite tables into
dictionaries without inherited properties, so captured keys such as
__proto__ stay data.
- Version databases with their write-ahead log as of each checkpoint, and
stage only digest-verified bytes for the SQLite and dconf readers.
- Detect the format per version, report format changes, keep JSON null and
empty containers as values, keep XML text and preserved whitespace, and
mark truncated views (including XML depth) and comparisons of them.
- Number versions and changes by checkpoint index, decide baseline
presence from the captured type, and keep tagged SQLite integers, text
bytes and blobs distinct.
- Carry the capture's limitations and omitted content forward, match state
directories by their home path, index changes once per capture, keep
failed handle closes owned, and finish the CLI through the shared owner.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep coverage, logs and handles honest in the state view
- Read coverage flags only from formats whose readers write them, so JSON
fields named truncated stay data.
- Fail a SQLite version whose write-ahead log existed but was not retained
or does not match its digest, instead of reading the database alone.
- Close retained content on every exit, including cancellation, and report
a close that still fails together with the primary failure.
- Finish the CLI with the canonical operation name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): export only what the computer can install and seed
- Refuse packages not from the Ubuntu 24.04 archive, since the computer
installs by name from that archive.
- Start the launch command in the captured working directory.
- Respell the home path only in files the state view read as text.
- Report files that were not retained instead of dropping them silently.
- Load captured dconf keys for apps that own no files.
- Keep home-level files as owned paths; rename reserved Python class names.
- Mark truncated SQLite and text views in the class's get view, and read
file types the way the state view does.
- Release the output tree through a resource scope, and read seeds through
an owner closed on every exit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep staged folders owned, symlinked state roots and XML values
- Stage database bytes in a private runtime root released through a
resource scope, so a failed removal keeps its owner and is reported with
the read's outcome.
- Report cleanup facts and owners from desktop failures instead of
wrapping them as a plain adapter error.
- Record the state directories as walked, in their physical spelling, and
match roles against both spellings, so files in a symlinked state
directory are not mistaken for selected documents.
- Keep whitespace-only XML leaf values; drop only formatting between
elements.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read XML in the exported app the way the state view does
Keep whitespace-only leaf values and xml:space="preserve" text in the
generated get view; drop only formatting between elements.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): release retained handles with their owners, read older captures
- Release retained-content handles whose close failed through a resource
scope on every exit; a release that fails again reports the exact handle
owners. A read that finished keeps its bytes when only its close failed.
- Accept captures written before state directories were recorded, and say
in the state view's limitations that symlinked state directories may be
misclassed for them.
- Compute each XML element's whitespace context once instead of scanning
siblings for every text run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): boot without the working folder, bound XML, accept mirrors
- Try the captured working directory only when an argument is relative,
and start the app even when that folder is missing.
- Cut the generated XML view at REA's node and depth limits, and keep one
unreadable file from failing data/get.
- Accept Ubuntu 24.04 mirrors, not only the canonical hosts; PPAs and
other repositories are still refused.
- Read seeds through the retained-content owner's new signature.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): state view review round
- Stage a retained -journal beside its database so the SQLite worker
accepts an inactive journal and refuses a hot one; a -wal or -journal
that existed but was not retained (including one omitted at the
baseline) makes the version unreadable.
- Keep tables that read when another fails, and let cancellation and
unfinished cleanup propagate instead of becoming view errors.
- Read text as a key file only when every line parses as one.
- Rank a file named in a window title or selected by the caller as a
document even when the application holds it open; match titles on
file-name boundaries; leave the display name out of dconf matching.
- Bound JSON and key file views (4 MiB, 500 levels, exact numbers) and
report too-large, missing and altered retained content separately.
- Add no version or change while a database stays absent.
- Read staged databases in place instead of copying them again.
- Give Evidence the same limitations as the result.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): export review round
- Load captured dconf keys without D-Bus: install dconf-cli, compile the
existing and captured key files into the user database, and warn on
failure instead of stopping the server.
- Start relative-argument launches in the captured working directory,
the home included, since the framework launches from the app folder;
respell the home itself and option values under it.
- Read views in a thread, skip files over 64 MiB, align the generated
readers with the state view's, and close SQLite connections.
- Never own the home or a shared base folder; map a symlinked state
directory's files back to their home spelling.
- Report omitted files and unretained opened folders.
- Name the module so it cannot shadow another, and keep the class off
the module's own names; derive names the bundle check accepts.
- Write seeds as they are read, and remove an unpublished bundle on
failure.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): capture review round
- Ask the launch process group and token carriers to exit before
killing them, on errors too; report incomplete cleanup with its facts.
- Count descendants of the launch as started by the capture even when
their environment cannot be read; treat a reused pid as a new process.
- Follow symlinked desktop entries; name a flatpak's state after its
application id; match flatpak windows by name only.
- Stop on end of input; refuse empty, duplicate and reserved checkpoint
names; require whitespace after the checkpoint keyword.
- Stay on each walk root's device, and never walk or retain `/` or a
directory containing the home; say so in the limitations.
- Refuse a window manager that does not publish _NET_CLIENT_LIST.
- Remove retained content when a capture fails before it is written.
- Skip reading a file larger than the remaining content budget.
- Record apt's archive origin for each package source.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read an unset client list as no windows when supported
xfwm4 leaves _NET_CLIENT_LIST unset while it manages no windows, so the
capture refused an ordinary desktop with nothing open. Read
_NET_SUPPORTED with the list: an unset list means no windows when the
window manager supports it, and the capture is refused only when it
does not. `xprop`'s "no such atom" output now reads as unset too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): accept packages by apt's archive origin
Accept a package source when apt reports its origin as Ubuntu for a
noble suite, so mirrors count and PPAs or other repositories that merely
have "ubuntu" in their URL do not. Captures made before origins were
recorded fall back to Canonical's own archive hosts. Seed reads report
why retained content could not be used.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): capture and export apps from vendor apt repositories
A closed-source app installed from its vendor's apt repository, such as
Typora, can now be captured and exported as a computer app.
- The capture records each non-Ubuntu repository listing the installed
version (uri, suite, components from apt's sources files, one-line or
deb822) and the key that signs it: the signed-by key, or the trusted
key that gpgv shows verifies the repository's signed index. The key
is retained with the capture.
- The capture records the packages owning the libraries the executable
loads (DT_NEEDED with RUNPATH and the linker cache, through REA's ELF
readers), since a vendor package may not depend on them.
- The export declares a verified vendor repository as an AptSource with
its key, installs a vendor package's library packages, and adds
--no-sandbox to an Electron app's launch, since a container denies
Chromium's sandbox.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): report a locked database at once in data/get
A database the running app holds locked made each data/get wait sqlite's
five-second busy timeout before reporting it. Record the Typora run in an
agentenv computer in the verification.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(desktop): detect key files without splitting the whole text
Read lines one at a time and stop at the first that a key file view
would drop, instead of splitting a file of up to 64 MiB into arrays
before its format is known.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep symlinked homes and state folders under the home in exports
- Respell a path under its state folder's logical spelling only when that
spelling is under the captured home, so a symlinked HOME keeps its
files; pair walked and logical state folders only when the capture
lists the same number, and match the innermost folder first.
- When two files map to one home path, keep the one present before the
capture and name the other in the limitations.
- Read key files in the generated data/get with the state view's rule,
checked against it by running the helper under python3.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): end captures on zombies and keep cleanup owners
- A zombie or dead process no longer keeps the session open.
- Remove only the partial capture file this run created.
- Keep the exact owners of handles and processes whose cleanup failed on
the returned error, so a retry can release them; test the published
capture's cleanup-failure branch with mixed outcomes and retries.
- Release the application once even when the exit checkpoint fails.
- A cancellation before the capture is written removes what it retained.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): vendor package review round
- Match apt sources through `apt-get indextargets`, which covers flat `./`
and `/` suites and gives each release file and sources entry.
- Accept a key whose signature verifies even when the release carries
another signature without a key here; keep only the verifying key from
a keyring holding several (OpenPGP packet splitter).
- Install the package from its repository's release (`name/<release>`),
declare one source per URI and suite, support only the capture host's
architecture, and refuse Ubuntu Pro repositories.
- List only library packages the computer can install; walk libraries only
for vendor installs, with RPATH inherited as ld.so does, normalized
paths, and DT_NEEDED bounded while reading.
- Put --no-sandbox after the program an env wrapper launches; keep the
gpgv folder's owner when its removal fails; respect --max-file-bytes for
keys; wait 0.25 s on a busy SQLite database in data/get.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): keep the vendor spec's gpgv command on one line
A code span broken across lines left `<binary keyring>` for VitePress to
read as an unclosed HTML tag, so the docs build failed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): add a validated task to exported desktop applications
Each capture can now yield one agentenv task in its exported bundle. An
agent writes the task (a prompt plus declarative checks over the app's
data/get views); REA validates it and stays model-free.
- `describe-desktop-application-task` summarises a capture's net change
per file (role, format, views before and after) and drafts checks.
- Checks: file presence, lines present/absent/counted, JSON-pointer values,
SQLite rows and counts. REA keeps a task only if a check fails on the
seed and every check passes on the captured end state; a file that was
not retained at the baseline counts as absent, as the computer starts.
- `export-desktop-application --task` writes tasks/NAME.json (deploy,
agent, skills, recording, prompt, verifier), a compiled verify.py that
reads data/get, and an eval. A boundary test runs the verifier under
python3 and checks it agrees with REA's evaluator on every check kind.
- Docs, spec and skill guidance, with the solve-rate recipe.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): keep the seed test in its own block
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): record the task verifier driven in an agentenv computer
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): record the Typora task run in an agentenv computer
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep task checks bounded, complete and attributable
- line_count counts lines holding a substring (contains) instead of
matching a regular expression, which could stall the verifier on
agent-written text and read differently in Python.
- value_equals of a whole object or array is undecided on a truncated
view.
- The draft keeps a database whose rows changed under an unchanged hash,
and a file whose truncated views match while it changed.
- A task file that cannot be read keeps the reader's detail, and a failed
cleanup keeps the read failure as its primary.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): leave a file with one version out of the task draft
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): update the task verification's test counts
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): record the task solve rate with Claude Code and Sonnet 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): capture and export apps installed from a downloaded .deb
capture-desktop-application --package-file reads the file's control fields
with dpkg-deb and records its SHA-256, size and dependencies when it holds the
installed package; the file itself is never kept. The library walk now also
runs for local installs.
export-desktop-application --package-url declares agentenv's
Download(url, sha256) for that file, its dependencies and library packages as
apt packages, and a run script that installs it with dpkg.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): seed app-named dotfiles and respell UTF-16 seeds
The capture now treats dotfiles in the home named after the application
(.varicad-cfg.ini, .notesrc) as state roots, so a file the app only reads
during the capture is still seeded. The exported seeding respells the
captured home in UTF-16 text files with a byte-order mark, keeping the mark.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): hold readiness until the app's own window shows
An exported app is ready only once a window of its class that the taskbar
lists is shown, so a splash screen of the class, which skips the taskbar, no
longer counts as the app being up.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): record VariCAD captured, exported and run in an agentenv computer
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep exported views strict JSON for infinite and inexact numbers
The exported data/get spells infinite and negative-zero SQLite reals as REA's
SQLite reader does ("Infinity", "-Infinity", "-0"), and refuses a JSON file
holding a number that reading would change (1e999, integers past 2^53, -0,
rounded fractions) with the state view's own error, instead of a view with
an infinity in it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep package-file facts tied to one file and URLs unechoed
- A --package-url refusal never repeats the URL, and credentials are checked
before the scheme.
- The package file's identity must hold between dpkg-deb's read and the hash.
- A failed close keeps its cleanup owner even when the capture is cancelled.
- Readiness also ignores windows typed as splash screens.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): record database logs and keep state changes distinct
- A selected path that is not a directory brings its -wal and -journal as
content roots, so the capture records whether they exist and retains them.
- The state view treats a file as absent only under recorded content roots
and refuses a database whose log presence is unknown.
- JSON changes list containers too, so an array that becomes an object with
the same members is a change.
- Key file entries escape the key as a JSON pointer segment, so [a/b] c and
[a] b/c stay apart.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep watching through unread samples and late workers
- An unreadable window list is unknown, not zero windows, and is named in
the limitations.
- Before ending the session, live run-token carriers no parent link reached
are tracked; an unreadable process table leaves the exit unknown.
- An env prefix's assignments find the program and name its XDG folders.
- flatpak run options that take a value are skipped with it, -- is honoured
and a /arch/branch suffix is dropped from the id.
- State directories include the entry's full id.
- Path options refuse empty values; roots count toward the entry limit.
- A capture file whose close failed stays owned and is retried on discard.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): bound apt sources, keep older captures and cleanup owners
- Sources files parse into one stanza per line or deb822 paragraph with its
URIs and suites kept as lists, so no URI x suite product is built.
- Repository architectures and release are optional, so captures written
before they were recorded parse; the export treats them as unrestricted
and unknown.
- Library sources are matched against every index of a declared
repository's URI, suite and components.
- Two failures side by side keep both causes and both cleanup owners, and a
host file whose close fails reports a lease on its handle.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): match escaped key file entries and suggest containers through members
- The draft's key file lookup uses the state view's escaped `group/key`, so a
key holding `/` or `~` still gets its suggestion.
- JSON and XML changes now list containers too; the draft suggests an added
or removed container through its listed members, and a container only
when none of its members is listed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read relative PATH entries from the launch folder
A program, or an empty or relative PATH entry, is resolved from the folder the
program starts in, as the launch does, so `env PATH=. app` with the entry's
Path still finds the app.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(desktop): compare JSON views on one shared path
JSON and XML changes are found by walking both trees with one shared pointer
path, building a pointer only for a listed value, so memory follows depth
rather than every container's full path. A pointer over 4096 characters is
counted but not listed. A 256-level document with 4000-character keys went
from about 251 MiB of heap to under 1 MiB.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep JSON Lines as text in the exported reader
The exported reader parses the whole text before an inexact number makes it
JSON with an error, so JSON Lines with an integer above 2^53 stay text as in
REA. Over-long integer spellings are unsafe without converting them. The
guide no longer claims large integers keep their value.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep the state-path help free of dash-led words
The CLI alias check reads `-wal` and `-journal` in help text as short flags,
so the help names the write-ahead log and rollback journal instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): never record or export apt source credentials
- Source URIs lose any user name and password as they are parsed, the way apt
splits them, and so do index-target URIs and descriptions and the sources
apt-cache policy lists, so matching works whether or not apt prints them.
- A repository whose entry held credentials is marked
`uri_credentials_removed`, and the export refuses it, or an older capture
whose URI still holds them, with a reason that names no URL.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): look programs up without normalizing their paths
Program paths and PATH entries are joined rather than resolved, so `..` after
a symbolic link means what it means to the launch. The lookup moves to its own
module.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): note inexact JSON numbers with a flag, not a list
The exported reader only needs to know whether any number was inexact, so it
keeps a boolean instead of every rejected spelling.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): refuse older captures that still hold apt credentials
A capture written before credentials were removed counted the credentialed
repository's libraries as installable, so the export refuses it whole and asks
for a new capture rather than declaring packages no source offers. Recorded
policy sources are redacted in export messages too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): keep the credential fixture on an example host
A login on a real host reads as a secret to the commit scanner, so the older
capture's fixture uses apt.example.com like the other credential tests.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): tidy looked-up paths and search relative entries only for env
Empty and `.` segments are dropped from a looked-up program path, which keeps
`/snap/bin/` entries recognisable as snaps, while `..` is still left for the
kernel. Relative and empty PATH entries are searched only in the PATH an `env`
prefix sets, which travels with the exported launch; the host's own PATH does
not.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep excluded and unwalked database logs unknown
The capture records the paths every walk left out, and the state view treats
a log as absent only under content roots the baseline walked completely and
outside those exclusions, so an excluded or unvisited log leaves the database
unread rather than read without it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): judge JSON numbers after duplicate keys settle
The exported reader marks an inexact number instead of flagging it during the
parse, then looks for marks in the parsed value, so a number a later duplicate
key overwrote no longer refuses the file, as in REA.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): redact a repository's policy source when it names it
An older capture whose policy source matched no sources entry kept that text
whole, and the export named the repository by it; the export now names it
without any user name or password.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): never signal the launch group on unknown membership
When the current read cannot observe a carrier's process group, the carrier
is signalled on its own instead of letting an older process-table row grant
a group-wide SIGTERM.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): look programs up in the launch's order and refuse host-relative finds
The lookup searches PATH as the launch does, an empty entry meaning the
launch folder, so inspection and launch name the same program. A program found
only through a relative or empty entry of the host's own PATH is refused,
since the exported computer would not have it; an env prefix's PATH travels
with the command.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): count unchecked paths and keep logs behind them unknown
Each walk counts the paths it could not stat and the directories on another
device it did not enter, and the checkpoint records it as `unchecked_paths`.
The state view treats a log as absent only when the baseline left nothing
unchecked, so a log whose stat failed stays unknown.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(desktop): scan parsed JSON with one iterator per level
The check for marked numbers walks the parsed value with an iterator per
nesting level instead of copying each container's children; a million-entry
array went from about 15.7 MiB to 8.1 MiB of peak traced memory.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): start a relative program from its captured folder
An exported launch whose program path is relative, such as `./app` from an
entry's Path, now changes to the captured working directory first, as it
already did for relative arguments.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(mcp): guide retained evidence input recovery
* style(mcp): format catalog and server types
* test(mcp): verify HTTP capacity recovery guidance
* test(mcp): model catalog recorder imports in cleanup fixtures
record-mcp-catalog.mjs loads tool contracts and the Evidence-input
contract before it registers cleanup owners. The cleanup fixture still
sentineled only the previous imports, so those loads failed before any
owner wrote cleanup.jsonl. Track the script's read() calls and replace
the new modules with the empty contract list this fault injection uses.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(mcp): report selection after failed target opens
* fix(mcp): retain resource root selection in open failures
* test(session): assert selection during cleanup failures
* test(mcp): delay failed open response across target switch
* test(session): distinguish no selection from pending cleanup
* test(session): assert selections during cleanup and cancellation
* test(mcp): join the failed-open gate before restoring its spy
The snapshot-switch gate waited only on its acknowledgement, so a request
that settled early hung until the runner timeout, and a later assertion
failure restored the spy while that request was still in flight. Race the
acknowledgement and join the launched open in finally before mockRestore.
---------
Co-authored-by: Cursor Agent <[email protected]>
* feat(native): page retained feature traces
* fix(snapshot): retain linked source evidence for replay
* test(snapshot): verify linked foreign evidence stays scoped
* docs(mcp): clarify imported residual questions
* docs(mcp): guide trace reference classification
* fix(cli): report result sources when Evidence publication fails
A trace keeps linked sources on the result. Use those records for the
undelivered source closure as well as the bundle, so a refused output
does not report them as not captured.
* ci: rerun pull request checks
The merge of origin/main did not start the CI workflow for this head.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(native)!: preserve value-flow absence semantics
* test(native): compare value-flow reasons with their owners
Stop freezing the unrecorded and Kuna unavailable sentences. Assert the
procedure-scoped unknown and that an explicit provider reason is preserved.
---------
Co-authored-by: Cursor Agent <[email protected]>
* feat(artifacts): inventory and extract ESP32 firmware images
ESP32-family app and bootloader images (magic 0xE9) had no reader, so
open_binary refused OTA downloads, app partitions and bootloaders.
Add the esp-image container format behind inspect_artifact and
extract_artifact. A pure decoder reads each byte once and keeps only
fixed-size fields. Derived facts cover the chip, from a table cited to
ESP-IDF v6.1; segment classification as ESP-IDF's should_map and
should_load; checksum and appended SHA-256 results; app and bootloader
descriptors; Secure Boot v2 signature blocks, observed and unverified;
and undescribed trailing byte ranges. Truncation and undescribed bytes
make coverage unknown. ESP8266 images are refused with a specific
reason, and ciphertext stays unrecognized.
Add the format to every target, Evidence, snapshot, live-summary and
artifact vocabulary, document it, and add an optional verify:esp-images
lane that compares esptool-built images with esptool image-info and
espsecure signature-info-v2.
Refs #3615
* test(artifacts): run the ESP image lane on esptool v5.6.0
The lane compared descriptor fields only when esptool image-info printed
one, so a descriptor that REA reports and esptool does not print passed
without comparison. Compare the descriptor kind in both directions.
Record that the lane passed for all 13 images with esptool and espsecure
v5.6.0, the version the layout facts cite, as well as with 4.12.0.
* refactor(artifacts): define ESP image layout sizes once
The decoder and the derived facts each defined the segment-header,
appended-digest and flash-sector sizes, the erased-byte scan and the
offset formatter. Define them once with the other fixed-layout structures
and import them in both places. The signature slot count is the one
constant that drives the slot scan.
Correct the observedEnd comment: it is the end of the last chunk read.
* fix(artifacts): report ESP-IDF's segment length limit by chip
ESP-IDF's verify_segment_header rejects a segment of 16 MiB or more
unless the chip maps flash through 32-bit cache addresses
(SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP: ESP32-S3, P4, C5, C61, H4 and
S31 in v6.1). Record that capability in the chip table and add a
limitation for such segments; unknown chips get none.
`loaded` repeats should_load. It does not mean the bootloader accepts the
segment: verify_load_addresses still rejects one outside the chip's RAM,
which REA does not model. Say so in the docs, the skill reference and the
inventory limitation.
* test(artifacts): check ESP lane tools first and verify extraction
The lane started work before checking its tools, so a missing RISC-V
target, ld.lld, esptool or espsecure surfaced as a raw spawn or command
error. Check each one before building and name the variable to set. Warn
when esptool is not v5.6.0, the release the layout facts cite.
Assert each segment's class for the ESP32-C3 app, its variants and the
bootloader, as the Xtensa image already did. Extract the app and the
signed image through the CLI and compare every file with the bytes at
esptool's segment offsets or in the appended signature sector; the
source's SHA-256 must be unchanged and equal the reported root_sha256.
* docs(artifacts): state what the ESP image lane does not establish
The Verification section listed the lane's checks but not its gaps: only
ESP32-C3 and ESP32 images come from a real producer, no real project
build, OTA download or flash dump is inspected, no signature is verified,
and the lane does not run in CI.
Record the timing of the final lane run on esptool v5.6.0.
* fix(artifacts): compare ESP segment declarations
Segment entries hash only their data, so compare_artifacts reported a
segment that moved to another load address with the same bytes as
unchanged; only the image itself changed. Compare each segment's
load_addr and data_len as its declaration, as Node SEA and PyInstaller
members are compared, and count a changed declaration as a metadata
change, also when content is unavailable.
Header offsets are layout and the classification is derived, so neither
is compared.
* test(artifacts): run ESP lane commands as owned process trees
The lane ran its tools through execFile. A timeout killed only the direct
child, so a linker or assembler that Clang or GCC had started could keep
running while the lane removed its workspace. Run every command through
createVerifierCommands, check the tools inside withVerifierCleanup, and
remove the workspace only after commands.close() has stopped every tree;
a failed close keeps the workspace.
Only a nonzero exit now counts as a refusal or an expected esptool
failure; timeouts and spawn failures propagate. Supervising each command
roughly doubles the lane's run time.
* test(artifacts): cancel ESP decoding after its first decoded chunk
The cancellation test requested cancel or close on setImmediate, which
can land while the file is still being opened and checked, so it never
showed that decoding had started. It also passed against a reader that
ignored both requests while decoding. Acknowledge the decoder's first
chunk, then cancel or close, join the operation, and assert that no
later chunk reaches the decoder and that the reader ends closed.
* docs(artifacts): record which ESP metadata comparison ignores
Segments are compared by their declaration. The image's and the
signature blocks' format_metadata are not: every field comes from their
own bytes, which content comparison already covers, and a block's
image_digest_matches follows the image's content.
* feat(artifacts): inventory and extract ESP flash layouts
A whole-flash dump of an ESP32-family chip was read as its bootloader
image, with the partition table, apps and data left as undescribed bytes,
or refused when it began with erased flash. Read it as an esp-flash-image
container instead.
The partition table is decoded as esp_partition_table_verify accepts it
and is found at the caller's partition_table_offset, at the default
0x8000, or by scanning 0x1000-aligned sectors for a table whose MD5
verifies, which the inventory reports as derived. The bootloader is
located at its chip's ROM bootloader offset. Each partition is an entry
with its declaration in flat format_metadata; the bootloader and app
partitions carry ESP image facts from the existing streaming decoder; the
otadata records are reported with a selection derived without rollback or
anti-rollback, and the running partition stays unknown. One unreadable
partition does not hide the others, and extraction writes every partition,
nvs_keys included, as ordinary data.
Flash layouts are detected before single ESP images. The scan runs only
when an ESP image sits at a ROM bootloader offset, and is refused when a
bootloader at another chip's offset shows that the dump does not start at
flash address 0. open_binary gains flash_offset and partition_table_offset,
which require format esp-flash-image and are part of the snapshot target
identity; the CLI takes them with --target-format esp-flash-image.
Comparison treats a partition's label, type, subtype, range and flags as
its declaration.
* test(artifacts): verify ESP flash layouts with gen_esp32part, merge-bin and otatool
Add the optional verify:esp-flash lane. It builds default and relocated
partition tables with ESP-IDF's gen_esp32part.py, assembles dumps with
esptool merge-bin, including one written from flash 0x1000, and writes
OTA records with otatool through flash_file_tool.py, which replaces
parttool's esptool calls with reads and writes of the dump file.
It compares REA's bootloader and app facts with esptool image-info, every
partition with gen_esp32part.py's decoding of the extracted table, and the
OTA records and derived slot with otatool read_otadata after each switch.
It also checks extracted bytes and source integrity, the scan and caller
table offsets, flash_offset on the 0x1000 dump, a corrupted app beside
valid partitions and a truncated dump. ESP32 linker scripts place the
conformance bootloader and app in ESP32 address ranges.
* fix(artifacts): read ESP flash layouts as the bootloader does
Read the partition table as bootloader_utility_load_partition_table
does: the last factory, otadata and same-subtype OTA declaration wins,
and a limitation names repeats. Plan each otadata record within its
partition and the dump. An otadata partition smaller than two sectors
selects none, the bootloader's INVALID_INDEX; a record the dump lacks
leaves the selection unknown while the other record stays reported. The
fallbacks follow the bootloader's indexes: ota_0-fallback and
factory-fallback name no partition when the table declares none, and the
assumptions add GPIO holds and deep-sleep wake.
Refuse a dump whose bootloader contradicts its origin before the default
table offset is tried, whether flash_offset was omitted or given, and keep
the ESP image reader's ESP8266 refusal. A table never lies inside the
located bootloader image, so single app images are no longer scanned. An
explicit esp-flash-image selection is decided before other container
signatures, and the native commands' --target-format no longer takes it.
Say when no bootloader confirms the default origin.
Exempt the primary bootloader and partition_table entries of ESP-IDF 5.3
from placement limits, report every region inside a longer one, and name
partitions by index when labels repeat ignoring case. Keep an undecodable
label's bytes in label_hex and compare them. Word undescribed ranges as
starting at a byte that is not erased, and treat a short erased region as
erased.
Add an MCP compare_artifacts test for partition declarations, reader
identity and close tests, scan edge cases, the 0x2000 ROM offset and a
literal otatool CRC.
* test(artifacts): verify ESP primary layout entries and the otadata fallback
Build a table with the primary bootloader and partition_table entries
that gen_esp32part.py fills from --primary-bootloader-offset, and check
that REA reads it with complete coverage, no overlap or misplacement
limitation, and a table that agrees with gen_esp32part's decoding. Erase
the otadata with otatool after the slot switches and check the records
against read_otadata and the factory fallback.
* refactor(artifacts): share positional entry lifecycle
The ESP image and ESP flash-layout readers carried the same 138 lines of
lifecycle code. PositionalArtifactReader now owns it: one preparation
under the reader's scope, a carrier recheck before each traversal,
refusal of forged entries, range streams that check their length and
recheck the carrier when they end, and a close that retires open
streams before it releases the carrier.
Each reader keeps its location, decoding, entries and metadata, and
returns its entries and coverage from prepareEntries(). Entries carry a
required byte range, which removes the unreachable null-range checks in
open(). Failure reasons, messages, order and cancellation do not change.
The Godot PCK reader keeps its own copy. It reports a member that ends
early as a format failure, where the ESP readers report integrity, and
the helper cannot express that without an option. The Node SEA reader
has the same difference.
* fix(artifacts): report Godot PCK and Node SEA members that end early as integrity
Behavior change: a Godot PCK or Node SEA member stream that delivers
fewer bytes than its entry declares now fails with reason "integrity"
and the message "<label> entry ended early: <path>". Before, it failed
with reason "format" and "<label> is malformed: member ended early:
<path>". Callers see the category integrity_mismatch instead of
execution_failure. The remediation text does not change.
Only a carrier that changes after open() rechecks its identity reaches
this path. The directory was valid when the inventory was taken, so a
short member means that the carrier changed, not that it is malformed.
The identity recheck that runs next also reports integrity, and the ESP
and ASAR readers already report a short member as integrity.
The new tests end the admitted descriptor's reads inside a member after
open() and check the reason and message for each reader.
* refactor(artifacts): move Godot PCK and Node SEA onto the positional lifecycle
The Godot PCK and Node SEA readers carried their own copies of the
positional entry lifecycle. They now extend PositionalArtifactReader and
keep only their location, decoding, entries and metadata.
prepareEntries() returns the entries and the coverage that each reader
reported before. Godot reports unknown coverage with one limitation for
an encrypted or sparse-bundle directory. Node SEA reports complete
coverage with its fuse contradiction as a limitation.
The base class takes two rules from these readers:
- open() refuses an encrypted entry as unavailable, after the
forged-entry check and before the carrier recheck: "Encrypted <label>
member is unsupported: <path>". ESP entries are never encrypted.
- Traversal yields to the event loop every 256 entries, starting before
the first entry. The ESP readers gain one yield before their first
entry. This changes scheduling only.
Failure reasons, messages, order and cancellation do not change. A
member that ends early has failed as integrity since the previous
commit. Godot's directory parse still checks only cancellation, so close
does not stop it early. Node SEA's close still drops an admitted decode
that no completed preparation released. Each reader now stores its
entries only after preparation succeeds; a failed preparation never
served them before either. PositionalArtifactEntry requires a byte
range, so the unreachable null-range checks in open() are gone.
* fix(artifacts): keep ESP flash coverage complete for a dump cut short
An esptool merge-bin dump ends after the last part it wrote, usually inside
its last partition. Such a partition made the layout's coverage unknown, so
artifact comparison of two merge-bin dumps reported every unchanged
partition as unknown.
The partition table names every partition whatever the file's length, so a
partition cut short or left out by the end of the file hides no entry. As
the ESP image reader does for a cut-short segment, its entry holds the bytes
the file has and an entry limitation gives the declared size; a partition
with no bytes stays in partitions_outside_dump. Coverage stays complete
unless undescribed bytes remain or no bootloader is located. An MCP
compare_artifacts test of two dumps that end inside ota_0 now classifies
every partition, and fails on the old rule.
* fix(artifacts): report written bytes in ESP partitions, erased slots included
An app slot is erased for ESP-IDF's bootloader when its image header bytes
are erased flash, and REA reported only that status. A slot can still hold
data further in: a dump read from a device held a complete older app image
1 MiB into an OTA slot reported as erased, with no limitation.
Each partition entry now records where the bytes the dump holds are
written, relative to the partition: written_offset, the first byte that is
not 0xFF, written_end, just past the last, and written_bytes, how many are
not 0xFF. They are null for a partition outside the dump. The status stays
erased, which is the bootloader's view, and a limitation names an erased
app slot that holds written bytes and says they are not read as an image.
The reader reads every partition byte once more to find them.
* fix(artifacts): derive the ESP boot selection for every partition table
The boot selection lived inside otadata, so a table without an otadata
partition, the common factory-only layout, had none. ESP-IDF's
bootloader_utility_get_selected_boot_partition returns the factory index
when the table declares no otadata partition.
Move it to a top-level boot_selection that every layout has. Its basis
names the bootloader's branch: otadata, factory-fallback and ota_0-fallback
from the records, no-otadata for a table without otadata, none for the
bootloader's INVALID_INDEX, and unknown when the dump lacks a record the
bootloader reads, which replaces the null selection. running_partition
moves with it, and otadata keeps its partition index and records. The
assumptions name what each branch leaves out: no records assumption
without otadata, and none for none or unknown. The real-tool lane checks
no-otadata on a table gen_esp32part built without otadata.
* fix(artifacts): say when an ESP image is a flash dump read from 0x1000
An ESP32 dump read from flash 0x1000, as read-flash 0x1000 or merge-bin
--target-offset 0x1000 writes it, opened automatically as its bootloader
image with a megabyte of undescribed bytes and no next probe. The flash
reader refuses that origin, but nothing told an agent which flash_offset to
pass.
espFlashDumpOrigin owns the inference: an image of a chip whose ROM loads
the bootloader from flash R, at file offset F, is the bootloader of a dump
read from R - F when a partition table sits where that origin puts 0x8000.
The ESP image reader asks it for a non-app image at file offset 0, so the
result does not depend on how the reader was created, and records
flash_dump {flash_offset, table_file_offset}, null otherwise, with a
limitation that says to reopen the file as esp-flash-image with that
flash_offset. inspect_artifact proposes the same open_binary call as a next
probe, and the flash reader's misplaced-origin refusal names the offset.
* perf(artifacts): summarize nested ESP images in the flash layout summary view
The summary view returned an ESP flash layout's root facts whole, so each
app partition and the bootloader carried every segment and signature
block, about half of a dump's summary.
The summary view now gives each nested image a summary instead of its
facts: status, chip, entry point, segment count, load ranges (each mapped,
loaded or unclassified segment's address and declared length), the
checksum and appended SHA-256 statuses, coverage and limitations. Complete
detail keeps the full facts, every segment included, and the retained
Evidence is unchanged. On a 16 MiB device dump the summary's root falls
from 5,141 to 2,657 bytes, and the summary Evidence from 10,038 to 7,554.
* test(artifacts): expect the misplaced image the ESP bootloader search reports
8a748d3cc added misplacedImage to the bootloader search, which the
locator uses to name the origin a misplaced bootloader implies, but one
test still compared the search result without it. Expect the image's
chip and file offset, and no image for an unknown chip.
* fix(artifacts): give every ESP partition from the dump's start an entry
After ecbc1a772 kept coverage complete for a dump cut short, a partition
wholly past the end of the file still had no entry, only a
partitions_outside_dump row. Comparing a merge-bin dump with a full device
read then reported that partition as added or removed, as if one table
did not declare it.
Every partition from the dump's first byte on is now an entry holding the
bytes the file has: none for one past the end, with a limitation saying
the file holds none of its bytes, no image facts and null written fields.
partitions_outside_dump keeps only partitions that begin before the dump's
first byte, and coverage is unknown when one does, since it has no entry.
Two dumps that lack the same partition now compare it as unchanged, and a
dump against a longer one reports the partition as changed; an MCP test
of a merge-bin dump against a full one checks that no path is added or
removed.
Unlike the ESP image reader, which marks a truncated image's coverage
unknown because later segments and signature blocks may be hidden, the
flash reader can list every partition from the table whatever the file's
length; only the entry limitation follows the image reader.
* fix(artifacts): select no ESP app from a partition table the bootloader rejects
boot_selection followed bootloader_utility_get_selected_boot_partition but
not the table check before it. For a table whose MD5 does not match, or
that has a second MD5 entry, esp_partition_table_verify fails, the
bootloader selects no app and resets. REA still derived a slot from the
otadata records, although the table's own limitation said the bootloader
rejects it.
The selection now has basis none, with no assumptions, for a table the
decoder marks invalid. It also follows two more branches of the
bootloader: an otadata partition at offset 0, which the bootloader takes
for none, selects the factory index as no-otadata, and a table without an
OTA app selects its fallback even when the dump lacks a record, since the
bootloader then ignores the records.
The bootloader also rejects a table whose partitions end past the flash
size in its header. REA does not model that check; the format guide says
so.
* fix(artifacts): require the ESP dump origin's table to follow the image
espFlashDumpOrigin named a later flash origin whenever a partition table
decoded where that origin puts 0x8000. Bytes inside the image's own
segments could be read that way, so an ESP32 bootloader whose segment data
held table-shaped bytes at file offset 0x7000 was reported as a dump read
from 0x1000.
The table must now start at or after the image's end, the rule the locator
already applies to the default table. The ESP image reader passes the
image_end it decoded, or the file size for a truncated image; the flash
reader's refusal passes the end of the misplaced image's segments. The
bootloader search drops its misplaced flag, which only restated
misplacedImage, and the next probe formats offsets with offsetText.
Reader tests cover the 0x1000 and 0x2000 ROM offsets, a standalone
bootloader, bytes that begin like a table, an app with a table at 0x7000
and a table inside a segment; without the boundary check, three of them
fail.
* fix(artifacts): give the ESP flash summary its own kind and each image's identity
The summary view's ESP flash root kept kind esp-flash-image, so a consumer
bound to kind could not tell an image's summary from its full facts, and
the advertised schema held two objects with the same kind const. It is now
esp-flash-image-summary.
The per-image summary also dropped the descriptor, which is what tells two
app slots apart, and named each image only by its partition index, which
the summary view, having no entry rows, could not resolve. Each nested
image now carries its entry path from the layout plan, in both views. The
summary adds the descriptor kind with its identifying fields (an app's
project_name, version, idf_ver, secure_version and app_elf_sha256; a
bootloader's version, idf_ver and secure_version), the signature block
count and each load range's classification. Its enums and fields derive
from the schemas that own them.
An MCP test reads a dump with an app in ota_0 and an erased ota_1 that
holds older bytes: it checks both summaries, that the summary's inventory
Evidence is the one the complete view returns with full facts, and the
erased slot's written range.
* test(artifacts): cover cancel and close during the ESP written-bytes scan
The written-bytes pass reads every byte of each partition the dump holds,
checking that the reader is open before each chunk, but only app decoding
had a cancellation and close test. A data partition, which only that pass
reads, now holds 9 MiB; once the scan has examined its first chunk, a
cancel rejects preparation with the caller's reason and a close with
unavailable, and the scan examines no further chunk.
The erased-slot test now asserts the whole set of erased-slot limitations,
so factory, ota_0 and nvs, which the bootloader reads or which are not app
slots, are shown to have none.
* docs(artifacts): re-time the ESP flash lane and state only observed causes
The lane ran again after its assertions changed; both esptool runs passed,
and the timed run's figures replace the earlier ones. The erased-slot
paragraph no longer names a cause for the older bytes, which REA cannot
observe, and the skill reference states that every table has a
boot_selection in its own sentence, with the none basis for a table the
bootloader rejects.
* test(artifacts): open an ESP partition past the end through the positional reader
A partition that lies past the end of a dump is an entry with no bytes,
and the merge with the positional lifecycle gives it the empty range at
the end of the file. The inventory tests listed that entry but never
opened it. Opening it through the shared reader now yields no bytes, and
its range is the file's size with a declared size of 0.
* fix(artifacts): retire ESP flash members as the ESP image reader does
#7498's maintainer commits made the ESP image reader keep a member owned
until its consumer closes it, retire fully buffered bytes when
cancellation arrives after production, and refuse to finish an inventory
paused across close. The ESP flash reader was copied from the earlier
reader. Main's shared stored-member lifecycle cases failed two of eight
against it: a paused inventory completed after close, and buffered member
bytes stayed readable after cancellation.
Give the flash reader the same lifecycle and run the shared cases against
it.
* docs(artifacts): re-time the ESP flash lane after merging main
The lane passed again with esptool v5.6.0 and 4.12.0 on the merge of upstream main. Record the v5.6.0 run's time and peak resident set.
* fix(artifacts): read ESP image Evidence recorded before flash_dump
The flash layout work made `flash_dump` a required field of the
`esp-image` root metadata. Evidence recorded before it has no such
field, so newer REA rejected records that were valid when they were
made: `compare_artifacts` and `inspect_analysis_view` reported
`evidence_integrity_mismatch` for them.
The field is now optional when read. A record without it parses as it
is, so the graph digest it commits to still matches; nothing fills the
field in. The image reader still writes it for every image, and the
inspection probe reads a missing field as no hint.
A regression test recommits an image inventory without the field and
passes it inline to `compare_artifacts` and `inspect_analysis_view`.
Against a server built with the required field, `compare_artifacts`
rejects it.
* fix(artifacts): stop the ESP flash gap scan when the reader closes
The scan for the first written byte in each undescribed gap checked
only the caller's signal. `close()` marks the reader closed and waits
for preparation, so it waited while the rest of a long erased gap was
read.
The gap scan now takes the reader's open check, as the written-bytes
scan and the image decoder do, and checks it before each chunk. Tests
cancel or close the reader after the scan's first chunk of a 9 MiB
erased gap and confirm that no later chunk is examined.
* fix(artifacts): name ESP partitions with Windows device labels by index
The safe-label rule accepted labels such as `NUL`, `con` or `COM1`, so
extraction wrote `partitions/NUL.bin`. Windows reserves those names
before any extension, and ordinary Windows tools cannot use such a
file.
A label that is a Windows device name (`CON`, `PRN`, `AUX`, `NUL`,
`COM0`-`COM9` and `LPT0`-`LPT9`, any case, alone or before a dot) now
takes the existing index fallback. The partition metadata and the
entry's source path keep the label.
* fix(artifacts): read ESP flash partitions' written bytes in one pass
Each partition got its own scan of the bytes the dump holds, so
partitions declared over the same bytes read them again and again.
Overlaps only add limitations, so a crafted table reached this normally:
94 overlapping partitions over a 32 MiB dump would examine about 3 GiB
before the first entry.
The reader now reads the bytes the partitions cover once. Partition
boundaries cut them into segments; one pass counts each segment's
written bytes and bounds, and each partition's extent is the sum of the
segments inside it. Every partition keeps its own declaration, extent
and limitations. The pass checks the reader's open state before each
chunk, as the per-partition scan did.
A test declares 60 partitions over one MiB, 40 over all of it and 20
over its first half, and checks each partition's extent and the
overlap limitations. The reader reads 1,113,256 bytes of the
1,114,112-byte dump; one scan per partition read 52,493,480.
* test(artifacts): fail the ESP lanes on failed commands and refusals
`otatool` turned a nonzero exit into returned text, which its callers
discarded, and `inspect()` returned `{ refused }`, which the flash lane
never checked. A failed command then surfaced later as a slot
assertion, an output-parser error or a TypeError, hiding its cause.
Commands the lanes need to succeed now reject with the output they
exited with: `otatool` and `inspect()` do so through a shared
`failedCommand`. An expected refusal is opt-in: `refusal()` returns
REA's output and fails the lane when REA opens the file. The ESP image
lane's two ESP8266 cases use it.
Checked by calling `otatool` on a file without a table, `inspect()` on a
file REA refuses and `refusal()` on a file REA opens; each rejects with
its cause.
* fix(artifacts): require a partition entry in an ESP partition table
The decoder counted the slot, not the partitions, so a lone MD5 entry
over the empty prefix, followed by the terminator, decoded as a table
with no partitions. Default and caller-selected detection then accepted
that empty layout, although the guide requires one partition.
A table now needs one partition entry before its terminator. An MD5
entry alone is refused as `entry 1 at 0x20 ends the table before any
partition entry`. The cases cover the decoder and both automatic and
caller-selected location.
* fix(artifacts): decode an image shared by ESP app partitions once
Each app partition got its own decode, so app partitions declared over
the same bytes decoded and hashed the same image again and again, the
same pattern the written-bytes scan had. With 60 app partitions over
one 960 KiB image the reader read 64,029,736 bytes of a 1,114,112-byte
dump.
Regions with the same file range now share one decode; each keeps its
own copy of the facts. Partitions with different ranges still decode
separately, because truncation can change the facts: at most one decode
per partition the table holds, each within its partition.
A test declares 60 app partitions over one image, 40 holding all of it
and 20 cutting it short, and expects three decodes: the bootloader, the
whole image and the cut one. The 60-partition dump now reads
2,161,864 bytes, as a dump with one app partition does.
* test(artifacts): run the shared member lifecycle cases on Godot PCK and Node SEA
The Godot PCK and Node SEA readers now extend PositionalArtifactReader,
whose member lifecycle comes from #7498's ESP reader. Main's shared
stored-member cases ran only for the ESP and uImage readers. Run them for
these two readers as well.
On main `6e9ee97a6`, both readers fail two of the eight cases: a paused
inventory finishes after `close()`, and member bytes buffered before
cancellation stay readable. On the positional base, all eight pass.
---------
Co-authored-by: morluto <[email protected]>
* feat(artifacts): read WeChat mini-program wxapkg containers
Add a read-only reader for plaintext wxapkg packages: the 0xBE/0xED
18-byte big-endian header, the bounded name/offset/size member table,
and verbatim member bytes at absolute carrier offsets.
- Members retain declared names, absolute offsets and sizes, so
source_location reports real carrier positions.
- V1MMWX PC-client packages are detected as encrypted coverage with
null member facts; extraction refuses with a typed error and no
decryption is attempted.
- Bounded table and directory budgets are admitted before allocation;
malformed marks, truncated tables, invalid UTF-8 names, member
extents inside the header or beyond the file, and counts that cannot
fit their table are typed format or limit failures.
- Member reads stream in bounded 64 KiB chunks with carrier freshness
checks; the reader joins outstanding streams before close and bounds
active streams and repeated member I/O.
Refs #8121.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(artifacts): bound wxapkg members to the declared data region
Normalize the two indexLength conventions before the table bounds check
and the table read. Member ranges must then lie in that declared data
region, which has to fit in the file.
Record wxapkg format metadata as not compared: header facts repeat the
package bytes, and entry source paths keep a producer root prefix.
* fix(artifacts): choose the wxapkg index after member ranges fit
Four trailing bytes made the rows-only layout end on the file and dropped
the count-inclusive layout. Keep every admitted layout until the parsed
rows and member ranges select one.
---------
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <[email protected]>
* fix(native): retain value trace source evidence
* fix(native): build trace error evidence only on failure
* test(mcp): expect unowned value trace refusal
* test(mcp): avoid freezing trace error wording
* test(native): keep value-trace stop coverage with source evidence
Main's stop and continuation cases now meet the trace's evidence-owner
precondition: direct calls supply a source id, and a completed MCP trace
runs in a session that can retain it.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(browser)!: keep the Chromium sandbox for REA-launched browsers
Playwright appends --no-sandbox unless a launch passes
chromiumSandbox: true. Neither the browser scenario launch nor the native
module resolver passed it, so every REA-launched Chromium ran without its
sandbox and REA_BROWSER_NO_SANDBOX had no effect.
Both launch sites now take the sandbox decision from ChromiumSandbox.ts:
off on Android or when REA_BROWSER_NO_SANDBOX=true, on otherwise. When
Chromium reports that it cannot start its sandbox, the launch failure's
remediation names the opt-out.
Fixes#8722
BREAKING CHANGE: hosts where Chromium cannot create its sandbox, such as
root users and Linux containers without user namespaces or a setuid
sandbox, now fail to launch until REA_BROWSER_NO_SANDBOX=true is set.
* fix(browser): name the sandbox opt-out on any sandboxed launch failure
Chromium reports an unusable sandbox through several unrelated startup
messages, including a misconfigured setuid helper and a refused namespace
move, and Playwright recognizes only some of them. Matching individual
messages missed real failures, so a sandboxed launch failure now always
names REA_BROWSER_NO_SANDBOX=true as a conditional remediation and the
retained startup diagnostics show whether the sandbox was the cause.
* fix(browser): keep default recovery for resolver failures after launch
The module resolver's outer catch also covers context, navigation and
evaluation failures after the browser launched. Attaching the sandbox
remediation there replaced their default recovery guidance. Attach the
shared launch remediation only when a sandboxed launch itself fails.
* docs(browser): note the Android sandbox exception for module traces
* feat(artifacts): inventory and extract ESP32 firmware images
ESP32-family app and bootloader images (magic 0xE9) had no reader, so
open_binary refused OTA downloads, app partitions and bootloaders.
Add the esp-image container format behind inspect_artifact and
extract_artifact. A pure decoder reads each byte once and keeps only
fixed-size fields. Derived facts cover the chip, from a table cited to
ESP-IDF v6.1; segment classification as ESP-IDF's should_map and
should_load; checksum and appended SHA-256 results; app and bootloader
descriptors; Secure Boot v2 signature blocks, observed and unverified;
and undescribed trailing byte ranges. Truncation and undescribed bytes
make coverage unknown. ESP8266 images are refused with a specific
reason, and ciphertext stays unrecognized.
Add the format to every target, Evidence, snapshot, live-summary and
artifact vocabulary, document it, and add an optional verify:esp-images
lane that compares esptool-built images with esptool image-info and
espsecure signature-info-v2.
Refs #3615
* test(artifacts): run the ESP image lane on esptool v5.6.0
The lane compared descriptor fields only when esptool image-info printed
one, so a descriptor that REA reports and esptool does not print passed
without comparison. Compare the descriptor kind in both directions.
Record that the lane passed for all 13 images with esptool and espsecure
v5.6.0, the version the layout facts cite, as well as with 4.12.0.
* refactor(artifacts): define ESP image layout sizes once
The decoder and the derived facts each defined the segment-header,
appended-digest and flash-sector sizes, the erased-byte scan and the
offset formatter. Define them once with the other fixed-layout structures
and import them in both places. The signature slot count is the one
constant that drives the slot scan.
Correct the observedEnd comment: it is the end of the last chunk read.
* fix(artifacts): report ESP-IDF's segment length limit by chip
ESP-IDF's verify_segment_header rejects a segment of 16 MiB or more
unless the chip maps flash through 32-bit cache addresses
(SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP: ESP32-S3, P4, C5, C61, H4 and
S31 in v6.1). Record that capability in the chip table and add a
limitation for such segments; unknown chips get none.
`loaded` repeats should_load. It does not mean the bootloader accepts the
segment: verify_load_addresses still rejects one outside the chip's RAM,
which REA does not model. Say so in the docs, the skill reference and the
inventory limitation.
* test(artifacts): check ESP lane tools first and verify extraction
The lane started work before checking its tools, so a missing RISC-V
target, ld.lld, esptool or espsecure surfaced as a raw spawn or command
error. Check each one before building and name the variable to set. Warn
when esptool is not v5.6.0, the release the layout facts cite.
Assert each segment's class for the ESP32-C3 app, its variants and the
bootloader, as the Xtensa image already did. Extract the app and the
signed image through the CLI and compare every file with the bytes at
esptool's segment offsets or in the appended signature sector; the
source's SHA-256 must be unchanged and equal the reported root_sha256.
* docs(artifacts): state what the ESP image lane does not establish
The Verification section listed the lane's checks but not its gaps: only
ESP32-C3 and ESP32 images come from a real producer, no real project
build, OTA download or flash dump is inspected, no signature is verified,
and the lane does not run in CI.
Record the timing of the final lane run on esptool v5.6.0.
* fix(artifacts): compare ESP segment declarations
Segment entries hash only their data, so compare_artifacts reported a
segment that moved to another load address with the same bytes as
unchanged; only the image itself changed. Compare each segment's
load_addr and data_len as its declaration, as Node SEA and PyInstaller
members are compared, and count a changed declaration as a metadata
change, also when content is unavailable.
Header offsets are layout and the classification is derived, so neither
is compared.
* test(artifacts): run ESP lane commands as owned process trees
The lane ran its tools through execFile. A timeout killed only the direct
child, so a linker or assembler that Clang or GCC had started could keep
running while the lane removed its workspace. Run every command through
createVerifierCommands, check the tools inside withVerifierCleanup, and
remove the workspace only after commands.close() has stopped every tree;
a failed close keeps the workspace.
Only a nonzero exit now counts as a refusal or an expected esptool
failure; timeouts and spawn failures propagate. Supervising each command
roughly doubles the lane's run time.
* test(artifacts): cancel ESP decoding after its first decoded chunk
The cancellation test requested cancel or close on setImmediate, which
can land while the file is still being opened and checked, so it never
showed that decoding had started. It also passed against a reader that
ignored both requests while decoding. Acknowledge the decoder's first
chunk, then cancel or close, join the operation, and assert that no
later chunk reaches the decoder and that the reader ends closed.
* docs(artifacts): record which ESP metadata comparison ignores
Segments are compared by their declaration. The image's and the
signature blocks' format_metadata are not: every field comes from their
own bytes, which content comparison already covers, and a block's
image_digest_matches follows the image's content.
* fix(artifacts): decide declaration comparison for every metadata kind
compare_artifacts listed the format_metadata kinds it compared by hand,
so a new kind went uncompared without notice; ESP segment load addresses
were first missed this way. One table beside the schemas now gives every
kind a declaration projection or the reason comparison uses none of its
facts, and a kind without an entry does not compile. Consumers read one
result per pair instead of naming formats.
- Godot PCK entries stay uncompared. Godot looks members up by the
simplified path without res://, which logical_path holds apart from
Unicode normalization. Godot 4.3 exports store res:// and 4.4.1
exports do not, so comparing source_path would mark every member
changed. The reader admits only the encryption flag, which the
occurrence's encrypted field records.
- A changed PyInstaller declaration is now a metadata change also when
the member's content is unavailable, as for ASAR, Node SEA and ESP
segments. It was unknown before, for no recorded reason.
- Root kinds and ESP signature blocks stay uncompared: their fields come
from their own bytes, which content comparison covers.
- A declaration that only one side records now adds one generic
limitation for every compared kind. Only Node SEA added one before,
with its own text.
* fix(artifacts): keep content comparison for metadata records with content
The record rule compared two metadata occurrences by their declaration
alone. A hand-built record that names content, with an equal declaration,
then hid a content change; PyInstaller records already did so before the
table. The rule now needs both records without an artifact_id, which is
every metadata record a reader emits.
Review changes in the same area:
- The unknown-declaration limitation also covers declarations of different
kinds and non-container observations, and tests compare it with its
constant.
- docs/godot-pck.md gives each Godot version's lookup path, the backslash
case, and that a member that becomes encrypted compares unknown. The
table points to it instead of restating it.
- The PyInstaller regression moves to an acceptance test whose MCP client
validates the advertised output schema, and the domain tests keep one
representative case per behavior.
* fix(artifacts): compare encryption changes when content is unavailable
An encrypted member has no content. Comparison then took the
content-unavailable fallback, which counted only ASAR storage and format
declarations, so a Godot PCK, ZIP or PYZ member that became encrypted
compared unknown although both sides record `encrypted`. The record rule
compared node-less metadata records by format declaration alone, so a PYZ
namespace record whose encryption flipped compared unchanged.
`OccurrenceMetadata` now owns the encryption comparison beside ASAR
storage and the format declaration. Every occurrence records `encrypted`,
so a pair's comparison is never unknown. The fallback, the record rule and
the metadata dimension read it there; `metadataChanged` no longer compares
`encrypted` itself. The record rule compares all of `OccurrenceMetadata`:
a changed value is changed in metadata, with availability when another is
unknown.
Decision: occurrence limitations still do not count for node-less
records. Readers derive them from the declared facts (the PYZ caveat
follows `encrypted`), and their wording can change between REA versions
while the record stays the same. The fallback does not compare them
either.
Behavior changes:
- A pair with unavailable content whose `encrypted` differs is changed
[metadata, availability], not unknown [availability].
- A node-less record whose `encrypted` differs is changed [metadata], not
unchanged.
- A hand-built node-less record that records ASAR storage now compares
it; no reader emits such records.
Remaining:
- The fallback still ignores `entry_kind` and `executable`, which readers
also declare, so a file that becomes a symlink, or a mode change on an
encrypted ZIP member, still compares unknown. `executable` has its own
dimension, so its fix needs content-free checks split from node checks.
Each is its own behavior decision.
- Node-bearing pairs still compare limitation wording; records do not.
Whether limitation wording is ever a comparison fact needs one decision.
* fix(artifacts): compare entry kind and executable when content is unavailable
When either side lacked verified content, comparison took a fallback that
counted only ASAR storage, encryption and format declarations. It compared
`entry_kind` only in the metadata check of pairs with content, and
`executable` only beside the node checks. A ZIP member that became a
symlink, whose target REA does not read, and an encrypted ZIP member whose
Unix mode gained an execute bit therefore compared unknown, although
readers declare both fields on every occurrence.
`OccurrenceMetadata` now owns the entry kind comparison beside encryption;
it is never unknown. Comparison checks fall into two groups: declared facts
that need no content (`executable` and the metadata declarations), and
facts that need both content nodes (content, kind, format, size, relations
and node metadata). The fallback, the record rule and pairs with content
share the declared checks and one rule for unknown declarations. Kind and
format stay content checks: without content they are guesses from the path.
Behavior changes:
- Unless both sides have content, a pair whose entry kind differs is
changed [metadata, availability], not unknown [availability].
- Such a pair whose `executable` differs is changed [executable,
availability], with metadata when another declared fact also differs.
- A hand-built node-less record whose `executable` differs is changed
[executable]. No reader emits such records.
Remaining: declared sizes and integrity digests still compare only through
observed bytes, so they do not count when content is unavailable.
* fix(artifacts): never mark a symlink executable
On POSIX hosts the directory reader recorded a symlink's own permission
bits as `executable`. Linux reports 0777 for every symlink and macOS
usually 0755, so directory, app bundle and DMG symlinks read true. zip.js,
the PyInstaller reader and the Windows branch record false. The previous
commit compares `executable` without content, so a symlink that two
readers or two hosts record differently compared changed in `executable`,
and a non-executable file that became a symlink also reported `executable`.
`hostExecutableMarking` now gives a symlink no marking on any host:
running a symlink runs its target, and the link's own bits do not control
that.
Behavior change: directory, app bundle and DMG inventories record
`executable: false` for symlinks. Occurrence IDs do not change, but the
graph digests and manifest IDs of inventories with symlinks do. Comparing
an inventory stored before this change with a new one therefore reports
changed [executable, availability] once for each such symlink.
* fix(artifacts): stop comparing limitation wording
Pairs with content compared node and occurrence limitations as metadata,
while node-less records, the content-unavailable fallback and inventory
limitations did not. Limitations explain what an observation could
establish. Readers mostly derive them from fields that comparison already
reads, from other occurrences or from what REA could read, and their
wording changes between REA versions. So adding `A` beside an unchanged
ZIP member `a` marked `a` changed in metadata through its case-collision
note, and an older and a newer inventory of equal bytes could differ
wherever a reader reworded a note.
No comparison path reads limitations now. Inventory limitations still
appear in the result, and occurrence limitations stay in the inventory
Evidence. Readers write no node limitations, so only hand-built inventories
compared them.
Behavior change: for a pair with content, limitation text no longer adds
metadata. A pair that differs only there compares unchanged, or unknown
[availability] when a declaration is unknown.
Remaining: a few facts appear only in limitation text, or also in fields
that comparison does not read, so their changes are no longer reported
while the compared bytes stay equal: an ASAR block integrity table or an
unsupported integrity declaration, a Godot member's all-zero MD5, a ZIP
name's leading `./` (also in `source_path`), and a PyInstaller PYZ image
digest (also in `pyz_sha256`; the archive root still reports the change).
Each needs a field or a comparison decision of its own if it matters.
* fix(artifacts): compare declared digests when content is unavailable
Without observed bytes on both sides, comparison ignored the containers'
declared integrity claims. Two encrypted Godot PCK members therefore
compared unknown even when their declared MD5s showed that the script had
changed. Godot's exporter stores the MD5 of a member's original bytes, so
the MD5 does not depend on the encryption IV. This comes from the 4.3,
4.4.1 and 4.7.1 exporter sources; no real encrypted export was compared.
When either side has no content, claims of one algorithm that differ now
make the path changed in metadata and availability. Equal claims add no
change, because they are unverified, and observed bytes still decide when
both sides have them. Godot PCK and ASAR readers record such claims.
For a readable member, a directory that declares or omits an MD5 only
decides whether REA can check its bytes, so such a member compares
unchanged. An encrypted member with an MD5 on only one side stays unknown.
Remaining: declared sizes still compare only through observed bytes.
* docs(artifacts): check the contract's compared kinds against the table
docs/mcp-contracts.md named the format_metadata kinds that
compare_artifacts compares in prose, by hand, so a kind added to the
declaration table could go unnamed. The integration branch already omits
esp-flash-partition, which the ESP flash layout branch adds to the table.
The paragraph now names each compared kind by its identifier. A
conformance test requires it to name exactly the kinds whose table entry
has a declaration, which artifactFormatMetadata.ts exports as
COMPARED_FORMAT_METADATA_KINDS, and no kind that comparison ignores.
* test(artifacts): share the comparison acceptance MCP fixture
Six artifact comparison acceptance tests each copied the same MCP
steps: open_binary, inspect_artifact, the inventory_artifact substep
Evidence, then compare_artifacts. The coverage test repeated the
inspect steps three times and kept its own inventory lookup.
tests/fixtures/artifactComparisonMcp.ts now owns these steps on the
schema-validating connectLocalToolsMcp client.
connectArtifactComparisonMcp() returns call, inspect(path) and
compare(left, right). inventoryEvidence() reads the inventory Evidence
that an inspection retains, from MCP or CLI output. Each test still
writes its own authored input and keeps its expectations.
The coverage lookup matched evidence.operation, not the substep
operation. The inspection schema fixes both to inventory_artifact, so
both select the same substep.
The plist acceptance test does not change. It compares the inspection
Evidence itself across CLI and MCP and passes the comparison Evidence
to verify_reconstruction and find_changed_behavior.
No assertion or scenario changes.
* fix(artifacts): leave .NET bundle member declarations uncompared
Upstream main compares no .NET bundle member facts. Comparing `file_type`
changed `compare_artifacts` output beyond this branch's fixes, so the kind
stays uncompared until that is decided. A changed manifest still shows as a
content change of the host.
* fix(artifacts): leave HEX region declarations uncompared
The comparison table compared a region's start_address and length. A
region's path names its start and end addresses, and its length follows
from them, so two regions that pair at one path always declare the same
facts: the entry could never find a change. It only made a region with
equal bytes unknown when the other side recorded no metadata, such as a
directory of extracted regions. The kind is not compared, as on main.
The contract paragraph also stops naming .NET bundle members, which
3e51a7760 left uncompared.
* test(artifacts): compare node-less records of any compared kind
The record rule follows the comparison table, not PyInstaller, but only
PyInstaller records exercised it. An ESP segment record shows both
outcomes: an equal declaration compares unchanged and a moved one changed
in metadata, without the availability that the fallback for unread
content would add.
* test(artifacts): compare a node-less record's recorded ASAR storage
The record rule compares ASAR storage beside the declaration and
`encrypted`, but no test recorded it on a metadata record. A changed
value is changed in metadata, and a value that only one side records
is unknown in availability, as for any occurrence.
* test(artifacts): compare UnityFS member declarations through MCP
The table compares a UnityFS member's declared source_path and raw
flags, but no comparison test covered the kind, so dropping either field
from the projection left every test passing. Bundles with equal member
bytes now show both: other flags, or a stored path in decomposed form,
compare changed in metadata, and reordered members, whose index and
stream offset are layout, compare unchanged.
* test(artifacts): use the shared comparison fixture for UnityFS
The UnityFS comparison test arrived from the bottom branch with its own
copy of the open_binary, inspect_artifact and compare_artifacts steps,
which tests/fixtures/artifactComparisonMcp.ts owns here. It now uses
connectArtifactComparisonMcp() like the other comparison acceptance
tests. No assertion changes.
* test(artifacts): find the contract's compared kinds by marker
The docs conformance test found its paragraph by an exact English phrase,
so rewording "also compares" failed it while the named kinds stayed
right. The paragraph now sits between BEGIN and END markers, as the
allocation report's sections do, and the test reads the kinds between
them. It still requires exactly the kinds whose table entry has a
declaration.
* fix(javascript): remove duplicate childNodes import
The squash of #7967 re-added the import already introduced in afc18131f,
so typecheck failed with TS2300.
* test(gdb): expect the command-output cap limitation
The retained-record limitation now names the command-output setting.
The coverage assertion still required the previous fixed-cap sentence.
* test(cli): keep rejected mcp add values off Amp settings
mcp add now rejects a flag in the --command value position. The Amp
registration test still treated that invocation as a successful selection.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(cli): export source Evidence for native value traces
* fix(cli): export source Evidence for native value traces
* fix(cli): preserve trace completeness in summaries
* fix(firmware): keep producer reports writable under small staging budgets
extract_firmware passed max_output_bytes straight to prlimit --fsize. RLIMIT_FSIZE
applies to every regular file the producer writes, including the mandatory
report.json and unblob.log outside the staging tree, so a staging budget smaller
than the report truncated it and failed the extraction even when every staged
file fit. Derive the per-file ceiling as the larger of the staging budget and the
8 MiB report budget; polling and the final inventory still enforce the aggregate
staging budget.
Fixes#3888
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(firmware): keep files stopped at the size ceiling over budget
The per-file RLIMIT_FSIZE ceiling equalled the largest size its readers
accept, and both the staging inventory and the report reader accept a
file of exactly their budget. A write the kernel stopped at the ceiling
therefore left a truncated file that passed as complete. Raise the
ceiling by one byte so such a file exceeds its budget and is rejected.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(firmware): report the effective file-size ceiling on limit failures
The exit path re-derived the staging budget for SIGXFSZ and process-limit
recovery, so a small-budget run killed at the 8 MiB + 1 ceiling reported
file_size_bytes 64 and said the cap was max_output_bytes. Pass the
ceiling runFirmwareCommand installs and describe how it is derived.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(firmware): reject staged files cut off at the per-file limit
RLIMIT_FSIZE stops a write at the limit without extending the file, and
the staging inventory admits a file of exactly max_output_bytes. With a
budget of 8 MiB or more the producer limit equalled the budget, so a
staged file the kernel cut short published as a complete extraction;
an inherited ceiling below the budget did the same.
Derive the per-run limit once in firmwareFileSizeLimit as one byte above
the largest budget, still capped by the inherited ceiling, so a file
stopped at the limit exceeds its budget. When the inherited ceiling
removes that byte, both staging inventories refuse a regular file that
reaches the installed limit with a nonretryable file-size constraint
naming the inherited limit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(firmware): derive limit adjustability from the per-run limit
The one byte of headroom makes the per-run file limit equal an
inherited ceiling of exactly max_output_bytes + 1, but adjustability
still compared that ceiling with the raw budget. A SIGXFSZ then advised
raising max_output_bytes, which the same ceiling clamps back to the
same limit.
Decide adjustability in firmwareFileSizeAdjustable from
firmwareFileSizeLimit itself: recommend a larger budget only when it
raises that limit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(firmware): compare file-size recovery with its owners
The SIGXFSZ adjustability cases and the inherited-ceiling workflow matched
fragments of remediation prose, so a harmless rewrite would fail them.
Compare the emitted guidance with firmwareSignalLimitError for the expected
branch, check that the two branches stay distinct, and compare the inventory
refusal with firmwareFileSizeLimitReached. The selected limit, resource and
retryability stay asserted as structured facts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
* fix(artifacts): keep inventorying an ASAR whose member declares an invalid size
A member whose declared size is not a non-negative safe integer, such as
Figma Desktop's .codesign placeholder (size -1000), made readAsarListing
throw and failed inventory of the whole archive without naming the entry.
Listing now keeps such a member with a null size and a limitation naming
the parsed value, as it already keeps an unreadable offset. Opening a
packed member with a null size raises ArtifactEntryContentUnavailable, so
inventory records it as unavailable and continues with the other members.
ElectronFileScope keeps the strict size rule.
Member metadata that still stops inventory now names the member and the
rejected fields instead of reporting a generic malformed archive.
Fixes#8715
* fix(artifacts): bound and preserve ASAR listing diagnostics
Quote an unsafe path component or a string size through a 256-unit
prefix plus its length, so one oversized header key cannot inflate the
failure or the retained limitation. Render numeric sizes with String so a
size that JSON.parse turned into Infinity is reported as Infinity rather
than null.
* fix(artifacts): keep ASAR listing diagnostics exact and located
Report a numeric size beyond the safe-integer range as having no
retained exact value instead of the number JSON.parse rounded or
overflowed, name object and array sizes correctly, and interpret each
declared size once without validating absent directory sizes.
Quote member and parent paths through the bounded source quote, name the
rejected root fields as member failures already do, and append the
archive path to listing failures so a nested archive stays identifiable.
* test(artifacts): match ASAR listing archive paths that contain spaces
* feat(desktop): suggest managed members for Unity predefined script assemblies
Unity Mono triage listed only inspect_managed_artifact probes, mostly for
engine and framework assemblies. Assembly-CSharp.dll and
Assembly-CSharp-firstpass.dll directly in a managed directory with a
UnityEngine reference witness now get an inspect_managed_members probe,
ordered before every managed artifact probe.
* docs(desktop): describe Unity Mono managed member probes
* refactor(desktop): read Unity managed directory from the witness fact
* refactor(desktop): reuse the managed directory index for predefined assemblies
* feat(desktop): point Unity member probes at summary delivery first
* test(desktop): no member probes without a Unity reference witness
* docs(desktop): trim Unity member probe note
* feat(desktop): suggest managed members in Unity IL2CPP backup folders
Unity IL2CPP builds write <Product>_BackUpThisFolder_ButDontShipItWithYourGame
next to the player. When shipped, its Managed/ holds pre-IL2CPP assemblies.
A path convention now records the folder on Assembly-CSharp*.dll directly in
its Managed/, including when the admitted root is the folder itself, and
triage offers inspect_managed_members for those files. Correspondence to
the shipped native code stays unknown.
* docs(desktop): note IL2CPP backup folder member probes
* docs(desktop): list IL2CPP metadata versions 24 and 27 for the metadata probe
* style(desktop): merge stacked doc comments on desktopPathConventions
* test(desktop): cover member probes in a macOS bundle managed directory
* docs(desktop): note that member probes need the game folder as root
* test(desktop): no member probe when the root is the Unity data folder
* test(desktop): check the data-folder root still gets managed probes
* test(desktop): check the IL2CPP backup convention against the probe bound
---------
Co-authored-by: gensokyo-dev <[email protected]>
* feat(setup): register LM Studio during setup
Register rea in mcp.json in the LM Studio home through a new lm_studio
client. The home follows LM Studio's own lookup, read-only: the path in
~/.lmstudio-home-pointer, else an existing ~/.cache/lm-studio, else
~/.lmstudio. An unreadable or relative pointer reports a path error
instead of a guessed home.
LM Studio parses mcp.json with strict JSON.parse, so comments, trailing
commas and a BOM are rejected before any backup or write. The entry
carries command, args and env with no type or timeout. Local models have
small context windows, so the registration selects
REA_MCP_INPUT_SCHEMA_PROFILE=compact unless a profile is set explicitly,
and doctor reports an entry without a profile as stale. One predicate now
owns that default for Kimi Code and LM Studio.
* fix(doctor): resolve provider budgets for LM Studio as setup does
* fix(mcp): report session tool target requirements accurately
- Declare requiresActiveTarget on inspect_address_context in sessionToolContracts.ts so closed-session discovery stops advertising an address query that fails with target_unavailable.
- Route session-kind tools through targetAvailability in CapabilityInventory.ts before the available fallback, and report target_unsupported with mode-level remediation for get_navigation_context and inspect_address_context on artifact and archive targets.
- Keep each navigation mode's missing_operations and remediation aligned with the target restriction so recommendations name the target instead of a provider.
* test(mcp): cover closed-session and artifact target requirements
- Add CapabilityInventory controls for closed-session address and explicit-path APK availability, artifact and archive restrictions with per-mode navigation flags, and structured reasons instead of pinned remediation sentences.
- Add a production stdio MCP regression in sessionTargetRequirements.test.ts that observes both binary_session entries before and after the calls, the target_unavailable address error, the successful unsigned-ZIP APK call, and the sibling navigation prerequisite.
Default page-origin scope excludes cross-origin APIs. Point agents at
JavaScript analysis for API-origin discovery, require page origin plus API
origins in allowed_origins (list replaces default), and bump the packaged
skill to version 60.
* fix(native): select versioned arm64e slices for codesign by family name
Current clang marks arm64e slices with pointer-authentication ABI
version 1 (subtype 0x81000002, lipo name arm64e.v1). The codesign
selector table accepted only ABI version 0, so inspect-signature failed
with capability_unavailable whenever such a slice was selected, for
example on any universal arm64e + x86_64 binary built today.
codesign has no versioned name: -a arm64e.v1 is "unknown architecture
name", and -a arm64e resolves to the first FAT slice with the arm64e
base subtype, whatever its ABI version. A selector is therefore exact
only for that first slice. Accept any versioned pointer-authentication
ABI for arm64e, and return a selector only when the slice is the first
in table order to carry that name; a later slice sharing it stays
unknown instead of being inspected through the earlier one. This also
removes the separate duplicate-name check from signature coverage.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): keep arm64e selectors unknown behind unmapped variants
codesign resolves a name to the first FAT slice with the same CPU type and
base subtype, so an earlier capability variant whose own name is unknown may
shadow a later arm64e slice. Compare identities rather than successful name
mappings.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(javascript): emit no timer for a literal non-function callback
Node validates the timer callback before creating a handle:
if (typeof callback !== 'function')
throw new ERR_INVALID_ARG_TYPE('callback', 'function', callback);
The throw is synchronous, before any timer exists, but the analysis reported a
`schedule` operation for such calls.
Measured on Node 24.22.1:
setTimeout("code", 0) -> THROWS ERR_INVALID_ARG_TYPE
setTimeout(`code`, 0) -> THROWS ERR_INVALID_ARG_TYPE
setTimeout(1, 0) -> THROWS ERR_INVALID_ARG_TYPE
setTimeout(1n, 0) -> THROWS ERR_INVALID_ARG_TYPE
setTimeout(true, 0) -> THROWS ERR_INVALID_ARG_TYPE
setTimeout(null, 0) -> THROWS ERR_INVALID_ARG_TYPE
setTimeout(void 0, 0) -> THROWS ERR_INVALID_ARG_TYPE
setTimeout([], 0) -> THROWS ERR_INVALID_ARG_TYPE
setTimeout({}, 0) -> THROWS ERR_INVALID_ARG_TYPE
setTimeout() -> THROWS ERR_INVALID_ARG_TYPE
setImmediate("code") -> THROWS ERR_INVALID_ARG_TYPE
setInterval("code", 0) -> THROWS ERR_INVALID_ARG_TYPE
--- accepted by Node ---
setTimeout(function(){}, 0) -> OK
setTimeout(() => {}, 0) -> OK
setTimeout(fn, 0) -> OK
setTimeout(class {}, 0) -> OK (fails only when the class is invoked)
So a schedule is dropped only for an exactly readable literal that is not a
function, or for a missing argument. An identifier, a call result or a spread may
still be a function at runtime and keeps its schedule.
* chore: retrigger title and readiness checks after the title was corrected
* fix(javascript): scope the timer callback rule to Node's timers module
greptile flagged two problems with the first version.
P1 - browser timers disappeared. The rule was applied to bare globals, but
browser and Electron renderer sources reach this analysis without a Node/browser
distinction, and there `setTimeout("refresh()", 0)` is a real timer: the host
evaluates the string. The rule now applies only when the timer is imported from
`node:timers`, where that form cannot exist. A bare global schedule keeps its
fact whatever argument 0 holds.
P2 - unused import. `readExactJavaScriptLiteral` was imported but never used; the
helper reads shapes through `unwrapJavaScriptExpression`. Removed.
Measured on Node 24.22.1 (the throw is what makes a node:timers schedule
impossible; the bare-global case is intentionally left alone):
import { setTimeout } from "node:timers";
setTimeout("code", 0) -> THROWS ERR_INVALID_ARG_TYPE
setTimeout(null, 0) -> THROWS ERR_INVALID_ARG_TYPE
setTimeout(fn, 0) -> OK
--- bare global, host may be a browser ---
setTimeout("code", 0) -> kept as a schedule
24 cases cover both sides plus the cancellation link.
* fix(javascript): reject every non-function global and unary timer callback
Reviewing the timer callback guard against Node 24.22.1 found two gaps. Both are
in the same "a call that always throws must not emit a resolved fact" family.
**An unmodified global that is not a function was accepted.** The guard read
literal nodes only, so a bare global identifier passed through even though Node
rejects it before creating a handle:
setTimeout(undefined) -> THREW ERR_INVALID_ARG_TYPE
setTimeout(NaN) -> THREW ERR_INVALID_ARG_TYPE
setTimeout(Infinity) -> THREW ERR_INVALID_ARG_TYPE
`isUnmodifiedGlobalReference` keeps a shadowed binding out of the rule, so a
local `const NaN = () => {}` keeps its schedule.
**Only `void` was treated as a non-function unary operator.** Every unary operator
yields a primitive, and Node rejects a primitive callback:
setTimeout(!0) -> THREW ERR_INVALID_ARG_TYPE
setTimeout(~0) -> THREW ERR_INVALID_ARG_TYPE
setTimeout(-1) -> THREW ERR_INVALID_ARG_TYPE
setTimeout(+"1") -> THREW ERR_INVALID_ARG_TYPE
setTimeout(typeof 1) -> THREW ERR_INVALID_ARG_TYPE
A class expression is still kept, because it passes Node's own `typeof` check and
only fails when invoked.
Fifteen regressions cover the rejected forms plus arrow, identifier, class,
spread and shadowed-global cases; ten fail on the previous commit.
* fix(javascript): let sloppy eval shadow the non-function global callback
Review finding. The new global check read `undefined`, `NaN` and `Infinity` as
unmodified globals, but sloppy `eval` can introduce a local function under one of
those names, which then shadows the global. Measured on Node 24.22.1 in script
scope:
function g() { eval("var undefined = () => {}"); setTimeout(undefined, 0); }
-> schedules a timer
function g() { eval("var NaN = () => {}"); setTimeout(NaN, 0); }
-> schedules a timer
`semanticOpaqueNameMayHaveChanged` now has to agree that no visible activation can
introduce such a name before the global reading is conclusive. In a module the
same `eval` is strict and introduces no outer binding, so the global reading still
holds and the timer is still dropped.
Three regressions cover the sloppy forms, and a fourth pins the no-eval cases as
still dropped. The three sloppy cases fail without the guard.
* feat(harmony): disclose unclassified inventory files
* test(harmony): let createEvidence own JSON admission in the partial-inventory case
* test(harmony): extract the projection assertion from the archive journey
* docs(harmony): qualify the unclassified disclosure rules and pin exact-case manifests
* test(harmony): replace limitation prose with structured coverage facts
The partial-inventory control froze the sentence "Source inventory coverage is
partial; absence is unknown." verbatim. docs/testing.md asserts structured
limit facts instead of freezing explanatory sentences and puts documentation
wording in review, so the sentence is no longer substring-matched.
`coverage.status` is a disjunction of independent causes in this projection:
incomplete inventory scope, unavailable content, and an exceeded bridge budget.
Asserting only `status: "partial"` left that attribution unstated, so the two
other causes are now pinned as well. `unavailable_components` being empty and
`bridge_candidate_coverage` being complete with zero candidates make the partial
result attributable to the missing occurrence alone.
Also drops an `artifact_id` comparison that could not fail: the fixture's four
files carry the same bytes, the preceding assertion already pins that shared
digest, and `artifact_id` is derived from the digest. Distinct paths sharing
content stay covered by the path array and the independent digest expectations.
Narrows a comment that claimed exact-case matching for every name in the
control; only the manifest names are case-sensitive, because the App Pack child
suffix match is case-insensitive.
* docs(harmony): name the disclosure location for a nested child package
The unclassified-disclosure paragraph used "there" to refer back to a clause
that had just named `app_pack_children`, so a reader could take a nested `.hap`
or `.hsp` member of a HAP or HSP root to be reported as an App Pack child. It is
reported in `unclassified_components`: the child list is populated only for an
App Pack root. Names the location explicitly.
* fix(javascript): emit no fs acquisition for a literal non-flag string
`fs.open` and `fs.openSync` validate a literal flag before touching the
filesystem:
if (!isFd(flags) && !FLAGS_SET.has(flags))
throw new ERR_INVALID_ARG_VALUE('flags', flags, ...);
The throw is synchronous, before a descriptor exists, but the analysis reported
an `acquire` operation for those calls.
Measured on Node 24.22.1 against a file that does not exist, so each valid flag
is exercised without an EEXIST masking it:
ACCEPTED as a flags string:
a a+ as as+ ax ax+ r r+ rs rs+ sa sa+ sr sr+ w w+ wx wx+ xa xa+ xw xw+
REJECTED with ERR_INVALID_ARG_VALUE:
"" A R W rb wb s s+ x r,x
A Python-style `"rb"`/`"wb"` is the common case: those are valid in Python and
fatal at the Node call site.
`FS_OPEN_FLAGS` in `javascriptStaticAnalysisHelpers.ts` already holds exactly
those 22 accepted flags. It is now exported and reused so the accepted set has
one owner. A numeric flag is a platform bitmask and a computed value may still be
valid, so only an exactly readable non-flag string is dropped.
`fs.createReadStream` keeps its acquisition: it does not validate the flag
synchronously, it returns a stream and reports the failure on the error event.
* chore: retrigger title and readiness checks after the title was corrected
* fix(javascript): reject every fs flag literal Node rejects
The first version only decided string flags. Node's check covers every flag
shape, and each one below throws `ERR_INVALID_ARG_VALUE` or `ERR_OUT_OF_RANGE`
synchronously, before a descriptor exists:
openSync(p, 1.5) -> THREW ERR_OUT_OF_RANGE
openSync(p, -1) -> THREW EINVAL
openSync(p, -1.5) -> THREW ERR_OUT_OF_RANGE
openSync(p, 1e400) -> THREW ERR_OUT_OF_RANGE
openSync(p, NaN) -> THREW ERR_OUT_OF_RANGE
openSync(p, Infinity) -> THREW ERR_OUT_OF_RANGE
openSync(p, 0n) -> THREW ERR_INVALID_ARG_VALUE
openSync(p, true) -> THREW ERR_INVALID_ARG_VALUE
openSync(p, false) -> THREW ERR_INVALID_ARG_VALUE
openSync(p, {}) -> THREW ERR_INVALID_ARG_VALUE
openSync(p, []) -> THREW ERR_INVALID_ARG_VALUE
--- accepted ---
openSync(p, 0) / 1 / 65536 / +5 -> ok
openSync(p) / undefined / null -> ok
openSync(p, "r") / "wx" / "sr" -> ok
`NaN` and `Infinity` are identifiers rather than literals, so they are decided
only through `isUnmodifiedGlobalReference`; a shadowed binding keeps the
acquisition.
* fix(javascript): judge an fs flag by its value, and ignore a shifting spread
Two P1 findings on the first version.
**A unary operator was judged by its operand.** `openSync(p, void 1.5)` passes
`undefined`, so Node applies the default read flag, but the branch saw a
fractional operand and dropped a real acquisition. `openSync(p, -0)` is the valid
zero bitmask and was also dropped. `staticFlagBits` now evaluates the operator:
openSync(p, void 1.5) -> ok (keeps the acquisition)
openSync(p, void 0) -> ok
openSync(p, -0) -> ok
openSync(p, -0.0) -> ok
openSync(p, +0) -> ok
openSync(p, ~-1) -> ok
openSync(p, -1) -> THREW EINVAL
openSync(p, -1.5) -> THREW ERR_OUT_OF_RANGE
openSync(p, +1.5) -> THREW ERR_OUT_OF_RANGE
openSync(p, !0) -> THREW ERR_INVALID_ARG_VALUE
openSync(p, ~0) -> THREW EINVAL
openSync(p, typeof 1) -> THREW ERR_INVALID_ARG_VALUE
**A spread before the flag slot shifts the runtime position.** With
`args = [path, "w"]`, `openSync(...args, "0600")` opens with mode `"0600"`, but
argument 1 is then the mode rather than the flag. The check now keeps the
acquisition whenever argument 0 is a spread, because the flag it inspects is not
provably the flag at runtime.
* fix(javascript): leave a signed fs bitmask undecided
greptile caught that the flag check rejected every negative integer, but Node
accepts some signed bitmasks. Measured on Node 24.22.1 against an existing file:
openSync(p, -2147483648) -> ok
openSync(p, -1073741824) -> ok
openSync(p, -1) -> THREW EINVAL
openSync(p, -2) -> THREW EINVAL
openSync(p, 2147483647) -> THREW EINVAL
openSync(p, 2147483648) -> THREW ERR_OUT_OF_RANGE
openSync(p, -2147483649) -> THREW ERR_OUT_OF_RANGE
So the sign does not decide: some negative values open a descriptor and others do
not, and which is which is a platform bitmask question this analysis cannot
settle. The check now rejects only a non-integer or a value outside the signed
32-bit range, and leaves an in-range negative undecided — the same choice
`greptile` asked for, on the principle that a call which may succeed must keep
its acquisition.
`!`, `typeof` and `delete` are still rejected, because those yield a boolean or a
string regardless of the operand.
* fix(javascript): read a no-expression template literal flag
Review finding. A no-expression template literal is exactly readable, so it is the
same flag string Node receives, but the guard only read `StringLiteral`:
fs.openSync(p, ` + "`rb`" + `) -> THREW ERR_INVALID_ARG_VALUE
fs.openSync(p, ` + "`r`" + `) -> ok
The flag slot now accepts an interpolation-free template; an interpolated template
stays undecided.
Four regressions cover both directions; two fail without the change.
* fix(artifacts): classify bundle executables by their own name
A directory bundle is selected for its executable, so the executable's
canonical name classifies it; the bundle directory spelling is not an
artifact alias. Bundle facts now reach artifact, archive and database
targets so the admitted-source digest binds to the executable.
Fixes#7362
* fix(artifacts): share the artifact identity across every artifact route
The trailing-archive fallback also carries the bundle facts now, so a
bundle whose executable is a script stub with an appended ZIP keeps its
digest bound to the executable.
* fix(artifacts): name the classification spelling at the resolver boundary
The resolver already knows whether a bundle directory or a file was
selected, so it hands the executable or the caller's spelling to the
reader instead of inferring it from the Info.plist. Every admitted route
now shares one identity, and the .app bundle operations accept a bundle
whatever its executable admitted as.
* fix(artifacts): route bundle keyed archive reads by bundle facts
A bundle whose executable is a plist now carries bundleInfoPlist, so
inspect_keyed_archive must read bundle-relative archives instead of the
standalone route that compares the selected bundle directory with the
executable.
* refactor(artifacts): build the selected ESP flash identity from the shared owner
The selected ESP flash route from main built path, source path and digest
inline. Use the resolver's shared identity so it carries the same facts as
every other admitted target.
* fix(web): omit importer integrity for the trusted resolver stub
Import-map integrity that covers the selected importer was applied to REA's
replacement resolver script. Strip only those integrity entries from the
installed map so native resolution can proceed while preserving dependency
integrity and the caller-selected map identity.
Fixes#8544
* fix(web): keep __proto__ import-map integrity keys on a null prototype
* fix(cli): emit compact JSON validation errors for demangle-swift
Piped and redirected --json argument diagnostics went through Incur's
always-indented Formatter, bypassing REA's destination-aware writer.
Buffer non-TTY SDK JSON like JSONL and rewrite through writeJsonOutput.
* fix(cli): keep token-count for piped SDK JSON documents
Compact JSON rewrite for non-TTY SDK output returned early before
token controls, so --llms/--schema --format json --token-count printed
the document instead of its count.
* fix(cli): do not token-count non-document JSON captures
Completion hooks and other non-JSON --json pipe output must stay verbatim;
token-count on non-JSON SDK captures applies only with sdkDocumentControls.
* test(cli): assert demangle JSON validation by code, not wording
* fix(javascript): emit no child process for a literal non-object options slot
Node validates the child_process options object before creating anything, so a
literal primitive or a literal array in that slot throws ERR_INVALID_ARG_TYPE
synchronously and no child process exists. The analysis still reported a complete
spawn for them.
Measured on Node 24.22.1:
spawn(cmd, "status") -> THROWS ERR_INVALID_ARG_TYPE
spawn(cmd, [], "pipe") -> THROWS ERR_INVALID_ARG_TYPE
spawn(cmd, [], null) -> THROWS ERR_INVALID_ARG_TYPE
spawn(cmd, [], []) -> THROWS ERR_INVALID_ARG_TYPE
execFile(cmd, "status") -> THROWS ERR_INVALID_ARG_TYPE
fork(worker, "status") -> THROWS ERR_INVALID_ARG_TYPE
fork(worker, [], "pipe") -> THROWS ERR_INVALID_ARG_TYPE
--- accepted by Node ---
spawn(cmd, args, undefined) -> SPAWNED
spawn(cmd, args, {}) -> SPAWNED
execFile(cmd, [], null) -> SPAWNED
fork(worker, [], null) -> SPAWNED
exec(cmd, "inherit") -> SPAWNED
The asymmetries matter: `spawn` rejects `null` and exempts only `undefined`,
while `fork` and `execFile` skip any nullish options, and `exec` spreads its
options and validates none of them. Drop a candidate only when the options slot
is a provable literal primitive or array, so an unresolved or non-literal value
stays a candidate.
`spawn(cmd, [], null)` is the sharpest case: it was indistinguishable from the
valid `undefined` form, including a resolved connects-stdio relation, for a call
that dies before any child exists.
* chore: retrigger title and readiness checks after the title was corrected
* fix(javascript): reject literal options Node shall reject, and drop a stray file
Two review findings on #8019.
greptile: `rejectsOptionsShape` still kept `spawn("tool", [], 0n)`, `-0` and
`1e400`, which Node also rejects before creating a child
(`ERR_INVALID_ARG_TYPE: The "options" argument must be of type object`). Those
forms are exactly the ones `readExactJavaScriptLiteral` refuses to price — a
BigInt, a non-finite numeric literal and a negated numeric literal — so read the
shape directly instead of widening the shared exact-value reader. `NaN` is an
identifier rather than a literal, so it still needs scope resolution and is left
as a documented gap.
Also remove `.rea-hopper-stage-YOryZ6/`, a local Hopper installation stub that an
`add -A` swept into the commit. It is unrelated to this change.
* fix(javascript): keep a void options slot, reject ! and ~
greptile flagged that `isUnpricedPrimitiveLiteral` dropped `spawn("tool", [], void 0)`,
`fork(..., void 0)` and `execFile(..., void 0)`. It checked the unary operand without
the operator, but `void <expr>` evaluates to `undefined`, which is the one options
value Node accepts in place of an object.
Measured on Node 24.22.1:
spawn(cmd, [], void 0) -> SPAWNED
spawn(cmd, [], void 5) -> SPAWNED
spawn(cmd, [], -0) -> THROWS ERR_INVALID_ARG_TYPE
spawn(cmd, [], -5) -> THROWS ERR_INVALID_ARG_TYPE
spawn(cmd, [], +5) -> THROWS ERR_INVALID_ARG_TYPE
spawn(cmd, [], !0) -> THROWS ERR_INVALID_ARG_TYPE
spawn(cmd, [], ~0) -> THROWS ERR_INVALID_ARG_TYPE
So the operator now decides: `void` keeps the spawn, `!` and `~` reject whatever
their operand, and `+`/`-` reject only when the operand is a numeric literal.
* fix(javascript): reject a non-finite global in the child-process options slot
An unshadowed `NaN` or `Infinity` in the options slot is a number, and Node's
options check rejects a number exactly as it rejects any other primitive. The
guard read only literal nodes, so the global identifier passed through. Measured
on Node 24.22.1:
spawn("e", NaN) -> THREW ERR_INVALID_ARG_TYPE
spawn("e", Infinity) -> THREW ERR_INVALID_ARG_TYPE
execFile("e", NaN) -> THREW ERR_INVALID_ARG_TYPE
fork("x.js", NaN) -> THREW ERR_INVALID_ARG_TYPE
--- accepted ---
exec("e", NaN) -> ok, because exec validates no options
spawn("e", undefined) -> ok, the one accepted global
spawn("e", null) -> ok
spawn("e", []) / ({}) -> ok
`isUnshadowedGlobal` keeps a local binding named `NaN` out of the rule, so a
shadowed value keeps its fact. Seven regressions cover the rejected forms plus
`exec`, `undefined`, `null`, an array, an object and a shadowed `NaN`; all seven
fail on the previous commit.
* fix(javascript): emit no promise for a literal non-callable executor
Node's `Promise` constructor calls the executor synchronously and throws a
`TypeError` when it is not callable, before any promise object exists. The
analysis reported a `constructor` promise operation with a hardcoded
`sourceResolution: "complete"`, which the graph projects as
`creates-promise` `resolved`.
Measured on Node 24.22.1:
new Promise() -> THREW TypeError
new Promise(1) / (-1) -> THREW TypeError
new Promise("work") / (`work`) -> THREW TypeError
new Promise(true) / (!0) -> THREW TypeError
new Promise(null) / (void 1) -> THREW TypeError
new Promise(1n) / (/re/) -> THREW TypeError
new Promise([]) / ({}) -> THREW TypeError
--- accepted ---
new Promise(() => {}) -> OK Promise
new Promise(async () => {}) -> OK Promise
new Promise(function () {}) -> OK Promise
new Promise(class {}) -> OK Promise
A class passes Node's own `typeof` check, so it keeps its operation. An
identifier, a call result or a spread may still supply a function and also keeps
its operation.
The filter gates the candidate push rather than `promiseCandidateDetails`, which
also decides whether an `await` becomes an `awaited-expression`: gating there
made `await new Promise(1)` emit a spurious task fact, whereas gating at the push
site makes it emit nothing, which is the honest answer.
* chore: retrigger title and readiness checks after the title was corrected
* fix(javascript): reject a non-finite executor, not only a literal
greptile caught that `new Promise(1e309)` still emitted a promise. Babel stores
that literal as `Infinity`, and `readExactJavaScriptLiteral` refuses to price a
non-finite number, so the guard missed it.
Measured on Node 24.22.1 — every one throws before a promise exists:
new Promise(1e309) -> THREW TypeError
new Promise(1e400) -> THREW TypeError
new Promise(Infinity) -> THREW TypeError
new Promise(-Infinity) -> THREW TypeError
new Promise(NaN) -> THREW TypeError
A numeric literal is now read directly rather than through the exact-value
reader. `Infinity` and `NaN` are identifiers rather than literals, so they are
decided through `isUnmodifiedGlobalReference`; a shadowed binding keeps its
promise, which is the same scoping rule the timer fix uses.
* fix(javascript): link a response body reader only to a settled fetch
`collectResponseConsumers` treated any `.json()`/`.text()`/`.arrayBuffer()` on a
binding holding a request result as a response consumer, and gave it the
request's endpoint at `resolution: "complete"`. That holds only for a settled
`fetch`.
Measured on Node 24.22.1:
await fetch(u) -> Response json/text/arrayBuffer = function
fetch(u) unawaited -> Promise json = undefined; pending.json() THROWS TypeError
http.get(u) -> ClientRequest json/text/arrayBuffer = undefined
http.request(u) -> ClientRequest json = undefined; request.text() THROWS TypeError
new WebSocket(u) -> WebSocket socket.json() THROWS TypeError
await http.get(u) -> ClientRequest (await does not help)
So four shapes emitted a resolved "this endpoint's body is read here" fact for a
call that throws before any body is read: an un-awaited fetch, `http.get`,
`http.request`, and a `WebSocket` handshake.
Link only requests that settle to a `Response`: a `fetch` call whose value is
awaited through expression wrappers. A binding that also holds a request kind
without a body reader keeps the consumer a candidate (`partial`) rather than a
proven one, and a binding with no settled fetch emits no consumer at all, which
matches the `collectReleases` / `forkRejectsStdioArray` precedent — the receiver
provably has no such member, so there is no fact to report.
* chore: retrigger title and readiness checks after the title was corrected
* fix(ghidra): keep a changed existing project's refusal typed through launch
When the selected REA_GHIDRA_PROJECT_PATH project changes after
open_binary commits its profile, the launcher's verified copy refuses it
with AnalysisArtifactChangedError. GhidraHeadlessLauncher wraps that in
GhidraLaunchError, and the session error's cause became the wrapper, so
the projection missed the existing artifact_changed branch and returned
generic execution_failure with rea doctor advice.
Read the launcher's primary failure when it is an
AnalysisArtifactChangedError, so the refusal reaches callers as
artifact_changed with the changed path and the existing reopen guidance.
The refusal stays fail-closed: no automatic reopen or fresh-import
fallback. Other launch failures keep their current projection.
Fixes#8217
* fix(ghidra): describe a project profile mismatch without a timing claim
When the owned copy of a selected Ghidra project no longer matches the
fingerprint committed by open_binary, copyExistingGhidraProject threw
RegularFileChangedError, so the artifact_changed reason said the input
"changed while being read". A digest mismatch only shows that the
project differs from its committed profile; the change may have happened
before the copy started.
Throw AnalysisArtifactChangedError directly with a reason that states
the mismatch and leaves the time of the change unestablished. Changes
observed while files are being read keep their existing reason.
* fix(cli): seal in-process Evidence before publishing
The CLI rebuilt a plain bundle from records it had already authenticated,
so writeEvidenceBundle cloned and authenticated the complete analyzed
graph again at the final checkpoint. On a default heap that second copy
could exceed available memory after minutes of analysis and discard the
whole run.
Seal the in-process bundle once so publication can stream the canonical
form without the redundant authentication copies. The regression test
fails on the previous behavior with resource_constraint under a one-byte
heap allowance.
Fixes#8411
* fix(cli): preserve the heap check for mutable Evidence
Only seal the publication bundle when every record is already immutable.
Mutable producer records keep the writer's existing heap check instead of
being copied before that check. The regression now covers the safe
fallback and lets the shared temporary-directory fixture own cleanup.
* test(cli): keep a distinct mutable source in the publication regression
Seal the main record and give the mutable source its own evidence ID so
the map cannot replace it before the bundle guard runs.
* fix(gdb): keep sessions usable after an oversized stream record
A single MI console, target or log record over the 16 MiB line limit
failed the whole session protocol, so close could not re-synchronize
and GDB plus its inferior stayed owned with no recovery path.
MI stream records carry only output and c-strings escape newlines, so
a raw LF always ends the record. #receive now drops the remainder of
an oversized stream record, marks pending command output and retained
records truncated, and keeps parsing later lines; oversized result
and async records still fail closed as before.
Fixes#8023.
* fix(gdb): keep earlier records in a chunk with an oversized stream record
* test(gdb): drop superseded oversized-record test copies
Artifact classification probes every ELF root for a Node SEA note. Each
PT_NOTE program header was scanned on its own, one read per note, so
headers that declare the same note bytes multiplied the work: a 295 KB
ELF with 4,096 such headers kept inspect-artifact busy for about three
and a half minutes.
Every note header takes at least 12 bytes, so disjoint tables hold at
most one note per 12 file bytes. The scan now stops there. Past that
count the remaining tables are unreadable: an ELF without a confirmed
blob keeps its native classification, and a confirmed blob fails closed.
* fix(wasm): name the compare_wasm_interfaces side of rejected Evidence
compare_wasm_interfaces parsed both sides inside one try and reported any rejection, such as tampered content, a different operation or a rebound subject, as an input issue at path [], which names no argument. Each side is now parsed separately and a rejection is reported at [left] or [right], as the function, native and managed comparers already do.
* test(wasm): cover both comparison sides through the MCP boundary
Android's package parser discards an activity, activity-alias or receiver
intent filter that declares no action, so such a filter neither makes the
component exported by default nor triggers the API 31 explicit-export
requirement. Services keep action-less filters. The manifest inventory
counted every declared filter, reporting exported_default true (or an
explicit-export validation error) for components Android does not export.
intent_filter_count still reports every declared filter.
Setup against npm latest already accepts --client freebuff. Replace the
stale proposed-integration wording while keeping the unverified native
client loading caveat.
Compact input schemas are the default advertisement since #5975, and the
profile drops every property description. On 23 of the 31 tools with a
guided `detail` option, including inspect_artifact and inspect_macho,
`detail` is advertised as a bare complete|summary enum, so callers take the
complete default. On a universal macOS app that reply was about 699k
characters for inspect_artifact and 336k for inspect_macho, against about
4.4k and 3.2k with summary.
Keep the canonical description of a root `detail` property in the inline
compact input form. The reduced form already keeps first-level guidance, and
compact output schemas are unchanged. Advertised compact input schemas grow
from 127,616 to 133,358 bytes across 233 tools; the largest schema stays at
4,107 bytes, within the 13,312-byte budget.
The new boundary test compares `detail` guidance between the full and compact
tools/list and fails before the change.
* fix(javascript): require exactly one fork IPC channel
Node's fork() rejects an explicit stdio array that carries more than one IPC
channel, and the throw is synchronous, before any child process exists:
if (ArrayPrototypeFilter(options.stdio, ...'ipc').length > 1)
throw new ERR_IPC_ONE_PIPE()
The analysis already dropped an array with no "ipc" (#7992) but still reported a
resolved fork for an array carrying it twice.
Measured on Node 24.22.1:
fork({stdio: ["ipc", "ipc"]}) -> THROWS ERR_IPC_ONE_PIPE
fork({stdio: ["pipe", "ipc", "ipc"]}) -> THROWS ERR_IPC_ONE_PIPE
fork({stdio: ["ipc"]}) -> SPAWNED connected=true
fork({stdio: ["pipe", "ipc"]}) -> SPAWNED connected=true
fork({stdio: ["pipe", "pipe", "pipe", "ipc"]})-> SPAWNED connected=true
So the required count is exactly one, not at least one. Count the resolved "ipc"
literals and reject the candidate unless the count is one; a spread or an
unreadable element may still change the count and is left alone, as before.
* chore: retrigger title and readiness checks after the title was corrected
The host no-return verifier, scripts/verify-real-ghidra-noreturn.mjs,
fails before its Evidence checks. ReaNoReturnProbe calls private bridge
methods through reflection, and two bridge changes left the probe behind:
- d4bd65c53 added a Function parameter to noReturnCallLimitations, and
225f26a5f renamed the method to functionBoundaryLimitations. The probe
still looked up noReturnCallLimitations(Iterable) and stopped with
NoSuchMethodException.
- 01a1b3fda made procedureReferences return a function observation
envelope. The probe read references and unresolved_calls from the top
level of that envelope instead of from its value.
Pass each function and its instructions to functionBoundaryLimitations,
and read procedureReferences results from the envelope value. The require
checks are unchanged. CI does not run this verifier.
These edits are the probe repair from robotaro's open draft that treats
demangled C++ runtime imports as returning, copied line for line, so
either change merges cleanly after the other.
Co-authored-by: robotaro <[email protected]>
The design guide said the setup allowlist does not copy
REA_MCP_MAX_RESPONSE_BYTES, but SELECTED_REGISTRATION_SETTINGS includes
it and setup forwards an explicit selection, as the client guide states.
Fixes#7420
An argument that accepts inline JSON or a JSON file path also reads the
request from standard input when it is "-". The behavior shipped without
documentation. Describe it next to the request-file example, with its
UTF-8, byte-order mark and 8 MiB limits.
Fixes#7138
The One analysis engine readiness row offered ghidra (or hopper, ida) while
the CLI accepts hopper, ghidra, ida, jadx and kuna for doctor --provider.
Add the missing IDs and keep the table aligned.
Related to #4624: docs/cli.md carries the same stale enumeration and is
updated separately in #4651.
* fix(ghidra): mark empty xrefs results as unresolved, not unreferenced
Ghidra 12.2 resolves no references to data addresses on some Mach-O
imports, and xrefs returned a bare empty list with nothing separating
'no references exist' from 'this provider resolved no references', so
callers concluded addresses were unreferenced.
projectExecution now attaches ghidraEmptyXrefsLimitation to an empty
xrefs result: the list reflects what this analysis resolved, not proof
the address is unreferenced. Non-empty results carry no caveat.
Covered by behavior tests for both branches; validated against
FM9-Edit-arm64 (expect: limitation) once the daemon session reopens.
* fix(ghidra): drop circular trace_feature cross-check from empty xrefs caveat
trace_feature calls xrefs internally, so recommending it as an independent
cross-check repeats the same lookup the caveat qualifies. Keep disassembly
as the single independent check.
* fix(ghidra): report actionable-reference absence and invalidate stale snapshots
Review follow-up on the empty xrefs caveat:
- The bridge filters synthetic entry-point references before observation, so
"resolved no references" could be false; the caveat now reports that no
actionable references were returned and names the omission.
- Drop the trace_feature cross-check: traceReferences observes xrefs per
matched string, repeating the same lookup instead of independent evidence.
- Bump instruction_references semantics to v3 so analysis snapshots captured
before the caveat replay no bare empty results; the profile commitment in
the snapshot binding no longer matches.
* feat(ghidra): mark empty function-reference facets as unresolved too
Extends the empty-xrefs caveat to the sibling ReferenceManager consumers
review follow-up identified: procedure_references and analyze_function
reference facets built on the same observation path could still present
empty arrays as collected facts.
- ghidraEmptyFunctionReferencesLimitation attaches when
procedure_references returns no references for the requested direction
and when a dossier observes no references in either direction;
callers/callees (function-manager derived, observed working on the
reported target) stay caveat-free.
- One emptyReferenceCaveat owner replaces the inline xrefs branch.
- function_references semantics bump to v3 so snapshots captured before
the caveat do not replay bare empty facets; raw profile digests
recomputed.
* fix(ghidra): cover annotation and instruction reference facets in the caveat
Review follow-up: annotate_native_function returns a dossier built by the
same analyzeFunction collection and inspect_native_instruction builds
incoming_references with visitObservedReferencesTo; both could present
empty arrays as collected facts under the reported ReferenceManager
failure.
- annotate_native_function attaches the caveat when its dossier observes
no references in either direction.
- inspect_native_instruction attaches it when incoming_references is an
explicit empty array (null or omission stays unavailable, matching the
field contract).
- Split the diagnostics describe to keep each block under the lint bound.
- Behavior tests cover attach and non-attach for both operations.
* fix(ghidra): name the honest cross-check limits in the reference caveats
Review follow-up: the motivating failure was a string address, so
'cross-check the disassembly at this address' pointed at nothing — data
and string addresses have no instruction to disassemble — and
inspect_native_instruction's incoming_references is built through the
same visitObservedReferencesTo path, so it was not independent either.
- xrefs caveat now states that no independent in-product cross-check
exists for the empty result, that surrounding disassembly only helps
at a code address, and that data or string addresses need an external
disassembly scan (otool -tV adrp/add pairing).
- Function-facet caveat separates outgoing references (own disassembly
is independent) from incoming references (no in-product check; scan
other code externally).
* fix(ghidra): use the address-scoped caveat for inspect_native_instruction
Greptile review follow-up: inspect_native_instruction checks incoming
references to one exact address, so the function-scoped caveat misdescribed
the scope; an instruction can be unreferenced while its function has
references elsewhere, and the address may have no containing function.
Attach the address-scoped xrefs caveat instead.
* fix(ghidra): qualify each empty reference direction independently
Analyze-function and annotate-native-function results attached the
unresolved-references caveat only when BOTH reference directions were
empty, so a dossier with one populated direction presented its empty
direction as a collected fact.
Split the combined function-references caveat into direction-scoped
limitations and attach one per empty direction (analyze_function,
annotate_native_function dossier readback, procedure_references by its
requested direction).
* fix(ghidra): qualify empty outgoing instruction references
inspect_native_instruction results carry two independent
ReferenceManager facets: the incoming list and the outgoing references
built from instruction.getReferencesFrom(). Only the incoming list was
checked, so an instruction with a populated incoming list and no
outgoing references presented the empty outgoing facet as a collected
fact, and any populated incoming list suppressed the caveat entirely.
Each facet is now qualified independently: the empty incoming list
keeps the address-scoped caveat; the empty outgoing list attaches the
new instruction-scoped outgoing limitation (this stream is not
entry-point filtered, so it claims no entry-point omission). Truth
table covered in describe("Ghidra empty instruction reference facets").
* fix(ghidra): gate the instruction outgoing caveat on decoded status
Non-decoded inspect_native_instruction results (data, not-in-listing,
not-instruction-boundary, outside-memory) return before the bridge ever
queries instruction.getReferencesFrom(), so their empty references list
is an unobserved facet, not an empty ReferenceManager observation. The
unconditional outgoing caveat claimed the ReferenceManager returned no
references from an instruction that does not exist at that address.
The outgoing limitation now attaches only when status is decoded; the
address-scoped incoming caveat still applies per the empty incoming
list, matching the original motivating data-address case.
* fix(ghidra): dossier incoming omission and external outgoing recovery
Two unqualified cases remained in the function-reference caveats:
- The analyze_function/annotation dossier excludes incoming references
whose source is inside the function's own body (a self-recursive call
is enough), so the projected empty list can mean 'no outside source
observed' or 'only in-body sources observed'. The dossier now uses a
dedicated incoming limitation naming the exclusion, pointing at the
outgoing edges/disassembly where in-body edges remain visible, and at
procedure_references(incoming) - the same ReferenceManager stream
without the in-body exclusion - as the in-product discriminator.
procedure_references keeps the generic incoming caveat: its incoming
stream is not in-body filtered.
- An external function has no local body, so the outgoing caveat's
'function's own disassembly' recommendation was impossible. Records
classified external now get an external-specific outgoing limitation
naming the defining artifact and this program's call sites instead
(procedure_references outgoing, analyze_function, annotation readback).
* fix(ghidra): qualify dossier incoming caveat observation
The dossier-specific incoming caveat claimed the ReferenceManager
observed no incoming references from outside the body, but
visitObservedReference (ReaGhidraBridge.java:3973-3974) drops
entry-point references before the body filter (1619-1621), so the
manager may have returned references the dossier never reports.
Reframe as 'returned no actionable' and name both omissions; correct
the recovery claim: procedure_references(incoming) lacks the in-body
filter but omits synthetic entry-point references as well.
* feat(elf): decode Linux core dump process and memory mapping notes
Inspect Linux core dump note tables and decode structured metadata without
requiring external tools or debuggers:
- NT_PRPSINFO (type 3): decode process ID, parent ID, process group,
session, UID, GID, execution state, filename, and command line arguments
bounded by the text budget.
- NT_FILE (type 0x46494c45): decode mapped memory ranges (start_address,
end_address, file_offset_pages) and mapped file paths with page size.
- NT_SIGINFO (type 0x53494749): decode faulting signal number, errno, and code.
- NT_AUXV (type 6): decode auxiliary vector pairs with known AT_* tag names.
- Support both 32-bit and 64-bit, little-endian and big-endian targets.
- Retain unknown core note types gracefully without partial success errors.
- Add comprehensive unit tests, fixtures, acceptance tests, and documentation.
* fix(elf): preserve raw path bytes and enforce auxv termination
* fix(elf): account for path hex text budget and enforce auxv trailing padding
* fix(elf): account for row and scan limits, ABI signal order, and lossless source IDs
* fix(elf): cache auxv padding scans, pre-charge entries, and isolate page-size test
* fix(browser): keep negative script columns Chrome reports
Chrome reported a Debugger.scriptParsed event with startColumn -60 and
endColumn -15 for an inline script in WordPress's helper frame. CDP declares
these columns as plain integers, but REA's admission required min(0), so one
script failed the whole observe-web-execution run with unreadable_output.
CDP resource columns now share one representation, cdpResourceColumnSchema,
for script start/end columns, initiator call frames, parser initiators and
event listeners. Positions on a script's first line include its start column,
so keeping the reported values keeps the documented script-relative
conversion consistent. The domain contract and web runtime docs state that a
negative column does not locate a column in the resource.
Fixes#8674
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(browser): apply the CDP column policy to passive capture
inspect_web_page and discover_webmcp_tools still read CDP columns through
cdpSourceCoordinates, whose own >= 0 check turned a negative column into
null and reported invalid_protocol_value. WebMCP discovery also dropped the
tool's whole registration_source.
cdpResourceColumnSchema now lives with the domain's CDP observation schemas.
Runtime admission, the passive coordinate helper and the runtime, console,
initiator and WebMCP output schemas all use it. Lines keep their
non-negative rule.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(reference): Exclude Git pointer files from source identities
Match .git pointer files in the default import policy after the existing
directory rules, preserving ordinary-checkout exclusion provenance and Git
HEAD discovery. Cover linked-worktree identities, repeated imports, nested
metadata, similarly named source files, and configured-secret precedence.
Independent review: parent independent review APPROVE for Draft CI on patch
61bddf108952c77858fa71c333b4bb8a05ab33e8b03f5c6bccd879cd19c7c379 and source
tree 2d2372a9ec. Review triggers: delegated
shipping output, source inventory/file I/O behavior, and more than 50 lines.
Validation: npm run check passed with Node 24.22.1/npm 11.16.0 on Windows;
tracked Vitest regression 1 passed/3 Windows skips; test discovery passed.
Eight offline Linux production imports passed on cached Node 22.19.0 as
supporting evidence only. Pinned Linux/macOS Vitest cases await hosted CI.
Fixes#7551
* test(reference): Isolate worktree fixtures from host Git settings
Route every fixture Git command through one wrapper with a filtered child
environment, owned empty global config/template/hook paths, and no system
config reads. Keep resource cleanup under the existing temporary owner.
These settings are scoped to synthetic child processes; host Git settings
and this repository's commit/push checks are unchanged.
Targeted independent review: parent review APPROVE on patch
4be9a2af3e40edac3d64f687170e966a87fcf762f1877f2c55016e120bcd90ac and tree
a2ba91ee9c. The reviewer confirmed the native
proof helper hash, complete fixture command coverage, and owned cleanup.
Review triggers: delegated follow-up and fixture file/process boundaries.
Validation: npm run check:fast passed; targeted Vitest 1 passed/3 native
Windows skips. On pinned Node 24.22.1, the actual helper rejects inherited
hooks/templates while commit, worktree creation, HEAD and status checks pass.
The unisolated control copies the injected template and its commit fails.
Refs #7551
Preserve the twenty-entry diagnostic prefix and report the full count,
omitted count and truncation fact at the owning report projection. Document
the recovery limitation and cover the public HAR-to-comparison workflow.
Fixes#7621
Validation: 121 source tests; three selected CLI regressions (five other
cases unselected); nine README conformance cases; full --noEmit typecheck;
documentation facts/metadata/schema/licenses; module boundaries and
architecture guards. VitePress rendering is blocked by Windows EPERM on its
worktree-local Vue symlink. Full aggregate coverage remains for Draft CI.
Independent code review: APPROVE, relayed by parent task
01a17716-6299-75fe-87e9-b0398b8c9019. Trigger: >=50 changed lines / >=3 files.
Reviewed tree: ba964efcd6
Reviewed full-index patch SHA256:
972dcfc219dd7b9606175b1848068f92210fa3f7d0a8a244c1ac7ce296ae32d9
* fix(flutter): read unlabeled Dart Version::String() from engines
Release libflutter.so embeds Dart's Version::String() without the
standalone VM's "Dart VM version:" label, so identify_flutter_build left
dart_version null on real APKs. Accept that channel-bearing form beside
the labeled scanner path.
* fix(flutter): qualify unlabeled version candidates
---------
Co-authored-by: morluto <[email protected]>
Chrome DevTools HAR exports declare creator WebInspector 537.36 and
report IPv6 serverIPAddress values in brackets: its HAR writer strips
the port with lastIndexOf(':') but its bracket removal,
replace(/\[\]/g, ''), only matches the literal pair '[]'. The pinned
schema then rejects the whole archive at the first such entry.
Extend the existing Playwright 1.58.2 adaptation to this declared
producer. Brackets are removed only in the disposable validation copy
and only around a valid IPv6 literal; reported spellings are retained
and the per-entry limitation names the declared producer.
Application analysis preserves the complete literal request endpoint,
including the empty string, and the event seed already accepts a
retained empty name. The endpoint seed still required a non-empty
value, so trace-javascript-semantics refused to select a retained ""
endpoint with invalid_request.
The endpoint seed now accepts any string and keeps exact matching.
Boundary-field seeds keep their minimum because boundary nodes are only
produced from method names.
Found while fixing #6458; the event half of that report landed in
cf5e4f6fa.
Co-authored-by: Claude Fable 5.1 <[email protected]>
* feat(binary-layout): admit ELF64 AArch64 and name the refused host
inspect_binary_layout refused every AArch64 ELF, and on a non-x64 host the
refusal read "supports Linux x64 only", which looked like a target refusal
(#8709 ran on arm64 WSL and suspected an e_machine check).
The pwntools adapter now admits little-endian ELF64 AArch64 EXEC/DYN/REL
objects alongside the existing profiles, pwntools must report aarch64/64/
little, and the Evidence subject names arm64. The host refusal now names the
detected platform/arch and says the target was not read; the host check
itself is unchanged.
verify:binary:layout cross-compiles an AArch64 executable and relocatable
object (never executed) and checks sections, symbols and relocations
against GNU readelf, decoding ELF64 r_info as a 32-bit type under a 32-bit
symbol index. The real-binary-layout workflow installs the toolchain.
Refs #8709
* test(binary-layout): refuse RISC-V as the unsupported machine and assert the host refusal path
verify:binary:layout used e_machine 183 (AArch64) as its unsupported
target, which this branch now admits; use EM_RISCV (243) instead.
The unsupported-host provider test now asserts the refusal names this
host's platform/arch and says the target was not read, so reverting the
decoder to the old message fails it.
* fix(native): stop calling an off-screen window absent
The native UI helper looked up its selected window with
`.optionIncludingWindow`, which returned nothing while that window was off
screen. An existing window owned by the selected process was reported as
`window-mismatch`, before any permission check, so the verifier could not reach
its permission boundary. Select from all windows instead; absent and foreign
windows still fail.
The verifier's changed-bytes check also read `error.message`, but since
cc90771af a selection failure carries its helper code in `error.issues`, so the
check could not pass. Read the issue. Together these let the permissions lane
complete on main.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(native): own UI test processes from the moment they launch
The off-screen test registered cleanup only after parsing the ready record, so a
startup failure left `/usr/bin/open` and its app running. The lane test killed
only the verifier on a timeout, leaving its launcher and detached fixture alive
until they expired.
Register the off-screen owner immediately after the launch; the app inherits the
run token, so the sweep needs no ready record. In the lane test, every stand-in
records itself and leaves on one release the test's cleanup awaits.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(debugger): let REA exit when a debugger could not be stopped
`rea debug-with-gdb` and `rea debug-with-rizin` printed cleanup_incomplete
and set exit status 1, but the retained child and its pipes kept the event
loop alive, so the process never exited. A stdio MCP server whose client
disconnected hung the same way.
closeOwnedDebuggerSessions is the owner both managers finish through. It now
unreferences retained sessions, as supervised providers already do after
incomplete cleanup, restores the references on retry, and names the retained
process IDs. The one-shot commands report those IDs, since no later command
can retry. Debugger ownership, signals and detach behavior are unchanged.
GDB cleanup requests also no longer hold the host until their 120 s command
deadline, because cleanup already bounds its own wait.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(debugger): name retained debuggers when an MCP server shuts down
An MCP server that exits with a retained debugger logged only "REA provider
cleanup failed" and "could not close cleanly", so nothing named the process
left for the caller to check. The PIDs were on OwnedDebuggerCleanupError, but
each shutdown reporter read only the outer aggregate's message, and the debug
failure_cause description dropped the field too.
The failure type now lives in its own dependency-free module with one reader
that walks composed failures. The shutdown report, the server's onclose log,
the debug description and the one-shot CLI all use it, and the shutdown report
writes the same remediation the CLI returns.
The fake debuggers now model each request exactly, with real reply shapes, and
a request they do not model fails the test.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(debugger): compare retained-debugger guidance with its formatter
The new checks froze the wording "Check process ... and its target".
docs/testing.md asks guidance checks to compare with the owning formatter,
alongside independent checks of the facts. They now do, and each still proves
the process ID is named, because a formatter comparison alone would pass if the
formatter dropped it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Static checkpoints were admitted against the same budget as semantic and
application results, half of the parent's free heap. Once those charges used
it, the next checkpoint stopped the analysis and every remaining source lost
its static facts, with most of the parent heap still free.
Semantic and application results still stop at that budget. Static checkpoints
may now continue to three quarters of the free heap while the live heap also
has room for another copy of what it holds. Without that room, admission falls
back to the result budget. The live condition is needed because the limit V8
reports counts young-generation capacity that retained objects cannot use:
without it, the parent aborted on heap exhaustion at small heap limits.
Reserved relationships are given up only when the larger share runs short. A
stop still fails with a typed error, which now reports the limit that applied,
what was available, the live heap in use and what the checkpoint needed.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(doctor): report provider settings a client registration does not record
Provider checks read the terminal running doctor, so a registration with no
env block looked ready while its server reported not_configured. Compare what
an available provider relied on with the registration's own file and report the
setting names in a non-blocking registration_environment check. The client's
effective environment stays unknown, so health and the exit status are unchanged.
Fixes#8202
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(doctor): write the Cordis fixture at its literal path
The test resolved the path through supportedClients, whose default environment
argument CodeQL traces to the fixture's writes even though the test passes {}.
It only needed the path, so spell it like the other fixtures in this file and
drop a leftover directory it never used.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(evidence): discover missing Evidence links without transferring records
An agent could only learn that a retained record's source was absent by reading
complete records or by having a downstream analysis refuse. The summary said
nothing about links, and complete-record retention was easy to mistake for
source closure.
- Add detail: links to get_evidence_bundle and CLI inspect-evidence-bundle:
paged link rows with retained or missing status and no record payloads, for
selected records against the whole session or file.
- Add retained-closure traversal, the walk composed workflows require, reporting
complete or incomplete. Direct reads make no closure claim.
- Count direct and missing direct links in the summary.
Fixes#8454
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(evidence): count a repeated Evidence link once in every delivery
The Evidence schema does not deduplicate evidence_links, so the summary counted
a repeated link twice while link rows and counts reported it once. Both now read
one helper for a record's distinct direct links.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(evidence): validate link delivery against the advertised schemas
The test never called tools/list, so neither the shared validator nor the MCP
client checked its replies against what each profile advertises. A schema that
rejected the links result could have passed. Fetch the advertised definition and
validate every request and reply against it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(native): select retained batch decompile results through analysis views
A completed batch_decompile could only be read whole. When it did not fit the
MCP response budget, or an agent wanted one function, inspect_analysis_view
refused the parent as unsupported and the only recovery was exporting the
session.
- Own the batch result schema in the domain, replacing the contract's copy and
the workflow's hand-written item types.
- Add summary and batch-decompile views: body-free rows keyed by original index
with exact filters and opt-in bodies, and UTF-16 pages of one body.
- Report the decompiling provider from the upstream analysis profile, since the
Evidence provider is the composing workflow.
- Keep batch limitations visibly aggregated rather than attributing them to one
successful item.
Fixes#8498
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(native): deliver batch decompile as a retained summary
A batch that fits still floods the caller's context with every body, and one
that does not fit only named an export. Selecting a body that cannot fit named
an export too, even though its text pages can.
- Accept detail: summary on batch_decompile: retain the complete batch first and
return its counts, identities and sizes. The default stays complete, and the
selection never reaches the Evidence parameters.
- Offer a ready-to-call body-free first page when a complete batch overflows.
- When a selected batch page overflows, continue from the same retained parent
with fewer rows, then with one body's text pages.
Refs #8498
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): count every unavailable entry label in a batch summary
label_unavailable only counted failed lookups, so a batch whose provider
answered null for every label reported zero. The identity contract defines a
null name as an unavailable label in both cases.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): continue an oversized batch view from the caller's own source
A batch view read from an Evidence file or inline record advertised a
retained-evidence continuation for a parent the session never retained, so
following it failed. A view's continuation now repeats the caller's source and
only narrows the selection. An inline record offers none, because it cannot be
repeated within the reply it overflowed. Non-batch views skip the selection
parse, since delivery asks about every view it returns.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(native): require advertised batch schemas through the shared validator
A local validator map dropped any tool without an output schema, so removing an
advertised schema would have left these contract tests green. Require both named
schemas and validate through createMcpSchemaValidation, as docs/testing.md asks.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): offer file view continuations on servers that retain nothing
A fixed-target server reads an Evidence file through inspect_analysis_view but
records nothing, so its overflow skipped the continuation and returned generic
CLI guidance. A view's continuation names the caller's source and needs no
session, so it no longer depends on recording. Retained references still do.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(cli): keep captured source Evidence when one-shot delivery never commits
A completed observation that was cancelled at final close, failed cleanup, or
never reached bundle commit returned its evidence links but dropped the source
records the invocation had already captured. The caller could neither read those
records nor tell whether they were missing, captured or written.
Report the closure at publishCliAnalysisEvidence, the shared boundary every
--evidence_output command uses. details.source_closure carries authentic captured
records within 64 KiB and explicit omissions: not_captured for a link this
invocation holds no record for, and not_inlined for a captured record outside this
response. A committed bundle keeps its receipt instead, nothing is written after
cancellation, and successful delivery is unchanged.
Fixes#8589
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(cli): keep the typed failure when collecting source closure cannot finish
A captured source with a data-sized evidence_links array made
pending.push(...links) exceed V8's call-argument limit. On final-close
cancellation or a refused publication that RangeError replaced the original typed
failure, which is the one outcome this delivery step exists to preserve.
Walk the closure through a Set, which is a deduplicating breadth-first queue, so
no link becomes a call argument and each link costs one lookup however often it
repeats. undeliveredSourceClosure now owns authentication and never throws: if
examining the closure fails, it reports collection_failure with the bounded error
type and an unknown closure state, and the original failure is still returned.
Fault the final-close tests on the scope the command gave to matching, after
matching completed, and assert that close was reached, so a moved close fails
instead of passing on an earlier one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Preserve the dynamic symbol's visibility attribute (default or protected) when extracting ELF dynamic exports, and expose it across complete Evidence and projected analysis views.
Historical Evidence records lacking visibility normalize safely to 'unknown', and projected views omitting visibility do not inject default values into partial row projections.
Fixes#8517
* fix(javascript): emit no event operation for a literal non-function listener
`EventEmitter` validates a listener before registering or removing anything:
if (typeof listener !== 'function')
throw new ERR_INVALID_ARG_TYPE('listener', 'function', listener);
The throw is synchronous, before any listener exists, but the analysis reported a
`register` or `remove` operation for those calls.
Measured on Node 24.22.1:
emitter.on(1) -> THREW ERR_INVALID_ARG_TYPE
emitter.on("x", 1) -> THREW ERR_INVALID_ARG_TYPE
emitter.on("x", null) -> THREW ERR_INVALID_ARG_TYPE
emitter.on("x", true) -> THREW ERR_INVALID_ARG_TYPE
emitter.on("x", {}) / ([]) -> THREW ERR_INVALID_ARG_TYPE
emitter.once / addListener / prependListener / prependOnceListener /
off / removeListener with a literal -> THREW ERR_INVALID_ARG_TYPE
--- accepted ---
emitter.on("x", () => {}) -> ok
emitter.on("x", function () {}) -> ok
`rejectsEventListener` drops the operation only for an exactly readable literal
that cannot be a function. An identifier, a call result or a spread may still
supply a function and keeps its operation, and a class expression passes Node's
own `typeof` check.
`emit` and `removeAllListeners` are untouched: `emit` passes its remaining
arguments as data to listeners that already exist, and `removeAllListeners` takes
only an optional event name.
* fix(javascript): price a non-finite listener and keep a shifted spread
Two review findings on the event-listener guard.
**A non-finite numeric literal was missed.** Babel stores `1e999` as a
`NumericLiteral` holding `Infinity`, and the exact-value reader refuses to price a
non-finite number, so the guard never saw it:
e.on("x", 1e999) -> THREW ERR_INVALID_ARG_TYPE
A numeric literal is always a number and therefore never callable, so the guard now
decides on the literal node directly.
**A leading spread shifts the listener position.** `e.on(...args, 1)` does not pass
`1` as the listener: Node spreads the array and ignores the trailing argument once
the listener is satisfied. The guard read `arguments[1]` regardless, so it dropped a
real registration. It now leaves the position undecided when argument 0 is a spread.
Five regressions cover both forms; all five fail without the fixes.
* fix(process): re-read unreadable tokens before signaling and name the process
Darwin KERN_PROCARGS2 samples a process's argument length and stack top
before it reads the address space, and exec publishes the new length
before it copies the new strings out (xnu sysctl_procargsx,
exec_copyout_strings). A read during exec can return a zero-filled or
stale strings area, and exec keeps the PID and start time that bracket
the read. The final sweep already re-read such tokens, but the
pre-signal group check read each member's token once, so one transient
read of a live group member failed the whole cleanup.
Move the bounded re-read into one shared fact-reading helper and use it
for both the sweep and the pre-signal group check. The group check binds
re-reads to the generation it first observed, so a reused PID stays
unresolved; the sweep keeps classifying the PID's current process.
An unreadable-token failure now carries the native identity read after
the token and the summary names the candidate by its native name,
parent, group and start identity. The Darwin helper and Linux procfs
reader report the short native process name (p_comm/comm) for display.
Fixes#8337
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(process): refresh other pre-signal members after token re-read waits
The pre-signal group check re-read only members whose token was
unavailable, so a readable member kept the relation it had before the
retry waits. A member that moved to another process group during those
waits could be validated on its old group, and the old group signaled
while the moved member kept running.
After any retry wait, the shared re-read helper now reads every other
live pre-signal fact once more with the same generation binding. A moved
member then takes the existing changed-group path, which retains its
ownership evidence and fails cleanup. An inconclusive refresh replaces
the stale fact so it fails closed. Nothing changes when no re-read ran,
and the sweep keeps classifying current processes because every group
it finds is revalidated before signaling.
Also cover PID reuse that happens before the re-read starts, where both
re-read identities agree and only the first-read generation rejects the
replacement's token.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(process): keep known identity changes final across token re-reads
The pre-signal re-read treated a first read without a same-generation
identity as unbound and replaced it with the re-read. On Linux the first
read can already establish PID reuse (an ESRCH token whose exit check sees
another generation); the re-read then classified the replacement, so a
zombie replacement could let cleanup succeed.
A pre-signal check now re-reads only same-generation facts. Changed,
exited, exiting and foreign-token facts are final. Each attempt also
re-reads the group's owned members instead of one separate refresh after
the loop, so a member that moves groups or turns unreadable while another
waits is seen within the same bound. The caller's run token selects the
owned members, and the sweep keeps classifying current processes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(setup): refuse Hopper on macOS below the release's minimum
Hopper setup planning accepted any macOS 12+ host as supported, and the
installer never compared the host with the selected release. The current
publisher-signed Hopper 6.6.1-demo requires macOS 12.4 (feed
deployment_target and the app's LSMinimumSystemVersion), so setup on
12.0-12.3 installed an app the host cannot run.
The installer now takes the minimum from the release that owns it: the
feed's deployment_target before the package download, and the signed
app's LSMinimumSystemVersion (decoded in-process after signature
verification) before copying. A host below either fails with the new
incompatible_host_version code and an installer detail naming both
versions; an incomparable app value is a bundle failure. Planning keeps
REA's own macOS 12 floor but reports support as unknown, since the
release minimum is read only after approval, and the plan discloses the
check. Info.plist decoding is shared with app-bundle resolution.
Fixes#8502
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(setup): compare the macOS Hopper plan detail with its owning disclosure
The planning test froze an explanatory sentence from the setup plan. The
macOS install detail now lives in macHopperInstallDisclosure beside the
commands and origins the installer owns, the plan uses it, and the test
compares the plan with that constant while keeping its independent
support-state assertions. An installer test also pins that the signed
app's requirement is read only after both signature checks pass.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(setup): compare macOS Hopper refusal details with their owners
The installer's refusal details were template literals inside
installMacHopper, so tests copied whole sentences. The installer now
formats them through exported macHopperHostVersionDetail and
macHopperIncomparableMinimumDetail. Tests compare each diagnostic and the
setup remediation with those owners and SetupInstallFailure's recovery,
and separately assert the URL, versions, setting name, failure code and
request order. The Info.plist type refusal is checked by error type, key
and path instead of a sentence fragment.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
The handshake check ran only inside startSession, which a query reached
only when the profile committed a startup input, for PE or for an existing
project. Any other session started lazily inside callTool, so analysis seeds,
disabled analyzers, extension results, MIPS soft-float storage or a RISC-V
calling convention that the profile did not commit were never checked and
the query returned its result.
Every query now runs the checked start first. The client shares one startup
attempt, so there is still one launch, and a provider restarted within the
session is checked again. That leaves startSession as the only start in the
provider client, so the PE data-type path reuses its session info.
Recovery guidance is unchanged: the former eager-start condition now selects
only whether a completed handshake counts as a running provider.
Fixes#8612
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(native): select retained batch decompile results through analysis views
A completed batch_decompile could only be read whole. When it did not fit the
MCP response budget, or an agent wanted one function, inspect_analysis_view
refused the parent as unsupported and the only recovery was exporting the
session.
- Own the batch result schema in the domain, replacing the contract's copy and
the workflow's hand-written item types.
- Add summary and batch-decompile views: body-free rows keyed by original index
with exact filters and opt-in bodies, and UTF-16 pages of one body.
- Report the decompiling provider from the upstream analysis profile, since the
Evidence provider is the composing workflow.
- Keep batch limitations visibly aggregated rather than attributing them to one
successful item.
Fixes#8498
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): count every unavailable entry label in a batch summary
label_unavailable only counted failed lookups, so a batch whose provider
answered null for every label reported zero. The identity contract defines a
null name as an unavailable label in both cases.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(native): require advertised batch schemas through the shared validator
A local validator map dropped any tool without an output schema, so removing an
advertised schema would have left these contract tests green. Require both named
schemas and validate through createMcpSchemaValidation, as docs/testing.md asks.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
After a validated seed, trace_native_values turned a cleanup-incomplete
failure from a call-target read or a followed callee dossier into a
plain-text unknown, kept dispatching reads, and returned successful
Evidence without the cleanup_incomplete status, cleanup reason or owned
resources.
The trace now classifies child failures with the shared stopsTraversal
predicate that call graph traversals use. A cancelled child still ends
the trace with its cancellation. A child whose provider cannot confirm
cleanup stops the trace: the provider's own error is the outcome, so its
resources and bound cleanup retry stay intact, and the work admitted
before the stop is returned as the trace's normal shape, with traversal
truncated by cleanup_incomplete and a located unknown at the stopping
read. That stop stays primary over a concurrent caller abort and over
the output-size check. Ordinary child failures remain unknowns.
MCP already retains such partials as workflow Evidence. The one-shot CLI
dropped them, so runDirectAnalysis now retains the partial workflow
Evidence on the failure through the same shared owner.
Fixes#8371
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(cli): describe one-shot timeouts for an invocation whose session has ended
A one-shot CLI analysis that reached a provider deadline told its caller
that the Ghidra session remained usable and reported a runtime-editable
limit applying to the next operation, although the command had already
released its provider session and runtime.
The shared error projection now takes an explicit caller lifetime.
Providers keep guidance that holds only for a continuing caller (session
reuse, same-session runtime updates) apart from the request outcome, and
the presentation adds it only for a continuing caller. One-shot owners
(runDirectAnalysis, runProviderAnalysis, finishAnalysisCommand and the
JavaScript application command) project for the invocation: details.limit
reports scope rea-invocation, applies_at next_invocation and the launch
environment seed, and the remediation leads with that setting for a new
invocation. MCP output keeps its runtime-update and same-session guidance.
Covers the Ghidra decompile timeout, decompiler failure, regex stack
exhaustion and startup timeout, the shared regex deadline, the Kuna
decompile timeout and the Hopper startup timeout.
Fixes#8311
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(cli): project composed child failures for the caller lifetime
Cleanup composition embedded its primary and cleanup failures as
continuing-caller projections, and baked the primary's message and the
cleanup's remediation into the composed error. A one-shot command whose
Ghidra startup timed out while shutdown also failed therefore still
reported a rea-runtime limit and update_analysis_settings in
details.diagnostics.primary_error.
childFailureDiagnostics now snapshots each embedded child at composition
for both lifetimes when its projection depends on the caller, so cleanup
history keeps its composition-time state and the final projection selects
the caller's form. composedCallerGuidance re-attributes message and
remediation with their continuing-only parts kept separate. Error copies
and redaction keep the invocation diagnostics. The Hopper startup timeout
now carries its continuing guidance on the error like the other producers.
Invocation limits keep agent_editable and rea_restart_required, with
values that hold for a new invocation, so only scope and applies_at
change and environment is added.
In existing-project mode a decompile timeout or a cancelled session now
says the next session copies and opens REA_GHIDRA_PROJECT_PATH again
rather than repeating import and analysis.
Tests compare delivered guidance with the owning formatters and constants
instead of freezing prose.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(discovery): carry provider repair instructions into capability discovery
Provider readiness reported only a failure description, so capability
discovery copied that description into tool_availability remediation and
dropped repairs the owners already had: the JEB discovery-deadline setting,
the ADB/Apktool execute-permission repair kept in diagnostics, and the IDA
and Kuna configuration recovery.
Unavailable ProviderAvailability now requires the owner's remediation
next to its reason, and readiness-derived unavailable capability
descriptors carry it too. Every producer supplies it (ADB, Apktool, JADX,
JEB, Ghidra, Hopper, IDA, Kuna, pcap, owned-process prerequisites, profile
rejection); the inventory uses it verbatim, falling back to the reason
only for static capability facts that declare no repair. ADB/Apktool
configuration failures own remediation as a field instead of diagnostics,
and execution refusals project it as remediation.action. JADX doctor reads
the availability's remediation instead of re-deriving it.
binary_session complete candidate availability and unavailable capability
groups expose remediation (CLI capabilities/providers too); summary omits
it and lists it in omitted_fields. The superseded IDA
configuration_remediation and windows_native_failure.remediation
diagnostics are removed.
Fixes#8506
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(discovery): keep owner repairs through provider selection and capability maps
Address review findings on the readiness-remediation contract:
- Provider selection rejections carry the availability owner's
remediation, CLI/MCP error details project it, and an explicit
selection refused by readiness uses it as remediation.action. IDA's
unreadable registration and Ghidra's Windows readiness repair no
longer degrade to generic selection advice now that the diagnostics
copies are gone.
- Ghidra's Windows readiness and existing-project capability maps carry
the same repair as the availability.
- pcap readiness keeps the limiter owner's repair (now a typed
remediationAction) and falls back to Wireshark guidance only when the
failure has none.
- ADB/Apktool repairs are owner constants that are complete sentences.
The execution refusal keeps its previous message-plus-repair recovery,
which the compiled CLI contract asserts.
- Regression tests compare with the owning formatter or constant and
add public MCP and CLI coverage for an explicit IDA selection.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(evidence): read selected complete records without the whole bundle
get_evidence_bundle could discover a record by exact ID, but reading it
required the complete canonical bundle: complete + filters was rejected,
and inspect_analysis_view refuses parents without a view, such as
annotations, pseudocode sources and native traces.
- Add detail: records, a page (offset, limit; default 0, 8) of the
complete matching records in canonical bundle order. Records keep their
original IDs, subjects, providers and readback; missing_evidence_links
names direct links of the returned records that are not retained.
- The selection is labelled as historical and not an importable bundle;
complete keeps returning the canonical bundle, and offset/limit
require detail: records.
- Filters keep one owner shared by summary and records delivery, for the
MCP session and the CLI's inspect-evidence-bundle.
- An oversized record page now advises a smaller limit or one
evidence_id; whole-bundle overflow guidance mentions record reads.
Fixes#8307
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(evidence): compare record-selection guidance with its owners
Assert the record-selection limitations and oversized-page guidance
against their owning constants, and check the reported limit facts
separately, instead of pinning explanatory phrases.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(setup): report the failed Hopper request URL and HTTP status
Hopper download callers collapsed every transport failure into a bare
`ok: false`, so HTTP 404 and 503 from the macOS feed and DMG, the Linux
publisher feed, the public key and the launch-time package download produced
identical generic advice without the failed URL.
The shared downloader now returns a typed failure (url plus http_status,
timeout, network, response_too_large or url_refused) and keeps caller
cancellation as a rejection. Installers attribute it to the request
(release_feed, package or public_key); HopperReleaseError carries it, and
setup projects it once into `download` and a remediation naming the URL,
outcome and next step. Replaces the Linux-only "Package download returned
HTTP" special case.
Fixes#8503
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(setup): refuse credentialed Hopper URLs and keep known HTTP outcomes
Native Fetch rejects a URL with userinfo using a TypeError that echoes the
whole URL, so a credentialed redirect reached `download.detail`, setup
remediation and publisher messages as a network failure. The downloader now
refuses request and redirect URLs carrying credentials as
`url_refused: credentials` before requesting them, and reports the caller URL
through the shared userinfo-removing `sanitizeBrowserUrl`.
Releasing an unadmitted body no longer replaces the outcome it was released
for: an errored (truncated) body after HTTP 503, or a failed cancel at the
size limit, keeps `http_status` or `response_too_large`.
Tests compare delivered remediation with the owning formatters and assert
request, URL, status and recovery class independently.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(javascript): page retained feature traces through analysis views
A bidirectional trace_application_feature result can exceed one MCP
response, and inspect_analysis_view refused trace parents as unsupported,
so the only recovery was exporting the whole session.
- Add a shared feature-trace view owner: a summary with the trace's
identity, producer counts and coverage, and pages of paths, nodes,
edges, seed_matches and native_handoffs in retained order with exact
filters. Node rows are compact unless observations are requested.
Page coverage never upgrades trace coverage.
- Accept an optional MCP view on trace_application_feature that retains
the complete trace before returning the selected facts; the CLI keeps
complete JSON and reads saved traces with inspect-analysis-view.
- Offer a ready-to-call first paths page when a retained complete trace
exceeds the MCP response budget.
- Share the distinct authority/state/confidence basis helper with
module references.
Fixes#8296
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(javascript): name the retained parent when a selected view overflows
Review follow-up for the feature-trace views.
- Share one delivery owner between trace_application_feature and
compare_source_to_bundle: stage the selected view against the
completed parent, keep the established contract that a cancelled
request commits no session facts, then commit sources and the parent.
- A selected view that exceeds the response budget now references the
retained parent and offers the same selection at half its limit,
instead of naming the view record, which cannot be a view parent.
- Cover two native handoffs paged one at a time, the oversized selected
page, and cancellation after the trace completes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(javascript): bind feature-trace pages to their collection rows
Advertise each feature-trace page with exactly its own row schema, so a
paths page cannot validate with node, edge, seed-match or handoff rows.
The native-trace view in #8533 received the same review finding; the two
sibling views now share one strict page contract.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(javascript): mix valid rows from another collection into a trace page
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(native): find procedures by the secondary names exact lookup accepts
search_procedures matched only each procedure's primary name, while exact
lookup also accepts Kuna inventory aliases (versioned-symbol and generated
sub_ names) and Ghidra symbols at an exact function entry. A search for a
name procedure_address resolves returned nothing, and trace_feature
silently missed the procedure.
Both providers now match the same names their exact lookup accepts. A
procedure still appears once under its primary name; the optional
matched_aliases field lists the secondary names that matched, so aliases
never become procedures of their own and list_procedures is unchanged.
The shared domain helper owns that row rule for Kuna and for the Ghidra
host-side Unicode folding pass. The Ghidra analysis profile records the
new search semantics so older snapshots cannot replay alias-blind results.
Fixes#8330
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(ghidra): match leaf spellings of procedure names in search
procedure_address accepts each entry symbol's leaf or namespace-qualified
spelling, but procedure search tested only qualified names, so a regex
such as ^audit_alias$ missed a secondary symbol ns::audit_alias (and ^same$
missed a primary alpha::same) that exact lookup selects.
Lookup and search now share one bridge definition of the names that select
a procedure, so they cannot drift. Matching secondary symbols are still
reported by their qualified names; because a leaf ends its qualified
spelling, the host's Unicode literal pass stays exact over those names.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(session): clear provider-inferred unavailability after recovery
A provider adapter or process failure marked every routed operation of
that provider unavailable, but a later success cleared only the operation
that succeeded. Sibling operations, and composed tools built on them, kept
advertising the stale failure until each was retried individually.
Move runtime availability into one owner that records whether each entry
was observed for the operation or inferred from another operation's
provider-wide failure. A success clears its own failure and that provider's
inferred entries while keeping failures observed on other operations or
providers and declared refusals. Outcomes are ordered by dispatch to the
provider client, so an older in-flight success no longer erases a newer
failure while a request queued behind a failure still clears it.
Fixes#8404
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(session): keep newer successes when older failures complete last
Runtime availability kept one merged entry per operation and forgot
successful dispatches. An older provider-wide failure that completed after
a newer success re-inferred unavailability for siblings the newer success
had already proved working, and a superseded refusal could survive its own
later success.
Record only the newest failure and newest success dispatch per operation
and per provider, and project unavailability from them: an operation is
unavailable while its own newest failure is newer than its own newest
success, or, as inferred, while its provider's newest provider-wide failure
is newer than any success from that provider. The projection no longer
depends on reply completion order.
The recovery tests now bound held-reply acknowledgements, release and join
every held call in finally, and compare remediation with the owning
formatters instead of copying their wording.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(enhanced): stop call graph traversals at unconfirmed provider cleanup
A relationship read whose provider could not confirm cleanup of resources it
owns was projected into a graph node or call-path failure, the traversal kept
dispatching queued reads, and get_call_graph / trace_call_path returned
success with the cleanup_incomplete status buried inside the result.
Both traversals now read relationships through one shared owner,
readCallRelationships, which classifies a cancelled read or a read with
unconfirmed owned cleanup as terminal. On such a cleanup stop the workflow
returns the provider's own error as the outcome, so its cleanup owner, retry
binding and live resolution stay intact, and keeps the work admitted before
the stop as the workflow's normal result shape with a caveat naming the
stopped procedure and the unexpanded count. The composed-workflow layer
turns that admitted result into partial workflow Evidence on the failure,
and the MCP adapter retains it through the shared partial-error delivery.
Ordinary read failures still produce successful partial results.
Fixes#8369
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(enhanced): retain every carried partial Evidence on enhanced failures
Enhanced-tool failures used the shared partial-error delivery only when this
PR's workflow partial was attached. A failing read that already carried its
own partial Evidence, and analyze_function dossier failures, were projected
inline and never recorded with the session writer, so readback and bundle
export could not recover them. Every enhanced execution failure now goes
through one delivery owner that uses toPartialErrorToolResult with the
session writer; it still falls back to inline when no writer or no valid
Evidence exists.
Tests compare the traversal caveat against its owning formatter instead of
copying the sentence.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Scheduled deep static checks on main fail knip because the
DarwinHelperCache type alias added in #8318 has no consumer. Remove
the alias; callers already use createDarwinHelperCache's inferred
return type.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: retain address context cleanup failures
* fix!: preserve address context input errors
BREAKING CHANGE: Strict clients that cached the previous full inspect_address_context output schema must refresh tools/list and accept the typed error field on unavailable facets.
* test: use session contract for address context
Reject native view sources whose predicate disagrees with the selected operation. Use the original predicate for existing Evidence; do not alter a record without recomputing its identity.
Resume refused every identity mismatch with one generic message and
generic input-schema recovery. Report the differing target, profile,
layout, or provider facts with the journal and current values so callers
can choose the matching provider/profile or a new output directory.
Fixes#8349
Bind typed service creation, observed availability and retryable cleanup in one composition owner. Reuse selected CLI/MCP factories and canonical contract groups without copying provider operation inventories.
Closes#8429. Refs #740.
Validated paired real application workflows, actual local Codex CLI Terra/high use, complete Evidence and Unknown checkpoint parity, compiler-backed import boundaries, SDK schemas and final real mixed routing. Automated review findings were fixed at their producing owners. Existing exact-base CodeQL alerts are documented on the PR under the owner-authorized unrelated-check exception; no global alerts or security rules were changed.
Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
Separate engine cleanup, investigation reset and terminal disposal in the shared application owner. Keep stable Evidence IDs and full Unknown revision history through engine close and target changes; serialize reset with all record writers and cleanup owners, optionally acknowledging a complete checkpoint before clearing. Bound retained representation without eviction.
Validation: 43 focused files / 533 distinct tests, source/type/lint/schema/documentation checks, nine genuine source cases across eight families, real Ghidra session identity and cleanup, and nine actual local Codex CLI terra/high runs with independent full-result and checkpoint audits. See docs/verification/investigation-lifecycle.md for measurements, partial attempts and verification limits.
Fixes#8347.
BREAKING CHANGE: close_binary retains investigation records. Use reset_investigation to clear them. Cross-process recovery requires an explicitly acknowledged Evidence bundle checkpoint.
Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
The DOS verifier compared a fresh CLI session with an MCP session that
its address-boundary helper had renamed and restored, so the entry's
symbol source differed (imported vs user_defined). Compare the CLI with
the pre-edit MCP window, then assert the restoration changes only the
symbol provenance.
Fixes#8189
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(native): label binary overview counts with their inventory scope
`binary_overview` described its counts as "exhaustive", and its result was
a pair of bare scalars. An agent could read them as a discovery of every
function and string in the file, or as comparable across providers. They
are exact totals of the rows each provider's inventories return.
The shared `projectBinaryOverview` now adds one provider-neutral
`inventory_coverage` record, so Ghidra's direct summary and the
listing fallback carry the same record:
`{ scope: "provider_inventory", discovery_coverage: "unknown" }`.
The record reuses the `discovery_coverage` vocabulary that
`list_procedures({detail:"summary"})` already reports, now owned by one
domain schema. The tool description states the scope and that counts are
not comparable across providers. Counts, segment omission semantics and
limitations are unchanged.
Fixes#8287
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): label overviews in the workflow so older snapshots stay valid
Analysis snapshots replay the projected inventory summary. A snapshot saved
before inventory_coverage existed would then fail the overview output schema.
The record describes what overview counts mean, whoever produced them, so
the binary_overview workflow now adds it on both its provider-summary and
listing paths. Provider values and projectBinaryOverview are unchanged, and
the composed overview's linked atomic record stays as the provider
reported it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): refresh composed overviews from before inventory_coverage
Bump the binary_overview workflow revision to 5, so snapshot and CLI replay
recompute a revision-4 composed overview from its atomic record. Replay
would otherwise return the cached overview without its count labels. The
revision-4 case joins the existing refresh scenario. The atomic Evidence
is not rewritten.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(kuna): omit Mach-O data symbols from the procedure inventory
Kuna's `functions --json` registers every Mach-O export as a function
entry, so globals in `__DATA,__data`, constants in `__TEXT,__const` and
`__mh_execute_header` were listed, counted and decompiled as procedures.
The Kuna session now classifies each inventory entry with the target's own
section table, read by the shared `inspect_macho` load-command reader from
the header bytes kept while its private copy is hashed. Entries in sections
that declare no instructions, or in the header and load commands, are data:
listings and searches omit them and report per-region counts, and
procedure operations refuse them by name or address while `address_name`
and `xrefs` still accept them. Instruction sections, symbol stubs and
symbol-pointer import slots are kept; addresses outside every section stay
with an explicit limitation, an unreadable section table keeps the whole
inventory unclassified, and ELF inventories are unchanged. Composed
workflows such as `decompile_all` and `binary_overview` follow the listing.
Fixes#8168
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(kuna): keep an entry's classification caveat on direct answers
Resolution now returns the selected entry's Mach-O placement and the
caveat it leaves open, built by the same constructor as the listings: the
one-entry unplaced wording, or the unreadable section table's reason.
Pseudocode, assembly, analyze_function (including the dossier's own
limitations), procedure_address, call queries by name and address_name
carry it; code and import-slot entries carry none.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(mcp): name the close_binary retry for retained cleanup
A close_binary that retired its target but could not confirm cleanup
returned cleanup_incomplete with retryable: false and only the generic
"Review the reported local resources" remediation, although the session
keeps every failed owner and the next close_binary on the same connection
retries them. Agents could abandon the connection that owns the only retry.
The MCP close tool now passes its retry operation to the session. The
session binds that typed AnalysisCleanupRetry to a failed retirement only
while it retains the cleanup for its next close; the close tool binds it to
process-capture failures its own scope retains and to compositions whose
session part was offered. Projection then reports
recovery.kind=retry_cleanup and appends a conditional instruction: keep the
connection, let the condition settle or resolve it, call close_binary
without snapshot_path and check the result. The hint disappears once that
cleanup resolves. retryable keeps describing the failed operation.
One-shot CLI closes, open_binary, analysis tools and failed snapshot saves
(which keep the target open) offer no retry. Cleanup composition copies the
owner's remediation without a retry offer, so the text appears once.
Fixes#8211
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test: derive close retry guidance from its owning formatters
The new close_binary retry assertions froze ProviderCleanupError's
explanatory sentence, so a harmless wording edit would break them. The MCP
test now compares delivered remediation with analysisErrorRemediationAction
applied to the delivered typed error plus cleanupRetryRemediation. The
domain tests use the same formatters, check that unbound and released errors
carry no retry text, and check that a bound composition adds the offer once.
Recovery fields, cleanup state and retry behavior keep separate assertions.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* perf(process): reuse a verified Darwin ownership helper across processes
Every REA process on macOS compiled the Swift ownership helper into a fresh
temporary root and then paid macOS's first-launch assessment of that new
executable: 408 ms + 325 ms of a 1.4 s `rea inspect-macho /bin/ls`.
With the default xcrun compiler and module cache, the reader now keeps one
content-addressed copy under ~/Library/Caches/rea/process-helper/<key>/.
The key covers the helper sources, toolchain selection, architecture and
Darwin release. A copy is executed only after the cache root, entry and
files are verified as current-user owned, not group- or world-writable,
not links, and digest-matched to the entry manifest. Malformed entries
owned by the user are removed and rebuilt; linked or foreign entries are
left untouched and the per-process compile runs as before. Any cache
failure falls back to that compile, so the cache never fails an ownership
operation.
A miss compiles exactly as before, then publishes a copy through an atomic
rename, keeping the first concurrent publisher, and runs the shared copy.
That way its first-launch assessment is paid once. Per-run roots keep their
existing cleanup; published entries are never removed by close or exit.
An explicit REA_DARWIN_SWIFTC_PATH or REA_DARWIN_MODULE_CACHE_PATH keeps the
per-process compile and writes nothing to the cache. `rea uninstall
--purge-data` removes the namespace.
Fixes#2483
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(process): harden the shared Darwin helper cache
Address review findings on the shared ownership-helper cache:
- Verify every real parent of the cache root: each must belong to the user
or root and not be writable by others (sticky root-owned directories
excepted), so no other user can rename a verified entry. Execute through
the real path.
- Open manifest and helper through the owned non-blocking regular-file
reader, so a FIFO cannot block preparation, and retain handles whose close
fails.
- Key entries on the swiftc and SDK that xcrun itself resolves, including
their real paths, sizes and modification times. DEVELOPER_DIR, TOOLCHAINS,
xcode-select and in-place updates therefore select new entries.
- Never remove or replace an invalid or published entry. Removal could race
another process that has verified or is running that path, so such an
entry falls back to the per-process compile.
- Give the cache a close() that retries retained staging removals and
handle closes and reports ProviderCleanupError while any remain. The
reader closes it with its per-run root.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(process): keep cache lookups owned and fall back from unlaunchable helpers
Address the second round of review on the shared ownership-helper cache:
- Pass the preparation signal to both xcrun toolchain lookups, and wait for
every started lookup before reporting the first failure. Cancellation and
close then stop a stalled lookup instead of waiting out its deadline.
A failed or cancelled key computation is retried by the next lookup.
- When the verified shared copy cannot launch (EACCES, EPERM, ENOEXEC,
ENOENT or an unrequested SIGKILL, for example from a noexec home volume),
the reader stops using the cache. It reuses its own compiled helper or
compiles one, and retries the read once.
- Anchor the cache's returned cleanup errors as cleanupRoot does, so every
error returned while cleanup was pending resolves after a retry succeeds.
- Build the cache path with appendCallerPath, so a home with symlinks and
`..` keeps the caller's meaning instead of being normalized lexically.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(process): keep concurrent fallbacks and dual cleanup failures
Each helper read now records before awaiting whether it launched the shared
copy, so a concurrent read's fallback cannot hide its own. When the per-run
root and the cache's retained resources both fail one close, the returned
error composes both. It stays incomplete until both owners recover.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(process): refuse shared helpers that an ACL lets others write
macOS ACLs can grant writes that the POSIX mode bits do not show. On
Darwin, lookup and publication list the real ancestors, the entry and its
files with `ls -lde`, and refuse sharing when any ACL entry allows a
write-class right. The default `group:everyone deny delete` entries on
macOS home folders are unaffected. The unlaunchable-helper test now removes
the execute bit from a verified copy, which refuses launch on both Linux
and macOS.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(guidance): distinguish focused matches from broad parent sections
Guidance search treated a parent section, including every descendant, as one
match. Terms scattered across unrelated subsections made a 49K-character root
the only result for "switch provider annotations", and every ancestor of a
focused hit was listed alongside it.
The section parser now records each section's own text (heading and body
before its first subsection) and its parent. Search keeps the same admission
rule but classifies each match by match_scope: "section" when every term is
in the section's own text, "subsections" when the terms are only spread
across subsections. Ancestors whose terms all sit inside one subsection are
no longer listed, section matches rank first, and subsections matches carry
own_text_terms plus bounded greedy pointers to the subsections holding the
remaining terms. Every match reports content_length so callers can judge a
read before making it. Read still returns the complete section.
Own text is tokenized once per request instead of each expanded subtree.
Fixes#8290
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(guidance): require the scattered-match checks to run
Count the subsections matches the real-bundle test inspects and fail when
none were checked, and document that a parent whose own text holds every
term stays listed beside its matching subsection.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
swiftc rejects assembly sources as inputs, so verify:ghidra:swift failed
during fixture preparation before any provider check. Assemble both
assembly fixtures to objects with xcrun clang and link only objects.
Fixes#8188
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(decompile): keep provider no-pseudocode facts on batch items
A batch_decompile item whose provider answered without pseudocode was
reported as unreadable output with doctor guidance, and the provider's
reason, such as a Kuna export failure for that entry, survived only in the
batch-wide limitations. The workflow read only the observation's result.
Read each item's complete observation. A null answer is now a
capability_unavailable item naming the provider, and every item answered
without usable text keeps that observation's own limitations, so a
batch-only Evidence export retains the association. Empty or non-text
pseudocode stays unreadable output; typed provider failures and successful
items are unchanged, and the batch Evidence still aggregates every caveat.
The batch workflow revision changes so earlier snapshots are not replayed
with the previous item semantics.
Fixes#8226
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(decompile): match null-pseudocode guidance to reported caveats
A null answer without caveats told the caller to read item limitations
that the batch omits when the list is empty. The guidance now depends on
whether that observation reported caveats, and says the provider gave no
further explanation when it did not.
The batch tests now select their contracts by name from the enhanced and
session families instead of the full catalog.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
trace_native_values set one truncated flag for two different facts: the
returned page omits retained nodes, and a budget stopped or narrowed the
traversal. A last page of a complete trace and a node- or edge-capped trace
both reported truncated: true with next_offset null and no unknown naming the
cap, so a caller could not tell whether to follow pages, raise a budget, or
treat the examined graph as exhausted.
Results now carry page_truncated for page omission and a traversal object
that is complete, or truncated with the observed reasons in schema order:
max_depth, max_functions, max_call_sites, max_nodes, max_edges,
node_payload_bytes, provider_pcode_truncated and ambiguous_call_candidates.
truncated keeps its previous meaning as the union of both.
A new budget owner admits nodes and edges and records every cause together
with a located unknown, only after a budget rejects work; an exactly filled
budget is no longer truncation. Depth and ambiguous-candidate limits are
resolved after traversal, so a destination analyzed or queued through a
shallower path is no longer reported as depth-limited. Once an edge is
rejected, remaining call sites in that procedure are not resolved.
Fixes#8283
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(ghidra): report unbacked file-offset addresses without schema repair
address_to_file_offset projected a well-formed address in uninitialized
or unmapped Ghidra memory as generic invalid input: the headline said the
arguments were invalid and recovery pointed at the input schema, while the
real reason survived only in a nested issue.
The bridge now gives each outcome its own code (address_unmapped,
address_uninitialized, file_mapping_absent, file_mapping_ambiguous,
file_offset_unrepresentable) instead of folding them into invalid_request.
The Ghidra projection keeps the bridge reason as the headline, points to
inspect_native_load_image, records the caller's exact address and the
file_mapping outcome, and scopes diagnostics to the request.
AnalysisInputError gains conformsToInputContract so a conforming request
omits recovery.kind=inspect_input_contract. Malformed addresses and other
bridge refusals keep their existing projection.
Fixes#8244
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(ghidra): match fixture block ends to bridge conventions
The fake bridge treated .text's exclusive list_segments end (0x1100) as an
inclusive bound, so address_to_file_offset mapped an address outside the
block. Give the fixture's blocks one owner (start and size) and derive each
reply from the production bridge convention: exclusive list_segments ends,
inclusive byte-search coverage, half-open containment for file offsets.
Add an MCP boundary test that reads the block edges from list_segments and
search_bytes, maps the last file-backed byte and refuses the first address
past each block with its outcome code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(ghidra): decide batch names independently of request order
The Ghidra bridge applied set_addresses_names in JSON member order, each
address in its own transaction, so a reordered but equivalent map could give
a shared name, or a name released by another selected address, to a
different address.
The bridge now resolves every destination (namespace and leaf name) against
the names held before the batch. Addresses that request the same destination,
or a name that another selected address holds, are rejected without applying;
the remaining edits are independent and apply as before. Both naming
operations report each failure's canonical address, reason and related
addresses. The adapter projects the bridge reply to the unchanged boolean
results and keeps the reply as raw_result only when an address failed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(ghidra): report each contested batch name once
A batch conflict listed every other requester on each failure, so k
addresses competing for one name produced k(k-1) related addresses, and
the adapter retained the whole reply, booleans included, as raw_result.
The bridge now reports each contested destination once in a conflicts
table with its namespace, name, requesting and holding addresses, and
each conflict failure refers to it by index. Conflicts are ordered by
their first requester, so indexes do not depend on member order. Single
and batch naming share one planner and reply layout. The adapter
validates the table in linear time and keeps only failures and conflicts
in raw_result.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Declare logical Evidence sources once and resolve exact retained inputs through
shared application workflows across 20 composition tools. Preserve domain-owned
authentication, source closure, caller choices, coverage and resource admission.
Validation includes public CLI/MCP regressions, local Codex terra/high trials
and fresh-import real-artifact workflows. Measurements, failed attempts and
coverage limits are recorded in docs/verification/evidence-input-composition-2026-10-31.md.
Fixes#8293.
Fixes#8281.
Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
* feat(artifacts): inventory and extract UF2 and DfuSe regions
open_binary refused UF2 and DfuSe firmware containers as an unsupported
format, so inspect_artifact and extract_artifact could not show which
bytes a drag-and-drop or DFU update writes at which addresses.
UF2 is recognized by a complete first block with all three magic
numbers. Valid main-flash payloads are joined into address-contiguous
regions per familyID, named family-0x<id>/0x<start>-0x<end>.bin, and
known IDs are named from a small table copied from Microsoft's
uf2families.json at a pinned commit. Numbering is counted per familyID
and block count: absent, repeated, out-of-order and out-of-range block
numbers are reported, and absent blocks leave coverage unknown.
Not-main-flash blocks are excluded and reported; file-container blocks
are reported and leave coverage unknown; invalid blocks and a partial
final block are excluded with their position. A rewrite of one address
with different bytes fails closed, naming both block spans.
DfuSe is recognized only by its prefix together with a DFU 1.1a suffix.
Each element becomes one region at its file range with its target's
alternate setting and name. Structural faults fail closed with a byte
offset; overlapping elements stay separate and are reported. A suffix
CRC mismatch follows integrity_policy: fail refuses with a dfu-crc32
integrity error, and record-and-continue records a root contradiction
that extraction also carries.
HEX, UF2 and DfuSe share one retained-region reader and one run
merger, so the HEX decoder and reader lose their private copies.
* docs(artifacts): document UF2 and DfuSe images and their esptool lane
Describe recognition, region names, metadata, numbering, failures, the
DfuSe suffix CRC under integrity_policy, and limits, and link the page
from the CLI and firmware guides and the agent skill reference.
npm run verify:uf2 runs esptool merge-bin --format uf2 on generated
inputs at known addresses, with and without MD5, with 256-byte
payloads, and on a concatenation of two chips' outputs. Inspection and
extraction through the built CLI must return every input at its address
under the familyID esptool itself reports.
* fix(artifacts): accept both DfuSe image size conventions
UM0391 calls DFUImageSize the file length, and dfu-util's dfuse-pack.py
writes that value, while ST's DfuFileMgr, MicroPython's dfu.py and pydfu
leave the 16-byte suffix out. The decoder accepted only the second form,
so it refused dfuse-pack.py output as malformed.
Accept either value and keep the declared one in image_size; any other
value still fails with the suffix offset and the file length. The tests
pin both producers' output for one image, CRC included, from their
build() functions transcribed and run under Python 3.
* fix(artifacts): keep reused UF2 block numbers from hiding missing blocks
esptool numbers every merged input from 0, so two inputs with the same
block count share one numbering group. A block lost from one input left
its number present in the other, and the file reported complete
coverage. Each block number now records the target addresses it was
seen at: equal address counts are complete copies, a resend to the same
address is one block, and unequal counts make coverage unknown with a
limitation and ambiguous_numbering_group_count. sequence_count becomes
numbering_group_count, and region block positions become
first_file_block and last_file_block.
Review follow-ups in the same change:
- compare_artifacts compares a DfuSe element's alternate setting and
target name, so bytes moved to another device memory are changed;
- the shared reader keeps no region bytes outside its map after close;
- one domain type replaces four identical decoder failure types;
- the UF2 decoder returns a typed limit instead of throwing on bytes
beyond its admitted size, and caps retained regions, not runs;
- DfuSe caps elements with bytes only, names empty elements and
element-less targets, drops the constant bVersion and bcdDFU fields,
and says whether the CRC matches when DFUImageSize fits neither
convention;
- classification evidence carries a suffix only when the probe saw the
end, and root classification reads the tail only for a DfuSe prefix;
- the UF2 domain region owns family_name, and the root records the
family registry revision; the table's MIT notice is preserved;
- docs pin the producer revisions (dfu-util ee6b393d and f9a537c9,
MicroPython cb99ca98), give the MD5 structure's real meaning, and
describe container contradictions in the MCP contract guide;
- tests and the esptool lane add the equal-count inputs and a block
lost from one of them, accepted boundaries beside each refusal, a
DfuSe root larger than the classification prefix, and CLI refusals.
* feat(artifacts): name the UF2 families that LibreTiny builds for
LibreTiny writes UF2 for Realtek, Beken and Bouffalo Lab modules. The
Microsoft registry revision REA already cites (cf3bd7f9) lists eight of
those families: RTL8710A, RTL8710B, RTL8720C, RTL8720D, BK7231U, BK7231N,
BK7251 and BL602. Their regions now carry those names. LN882H, which
that revision omits, keeps its familyID with no name.
* fix(artifacts): count DfuSe declarations without bytes instead of listing them
Empty DfuSe elements have no entry, so DFUSE_MAX_ELEMENTS does not cap
them, and the target walk kept a description string for each one. At 8
bytes per element, a file under the 128 MiB ceiling can declare about
16.7 million of them. The walk now counts every declaration without bytes
and builds only the first NAMED_ELEMENTS descriptions, which is all that
the limitation shows. The limitation text and empty_element_count do not
change. The guide now says that both DfuSe limitations name the first four.
On this host, decoding 4,000,000 empty elements (a 32 MB file) ran out of
memory under a 128 MB V8 heap before this change, and it peaked at 1.59 GB
RSS with the default heap. It now decodes in 114 ms with a 220 MB peak.
---------
Co-authored-by: morluto <[email protected]>
* feat(artifacts): inspect classic iNES container regions
* fix(artifacts): preserve iNES source disappearance as integrity failure
* fix(artifacts): revalidate iNES source before parsing header
* fix(artifacts): validate admitted roots before classification
* fix(artifacts): normalize source loss during root hashing
* fix(artifacts): bind iNES extraction admission to inventory
* test(artifacts): clarify non-executing inspection wording
* fix: preserve binary plist detection with extended magic probe
* test(browser): wait for popup document loads before capture
* fix(artifacts): honor cancellation across source validation
* fix(artifacts): prefer artifact magic over implicit database suffix
* fix(artifacts): select extraction readers from scanned format
* fix(artifacts): complete prefix reads and share ZIP format admission
* fix(browser): budget scenario cleanup for content drain and shutdown
* test(artifacts): admit valid universal inputs before routing
* test(artifacts): verify iNES workflows on macOS
* test(ci): own iNES macOS coverage and portable fixture paths
* fix: preserve cleanup retries and avoid duplicate inventory parsing
* fix(contracts): share ZIP target formats across MCP lifecycle schemas
* fix(target): preserve typed cancellation during descriptor admission
* refactor(artifacts): use JSON-safe inventory producer output directly
* test(artifacts): share file read setup and satisfy CI checks
* fix(artifacts): classify changed admission as integrity failure
* ci: run Mach-O summary delivery coverage on macOS
* test(javascript): align parameter provenance with main contract
* test(browser): await discoverable fixture pages before runtime verification
* fix(artifacts): pass target metadata to format detection
* fix(artifacts): bind extraction to opened target
* fix: repair the static-check failures on the default branch (#2895)
* fix: repair the static-check failures on the default branch
The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.
- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
`src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
`tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
union in `src/application/SupportedClients.ts`, and an unchecked index in
`tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
inferred as `string[][]`.
Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.
Validation: `npm run check:fast` and `npm run format:check` pass.
* fix(setup): keep Devin on the JSON configuration dialect
---------
Co-authored-by: 吴佳浩 <[email protected]>
Co-authored-by: morluto <[email protected]>
(cherry picked from commit df3d96da80)
* fix(artifacts): bind session inventories to target identity
* test(browser): bound fixture readiness acknowledgements
---------
Co-authored-by: 64579525 <[email protected]>
Co-authored-by: 吴佳浩Alben <[email protected]>
Co-authored-by: 吴佳浩 <[email protected]>
* feat(ghidra): record the RISC-V float-ABI calling-convention decision
Decision D3 (#7477), provisional: admit ELF32 soft-float (ilp32) RISC-V
images through a lane that keeps the pinned RISCV:LE:32:default language and
corrects the calling convention in the bridge.
This commit is the contract only; admission is unchanged.
- The domain owns the e_flags float-ABI vocabulary and parses
.riscv.attributes and Tag_RISCV_arch into a declared, absent or
unavailable ISA declaration.
- The Ghidra profile owns the lane table (class and float ABI), lane ids
built from fields, the ilp32-prototype-v1 policy and its __rea_ilp32
prototype, and the extensions Ghidra 12.1.4 decodes with p-code.
- The session handshake accepts the bridge's calling-convention report.
- docs/ghidra-riscv.md keeps the decision record and per-ABI policies for
ilp32 and ilp32f; a test keeps it in step with the tables.
- tests/conformance/c/riscv-float-abi.c is the source the policies were
measured on.
* feat(ghidra): admit RISC-V ilp32 ELF with a derived calling convention
ESP32-C2, C3, C5, C6, C61, H2 and H21 builds are ELF32 soft-float (ilp32)
images, which the RISC-V lane refused at its double-float check. Ghidra
12.1.4's generic RV32 compiler specification models ilp32d, so its stock
prototype would read float and double arguments from fa0-fa7.
- Target resolution reads the one SHT_RISCV_ATTRIBUTES section through the
same bounded ELF declaration reader as range validation, and records
Tag_RISCV_arch as declared, absent or unavailable.
- Admission selects a lane from the class and float-ABI table. The soft-float
lane also requires an RV32I Tag_RISCV_arch that declares only extensions
Ghidra decodes with p-code; the refusal names any others (Zcb, Zcmp, Zcmt,
Zb*, xesp*).
- Soft-float profiles commit float_abi, arch, riscv_calling_convention
ilp32-prototype-v1 and the lane elf32-exec-le-soft-float-generic-v1.
Double-float profiles are unchanged.
- ReaGhidraRiscvCallingConvention.java runs after analysis. It derives
__rea_ilp32 from the default prototype without floating-point entries and
rules, with fs0-fs11 killed by calls, self-checks its storage and assigns
it to every non-thunk function, external function and function
definition. Its report is written last.
- The provider client requires that report exactly when the profile commits
the policy, before any operation, and keeps it as session limitations and
caller-project history.
ELF32 single-float (ilp32f), quad-float and ELF64 soft- or single-float
images stay refused, now with a reason built from the lane table.
* test(ghidra): verify RISC-V ilp32 against real Ghidra
verify:ghidra:riscv now also builds tests/conformance/c/riscv-float-abi.c
with ESP32-C3's -march=rv32imc_zicsr_zifencei -mabi=ilp32, typed with DWARF
and linked again with --strip-all. Every allocated section of the stripped
image must keep its address and bytes.
- riscv-checks.mjs holds the ilp32 storage of each fixture function together
with the words the compiler's call site writes there. An LLVM objdump
reading of the caller must find every word at its stated location: an
unaligned named double, the a7/stack split, two floats in an integer pair,
a large structure by reference, aligned variadic pairs and an indirect
call.
- Through CLI and stdio MCP, the soft-float builds must report the soft-float
lane and ilp32-prototype-v1, __rea_ilp32 with exactly that storage (one
integer word per location when stripped), caller pseudocode that passes the
source constants and names no floating-point register, the prototype
outcome in retained Evidence, and decoded fence.i and csrr.
- The RV32 double-float image relabelled as soft-float, refused before, is
admitted through the new lane. RV64 soft-float, single-float and unsupported
bits are refused with their reasons.
- riscv-checks.test.ts runs the checks offline against recorded LLVM and
Ghidra output, including Ghidra's stock ilp32d prototypes as a negative
control.
* fix(ghidra): pass RISC-V ilp32 values over 8 bytes by reference
The psABI passes any value wider than 2 x XLEN by reference, scalars
included. __rea_ilp32 converted only structures and unions over 8 bytes to
pointers, so a long double (16 bytes) was joined into a1-a4. Measured on
Ghidra 12.1.4, rea_riscv_abi_quad(long double value, int sel) read value
from a4:4,a3:4,a2:4,a1:4 and sel from a5, where clang passes a pointer in
a1 and sel in a2.
- ReaGhidraRiscvCallingConvention.java now has one derivation driven by a
per-policy row (floating-point entry size, preserved floating-point
registers). Every policy inserts a rule before the input list's final
join that converts any value of 9 bytes or more to a pointer; a return
that size already becomes a caller buffer. The self-check requires a
16-byte value to pass by reference.
- The fixture gains rea_riscv_abi_quad. The call-site oracle now checks the
words the by-reference pointers point at, for the large structure and the
long double, and the recorded data comes from an LLVM 19 build.
- docs/ghidra-riscv.md: the decision record and the ilp32 policy row.
ilp32-prototype-v1 keeps its id: no release carries it yet.
* test(ghidra): check RISC-V lane limitations by their facts
The limitation tests froze a full explanatory sentence, the note count and
array positions, so a harmless rewording failed them: the pattern Greptile
flagged on #7486. Assert the facts the notes must carry instead: the pinned
language, the static-linkage boundary, one note naming the declared ABI,
the ABI the pinned language models and the prototype REA applies, and the
declared ISA.
* test(ghidra): check return storage in stripped RISC-V ilp32 builds
The stripped branch of assertRiscvIlp32Storage compared parameter storage
only, so an inferred return that lost the high word of a register pair,
such as rea_riscv_abi_wide at a0:4 instead of a1:4,a0:4, still passed.
Stripped returns must now use the typed build's storage, or none when the
result goes through caller memory. Recovered type names are still not
compared: a test accepts other type names and refuses the dropped high word.
* docs(ghidra): record decision D3 as decided in #7477
* feat(artifacts): inventory and extract ESP32 firmware images
ESP32-family app and bootloader images (magic 0xE9) had no reader, so
open_binary refused OTA downloads, app partitions and bootloaders.
Add the esp-image container format behind inspect_artifact and
extract_artifact. A pure decoder reads each byte once and keeps only
fixed-size fields. Derived facts cover the chip, from a table cited to
ESP-IDF v6.1; segment classification as ESP-IDF's should_map and
should_load; checksum and appended SHA-256 results; app and bootloader
descriptors; Secure Boot v2 signature blocks, observed and unverified;
and undescribed trailing byte ranges. Truncation and undescribed bytes
make coverage unknown. ESP8266 images are refused with a specific
reason, and ciphertext stays unrecognized.
Add the format to every target, Evidence, snapshot, live-summary and
artifact vocabulary, document it, and add an optional verify:esp-images
lane that compares esptool-built images with esptool image-info and
espsecure signature-info-v2.
Refs #3615
* test(artifacts): run the ESP image lane on esptool v5.6.0
The lane compared descriptor fields only when esptool image-info printed
one, so a descriptor that REA reports and esptool does not print passed
without comparison. Compare the descriptor kind in both directions.
Record that the lane passed for all 13 images with esptool and espsecure
v5.6.0, the version the layout facts cite, as well as with 4.12.0.
* refactor(artifacts): define ESP image layout sizes once
The decoder and the derived facts each defined the segment-header,
appended-digest and flash-sector sizes, the erased-byte scan and the
offset formatter. Define them once with the other fixed-layout structures
and import them in both places. The signature slot count is the one
constant that drives the slot scan.
Correct the observedEnd comment: it is the end of the last chunk read.
* fix(artifacts): report ESP-IDF's segment length limit by chip
ESP-IDF's verify_segment_header rejects a segment of 16 MiB or more
unless the chip maps flash through 32-bit cache addresses
(SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP: ESP32-S3, P4, C5, C61, H4 and
S31 in v6.1). Record that capability in the chip table and add a
limitation for such segments; unknown chips get none.
`loaded` repeats should_load. It does not mean the bootloader accepts the
segment: verify_load_addresses still rejects one outside the chip's RAM,
which REA does not model. Say so in the docs, the skill reference and the
inventory limitation.
* test(artifacts): check ESP lane tools first and verify extraction
The lane started work before checking its tools, so a missing RISC-V
target, ld.lld, esptool or espsecure surfaced as a raw spawn or command
error. Check each one before building and name the variable to set. Warn
when esptool is not v5.6.0, the release the layout facts cite.
Assert each segment's class for the ESP32-C3 app, its variants and the
bootloader, as the Xtensa image already did. Extract the app and the
signed image through the CLI and compare every file with the bytes at
esptool's segment offsets or in the appended signature sector; the
source's SHA-256 must be unchanged and equal the reported root_sha256.
* docs(artifacts): state what the ESP image lane does not establish
The Verification section listed the lane's checks but not its gaps: only
ESP32-C3 and ESP32 images come from a real producer, no real project
build, OTA download or flash dump is inspected, no signature is verified,
and the lane does not run in CI.
Record the timing of the final lane run on esptool v5.6.0.
* fix(artifacts): compare ESP segment declarations
Segment entries hash only their data, so compare_artifacts reported a
segment that moved to another load address with the same bytes as
unchanged; only the image itself changed. Compare each segment's
load_addr and data_len as its declaration, as Node SEA and PyInstaller
members are compared, and count a changed declaration as a metadata
change, also when content is unavailable.
Header offsets are layout and the classification is derived, so neither
is compared.
* test(artifacts): run ESP lane commands as owned process trees
The lane ran its tools through execFile. A timeout killed only the direct
child, so a linker or assembler that Clang or GCC had started could keep
running while the lane removed its workspace. Run every command through
createVerifierCommands, check the tools inside withVerifierCleanup, and
remove the workspace only after commands.close() has stopped every tree;
a failed close keeps the workspace.
Only a nonzero exit now counts as a refusal or an expected esptool
failure; timeouts and spawn failures propagate. Supervising each command
roughly doubles the lane's run time.
* test(artifacts): cancel ESP decoding after its first decoded chunk
The cancellation test requested cancel or close on setImmediate, which
can land while the file is still being opened and checked, so it never
showed that decoding had started. It also passed against a reader that
ignored both requests while decoding. Acknowledge the decoder's first
chunk, then cancel or close, join the operation, and assert that no
later chunk reaches the decoder and that the reader ends closed.
* docs(artifacts): record which ESP metadata comparison ignores
Segments are compared by their declaration. The image's and the
signature blocks' format_metadata are not: every field comes from their
own bytes, which content comparison already covers, and a block's
image_digest_matches follows the image's content.
* feat(artifacts): inventory and extract ESP flash layouts
A whole-flash dump of an ESP32-family chip was read as its bootloader
image, with the partition table, apps and data left as undescribed bytes,
or refused when it began with erased flash. Read it as an esp-flash-image
container instead.
The partition table is decoded as esp_partition_table_verify accepts it
and is found at the caller's partition_table_offset, at the default
0x8000, or by scanning 0x1000-aligned sectors for a table whose MD5
verifies, which the inventory reports as derived. The bootloader is
located at its chip's ROM bootloader offset. Each partition is an entry
with its declaration in flat format_metadata; the bootloader and app
partitions carry ESP image facts from the existing streaming decoder; the
otadata records are reported with a selection derived without rollback or
anti-rollback, and the running partition stays unknown. One unreadable
partition does not hide the others, and extraction writes every partition,
nvs_keys included, as ordinary data.
Flash layouts are detected before single ESP images. The scan runs only
when an ESP image sits at a ROM bootloader offset, and is refused when a
bootloader at another chip's offset shows that the dump does not start at
flash address 0. open_binary gains flash_offset and partition_table_offset,
which require format esp-flash-image and are part of the snapshot target
identity; the CLI takes them with --target-format esp-flash-image.
Comparison treats a partition's label, type, subtype, range and flags as
its declaration.
* test(artifacts): verify ESP flash layouts with gen_esp32part, merge-bin and otatool
Add the optional verify:esp-flash lane. It builds default and relocated
partition tables with ESP-IDF's gen_esp32part.py, assembles dumps with
esptool merge-bin, including one written from flash 0x1000, and writes
OTA records with otatool through flash_file_tool.py, which replaces
parttool's esptool calls with reads and writes of the dump file.
It compares REA's bootloader and app facts with esptool image-info, every
partition with gen_esp32part.py's decoding of the extracted table, and the
OTA records and derived slot with otatool read_otadata after each switch.
It also checks extracted bytes and source integrity, the scan and caller
table offsets, flash_offset on the 0x1000 dump, a corrupted app beside
valid partitions and a truncated dump. ESP32 linker scripts place the
conformance bootloader and app in ESP32 address ranges.
* fix(artifacts): read ESP flash layouts as the bootloader does
Read the partition table as bootloader_utility_load_partition_table
does: the last factory, otadata and same-subtype OTA declaration wins,
and a limitation names repeats. Plan each otadata record within its
partition and the dump. An otadata partition smaller than two sectors
selects none, the bootloader's INVALID_INDEX; a record the dump lacks
leaves the selection unknown while the other record stays reported. The
fallbacks follow the bootloader's indexes: ota_0-fallback and
factory-fallback name no partition when the table declares none, and the
assumptions add GPIO holds and deep-sleep wake.
Refuse a dump whose bootloader contradicts its origin before the default
table offset is tried, whether flash_offset was omitted or given, and keep
the ESP image reader's ESP8266 refusal. A table never lies inside the
located bootloader image, so single app images are no longer scanned. An
explicit esp-flash-image selection is decided before other container
signatures, and the native commands' --target-format no longer takes it.
Say when no bootloader confirms the default origin.
Exempt the primary bootloader and partition_table entries of ESP-IDF 5.3
from placement limits, report every region inside a longer one, and name
partitions by index when labels repeat ignoring case. Keep an undecodable
label's bytes in label_hex and compare them. Word undescribed ranges as
starting at a byte that is not erased, and treat a short erased region as
erased.
Add an MCP compare_artifacts test for partition declarations, reader
identity and close tests, scan edge cases, the 0x2000 ROM offset and a
literal otatool CRC.
* test(artifacts): verify ESP primary layout entries and the otadata fallback
Build a table with the primary bootloader and partition_table entries
that gen_esp32part.py fills from --primary-bootloader-offset, and check
that REA reads it with complete coverage, no overlap or misplacement
limitation, and a table that agrees with gen_esp32part's decoding. Erase
the otadata with otatool after the slot switches and check the records
against read_otadata and the factory fallback.
* fix(artifacts): keep ESP flash coverage complete for a dump cut short
An esptool merge-bin dump ends after the last part it wrote, usually inside
its last partition. Such a partition made the layout's coverage unknown, so
artifact comparison of two merge-bin dumps reported every unchanged
partition as unknown.
The partition table names every partition whatever the file's length, so a
partition cut short or left out by the end of the file hides no entry. As
the ESP image reader does for a cut-short segment, its entry holds the bytes
the file has and an entry limitation gives the declared size; a partition
with no bytes stays in partitions_outside_dump. Coverage stays complete
unless undescribed bytes remain or no bootloader is located. An MCP
compare_artifacts test of two dumps that end inside ota_0 now classifies
every partition, and fails on the old rule.
* fix(artifacts): report written bytes in ESP partitions, erased slots included
An app slot is erased for ESP-IDF's bootloader when its image header bytes
are erased flash, and REA reported only that status. A slot can still hold
data further in: a dump read from a device held a complete older app image
1 MiB into an OTA slot reported as erased, with no limitation.
Each partition entry now records where the bytes the dump holds are
written, relative to the partition: written_offset, the first byte that is
not 0xFF, written_end, just past the last, and written_bytes, how many are
not 0xFF. They are null for a partition outside the dump. The status stays
erased, which is the bootloader's view, and a limitation names an erased
app slot that holds written bytes and says they are not read as an image.
The reader reads every partition byte once more to find them.
* fix(artifacts): derive the ESP boot selection for every partition table
The boot selection lived inside otadata, so a table without an otadata
partition, the common factory-only layout, had none. ESP-IDF's
bootloader_utility_get_selected_boot_partition returns the factory index
when the table declares no otadata partition.
Move it to a top-level boot_selection that every layout has. Its basis
names the bootloader's branch: otadata, factory-fallback and ota_0-fallback
from the records, no-otadata for a table without otadata, none for the
bootloader's INVALID_INDEX, and unknown when the dump lacks a record the
bootloader reads, which replaces the null selection. running_partition
moves with it, and otadata keeps its partition index and records. The
assumptions name what each branch leaves out: no records assumption
without otadata, and none for none or unknown. The real-tool lane checks
no-otadata on a table gen_esp32part built without otadata.
* fix(artifacts): say when an ESP image is a flash dump read from 0x1000
An ESP32 dump read from flash 0x1000, as read-flash 0x1000 or merge-bin
--target-offset 0x1000 writes it, opened automatically as its bootloader
image with a megabyte of undescribed bytes and no next probe. The flash
reader refuses that origin, but nothing told an agent which flash_offset to
pass.
espFlashDumpOrigin owns the inference: an image of a chip whose ROM loads
the bootloader from flash R, at file offset F, is the bootloader of a dump
read from R - F when a partition table sits where that origin puts 0x8000.
The ESP image reader asks it for a non-app image at file offset 0, so the
result does not depend on how the reader was created, and records
flash_dump {flash_offset, table_file_offset}, null otherwise, with a
limitation that says to reopen the file as esp-flash-image with that
flash_offset. inspect_artifact proposes the same open_binary call as a next
probe, and the flash reader's misplaced-origin refusal names the offset.
* perf(artifacts): summarize nested ESP images in the flash layout summary view
The summary view returned an ESP flash layout's root facts whole, so each
app partition and the bootloader carried every segment and signature
block, about half of a dump's summary.
The summary view now gives each nested image a summary instead of its
facts: status, chip, entry point, segment count, load ranges (each mapped,
loaded or unclassified segment's address and declared length), the
checksum and appended SHA-256 statuses, coverage and limitations. Complete
detail keeps the full facts, every segment included, and the retained
Evidence is unchanged. On a 16 MiB device dump the summary's root falls
from 5,141 to 2,657 bytes, and the summary Evidence from 10,038 to 7,554.
* test(artifacts): expect the misplaced image the ESP bootloader search reports
8a748d3cc added misplacedImage to the bootloader search, which the
locator uses to name the origin a misplaced bootloader implies, but one
test still compared the search result without it. Expect the image's
chip and file offset, and no image for an unknown chip.
* fix(artifacts): give every ESP partition from the dump's start an entry
After ecbc1a772 kept coverage complete for a dump cut short, a partition
wholly past the end of the file still had no entry, only a
partitions_outside_dump row. Comparing a merge-bin dump with a full device
read then reported that partition as added or removed, as if one table
did not declare it.
Every partition from the dump's first byte on is now an entry holding the
bytes the file has: none for one past the end, with a limitation saying
the file holds none of its bytes, no image facts and null written fields.
partitions_outside_dump keeps only partitions that begin before the dump's
first byte, and coverage is unknown when one does, since it has no entry.
Two dumps that lack the same partition now compare it as unchanged, and a
dump against a longer one reports the partition as changed; an MCP test
of a merge-bin dump against a full one checks that no path is added or
removed.
Unlike the ESP image reader, which marks a truncated image's coverage
unknown because later segments and signature blocks may be hidden, the
flash reader can list every partition from the table whatever the file's
length; only the entry limitation follows the image reader.
* fix(artifacts): select no ESP app from a partition table the bootloader rejects
boot_selection followed bootloader_utility_get_selected_boot_partition but
not the table check before it. For a table whose MD5 does not match, or
that has a second MD5 entry, esp_partition_table_verify fails, the
bootloader selects no app and resets. REA still derived a slot from the
otadata records, although the table's own limitation said the bootloader
rejects it.
The selection now has basis none, with no assumptions, for a table the
decoder marks invalid. It also follows two more branches of the
bootloader: an otadata partition at offset 0, which the bootloader takes
for none, selects the factory index as no-otadata, and a table without an
OTA app selects its fallback even when the dump lacks a record, since the
bootloader then ignores the records.
The bootloader also rejects a table whose partitions end past the flash
size in its header. REA does not model that check; the format guide says
so.
* fix(artifacts): require the ESP dump origin's table to follow the image
espFlashDumpOrigin named a later flash origin whenever a partition table
decoded where that origin puts 0x8000. Bytes inside the image's own
segments could be read that way, so an ESP32 bootloader whose segment data
held table-shaped bytes at file offset 0x7000 was reported as a dump read
from 0x1000.
The table must now start at or after the image's end, the rule the locator
already applies to the default table. The ESP image reader passes the
image_end it decoded, or the file size for a truncated image; the flash
reader's refusal passes the end of the misplaced image's segments. The
bootloader search drops its misplaced flag, which only restated
misplacedImage, and the next probe formats offsets with offsetText.
Reader tests cover the 0x1000 and 0x2000 ROM offsets, a standalone
bootloader, bytes that begin like a table, an app with a table at 0x7000
and a table inside a segment; without the boundary check, three of them
fail.
* fix(artifacts): give the ESP flash summary its own kind and each image's identity
The summary view's ESP flash root kept kind esp-flash-image, so a consumer
bound to kind could not tell an image's summary from its full facts, and
the advertised schema held two objects with the same kind const. It is now
esp-flash-image-summary.
The per-image summary also dropped the descriptor, which is what tells two
app slots apart, and named each image only by its partition index, which
the summary view, having no entry rows, could not resolve. Each nested
image now carries its entry path from the layout plan, in both views. The
summary adds the descriptor kind with its identifying fields (an app's
project_name, version, idf_ver, secure_version and app_elf_sha256; a
bootloader's version, idf_ver and secure_version), the signature block
count and each load range's classification. Its enums and fields derive
from the schemas that own them.
An MCP test reads a dump with an app in ota_0 and an erased ota_1 that
holds older bytes: it checks both summaries, that the summary's inventory
Evidence is the one the complete view returns with full facts, and the
erased slot's written range.
* test(artifacts): cover cancel and close during the ESP written-bytes scan
The written-bytes pass reads every byte of each partition the dump holds,
checking that the reader is open before each chunk, but only app decoding
had a cancellation and close test. A data partition, which only that pass
reads, now holds 9 MiB; once the scan has examined its first chunk, a
cancel rejects preparation with the caller's reason and a close with
unavailable, and the scan examines no further chunk.
The erased-slot test now asserts the whole set of erased-slot limitations,
so factory, ota_0 and nvs, which the bootloader reads or which are not app
slots, are shown to have none.
* docs(artifacts): re-time the ESP flash lane and state only observed causes
The lane ran again after its assertions changed; both esptool runs passed,
and the timed run's figures replace the earlier ones. The erased-slot
paragraph no longer names a cause for the older bytes, which REA cannot
observe, and the skill reference states that every table has a
boot_selection in its own sentence, with the none basis for a table the
bootloader rejects.
* fix(artifacts): retire ESP flash members as the ESP image reader does
#7498's maintainer commits made the ESP image reader keep a member owned
until its consumer closes it, retire fully buffered bytes when
cancellation arrives after production, and refuse to finish an inventory
paused across close. The ESP flash reader was copied from the earlier
reader. Main's shared stored-member lifecycle cases failed two of eight
against it: a paused inventory completed after close, and buffered member
bytes stayed readable after cancellation.
Give the flash reader the same lifecycle and run the shared cases against
it.
* docs(artifacts): re-time the ESP flash lane after merging main
The lane passed again with esptool v5.6.0 and 4.12.0 on the merge of upstream main. Record the v5.6.0 run's time and peak resident set.
* fix(artifacts): read ESP image Evidence recorded before flash_dump
The flash layout work made `flash_dump` a required field of the
`esp-image` root metadata. Evidence recorded before it has no such
field, so newer REA rejected records that were valid when they were
made: `compare_artifacts` and `inspect_analysis_view` reported
`evidence_integrity_mismatch` for them.
The field is now optional when read. A record without it parses as it
is, so the graph digest it commits to still matches; nothing fills the
field in. The image reader still writes it for every image, and the
inspection probe reads a missing field as no hint.
A regression test recommits an image inventory without the field and
passes it inline to `compare_artifacts` and `inspect_analysis_view`.
Against a server built with the required field, `compare_artifacts`
rejects it.
* fix(artifacts): stop the ESP flash gap scan when the reader closes
The scan for the first written byte in each undescribed gap checked
only the caller's signal. `close()` marks the reader closed and waits
for preparation, so it waited while the rest of a long erased gap was
read.
The gap scan now takes the reader's open check, as the written-bytes
scan and the image decoder do, and checks it before each chunk. Tests
cancel or close the reader after the scan's first chunk of a 9 MiB
erased gap and confirm that no later chunk is examined.
* fix(artifacts): name ESP partitions with Windows device labels by index
The safe-label rule accepted labels such as `NUL`, `con` or `COM1`, so
extraction wrote `partitions/NUL.bin`. Windows reserves those names
before any extension, and ordinary Windows tools cannot use such a
file.
A label that is a Windows device name (`CON`, `PRN`, `AUX`, `NUL`,
`COM0`-`COM9` and `LPT0`-`LPT9`, any case, alone or before a dot) now
takes the existing index fallback. The partition metadata and the
entry's source path keep the label.
* fix(artifacts): read ESP flash partitions' written bytes in one pass
Each partition got its own scan of the bytes the dump holds, so
partitions declared over the same bytes read them again and again.
Overlaps only add limitations, so a crafted table reached this normally:
94 overlapping partitions over a 32 MiB dump would examine about 3 GiB
before the first entry.
The reader now reads the bytes the partitions cover once. Partition
boundaries cut them into segments; one pass counts each segment's
written bytes and bounds, and each partition's extent is the sum of the
segments inside it. Every partition keeps its own declaration, extent
and limitations. The pass checks the reader's open state before each
chunk, as the per-partition scan did.
A test declares 60 partitions over one MiB, 40 over all of it and 20
over its first half, and checks each partition's extent and the
overlap limitations. The reader reads 1,113,256 bytes of the
1,114,112-byte dump; one scan per partition read 52,493,480.
* test(artifacts): fail the ESP lanes on failed commands and refusals
`otatool` turned a nonzero exit into returned text, which its callers
discarded, and `inspect()` returned `{ refused }`, which the flash lane
never checked. A failed command then surfaced later as a slot
assertion, an output-parser error or a TypeError, hiding its cause.
Commands the lanes need to succeed now reject with the output they
exited with: `otatool` and `inspect()` do so through a shared
`failedCommand`. An expected refusal is opt-in: `refusal()` returns
REA's output and fails the lane when REA opens the file. The ESP image
lane's two ESP8266 cases use it.
Checked by calling `otatool` on a file without a table, `inspect()` on a
file REA refuses and `refusal()` on a file REA opens; each rejects with
its cause.
* fix(artifacts): require a partition entry in an ESP partition table
The decoder counted the slot, not the partitions, so a lone MD5 entry
over the empty prefix, followed by the terminator, decoded as a table
with no partitions. Default and caller-selected detection then accepted
that empty layout, although the guide requires one partition.
A table now needs one partition entry before its terminator. An MD5
entry alone is refused as `entry 1 at 0x20 ends the table before any
partition entry`. The cases cover the decoder and both automatic and
caller-selected location.
* fix(artifacts): decode an image shared by ESP app partitions once
Each app partition got its own decode, so app partitions declared over
the same bytes decoded and hashed the same image again and again, the
same pattern the written-bytes scan had. With 60 app partitions over
one 960 KiB image the reader read 64,029,736 bytes of a 1,114,112-byte
dump.
Regions with the same file range now share one decode; each keeps its
own copy of the facts. Partitions with different ranges still decode
separately, because truncation can change the facts: at most one decode
per partition the table holds, each within its partition.
A test declares 60 app partitions over one image, 40 holding all of it
and 20 cutting it short, and expects three decodes: the bootloader, the
whole image and the cut one. The 60-partition dump now reads
2,161,864 bytes, as a dump with one app partition does.
Since 651afa445 target opening refuses every ELF declaration beyond EOF,
including carved copies, and ac6e0e258 moved the carved MIPS boundary tests
to that contract. The real-Ghidra MIPS lane still expected a carved image to
resolve with section-table limitations and be refused later by Ghidra
admission, so verify:ghidra:mips failed at its first carved fixture.
The lane now requires open_binary and the CLI to refuse the carved copy as
target_unavailable (file_format_rejected) with the exact declared
section-table range and captured size. The carve and that expectation live in
the shared MIPS checks, and a boundary regression applies them to the
production resolver and error projection, so a later contract change fails
without real Ghidra. The MIPS and PSP guides describe the current contract.
* fix(ghidra): report a missing existing-project program as a configuration error
When REA_GHIDRA_PROJECT_PATH selects a project that has no root program
with the selected name, Ghidra's headless analyzer aborts with
"Requested project program file(s) not found: <name>". REA reported this
as a generic execution_failure with rea doctor advice, and the reason was
visible only in the captured diagnostics.
Recognize that refusal in the operation-scoped launcher output, matching
only the exact name REA passed to -process, and return a
ConfigurationError for REA_GHIDRA_PROGRAM_NAME. The message says whether
the name was configured or defaulted from the target file name, and the
remediation explains how to select the exact root program. The selected
project is unchanged and there is no fallback to a fresh import. Other
launch failures keep their existing projection.
Fixes#8216
* test(ghidra): assert missing-program guidance through its formatter
Compare the projected setting constraint and remediation with
ghidraMissingProgramGuidance instead of freezing explanatory phrases,
following docs/testing.md. The program name and configured-or-defaulted
origin are still checked independently.
Expose static Electron protocol call facts through the shared application graph and existing CLI/MCP Evidence views. Preserve canonical case-insensitive scheme identity, original spellings, exact source provenance and explicit unknowns; keep registration success and runtime routing unproven.
Integration fixes also enforce module-only privileged API ownership, reuse the shared finding-key owner, and align the owning type documentation with the Evidence semantics. Validation and bounded real Obsidian CLI/MCP measurements are recorded in the PR.
Co-authored-by: UnknownHacker1 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
In fresh-import mode, the startup-timeout remediation now points to
REA_GHIDRA_PROJECT_PATH for reusing a completed analysis, next to the
existing REA_GHIDRA_STARTUP_TIMEOUT_MS and REA_GHIDRA_DISABLED_ANALYZERS
advice.
In existing-project mode, the message no longer says Ghidra was
importing and analyzing the target, and the remediation no longer
suggests REA_GHIDRA_DISABLED_ANALYZERS, which configuration validation
rejects together with REA_GHIDRA_PROJECT_PATH.
Refs #3751
* fix(javascript): emit no socket for a literal out-of-range port
`net` and `tls` validate a numeric port before creating a socket, so a literal
port Node rejects throws synchronously and no socket exists — but the analysis
reported an `acquire` operation.
Measured on Node 24.22.1:
net.connect(0) / 80 / 65535 -> Socket created
net.connect(65536) -> THREW ERR_SOCKET_BAD_PORT
net.connect(-1) / 1.5 -> THREW ERR_SOCKET_BAD_PORT
net.connect(NaN) / Infinity -> THREW ERR_SOCKET_BAD_PORT
net.connect(1e400) -> THREW ERR_SOCKET_BAD_PORT
net.connect(0n) / true / false -> THREW ERR_INVALID_ARG_TYPE
net.connect(null) -> THREW ERR_INVALID_ARG_TYPE
net.connect("") -> THREW ERR_SOCKET_BAD_PORT
net.connect("/tmp/x.sock") -> Socket created (Unix socket path)
net.connect({ port: 80 }) -> Socket created
net.connect(undefined) / {} / [] -> THREW ERR_MISSING_ARGS (undecided)
--- tls mirrors net ---
tls.connect(80) -> Socket created
tls.connect(65536) / -1 / NaN -> THREW ERR_SOCKET_BAD_PORT
A string is a Unix socket path and an object may carry a path, so only an
exactly readable non-port literal is decided. `undefined` and an empty object
throw `ERR_MISSING_ARGS`, which is a missing option rather than a bad port, so
they stay undecided. `NaN` and `Infinity` are identifiers rather than literals
and are decided only through `isUnmodifiedGlobalReference`.
* chore: retrigger title and readiness checks after the title was corrected
* fix(javascript): place the port check away from the fs flag check
`#8125` adds an fs flag guard at the top of `collectAcquisitions` and this branch
added a socket port guard in the same spot, so the two conflicted on every
rebase. The port check now filters the completed candidate list from
`collectJavaScriptSemanticResources` and its helpers sit at the end of the file,
which cannot collide with the fs flag guard.
* fix(javascript): drop the redundant in-loop port guard
The caller now filters the completed candidate list, so the loop guard became
dead code. Removing it also keeps this branch from editing the same lines as the
fs flag guard in #8125.
Resolve exact external method targets from directly encoded DEX invokes.
Preserve complete caller identity, bounded position evidence and follow-up
selectors. Report aggregate position coverage independently of the caller
inventory, and reuse bounded prototype facts within the exact session.
Absorbs #8096. Fixes#7811.
Co-authored-by: hhang <[email protected]>
* feat(dotnet): expose field constant defaults
* test(dotnet): guard field default comparison warning
Compare actual ordinary and constant-bearing observations independently of the limitation producer. Preserve warning delivery and unchanged matching assertions while allowing harmless wording changes.
Refs #5150
* test(mcp): retain structured bundle path diagnostics
Keep absolute-path refusal and all rejected parameter facts independent of legacy flat or current structured SDK diagnostic rendering. Retain relative CLI, absolute MCP and no-Evidence controls.
Refs #5150
* Revert "fix(ci): integrate main and repair inherited static checks"
This reverts commit afd4d571f6, reversing
changes made to 317bf3fe23.
* fix(ci): replay reviewed field-default integration
* fix(analysis-view): keep inferred confidence on views of inferred Evidence (#8099)
createAnalysisViewEvidence chose view confidence from the view result kind
only, so every summary, page or item view of an inferred parent
(compare_source_to_bundle, inspect_evm_interface, partially resolved
inspect_il2cpp_metadata) was sealed as derived while keeping the parent's
authority. A projection never makes its facts more certain: an inferred
parent now yields inferred views through CLI and MCP alike. Observed and
derived parents keep their existing view confidence and identities.
Fixes#8098
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(apple): read flattened framework Info.plist from Resources (#8101)
A macOS framework without Versions/ keeps its binary, _CodeSignature/ and
Resources/Info.plist directly in the .framework, as Electron and Gecko apps
ship it. Bundle anatomy looked for a shallow bundle's plist only at its root
and reported info_plist_path: null under complete coverage.
Resolve a shallow bundle's root Info.plist first, then Resources/Info.plist,
the same layout a framework version directory uses. Keep the iOS platform
rule tied to a root-level Info.plist so a Resources/-only application root
is not classified as iOS.
Fixes#8100
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(desktop): charge retained rows at their measured representation (#8105)
* fix(desktop): charge retained rows at their measured representation
The desktop scan reserved 1024 + 24 bytes per path character for every
row, omission and the root against its 32 MiB representation budget. Real
rows cost 800-940 heap and 490-640 JSON bytes, so the charge overstated
them 6-13 times and large macOS bundles stopped at 3,700-7,200 entries,
far below max_entries.
Charge each path at the larger of its UTF-16 heap size and its escaped
JSON size, plus the existing fixed row and omission overheads, in one
helper. The bound still covers the retained heap and the serialized
report, including control-character paths that expand six times in JSON.
Fixes#8104
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): charge every retained copy of a path
The tighter row charge counted each path once, but rows repeat it in
convention-fact values and the next probes those facts offer, and host
failure issues name the absolute path. Control-character paths under a
convention such as Content/Paks or resources/app.asar therefore serialized
47-80 MiB of reports within the 32 MiB budget.
Reserve each copy through the same text helper where it is stored: path
convention facts with their row, the probes any fact offers (at most two,
pinned by a projection test) with that fact, retained read ranges before
their read, and probe failure issues before they are recorded.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(source-maps): resolve empty sourceRoot sources against the map URL (#8107)
webpack 5 and tsc write "sourceRoot": "" for map-relative sources. Since
5f702dd6c the shared join added "/" for any non-null root, so these
sources resolved from the URL root: rxjs's ../../../src/internal/Observable.ts
under /npm/[email protected]/dist/cjs/internal/ became /src/internal/Observable.ts,
and xterm's webpack://xterm/./src/... became https://host/webpack://xterm/....
Treat an empty root like a null root in sourceMapReference, the single join
used by trace-web-source-location, analyze-javascript-application and the
web source-map fetcher. Chrome, Firefox and trace-mapping skip an empty root,
and tc39/ecma426#183 wrote the current steps to match them. Non-empty roots,
including "/" and repeated slashes (#6726), keep their existing behavior.
Fixes#8106
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(javascript): stop reporting self-defaulted receivers as module loads (#8109)
The CommonJS loader search treated any repeated state on its own ancestor
chain as an unknown loader. A self-default such as
`session.messages = session.messages || []` forms that cycle, because a
member read inside the assigned value was attributed to the later call's
read site and selected the same write again. Each `push` on the value was
then reported as an unresolved `require` load, and graph coverage became
partial.
Record a member read inside a flowed value as a depth-1 captured selection
at its own position, as member mutations already do for flowed origins.
Treat a revisited state as adding no source; the cycle's other entries are
still expanded once each. Remove the superseded ancestor tracking.
Fixes#8102
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(captures): admit browser-serialized HAR request URLs (#8112)
* fix(captures): admit browser-serialized HAR request URLs
[email protected] checks request.url with ajv-formats' strict RFC 3986
`uri` format. Browsers record and send URLs as WHATWG serializes them,
which keeps characters such as `|` and `{` unescaped in the query, so one
Google Fonts or GraphQL GET entry made inspect-web-network-capture and
environment-build reject the whole HAR.
HAR 1.2 defines request.url only as the absolute request URL. Own that
correction in the HAR validator: every URL the RFC 3986 check accepts
stays accepted, and a URL that WHATWG URL serialization reproduces
unchanged is accepted too. Spellings WHATWG would rewrite (a backslash
authority, a tab, a space) and relative references still fail.
Fixes#8110
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(captures): state that RFC 3986 request URLs stay admitted
The HAR request URL rule is "passes RFC 3986, or WHATWG serialization
reproduces it unchanged". A spelling WHATWG would rewrite therefore fails
only when it also fails the RFC 3986 check; an uppercase host still
passes. Say so in the docs and the validator comment, and pin the
accepted uppercase-host case in the CLI test.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(apple): charge the XML plist decoder's DOM before decoding (#8115)
* fix(apple): charge the XML plist decoder's DOM before decoding
The XML preflight charged 4 bytes per character and 256 bytes per start
or end tag. The decoder builds an @xmldom/xmldom DOM that measured
1,000-1,160 bytes per element and up to 240 bytes per text, comment or
CDATA node, which were not charged at all. A 1.75 MB plist of 230,000
<true/> lines was admitted under the 64 MiB decode budget and peaked at
249 MiB of heap; shipped Apple plists peaked at 80-104 MiB.
Charge each element and each text, comment, CDATA or DOCTYPE node at a
measured upper bound, keeping the character and number-projection terms.
The binary estimator already bounds its decoder and is unchanged.
Fixes#8113
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(apple): charge every XML element, attribute and reference
The XML preflight recognized only element names starting with an ASCII
letter, so names such as é, _, a CJK or astral character escaped the
element charge: 100,000 <é/> elements (0.5 MB) built an 84 MiB DOM before
the plist decoder rejected the tag. Attributes and entity or character
references were also uncharged; 20 attributes per element measured about
210 bytes each, and each reference about 26 bytes beyond its characters.
Classify markup by the character after "<" instead of a name pattern, so
anything other than an end tag, comment, CDATA, PI or declaration counts as
an element (saxes rejects malformed markup before the DOM is built). Charge
each unquoted "=" in a start tag as an attribute node and every "&" as a
reference.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(apple): charge XML references only where the parser expands them
The previous commit charged every "&" in the document as a reference,
including literal ampersands inside CDATA sections and comments. A valid
plist with one CDATA string of 1,000,000 ampersands was refused although
it is a single node the decoder holds as one string.
Count "&" during the existing token walk, only in text runs and in the
attribute values of start tags, so comments, CDATA, PIs and declarations
add no reference charge and the scan stays a single pass.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(fit): preserve leading U+FEFF in property strings (#8083)
* fix(environment): retain conflicting scenario MIME uncertainty (#8082)
* fix(environment): retain conflicting scenario MIME uncertainty
* test(mcp): assert structured absolute-path diagnostics
* test(mcp): assert typed selector and stdio diagnostics
* fix(electron): keep literal IPC sends ambiguous beside unresolved handlers (#8120)
* fix(electron): keep literal IPC sends ambiguous beside unresolved handlers
A main handler on an unresolved channel can receive any literal renderer
send of a compatible mode, but pairing ignored it unless it had a scoped
receiver. The send was then reported unpaired, or paired with another
handler. Treat an unresolved-channel handler as a competitor that never
pairs.
The wrapper pass seeds only calls with a literal first argument, so it now
records which invocations of a dynamic site it traced. The syntax finding
gains `invocation_channels: "literal"` only when every invocation is a
traced direct call of an unexported local function supplying a literal
channel. Such a site is already represented by its per-invocation findings
and does not compete.
Fixes#8118
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(electron): keep guarded wrapper registrations competing in IPC pairing
A guarded wrapper registration keeps its predicate on a call path and has
no IPC finding. It still counted toward `invocation_channels: "literal"`,
so pairing lost it: a send on that channel was reported unpaired, or
paired uniquely with a direct handler. The enumerated site now carries
these channels as `guarded_invocation_channels`, and each one competes for
its own channel without pairing.
Index each binding's direct call sites by appending in place. Spreading
the earlier calls on every reference was quadratic in a file's call count.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(ci): remove unused exports that fail deep static checks (#8095)
* fix(ci): remove unused exports that fail deep static checks
Knip on main reports three analysis-view hub re-exports, two type
re-exports, an unused artifact-format guard and an alias constant. Drop
the dead re-exports and guard, and give the debugger control-reply budget
its own literal: it is a separate budget from the caller command-output
default, as its comment already states.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(config): read the Rizin control budget from its own constant
The Rizin control-reply admission limit still took its value from the
caller command-output default, while GDB used DEBUGGER_CONTROL_REPLY_BYTES.
Read the dedicated constant in both so the control budget has one owner.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* perf(javascript): keep one member-effect record per stored fact (#8103)
Member-mutation indexing appended a binding write or escape record for
every traversal state that reached it. States that differ only in
coverage fields the record never stores (origin, owner initializer,
getter widening) produced identical records, and each later receiver
query rescanned all of them. Record each stored fact once, alongside the
existing required-module effect deduplication. Propagation is unchanged.
Effect ordering also re-walked the same top-level statement ancestry for
every candidate record. Cache it with the other initializer positions,
and test the selected path before the costlier receiver comparison.
On the #7774 reproducer the semantic IR and the complete
analyze-javascript-application Evidence are byte-identical, while the
CLI run drops from 146 s to 40 s.
Refs #7774
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(native): open extension, framework, XPC and plug-in bundle directories (#8117)
* feat(native): open extension, framework, XPC and plug-in bundle directories
open_binary and the CLI inspection commands admitted only .app
directories and pointed every other Apple bundle at the JavaScript
analyzer. Resolve .appex, .framework, .xpc, .systemextension, .bundle
and .plugin directories through their Info.plist CFBundleExecutable, in
the Contents or flat root layout, plus the versioned framework layout
under Versions/Current. Exactly one layout may declare the bundle and
every resolved path must stay inside it.
Unresolvable bundles fail closed with typed constraints, probed paths
and the Info.plist that was read. Other Apple bundle types are refused as
unsupported instead of being offered the JavaScript route. Resource-root
discovery shares the same layout rule, so inspect_plist reads a
versioned framework's Info.plist. The recognized bundle vocabulary now
has one owner in the domain.
Refs #4310
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): refuse kernel extensions and state bundle error fields
Name the Apple bundle types binary routes recognize but do not open
(.dext, .kext, .qlgenerator, .mdimporter) explicitly, so a kernel
extension is refused as unsupported instead of receiving the JavaScript
route. Document which bundle refusals carry probed_paths and info_plist,
and make success tests throw the typed admission error before asserting.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(native): open flattened frameworks through Resources/Info.plist
Electron and Gecko apps ship frameworks without Versions/: the program
file sits at the framework root beside Resources/Info.plist, the layout
#8101 taught bundle anatomy. Read that layout for a framework without
Versions/, and keep the versioned layout for one with it, so a versioned
framework's root Resources link is never read as a second declaration.
The resource root reports it as flattened-framework.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* chore(security): configure dependency and ownership reviews
* perf(javascript): index member-effect paths for projected reads (#8108)
* perf(javascript): index member-effect paths for projected reads
Each projected read scanned every member-write record of its binding and
tested the record path against the selection. On the #7774 reproducer
only 3.7% of scanned records pass that test. Index each binding's
append-only records by path in a lazily built trie that extends itself
with newly appended records, and visit only records whose exact keys a
selected choice names or whose keys are wildcards. Candidates come back
in record order and still pass the exact predicate, now owned next to
the index as semanticEffectPathMatches.
The semantic IR and the complete analyze-javascript-application Evidence
are byte-identical. On the full reproducer each worker phase drops from
about 80 s to 29 s, and to 8 s together with #8103.
Refs #7774
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(javascript): stop escape checks at the first indexed match
The escape check only asks whether any matching escape is unsuperseded,
so it does not need ordered candidates. Share one trie traversal between
the ordered write candidates and an early-exit visitor for this check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(hopper): distinguish an absent demo dialog from publisher rejection (#8074)
* fix(hopper): distinguish an absent demo dialog from publisher rejection
The Linux demo watcher collapsed an admitted launcher with no first-run dialog into unsupported_demo_dialog. Report that post-authentication timeout as demo_dialog_absent, keep the unsupported dialog code for an observed but unrecognized Hopper window, and allow REA_HOPPER_DEMO_DIALOG_TIMEOUT_MS to widen the fixed wait. Closes#8008.
* fix(hopper): tolerate callers without the dialog timeout argument
* fix(hopper): report an invalid demo dialog timeout as its own failure
An invalid REA_HOPPER_DEMO_DIALOG_TIMEOUT_MS exited 73 and was shown as
invalid_launch_command. Exit 82 is invalid_dialog_timeout. The public error
names the setting, shows the rejected value and source, lists the allowed
range, and says to restart REA.
Drive the demo watcher with controlled windows and time. No window exits 81,
a Hopper window without a supported dialog exits 76, and a dialog that arrives
after the old 10 second limit is still accepted.
* fix(hopper): keep startup stderr capture on AdapterFailure
DemoWatchStop was inserted above with_stderr, so launch failures called a
method they no longer had and became unexpected_failure. Restore with_stderr
on AdapterFailure and reject a watcher fixture that cannot capture stderr.
---------
Co-authored-by: Cursor Agent <[email protected]>
* test(ci): require provider checks and reduce test startup (#8145)
* test(ci): require provider coverage and reuse test sessions
* fix(ci): prepare the runtime before Python producer tests
The fast Python job ran the suite on a fresh checkout, so analysis-efficiency
checks failed without compiled process modules. Install locked dependencies
and prepare the runtime before the suite.
* fix(ci): keep provider test reports inside the checkout
The provider-report reader accepted an arbitrary path from argv. Resolve the
path and reject anything outside the checkout before reading it. The packet
and signal workflows write those reports under .cache.
* fix(ci): require a test module in each named Python bridge family
Discovery still runs every bridge test. The mitmproxy, native, and pwndbg
families must each keep a test module, so deleting one cannot pass through
the others.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(javascript): emit no http request for a target that is not a URL (#8147)
* fix(javascript): emit no http request for a target that is not a URL
`node:http` parses a string target with `new URL(...)` before a request object
exists, so a literal that is not an absolute URL throws `ERR_INVALID_URL`
synchronously. The analysis reported `endpoint` at `resolution: "complete"` for
those calls, and the graph then projected `constructs-request` as `resolved`.
Measured on Node 24.22.1:
http.get("nope") -> THREW ERR_INVALID_URL
http.get("example.test/api") -> THREW ERR_INVALID_URL
http.get("/api/items") -> THREW ERR_INVALID_URL
http.get("/api/items", { host }) -> THREW ERR_INVALID_URL
http.get("") -> THREW ERR_INVALID_URL
http.get("//example.test/x") -> THREW ERR_INVALID_URL
http.get("http://") -> THREW ERR_INVALID_URL
http.request("nope") -> THREW ERR_INVALID_URL
https.get("/api/items") -> THREW ERR_INVALID_URL
--- accepted ---
http.get("http://example.test/x") -> OK ClientRequest
http.get("HTTP://example.test/x") -> OK ClientRequest
http.get("http:/x") -> OK ClientRequest
http.get("http:///x") -> OK ClientRequest
http.get("http:x") -> OK ClientRequest
`URL.canParse` is the exact predictor of that branch, so a relative spelling such
as `"http:x"` keeps its operation. It already has precedent in this codebase
(`javascriptPackageImportAliases.ts`).
Two boundaries are deliberate. `fetch` and `new WebSocket` are host-supplied
globals: a browser resolves a relative target against the document URL, so their
spellings are not judged by Node's absolute-URL rule. A protocol mismatch such as
`http.get("ftp://...")` is also left alone because an options object can rescue
it, so it is not a provable throw.
The guard is scoped to `method === "get" | "request"`, which only
`requestMethod`'s `http`/`https` builtin branch can produce.
* chore: retrigger title and readiness checks after the title was corrected
* refactor(ci): unify lane ownership and guard Python tests (#8150)
* refactor(ci): unify lane ownership and guard Python tests
* fix(ci): reject entirely skipped bridge subtests
* docs: correct the raw ARM example and document a Cortex-M language override (#7483)
* docs(raw-binary): replace the STM32-style ARM example
The headline raw example opened ecu.bin at 0x8000000, an STM32 flash
address, while the arm lane decodes in ARM state. Map a 32-bit ARM-state
image at 0x8000 instead, and state beside the example that Thumb-only
images, including Cortex-M firmware, are not supported.
* docs(ghidra): document an interim Cortex-M language override
Ghidra imports little-endian ARM ELF files as ARM:LE:32:v8, which
defaults to ARM state and marks Thumb code only from $t mapping symbols
and odd function-symbol addresses. Unpinned arm ELF already admits
REA_GHIDRA_LANGUAGE_ID, so document ARM:LE:32:Cortex (ARMv6-M, ARMv7-M,
ARMv7E-M) and ARM:LE:32:v8-m (ARMv8-M) as an unverified interim.
State the override's full effect while it is set: raw-binary, DOS, DOL,
PSP and language-pinned lanes such as RISC-V refuse it, and REA asks
Ghidra to import every other target in the ARM language, because it does
not check the override's processor against the target.
* fix(ida): admit architecture families by allowlist (#7485)
IDA refused MIPS, RISC-V, PowerPC, Hexagon and arm64_32 by denylist, so
any family nobody excluded was reported supported, and the label ternary
printed "Hexagon" for any other metadata family. Admit exactly the
families IDA admits today (x86, x86_64, arm, arm64) and name every other
family from its own architecture id. Families that Ghidra admits keep
the pointer to a Ghidra profile.
Refusal text now uses architecture ids, for example "no verified mips
lane" instead of "no verified MIPS lane".
* ci(ghidra): probe M-profile ARM and Xtensa decoding (#7482)
* ci(ghidra): probe M-profile ARM and Xtensa decoding
The embedded plan relies on ARM:LE:32:Cortex, ARM:LE:32:v8-m and
Xtensa:LE:32:default, but CI only exercised ARM:LE:32:v7. Import
source-owned bytes for each language at 0x1000 with -noanalysis and
check how they decode from the language's default context: Cortex and
v8-m must start in Thumb state, v8-m must decode the ARMv8-M MSPLIM
register, and Xtensa must decode entry and retw.n.
Add the probe scripts the job runs, including the existing ARM context
probe, to the ghidra_bridge change scope.
* ci(ghidra): match decode-probe operands as complete operands
The operand expectation searched the instruction text for a substring,
so mrs/4/msp also accepted mrs r0,msplim. Compare it with each operand
Ghidra reports for the instruction, so only a complete operand matches.
The Cortex (msp, lr), v8-M (msplim, lr) and Xtensa (a1) expectations are
unchanged.
* test: guard target format vocabularies and DOL snapshot saves (#7481)
* test(mcp): save and reopen a GameCube DOL snapshot
#7290 reported that DOL Evidence could not be bundled or saved; 50822e1ef
fixed the bundle manifest and added bundle export/import coverage, but no
test saved a DOL session through close_binary. Save a DOL snapshot with
recorded Evidence, check its target and artifact manifest, then reopen it
and replay the read without running the provider again.
* test(binary): require every target format in each target vocabulary
A new target format must be added by hand to the Evidence subject,
bundle manifest, snapshot target and live summary vocabularies; missing
one caused #7290. Give every BinaryTarget format a fixture through a
mapped type, so a new format fails compilation until it has one, and
push each fixture through the producers that persist or project targets.
Caller-selected format hints must name executable target formats.
This is a guard until target formats have a single owner.
* test(mcp): assert the replayed DOL result and the owned replay limitation
The DOL snapshot replay test checked the subject and the provider-call
count but not the answer, so a replay returning null or another value
would pass. Compare the replayed normalized result with the original.
Name the replay limitation as SNAPSHOT_REPLAY_LIMITATION in the snapshot
cache that adds it, and assert that value instead of repeating the
sentence.
* test: list Intel HEX and S-record targets in the vocabulary guard
Upstream added the intel-hex and srec formats (#7136). The guard requires
a fixture for every target format, so after merging main it no longer
typechecked; both formats now have archive fixtures and pass the same
persistence and projection checks.
* test: list UnityFS among the target format fixtures
Upstream #7699 added the unityfs target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.
* test: list uImage among the target format fixtures
Upstream #7234 added the uimage target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.
* test: list the ESP image among the target format fixtures
Upstream #7498 added the esp-image target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.
* fix: correct stale provider refusal and diagnostic text (#7484)
* fix(ghidra): name every admitted family in the architecture refusal
The refusal listed x86, x86_64, arm, arm64, MIPS, RISC-V, raw 6502 and
raw m68k, but the admission set also holds powerpc and hexagon. Build
the text from SUPPORTED_ARCHITECTURES, marking raw-only families, so a
newly admitted family cannot be left out again, and name the refused
family.
* fix(hopper): stop calling the Ghidra Hexagon lane verified
Hopper declined Hexagon targets with "Select a verified Ghidra Hexagon
profile instead", but REA has no verified Hexagon lane. Record whether
each declined family's Ghidra lane is verified, and say that Ghidra
admits Hexagon through an unverified lane.
Name declined families by architecture id, as the Ghidra and IDA
refusals do.
* fix(ghidra): name the pinned language in profile readback refusals
The session readback said "MIPS" whenever a provider-pinned profile
conflicted with the target or with REA_GHIDRA_LANGUAGE_ID, although the
RISC-V, PowerPC and DOL lanes are pinned too. The readback helper was
MIPS-named and re-parsed the language slot that
ghidraProfileLanguageOverride already owns. Move it beside that owner as
a family-neutral readback and name the pinned language id instead.
* fix(ghidra): list DOL among independently verified load images
GameCube DOL load images are independently verified, but the declined
format reason, the inspect_native_load_image capability note and the
Windows P0 guide listed only DOS MZ, DOS COM and raw-binary.
* fix(ghidra): count RV32E's sixteen integer registers
The EF_RISCV_RVE refusal described a 15-register ABI. RV32E has 16
integer registers, x0-x15, as docs/ghidra-riscv.md already says.
* fix(elf): name the refused e_machine
"unsupported ELF architecture" did not say which machine was refused.
Include the numeric e_machine, for example EM_XTENSA 94.
* test(hopper): assert declined-lane facts and owned refusal reasons
The MIPS case banned the phrase "not verified" instead of checking
facts, so wrong advice would still pass. Expose each declined family's
Ghidra lane status, assert those statuses, check that the advice depends
on the status, and compare Hopper's refusals with
hopperDeclinedArchitectureReason. The RISC-V refusal table now compares
the provider's reason with ghidraRiscvUnsupportedReason and expects the
EF_RISCV_RVE declaration name rather than fixed wording.
* test(ghidra): assert verified load-image formats as owned facts
The declined-format test required display phrases such as "DOS MZ" and
"GameCube DOL", so harmless rewording failed it while wrong guidance
containing those names passed. The format list was also spelled three
times: in the attestation's membership check, its reason and the
inspect_native_load_image capability note. Give the verified formats one
owner with the formatters for both texts, assert the list as a fact, and
compare the delivered reason and capability note with those formatters.
* fix(hopper): stop restating which Ghidra lanes REA has verified
Hopper's refusal for families it declines carried its own table of
which Ghidra lanes REA has verified (mips, riscv and powerpc verified,
hexagon not). That restated the Ghidra provider's facts in Hopper and
goes stale whenever they change, the drift that made the Hexagon refusal
wrong in the first place. Reading Ghidra's facts from Hopper would
couple the providers instead; no module in src/hopper imports from
src/ghidra.
The table and hopperDeclinedGhidraLane are removed.
hopperDeclinedArchitectureReason now states only what Hopper owns: it
does not admit the family, and the Ghidra provider's lane reports
whether REA has verified it. The arm64_32 sentence is unchanged.
The test checks every declined family against the formatter: each
reason names the family and Ghidra, and apart from the name all reasons
are the same text, so none states a per-family Ghidra status.
* fix(hopper): route declined families to Ghidra without promising its lane facts
The refusal said the Ghidra lane reports whether REA has verified it, but
Ghidra admits Hexagon with no such note, so a caller could not find the
promised status. Hopper now only states that it does not admit the family
and routes the caller to the Ghidra provider.
---------
Co-authored-by: hhang <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: ShuaixinHuang <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Henglie <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: 吴佳浩Alben <[email protected]>
Co-authored-by: Entrpi <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
* fix(ghidra): name every admitted family in the architecture refusal
The refusal listed x86, x86_64, arm, arm64, MIPS, RISC-V, raw 6502 and
raw m68k, but the admission set also holds powerpc and hexagon. Build
the text from SUPPORTED_ARCHITECTURES, marking raw-only families, so a
newly admitted family cannot be left out again, and name the refused
family.
* fix(hopper): stop calling the Ghidra Hexagon lane verified
Hopper declined Hexagon targets with "Select a verified Ghidra Hexagon
profile instead", but REA has no verified Hexagon lane. Record whether
each declined family's Ghidra lane is verified, and say that Ghidra
admits Hexagon through an unverified lane.
Name declined families by architecture id, as the Ghidra and IDA
refusals do.
* fix(ghidra): name the pinned language in profile readback refusals
The session readback said "MIPS" whenever a provider-pinned profile
conflicted with the target or with REA_GHIDRA_LANGUAGE_ID, although the
RISC-V, PowerPC and DOL lanes are pinned too. The readback helper was
MIPS-named and re-parsed the language slot that
ghidraProfileLanguageOverride already owns. Move it beside that owner as
a family-neutral readback and name the pinned language id instead.
* fix(ghidra): list DOL among independently verified load images
GameCube DOL load images are independently verified, but the declined
format reason, the inspect_native_load_image capability note and the
Windows P0 guide listed only DOS MZ, DOS COM and raw-binary.
* fix(ghidra): count RV32E's sixteen integer registers
The EF_RISCV_RVE refusal described a 15-register ABI. RV32E has 16
integer registers, x0-x15, as docs/ghidra-riscv.md already says.
* fix(elf): name the refused e_machine
"unsupported ELF architecture" did not say which machine was refused.
Include the numeric e_machine, for example EM_XTENSA 94.
* test(hopper): assert declined-lane facts and owned refusal reasons
The MIPS case banned the phrase "not verified" instead of checking
facts, so wrong advice would still pass. Expose each declined family's
Ghidra lane status, assert those statuses, check that the advice depends
on the status, and compare Hopper's refusals with
hopperDeclinedArchitectureReason. The RISC-V refusal table now compares
the provider's reason with ghidraRiscvUnsupportedReason and expects the
EF_RISCV_RVE declaration name rather than fixed wording.
* test(ghidra): assert verified load-image formats as owned facts
The declined-format test required display phrases such as "DOS MZ" and
"GameCube DOL", so harmless rewording failed it while wrong guidance
containing those names passed. The format list was also spelled three
times: in the attestation's membership check, its reason and the
inspect_native_load_image capability note. Give the verified formats one
owner with the formatters for both texts, assert the list as a fact, and
compare the delivered reason and capability note with those formatters.
* fix(hopper): stop restating which Ghidra lanes REA has verified
Hopper's refusal for families it declines carried its own table of
which Ghidra lanes REA has verified (mips, riscv and powerpc verified,
hexagon not). That restated the Ghidra provider's facts in Hopper and
goes stale whenever they change, the drift that made the Hexagon refusal
wrong in the first place. Reading Ghidra's facts from Hopper would
couple the providers instead; no module in src/hopper imports from
src/ghidra.
The table and hopperDeclinedGhidraLane are removed.
hopperDeclinedArchitectureReason now states only what Hopper owns: it
does not admit the family, and the Ghidra provider's lane reports
whether REA has verified it. The arm64_32 sentence is unchanged.
The test checks every declined family against the formatter: each
reason names the family and Ghidra, and apart from the name all reasons
are the same text, so none states a per-family Ghidra status.
* fix(hopper): route declined families to Ghidra without promising its lane facts
The refusal said the Ghidra lane reports whether REA has verified it, but
Ghidra admits Hexagon with no such note, so a caller could not find the
promised status. Hopper now only states that it does not admit the family
and routes the caller to the Ghidra provider.
* test(mcp): save and reopen a GameCube DOL snapshot
#7290 reported that DOL Evidence could not be bundled or saved; 50822e1ef
fixed the bundle manifest and added bundle export/import coverage, but no
test saved a DOL session through close_binary. Save a DOL snapshot with
recorded Evidence, check its target and artifact manifest, then reopen it
and replay the read without running the provider again.
* test(binary): require every target format in each target vocabulary
A new target format must be added by hand to the Evidence subject,
bundle manifest, snapshot target and live summary vocabularies; missing
one caused #7290. Give every BinaryTarget format a fixture through a
mapped type, so a new format fails compilation until it has one, and
push each fixture through the producers that persist or project targets.
Caller-selected format hints must name executable target formats.
This is a guard until target formats have a single owner.
* test(mcp): assert the replayed DOL result and the owned replay limitation
The DOL snapshot replay test checked the subject and the provider-call
count but not the answer, so a replay returning null or another value
would pass. Compare the replayed normalized result with the original.
Name the replay limitation as SNAPSHOT_REPLAY_LIMITATION in the snapshot
cache that adds it, and assert that value instead of repeating the
sentence.
* test: list Intel HEX and S-record targets in the vocabulary guard
Upstream added the intel-hex and srec formats (#7136). The guard requires
a fixture for every target format, so after merging main it no longer
typechecked; both formats now have archive fixtures and pass the same
persistence and projection checks.
* test: list UnityFS among the target format fixtures
Upstream #7699 added the unityfs target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.
* test: list uImage among the target format fixtures
Upstream #7234 added the uimage target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.
* test: list the ESP image among the target format fixtures
Upstream #7498 added the esp-image target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.
* ci(ghidra): probe M-profile ARM and Xtensa decoding
The embedded plan relies on ARM:LE:32:Cortex, ARM:LE:32:v8-m and
Xtensa:LE:32:default, but CI only exercised ARM:LE:32:v7. Import
source-owned bytes for each language at 0x1000 with -noanalysis and
check how they decode from the language's default context: Cortex and
v8-m must start in Thumb state, v8-m must decode the ARMv8-M MSPLIM
register, and Xtensa must decode entry and retw.n.
Add the probe scripts the job runs, including the existing ARM context
probe, to the ghidra_bridge change scope.
* ci(ghidra): match decode-probe operands as complete operands
The operand expectation searched the instruction text for a substring,
so mrs/4/msp also accepted mrs r0,msplim. Compare it with each operand
Ghidra reports for the instruction, so only a complete operand matches.
The Cortex (msp, lr), v8-M (msplim, lr) and Xtensa (a1) expectations are
unchanged.
IDA refused MIPS, RISC-V, PowerPC, Hexagon and arm64_32 by denylist, so
any family nobody excluded was reported supported, and the label ternary
printed "Hexagon" for any other metadata family. Admit exactly the
families IDA admits today (x86, x86_64, arm, arm64) and name every other
family from its own architecture id. Families that Ghidra admits keep
the pointer to a Ghidra profile.
Refusal text now uses architecture ids, for example "no verified mips
lane" instead of "no verified MIPS lane".
* docs(raw-binary): replace the STM32-style ARM example
The headline raw example opened ecu.bin at 0x8000000, an STM32 flash
address, while the arm lane decodes in ARM state. Map a 32-bit ARM-state
image at 0x8000 instead, and state beside the example that Thumb-only
images, including Cortex-M firmware, are not supported.
* docs(ghidra): document an interim Cortex-M language override
Ghidra imports little-endian ARM ELF files as ARM:LE:32:v8, which
defaults to ARM state and marks Thumb code only from $t mapping symbols
and odd function-symbol addresses. Unpinned arm ELF already admits
REA_GHIDRA_LANGUAGE_ID, so document ARM:LE:32:Cortex (ARMv6-M, ARMv7-M,
ARMv7E-M) and ARM:LE:32:v8-m (ARMv8-M) as an unverified interim.
State the override's full effect while it is set: raw-binary, DOS, DOL,
PSP and language-pinned lanes such as RISC-V refuse it, and REA asks
Ghidra to import every other target in the ARM language, because it does
not check the override's processor against the target.
* fix(javascript): emit no http request for a target that is not a URL
`node:http` parses a string target with `new URL(...)` before a request object
exists, so a literal that is not an absolute URL throws `ERR_INVALID_URL`
synchronously. The analysis reported `endpoint` at `resolution: "complete"` for
those calls, and the graph then projected `constructs-request` as `resolved`.
Measured on Node 24.22.1:
http.get("nope") -> THREW ERR_INVALID_URL
http.get("example.test/api") -> THREW ERR_INVALID_URL
http.get("/api/items") -> THREW ERR_INVALID_URL
http.get("/api/items", { host }) -> THREW ERR_INVALID_URL
http.get("") -> THREW ERR_INVALID_URL
http.get("//example.test/x") -> THREW ERR_INVALID_URL
http.get("http://") -> THREW ERR_INVALID_URL
http.request("nope") -> THREW ERR_INVALID_URL
https.get("/api/items") -> THREW ERR_INVALID_URL
--- accepted ---
http.get("http://example.test/x") -> OK ClientRequest
http.get("HTTP://example.test/x") -> OK ClientRequest
http.get("http:/x") -> OK ClientRequest
http.get("http:///x") -> OK ClientRequest
http.get("http:x") -> OK ClientRequest
`URL.canParse` is the exact predictor of that branch, so a relative spelling such
as `"http:x"` keeps its operation. It already has precedent in this codebase
(`javascriptPackageImportAliases.ts`).
Two boundaries are deliberate. `fetch` and `new WebSocket` are host-supplied
globals: a browser resolves a relative target against the document URL, so their
spellings are not judged by Node's absolute-URL rule. A protocol mismatch such as
`http.get("ftp://...")` is also left alone because an options object can rescue
it, so it is not a provable throw.
The guard is scoped to `method === "get" | "request"`, which only
`requestMethod`'s `http`/`https` builtin branch can produce.
* chore: retrigger title and readiness checks after the title was corrected
* test(ci): require provider coverage and reuse test sessions
* fix(ci): prepare the runtime before Python producer tests
The fast Python job ran the suite on a fresh checkout, so analysis-efficiency
checks failed without compiled process modules. Install locked dependencies
and prepare the runtime before the suite.
* fix(ci): keep provider test reports inside the checkout
The provider-report reader accepted an arbitrary path from argv. Resolve the
path and reject anything outside the checkout before reading it. The packet
and signal workflows write those reports under .cache.
* fix(ci): require a test module in each named Python bridge family
Discovery still runs every bridge test. The mitmproxy, native, and pwndbg
families must each keep a test module, so deleting one cannot pass through
the others.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(hopper): distinguish an absent demo dialog from publisher rejection
The Linux demo watcher collapsed an admitted launcher with no first-run dialog into unsupported_demo_dialog. Report that post-authentication timeout as demo_dialog_absent, keep the unsupported dialog code for an observed but unrecognized Hopper window, and allow REA_HOPPER_DEMO_DIALOG_TIMEOUT_MS to widen the fixed wait. Closes#8008.
* fix(hopper): tolerate callers without the dialog timeout argument
* fix(hopper): report an invalid demo dialog timeout as its own failure
An invalid REA_HOPPER_DEMO_DIALOG_TIMEOUT_MS exited 73 and was shown as
invalid_launch_command. Exit 82 is invalid_dialog_timeout. The public error
names the setting, shows the rejected value and source, lists the allowed
range, and says to restart REA.
Drive the demo watcher with controlled windows and time. No window exits 81,
a Hopper window without a supported dialog exits 76, and a dialog that arrives
after the old 10 second limit is still accepted.
* fix(hopper): keep startup stderr capture on AdapterFailure
DemoWatchStop was inserted above with_stderr, so launch failures called a
method they no longer had and became unexpected_failure. Restore with_stderr
on AdapterFailure and reject a watcher fixture that cannot capture stderr.
---------
Co-authored-by: Cursor Agent <[email protected]>
* perf(javascript): index member-effect paths for projected reads
Each projected read scanned every member-write record of its binding and
tested the record path against the selection. On the #7774 reproducer
only 3.7% of scanned records pass that test. Index each binding's
append-only records by path in a lazily built trie that extends itself
with newly appended records, and visit only records whose exact keys a
selected choice names or whose keys are wildcards. Candidates come back
in record order and still pass the exact predicate, now owned next to
the index as semanticEffectPathMatches.
The semantic IR and the complete analyze-javascript-application Evidence
are byte-identical. On the full reproducer each worker phase drops from
about 80 s to 29 s, and to 8 s together with #8103.
Refs #7774
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(javascript): stop escape checks at the first indexed match
The escape check only asks whether any matching escape is unsuperseded,
so it does not need ordered candidates. Share one trie traversal between
the ordered write candidates and an early-exit visitor for this check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(native): open extension, framework, XPC and plug-in bundle directories
open_binary and the CLI inspection commands admitted only .app
directories and pointed every other Apple bundle at the JavaScript
analyzer. Resolve .appex, .framework, .xpc, .systemextension, .bundle
and .plugin directories through their Info.plist CFBundleExecutable, in
the Contents or flat root layout, plus the versioned framework layout
under Versions/Current. Exactly one layout may declare the bundle and
every resolved path must stay inside it.
Unresolvable bundles fail closed with typed constraints, probed paths
and the Info.plist that was read. Other Apple bundle types are refused as
unsupported instead of being offered the JavaScript route. Resource-root
discovery shares the same layout rule, so inspect_plist reads a
versioned framework's Info.plist. The recognized bundle vocabulary now
has one owner in the domain.
Refs #4310
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): refuse kernel extensions and state bundle error fields
Name the Apple bundle types binary routes recognize but do not open
(.dext, .kext, .qlgenerator, .mdimporter) explicitly, so a kernel
extension is refused as unsupported instead of receiving the JavaScript
route. Document which bundle refusals carry probed_paths and info_plist,
and make success tests throw the typed admission error before asserting.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(native): open flattened frameworks through Resources/Info.plist
Electron and Gecko apps ship frameworks without Versions/: the program
file sits at the framework root beside Resources/Info.plist, the layout
#8101 taught bundle anatomy. Read that layout for a framework without
Versions/, and keep the versioned layout for one with it, so a versioned
framework's root Resources link is never read as a second declaration.
The resource root reports it as flattened-framework.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Member-mutation indexing appended a binding write or escape record for
every traversal state that reached it. States that differ only in
coverage fields the record never stores (origin, owner initializer,
getter widening) produced identical records, and each later receiver
query rescanned all of them. Record each stored fact once, alongside the
existing required-module effect deduplication. Propagation is unchanged.
Effect ordering also re-walked the same top-level statement ancestry for
every candidate record. Cache it with the other initializer positions,
and test the selected path before the costlier receiver comparison.
On the #7774 reproducer the semantic IR and the complete
analyze-javascript-application Evidence are byte-identical, while the
CLI run drops from 146 s to 40 s.
Refs #7774
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(ci): remove unused exports that fail deep static checks
Knip on main reports three analysis-view hub re-exports, two type
re-exports, an unused artifact-format guard and an alias constant. Drop
the dead re-exports and guard, and give the debugger control-reply budget
its own literal: it is a separate budget from the caller command-output
default, as its comment already states.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(config): read the Rizin control budget from its own constant
The Rizin control-reply admission limit still took its value from the
caller command-output default, while GDB used DEBUGGER_CONTROL_REPLY_BYTES.
Read the dedicated constant in both so the control budget has one owner.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(electron): keep literal IPC sends ambiguous beside unresolved handlers
A main handler on an unresolved channel can receive any literal renderer
send of a compatible mode, but pairing ignored it unless it had a scoped
receiver. The send was then reported unpaired, or paired with another
handler. Treat an unresolved-channel handler as a competitor that never
pairs.
The wrapper pass seeds only calls with a literal first argument, so it now
records which invocations of a dynamic site it traced. The syntax finding
gains `invocation_channels: "literal"` only when every invocation is a
traced direct call of an unexported local function supplying a literal
channel. Such a site is already represented by its per-invocation findings
and does not compete.
Fixes#8118
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(electron): keep guarded wrapper registrations competing in IPC pairing
A guarded wrapper registration keeps its predicate on a call path and has
no IPC finding. It still counted toward `invocation_channels: "literal"`,
so pairing lost it: a send on that channel was reported unpaired, or
paired uniquely with a direct handler. The enumerated site now carries
these channels as `guarded_invocation_channels`, and each one competes for
its own channel without pairing.
Index each binding's direct call sites by appending in place. Spreading
the earlier calls on every reference was quadratic in a file's call count.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(apple): charge the XML plist decoder's DOM before decoding
The XML preflight charged 4 bytes per character and 256 bytes per start
or end tag. The decoder builds an @xmldom/xmldom DOM that measured
1,000-1,160 bytes per element and up to 240 bytes per text, comment or
CDATA node, which were not charged at all. A 1.75 MB plist of 230,000
<true/> lines was admitted under the 64 MiB decode budget and peaked at
249 MiB of heap; shipped Apple plists peaked at 80-104 MiB.
Charge each element and each text, comment, CDATA or DOCTYPE node at a
measured upper bound, keeping the character and number-projection terms.
The binary estimator already bounds its decoder and is unchanged.
Fixes#8113
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(apple): charge every XML element, attribute and reference
The XML preflight recognized only element names starting with an ASCII
letter, so names such as é, _, a CJK or astral character escaped the
element charge: 100,000 <é/> elements (0.5 MB) built an 84 MiB DOM before
the plist decoder rejected the tag. Attributes and entity or character
references were also uncharged; 20 attributes per element measured about
210 bytes each, and each reference about 26 bytes beyond its characters.
Classify markup by the character after "<" instead of a name pattern, so
anything other than an end tag, comment, CDATA, PI or declaration counts as
an element (saxes rejects malformed markup before the DOM is built). Charge
each unquoted "=" in a start tag as an attribute node and every "&" as a
reference.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(apple): charge XML references only where the parser expands them
The previous commit charged every "&" in the document as a reference,
including literal ampersands inside CDATA sections and comments. A valid
plist with one CDATA string of 1,000,000 ampersands was refused although
it is a single node the decoder holds as one string.
Count "&" during the existing token walk, only in text runs and in the
attribute values of start tags, so comments, CDATA, PIs and declarations
add no reference charge and the scan stays a single pass.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(captures): admit browser-serialized HAR request URLs
[email protected] checks request.url with ajv-formats' strict RFC 3986
`uri` format. Browsers record and send URLs as WHATWG serializes them,
which keeps characters such as `|` and `{` unescaped in the query, so one
Google Fonts or GraphQL GET entry made inspect-web-network-capture and
environment-build reject the whole HAR.
HAR 1.2 defines request.url only as the absolute request URL. Own that
correction in the HAR validator: every URL the RFC 3986 check accepts
stays accepted, and a URL that WHATWG URL serialization reproduces
unchanged is accepted too. Spellings WHATWG would rewrite (a backslash
authority, a tab, a space) and relative references still fail.
Fixes#8110
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(captures): state that RFC 3986 request URLs stay admitted
The HAR request URL rule is "passes RFC 3986, or WHATWG serialization
reproduces it unchanged". A spelling WHATWG would rewrite therefore fails
only when it also fails the RFC 3986 check; an uppercase host still
passes. Say so in the docs and the validator comment, and pin the
accepted uppercase-host case in the CLI test.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
The CommonJS loader search treated any repeated state on its own ancestor
chain as an unknown loader. A self-default such as
`session.messages = session.messages || []` forms that cycle, because a
member read inside the assigned value was attributed to the later call's
read site and selected the same write again. Each `push` on the value was
then reported as an unresolved `require` load, and graph coverage became
partial.
Record a member read inside a flowed value as a depth-1 captured selection
at its own position, as member mutations already do for flowed origins.
Treat a revisited state as adding no source; the cycle's other entries are
still expanded once each. Remove the superseded ancestor tracking.
Fixes#8102
Co-authored-by: Claude Opus 5.5 <[email protected]>
webpack 5 and tsc write "sourceRoot": "" for map-relative sources. Since
5f702dd6c the shared join added "/" for any non-null root, so these
sources resolved from the URL root: rxjs's ../../../src/internal/Observable.ts
under /npm/[email protected]/dist/cjs/internal/ became /src/internal/Observable.ts,
and xterm's webpack://xterm/./src/... became https://host/webpack://xterm/....
Treat an empty root like a null root in sourceMapReference, the single join
used by trace-web-source-location, analyze-javascript-application and the
web source-map fetcher. Chrome, Firefox and trace-mapping skip an empty root,
and tc39/ecma426#183 wrote the current steps to match them. Non-empty roots,
including "/" and repeated slashes (#6726), keep their existing behavior.
Fixes#8106
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(desktop): charge retained rows at their measured representation
The desktop scan reserved 1024 + 24 bytes per path character for every
row, omission and the root against its 32 MiB representation budget. Real
rows cost 800-940 heap and 490-640 JSON bytes, so the charge overstated
them 6-13 times and large macOS bundles stopped at 3,700-7,200 entries,
far below max_entries.
Charge each path at the larger of its UTF-16 heap size and its escaped
JSON size, plus the existing fixed row and omission overheads, in one
helper. The bound still covers the retained heap and the serialized
report, including control-character paths that expand six times in JSON.
Fixes#8104
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): charge every retained copy of a path
The tighter row charge counted each path once, but rows repeat it in
convention-fact values and the next probes those facts offer, and host
failure issues name the absolute path. Control-character paths under a
convention such as Content/Paks or resources/app.asar therefore serialized
47-80 MiB of reports within the 32 MiB budget.
Reserve each copy through the same text helper where it is stored: path
convention facts with their row, the probes any fact offers (at most two,
pinned by a projection test) with that fact, retained read ranges before
their read, and probe failure issues before they are recorded.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
A macOS framework without Versions/ keeps its binary, _CodeSignature/ and
Resources/Info.plist directly in the .framework, as Electron and Gecko apps
ship it. Bundle anatomy looked for a shallow bundle's plist only at its root
and reported info_plist_path: null under complete coverage.
Resolve a shallow bundle's root Info.plist first, then Resources/Info.plist,
the same layout a framework version directory uses. Keep the iOS platform
rule tied to a root-level Info.plist so a Resources/-only application root
is not classified as iOS.
Fixes#8100
Co-authored-by: Claude Opus 5.5 <[email protected]>
createAnalysisViewEvidence chose view confidence from the view result kind
only, so every summary, page or item view of an inferred parent
(compare_source_to_bundle, inspect_evm_interface, partially resolved
inspect_il2cpp_metadata) was sealed as derived while keeping the parent's
authority. A projection never makes its facts more certain: an inferred
parent now yields inferred views through CLI and MCP alike. Observed and
derived parents keep their existing view confidence and identities.
Fixes#8098
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(kuna): report document failures as typed codes
Kuna JSON, graphs, xrefs, spans, and command checks threw Error and
published the sentence. Those refusals are now KunaDocumentError codes
with the field or path that failed. Output errors keep the code as
reason and the same facts in diagnostics.
* fix(kuna): keep incoming-reference address fields
---------
Co-authored-by: Cursor Agent <[email protected]>
* refactor(javascript): break the semantic analysis import cycle
Split shared semantic facts out of the analysis hub so projectors of
effects, evaluation, and identity no longer import one another in a cycle.
* refactor(javascript): share the import specifier reader
Read static imports in one module used by the boundary checker and both
cycle guards, so those rules cannot drift apart.
---------
Co-authored-by: Cursor Agent <[email protected]>
* refactor(analysis-view): break the analysis view hub cycle
Move request fragments, the request union, and the result union out of
analysisView.ts so projectors no longer import the module that imports them.
The dispatcher re-exports the public schemas and seals projector results.
Co-authored-by: morluto <[email protected]>
* fix(analysis-view): check inferred projector results
Require each projector result to match the unsigned result union before
sealing it, and parse the cycle guard's imports with Babel so comments
and side-effect imports are not misread.
* fix(analysis-view): close IL2CPP and Android symbol view inputs
Admit those projectors through AdmittedView and check test selections
against their request schemas, so misspelled fields fail at the call site.
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(debugger): share GDB and Rizin session caps as REA_ settings
Move the duplicated deadline guidance into the owned debugger helper and
replace the shared session, queue, output, and 120s command caps with
launch settings that name their value, source, and range.
* test(gdb): assert the command-output setting on truncated exit evidence
The retained-output limitation now names REA_DEBUGGER_MAX_COMMAND_OUTPUT_BYTES
instead of the old fixed-cap sentence.
* test(gdb): assert command-output limit fields on truncated exit
A truncated GDB exit now carries the command-output setting, value, and
source on the cleanup error. The coverage test reads those fields.
* fix(debugger): keep control replies and evidence hashes inside their budgets
Point the session-cap mutation target at DEFAULT_DEBUGGER_MAX_SESSIONS.
Give startup and cleanup replies a fixed 16 MiB budget so a small caller
output cap cannot truncate them. Lower the command-output maximum so
escaped Evidence JSON stays under Node's string ceiling, and keep GDB
startup timeout diagnostics when the error is rebuilt.
* test(debugger): throw typed Result errors before success checks
Success cases no longer assert ok and return. They throw the typed
error first, then check session, deadline, and close fields.
---------
Co-authored-by: Cursor Agent <[email protected]>
* refactor(admission): own inspector, web, and CLI state per host
Replace the process-global inspector, web-bundle, guidance, and JavaScript
counters with objects a host passes in, so independent instances do not
share admission or cleanup.
* fix(admission): keep worker and file cleanup on the host
Web-bundle and JavaScript workers retry only their host registry, and
each Inspector admission authorizes and closes its own file scope.
Browser providers can share one web-bundle admission.
* test(inspector): gate one admission's file cleanup
The isolation test delayed the first ArtifactResourceScope.retryCleanup
call, so a second call on a shared ElectronFileScope still finished.
Hold first.files.retryCleanup until the other admission's close
finishes, then release and join before restoring the method.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(domain): replace bare parser errors with typed codes
OpenStep syntax, canonical JSON digests, managed-graph source checks,
frame fingerprints, and reference-flow invariants threw Error and
published the sentence. Each refusal now carries a code. Tests assert
that code, including OpenStep position, instead of the previous prose.
* test(domain): pin OpenStep refusal codes and positions
The syntax cases accepted any code and any offset. Each container
refusal now expects its code and source position, including the deep
suffix that changes (x from unterminated_array to expected_separator.
Scalar failures stay identical to the pinned decoder.
* test(filesystem): assert OpenStep keyed-archive codes
The deep-suffix keyed archive cases compared ArtifactReaderFailure
message text with the pinned decoder sentence. They now require
reason format and the OpenStep code and source position on the cause.
---------
Co-authored-by: Cursor Agent <[email protected]>
* refactor(adb): move device and client operations out of the provider switch
Keep AdbProvider as the session owner. Device listing, device inspection,
and client identity now run in AdbDeviceOperations, and shared listing
execution construction lives next to the client identity result.
Co-authored-by: morluto <[email protected]>
* refactor(adb): use the shared client and provider identity types
Drop the ClientIdentity and ProviderIdentity aliases so method signatures
name AdbClientResult and AdbProviderIdentity directly.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(errors): store resource-constraint recovery as one shape
AnalysisResourceConstraintError kept remediationAction, fixedAllowance,
and retryable as independent optionals, so a refusal could claim both a
fixed allowance and a separate remediation. The constructor now stores
the existing ResourceConstraintRecovery union, and the three accessors
read that object.
* test(errors): cover competing resource-constraint recovery fields
A runtime options object can still carry both a fixed allowance and a
remediation action. The constructor keeps the allowance and drops the
other field; the regression now supplies both.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(native)!: report NativeAOT failures as reason codes
PeImage admission failures and parser recoveries used to publish
Error.message as NativeAotReport.reason. The report is now a union on
status, and reason is a typed code with the fields that code needs.
Complete reports keep reason null and a located header. Partial and
not_applicable reports carry a code. Caller budget mistakes throw
AnalysisInputError or AnalysisResourceConstraintError with a diagnostics
code instead of a bare Error.
* fix(native): keep out-of-domain hydration pointers partial
An inline pointer outside the unsigned 64-bit domain made
writeBigUInt64LE throw RangeError, and the hydration catch rethrew it
instead of recording a partial report. Those targets are now an
admission detail. The load-image summary schema still accepts the
optional Ghidra extension's string reason.
---------
Co-authored-by: Cursor Agent <[email protected]>
* refactor(artifacts): describe formats in one registry
Reader construction, classification retention, container role, and
extraction now read one descriptor per domain format id. unknown has
no reader, and zip package families share one capability row.
* fix(artifacts): keep reader presence in format descriptor types
Descriptor helpers no longer widen every format to an optional reader,
so ArtifactReaderFormat stays the union of formats that construct one.
---------
Co-authored-by: Cursor Agent <[email protected]>
* refactor(contracts): register CLI and MCP tools by family id
Walk the published tool-family list once for each adapter so a new
family id must supply both a CLI registrar and an MCP registrar.
* fix(cli): keep family registration off the contract tables
Walk family ids from the startup-safe list, and register the web script
commands with the application family that owns their MCP contracts.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(adb): verify pushes to long device paths
sha256sum echoes the device path, so a correct digest line for a path
near 1 KiB exceeded the fixed 1 KiB capture budget. The resulting
resource failure was then reported as device_digest_source "unavailable",
the same outcome as a device without sha256sum. Size the budget to the
path, and return a capture-budget failure as a resource constraint
instead of an unverified success.
Fixes#3369
* Keep adbDeviceAnalysis.ts as on main
* test(adb): keep only the long-path digest test
Main already covers an oversized checksum answer as a resource
constraint with the completed push retained.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(native): preserve Mach-O target selection in macOS tools
* fix(native): retain exact Mach-O slice signature coverage
* fix(native): admit x86_64 LIB64 signature selectors
* test(native): match signature checks to actual slices
* fix(native): recognize vtool refusals on private Mach-O aliases
Match vtool's container refusal to the operand the command received so a
parenthesized universal path keeps completed facts and the refusal
limitation. Expect codesign -a only when the target format is universal.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(session): align resource-only status and snapshot recovery
* fix(session): report independent workflows during resource sessions
* test(mcp): honor SQLite runtime availability
* test: compare resource snapshot guidance with its formatter
Assert projected error codes and the owning remediation action for
resource-session snapshot refusals, and type MCP error text as a closed
content item.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(hopper): reconcile observed caller relationships
* fix(hopper): retain completed caller scans on failure
Attach the native reply and collected forward facts when reconciliation
fails or is cancelled, and check scan stop codes separately from their
wording. Live observations keep REA evidence ids rather than provider
supplied ones, and the real Hopper verifier uses advertised MCP schemas.
* fix(hopper): answer caller-scan methods in the package fixture
The packaged Hopper stand-in echoed unknown bridge methods, so
analyze_function rejected rea_caller_scan_target as an invalid scan
target and the native package baseline returned unreadable_output.
* fix(hopper): share caller identities in failed scan observations
Failure partials repeated each caller identity under every destination.
The page budget counted compact replies, then schema parsing copied the
expanded form. Identities are stored once and destinations cite addresses.
* refactor(config): own the Hopper caller-scan budget default
The environment schema hard-coded the 2000 ms scan default. The domain
limit owner now names that millisecond budget and the schema reuses it.
* test(mcp): assert bundle path issues by field
compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.
* test(flutter): assert ABI selector refusals as structured issues
inspect_dart_aot reports the ABI label as an issue on path abi. The
message is the selector contract, without an abi: prefix.
* fix(session): retain caller observations that cannot be replayed
A missing document selector blocks snapshot replay. Backward call-graph
and trace reads still keep the native result and forward observations,
and the workflow Evidence links those records.
* fix(session): keep decompile bodies out of the in-memory ledger
Caller and callee walks still stay in Evidence without a document.
procedure_pseudo_code does not, so decompile_all leaves each body in the
file-backed export instead of retaining the whole export in memory.
* fix(hopper): own caller-scan recovery and the page byte allowance
Scan facts stay in the domain. Environment recovery, including the time
budget bounds and the fixed allowances with no runtime override, is
appended from configuration. Setup forwards the budget, and the bridge
uses one byte allowance for instructions, native callers, and pages.
* fix(hopper): retain compact caller references within the scan budget
Recovered call sites stay in the address form the scan already counted.
Expanded identity copies are measured before allocation and refused when
they exceed the retention budget. Instruction lists are not part of the
retained reply.
* fix(session): omit instruction payloads from retained caller evidence
Documentless caller and callee reads stay in the ledger for the session.
Their evidence keeps call sites and drops instruction lists the scan
already consumed.
* fix(hopper): keep the owning caller address on each compact call site
A page reference marked source_procedure "caller" belongs to that page
item's caller. Saved sites record that address, and an owner missing from
the callee inventory is stored once instead of copied onto every site.
* fix(session): leave Hopper reply shaping to the Hopper adapter
Documentless caller evidence stores the adapter's raw result unchanged.
The session no longer reads native_result.instructions.
* test(hopper): throw typed scan failures before reading results
Successful caller-scan results now throw the typed error instead of
returning after an ok check. Expected failures assert structured tags,
stop codes, and reported limits.
---------
Co-authored-by: Cursor Agent <[email protected]>
* feat(artifacts): inventory and extract Bun standalone executable module graphs
Adds a Bun standalone artifact reader for Mach-O, PE, ELF and appended
trailers, supporting the 36-byte and 52-byte module record layouts. It
preserves module names, entry-point details and raw metadata, and
exposes contents, sourcemaps, bytecode and module-info as separate
entries for safe extraction without executing the binary. Parsing
rejects malformed or ambiguous graphs and enforces module-count,
retained-byte and expansion limits. Adds synthetic parser tests, CLI and
MCP coverage, agent skill guidance, and a verification script for
Bun-produced executables.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe
* fix(artifacts): bound Bun section probes and reject truncated sections
Split Bun recognition into a bounded probe that reads only section
headers, length prefixes, and the Offsets/trailer suffix, and a single
payload load used by the reader. Inventory classification and extraction
admission no longer allocate the whole graph.
Match ELF `.bun` section names by comparing only the name bytes and
terminator, so many headers sharing one long name cost constant work each.
Reject sections whose declared extent ends past EOF instead of reading a
silently shortened window.
Launch Bun, Docker, and REA in the standalone verifier through the shared
owned verifier commands, and release the runtime root only after those
command families have settled.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe
* style(artifacts): format Bun reader integration
* fix(contracts): describe static Bun inventory facts
* fix(artifacts): bound Bun metadata reads and combined path growth
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
* fix(cli): report incomplete debugger cleanup as typed outcomes
debug-with-gdb and debug-with-rizin hand-rolled final cleanup in
try/finally blocks: a failed or interrupted final close only logged
and assigned the process exit status, so callers still received
complete successful Evidence with no typed error or partial
observation.
Both commands now route through the shared close owner used by the
other one-shot CLI commands (deliverCliAnalysisEvidence /
finishAnalysisCommand) via a small closeDebuggerCommandSession
helper. A rejected closeAll projects as ProviderCleanupError, which
the owner turns into cleanup_incomplete retaining the completed
Evidence as partial observation; a signal observed after finalization
projects as cancelled with the same retained observation. The
per-session close failure still only logs, preserving the rule that
the retained owner's closeAll decides completion.
Fixes#7378. Fixes#7429.
* test(cli): reject unmodeled GDB requests in cleanup regressions
* test(cli): match modeled GDB requests exactly
* Update tests/boundary/cli/gdbStartupCleanupCli.test.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---------
Co-authored-by: morluto <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix(firmware): preserve symlink target bytes
* fix(apple): preserve undecodable dylib link targets
* style(tests): fix CI formatting
* test(firmware): select the named contract directly
* test(firmware): record raw bytes for a decodable symlink target
* test(mcp): assert bundle path issues by field
compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.
* test(firmware): stat raw links without following them
access follows a symlink, so a published dangling raw-link still looked
absent. lstat and readlink inspect the link itself.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(cli): preserve literal Markdown rendering
* fix(cli): retain literal Markdown attributes
* fix(cli): preserve literal punctuation in markdown
* test(cli): update escaped empty object expectations
* style(cli): format the literal empty-object Markdown expectation
* test(mcp): assert bundle path issues by field
compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.
* test(flutter): assert ABI selector refusals as structured issues
inspect_dart_aot reports the ABI label as an issue on path abi. The
message is the selector contract, without an abi: prefix.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(javascript)!: preserve operator influence in semantic traces
* test(javascript): align semantic influence regressions
* style(javascript): format semantic IR
* test(javascript): follow literal argument seed influence
* fix(javascript): keep one literal node per operator operand
Reuse an existing literal for operator influence, and treat TypeScript
assertion wrappers as transparent when deciding whether a call result is used.
* test(mcp): assert bundle path issues by field
compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.
* fix(javascript): index operator literals by range and initializer
Repeated string operands rescanned every retained literal and every
initializer flow. Lookup now uses those indexes, and a 1200-declaration
file still projects one literal per operand.
* fix(javascript): keep literal and call facts on their own occurrences
Operator literals match their own source range, so a nested function does
not inherit the enclosing declaration's literal. A direct argument still
reaches the call, while the assigned result follows modeled returns.
for-loop initializers and updates are discarded call results.
* test(javascript): expect conditional operand literals beside binding values
A conditional binding keeps one candidate literal per union value and one
operator literal per operand token. Those are different facts, so the
aggregate payload check counts four nodes and compares literal seeds in
query order.
* perf(javascript): index call return flows by call site
projectCalls filtered every call return for each assigned result. Group
those flows once while projecting returns-to-call edges.
---------
Co-authored-by: Cursor Agent <[email protected]>
* docs(apk): clarify rotated signer agreement
* test(mcp): assert bundle path issues by field
compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.
* fix(javascript): ignore worker frames for a different source job
A failure frame for another job was dequeued as an output error, so a
later caller abort lost the race and was not reported as cancellation.
* test(flutter): assert ABI selector refusals as structured issues
inspect_dart_aot reports the ABI label as an issue on path abi. The
message is the selector contract, without an abi: prefix.
* fix(javascript): check each worker frame condition once
A ready frame is rejected on its own. A non-ready frame for another job
keeps waiting. The following id comparison repeated that skip, so it
never ran.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(browser): preserve source-map declaration uncertainty
* style(tests): fix CI formatting
* fix(browser): retain known source map differences
* fix(web): bind map comparisons to script metadata
* fix(browser): match source maps with partial script metadata
Keep each script's observed hash and declaration constraints instead of
dropping a metadata field for the whole URL group.
* test(flutter): assert ABI selector refusals as structured issues
MCP input validation reports the ABI path and the schema message separately.
The old concatenated "abi: ..." sentence is no longer the delivered error.
* fix(web): prove large source-map changes without factorial search
Groups larger than eight scripts were reported as unexplained, so nine
x.map declarations plus one omission compared as unknown against ten
y.map declarations. Declaration counts reject impossible placements, and
augmenting paths assign the rest without retrying identical scripts.
* test(mcp): assert bundle path issues by field
compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.
* fix(web): match repeated source-map scripts by count
Identical declarations share one group, and a shared omission pool is
counted once. A 2000-script self-compare no longer builds a dense
assignment graph.
---------
Co-authored-by: Cursor Agent <[email protected]>
* feat(environment): serve the writes callers make without recorded form fields
- A create member a recording sent empty ("", null, false, [] or {}) is no
longer required; a call that leaves it out is applied as sending that value
(body.blank).
- List views render envelope members that list the listed entities' ids
(Paperless's `all`), so creates in such collections are confirmed.
- Updates move their entity in collections kept sorted by a field they write,
and a failed whole-collection read after an update can teach that order.
- A later create that stores an unruled field otherwise is withheld at the
create, and an item read may fill values a create's answer had not loaded.
- An unsent field respelled between null, [] and {} is unchanged.
- The skill asks for one create with every field and one with only what a
person must give.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skill): show how to read signed-in pages before recording
Agents guessed capture keys (artifacts, dom, final) to see pages behind
sign-in, which inspect_web_page cannot reach.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): accept every empty spelling recorded creates sent
A create that fills only what a person must give sends `null` where the
full create sent a string, and `null` or `false` for the same flag. The
merged plan now accepts each recorded empty value, and a call that leaves
the member out is applied as the earliest call sent it.
Also adds the spec folder for this change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skill): keep the vary-fields step neutral on UI or API writes
Agents read "the form" as a cue to write through the UI, where some
applications' request bodies are not exposed; a body-unusable UI write
now re-records once with request steps.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record verification for blank members and sorted writes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): address review of blank members and sorted writes
- Bulk updates move their entities in turn against the others' earlier
values and install the final order once, as replay does.
- A member's recorded empty values stay accepted once a later call teaches
its default; a left-out member with a default keeps the default.
- Write answers spell empty lists and maps as their recorded answers did.
- A rename can teach a sort when the creates chose one by a field the
update did not write, such as ids that ascend with titles.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): serve the writes that later reads explain
- An item read whose object is marked deleted (deleted, isDeleted or
is_deleted true) agrees with an absent entity, as EspoCRM answers a
deleted record; such an object never seeds or adopts an entity.
- maxSize, max_results and maxResults are page sizes, for whole-collection
list lines and the list query's limit role.
- An item view also shows the fields only reads after its type's first write
show, when no initial entity holds them (Vikunja's task subscription),
without requiring them; write answers leave such a field out for entities
without it, and it no longer withholds the write as incomplete-entities.
- The skill asks updates to change the fields a person would change, and
expects a deleted object to read back as 404 or marked deleted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer sent empty members as sent, and keep a placed create's order
- A write answer spells an empty list or map the call did not send as its
recorded answer did; one the call sent is answered as sent, so a create
sending `[]` is not answered `null` because the first recorded create was.
- A read listing the whole collection that placed a create leaves the create
only the sorted inserts whose order that read follows, so a later rename
cannot teach a sort an earlier read ruled out.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer bulk updates without fields only later reads show
A bulk update's answer renders each entity with the same allowance a single
update's answer has: a field only reads after the type's first write showed,
which no initial entity holds, is left out for an entity without it, so a
bulk update of a seeded task is no longer refused for lacking it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): correct how the EspoCRM contact update task passes
The agent trajectories show gpt-oss passes it by creating a replacement contact
and deleting the original, since the served update refuses emailAddress; the
update itself is refused in both builds.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer a read of a served-deleted entity marked deleted
An application that soft-deletes still answers a deleted record by id, marked
deleted. Serving now keeps an entity a served delete removed from a type whose
recorded item reads show that, until a reset or load and within the size
data/add loads, and answers a read of it with its last fields and the flag
true, instead of refusing the read.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep unloaded create answers, check bulk answers with sent members, narrow inserts only on collection-order reads
- A create whose item read loaded values its answer had not records them
(`observation.read.unloaded`), and a served create's answer keeps them as
the recorded answer showed them, a zero-time owner included, while the state
holds the read's.
- Replay checks a bulk update's recorded answers with each target's sent
members, as serving answers them, so echoed `null` and `[]` no longer make
the plan body-unusable.
- A whole-collection read whose query sorts it shows the query's order, so it
no longer narrows a create's sorted insert candidates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the census with main's unknown-clock rule merged
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): serve query numbers, counted values and made objects
- A query parameter accepts a number or boolean, which serving sends as its
JSON text, as agents send page sizes and flags.
- A sequence may start at the value its first create took above every value
its type held (Vikunja's per-project task index), and a later create's
composed, template or started-sequence rule is adopted when it also spells
the first create's value.
- A made-object rule explains an object the application makes with each
created entity: members the request stamped, members holding the entity's
own id, and counts one past the held objects' values.
- A create's answer names the id of the one object a one-element array member
holds, so bulk creates link to their type.
- The skill reads each listed object by id before writing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): let a linked create take path values it stores
A create linked to its type's own collection, whose path has no parameters,
accepts any path values when its plan stores each one in a field, as a Vikunja
task stores its project; replay and serving apply it alike.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn a sort from a rename only when moving its entities reaches it
Serving moves only the entities a write changes in a collection kept sorted,
so replay now tries a sort after a rename only when the entities no write the
read exercised changed already follow it. Seeded folders listed out of title
order no longer teach a title sort that serving would answer with another
order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep unloaded create answers a sent value did not echo, and learn no sort from query-sorted reads
- A served create keeps a value its recorded answer had not loaded unless the
call sent that member and the plan stores it as sent, so a create sending
`active: false` is answered `false` as recorded while the state holds the
read's `true`.
- A rename no longer teaches a sort from a whole-collection read its query
sorts, whose ties need not follow the collection's own order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): verify query values, counted values and made objects
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn the soft-delete flag in the state view and serve by it
Extraction now records the member later item reads set true on a record the
application soft-deleted, and the item view carries it as `deleted_flag`.
Serving reads that fact instead of scanning recorded bodies itself, so one
owner decides what a soft delete looks like.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the call-values checks at #727493821024
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn the soft-delete flag from item reads with a query too
A client may read a deleted record back only with a query, such as a
cache-busting parameter. Every successful read of the item tool now shows the
flag; only plain later reads still add fields.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the call-values checks at #727445832406
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn sorts only from orders a list read confirmed
- A sort retry after a failed whole-collection read requires the entities no
write changed since a list last showed them to follow it already, rather than
excusing every entity the update's tool ever wrote: an earlier rename a list
confirmed in place stays where it was.
- An update takes its creates' sort only from a create a list read showed
placed; replay still tries a sort a create guessed from the initial order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): drop entities read back as deleted and answer soft deletes as deleted
- A held entity a later read shows marked deleted is dropped, not adopted with
the marked object's fields.
- Bulk-update replay renders answers with the optional and later-only fields
serving uses, so a bulk update of created and seeded entities replays.
- A served delete of a soft-deleting type answers with the entity marked
deleted, single and bulk.
- A deleted flag only query reads showed is a field of the item view, so a
served tombstone keeps it where live records omit it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): check made objects as recorded and keep their ids and clocks honest
- A made object's clock counts as a write clock, so a capture with no clock
anchor withholds the create instead of stamping 1970.
- Members holding the entity's identity follow the id written, generated or
sent, through the type's id field.
- A later create's made object is checked as recorded, before its own members
are rewritten, so one naming another entity withholds the plan.
- A later create's template is adopted only when it spells that create's value
too, not just when the first create's text holds its source value.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): move updated entities only by placed sorts and keep query-sorted reads from confirming order
Replay moved an updated entity by a sort a create only guessed, while the
update plan exported only placed sorts, so a read could confirm an update
by a move serving would not make. Both now use the placed sort. A passing
list read clears its entities from the moved set only when the state
decides the order of every entity it lists, so a read whose query sorts
it no longer hides an earlier move from a later sort check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): count six withheld plans that explain more, not seven
The bulk task create counted among them is served on this branch.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): try a learned sort by moving each pending write in turn, as serving does
#retrySort sorted the whole collection, which a list could match while
serving, moving each renamed entity in turn against the others' values
before their own writes, gave another order. The state now records the
values each write replaced, and the retry replays the writes since a
list in collection order last showed their entities.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer a served delete with the deleted flag its recorded answer showed
An application may answer a delete with the record as it was and mark it
deleted only on later reads. Delete answers, single and bulk, now take
the flag's value from the recorded answer; reads of a served-deleted
entity still show it marked deleted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): set aside two hunks next to lines main's saved-values change rewrites
Main's #7267 changes the lines beside the blank-member docs and the
empty-spelling check in explainUpdate. Setting them aside lets that merge
apply cleanly; the commit after it puts them back on top of main's text.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): restore the set-aside hunks on main and bump the skill to 51
Puts the blank-member docs and the empty-spelling check back beside main's
slug and composed text and heldSlugRule. Main is at skill 50 with
different text, so this branch's skill changes take 51.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): bump the skill to 52 above #7260's 51
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): put back the call-values hunks main's merge took its side for, and bump the skill to 53
The merge of main kept main's side where it overlapped this branch, so it
could commit without a conflict. This restores this branch's side next to
main's: made-object agreement beside the & slug spellings, sequence starts
in the earlier evidence (now built by earlierOf), the combined value
imports, and recording step 2 beside main's UI-first step 3. Main is at
skill 50 and #7274 at 52, so this branch takes 53.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep sorts a list showed in place ruled out, and created answers' nested members as recorded
A whole list in collection order that showed a renamed entity where a
sort by the renamed field would have moved it now rules that sort out, so
a later rename of the same entity cannot teach it. A create's answer
leaves out members its item read added inside an object the answer
showed, as the recorded answer did; the state still stores them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): serve writes whose calls send different members
Recordings now vary what a type's two creates and updates send, and REA
withheld writes where a later call left out what the first sent. An update
plan's required members narrow to those every call sent; a member one create
sent and another left for the application to number is a sequence that
takes the value a call sends; a PUT's left-out member the application
cleared becomes that member's default; and an update's clock is held as
the read after it spells it. The skill spaces creates (skill 54).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep a later create's made object only with clocks its request stamped
followsMadeObjects only checked that a made object's clocks parsed, so a
later create whose object showed an older clock still passed, while serving
stamps the write's clock. Its clocks must now be in the rule's format and
within the stamp window of that create's request.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): accept sent values either way, check defaults against every call, and store update clocks as reads spell them
A sequence that takes sent values now accepts one a later create sent
after the first left the member to be numbered. A default an update learned
must agree with every update that left its member out, else the plan is
withheld. An update plan whose read-back spells a stamped clock at a coarser
precision keeps that read as observation.read, and serving stores the
update's clocks in its spelling while answers keep the response's.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): check recorded member and sequence values
* fix(environment): compare soft-deleted read fields
* fix(environment): reject contradictory learned sequences
* fix(environment): preserve initially deleted item observations
* fix(environment): index stored deleted entities separately
* fix(environment): replay lists that include deleted records
* feat(environment): serve sorted creates, listed references and toggles
A create a whole listing placed last is placed again by the sort a later
listing shows. A field holding the entities a sent list of ids names is a
reference with `many`, and a create that sent the member empty agrees with
one that named entities. A scenario request whose body Playwright did not
expose and that names no Content-Type sent no body. An item action calls
show flipping a boolean both ways learns a `toggle` rule. Skill 55 asks
recordings to run an action twice on one object.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): count a sequence number a create sends, as replay does
Serving now records a number a call sends for a `sequence` with `sent` in the
same count history it generates from, so after that entity is deleted the next
generated number still follows it, as replay's takeSequences does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the final gate run in the verification
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): make no count of a made-object member earlier creates held alike
A count only a later create shows starts at the exemplar's value, which is
right only while the exemplar is the one create accepted: creates accepted
after it showed the member unchanged, which a count would have moved. The
merge now refuses such a count, as the top-level sequence check does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): restate the verification after merging #7539
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): check PUT defaults against reads, and respell update clocks by serving's rule
A default a PUT learns must also agree with what the read after an earlier or
the current call showed for the member its answer left out, so a call whose
read showed the member kept withholds a later clearing default.
Replay and serving now choose an update clock's stored spelling by one rule,
readSpellsCoarser: the read after it at a coarser precision in the same zone
and separator. A read in another zone or separator is no longer adopted by
replay, which serving could not store, and a create's plain clocks stay as
answered, as serving makes them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep sorts earlier listings ruled out, and empty added references
A sort a whole listing that placed a create did not follow stays ruled out for
that create, though the entity that showed it is later deleted. A create that
sends a reference's member empty after a later create added the reference
agrees with it, as with a reference the first create showed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): restate the verification after the review round
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): leave out a reference the observed create left out
A create the plan observes that left out a reference's member stored no such
field, so a served call leaving the member out stores none either, though a
later recorded create sent it empty; a call sending it empty still stores that.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): restate the verification after #7539 merged main
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep the largest sequence value in replay, and store update clocks by the kept read
Replay now keeps the largest value a sequence took, as serving's commitWrite
does, so a lower sent value no longer lowers the next number.
Serving stores every update's clocks in the spelling of the read the plan
keeps as observation.read; replay now does the same through
readSpelledClocks, instead of adopting each update's own read, so a later
update whose read spells its clock otherwise contradicts the plan. The plain
clock branch of restampedFields, which no caller used any more, is gone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): restate the verification after merging #75393b800a10
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep learned sorts, references, and toggles faithful
Do not adopt a sort an earlier update already ruled out. Learn a list
reference only when every element shows the same nonempty fields. Promote
a toggle only when every call started from a boolean.
* test(environment): compare list-reference fields with matching values
The mixed-field case uses the held tag's color, so rejection comes from
the field set rather than a value the recording never stored.
* test(mcp): assert bundle path issues by field
The compare_bundles rejection reports each relative path as an
invalid_format issue instead of one joined diagnostic sentence.
* test(mcp): assert field issues for bundle paths and Dart ABI
Relative bundle paths and rejected ABI selectors are reported as
input issues on those fields, not as one joined diagnostic sentence.
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
* fix(process): corroborate Linux exits after procfs environ read races
A token read failing with ENOENT/ESRCH may race with process exit while
native identity observation still sees a live PID. Recheck native lifetime
and accept absence only when corroborated; keep unverifiable or reused
PIDs fail closed.
Refs #7790
* fix(process): recheck Linux exit after final token retry
A process can exit after the final retry of a failed procfs environment
read. Re-observe native identity before producing final cleanup failures,
and keep PID replacement and uncertain identity fail-closed.
Add five integration-facing sweep regression cases for absence, live,
unavailable, replaced and zombie-replaced candidates.
Refs #7790
* fix(process): bound native exit settlement for token-open races
After a terminal ENOENT/ESRCH run-token read, allow a tightly bounded
native-only settlement window to corroborate pending Linux process exit.
Retain an explicit failure for live/unavailable or changed PID identities,
without inferring exit from the token error itself.
Cover delayed absence, delayed replacement and bounded live retries.
Refs #7790
* fix(process): share native token exit settlement
* fix(process): signal only groups with validated members
* fix(process): retain lineage through exit settlement
* fix(process): type mixed identity observations in exit checks
Map inference collapsed a readable/absent union onto the absent
overload after main tightened ProcessIdentityObservation.
* fix(process): refresh groups whose members exit during revalidation
A same-group child forked after the revalidation snapshot is not an
empty group, so cleanup reads the table again before skipping the signal.
* test(process): arm group-refresh exit from the revalidation snapshot
The regression follows the table read after planning observed the
launcher token, and requires a later refresh to see the forked child.
* test(mcp): assert bundle path issues by field
The compare_bundles rejection reports each relative path as an
invalid_format issue instead of one joined diagnostic sentence.
* test(mcp): assert field issues for bundle paths and Dart ABI
Relative bundle paths and rejected ABI selectors are reported as
input issues on those fields, not as one joined diagnostic sentence.
---------
Co-authored-by: Harshit Sharma <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
* refactor(macho): extract non-throwing container and slice load-command readers
Split readMachoImage into readMachoContainer, which classifies a file as
thin, FAT or not Mach-O and keeps FAT range issues per slice, and
readSliceLoadCommands, which reads one slice header and frames its load
commands, returning every command framed before a failure.
readMachoImage keeps its strict policy on top of them: commands framed
before a framing defect still decode first, an unsupported FAT slice
still yields to a later malformed one, and the first FAT range issue is
still reported. A characterization test pins that precedence for inputs
with several defects; it passes against both the previous and the new
implementation.
Part of #4306.
* feat(macho): report per-slice encryption status in inspect_macho
inspect_macho now reports the LC_ENCRYPTION_INFO(_64) state of every
slice in a new `encryption` collection, read in process from load
commands without reading code bytes.
- Each slice is encrypted, not-encrypted, absent, malformed or unknown.
Absent requires a completed load-command walk; malformed and unknown
carry a reason and keep commands read before the failure.
- The named range is end-exclusive in slice and input-file coordinates
and maps to file-backed segment fragments, VM addresses and the
overlapping nonempty sections.
- A ciphertext range adds a limitation naming its input-file bytes.
- The in-process read and the native tools are bracketed by one regular
file descriptor and stat identity checks; a change reports
artifact_changed. A missing or inaccessible target keeps the tool
facts and reports encryption as non-exhaustive, never absent.
- Retained Evidence recorded before this field is parsed by the owning
schema and reports encryption as unknown in the macho view and in
managed/native verification.
Fixes#4306
* fix(macho): keep slice facts and failures under large tables and close errors
- Frame load commands through a visitor instead of retaining every
command, restoring the pre-refactor memory profile (2M eight-byte
commands: peak heap 370 MiB -> 0.9 MiB).
- Confirm a large FAT container from a valid slice's Mach-O header so a
sibling's range fault stays per slice instead of hiding healthy slices.
- Name only sections whose bytes the range and their segment both cover.
- Throw inspection failures inside the owned descriptor callback so a
close failure keeps them as its primary error.
* fix(macho): map encrypted ranges only within segment VM extents
mapRange gave every file-backed segment fragment VM addresses, even for a
segment with no VM size, whose file bytes dyld never maps, and past vmsize
when a segment declared more file bytes than memory.
Read vmsize with the segment. A segment with no VM size keeps its fragment
with null VM addresses. A file size beyond the VM size, or a VM range past
the address space, makes the mapping unknown with a reason while the slice
keeps its encryption status.
The docs also claimed the encryption read never reads code bytes. Like
every Mach-O container read, a 0xcafebabe input of at most 16 MiB is read
whole to rule out a JVM class file; say so.
* fix(macho): read encryption through the admitted descriptor facts
Main's withArtifactRegularFile now hands callers the admitted handle and
its initial stats. Use those facts instead of re-stating the handle, and
pass the legacy Evidence payload straight to createEvidence.
* fix(macho): retain a finished report when the target close fails
The file owner keeps the completed callback value when the descriptor close
fails or cancellation arrives during close. inspect_macho now validates that
value and retains the report as the error's partial observation, as
inspect_signature does, instead of returning only the cleanup or
cancellation error.
* test(native): supply readEncryption in merged dyld refusal cases
main's new dyld refusal cases build NativeMachoInspectionContext without
the encryption reader this branch requires.
* test(views): bind the Mach-O encryption page to its facet
main's facet-ownership case builds inspect_macho Evidence without the
encryption coverage this branch requires; the fixture now carries one
slice row and the loop checks the encryption page like the other facets.
* fix(macho): bound encryption commands and range mapping across slices
A slice retains at most two encryption load commands, since a second one
already makes it malformed; the reason keeps the full count. Range mapping
in every slice shares one budget of segment and section records, charged
from each segment command's capacity before it is decoded. A slice past
the budget keeps its encryption status and reports an unknown mapping.
* style(macho): format encryption reader and binding test
* fix(macho): stop retaining segments past the mapping budget
Segment commands are collected during framing only while their mapping
records still fit in the budget shared by all slices, and the budget is
charged only when the slice maps its range.
* docs(macho): clarify retained mapping allowance
* fix(ci): own the Mach-O encryption test in Apple scope
---------
Co-authored-by: t <t@t>
Co-authored-by: gensokyo-dev <[email protected]>
* feat(environment): serve the writes callers make without recorded form fields
- A create member a recording sent empty ("", null, false, [] or {}) is no
longer required; a call that leaves it out is applied as sending that value
(body.blank).
- List views render envelope members that list the listed entities' ids
(Paperless's `all`), so creates in such collections are confirmed.
- Updates move their entity in collections kept sorted by a field they write,
and a failed whole-collection read after an update can teach that order.
- A later create that stores an unruled field otherwise is withheld at the
create, and an item read may fill values a create's answer had not loaded.
- An unsent field respelled between null, [] and {} is unchanged.
- The skill asks for one create with every field and one with only what a
person must give.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skill): show how to read signed-in pages before recording
Agents guessed capture keys (artifacts, dom, final) to see pages behind
sign-in, which inspect_web_page cannot reach.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): accept every empty spelling recorded creates sent
A create that fills only what a person must give sends `null` where the
full create sent a string, and `null` or `false` for the same flag. The
merged plan now accepts each recorded empty value, and a call that leaves
the member out is applied as the earliest call sent it.
Also adds the spec folder for this change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skill): keep the vary-fields step neutral on UI or API writes
Agents read "the form" as a cue to write through the UI, where some
applications' request bodies are not exposed; a body-unusable UI write
now re-records once with request steps.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record verification for blank members and sorted writes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): address review of blank members and sorted writes
- Bulk updates move their entities in turn against the others' earlier
values and install the final order once, as replay does.
- A member's recorded empty values stay accepted once a later call teaches
its default; a left-out member with a default keeps the default.
- Write answers spell empty lists and maps as their recorded answers did.
- A rename can teach a sort when the creates chose one by a field the
update did not write, such as ids that ascend with titles.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): serve the writes that later reads explain
- An item read whose object is marked deleted (deleted, isDeleted or
is_deleted true) agrees with an absent entity, as EspoCRM answers a
deleted record; such an object never seeds or adopts an entity.
- maxSize, max_results and maxResults are page sizes, for whole-collection
list lines and the list query's limit role.
- An item view also shows the fields only reads after its type's first write
show, when no initial entity holds them (Vikunja's task subscription),
without requiring them; write answers leave such a field out for entities
without it, and it no longer withholds the write as incomplete-entities.
- The skill asks updates to change the fields a person would change, and
expects a deleted object to read back as 404 or marked deleted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer sent empty members as sent, and keep a placed create's order
- A write answer spells an empty list or map the call did not send as its
recorded answer did; one the call sent is answered as sent, so a create
sending `[]` is not answered `null` because the first recorded create was.
- A read listing the whole collection that placed a create leaves the create
only the sorted inserts whose order that read follows, so a later rename
cannot teach a sort an earlier read ruled out.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer bulk updates without fields only later reads show
A bulk update's answer renders each entity with the same allowance a single
update's answer has: a field only reads after the type's first write showed,
which no initial entity holds, is left out for an entity without it, so a
bulk update of a seeded task is no longer refused for lacking it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): correct how the EspoCRM contact update task passes
The agent trajectories show gpt-oss passes it by creating a replacement contact
and deleting the original, since the served update refuses emailAddress; the
update itself is refused in both builds.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer a read of a served-deleted entity marked deleted
An application that soft-deletes still answers a deleted record by id, marked
deleted. Serving now keeps an entity a served delete removed from a type whose
recorded item reads show that, until a reset or load and within the size
data/add loads, and answers a read of it with its last fields and the flag
true, instead of refusing the read.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep unloaded create answers, check bulk answers with sent members, narrow inserts only on collection-order reads
- A create whose item read loaded values its answer had not records them
(`observation.read.unloaded`), and a served create's answer keeps them as
the recorded answer showed them, a zero-time owner included, while the state
holds the read's.
- Replay checks a bulk update's recorded answers with each target's sent
members, as serving answers them, so echoed `null` and `[]` no longer make
the plan body-unusable.
- A whole-collection read whose query sorts it shows the query's order, so it
no longer narrows a create's sorted insert candidates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the census with main's unknown-clock rule merged
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): serve query numbers, counted values and made objects
- A query parameter accepts a number or boolean, which serving sends as its
JSON text, as agents send page sizes and flags.
- A sequence may start at the value its first create took above every value
its type held (Vikunja's per-project task index), and a later create's
composed, template or started-sequence rule is adopted when it also spells
the first create's value.
- A made-object rule explains an object the application makes with each
created entity: members the request stamped, members holding the entity's
own id, and counts one past the held objects' values.
- A create's answer names the id of the one object a one-element array member
holds, so bulk creates link to their type.
- The skill reads each listed object by id before writing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): let a linked create take path values it stores
A create linked to its type's own collection, whose path has no parameters,
accepts any path values when its plan stores each one in a field, as a Vikunja
task stores its project; replay and serving apply it alike.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn a sort from a rename only when moving its entities reaches it
Serving moves only the entities a write changes in a collection kept sorted,
so replay now tries a sort after a rename only when the entities no write the
read exercised changed already follow it. Seeded folders listed out of title
order no longer teach a title sort that serving would answer with another
order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep unloaded create answers a sent value did not echo, and learn no sort from query-sorted reads
- A served create keeps a value its recorded answer had not loaded unless the
call sent that member and the plan stores it as sent, so a create sending
`active: false` is answered `false` as recorded while the state holds the
read's `true`.
- A rename no longer teaches a sort from a whole-collection read its query
sorts, whose ties need not follow the collection's own order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): verify query values, counted values and made objects
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn the soft-delete flag in the state view and serve by it
Extraction now records the member later item reads set true on a record the
application soft-deleted, and the item view carries it as `deleted_flag`.
Serving reads that fact instead of scanning recorded bodies itself, so one
owner decides what a soft delete looks like.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the call-values checks at #727493821024
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn the soft-delete flag from item reads with a query too
A client may read a deleted record back only with a query, such as a
cache-busting parameter. Every successful read of the item tool now shows the
flag; only plain later reads still add fields.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the call-values checks at #727445832406
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn sorts only from orders a list read confirmed
- A sort retry after a failed whole-collection read requires the entities no
write changed since a list last showed them to follow it already, rather than
excusing every entity the update's tool ever wrote: an earlier rename a list
confirmed in place stays where it was.
- An update takes its creates' sort only from a create a list read showed
placed; replay still tries a sort a create guessed from the initial order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): drop entities read back as deleted and answer soft deletes as deleted
- A held entity a later read shows marked deleted is dropped, not adopted with
the marked object's fields.
- Bulk-update replay renders answers with the optional and later-only fields
serving uses, so a bulk update of created and seeded entities replays.
- A served delete of a soft-deleting type answers with the entity marked
deleted, single and bulk.
- A deleted flag only query reads showed is a field of the item view, so a
served tombstone keeps it where live records omit it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): check made objects as recorded and keep their ids and clocks honest
- A made object's clock counts as a write clock, so a capture with no clock
anchor withholds the create instead of stamping 1970.
- Members holding the entity's identity follow the id written, generated or
sent, through the type's id field.
- A later create's made object is checked as recorded, before its own members
are rewritten, so one naming another entity withholds the plan.
- A later create's template is adopted only when it spells that create's value
too, not just when the first create's text holds its source value.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): move updated entities only by placed sorts and keep query-sorted reads from confirming order
Replay moved an updated entity by a sort a create only guessed, while the
update plan exported only placed sorts, so a read could confirm an update
by a move serving would not make. Both now use the placed sort. A passing
list read clears its entities from the moved set only when the state
decides the order of every entity it lists, so a read whose query sorts
it no longer hides an earlier move from a later sort check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): count six withheld plans that explain more, not seven
The bulk task create counted among them is served on this branch.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): try a learned sort by moving each pending write in turn, as serving does
#retrySort sorted the whole collection, which a list could match while
serving, moving each renamed entity in turn against the others' values
before their own writes, gave another order. The state now records the
values each write replaced, and the retry replays the writes since a
list in collection order last showed their entities.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer a served delete with the deleted flag its recorded answer showed
An application may answer a delete with the record as it was and mark it
deleted only on later reads. Delete answers, single and bulk, now take
the flag's value from the recorded answer; reads of a served-deleted
entity still show it marked deleted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): set aside two hunks next to lines main's saved-values change rewrites
Main's #7267 changes the lines beside the blank-member docs and the
empty-spelling check in explainUpdate. Setting them aside lets that merge
apply cleanly; the commit after it puts them back on top of main's text.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): restore the set-aside hunks on main and bump the skill to 51
Puts the blank-member docs and the empty-spelling check back beside main's
slug and composed text and heldSlugRule. Main is at skill 50 with
different text, so this branch's skill changes take 51.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): bump the skill to 52 above #7260's 51
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): put back the call-values hunks main's merge took its side for, and bump the skill to 53
The merge of main kept main's side where it overlapped this branch, so it
could commit without a conflict. This restores this branch's side next to
main's: made-object agreement beside the & slug spellings, sequence starts
in the earlier evidence (now built by earlierOf), the combined value
imports, and recording step 2 beside main's UI-first step 3. Main is at
skill 50 and #7274 at 52, so this branch takes 53.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep sorts a list showed in place ruled out, and created answers' nested members as recorded
A whole list in collection order that showed a renamed entity where a
sort by the renamed field would have moved it now rules that sort out, so
a later rename of the same entity cannot teach it. A create's answer
leaves out members its item read added inside an object the answer
showed, as the recorded answer did; the state still stores them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): serve writes whose calls send different members
Recordings now vary what a type's two creates and updates send, and REA
withheld writes where a later call left out what the first sent. An update
plan's required members narrow to those every call sent; a member one create
sent and another left for the application to number is a sequence that
takes the value a call sends; a PUT's left-out member the application
cleared becomes that member's default; and an update's clock is held as
the read after it spells it. The skill spaces creates (skill 54).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep a later create's made object only with clocks its request stamped
followsMadeObjects only checked that a made object's clocks parsed, so a
later create whose object showed an older clock still passed, while serving
stamps the write's clock. Its clocks must now be in the rule's format and
within the stamp window of that create's request.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): accept sent values either way, check defaults against every call, and store update clocks as reads spell them
A sequence that takes sent values now accepts one a later create sent
after the first left the member to be numbered. A default an update learned
must agree with every update that left its member out, else the plan is
withheld. An update plan whose read-back spells a stamped clock at a coarser
precision keeps that read as observation.read, and serving stores the
update's clocks in its spelling while answers keep the response's.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): check recorded member and sequence values
* fix(environment): compare soft-deleted read fields
* fix(environment): reject contradictory learned sequences
* fix(environment): preserve initially deleted item observations
* fix(environment): index stored deleted entities separately
* fix(environment): replay lists that include deleted records
* fix(environment): count a sequence number a create sends, as replay does
Serving now records a number a call sends for a `sequence` with `sent` in the
same count history it generates from, so after that entity is deleted the next
generated number still follows it, as replay's takeSequences does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): make no count of a made-object member earlier creates held alike
A count only a later create shows starts at the exemplar's value, which is
right only while the exemplar is the one create accepted: creates accepted
after it showed the member unchanged, which a count would have moved. The
merge now refuses such a count, as the top-level sequence check does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): check PUT defaults against reads, and respell update clocks by serving's rule
A default a PUT learns must also agree with what the read after an earlier or
the current call showed for the member its answer left out, so a call whose
read showed the member kept withholds a later clearing default.
Replay and serving now choose an update clock's stored spelling by one rule,
readSpellsCoarser: the read after it at a coarser precision in the same zone
and separator. A read in another zone or separator is no longer adopted by
replay, which serving could not store, and a create's plain clocks stay as
answered, as serving makes them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep the largest sequence value in replay, and store update clocks by the kept read
Replay now keeps the largest value a sequence took, as serving's commitWrite
does, so a lower sent value no longer lowers the next number.
Serving stores every update's clocks in the spelling of the read the plan
keeps as observation.read; replay now does the same through
readSpelledClocks, instead of adopting each update's own read, so a later
update whose read spells its clock otherwise contradicts the plan. The plain
clock branch of restampedFields, which no caller used any more, is gone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep a left-out default from numbering, a read's sub-millisecond digits, and references named by member
A member a call left out and the application stored a fixed value for is no
longer turned into a numbered sequence by a later call that left it out.
Serving shortens an update's clock to the read's precision from the answer's
own digits, keeping microseconds. A PUT that names a reference field's entity
by its member supplies that field, for the default check and in serving.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): count a reference a later PUT names toward the default an earlier one cleared
The default check now reads the reference rules the current call shows as
well as the plan's, so a PUT naming the owner by ownerId after one that
cleared it is served, as in the other order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): number a left-out member past every sent value, and never fill it with a sent blank
A later create the application numbered at or below a value an earlier create
sent no longer turns the echo into a numbered sequence, since serving counts
past every sent value. A member a numbered sequence covers is not filled with
an empty value another call sent when a call leaves it out.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): count sent values in the sequence's scope before numbering a left-out member
The largest value earlier creates sent is kept by type and by collection, and
a collection-scoped sequence adopted later checks only its own collection, so
a number sent in another collection no longer withholds a create numbered
within its own.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
An Objective-C category whose class field is null, or is bound to a
symbol other than `_OBJC_CLASS_$_`, was recorded as partial with
`category_class_unresolved`, but no issue was raised, so the
`objc_properties_categories` coverage facet reported `partial` with a
null reason. swiftc emits such categories for @objc members of an
extension on another module's Swift class and binds the class field to
the class metadata symbol (`_$s…CN`); Keynote and Notability carry them.
Report the reference as a `category_class_unresolved` issue naming the
bound symbol, or stating that the reference is null, matching the issue
already raised for an unreadable local class reference.
TYPE_NAMES stopped at flat_dt (8), while the U-Boot include/image.h
commit that the tables are pinned to defines image types 0-47. A header
declaring a later type, such as kernel_noload (14) or tfa-bl31 (45),
was reported with type_name "unknown".
Add types 9-47 with their mkimage -T short names from boot/image.c at
the same commit. IH_TYPE_LOADABLE (22) has no short name there and is
named "loadable", as other tables already name identifiers U-Boot does
not list. Identifiers past the enum stay "unknown".
Reuse the pinned SDK's combined array-element and object-member admission
before canonical validation, deferred runtime loading and tool execution.
Share a configurable finite allowance across eager/deferred HTTP and stdio,
and project refusals to bounded, actionable transport resource errors.
Preserve canonical CLI/application contracts, caller-selected input values,
ordinary validation semantics and complete Evidence access. Exercise actual
transport boundaries, real artifact/TShark workflows, packaged installation,
continued service and downstream identity/capacity regressions.
Fixes#7882
BREAKING CHANGE: MCP tool arguments now default to 1,024 combined array
elements and object members per call. Select a larger positive finite
REA_MCP_MAX_INPUT_ELEMENTS allowance and restart for larger inline inputs,
or use retained/file selectors where supported. Inputs are never truncated.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Node's CommonJS loader matches registered extensions case-sensitively, so
require("./data.JSON") and require("./addon.NODE") run as JavaScript.
Derive target_json_status and the native loads relation from the exact
loader suffix instead of the inventory's case-folded file kind.
Fixes#7623
inspect-apple-ips and inspect-pe-imports were the two commands in the
binary diagnostics group whose positional target was still a plain
string. An empty argument passed CLI validation, was resolved to the
working directory, and failed later as a non-file input, unlike their
siblings (inspect-elf-notes, inspect-pe-certificates and others) that
already reject it at validation.
Declare both targets with the shared nonempty, NUL-free local path
schema the rest of the file uses. Explicit "." is unchanged.
Fixes#7421
* fix(javascript): emit no fork when a literal stdio array omits the IPC channel
Node's fork() requires an explicit stdio array to carry the IPC channel:
if (!ArrayPrototypeIncludes(options.stdio, 'ipc'))
throw new ERR_CHILD_PROCESS_IPC_REQUIRED('options.stdio')
The throw is synchronous, before any child process exists, but the analysis
reported a resolved fork with stdioMode "array" — byte-identical to the valid
`[..., "ipc"]` form — which projects to a child-process node, a resolved spawns
relation and resolved connects-stdio.
Measured on Node 24.22.1:
fork({stdio: ["inherit","inherit","inherit"]}) -> THROWS ERR_CHILD_PROCESS_IPC_REQUIRED
fork({stdio: ["pipe","pipe","pipe"]}) -> THROWS ERR_CHILD_PROCESS_IPC_REQUIRED
fork({stdio: ["ipc"]}) -> SPAWNED connected=true
fork({stdio: ["inherit","inherit","inherit","ipc"]}) -> SPAWNED connected=true
fork({stdio: "pipe"}) -> SPAWNED connected=true
spawn({stdio: ["inherit","inherit","inherit"]})-> SPAWNED connected=false
A string stdio is normalised with "ipc" appended, so only the array form can omit
it. The same array is valid for spawn, which has no IPC requirement.
Drop a fork candidate only when its stdio array is fully literal and provably
lacks "ipc"; a spread or a non-literal element may still supply the channel and
stays a candidate.
* chore: retrigger title and readiness checks after the title was corrected
* feat(artifacts): inventory and extract U-Boot FIT images
open_binary refused U-Boot Flattened Image Tree files as an unsupported
format, so inspect_artifact and extract_artifact could not list or
recover a FIT's kernel, devicetree, ramdisk and loadable images.
Recognize an FDT v16/v17 with a top-level /images node and parse it with
the existing parseDevicetree. Each /images child becomes an inventory
entry named images/<unit-name>.bin that extracts its stored bytes from
embedded data, data-offset (past the 4-aligned devicetree end) or
data-position (from the file start). Image format_metadata records the
declared type, arch, os, compression, load and entry; root metadata
records the description, timestamp and configurations.
crc32, md5, sha1, sha256, sha384 and sha512 hash nodes are verified in
one streamed pass per image; other algorithms are reported unverified.
Signatures are reported, not verified, and nothing is decompressed,
decrypted or executed. Data outside the file, overlapping or duplicate
image data, a missing or repeated data source and hash mismatches fail
closed. The devicetree is bounded by the fixed 16 MiB allowance;
external image data is streamed from disk.
Refs #7129
* fix(artifacts): bound FIT configuration references and suggest analysis only for plain images
* fix(artifacts): retain buffered FIT stream cancellation
* test(artifacts): gate FIT faults on the observed read phase
* fix(artifacts): verify FIT links and summary identities
* fix(artifacts): bound FIT diagnostics and retain unknown signatures
* fix(artifacts): compare FIT declarations and resolved data links
* fix(artifacts): admit FIT raw probes through the shared loader contract
* fix(artifacts): preserve FIT field availability independently
* fix(artifacts): isolate nested entry metadata from reader facts
---------
Co-authored-by: gensokyo-dev <[email protected]>
Co-authored-by: gensokyo-dev <342580529+users.noreply.github.com>
Local file commands in the core binary, native, managed and native-tool
CLI groups declared their positional target as a plain string. An empty
argument therefore reached path admission, where it resolved to the
working directory, as docs/cli.md says it never should.
Declare those targets with the shared nonempty, NUL-free local path
schema already used by analyze, so the CLI rejects them as a
VALIDATION_ERROR naming the field before any analysis runner is called.
Explicit "." is unchanged.
Fixes#7436Fixes#7404
* test(firmware): exercise the staging ceiling through the emitted refusal
The ceiling recovery added in #6971 was covered through the wording helper only, so a wiring change that passed the schema ceiling where the selected value belongs, or that dropped the remediation, would have stayed green.
Adds boundary coverage that calls inventoryFirmwareOutput itself: a sparse file reaches the real byte ceiling without storing the bytes and asserts the projected refusal carries the ceiling recovery, and a one-byte tree below the ceiling asserts the raise advice. Verified red on both regressions — passing the schema ceiling for the selected value fails the below-ceiling case, and restoring the single-form remediation fails both.
* test(firmware): compare the emitted recovery with its owner
The wiring test matched three phrases of the emitted remediation, so an ad-hoc or truncated action that kept those phrases would still pass while the emitted recovery had diverged from firmwareStagingBudgetRemediation.
Assert exact equality with the owner's output for the selected value in both cases instead. Verified red: replacing the emitted action with a divergent sentence that keeps the ceiling phrases now fails both cases, where the phrase matching passed.
* chore: retrigger title and readiness checks after the title was corrected
* test(firmware): drive the staging ceiling through the request boundary
greptile and codex both flagged that the ceiling tests called
inventoryFirmwareOutput directly, so they would still pass if request parsing or
the provider wiring dropped or replaced max_output_bytes.
Add a request-level test that records the parsed request a provider receives:
the caller's max_output_bytes arrives unchanged at the schema ceiling and below
it, the documented default applies when omitted, and a budget above the schema
maximum is rejected before the provider runs. That is the wiring the previous
tests assumed rather than asserted.
* fix(javascript): match resource release methods to the acquired handle
Every acquisition family admitted every release method, so `h.close()`,
`h.destroy()` and `h.end()` each resolved as a complete release of whatever the
binding held. Node exposes a different subset per acquisition, and the rejected
pairs throw at the call site instead.
Measured on Node 24.22.1:
| acquisition | value | release members |
| --- | --- | --- |
| `fs.openSync` | number | none |
| `fs.open` (callback) | undefined | none |
| `fs.createReadStream` | ReadStream | `close`, `destroy` (`end` is the byte-offset field) |
| `fs.createWriteStream` | WriteStream | `close`, `destroy`, `end` |
| `fs/promises.open` | FileHandle | `close` |
| `net.connect` | Socket | `destroy`, `end` |
| `tls.connect` | TLSSocket | `destroy`, `end` |
So `fs.openSync(...)` + `fd.close()`, `createReadStream(...)` + `.end()`,
`net.connect(...)` + `.close()` and the FileHandle cases emitted a `releases` /
`resolved` fact for a call that throws.
Carry the specifier with each acquisition candidate, list the release methods
each pair actually has, and emit no release when the pair is not one of them. A
release stays `complete` only when the binding held exactly one acquisition and
that acquisition admitted the method.
`tls.createConnection` is not a Node export at all: `'createConnection' in
require('node:tls')` is false and the ESM import does not link. Drop it from the
table so a `tls.createConnection` call yields no acquisition.
* chore: retrigger title and readiness checks after the title was corrected
* fix(javascript): cover a mixed-family binding before completing a release
greptile asked for the mixed-family case: a binding that can hold either a
FileHandle or a socket, then calls close(). The FileHandle is the only linked
acquisition and the release stays partial.
That case also showed the `candidates.length === 1` guard was dead: an
acquisition of another family is not in `linked`, so `holdsOnlyAcquisitions`
already returns false for it and the release already stayed partial without the
guard. Removing the guard and keeping the test means the behaviour is now pinned
by a test rather than by two overlapping conditions.
* fix(process): normalize descendants of slash-spelled directory roots
A working directory or observation path ending in / or /. kept its
trailing separator as the alias, so descendant paths never met the
delimiter lookahead and stayed literal. Strip those spellings before
building aliases and version the path normalization contract as v3.
Fixes#7584
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(process): normalize filesystem root descendants
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
Field, MethodDef and MemberRef signature issues reported the invalid operand's offset inside its blob, while local signature issues in the same coverage array used artifact file offsets. Translate both through one helper that adds the blob's artifact base.
Fixes#7385
REA_GHIDRA_DISABLED_ANALYZERS parses a blank or comma-only value to no override, but the existing-project conflict check tested the raw value. MCP startup exited and doctor reported config_drift for an input that selects nothing. The check now uses the parsed value.
Fixes#7422
A selected .gpr marker that does not exist and a target that existing-project mode cannot serve both surfaced as execution_failure with no setting and a doctor action that cannot help. Both now return configuration_invalid naming REA_GHIDRA_PROJECT_PATH. The ENOENT diagnostic of the missing marker is kept.
Fixes#7417Fixes#7416
* fix(call-path): reject incompatible coordinate mappings
Use the existing provider-owned coordinate context comparison before composing dossiers. Preserve same-mapping profiles and missing-dossier uncertainty. Fixes#7345.
* test(call-path): assert the structured rejection instead of its wording
docs/testing.md asks for structured error checks. Assert the issue path and reason, and keep the MCP category and code.
* fix(javascript): read spawn env as Node reads it
Node's normalizeSpawnArguments starts with `const env = options.env ||
{ ...process.env }`, so a falsy env is replaced by a copy of the parent
environment. A child started with env: null, undefined, 0, "" or false
therefore inherits everything, exactly as if no env were passed.
REA tested the key's presence alone, so every one of those reported
environment_supplied: true and the graph emitted a resolved supplies-env
relation. The presence test also missed the opposite direction: an env
supplied through an inline object spread reported false although Node uses
it.
Read the declared value the way stdioMode already reads stdio: an explicit
property whose statically known value is falsy does not supply an
environment, and an inline spread that carries env does.
Verified on Node v24.22.1 with a child that prints a parent marker variable:
no options, env: null, undefined, void 0, 0, "" and false all print the
marker, while env: {} prints nothing and env: { A: "1" } prints only that.
* fix(javascript): read falsy numeric literals as Node does
greptile flagged that environmentSupplied still reported a supplied environment for
`env: -0` and `env: 0n`: staticJavaScriptTruthiness recognized neither form, so it
returned undefined and `!== false` turned that into true. Node's
normalizeSpawnArguments uses `options.env || { ...process.env }`, so both inherit.
Extend the shared truthiness reader to cover a BigInt literal and a negated numeric
literal, and add regression cases for `-0`, `0n` and `0.0`.
The reader is shared with the route scan and the state tracker; both treat
`undefined` as "unknown", so a more precise `false` only strengthens them.
The inventory registry gained read_binary_inventory_summary, so the
read-only inventory is fifteen inventory and twelve function operations.
Update the adapter summary and the Windows lane descriptions to match the
registries the Windows verifier derives its coverage from. The historical
Windows verification record keeps its observed 26-operation count.
Fixes#7611
* fix(pe): report exhausted certificate decoder allowances as resource limits
A PKCS#7 payload that exhausted the fixed 64 KiB decoded-text or 16,384-value ASN.1 allowance was refused as malformed input with advice to correct request fields. Keep the allowance and limit on the decoder failure and report it as a nonretryable resource_constraint with fixed-allowance recovery.
Fixes#7531
* test(pe): cover certificate allowances through MCP
---------
Co-authored-by: morluto <[email protected]>
* fix(environment)!: report recorded reads and target uncertainty
BREAKING CHANGE: Environment state and coverage can report method-replay for non-GET safe reads; exhaustive reason consumers must handle it.
* docs(environment): include safe-method replay reason
* fix(ghidra)!: distinguish unlisted instruction addresses
BREAKING CHANGE: Ghidra now returns not-in-listing for in-memory addresses without listed instructions or defined data. Consumers matching the former undecodable value must handle the new status.
* fix(ghidra): invalidate snapshots with old instruction status
* test(ghidra): update raw profile commitment goldens
* feat(artifacts): compare artifacts by Evidence reference and from the CLI
compare_artifacts made agents send back the complete inventory Evidence
that REA already retains, and the CLI had no artifact comparison.
Each side of compare_artifacts now takes exactly one of: complete inline
Evidence (unchanged), {kind: "retained-evidence", evidence_id}, or
{kind: "evidence-file", path, evidence_id?}. The two references are the
sources inspect_analysis_view accepts; both tools now compose one
definition in evidenceReference.ts. A summary still resolves to its exact
retained parent, and every source form gives the same comparison Evidence.
An unresolvable retained reference is refused with a typed missing
reference error.
The application layer owns source resolution and the comparison Evidence,
so MCP and the new `rea compare-artifacts` command share one workflow.
readEvidenceFileSource reports bundle-selection refusals at the caller's
own field path.
* refactor(artifacts): resolve compare_artifacts sides like compare_functions
compare_functions, compare_wasm_interfaces and the JavaScript application
comparisons name each side with evidenceInputSchema and resolve it with
resolvePairedEvidenceRequest. compare_artifacts reached the same result
for inline and retained sides through its own dispatch.
evidenceSourceInputSchema now extends evidenceInputSchema with the
Evidence-file reference, and resolvePairedEvidenceSources sends inline and
retained sides to the same resolveEvidenceInput and file sides to the
shared Evidence file reader. compare_artifacts uses both, so the forms it
shares with compare_functions are parsed and resolved by one owner. The
advertised input schema is unchanged.
* docs(cli): point rea compare to compare-artifacts for inventory contents
rea compare (compare_bundles) compares records by Evidence identity and
explicit pairs and never reads artifact contents, so two build
inventories stay unresolved there. Its help and the CLI guide now name
compare-artifacts for that comparison.
* fix(artifacts): resolve summary parents within Evidence source bundles
* perf(evidence): reuse file-owned immutable authentication proofs
* test(evidence): inject failures at both authentication boundaries
---------
Co-authored-by: Entrpi <[email protected]>
Normalize raw URL separators after admitting an original relative ESM
request, before artifact path validation and root confinement. Preserve
the authored declaration and the existing encoded-separator checks.
Retain Node-backed static and dynamic loader controls and compare the
application graph with the historical importer already present on main.
Narrow PR #4323 to this remaining case after the upstream #4269 fix.
Signed-off-by: Jiakai Xu <[email protected]>
U-Boot's mkimage writes the multi-file size table for script images
(`mkimage -T script`, the boot.scr format) exactly as it does for multi
images, `mkimage -l` lists the script as "Image 0", and the `source`
command skips the table before running the script ("scripts are just
multi-image files with one component", boot/image-board.c).
The uImage reader parsed the table only for IH_TYPE_MULTI, so a script
image reported sub_image_count 0 and exposed only payload.bin, whose
first bytes are the binary size table rather than the script text.
Treat IH_TYPE_SCRIPT like IH_TYPE_MULTI: parse the same bounded table,
expose image-<n>.bin components and report the size-table limitation.
payload.bin is unchanged. A script payload without a valid table now
fails closed, as a multi image already does.
* fix(cli): align MCP doctor completion with its options
* fix(cli): explain zsh completion initialization
* fix(cli): include launcher timing in doctor completion
* style(cli): format doctor completion schema
* feat(artifacts): inventory and extract ESP32 firmware images
ESP32-family app and bootloader images (magic 0xE9) had no reader, so
open_binary refused OTA downloads, app partitions and bootloaders.
Add the esp-image container format behind inspect_artifact and
extract_artifact. A pure decoder reads each byte once and keeps only
fixed-size fields. Derived facts cover the chip, from a table cited to
ESP-IDF v6.1; segment classification as ESP-IDF's should_map and
should_load; checksum and appended SHA-256 results; app and bootloader
descriptors; Secure Boot v2 signature blocks, observed and unverified;
and undescribed trailing byte ranges. Truncation and undescribed bytes
make coverage unknown. ESP8266 images are refused with a specific
reason, and ciphertext stays unrecognized.
Add the format to every target, Evidence, snapshot, live-summary and
artifact vocabulary, document it, and add an optional verify:esp-images
lane that compares esptool-built images with esptool image-info and
espsecure signature-info-v2.
Refs #3615
* test(artifacts): run the ESP image lane on esptool v5.6.0
The lane compared descriptor fields only when esptool image-info printed
one, so a descriptor that REA reports and esptool does not print passed
without comparison. Compare the descriptor kind in both directions.
Record that the lane passed for all 13 images with esptool and espsecure
v5.6.0, the version the layout facts cite, as well as with 4.12.0.
* refactor(artifacts): define ESP image layout sizes once
The decoder and the derived facts each defined the segment-header,
appended-digest and flash-sector sizes, the erased-byte scan and the
offset formatter. Define them once with the other fixed-layout structures
and import them in both places. The signature slot count is the one
constant that drives the slot scan.
Correct the observedEnd comment: it is the end of the last chunk read.
* fix(artifacts): report ESP-IDF's segment length limit by chip
ESP-IDF's verify_segment_header rejects a segment of 16 MiB or more
unless the chip maps flash through 32-bit cache addresses
(SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP: ESP32-S3, P4, C5, C61, H4 and
S31 in v6.1). Record that capability in the chip table and add a
limitation for such segments; unknown chips get none.
`loaded` repeats should_load. It does not mean the bootloader accepts the
segment: verify_load_addresses still rejects one outside the chip's RAM,
which REA does not model. Say so in the docs, the skill reference and the
inventory limitation.
* test(artifacts): check ESP lane tools first and verify extraction
The lane started work before checking its tools, so a missing RISC-V
target, ld.lld, esptool or espsecure surfaced as a raw spawn or command
error. Check each one before building and name the variable to set. Warn
when esptool is not v5.6.0, the release the layout facts cite.
Assert each segment's class for the ESP32-C3 app, its variants and the
bootloader, as the Xtensa image already did. Extract the app and the
signed image through the CLI and compare every file with the bytes at
esptool's segment offsets or in the appended signature sector; the
source's SHA-256 must be unchanged and equal the reported root_sha256.
* docs(artifacts): state what the ESP image lane does not establish
The Verification section listed the lane's checks but not its gaps: only
ESP32-C3 and ESP32 images come from a real producer, no real project
build, OTA download or flash dump is inspected, no signature is verified,
and the lane does not run in CI.
Record the timing of the final lane run on esptool v5.6.0.
* fix(artifacts): compare ESP segment declarations
Segment entries hash only their data, so compare_artifacts reported a
segment that moved to another load address with the same bytes as
unchanged; only the image itself changed. Compare each segment's
load_addr and data_len as its declaration, as Node SEA and PyInstaller
members are compared, and count a changed declaration as a metadata
change, also when content is unavailable.
Header offsets are layout and the classification is derived, so neither
is compared.
* test(artifacts): run ESP lane commands as owned process trees
The lane ran its tools through execFile. A timeout killed only the direct
child, so a linker or assembler that Clang or GCC had started could keep
running while the lane removed its workspace. Run every command through
createVerifierCommands, check the tools inside withVerifierCleanup, and
remove the workspace only after commands.close() has stopped every tree;
a failed close keeps the workspace.
Only a nonzero exit now counts as a refusal or an expected esptool
failure; timeouts and spawn failures propagate. Supervising each command
roughly doubles the lane's run time.
* test(artifacts): cancel ESP decoding after its first decoded chunk
The cancellation test requested cancel or close on setImmediate, which
can land while the file is still being opened and checked, so it never
showed that decoding had started. It also passed against a reader that
ignored both requests while decoding. Acknowledge the decoder's first
chunk, then cancel or close, join the operation, and assert that no
later chunk reaches the decoder and that the reader ends closed.
* docs(artifacts): record which ESP metadata comparison ignores
Segments are compared by their declaration. The image's and the
signature blocks' format_metadata are not: every field comes from their
own bytes, which content comparison already covers, and a block's
image_digest_matches follows the image's content.
* docs(artifacts): state that ESP mmu_page_size is a base-2 logarithm
The app descriptor's mmu_page_size keeps ESP-IDF's name and value, the
MMU page size in log base 2 as esp_app_desc_t declares it, so 16 means
64 KiB. Say so in docs/esp-image.md and the schema comment; a reader
could otherwise take 16 as 16 KiB.
* fix(esp-image): retain member consumption and share lifecycle regressions
* test(artifacts): scope MCP catalogs and budget full-schema validation
* test(artifacts): verify compact output profiles and document bounded catalogs
* test(artifacts): close readers when cleanup-test admission fails
* test(artifacts): compare ESP image limitations with their owners
The decoder and reader tests froze complete limitation sentences, so a
harmless rewording failed them. The wording now has two owners:
ESP_IMAGE_LIMITATIONS in espImageFacts.ts, which the decoder, the inventory
and the reader use, and ESP_DESCRIPTOR_LIMITATIONS beside the descriptor
decoder. Tests compare delivered limitations with the owner, called with the
facts each case establishes (segment index, declared and available bytes,
offsets, chip, page range), next to the structured assertions they already
make. Delivered text is unchanged.
* test(esp-image): assert structured facts and owned guidance
* fix(esp-image): keep spi_flash_mode and descriptor fields before a truncated tail
ESP-IDF v6.1's esp_app_desc_t declares spi_flash_mode at byte 181, after
mmu_page_size, as the committed reference layout shows; the descriptor now
keeps it as stored. The decoder also required all 256 descriptor bytes,
though it reads none past byte 181, so a file ending in the reserved tail
lost the version, project, digest and revision facts it does hold. The app
descriptor now needs only its fields; the segment's truncation is reported
as before.
* test(esp-image): pin the P-192 public key digest to espsecure's layout
espsecure packs ECDSA signature blocks as "<BBBx32sB64s64s1031x" and
zero-pads a 48-byte P-192 point to 64 bytes, so the signature starts at
byte 101 for every SHA-256 curve. signature_info_v2 hashes sig_block[36:101]
and _digest_sbv2_public_key the curve ID with the padded point. No test built
a P-192 block; this one checks public_key_sha256 against that definition.
* fix(esp-image): keep each app descriptor field the file holds in full
A file can end anywhere inside the 256-byte esp_app_desc_t. The decoder
needed every field byte, so a cut at byte 176 dropped the version, project
and app_elf_sha256 it does hold. Once the 4-byte magic identifies an app
descriptor, each field is now kept when the file holds all of its bytes and
is null otherwise; observed_length records where the descriptor ends, so a
cut field is told apart from text that is not UTF-8. The fields after the
magic become nullable in the metadata schema.
* fix(esp-image): report a signature block whose image digest does not match
A CRC-valid Secure Boot v2 block can carry another image's digest. The
block recorded image_digest_matches false, but no limitation said so, unlike
a checksum or appended-SHA mismatch, so CLI and MCP summaries showed only
the generic unverified note. The mismatch is now a limitation; coverage
stays complete and signature_status stays unverified. The three other
signature-sector limitations move into ESP_IMAGE_LIMITATIONS with their
text unchanged, so all ESP image wording has one owner.
* refactor(esp-image): remove redundant descriptor byte guards
---------
Co-authored-by: gensokyo-dev <[email protected]>
Seal sensitive terminal chunks before deferred rendering, apply one bounded
capture-local literal matcher, and retain exact redacted byte accounting.
Keep private input facts in explicit fields and separate comparison policies.
Retain distinct terminal failures once and verify structured refusal facts.
Fixes#7782
BREAKING CHANGE: sensitive interaction data is empty with sensitive and input_bytes fields instead of a byte-count placeholder. Sensitive raw_data is also redacted. Read input_bytes and recapture both comparison sides when migrating older private-input Evidence.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
CSP3 parsing keeps the first directive of each name in a policy and ignores
later repeats; Chrome reports "Ignoring duplicate Content-Security-Policy
directive". parseCsp flattened every policy's directives into one stream, so
a header such as `script-src 'self'; script-src 'unsafe-inline'` reported the
ignored `'unsafe-inline'` directive as declared and counted nonces and hashes
from it. Track directive names per policy so only the effective directive is
retained, while the same name in separate comma-separated or repeated-field
policies is still listed once per policy.
* fix(javascript): keep both-direction feature traces from crossing shared containers
trace_application_feature with direction "both" walked the application
graph as if it were undirected. From a seed, one outgoing or incoming step
into a shared container (an asset or the artifact root) followed by steps
in the other direction reached every sibling of that container, so a trace
seeded on one storage key returned the whole connected graph.
"both" now returns the union of the outgoing and incoming closures. Each
walk keeps its own predecessor chains, so a terminal path never changes
direction midway. "outgoing" and "incoming" are unchanged.
* test(javascript): check both-direction trace paths end to end
The existing tests checked predecessor maps but no returned path. Add a
trace whose seed reaches one storage terminal outgoing and another
incoming, and assert each path's complete node_ids and edge_ids. A
pathTo that reads only the first walk's map returns a one-node path for
the incoming terminal and fails this test.
* fix(javascript): confine only ascended containers in both-direction traces
Making "both" the union of separate outgoing and incoming walks dropped
modules that a feature reaches by crossing directions. In the Notes
known-answer evaluation, the notes:export channel is pointed at by both
the preload asset (invokes) and the handler (handles); its preload,
renderer and csv modules are reached only through relationships that
change direction, so the trace kept main.js alone and the feature_modules
claim failed in tests/boundary/mcp/agentKnownAnswerMcp.test.ts.
Walk both directions in one traversal again, and confine only the
containment case the change was meant to stop: a node reached by stepping
up a contains edge from one of its children follows only further contains
steps up. A shared asset or artifact root then no longer leads into its
other children, while relationships across the feature are kept. The
Notes trace returns csv.js, main.js, preload.js and renderer.js again
(30 nodes; the previous main returned the whole 32-node graph).
outgoing and incoming traces are unchanged, and paths come from one
predecessor map again, as before this branch.
* fix(javascript): stop double-expanding application projection estimates
estimateJavaScriptStaticApplicationProjection multiplied the per-record
metadata allowance (2 KB plus the path) by the 8x expansion that it also
applies to checkpoint bytes. A projected record serializes to about 0.46 KB,
so the allowance alone already covers retention; expanded again, the
charge was about 25x the serialized application relationships.
The charge accumulates in the parent's shared result budget. On a
synthetic 2.8 MB Vite-style build (88 files, one 456 KB chunk), the first
10 files used 2.0 GB of a 2.08 GB budget while the parent heap held about
300 MiB. The other 78 files lost their application relationships and were
offered a 0-byte semantic budget. On a 201-file build the charges exceeded
the budget at file 93 and the analysis failed with resource_constraint,
leaving the remaining sources without static facts.
Keep the 8x expansion for checkpoint and repeated bytes and count the
per-record allowance once. The same builds now refuse 14 and 102
projections instead of 78 and a failed run; peak RSS stays at 1.2 GB and
2.0 GB under a 4.2 GB parent heap limit.
workerFailureComposition simulated 64 KiB of parent result budget, which
the corrected charge for its one-finding checkpoint fits. Lower that
headroom to 32 KiB so the case still exercises a refused projection.
* test(javascript): cover application projections near the shared parent budget
The estimate test compares one file's charge with its serialized graph,
but no test put several sources against one parent result budget. Replay
one recorded static checkpoint for five sources against an exact budget:
sources 1-3 fit, source 4 misses by half a source and keeps its static
facts with the reported required and budget bytes, and the last source
fits again once no checkpoint reserve is held back. A worker that charges
the projection twice, or not at all, passes the existing tests and fails
this one.
* test(javascript): cover a malformed source before a valid one under the shared budget
The testing guide asks representation-expansion and capacity changes for
a large malformed input followed by a valid input, and for escaped or
expanded representations.
Under an exact parent budget, a 3 MiB checkpoint cut off inside a string
comes first and is rejected as unreadable output with no static facts.
The valid source after it keeps its application relationships; it fits
only if the rejected bytes took no charge. A worker that charges the
static checkpoint on receipt, before parsing, passes the existing tests
and fails this one.
The estimate ratio test also runs with a nested source path and storage
keys holding quotes, backslashes, a tab and multibyte text, which expand
in serialized JSON; the charge stays within 3x to 8x of the serialized
application graph (5.3x).
* fix(javascript): resolve Vite build-output preload dependencies
With an absolute base, Vite writes __vite__mapDeps entries relative to the
build output directory (for example assets/chunk.js), and its preload helper
prefixes the configured base at runtime. REA emitted these entries as
dynamic-import module specifiers, so a scheme-less path without a leading
dot resolved as a bare package and every entry was reported as an
unresolved "external" module, even when the build output directory was
selected.
The static analyzer now marks such entries with a vite-output: specifier,
as Webpack chunk loads are marked with chunk:. Entries starting with ./,
../ or /, and scheme-qualified entries, keep their previous handling. The
graph resolves a marked entry as a URL path under each ancestor directory
of the importing file; exactly one ancestor holding the file establishes
the build output directory. Otherwise the entry is not-found (or
unavailable when several ancestors match) with a limitation that names
the build output directory.
On a synthetic 7-file Vite build, the 12 preload entries that were
"external" now resolve when the build output directory is selected, and
are not-found with that guidance when only assets/ is selected.
* fix(javascript): keep unanalyzed Vite output candidates
resolveViteOutputDependency considered only resolved ancestors. A file
that exists under an ancestor but was not analyzed (for example
dist/assets/lazy.css for the entry assets/lazy.css) resolves there as
unavailable, and that result was replaced by the root's not-found with a
limitation saying that no ancestor holds the file.
Keep resolved and unavailable ancestor candidates together. One candidate
is returned with its own path and limitations; several are reported as
unavailable with every candidate path and limitation. Only when no
ancestor can hold the file does the root's not-found result stand.
Apktool 2.9.0 replaced SnakeYAML with the YamlWriter that 3.x still uses:
apktool.yml starts with an untagged `version: 2.x.y` line and strings are
written unquoted with Java-style escapes. Only a `version: 3.x.y` header
selected the producer dialect, so 2.9.0-2.12.x output went through YAML
core parsing: a versionName of `1.0 #beta` became `1.0`, escaped quotes kept
their backslashes, an empty name became null, and `1.0: beta` made the whole
decode fail as invalid metadata.
Select the line-oriented reader for 2.9.0 and later as well. Older tagged
SnakeYAML documents keep YAML scalar semantics.
Every source string literal without an established Next root was tested
against /(?:^|\/)app\/(?:[\s\S]*\/)?page(?:\.[cm]?[jt]sx?|-[^/?#]+\.js|)(?:[?#].*)?$/u.
The unanchored search restarted at every app/ segment and rescanned to the
end of the literal, and each page? or page# candidate scanned to the next
line terminator before $ failed. A long literal therefore took quadratic
time on the analysis thread: about 6 s for a 256 KB literal of repeated
app/ segments, and 3.5 s for 224 KB of x/page? followed by a newline.
The first app/ segment admits every later page segment, and . cannot cross
a line terminator, so a query or fragment after the page filename starts on
the last line. Test the tail after the first app/ segment one page segment
at a time instead; results are unchanged.
Use one command owner for setup, client writers, Gemini policy and CLI defaults.
Migrate recognized runner registrations with backups and remove runner prewarm.
Retain unknown identities and exercise real offline npm, CLI and MCP workflows.
Fixes#7757
BREAKING CHANGE: managed MCP registrations use the selected absolute runtime
and REA entry script, including package-runner cache paths. Keep that installation
available or restore it before setup; unknown registrations require confirmed
manual repair. Setup no longer returns prewarm or emits its progress events.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
* feat(artifacts): add U-Boot legacy uImage reader (part of #7129)
Recognize legacy uImages by their 0x27051956 magic and inventory them as containers: 64-byte header facts (arch/os/type/compression raw and named, load/entry addresses, name, timestamp) with header and data CRC32 verified, stored payload extractable as payload.bin, and multi-file images split from the stored size table into image-<n>.bin. Compressed payloads stay as stored; nothing is executed or decompressed. Wires the format through classification, the artifact reader dispatch, extraction, and the evidence/snapshot format vocabularies. Related to #7129 (uImage part only); HEX/SREC and FIT are out of scope.
* fix(uimage): preserve aligned component ranges and exact header facts
* fix(uimage): retain buffered members through consumption and cancellation
* fix(uimage): preserve active read and validation failure precedence
---------
Co-authored-by: gensokyo-dev <[email protected]>
Admit the final macOS Hopper launcher arguments before application handoff.
Keep prepared-image source identity, return precise typed refusal locations,
and preserve bootstrap/lease cleanup. Update native CLI/MCP recovery
coverage and the installation guide.
Fixes#7727
BREAKING CHANGE: macOS Hopper launches refuse double quotes, backslashes
and line breaks in final helper paths or arguments before application
handoff. Select literal-safe launcher/runtime/database paths and loader
arguments, or a compatible provider. Original executable names can remain
unchanged when their prepared image path is literal-safe.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
* feat(artifacts): read UnityFS AssetBundle containers
inspect-artifact and extract-artifact now recognize the UnityFS signature
and treat format versions 6 through 9 as containers. Header facts (format
version, player/engine strings, declared size, dataflags, blocks-info
bounds and the declared 16-byte data hash) are retained alongside a
directory of CAB-* and .resS members. Member bytes stream on demand:
only the blocks covering the requested member are read and decompressed
with bounded expansion and cancellation.
Bundles declaring asset-bundle encryption keep header facts and report
unknown directory coverage. LZMA blocks-info reports unknown coverage;
LZMA member reads fail as unavailable while the directory stays
readable. Malformed counts, out-of-stream members and declared sizes
beyond the file fail as format errors. UnityFS classifies by bytes in
root detection, inventory graph construction and desktop inspection,
which emits a unityfs-header fact.
Validation: tsc --noEmit, oxlint (no new findings on touched files),
17 reader tests covering LZ4HC/uncompressed tables, blocks-info at end,
format 6 without padding alignment, multi-block members, malformed
input, unsupported compression, cancellation and abandoned streams;
full artifacts/domain/conformance suites pass. Verified against one
real YooAsset bundle (Unity 2021.3.32f1, format 8, LZ4HC): the
streamed SerializedFile contained its managed assembly byte-identical
to a UnityPy extraction. Other producer variants unverified.
* fix(unityfs): own bounded member reads and preserve partial evidence
* refactor(unityfs): clarify producer revision flag decisions
* fix(artifacts): preserve coverage authority and bound UnityFS block work
---------
Co-authored-by: gensokyo-dev <[email protected]>
Resolve Windows ADB, Apktool and update commands against the selected
PATH/PATHEXT before launch. Keep explicit paths and relative PATH choices,
report lookup failures, and reuse absolute batch invocation and ownership.
Add native launch/argv regressions, real npm metadata probes and focused
CI ownership so these boundaries retain Windows verification on the fast tier.
Fixes#7724
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Reuse shared padded-base64 facts and derive encoded admission from the existing decoded-byte budget. Refuse overflow before decoded allocation and retain padded syntax. Cover capacity, compact HTTP replies and live MCP state readback.
Fixes#7719
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
* feat(artifacts): inventory and extract Intel HEX and Motorola S-record regions
open_binary refused Intel HEX and S-record programming files as an
unsupported format, so inspect_artifact and extract_artifact could not
answer which bytes load at which addresses.
Recognize both encodings by a fully valid first record and decode them
statically into address-contiguous regions, each an inventory entry named
0x<start>-0x<end>.bin and extractable as raw bytes. Root format_metadata
records the encoding and variant, record counts, unique and duplicate
bytes, address bounds, termination, declared start address, S0 header and
validated S5/S6 count. Gaps are never filled; start addresses are declared
facts, not proven entry points.
Checksum, record-type, byte-count, line-length, overlap-conflict, address
wrap and post-termination errors fail closed with the line number; a
missing termination record leaves coverage unknown. Decoding is bounded by
a 128 MiB file ceiling (decoded bytes are at most half the file and are
reserved after the size check) and 65,536 address-discontiguous runs.
Refs #7129
* fix(artifacts): refuse Intel HEX extended address records with a non-zero address
* fix(artifacts): bound HEX region copies and retain stream cancellation
---------
Co-authored-by: gensokyo-dev <[email protected]>
Refresh compatible security fixes and verified dependency notices. Keep
historical HTTP/3 guest verification separate from the refreshed trust store.
Fixes#7700
Assisted-by: Aya (gensokyo-skills:aya-source-investigation)
* feat(environment): serve the writes callers make without recorded form fields
- A create member a recording sent empty ("", null, false, [] or {}) is no
longer required; a call that leaves it out is applied as sending that value
(body.blank).
- List views render envelope members that list the listed entities' ids
(Paperless's `all`), so creates in such collections are confirmed.
- Updates move their entity in collections kept sorted by a field they write,
and a failed whole-collection read after an update can teach that order.
- A later create that stores an unruled field otherwise is withheld at the
create, and an item read may fill values a create's answer had not loaded.
- An unsent field respelled between null, [] and {} is unchanged.
- The skill asks for one create with every field and one with only what a
person must give.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skill): show how to read signed-in pages before recording
Agents guessed capture keys (artifacts, dom, final) to see pages behind
sign-in, which inspect_web_page cannot reach.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): accept every empty spelling recorded creates sent
A create that fills only what a person must give sends `null` where the
full create sent a string, and `null` or `false` for the same flag. The
merged plan now accepts each recorded empty value, and a call that leaves
the member out is applied as the earliest call sent it.
Also adds the spec folder for this change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skill): keep the vary-fields step neutral on UI or API writes
Agents read "the form" as a cue to write through the UI, where some
applications' request bodies are not exposed; a body-unusable UI write
now re-records once with request steps.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record verification for blank members and sorted writes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): address review of blank members and sorted writes
- Bulk updates move their entities in turn against the others' earlier
values and install the final order once, as replay does.
- A member's recorded empty values stay accepted once a later call teaches
its default; a left-out member with a default keeps the default.
- Write answers spell empty lists and maps as their recorded answers did.
- A rename can teach a sort when the creates chose one by a field the
update did not write, such as ids that ascend with titles.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): serve the writes that later reads explain
- An item read whose object is marked deleted (deleted, isDeleted or
is_deleted true) agrees with an absent entity, as EspoCRM answers a
deleted record; such an object never seeds or adopts an entity.
- maxSize, max_results and maxResults are page sizes, for whole-collection
list lines and the list query's limit role.
- An item view also shows the fields only reads after its type's first write
show, when no initial entity holds them (Vikunja's task subscription),
without requiring them; write answers leave such a field out for entities
without it, and it no longer withholds the write as incomplete-entities.
- The skill asks updates to change the fields a person would change, and
expects a deleted object to read back as 404 or marked deleted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer sent empty members as sent, and keep a placed create's order
- A write answer spells an empty list or map the call did not send as its
recorded answer did; one the call sent is answered as sent, so a create
sending `[]` is not answered `null` because the first recorded create was.
- A read listing the whole collection that placed a create leaves the create
only the sorted inserts whose order that read follows, so a later rename
cannot teach a sort an earlier read ruled out.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer bulk updates without fields only later reads show
A bulk update's answer renders each entity with the same allowance a single
update's answer has: a field only reads after the type's first write showed,
which no initial entity holds, is left out for an entity without it, so a
bulk update of a seeded task is no longer refused for lacking it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): correct how the EspoCRM contact update task passes
The agent trajectories show gpt-oss passes it by creating a replacement contact
and deleting the original, since the served update refuses emailAddress; the
update itself is refused in both builds.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer a read of a served-deleted entity marked deleted
An application that soft-deletes still answers a deleted record by id, marked
deleted. Serving now keeps an entity a served delete removed from a type whose
recorded item reads show that, until a reset or load and within the size
data/add loads, and answers a read of it with its last fields and the flag
true, instead of refusing the read.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep unloaded create answers, check bulk answers with sent members, narrow inserts only on collection-order reads
- A create whose item read loaded values its answer had not records them
(`observation.read.unloaded`), and a served create's answer keeps them as
the recorded answer showed them, a zero-time owner included, while the state
holds the read's.
- Replay checks a bulk update's recorded answers with each target's sent
members, as serving answers them, so echoed `null` and `[]` no longer make
the plan body-unusable.
- A whole-collection read whose query sorts it shows the query's order, so it
no longer narrows a create's sorted insert candidates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the census with main's unknown-clock rule merged
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): serve query numbers, counted values and made objects
- A query parameter accepts a number or boolean, which serving sends as its
JSON text, as agents send page sizes and flags.
- A sequence may start at the value its first create took above every value
its type held (Vikunja's per-project task index), and a later create's
composed, template or started-sequence rule is adopted when it also spells
the first create's value.
- A made-object rule explains an object the application makes with each
created entity: members the request stamped, members holding the entity's
own id, and counts one past the held objects' values.
- A create's answer names the id of the one object a one-element array member
holds, so bulk creates link to their type.
- The skill reads each listed object by id before writing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): let a linked create take path values it stores
A create linked to its type's own collection, whose path has no parameters,
accepts any path values when its plan stores each one in a field, as a Vikunja
task stores its project; replay and serving apply it alike.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn a sort from a rename only when moving its entities reaches it
Serving moves only the entities a write changes in a collection kept sorted,
so replay now tries a sort after a rename only when the entities no write the
read exercised changed already follow it. Seeded folders listed out of title
order no longer teach a title sort that serving would answer with another
order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep unloaded create answers a sent value did not echo, and learn no sort from query-sorted reads
- A served create keeps a value its recorded answer had not loaded unless the
call sent that member and the plan stores it as sent, so a create sending
`active: false` is answered `false` as recorded while the state holds the
read's `true`.
- A rename no longer teaches a sort from a whole-collection read its query
sorts, whose ties need not follow the collection's own order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): verify query values, counted values and made objects
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn the soft-delete flag in the state view and serve by it
Extraction now records the member later item reads set true on a record the
application soft-deleted, and the item view carries it as `deleted_flag`.
Serving reads that fact instead of scanning recorded bodies itself, so one
owner decides what a soft delete looks like.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the call-values checks at #727493821024
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn the soft-delete flag from item reads with a query too
A client may read a deleted record back only with a query, such as a
cache-busting parameter. Every successful read of the item tool now shows the
flag; only plain later reads still add fields.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the call-values checks at #727445832406
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn sorts only from orders a list read confirmed
- A sort retry after a failed whole-collection read requires the entities no
write changed since a list last showed them to follow it already, rather than
excusing every entity the update's tool ever wrote: an earlier rename a list
confirmed in place stays where it was.
- An update takes its creates' sort only from a create a list read showed
placed; replay still tries a sort a create guessed from the initial order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): drop entities read back as deleted and answer soft deletes as deleted
- A held entity a later read shows marked deleted is dropped, not adopted with
the marked object's fields.
- Bulk-update replay renders answers with the optional and later-only fields
serving uses, so a bulk update of created and seeded entities replays.
- A served delete of a soft-deleting type answers with the entity marked
deleted, single and bulk.
- A deleted flag only query reads showed is a field of the item view, so a
served tombstone keeps it where live records omit it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): check made objects as recorded and keep their ids and clocks honest
- A made object's clock counts as a write clock, so a capture with no clock
anchor withholds the create instead of stamping 1970.
- Members holding the entity's identity follow the id written, generated or
sent, through the type's id field.
- A later create's made object is checked as recorded, before its own members
are rewritten, so one naming another entity withholds the plan.
- A later create's template is adopted only when it spells that create's value
too, not just when the first create's text holds its source value.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): move updated entities only by placed sorts and keep query-sorted reads from confirming order
Replay moved an updated entity by a sort a create only guessed, while the
update plan exported only placed sorts, so a read could confirm an update
by a move serving would not make. Both now use the placed sort. A passing
list read clears its entities from the moved set only when the state
decides the order of every entity it lists, so a read whose query sorts
it no longer hides an earlier move from a later sort check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): count six withheld plans that explain more, not seven
The bulk task create counted among them is served on this branch.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): try a learned sort by moving each pending write in turn, as serving does
#retrySort sorted the whole collection, which a list could match while
serving, moving each renamed entity in turn against the others' values
before their own writes, gave another order. The state now records the
values each write replaced, and the retry replays the writes since a
list in collection order last showed their entities.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer a served delete with the deleted flag its recorded answer showed
An application may answer a delete with the record as it was and mark it
deleted only on later reads. Delete answers, single and bulk, now take
the flag's value from the recorded answer; reads of a served-deleted
entity still show it marked deleted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): set aside two hunks next to lines main's saved-values change rewrites
Main's #7267 changes the lines beside the blank-member docs and the
empty-spelling check in explainUpdate. Setting them aside lets that merge
apply cleanly; the commit after it puts them back on top of main's text.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): restore the set-aside hunks on main and bump the skill to 51
Puts the blank-member docs and the empty-spelling check back beside main's
slug and composed text and heldSlugRule. Main is at skill 50 with
different text, so this branch's skill changes take 51.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): bump the skill to 52 above #7260's 51
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): put back the call-values hunks main's merge took its side for, and bump the skill to 53
The merge of main kept main's side where it overlapped this branch, so it
could commit without a conflict. This restores this branch's side next to
main's: made-object agreement beside the & slug spellings, sequence starts
in the earlier evidence (now built by earlierOf), the combined value
imports, and recording step 2 beside main's UI-first step 3. Main is at
skill 50 and #7274 at 52, so this branch takes 53.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep sorts a list showed in place ruled out, and created answers' nested members as recorded
A whole list in collection order that showed a renamed entity where a
sort by the renamed field would have moved it now rules that sort out, so
a later rename of the same entity cannot teach it. A create's answer
leaves out members its item read added inside an object the answer
showed, as the recorded answer did; the state still stores them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep a later create's made object only with clocks its request stamped
followsMadeObjects only checked that a made object's clocks parsed, so a
later create whose object showed an older clock still passed, while serving
stamps the write's clock. Its clocks must now be in the rule's format and
within the stamp window of that create's request.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): compare soft-deleted read fields
* fix(environment): reject contradictory learned sequences
* fix(environment): preserve initially deleted item observations
* fix(environment): index stored deleted entities separately
* fix(environment): replay lists that include deleted records
* fix(environment): make no count of a made-object member earlier creates held alike
A count only a later create shows starts at the exemplar's value, which is
right only while the exemplar is the one create accepted: creates accepted
after it showed the member unchanged, which a count would have moved. The
merge now refuses such a count, as the top-level sequence check does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
A saved response's decoded size is bounded by its received size, or the
declared length of one served from cache, times 1032 for each gzip or deflate
coding, the most DEFLATE expands. Grafana answers folder creates gzipped, so a
UI step saving a new folder's uid was refused. Other codings and unknown or
zero sizes stay refused before the body is read.
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(environment): serve the writes callers make without recorded form fields
- A create member a recording sent empty ("", null, false, [] or {}) is no
longer required; a call that leaves it out is applied as sending that value
(body.blank).
- List views render envelope members that list the listed entities' ids
(Paperless's `all`), so creates in such collections are confirmed.
- Updates move their entity in collections kept sorted by a field they write,
and a failed whole-collection read after an update can teach that order.
- A later create that stores an unruled field otherwise is withheld at the
create, and an item read may fill values a create's answer had not loaded.
- An unsent field respelled between null, [] and {} is unchanged.
- The skill asks for one create with every field and one with only what a
person must give.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skill): show how to read signed-in pages before recording
Agents guessed capture keys (artifacts, dom, final) to see pages behind
sign-in, which inspect_web_page cannot reach.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): accept every empty spelling recorded creates sent
A create that fills only what a person must give sends `null` where the
full create sent a string, and `null` or `false` for the same flag. The
merged plan now accepts each recorded empty value, and a call that leaves
the member out is applied as the earliest call sent it.
Also adds the spec folder for this change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skill): keep the vary-fields step neutral on UI or API writes
Agents read "the form" as a cue to write through the UI, where some
applications' request bodies are not exposed; a body-unusable UI write
now re-records once with request steps.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record verification for blank members and sorted writes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): address review of blank members and sorted writes
- Bulk updates move their entities in turn against the others' earlier
values and install the final order once, as replay does.
- A member's recorded empty values stay accepted once a later call teaches
its default; a left-out member with a default keeps the default.
- Write answers spell empty lists and maps as their recorded answers did.
- A rename can teach a sort when the creates chose one by a field the
update did not write, such as ids that ascend with titles.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer sent empty members as sent, and keep a placed create's order
- A write answer spells an empty list or map the call did not send as its
recorded answer did; one the call sent is answered as sent, so a create
sending `[]` is not answered `null` because the first recorded create was.
- A read listing the whole collection that placed a create leaves the create
only the sorted inserts whose order that read follows, so a later rename
cannot teach a sort an earlier read ruled out.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep unloaded create answers, check bulk answers with sent members, narrow inserts only on collection-order reads
- A create whose item read loaded values its answer had not records them
(`observation.read.unloaded`), and a served create's answer keeps them as
the recorded answer showed them, a zero-time owner included, while the state
holds the read's.
- Replay checks a bulk update's recorded answers with each target's sent
members, as serving answers them, so echoed `null` and `[]` no longer make
the plan body-unusable.
- A whole-collection read whose query sorts it shows the query's order, so it
no longer narrows a create's sorted insert candidates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn a sort from a rename only when moving its entities reaches it
Serving moves only the entities a write changes in a collection kept sorted,
so replay now tries a sort after a rename only when the entities no write the
read exercised changed already follow it. Seeded folders listed out of title
order no longer teach a title sort that serving would answer with another
order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep unloaded create answers a sent value did not echo, and learn no sort from query-sorted reads
- A served create keeps a value its recorded answer had not loaded unless the
call sent that member and the plan stores it as sent, so a create sending
`active: false` is answered `false` as recorded while the state holds the
read's `true`.
- A rename no longer teaches a sort from a whole-collection read its query
sorts, whose ties need not follow the collection's own order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn sorts only from orders a list read confirmed
- A sort retry after a failed whole-collection read requires the entities no
write changed since a list last showed them to follow it already, rather than
excusing every entity the update's tool ever wrote: an earlier rename a list
confirmed in place stays where it was.
- An update takes its creates' sort only from a create a list read showed
placed; replay still tries a sort a create guessed from the initial order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): move updated entities only by placed sorts and keep query-sorted reads from confirming order
Replay moved an updated entity by a sort a create only guessed, while the
update plan exported only placed sorts, so a read could confirm an update
by a move serving would not make. Both now use the placed sort. A passing
list read clears its entities from the moved set only when the state
decides the order of every entity it lists, so a read whose query sorts
it no longer hides an earlier move from a later sort check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): try a learned sort by moving each pending write in turn, as serving does
#retrySort sorted the whole collection, which a list could match while
serving, moving each renamed entity in turn against the others' values
before their own writes, gave another order. The state now records the
values each write replaced, and the retry replays the writes since a
list in collection order last showed their entities.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): set aside two hunks next to lines main's saved-values change rewrites
Main's #7267 changes the lines beside the blank-member docs and the
empty-spelling check in explainUpdate. Setting them aside lets that merge
apply cleanly; the commit after it puts them back on top of main's text.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): restore the set-aside hunks on main and bump the skill to 51
Puts the blank-member docs and the empty-spelling check back beside main's
slug and composed text and heldSlugRule. Main is at skill 50 with
different text, so this branch's skill changes take 51.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep sorts a list showed in place ruled out, and created answers' nested members as recorded
A whole list in collection order that showed a renamed entity where a
sort by the renamed field would have moved it now rules that sort out, so
a later rename of the same entity cannot teach it. A create's answer
leaves out members its item read added inside an object the answer
showed, as the recorded answer did; the state still stores them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(environment): serve the writes callers make without recorded form fields
- A create member a recording sent empty ("", null, false, [] or {}) is no
longer required; a call that leaves it out is applied as sending that value
(body.blank).
- List views render envelope members that list the listed entities' ids
(Paperless's `all`), so creates in such collections are confirmed.
- Updates move their entity in collections kept sorted by a field they write,
and a failed whole-collection read after an update can teach that order.
- A later create that stores an unruled field otherwise is withheld at the
create, and an item read may fill values a create's answer had not loaded.
- An unsent field respelled between null, [] and {} is unchanged.
- The skill asks for one create with every field and one with only what a
person must give.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skill): show how to read signed-in pages before recording
Agents guessed capture keys (artifacts, dom, final) to see pages behind
sign-in, which inspect_web_page cannot reach.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): accept every empty spelling recorded creates sent
A create that fills only what a person must give sends `null` where the
full create sent a string, and `null` or `false` for the same flag. The
merged plan now accepts each recorded empty value, and a call that leaves
the member out is applied as the earliest call sent it.
Also adds the spec folder for this change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skill): keep the vary-fields step neutral on UI or API writes
Agents read "the form" as a cue to write through the UI, where some
applications' request bodies are not exposed; a body-unusable UI write
now re-records once with request steps.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record verification for blank members and sorted writes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): address review of blank members and sorted writes
- Bulk updates move their entities in turn against the others' earlier
values and install the final order once, as replay does.
- A member's recorded empty values stay accepted once a later call teaches
its default; a left-out member with a default keeps the default.
- Write answers spell empty lists and maps as their recorded answers did.
- A rename can teach a sort when the creates chose one by a field the
update did not write, such as ids that ascend with titles.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): serve the writes that later reads explain
- An item read whose object is marked deleted (deleted, isDeleted or
is_deleted true) agrees with an absent entity, as EspoCRM answers a
deleted record; such an object never seeds or adopts an entity.
- maxSize, max_results and maxResults are page sizes, for whole-collection
list lines and the list query's limit role.
- An item view also shows the fields only reads after its type's first write
show, when no initial entity holds them (Vikunja's task subscription),
without requiring them; write answers leave such a field out for entities
without it, and it no longer withholds the write as incomplete-entities.
- The skill asks updates to change the fields a person would change, and
expects a deleted object to read back as 404 or marked deleted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer sent empty members as sent, and keep a placed create's order
- A write answer spells an empty list or map the call did not send as its
recorded answer did; one the call sent is answered as sent, so a create
sending `[]` is not answered `null` because the first recorded create was.
- A read listing the whole collection that placed a create leaves the create
only the sorted inserts whose order that read follows, so a later rename
cannot teach a sort an earlier read ruled out.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer bulk updates without fields only later reads show
A bulk update's answer renders each entity with the same allowance a single
update's answer has: a field only reads after the type's first write showed,
which no initial entity holds, is left out for an entity without it, so a
bulk update of a seeded task is no longer refused for lacking it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): correct how the EspoCRM contact update task passes
The agent trajectories show gpt-oss passes it by creating a replacement contact
and deleting the original, since the served update refuses emailAddress; the
update itself is refused in both builds.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer a read of a served-deleted entity marked deleted
An application that soft-deletes still answers a deleted record by id, marked
deleted. Serving now keeps an entity a served delete removed from a type whose
recorded item reads show that, until a reset or load and within the size
data/add loads, and answers a read of it with its last fields and the flag
true, instead of refusing the read.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep unloaded create answers, check bulk answers with sent members, narrow inserts only on collection-order reads
- A create whose item read loaded values its answer had not records them
(`observation.read.unloaded`), and a served create's answer keeps them as
the recorded answer showed them, a zero-time owner included, while the state
holds the read's.
- Replay checks a bulk update's recorded answers with each target's sent
members, as serving answers them, so echoed `null` and `[]` no longer make
the plan body-unusable.
- A whole-collection read whose query sorts it shows the query's order, so it
no longer narrows a create's sorted insert candidates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the census with main's unknown-clock rule merged
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn a sort from a rename only when moving its entities reaches it
Serving moves only the entities a write changes in a collection kept sorted,
so replay now tries a sort after a rename only when the entities no write the
read exercised changed already follow it. Seeded folders listed out of title
order no longer teach a title sort that serving would answer with another
order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep unloaded create answers a sent value did not echo, and learn no sort from query-sorted reads
- A served create keeps a value its recorded answer had not loaded unless the
call sent that member and the plan stores it as sent, so a create sending
`active: false` is answered `false` as recorded while the state holds the
read's `true`.
- A rename no longer teaches a sort from a whole-collection read its query
sorts, whose ties need not follow the collection's own order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn the soft-delete flag in the state view and serve by it
Extraction now records the member later item reads set true on a record the
application soft-deleted, and the item view carries it as `deleted_flag`.
Serving reads that fact instead of scanning recorded bodies itself, so one
owner decides what a soft delete looks like.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn the soft-delete flag from item reads with a query too
A client may read a deleted record back only with a query, such as a
cache-busting parameter. Every successful read of the item tool now shows the
flag; only plain later reads still add fields.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): learn sorts only from orders a list read confirmed
- A sort retry after a failed whole-collection read requires the entities no
write changed since a list last showed them to follow it already, rather than
excusing every entity the update's tool ever wrote: an earlier rename a list
confirmed in place stays where it was.
- An update takes its creates' sort only from a create a list read showed
placed; replay still tries a sort a create guessed from the initial order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): drop entities read back as deleted and answer soft deletes as deleted
- A held entity a later read shows marked deleted is dropped, not adopted with
the marked object's fields.
- Bulk-update replay renders answers with the optional and later-only fields
serving uses, so a bulk update of created and seeded entities replays.
- A served delete of a soft-deleting type answers with the entity marked
deleted, single and bulk.
- A deleted flag only query reads showed is a field of the item view, so a
served tombstone keeps it where live records omit it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): move updated entities only by placed sorts and keep query-sorted reads from confirming order
Replay moved an updated entity by a sort a create only guessed, while the
update plan exported only placed sorts, so a read could confirm an update
by a move serving would not make. Both now use the placed sort. A passing
list read clears its entities from the moved set only when the state
decides the order of every entity it lists, so a read whose query sorts
it no longer hides an earlier move from a later sort check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): try a learned sort by moving each pending write in turn, as serving does
#retrySort sorted the whole collection, which a list could match while
serving, moving each renamed entity in turn against the others' values
before their own writes, gave another order. The state now records the
values each write replaced, and the retry replays the writes since a
list in collection order last showed their entities.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer a served delete with the deleted flag its recorded answer showed
An application may answer a delete with the record as it was and mark it
deleted only on later reads. Delete answers, single and bulk, now take
the flag's value from the recorded answer; reads of a served-deleted
entity still show it marked deleted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): set aside two hunks next to lines main's saved-values change rewrites
Main's #7267 changes the lines beside the blank-member docs and the
empty-spelling check in explainUpdate. Setting them aside lets that merge
apply cleanly; the commit after it puts them back on top of main's text.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): restore the set-aside hunks on main and bump the skill to 51
Puts the blank-member docs and the empty-spelling check back beside main's
slug and composed text and heldSlugRule. Main is at skill 50 with
different text, so this branch's skill changes take 51.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(environment): bump the skill to 52 above #7260's 51
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep sorts a list showed in place ruled out, and created answers' nested members as recorded
A whole list in collection order that showed a renamed entity where a
sort by the renamed field would have moved it now rules that sort out, so
a later rename of the same entity cannot teach it. A create's answer
leaves out members its item read added inside an object the answer
showed, as the recorded answer did; the state still stores them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): compare soft-deleted read fields
* fix(environment): preserve initially deleted item observations
* fix(environment): index stored deleted entities separately
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
Check destination kind before overwrite policy and cover directory recovery,
regular-file controls, symlink modes and preservation.
Fixes#7444.
Independent review: APPROVE for patch SHA-256
3d65eb23d01da9b6df786ad0db337dcff79d85923f23678a5168babb88c16080,
relayed by the coordinating thread (reviewer identity not supplied in the
delegated packet). Triggers: file I/O, error handling, three shipping files.
Validation: baseline/candidate runtime builds and actual synthetic CLI probes;
initial focused run 70 passed, 2 Windows EPERM fixture failures, 3 skipped.
Final changed filesystem file 42 passed, 5 skipped; symlink semantics and
full aggregate checks remain unverified. Targeted format/lint/discovery passed.
Ship Claude Code and Codex from one release-pinned npm artifact with bundled runtime dependencies, extraction-only MCP qualification, plugin-aware recovery, and synchronized release metadata.
Integrates #2815 and the original proposal #1977. The original commits remain available on the PR branch; retain their author and assistant credit in this squash.
Validation: required CI passed, real Codex/Claude loading verified, and reviewed packaging/recovery findings addressed.
Co-authored-by: GamerDubz <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: OpenAI Codex <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Retire redundant historical JSON copies from the active documentation tree after verifying identical published release snapshots and an immutable route-report archive. Preserve current release data, original evidence, release publication logic and historical access.
Validation: 48 release tests; documentation conformance and rendered archive link/digest; Git blob/SHA-256 checks; selected current-head CI passed. Greptile: 5/5 with no actionable findings. Codex GitHub review unavailable due quota.
Closes#7517.
Migrate MCP session tests to the named lifecycle output schema and preserve raw reply envelopes. Move R8 type/alias checks to the production field resolver and recovery guidance checks to their scoped factory; remove superseded test-only exports and an unused import.
Validation: 99 focused tests, an exact-old-schema negative control, full selected CI including all 8 shards and source tests, and Greptile source review passed. Fresh-head real stdio MCP analyzed [email protected] and retained a complete authenticated Evidence bundle after close; compact/full catalogs are unchanged. Full local typed lint hit the 3 GiB memory guard; scoped typed lint passed. Codex review quota exhausted.
Fixes#7513.
Use one typed-checker worker by default without changing semantic scope or caller overrides. Reuse the bounded CLI worker for pure argument validation while retaining fresh-process coverage and all upstream cases.
Keep registration tests independent of real package-runner downloads while asserting the actual prewarm command and startup context. Pass the advertised binary_session definition by name to package verification, retaining default SDK validation and complete catalog/contract checks while avoiding compilation of unrelated tool outputs. Preserve all four installed-tarball scenarios and existing deadlines.
Strengthen Hermes preview operation/backup assertions and clarify physical-path routing documentation.
Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
Android overload selection declared a plain number, so the CLI argument
parser coerced the raw token with Number before the schema ran. An empty
token therefore arrived as 0, and hexadecimal, exponent, signed, and
float spellings arrived as their numeric value, leaving both commands to
proceed to artifact admission on a selector the caller never wrote as a
decimal integer.
Reuse the existing decimal token parser that the other integer CLI
options already use. Omitted and explicit decimal zero keep their current
meanings, and the published schema for overloadIndex is unchanged.
Validation: npm run compile, the cli-boundary vitest project for
tests/boundary/cli/cliIntegerArguments.test.ts (56 passed), npm run
typecheck, npm run lint, npm run lint:typed, npm run format:check, npm
run metadata:check, npm run verify:architecture-guards, and npm run
knip:production. The new cases fail against the base source and pass with
the change.
Fixes#7430.
Co-authored-by: S1Mon <[email protected]>
The phone header gave each link an equal minmax(0, 1fr) column and
allowed overflow-wrap: anywhere, so single-word labels such as
"Showcases", "Anleitungen", "Руководства" and "Посібники" broke between
letters at 320-430px. Links now share the row as flex items that never
shrink below their longest word. Labels wrap only at spaces, and a word
wider than the whole row can still break, preserving the enlarged-text
fit from #3598.
Refs #3597
Cache direct return/yield source expressions by immutable AST identity for all existing static and semantic consumers. Weak keys bound retention to AST lifetime; mutable analysis facts and Evidence remain unchanged. Refs #6311; full CastleIO analysis still times out.
Validation: 225 focused tests; baseline cache-regression controls; build, serial check:fast and test discovery passed. CastleIO direct-syntax facts equal with 78,272 warm lookups reduced from 2.72-2.81s to 13.0-13.4ms. Complete sourcemap-codec CLI analyses retained byte-identical Evidence; no end-to-end speedup or token reduction claimed.
CI: source 20,888 passed / 15 skipped; seven built-dependent shards, static, docs, package builds, Linux native baseline and real JS recovery CLI/MCP passed. Shard 6 Hermes path-preview failures both reproduce on exact main b9d1a61dd; Portable tests fails downstream. Greptile 5/5, no findings; Codex quota exhausted. Authorized admin bypass for unrelated baseline CI and unchecked human readiness; no human review attestation made.
Clarify the Objective-C analyzer message, coverage limitation, structured failure diagnostics, and existing opt-out/startup resource controls.
Verification: exact documentation change applied on main 79d2fc054; docs:check passed. Installed Ghidra 12.2 analyzer/scheduler and REA error/launch owners inspected. No real macOS target reproduction; original failure remains unknown. PR CI and Greptile passed; Codex review unavailable.
rea doctor reported a skill copy with an unreadable or unresolved path as identity state unknown but classified its skill:identity check as config_drift, which claims the copy has drifted. docs/installation.md keeps an unreadable skill identity unknown, and the sibling registration checks already classify unknown state as unknown. The skill check and the DoctorScope fallback now do the same.
* fix(hermes): preserve consumers of anchored registrations
Keep original alias values and comments before setup or uninstall replaces an anchored REA node. Preserve original nested bindings and retained sharing through the common YAML writer.
Fixes#3325
* test(hermes): assert retained alias object identity
* fix(hermes): preserve removed keys and surviving alias owners
* test(cli): restore retained-view cancellation phase coverage
* fix(setup): retain copied Hermes anchor comments
* chore(hermes): reverse incidental merge hook changes
Reverse the merge relative to its included-main parent to restore exact upstream bytes. Reapply only the independently reviewed shared-owner and regression patch in a normal follow-up commit. Keep all ordinary history and normal hooks; no remote push of the rejected hook drift.
Refs #3325
* fix(hermes): retain reviewed shared-owner integration
* chore(hermes): reverse incidental merge hook changes
Reverse the merge relative to its included-main parent to restore exact upstream bytes. Reapply only the independently reviewed shared-owner and regression patch in a normal follow-up commit. Keep all ordinary history and normal hooks; no remote push of the rejected hook drift.
Refs #3325
* fix(hermes): retain exact reviewed shared-owner regression
* test(native): align plist command fixtures with main
Adopt the two maintained-main fixture files as of
dd198d037f. Distinguish exact XML lint
validation from conversion commands and retain truthful provenance,
source-value, format, limitations and unknown-scalar assertions.
The original seven provenance failures reproduce before this repair.
The affected three-file group passes all 86 checks afterward. Product
code and installed-package bytes remain unchanged.
Co-authored-by: morluto <[email protected]>
* fix(native): retain bound-target dispatch failure contracts
Adopt morluto's canonical Apple dispatch metadata owner and bounded MCP
and cleanup controls from 41e10b240c.
Keep fresh admission separate from already-bound target changes and
retain access-denied classification, exact physical causes and cleanup
ownership. Update the two stale unit expectations exposed by Source CI.
The original unit pair reproduces before the correction. Current focused
regressions, package verification and installed feature controls pass.
Only the canonical Apple owner and related controls are ported; this does
not integrate unrelated later-main changes or create another contribution.
Co-authored-by: morluto <[email protected]>
* test: align plist provenance and package listing fixtures
Keep the exact XML lint invocation in CLI provenance and include the
required CLI environment owner in the synthetic package member list.
Preserve binary provenance and all LF, CRLF, literal-CR and bytecode checks.
* revert(hermes): restore canonical main before reviewed replay
This reverts commit 575a10d194, reversing
changes made to bb17e90f27.
* test(cli): retain full cancellation oracle with prepared projection
---------
Co-authored-by: hhang <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(native): classify confirmed dSYM dyld refusals
* style(native): format target refusal handling
* fix(native): confirm precise Mach-O layout refusals before export fallback
* test(cli): observe cancellation at stable source resolution boundary
* fix(native): reject contradictory dyld layout refusals
* fix(native): preserve mixed dyld diagnostic failures
* fix(native): retain the evidence behind contradictory layout refusals
When the imports and exports refusals disagree, or a refusal disagrees with
the parsed Mach-O header, inspect_macho returned a generic output error that
kept only one dyld_info capture. It dropped the header file type and the other
refusal, so the failure could not show why the admitted diagnostic was
rejected.
The contradiction now has its own error. Its diagnostics keep the parsed file
type and every admitted refusal (mode, admitted layout, exit code and output),
and the captured output is still the conflicting command. The test checks the
reason and the retained diagnostics for each contradiction case.
* test(native): allow time for real Mach-O CLI and MCP controls
Link original DEX native declarations to per-ABI function exports and relocated static JNI table candidates through shared CLI/MCP workflows. Preserve global attribution ambiguity, exact provenance, partial coverage and retained Evidence; default MCP delivery to bounded summaries with filtered/batch pages.
Use one immutable ELF relocation decoder with streaming IL2CPP materialization, cooperative bounded export/version parsing and bounded JNI state. Reuse validated session inventories without loading target libraries or executing JNI code.
Validation: relevant type/lint/build and parser/domain/public CLI/MCP regressions; genuine four-ABI NDK fixtures and independent library/slot oracles; actual JDK/JADX Conscrypt workflows with retained-source removal. Full measurements and limitations are recorded in docs/verification/android-jni-bindings-2026-10-27.md.
Closes#6403.
Restore independent PE fixtures and dependency-complete standalone artifact verifier tests. Execute existing real Windows native behavior and inspection recovery proofs before unrelated curated tests.
Validation: 101 focused tests; full typecheck; scoped lint, module/discovery/format/docs checks; valid/corrupt real-image verifier controls; real Windows artifact, behavior, and inspection recovery proof steps passed. Greptile found no actionable issues. Codex review quota exhausted.
CI remains red from exact-base failures: Source 9, Windows 7 (base 15; loader 8 repaired), and built shard 4's 7 matching failures; 7 other shards cancelled by fail-fast. Later Windows package/Node 22/24 checks skipped. User explicitly authorized bypass of established main failures. No claim of complete CI or unavailable coverage.
Fixes#7474.
Declare copied complete browser observations as abbreviated object operands, keeping canonical validation and normalization controls unchanged. Preserve all other tool declarations and Evidence identities.
Refs #4973 and #4081.
Apply caller-selected R8/ProGuard mappings to shared Android inspections, preserving raw identities, exact verification, ambiguity and provenance. Add retained summaries and bounded/cached parsing, with measured real R8/Conscrypt workflows and review fixes.
Fixes#6329.
Co-authored-by: 555aaditya <[email protected]>
Move the retained allocation report and unchanged receipt JSON into verification documentation. Keep one report producer, repair current and archived links, document test-support ownership, and remove retired lint inputs.
Fixes#7458.
Validated focused/Python checks, report consistency, docs rendering, and real Bash CLI/MCP facts against an independent oracle. Greptile reviewed all seven files without comments. CI source/Windows/shard-8 failures match clean-main or existing-main controls; the requester authorized merging with those established failures. Codex review quota was unavailable.
768b58c6d (fix(setup): honor Droid home override) made factoryDirectory
resolve from FACTORY_HOME_OVERRIDE, so setup now writes the registration
and the personal skill copy under that home instead of ~/.factory. The
Factory Droid section still described only ~/.factory, and the page names
every other client's home override (KIMI_CODE_HOME, QODER_CONFIG_DIR,
QWEN_HOME, HERMES_HOME, CODEX_HOME, GEMINI_CLI_HOME, PI_CONFIG_DIR, DSH_HOME),
so Droid was the only one missing.
Match v1 APK certificate and signer serials by signed integer value. Normalize each comparison identity once and preserve raw certificate bytes and Evidence.
37 source/integration tests, 23 real Android SDK verifier cases, SDK-valid padded APK CLI/MCP oracle and serial static checks passed. Hosted real-apk-signature passed. CI27pass7fail2skip7cancel; exact-main controls reproduce17source and14targeted existing-main assertions. NativeWindows/Mac/Node22 baseline gaps and canceled shards are documented. Greptile sole finding is unchanged upstream browser privacy code, scoped and resolved without a fix claim. Requester authorized readiness and unrelated-main failure bypass; no human review is attested.
Co-authored-by: gensokyo-dev <[email protected]>
Extend static manifest declarations and retain precise source locations and historical unknowns. Share bounded selected component views across CLI and MCP with complete parent Evidence.
Verified real ApiDemos, an AAPT2-built fixture, legacy Evidence, default-package metadata and relevant CLI/MCP contracts. Recorded whole-task token estimates, timing/resource limits and independent main-failure controls in the PR.
Co-authored-by: Harsh <[email protected]>
Normalize transport-specific discovery facts at their shared owner; preserve upstream duplicate-ID rejection and correct discovery-time attachment uncertainty in all consumers.
53 focused regressions, serial static checks and real Chrome CLI/MCP fault injection/inspection pass. Greptile final head 5/5. Exact-main controls reproduce all 17 source and 21 shard-8 failures; seven shards cancelled and remain unverified. Requester authorized readiness and unrelated-main CI bypass. Human review is not attested.
Co-authored-by: gensokyo-dev <[email protected]>
Keep cookies with opaque partitions distinct from unpartitioned identities while retaining value hashes and incomplete comparison coverage. Preserve known partition hashes and use the existing shared CLI/MCP fingerprint owner.
Validated with 63 source and 57 storage boundary tests, builds/static checks, and real Chrome156 CLI/MCP controls. Live opaque-cookie emission was not reproduced; its protocol regression is explicit. Hosted CI's unrelated failures were reproduced against exact main a905a71e3; seven fail-fast-cancelled shards remain unverified; Greptile's sole existing-main finding was scoped and documented. Human readiness remains unasserted; bypass authorized by the requester.
Co-authored-by: gensokyo-dev <[email protected]>
Remove the derived package renderer property from new application graphs. Use renderer_declarations for source fields and package loads edges for the selected candidate; previously retained Evidence remains readable.
Add validate-browser-scenario for offline schema and semantic checks of inline JSON, local files and stdin. Return compact validity/action/secret counts without reading secret values or discovering, launching, attaching to or navigating a browser.
Execution and validation share CLI input admission and the canonical domain schema. Preserve existing capture lifecycle, diagnostics, cancellation and caller-relative executable paths.
Integrates #7149 with attribution. Fixes#5488. Validation includes 24 focused tests, static/build/docs checks, and the preserved real PocketBase scenario with its backend stopped and secret variables unset. Verification and resource/token measurements are recorded in docs/verification/browser-scenario-validation-2026-10-27.md.
Co-authored-by: Ace <[email protected]>
Save typed response fields and reuse generated IDs or authentication values across browser scenario requests, UI observations and navigation through the shared CLI/MCP workflow.
Preserve exact numbers, retained Evidence, explicit source/coverage limits and prospective page-value semantics. Bound response decoding, retained spellings and request serialization before allocation, and protect marked response values across source records, follow-up text and planned screenshots.
Integrates Edgar Arakelyan's #7162, including follow-ups through 9f5191ab0. Final local validation: 560 feature-branch tests and 561 current-main integration tests, static/build/docs checks and real PocketBase CLI/MCP privacy workflows. Whole-task comparison and verification limits are recorded in docs/verification/browser-saved-values-2026-10-26.md.
Co-authored-by: Edgar Arakelyan <[email protected]>
Replace overlapping traversals with one bounded relationship owner. Preserve superview direction, treat conflicting or unsupported registry placement as unknown, and retain source facts and coverage.
Keep historical inventory summary variants with their projection owner and share embedded PCK trailer observations while preserving each reader’s admission policy.
Share exact live target variants across open and session responses, preserving historical Evidence and snapshot admission. Consolidate equivalent ELF metadata aliases at their owner.
Pass parsed MIPS program declarations to shared-load analysis and validate IDA string address selectors before enumeration, including empty inventories.
Move shared function, dossier, inventory, reference and output semantics from the legacy Hopper module to native domain owners. Migrate provider, adapter, test and verifier consumers directly.
Reuse the read-side capture validation before publication so exported bundles can be imported. Preserve historical and foreign record identity and existing destination files on failure.
Resolve IDA compatibility facts in one place and migrate capability, session, and verifier consumers. Move native search fields to the domain and reuse residual unknown fields for filters.
Migrate schema, installer, ASN.1, and tooling consumers to their owning definitions. Remove the redundant CI task entry and the deprecated Cutter command-local JSON flag.
BREAKING CHANGE: Replace Cutter --json=true or --json=false with --json-output=true or --json-output=false. Bare --json still controls CLI result formatting.
Use canonical owners for literal matches, tool families, MCP schema presentation and IDA protocol selection; remove unused web-bundle reexports. Remove the duplicate CLI command name and keep the canonical compare workflow.
BREAKING CHANGE: rea compare-bundles has been removed. Use rea compare instead. The MCP compare_bundles tool is unchanged.
Parse JEB failure envelopes at the provider boundary and retain the original response, recovery facts, and selected inputs. Do not infer an unsupported target from undocumented error codes; preserve completed primary observations when metadata requests fail.
BREAKING CHANGE: JEB engine refusals now report execution_failure rather than unsupported_target. Consumers should inspect the retained upstream diagnostics for the engine's reason and recovery facts.
Authenticate exact retained parents against current and documented historical summary projections, including caller-selected display paths. Keep source facts, Evidence commitments and unsupported-projection rejection intact.
Attach to external browsers without implicit default-context overrides, and own temporary focus only on the selected page. Preserve unrelated page media and exact page inventory through CLI/MCP. Remove bootstrap-page retries; retain late/failed cleanup ownership and verified SDK notices.
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Co-authored-by: gensokyo-dev <[email protected]>
Compare each retained member's exact runtime key and role without treating archive order as semantic identity. Keep one-sided legacy declaration gaps unknown, preserve proven changes with unavailable content, and retain original Evidence.
Record packed and unpacked ASAR file declarations at the reader boundary and compare known storage changes without treating historical omissions as packed. Preserve known differences when bytes are unavailable and retain existing integrity precedence.
BREAKING CHANGE: ASAR file occurrences may now contain unpacked: false. Consumers that validate only true must accept an optional boolean; absent historical fields remain unknown.
Normalize function address and name without requiring an unused upstream size. Preserve complete upstream observations and the explicit unknown body coverage across IDA protocol profiles.
Reuse completed embedded-format observations only after validating the admitted file identity and state, and centralize reader construction across inventory and extraction. Preserve early refusal, selected-path checks, ZIP ownership, and each operation's traversal policy. A source changed between preflight and inventory now fails integrity validation.
Preserve status independently of malformed content and use one declaration reader for route grouping, state extraction and manifest eligibility. Verify the partial facts through CLI and MCP views.
Derive each facet's row schema from the Mach-O producer and validate it against the requested page. Preserve the existing wire shape and historical defaults.
Distinguish omitted slice fields from declarations that cannot be interpreted, and preserve their source in native results and artifact limitations. Keep structural fallback and capacity failures explicit.
Use one owner for exact recorded timestamps and documented native-Date fallback in state and probe replay. Retain the source and disclose native precision and host-timezone dependence in state limitations.
Validated historical chronology, timezone and unknown-duration regressions against prior behavior.
Keep missing or ambiguous scenario request durations unknown, retain learned clock rules, and withhold plans that cannot generate an observed clock. Refuse such writes in older imported states before changing state or generators.
BREAKING CHANGE: The write-plan reason can now be unknown-clock; strict clients must accept the new output value.
Validated state, CLI and advertised MCP workflows, documentation, and focused clock and cleanup regressions.
Retain valid request starts independently of duration and require observed completion for interval-based state and write inferences. Bound indexed interference checks across captured writes while preserving ordinal fallback and unmodified Evidence.
Validated state, comparison, CLI and MCP workflows with known, unknown, overlapping and redacted timing controls; check:fast passed.
Preserve producer-supported recovery guidance after an existing-image DMG attachment refusal, without borrowing or detaching existing mounts. Keep diagnostics, root provenance and unknown coverage; centralize guidance ownership and run the real mounted-image regression in macOS CI.
Co-authored-by: gensokyo-dev <[email protected]>
The collection-paging paragraph says complete Evidence stays accessible through
`get_evidence`, but no tool by that name exists. The session family registers
`get_evidence_bundle`, which is the only name the catalog, the contracts and the
CLI equivalents use.
Every other page already uses the full name, so this was the only place a reader
or agent would look for a tool that cannot be called.
Admit same-major Ghidra releases at or above the verified minor through the existing compatible/unverified policy. Preserve the observed version, verified12.1.4 pin, installation-declared Java floor and separate NativeAOT verification gate.
367 targeted tests/static/docs, all10 real Java bridge-source compilation, and actual Linux12.2/JDK25 CLI/MCP queries and cleanup pass. JDK21 remains refused for12.2. Exact-main controls reproduce all3 unrelated CI assertions; canceled shards and an apt-blocked binary-layout lane remain unverified.
Co-authored-by: gensokyo-dev <[email protected]>
Distinguish invalid supported timestamp spellings from unrecognized legacy formats at the recorded-date owner. Probe replay retains ordinal order for invalid dates without changing the existing legacy fallback.
Validated date parsing, state ordering and comparison workflows; the prior implementation reorders the invalid-date regression.
Retain a limitation when ASAR block integrity fields are present. Whole-file hash verification does not establish block verification or Electron runtime acceptance.
Validated inventory and extraction workflows, including partial and malformed block declarations.
Use the shared recorded timestamp interpretation for replay ordering, including exact fractions and UTC interpretation of supported zone-less forms. Preserve native-Date fallback for unsupported historical spellings.
Validated environment comparison, state ordering and CLI tests; five new cases reproduce prior drift.
Carry reported algorithms and hashes into listing limitations when the declaration cannot be compared, retaining the existing verification and extraction policies. Bound escaped text before allocation.
Validated ASAR inventory, extraction, cleanup and MCP workflows; prior code omits the declaration details.
Read bounded plist format probes and Linux resource-limit reports through positive short reads before classifying their contents. Preserve explicit EOF, report capacity, physical failures and descriptor cleanup ownership.
Validated 66 focused tests and check:fast; nine new fault-injected cases fail against the previous implementation. Linux procfs was not verified on a real Linux host.
Share provider availability, admission, logging and Evidence delivery across browser, Electron and runtime tools. Reuse retained summary delivery for Python and BEAM selected views while preserving preflight and complete parent Evidence.
Validated 47 focused MCP and retained-view acceptance tests and check:fast.
Reuse the positional range owner for target metadata, Hopper Mach-O selection and Kuna spans. Preserve physical failures, completed span evidence and cleanup ownership across cancellation and short reads.
Validated 147 focused file and lifecycle tests; ten new cases reproduced failures against the prior implementation.
Resolve ordered descriptor values at the shared JavaScript fact owner and preserve uncertain intrinsic writes instead of stale exports. Normalize ordinary method values and reuse canonical primitive, prototype, ambient identity and ordering facts across consumers.
384 selected tests, format/static/docs checks, all final CI jobs and reviews pass. Actual Node controls cover 35 bundles; one possibly repeated factory explicitly stays unknown. Shipped Obsidian selected-source complete Evidence matches base/fixed CLI and MCP, with coverage and resource measurements in the PR.
Co-authored-by: gensokyo-dev <[email protected]>
Continue reading Mach-O and Electron ranges after positive short descriptor reads, while preserving explicit EOF, cancellation, file identity and cleanup semantics. Advance fuse scanning and hashing by the bytes actually read.
Validated focused filesystem, Mach-O, dyld, Electron and MCP workflows: 117 tests passed, two Windows-only tests skipped; check:fast passed.
Use the common analysis-view reader for Rust and native inventory validation, preserving historical defaults and original Evidence while retaining schema-specific validation.
Validated 123 focused view and service tests and check:fast.
Centralize completed file observations and cleanup error projection across stable-file providers while preserving each provider’s failure classification and exact cleanup leases. Cover BEAM finalization cancellation and devicetree cleanup retries.
Validated 223 focused lifecycle and MCP tests and check:fast.
Parse shared request and response fields at the CDP boundary while preserving capture, session and runtime admission policies. Treat empty request IDs as unknown so unrelated events cannot be joined, and retain redirect occurrences and partial response evidence.
Validate 221 focused tests, fast checks, and real Chrome network and runtime verifiers.
Derive member roles at the table-of-contents boundary and share declaration and Python-module interpretations across validation, projection, metadata, header reconstruction and nested PYZ traversal. Retain source type codes and generic-file behavior for other printable types.
Validation: 217 focused archive, CLI and MCP tests passed, including mixed-role byte and metadata checks. Typecheck and lint passed.
Share Field scalar facts and use one coded-index tag registry for widths and decoding. Preserve reserved constructor tags, generic-instantiation restrictions, heap failure order and selected-field signature admission.
Derive native import member kind from the validated MemberForwarded coordinate independently of name lookup, retaining known field or method identity when its display name is unreadable.
Validation: 368 focused source, CLI and MCP tests passed; five lost-kind regressions reproduced on the previous commit. Heap-width, coded-width, reserved-tag and selection boundaries are covered. Typecheck and lint passed.
Have the MethodDef row owner derive code type, managed kind and the P/Invoke flag for both body decoding and native-boundary inventory. Remove duplicate masks and unreachable fallbacks while preserving raw flags, zero-RVA precedence and ImplMap-specific declaration policy.
Validation: 280 focused source, CLI and MCP tests passed, including cross-workflow flag combinations and declaration/body policy boundaries. Typecheck and lint passed.
Centralize CDP script/context declarations and frame-claim interpretation across web capture, Electron, V8, active runtime and storage seeding. Preserve each workflow's admission, provenance and retention policies while removing superseded helpers and schemas.
Malformed or conflicting frame claims now leave passive scripts unattributed. Reused context IDs replace future frame authority without rebinding earlier scripts, and source readers reject empty script IDs.
Validation: 153 focused tests and check:fast passed; 13 regression failures reproduced on the prior commit. Real Chrome script export and runtime CLI/MCP verifiers passed, including correctly reported uncertain cleanup after intentional target closure. Electron regression coverage used simulated CDP.
Pass validated blob location and decoded metadata with their admitted descriptor from classification to the reader. Keep root cleanup ownership, freshness checks, format validation order and direct-reader decoding intact, then release borrowed facts after preparation.
Validation: 147 focused source, CLI and MCP tests passed, including real Node blob production; check:fast passed. Optional injected-ELF Docker verification was unavailable because the Docker daemon is not running.
Derive dependency and generic-error facts once per owning task, chunk or multifile stage. Reuse those facts for failure classification and dependency aggregation, preserving unnamed identities, severity precedence and raw diagnostics.
Validation: 70 focused tests passed; 41 platform-guarded tests skipped on macOS. Typecheck and lint passed. Real Unblob/Linux provider behavior was not run.
Share CPython magic framing, version and header facts while keeping standalone marshal coverage and PyInstaller reconstruction eligibility distinct. Preserve legacy headers, opaque unknowns and source bytes.
Validated 272 focused tests across source, CLI and MCP workflows, a 38-test reader recheck, and check:fast.
Parse each frame event using its declared protocol fields and retain loader and ancestry only across same-document updates. This removes alias-based attribution, invalidates malformed committed frame facts, and prevents stale WebMCP tools surviving a same-URL document replacement.
Migrate Electron, screenshot, capture, timeline, and execution consumers to the shared facts owner and retire the old helper exports. Validated 428 focused tests, test discovery, typecheck/lint, and real Chrome CLI/MCP network capture; Electron and WebMCP regressions use simulated CDP.
Page retained web-bundle endpoint observations and inferences through shared CLI/MCP projections, preserving source identities, scoped unknowns, coverage and complete Evidence access.
Based on HarsH’s original implementation in #7213 (6b1d5d42). Real Chromium/CLI/SDK verification and whole-task cost limits are recorded in docs/verification/web-bundle-paging-2026-10-26.md.
Explicit user authorization permits leaving human-readiness declarations unchecked and bypassing reproduced main failures (ADB listing fixture, package catalog expectation, untouched APK verifier formatting). No human-review attestation is made.
Co-authored-by: HarsH <[email protected]>
Retain file identity after Rust, web artifact, shared selected-input and historical capture reads complete before close or cancellation fails. Preserve descriptor-set snapshots in diagnostics alongside sanitized capture observations.
Classify canonical physical capture failures before cleanup composition; source permission errors and bare syscall failures keep their meaning through late cancellation and retry. Keep explicit sensitive-value redaction on retained facts.
Validation: 247 focused cases passed, two Linux-only cases skipped; build, typecheck, lint and formatting passed. Real descriptors with controlled finalization faults establish reader ownership and facts. Worker reply and multi-file database completion semantics remain separate audit work.
Normalize completed stable-file path, digest and length in one shared observation owner. Migrate thirteen identical provider projections while preserving their selected paths, schema checks and cleanup composition.
Keep specialized metadata and worker reply observations at their existing owners. Validation: shared real-descriptor coverage and provider lifecycle tests passed; build, typecheck, lint and formatting passed.
Retain exact completed file path, digest and length in Python pyc and Erlang BEAM failure observations. Translate Python close failures at the provider boundary and bind them to the retained descriptor cleanup leases so later successful retries resolve prior errors.
Exercise actual services and file handles with completed reads, cancellation during finalization and physical read failure followed by cancellation. Assert no fabricated snapshot on early failure and resolution of every prior cleanup report.
Validation: six focused descriptor regressions plus existing filesystem, parser and MCP coverage passed; build and static checks passed. Authored format fixtures establish portable reader behavior, not CPython/Erlang runtime execution.
Count retained UTF-8 text rather than UTF-16 code units, including exact-byte base64 spellings. Bound the decoded version-name map independently from output expansion, releasing replaced entries from its live storage charge.
Add real service/provider boundary controls for accepted and over-budget symbol names, version names and unused version metadata. Existing per-string bounds remain the allocation owner.
Validation: native parser, filesystem and CLI/MCP views passed; build, typecheck, lint and formatting passed. No serialized-report size cap, OOM reproduction or performance improvement is claimed.
Charge PE alias names when storing them, before target interpretation can suppress output rows. Validate unused-slot names as bytes without retaining or decoding strings. Keep row and forwarder accounting separate so names are charged once.
Preserve alias ordering, malformed unused-name issues and ordinal-only suppression. Add production service/provider retention regressions using compact shared-pointer fixtures.
Validation: 43 focused parser, filesystem, CLI and stdio MCP tests; fresh build, typecheck, lint and formatting passed. Three bounded Node 24.22.1 measurement pairs showed median process RSS high-water 154000 to 93168 KiB; timing and sampled family memory have cache/startup/sampling limits. No Windows loader acceptance or OOM reproduction claim.
Capture one environment snapshot per admitted WABT, JADX, firmware and Wakaru operation. Use it for tool selection, session identity and child launches while preserving configuration updates between operations.
Regressions reproduce changed JVM options under an old session key, mixed engine/limiter selection and environment drift between subprocesses. Keep selected-wrapper and cleanup coverage in a shared fixture with separate scenarios.
Validation: 115 distinct focused tests passed across the final five-file selection, including seven selected-command cases after refactoring. Fresh build, typecheck, lint and formatting passed. Tests use real subprocess fixtures; no real JADX/WABT/Binwalk/Wakaru engine or native Windows verification is claimed.
Retain authenticated provider source records independently of snapshot replay eligibility and return them explicitly to plain application callers, including late cancellation. CLI/MCP commit source records before delivering links; function dossiers share the canonical Evidence constructor. Keep directory replies compact and preserve successful retry verification.
Real Ghidra CLI/MCP investigations verify 652-function ls, historical report provenance, cross-session resume, unchanged source/project content and complete offline Evidence. Final full 14,338-function libcrypto comparison matches all metadata/pseudocode facts and independent fresh-import observations: 14,194 written, 143 external, one recorded deadline; 494.03 s, 445.63 CPU s, 1.584 GiB sampled family peak under 2-CPU/8-GiB guards, with no OOM or cleanup failure.
144 focused tests and static/docs/site checks pass; latest-main integration passes 289 cases plus typecheck/lint and actual CLI snapshot source retrieval. Final CI source/static/build/docs/quality/package checks pass. Four ADB shard failures also occur in main CI 40349559391; cancelled coverage remains unverified and author declarations stay unchecked under the user's explicit bypass authorization. No human-review attestation is made.
Parse literal import framing and retain ambiguous boundaries. Consult undefined symbol-table names only when needed to distinguish producer spacing, with import and export captures owned separately.
Validation: 59 native tests passed and real Apple tools on inert arm64 fixtures reproduced the old loss and verified the fix. Fresh build, typecheck and lint passed. Small warm-task median elapsed was unchanged; cold costs and agent tokens were not measured.
Consume Toybox’s single NAME separator without stripping source whitespace. Keep MCP subprocess fixtures independent of ambient PATH and validate calls through the existing named-schema helper.
Validation: focused ADB provider/parser and MCP tests passed; no real Android device was used.
Distinguish local provider readiness failures from target-support refusals in shared CLI/MCP diagnostics, preserving original codes and evidence. Avoid claiming configured providers are unconfigured.
120 focused tests, static/docs checks and real ELF CLI/MCP verification pass. Hosted CI has the same four ADB failures as exact main d9908fd7a; three canceled shards remain unverified. Merge uses the user's explicit authorization to bypass proven main failures. Fresh Codex and Greptile reviews are clear.
Fixes#7214
Co-authored-by: gensokyo-dev <[email protected]>
Respect Node asynchronous exec/execFile method-default stdio instead of interpreting an ignored option. Remove the superseded callback-shape query while preserving caller input, Evidence, uncertainty, argv and fork controls. Independently validated on current main through actual Node pipe capture, matching real-app CLI/MCP facts, 81 targeted tests and static checking. Closes#7217.
Require operator-declared hostnames before serving MCP HTTP outside loopback, preserving Host/Origin gates. Parse hostname identities once, share typed startup recovery across CLI/MCP, and announce an admitted client URL. Generated AgentEnv bundles declare their client/service names and validate DNS/HTTP name identity before export.
Validated through real packaged HTTP requests, direct SDK connection, compiled export refusals, 184 focused tests, static and documentation checks on the refreshed main integration. Final CI and review evidence is recorded in the PR body.
BREAKING CHANGE: non-loopback deployments require MCP_ALLOWED_HOSTS or rea mcp --http --allowed-hosts; bundle names must be unchanged HTTP hostnames within the 63-octet DNS label bound.
Co-authored-by: gensokyo-dev <[email protected]>
Route six artifact verifiers through the canonical complete MCP output and error parsers. Remove duplicated JSON/schema interpretation while retaining Evidence authentication, provider provenance, independent CLI parsing and CLI/MCP parity checks. Validate malformed success/error envelopes at the shared boundary.
Validation: 11 boundary tests, Go compiler verifier (30 cases), and synthetic IL2CPP/Flutter verifier workflows passed; typecheck/lint passed. PCAP readiness passed with stub tools, but capture inspection is unsupported on macOS. Linux-only binary-layout and EVM verifier toolchains were unavailable.
Share release JSON decoding, vendor fields and package integrity checks across macOS setup and Linux publishing/cache reads. Reject malformed UTF-8 instead of repairing macOS metadata, retain established initial BOM tolerance, and preserve complete vendor rows through admission and cache retention. Keep platform channel, signature, installation and cleanup policies unchanged.
Validation: 134 focused tests passed with 21 platform skips; typecheck and lint passed. Linux-only signed-cache cases were updated but skipped on the macOS host. No live Hopper installation performed.
Reuse selected Ghidra projects through verified private read-only copies, with shared CLI/MCP configuration, committed project identity, import-SHA verification and explicit historical/unknown analysis semantics. Preserve exact cancellation and platform capability boundaries.
Actual Ghidra 12.1.4/JDK21 Linux CLI/MCP investigations matched fresh import on 652-function ls and 14,338-function libcrypto inventories and pseudocode; authenticated complete Evidence, mismatch/staleness refusal, saved historical reports and shared /tmp projects verified. 958 focused tests passed with four platform skips; build/static/docs/format passed. Real macOS/Windows reuse remains unverified; Windows reuse is unavailable. See PR verification for measured whole-task costs and final CI attribution.
Co-authored-by: akushonkamen <[email protected]>
Co-authored-by: morluto <[email protected]>
Give the object-structured provider envelope one owner that retains original dictionary members. Remove duplicate schemas, text extraction and JSON-or-text decoding plus the downstream casts that repaired lossy record clones. Keep provider-specific error, truncation, wrapper and payload-selection policies in each adapter.
Validation: IDA/JEB compatibility tests, pinned SDK transport tests and provider regressions passed; check:fast passed. Real IDA/JEB applications were not exercised.
Preserve XML, OpenStep and binary dictionary keys at their format owners. Share validated binary reference facts with materialization while retaining scalar precision, UID identity and bounded expansion. Migrate archive, Interface Builder, native and app-bundle consumers, then remove obsolete key-omission accounting and schema copies.
Validated source regressions and compiled CLI/MCP workflows, including native plist fidelity and archive identity. Typecheck, lint, docs and test discovery pass. No performance benchmarks were required.
Use portable hex escapes for Windows absolute-path separators in the shared local path schema. Preserve host path semantics and caller spelling; validate Annex B/u/v compilation and Node win32 agreement. Closes#7151.
Publish portable browser request-path and HTTP-token patterns at their canonical owner, preserving input spelling and existing policy. Independently reproduced and verified through actual full-profile stdio MCP catalog and domain/MCP regressions on latest main. Closes#7168.
Preserve observed failed-response HTTP status through Linux Hopper setup diagnostics without changing download rejection, retry, or installation policy. Independently validated on latest main through red/green regressions, actual TLS sockets, focused checks and CI. Closes#7182.
Keep binary_session available when Linux owned-child probes lack procps ps. Preserve typed ownership-prerequisite failures in direct execution and report per-provider readiness in status. Independently validated on latest main through real stdio MCP plus focused regression, static and documentation checks. Closes#7164.
Upgrade Zod to 4.6.6 and refresh locked license inventories. Preserve REA's established timestamp spellings through one shared UTC/offset grammar, including completion-verifier consumers. Size the stdin resource regression from the child's actual heap capacity.
Fixes#7173
Includes the dependency and inventory changes proposed in #7184.
Co-authored-by: tijo95 <[email protected]>
Scope MCP discovery, setup, doctor, status and recovery guidance by selected tool families using one contract inventory and shared catalog identity. Preserve default discovery, canonical validation and complete Evidence.
Fixes#3246. Builds on #3026. Real libcrypto workflow verifies all 5,367 exports and identical complete Evidence. Static/source/build/docs/Windows/package/Greptile checks pass; ten assertions reproduced identically on clean main and feature across Node 24/22, with cancelled coverage explicitly unverified. Merge uses the user-authorized bypass for baseline failures and unchecked human-readiness declarations, without attesting human review.
Co-authored-by: 0xLLVEL <[email protected]>
Co-authored-by: Codebuff <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
Add opt-in bounded output-schema advertisements while preserving the full default, canonical server validation and complete structured content. Verify real stdio discovery under a 1 MiB receive cap, all 5,367 libcrypto export coordinates, complete Evidence parity, whole-task serialization estimates and resource bounds.
Fixes#2512. Integrates #7054 by Akshad Jogi with original authorship retained.
Co-authored-by: Akshad Jogi <[email protected]>
Move shared request parsing into its application owner, migrate all consumers, and remove six duplicated admission blocks while preserving cancellation and validation policy.
Keep operator-selected JavaScript paths in verifier parent Evidence while retaining canonical comparison guards. Validated with 204 passing tests, 3 Linux-x64-only skips, typecheck, lint, and test discovery.
Share ordered schema positions between Gemini literal checks, DeepSeek regex
checks, and catalog array-shape checks. Reuse the canonical keyword and local
reference owners; keep unreferenced instance literals opaque and unresolved
external or dynamic targets unknown.
Gemini and catalog checks now catch constraints in schemas reached through
references into examples. Preserve source paths, resource scopes, cycles and
regex occurrence counts. Align fixture first-error selection with the native
Gemini check and use consistent bracketed array locations in diagnostics.
Validation: 55 focused tests passed, including full/compact MCP catalogs;
typecheck, lint, discovery, and Node source-loader checks passed. The old
Gemini scanner reproduced the local-reference false negative. Installed
Gemini and DeepSeek clients were not run.
This reverts commit 7336a566ac (#7119).
After #7120 and the other merges of 2026-10-25 grew inspect_analysis_view's
complete input schema (71,762 bytes plus 3,003 bytes of examples), the 64 KiB
fitting from #7119 could no longer keep instance level 3 and fell back to
level 2. That summarized the inline Evidence source and the view selector
collections, so main CI failed in toolInputReferences, toolSchemaValidity,
analysisViewMcp, absoluteLocalPathSchema, javascriptFactsViewMcp and
godotPckMcp. #7119's own CI ran against bc143125e, before that growth.
Reverting restores the complete full-profile advertisement while the size
limit is redesigned against these contracts (#4081 stays open).
Co-authored-by: Claude Opus 5.5 <[email protected]>
Migrate twelve application service files to the existing provider input owner,
preserving schema causes, cancellation precedence, caller selection, and
retained Evidence. Keep flows with distinct error policies separate.
Replace the DeepSeek verifier's recursive object scan with schema-position
traversal using the canonical keyword and local-reference owners. Literal
pattern fields no longer cause false regex failures; patternProperties keys
are checked alongside actual pattern constraints in native regex modes.
Validation: 271 focused workflow/service tests and six verifier regressions
passed; static checks, discovery, and actual Node source-loader smoke passed.
The baseline scanner reproduced the new regressions. Installed DeepSeek client
execution was not run.
Retain validated DOS relocation records in their domain owner and consume
those facts in Ghidra load-image preparation. Preserve ordered duplicate
aliases, overlapping writes, and original/final byte evidence.
Share FAT header facts across generic, native, and Hopper readers without
merging their distinct admission policies. Replace managed method bodies'
two identical mapping lookups with one owned offset-and-extent result.
Validation: 660 focused tests passed across 19 suites; typecheck, lint, and
test discovery passed. Hopper/Ghidra checks used fixtures and simulated
provider observations; no real external provider execution was performed.
Share ELF note headers across note inspection, Flutter build IDs, Node SEA,
and recorded-crash source binding. Decode REL/RELA records once for MIPS
shared-load analysis and IL2CPP linking, removing superseded scalar helpers.
Share the CLR header prefix between desktop detection and managed PE parsing
while preserving their distinct read bounds and admission policies. Cover
those differences and ignored R_NONE fields with production-boundary tests.
Validation: 493 focused tests passed, two existing skips; static checks and
test discovery passed. Public managed/desktop CLI and ELF notes CLI/MCP
workflows exercised; no real external analysis providers were run.
Reuse the existing provider input admission helper across twelve services,
preserving cancellation, schema errors, path policies, and Evidence handling.
Share ELF dynamic records across Dart, MIPS, and IL2CPP readers, and reuse
Mach-O segment and section records for Node SEA location.
Require canonical DOS ownership before GPU PE section discovery so relocation
bytes cannot masquerade as a PE pointer. Cover valid short DOS headers and
the malformed case through both inventory and the public CLI.
Centralize ELF section and Godot trailer declarations while preserving caller admission policies. Route six PE entrypoints through canonical DOS ownership so relocation data cannot establish a PE header, and update NativeAOT interpretation identity and verifier dependency coverage.
Share exact ELF program-header facts across nine consumers while preserving their admission and precision policies. Centralize conformance path and digest binding, retain verifier-specific checks, and route fixture-owner changes to CI qualification.
Allow native matching representation budgets to be selected at runtime or launch while retaining the 96 MiB default and main shared-provenance accounting. Preserve actionable configuration recovery through the setting owner and verify complete CLI/MCP matches for 20k/50k synthetic inventories and real stripped LLVM 14/15 inventories.
Fixes#6406.
Based on #7112 by Kaoru0822; original authorship and co-author credit retained.
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
This reverts commit 7336a566ac (#7119).
After #7120 and the other merges of 2026-10-25 grew inspect_analysis_view's
complete input schema (71,762 bytes plus 3,003 bytes of examples), the 64 KiB
fitting from #7119 could no longer keep instance level 3 and fell back to
level 2. That summarized the inline Evidence source and the view selector
collections, so main CI failed in toolInputReferences, toolSchemaValidity,
analysisViewMcp, absoluteLocalPathSchema, javascriptFactsViewMcp and
godotPckMcp. #7119's own CI ran against bc143125e, before that growth.
Reverting restores the complete full-profile advertisement while the size
limit is redesigned against these contracts (#4081 stays open).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Refs: #7135
Scope expected Ghidra input failures to the active request after the authenticated startup handshake. Preserve operation output, identity and log coordinates while retaining cumulative output with its session owner; lifecycle and unexpected failures keep full diagnostics.
Share remote input-code classification with domain error projection. Verify startup rejection, healthy continuation, cleanup, unknown errors, OOM diagnostics and real CLI/MCP workflows. The measured unknown-symbol reply falls from 13,170 to 2,209 bytes with unchanged successful facts and Evidence IDs.
Add optional native load-image summary delivery and retained selected-row pages through shared CLI/MCP analysis views. Preserve source verification, provenance, unavailable collections, exact row identity and opt-in byte/check payloads.
Preserve direct caller paths during shared CLI summary publication and cover raw/DOL projection contracts through the actual SDK. Real Ghidra ELF CLI/MCP captures, retained and file-backed pages, focused regressions and static checks passed; the PR records existing main schema failures and the JavaScript resource-recovery CI uncertainty.
A write below a slot that stores a value read from the same slot, such
as `s.c = s.c.p; s.c.i = 1` on a parameter, revisits the binding with
one more segment after the stored key on every turn. The cycle closure
only recognized segments added at the front of the path (`node =
node[key]`), so the work-list never terminated and exhausted the owned
worker heap (CodeMirror 5 `mode/stylus/stylus.js`).
Recognize a contiguous segment inserted after a stable base, check that
repeating it after that base can never be pruned, and escape through it
once, as the self-alias rule already does. Front insertions keep their
existing order and result.
Fixes#7130
Co-authored-by: Claude Opus 5.5 <[email protected]>
Reference-store identity queries resolved the written target as a value,
so once a step consumed the written key the walk continued into the value
read from that slot and recorded it, and everything reachable from it, as
an object the store mutates. Every value stored into one local object at
unknown keys then received an entry for every such store, and each new
store re-queued every earlier one. Ambient-write indexing grew cubically
in the number of such stores.
Receiver queries now drop items whose projection no longer ends at the
written key. Stored-value selection has one owner, which the resolver and
the store fixpoint share. The fixpoint re-queues a dependent only when a
changed entry can reach it with a selection that its receiver query keeps.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(mcp)!: return a compact binary_session status by default
binary_session {} with no target open returned about 66 KB, most of it
provider rejection diagnostics and candidate capability sets that the
skill's recovery flow re-reads on every retry. 6.8.0 grew it by 21%
because the shared regex capability limitation began repeating setting
bounds and step-by-step recovery for every search operation of every
provider (#6099, #6267, #6301, #7049) and IDA gained that limitation for
its two search operations (#4862).
The regex limitation now states the effective deadline and heap, their
sources and where to change them; the deadline and heap errors keep the
full recovery. binary_session gains detail: summary (default) or
complete. The summary keeps open state, target identity, the binding,
route capabilities, run, activity and health status, each candidate's
availability and target-support status, code and reason, and every
tool's availability, reason, remediation and modes, and lists the
fields it leaves out in omitted_fields. Complete detail returns the
previous shape. The CLI capabilities and providers commands keep
complete provider facts through the same application projection.
BREAKING CHANGE: binary_session {} no longer returns
analysis_provider_candidates[].availability.diagnostics,
target_support.diagnostics or capabilities, or tool_availability group
surface, effects and annotations. Pass {"detail": "complete"} to get
them.
Fixes#7109
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(mcp): key binary_session output fields by detail
The output schema accepted detail: "complete" with summary candidates or
tool groups, and a summary without omitted_fields, so detail did not
tell callers which fields existed. Each session kind is now crossed
with a detail-specific shape: a summary requires omitted_fields and
summary-only candidates and tool groups; complete requires diagnostics,
candidate capabilities and static tool flags and forbids omitted_fields.
Both the canonical schema and the SDK-validated advertised schema reject
mismatched shapes.
The verifier helper accepts summary candidates only for a validated MCP
summary; CLI provider output still requires candidate capabilities.
Regex limitation tests check values, sources and setting names instead
of whole sentences.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(cli): report JSON Lines selection refusals as caller errors
A --format jsonl refusal because the selected value is not a record
collection is the caller's output selection, not unreadable analysis
output. Report it as invalid_request/invalid_input that names the
selection and its observed shape, keep the --format json /
--filter-output remediation, and drop the rea doctor advice.
AnalysisInputError gains an optional producer remediation, mirroring
the other refusal classes. The JSON Lines owner examines its documented
boundaries once for both admission and refusal facts.
docs/cli.md now states that columnar results such as
inspect-sqlite-database --table rows are not record collections.
Fixes#7110
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(cli): carry JSON Lines refusal facts as structured observations
JSON Lines refusals now put the location and shape of each refused value
in details.issues[].observed, and the message is built from those facts.
Tests assert the classification, issue path, observed facts, and the
shared remediation constant instead of fixed sentences.
The filtered analysis-view export check now uses a non-empty request
and compares the emitted rows with the unfiltered export.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Logical branch selection evaluated the left operand to decide which
branches stay reachable, and value evaluation then evaluated the selected
left branch again. In a left-nested `a || b || c ...` chain every level
repeated that, so semantic analysis doubled per operand and a 24-operand
chain exhausted the per-source deadline.
The selection now carries the left operand's value with the branch, and
value evaluation reuses it. Provenance still walks the branch nodes.
Results are unchanged.
Fixes#7115
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(mcp): fit every advertised input schema within the 64 KiB client limit
Claude Desktop drops a local connector tool whose compact-JSON input
schema exceeds 64 KiB. The full input profile advertised
compare_web_captures at 171,867 bytes and inspect_analysis_view at
71,632 bytes, because the self-contained projection inlines the
complete producer capture schemas once per side.
The full advertisement now fits the limit at its producing boundary.
A property whose schema repeats an earlier sibling's points at it, and
schemas below the deepest instance level that fits are summarized as
their types, size constraints and descriptions. Every rewrite widens the
advertisement through the existing relaxation rules for oneOf, not,
if/then/else, contains and unevaluated keywords, so canonical inputs
stay accepted and the canonical contract still validates every call.
Schemas within the limit are unchanged, and the compact profile is
still presented from the complete projection.
Numeric const and enum literals are also relaxed in the advertisement,
like boolean literals, since Gemini accepts only string enum members.
Fixes#4081
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(mcp): keep fitted input summaries widening and at the deepest fitting level
Array summaries took item types only from `items`, so a tuple such as
`{prefixItems: [{type: string}], items: {type: number}}` was advertised
as a number array that rejected `["a", 1]`. Item types now include tuple
positions from `prefixItems`, a draft-07 `items` array, and
`additionalItems`, and stay unconstrained when later elements are open.
A summary can also be longer than the schema it replaces (a nullable
array becomes an anyOf), so candidate size is not monotone in the
retained depth and stopping at the first overflow could skip a deeper
candidate that fits. A schema is now kept whole when its summary is not
smaller, and fitting searches from the deepest level down.
Property tests check that every summary accepts what its schema accepts
over every catalog input subschema and synthetic tuples, and that the
fitted schema is the deepest fitting candidate. The browser comparison
boundary test now expects a malformed `after` capture to be rejected
canonically, since `after` is advertised as the same schema as `before`;
the same malformation on `before` stays advertised-invalid.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(javascript): bind defaulted and destructured Webpack factory roles
A Webpack factory whose module parameter was defaulted (`e = {}`) or
destructured (`{exports: x}`) reported an exact empty export list with
complete coverage, because roles were taken only from plain identifiers.
Bind factory roles once, by position, in javascriptBundlerFactoryRoles.
Defaulted module/exports parameters keep their role (the convention always
supplies the argument), `{exports: alias}` binds the alias to
module.exports, and a repeated sloppy parameter name binds its last
argument. Other role patterns (rest, array, nested or computed keys, a
non-identifier loader) receive no role and count as an unknown finding,
so the file's analysis is partial instead of claiming no exports. The
export collector, the factory require name and the standalone bootstrap
externals all use these parsed roles.
Fixes#7113
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(javascript): keep factory receivers through unordered reads
A defaulted role parameter kept the unused default in the binding facts
passed to write ordering, so a receiver read inside a loop, nested block
or closure was unresolved and its export silently dropped while the
module stayed complete.
The role owner now removes defaults the calling convention never runs
before ordering. Separately, an export-shaped write through a role
binding whose read stays unordered now counts as an unknown finding
instead of being dropped, so an incomplete list is never reported as
complete.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(javascript): treat never-run factory parameter defaults as dead code
Removing unused defaults from binding facts let writes inside a default
expression reach the export collector: `(e = {}, t = (() => {
e.exports.ghost = 1; })())` reported `ghost` although Webpack supplies
both arguments. Loader calls in such defaults also produced require
references and async chunk keys.
The role owner now records the default of every role parameter, any
binding pattern included, as never run. The export collector, the
async-chunk walk and the static source traversal skip those subtrees,
so they yield no exports, references or chunk keys. Defaults of later,
non-role parameters still run and are still reported.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
When cancellation or the deadline interrupted LLDB after it had published
its bridge result, the tracer never read that result, so an explicit
`target_process_created: false` (or `target_process_exit_observed: true`)
was lost. The run kept a `native-target:unknown` marker that no cleanup
retry could release, wedging the tracer for every later trace.
Read the published result once LLDB's process group is stopped, even for
an interrupted run, and apply the same target-gone proof in cleanup retry.
Cancellation or timeout stays the primary outcome; a created target, a
truncated result, or a missing PID alone stays unresolved.
Fixes#5385
Co-authored-by: Claude Opus 5.5 <[email protected]>
* perf(native): charge matching for what it copies, not borrowed retained inventories
Native function matching deep-walked every retained list_procedures record
at about 2.8 KB per Ghidra-shaped row, although the session ledger lends
those immutable records without a copy, and charged each inventory's one
Evidence ID once per function. The report and Evidence copies of every
emitted member were not charged at all.
Charge each inventory row before parsing for the member it becomes,
including its report and Evidence copies, charge the shared inventory ID
once, and skip the walk only for borrowed immutable inventories and closure
records that matching never parses. Inline Evidence, bundle records and
selected observations keep their existing charges. Retained references now
fit about 22,000 rows per side instead of 10,000, and an oversized side is
refused before its rows are parsed.
Refs #6406
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): charge the envelope of borrowed matching inputs
Borrowed inventories skipped admission entirely, but matching copies their
limitations into the report and Evidence and digests their profile, so a
retained inventory with few rows and large limitations escaped the
allowance. Charge every envelope field and leave only the result payloads,
which row charging covers or matching never parses, uncharged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Reduced compact input schemas gave every array property an any-value item
schema, a six-member type list beside nested `items`. Gemini function
declarations accept `items` only beside the single `array` type, so every
request through a Gemini-backed client failed with
`properties[functions].items.items: field predicate failed: $type == Type.ARRAY`.
Reduced array properties now advertise the element types their canonical item
schemas accept. Unknown, tuple, and mixed array elements use the empty item
schema, and properties that may be arrays or another type list typed anyOf
branches. An inline `not: {}` keeps the empty schema. The compact catalog test
now rejects `items` beside a non-array type as well as untyped item unions.
Fixes#5201
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(artifacts): admit raw UDRW disk images
Raw (UDRW, UDTO) disk images have no UDIF koly trailer, so target
admission and root inventory both refused them as unsupported binaries.
Recognize disk images in one domain owner (koly trailer, GPT, Apple
partition map, MBR, or an unpartitioned HFS+/HFSX volume header) that
both boundaries consume.
hdiutil reports "has no checksum" for these images; the native reader
now treats that status line as an explicit absent checksum, records the
nonzero verify command, attaches read-only, and reports that integrity
was not verified. Checksum mismatches and unrecognized images are still
refused.
Positional range reads used by these probes now fill until end of file
so short host reads cannot hide a signature.
Fixes#5687
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(artifacts): read disk image signature windows at the recognizer
Target admission passed its first unfilled 4 KiB read to the raw-image
checks, so a short host read could hide a GPT, APM, MBR or HFS+
signature that inventory (which fills its prefix) still observed. The
recognizer now reads its own leading window through the caller's
filling positional read, and target admission fills its format-probe
prefix as well. The controlled short-read regression now covers target
admission alongside inventory.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
inspect_macho reported only the symbol-table spelling (`_main`), while
LLDB, dlsym and observe_native_calls use the runtime spelling (`main`).
Passing a static name as a breakpoint silently selected a different
function, so results from the two tools could not be joined by name.
Keep `name` as the exact symbol-table fact and add a derived
`runtime_name` to every export and import row, produced by one shared
Mach-O symbol-name owner. Managed P/Invoke verification now uses the
same owner instead of its own underscore stripping.
Fixes#5298
Co-authored-by: Claude Opus 5.5 <[email protected]>
Retain complete browser scenario Evidence while delivering compact summaries and selected step, event, transaction and text views through shared CLI/MCP workflows. Preserve initial CLI failures across cleanup retries and validate with real Chromium/TodoMVC.
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(environment): take created entities' read-backs and re-derived update members
- A create's observation takes the fields the first read of its entity
shows that the answer lacks, and an item read's clock spellings, when the
read agrees with the answer otherwise. The plan records the read
(observation.read with its shape); an item read then observes rather than
checks, a list read only adds fields and still checks. Serving merges the
same recorded objects.
- An update member sent back as the clock the entity held and stored as a
fresh clock near the call is a clock rule; one stored as the slug of
another member stored as sent is a slug rule. Both are accepted with any
value, as whole-object writes send what they read.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the write-views verification
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): reconcile write views without hiding contradictions
* fix(environment): reject incompatible slug-only saves
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(javascript): report fork's silent stdio as piped
Node's child_process.fork pipes stdin, stdout and stderr when options.silent is truthy and options.stdio is neither a string nor an array, and inherits them otherwise. REA never read silent, so fork("worker.js", { silent: true }) was reported with stdio_mode default (inherited). The unset-stdio case of fork now reads silent: truthy gives pipe, falsy or missing gives default, and an unresolved value gives dynamic.
* fix(javascript): apply fork's silent rule to other known stdio values
* fix(ghidra): report a DOL language override conflict as a configuration error
A REA_GHIDRA_LANGUAGE_ID outside PowerPC:BE:32:* made an otherwise admitted GameCube DOL an unsupported target. docs/ghidra-provider.md and the other fixed-import lanes treat such an override as a configuration error naming the conflicting setting. The DOL lane now uses the same languageOverrideConflict owner and names only REA_GHIDRA_LANGUAGE_ID, since DOL accepts a compiler-spec override.
* test(ghidra): keep a valid compiler spec out of the DOL language conflict
* feat(binary-layout): admit ELF32 i386, ARM and MIPS layout profiles
inspect_binary_layout refused every ELF32 object, so router and IoT
firmware (ARM32, MIPS32) got no layout facts at all.
The pwntools adapter now admits EXEC/DYN/REL objects in these profiles:
ELF64 x86-64, ELF32 i386, ELF32 little-endian ARM and ELF32 little- or
big-endian MIPS. Header fields are read in the declared byte order, the
ELF32 header is bounds-checked at 52 bytes, and pwntools' own reported
architecture, class and byte order must match the admitted profile.
Other machines, classes and byte orders keep an unsupported_target
refusal that names the admitted profiles.
The output schema admits exactly those architecture combinations, and
the Evidence subject now names the reported machine instead of always
claiming x86_64.
verify:binary:layout cross-compiles i386, ARM, MIPS and MIPSel
executables and relocatable objects (never executed) and checks every
section name, address, offset and size against GNU readelf through CLI
and MCP. The real-binary-layout workflow installs those toolchains.
Refs #6934
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* ci(binary-layout): install target libc headers for ELF32 fixtures
--no-install-recommends skips the libc6-dev-*-cross packages the cross
compilers only recommend, so the i686 fixture fell back to the host's
/usr/include and failed to find bits/libc-header-start.h. Install the
four target libc development packages explicitly.
Refs #6934
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(binary-layout): size the minimum symbol entry by ELF class
The relocation reference check required a 24-byte Elf64_Sym entry, so
every positive symbol reference in an ELF32 report was refused as
unreadable output. Elf32_Sym is 16 bytes; take the minimum from the
reported class. A 64-bit table with 16-byte entries is still refused.
Refs #6934
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(binary-layout): compare ELF32 symbols and relocations with readelf
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
Apple's crash report JSON documents termination.byPid, byProc and flags, the body's parentPid, and
the metadata line's build_version and platform. The parser shapes did not list them, so they were
reported only by name in unlisted_keys / unlisted_body_keys and their values were dropped: a
watchdog kill no longer said which process ended it, and process kept parentProc without its pid.
#4297 asks for termination members beyond namespace/code/indicator to be retained as recorded, and
docs/apple-ips.md says documented fields are reproduced.
They are now kept: termination.by_proc, termination.by_pid and termination.flags, process.parent_pid
and metadata.build_version / metadata.platform. The integers are recorded integers, so they count
toward max_integer_text_bytes and a type mismatch is recorded like the other fields.
* fix(cli): preserve literal Markdown table facts
* test(cli): retain capacity diagnostics through Markdown escaping
* fix(cli): keep intraword underscores readable in Markdown tables
Underscore runs between letters or digits cannot open or close CommonMark emphasis, so escaping them only adds noise to identifiers such as source_index and invalid_request in raw Markdown. Escape underscore runs only at word boundaries and restore the original diagnostic assertions.
inspect_web_network_capture reported a record_ordinals entry past the capture's records as an invalid_format issue on record_ordinals, with no bounds. It is now an out_of_range issue on that entry (record_ordinals[i]) with minimum 0 and maximum total_records - 1, or invalid_value when the capture has no records.
RFC 9110 section 5.5 excludes leading and trailing whitespace from a field value. The gRPC-web trailer parser stripped only leading SP/HTAB, so 'grpc-status: 0 ' was reported as '0 '. Trailing SP/HTAB is now excluded too; internal whitespace and the examined byte count are unchanged.
REA_GHIDRA_LANGUAGE_ID only allowed LE or BE as the endian field and no '/' in the processor, so it rejected four language IDs that ship with Ghidra: ARM:LEBE:32:v7LEInstruction and v8LEInstruction (ARM BE8) and M16C/60:LE:16:default and M16C/80:LE:16:default. The syntax check now allows LEBE and a slash between processor name parts; the installed .ldefs catalog check is unchanged.
Preserve GitHub User metadata for Copilot while excluding it from generated human thanks and accepted prose credits. Extend the real inventory/render/check regression and document the known agent identity.
* feat: add FreeBuff agent support for REA setup
Register FreeBuff as a supported setup client using the shared user
agents directory (~/.agents/mcp.json with mcpServers, skills in
~/.agents/skills), which matches what FreeBuff Desktop loads. Adds the
client definition, boundary tests, supported-agent docs, and the skill
reference client list.
* fix(freebuff): detect FreeBuff from its profile directory, not shared .agents
The ~/.agents marker made FreeBuff appear installed for any home where
REA had installed shared skills (the default skill destination, also
Gemini CLI's), producing false doctor failures. Detect FreeBuff from
its own fixed profile directory ~/.config/freebuff-desktop instead,
and add a regression test covering a home with only the shared agents
skill directory.
* fix(setup): require product evidence for FreeBuff discovery
Address review findings at their producing boundaries and preserve directly affected consumers. Source inspection and git diff --check completed. Tests and automated checks were not run at the user’s explicit request.
* fix(uninstall): retain unverified shared client configuration
* fix(clients): preserve scoped removal evidence
* fix(doctor): omit undefined selectedClientIds under exactOptionalPropertyTypes
Passing selectedClientIds explicitly as undefined fails typecheck on this
branch (TS2379), which blocks the focused test workflow before any test
can run. Spread the option only when defined.
* fix(update): separate ownership evidence from product applicability
rea update's maintenance scope inspected only product applicability
evidence, so when a FreeBuff profile was absent but REA already owned a
version-pinned row in the shared ~/.agents/mcp.json, the plan omitted
freebuff and left the integration stale. FreeBuff now declares its
registration storage as shared, which proves ownership of an owned row
but never product applicability, and maintenance requests ownership
evidence through that declaration instead of reinterpreting the shared
paths. Doctor and setup keep readiness evidence, foreign rows stay out
of maintenance, and the Doctor exactOptionalPropertyTypes fix makes the
focused test workflow runnable.
* test(dotnet): split oversized operand extent diagnostics describe
The describe callback reached 162 lines (max 150), failing
max-lines-per-function and blocking the pre-push check for this branch.
Move the single-case extent scenarios into their own describe; no test
content or order changes.
* fix(update): preserve ZCode's disabled shared registration during maintenance
The shared user agents file doubles as ZCode's fallback, where
`enable: false` disables the registration. Ownership-based maintenance
read that row as a FreeBuff registration, whose format does not
recognize the flag, so the row was selected and the emitted setup
command rewrote it, dropping the flag and silently re-enabling a
registration the user had disabled.
registrationActivation now honors `enable: false` for any client whose
registration storage is shared, because the activation fact binds every
reader of that storage. Maintenance leaves such rows out; doctor
reports them as stale, matching ZCode's own treatment.
* fix(doctor): let explicit client selection authorize the config read
When an ACL or an inaccessible shared parent made every access probe
fail, a client selected by name was omitted before the configuration
read could return its unreadable-config detail, so doctor reported it
as not detected with installation remediation: an unresolved selected
path collapsed to absence.
An explicit selection now authorizes the read outright and the probes
serve automatic discovery only. A selected client with an unreadable
config reports invalid with the read failure; one with no config
reports missing; automatic discovery still probes before inspecting.
* fix(uninstall): share shared-storage checks between ZCode and FreeBuff
clients() queued ~/.agents/mcp.json twice: once as zcode_fallback and once
as freebuff. ZCode has no discoveryPaths, so its strict read still blocked
uninstall on malformed shared text, despite FreeBuff's new retain-and-continue
behavior. For a valid owned entry, ZCode removed it first, so FreeBuff reported
skipped rather than removed.
Make the shared file's checks and removal one shared decision:
- Add zcodeDiscoveryPaths to ZCode so it has its own applicability evidence.
- Gate zcode_fallback push on applicable, not just zcodeFallbackPath presence.
- Report shared storage ownership as one decision for both clients:
FreeBuff and ZCode now use clientOwnershipEvidencePaths together, so a
recognizable REA-owned row proves ownership once for the shared storage, and
both clients report the same result consistently.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix(uninstall): merge duplicated host and prove shared ownership from owned rows
Uninstall.ts declared systemUninstallHost twice, preventing module load,
with the exported copy returning only clients and the second copy missing
clientWriteBlocker. Merge into one exported host carrying both blockers.
hasOwnershipEvidence used access() existence, so an unrelated malformed
~/.agents/mcp.json established ownership and blocked every removal.
Ownership now requires a readable, recognizable REA-owned row via the
injected filesystem, so unrelated shared data stays retained.
Also cover ZCode discoveryPaths in its path test.
* fix(uninstall): keep denied product-local configurations fail-closed
Unproven product applicability marked every configuration path of that client unverified, so a denied ZCode profile turned an unreadable product-local config into a retained item: uninstall continued and could report complete with REA still registered. The retain-and-continue exception now applies only to storage REA does not own, classified once by clientSharedRegistrationPaths (shared configuration plus another product's same-scope fallback).
Covers the Greptile P1 on Uninstall.ts. Validation: the new test fails on the pre-fix Uninstall and passes after it; freebuff/zcode/updateMaintenance/bom/dsh suites green; clientLifecycle, clientRegistrationStatus and claudeDesktop keep the same 8 host failures as the pre-merge baseline (symlink EPERM, stale-vs-aligned on Windows).
* fix(update): retain FreeBuff when ZCode reads the shared fallback
Maintenance deduplicated clients on the path each inspection read, so ZCode's fallback view of ~/.agents/mcp.json marked that file "covered" and dropped FreeBuff from the scope. ZCode setup writes ~/.zcode/cli/config.json and deliberately leaves the fallback untouched (docs/client-configuration.md), so the emitted command refreshed only ZCode while the shared row both products load stayed pinned to the old package version. Each setup action rewrites only its own configuration path and no other action rewrites the shared row, so every selected client keeps its own maintenance action; the read-path covered/deduped filter is removed.
Covers the Codex P2 on UpdateMaintenance.ts:104. Validation: the new regression fails on e2fdec384 with {clients:["zcode"]} and passes with {clients:["freebuff","zcode"]}; updateMaintenance 31/31, setupClientConfigCleanupOwnership, cliUpdateAlias, updateOutput, cliStartupImports green; tsc --noEmit, oxlint (0 errors) and oxfmt --check clean; clientRegistrationStatus keeps the same 4 symlink/stale-vs-aligned failures this Windows host has on base e2fdec384 (verified by stashing the change).
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
* fix(uninstall): preserve completed reads as shared ownership evidence
hasOwnershipEvidence caught every read failure and kept scanning, so a handle-close failure after a successful read — a RegularFileCleanupFailure whose completed outcome still contains the exact shared-file text — was discarded as "no proof". FreeBuff's applicability-unverified storage therefore never got its ownership fact, and once the retained handle blocked later reads, readClientConfiguration downgraded them to retained: inspection passed, removals soft-failed, and runUninstall(true) could purge REA data while the active shared registration remained. The catch now parses the completed outcome for the ownership text while missing or unreadable storage and malformed data keep their proves-nothing semantics, so ownership holds and every later read failure stays blocking.
Covers the Codex P2 on Uninstall.ts:252. Validation: the new regression fails on 4dbfdf1a9 (freebuff reports no blocking failure while the poisoned-scope error lands on claude_code) and passes after; setupClientConfigCleanupOwnership 25/25; the other 21 Uninstall-consumer suites are byte-identical to base 4dbfdf1a9 (61 pre-existing Windows-host failures: symlink EPERM, stale-vs-aligned); tsc --noEmit, oxlint (0 errors, same 6 warnings) and oxfmt --check clean.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
* fix(uninstall): keep owned-row proof through removal refusals
hasOwnershipEvidence treated any clientRegistrationRemoval refusal as no ownership: an effective REA-owned rea member shadowed by an earlier foreign duplicate (shadowed_unowned_member), or an owned native row beside a foreign V1 fallback (foreign_legacy_fallback), discarded the already-parsed positive row fact. With FreeBuff unverified and close failures recurring on the preflight/removal rereads, those failures were then downgraded to retained, so runUninstall(true) removed the skill and purged REA data while the active owned row remained. The refusal now carries the owned rows that motivated it — the refusal only refuses the edit — and hasOwnershipEvidence evaluates that fact on both paths; preflight and removal keep refusing the unsafe edit exactly as before.
Covers the Codex P2 on Uninstall.ts:269. Validation: the new regression fails on 225cb214a (freebuff reports no blocking failure while the poisoned-scope error lands on claude_code) and passes after; setupClientConfigCleanupOwnership 26/26; the 21 Uninstall-consumer suites are byte-identical to the recorded 225cb214a baseline (61 pre-existing Windows-host failures: symlink EPERM, stale-vs-aligned); tsc --noEmit, oxlint (0 errors, same 6 warnings) and oxfmt --check clean.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
* fix(uninstall): keep unresolved discovery probes from softening shared failures
The discovery probe swallowed every stat failure, so a FreeBuff profile denied with EACCES/EIO was indistinguishable from an absent profile: shared storage was marked applicability-unverified and, when ~/.agents/mcp.json also remained unreadable, the ownership and removal reads softened to retained. runUninstall(true) then removed the skill and purged REA data while an installed FreeBuff could still hold an active registration — the regression even reports complete. Absence (ENOENT) is now the only state that leaves shared paths unverified; any other probe failure leaves applicability unresolved, and shared-config failures stay blocking.
Covers the Codex P2 on Uninstall.ts:318. Validation: the new regression fails on 7572a7082 with status complete and purged data and passes after; freebuffClientConfiguration 13/13; the Uninstall-consumer batches are byte-identical to the recorded baselines (61 pre-existing Windows-host failures: symlink EPERM, stale-vs-aligned); tsc --noEmit, oxlint (0 errors, same warning rules) and oxfmt --check clean.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
* fix(uninstall): retain completed configuration reads as removal evidence
A close failure after a completed read discarded the text that read
produced, so an owned registration row the text proves softened to
retained. The completed text now drives removal, keeping shared-config
failures blocking when ownership evidence exists.
Co-Authored-By: Anthropic <[email protected]>
* fix(uninstall): stop removal when a cleanup retry leaves a handle unreleased
A completed reread whose handle the cleanup retry cannot release now fails
blockingly instead of serving the text: with no owned row, the soft path
let removeAll purge managed data before close() reported the incomplete
cleanup. The completed text is served only after the retry releases the
handle; an unreleased handle is REA's own failure, never applicability
uncertainty, so it bypasses the retained softening.
Co-Authored-By: Anthropic <[email protected]>
* fix(uninstall): keep the ZCode fallback when its profile probe is unresolved
A denied or failed profile probe leaves ZCode's applicability unresolved,
never absent, but the fallback branch dropped the shared row on
applicability alone: with the native config and FreeBuff's profile both
absent, an installed ZCode kept its uninspected registration while
uninstall purged skill and data. Unresolved probes now keep the fallback
in scope with the same fail-closed treatment as other unresolved shared
paths; definitive absence still keeps ZCode out of another product's
primary storage.
Co-Authored-By: Anthropic <[email protected]>
* fix(setup): preserve a shared row's enable:false during FreeBuff repair
The shared agents file doubles as ZCode's fallback, so its `enable: false`
binds every reader: doctor now reports the disabled row as stale for
FreeBuff, but following that remediation rebuilt the generic JSON entry
without the flag and silently re-enabled a registration ZCode disabled.
Repairs of shared storage now carry the activation fact forward, while a
client's own native storage keeps its established single-reader repair
contract; the denied-profile uninstall regression also pins the
fallback's new fail-closed inspection beside the denied read.
Co-Authored-By: Anthropic <[email protected]>
* test(uninstall): retarget the completed-reread regression to a released retry
The concurrent-writer regression held the reread handle open forever,
which the fail-closed path now serves: it duplicated the unreleased-handle
regression while documenting the opposite contract. The fixture's close
spy gains a failure limit so the cleanup retry releases the handle, and
the test again proves the completed text drives removal of the
concurrently added owned row, with purge verified against the cache
layout purge actually removes.
Co-Authored-By: Anthropic <[email protected]>
* fix(doctor): report a preserved shared disable as a manual conflict
Preserving a shared row's enable:false left doctor recommending a setup
run that registrationCurrent correctly treats as already current, so the
scoped doctor returned needs_human forever. The shared-enable
interpretation now has one owner, sharedEnableFlagBinds, used by both
the activation rule and the remediation: a row disabled through the
shared flag reports the manual conflict and states that rea setup will
not change it, instead of an endlessly ineffective repair.
Co-Authored-By: Anthropic <[email protected]>
* fix(doctor): keep a native ZCode disable on the setup repair path
The manual-conflict remediation matched a native zcode row through
sharedEnableFlagBinds, but setup drops enable:false on single-reader
storage, so a native disabled registration was told setup would not
change it. The remediation now reads setup's own writer rule,
exported as setupPreservesDisabledActivation: shared storage and the
zcode fallback view keep the manual-conflict message, while a native
row keeps the working rea setup instruction. Regressions pin both
remediations.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
* revert: drop the unrelated managed-CIL describe split
The split does not belong to FreeBuff client support and only widens this
PR's diff.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
* fix(uninstall): remove the shared agent registration once for both clients
ZCode's ~/.agents/mcp.json fallback and FreeBuff's own configuration queued the
same physical file twice, so ZCode removed the row and FreeBuff then reported
it absent, losing the outcome for the client that owns the file.
Resolve every configuration path to one owner -- the client whose
configuration it is -- and project that single inspection and removal to each
client that loads it. A fallback reader still contributes its discovery probe,
so an installed or unresolved reader keeps the shared file fail-closed.
Discovery also accepted a dangling ~/.config/freebuff-desktop link, which setup
and doctor reject because they follow the target, so an unrelated malformed
shared file blocked every other removal. Resolve the link before treating the
profile as installation evidence, keeping lstat for removal safety.
Also date the FreeBuff release wording against the published 6.7.0 package.
* fix(uninstall): keep a row per client when two clients own one file
OPENCODE_CONFIG can point OpenCode at FreeBuff's configuration file, where
OpenCode writes `mcp.rea` and FreeBuff writes `mcpServers.rea`. Grouping by
filename gave that path a single owner, so uninstall removed only OpenCode's
row, reported that removal for FreeBuff, and could purge managed data with
FreeBuff's registration still active.
Share the decision only through a fallback view of another client's row: every
client that declares the path as its own configuration keeps its own
inspection and removal, and the fallback reader reports each of them.
* fix(clients): preserve discovery and fallback registration identity
Follow product discovery link targets without losing configuration entry
presence or explicitly selected inspections. Associate fallback readers with
the format owner's registration table and project outcomes by owner and path,
so OpenCode removal cannot be reported as a ZCode fallback removal.
All three new boundary regressions fail against the previous source and pass
with the fixes. Validation: check:fast; 217 focused tests passed; the two
changed test files were rerun with 50 passing tests. No installed FreeBuff
client was launched.
---------
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Codebuff <[email protected]>
* fix(apple): keep the recorded image name and frame symbol offset of IPS reports
Apple's IPS format documents usedImages[].name and frames[].symbolLocation, and docs/apple-ips.md says documented body fields are reproduced, but the parser shapes did not list either member. They were syntax-checked and discarded without an unlisted_keys entry, so a symbolicated frame lost its +offset and every image its binary name. Both are now kept: images[].name and frames[].symbol_location as a recorded integer, with type mismatches recorded like other fields.
* fix(apple): count frame symbol offsets in the integer text check
When formatting arrays of objects that have no own enumerable keys as
Markdown, formatObjectTable derives empty headers and outputs a malformed
table with an invalid delimiter row. Require at least one item with keys in
isObjectArray and fall back to JSON serialization when headers are empty.
Fixes#7057
Content-identical assets share a node whose observations are ordered by identity. Accept either retained display path when selecting that node, while retaining exact per-path reference and observation assertions.
* fix(browser): report an out-of-range script_index as a selection fault
A valid export_web_scripts manifest without the selected script_index was reported as an invalid_format input issue, as if the manifest file were malformed. It is now an out_of_range issue on script_index with the valid bounds, or invalid_value when the manifest has no scripts.
* test(browser): keep the out-of-range index case in its own block
---------
Co-authored-by: morluto <[email protected]>
Separate syntax, semantic uncertainty, examination limits and observed Electron applicability at the static producer, then preserve them through worker, graph and retained/inline summary views. Derive the established parse_status from the same facts and leave historical measurements unknown. Give semantic resource-limit classifications one schema owner.
Replace repeated block-prefix searches and copies with collector-scoped syntax indexes. Keep invocation environments separate, charge indexing/replay to the existing work budget and yield during prefix work. Remove the superseded synchronous collector entrypoint.
Validated producer, worker, CLI/MCP, historical and partial-coverage regressions, plus actual cancellation during indexing. Bounded authored 128/256-call controls preserved all prior facts while replacing repeated search/copy work with one index pass; this is not a throughput or agent-token benchmark.
Convert native directory separators only at Git patch argument production. Exercise fresh, applied and invalid dependency patches in private copies, preserving protectNTFS, and select the regression for Windows CI.
Replace the spelling-based mock inventory with one binding-aware Oxlint rule. Canonical test projects own isolation for both Vitest and lint; external temporary CLI probes avoid concurrent test discovery.
Validated actual local Git and Oxlint execution, resolved-policy parity, CI selection and report tests. Native Windows verification remains with CI. Refs: #7050
Use the domain-owned activity extra count in CLI parsing and advertisement. Preserve nested Rosetta registers at the selecting parser and qualified register projection, sharing the existing aggregate budget.
Port the Rosetta work from Adolanium commits 1889ac153 and c9e93bd88 in #6976, including morluto follow-up 07fa932ee. Add malformed-container and mixed-thread budget controls. CLI/MCP coverage uses a recorded ReportCrash excerpt in an authored report, without generating a native crash or contacting an Android device.
Refs: #6291, #6976, #6977
Co-authored-by: Adolanium <[email protected]>
Preserve primary copy failures, exact descriptor retry ownership and complete-session cleanup state. Make the authored JEB HTTP fixture join admitted callbacks after transport closure.
Validated with controlled real-file faults, cancellation/finalization and retry cases, plus local HTTP and existing CLI/MCP lifecycle regressions. No real IDA or JEB engine was exercised.
Remove the redundant async declaration from the Ghidra session assertion helper so its existing callers receive assertion failures within their synchronous error boundary. Keep call order unchanged.
Validation: a public-helper Node subprocess accepts authored valid handshake facts and rejects incomplete analysis and a target-digest mismatch. The regression failed for the original async declaration with Missing expected exception, then passed with the fix. No installed Ghidra execution was performed.
Clarify structural parsing, admitted facts and freshness, useful facade and compatibility responsibilities, immutable JSON proof, and partial Evidence retention. Document fixture cleanup ownership, semantic fault injection and the limits of static checks; extend the existing PR owner hint. Correct the session-record architecture label.
Validation: docs:check (including nine README consistency tests), rendered docs:build with link validation, touched Markdown formatting and git diff --check passed. Source and installed Vitest cleanup behavior were reviewed. No runtime behavior changed.
Turn recurring parser and projection failures into a selectable regression
guide covering format assumptions, raw identity, missing values, partial work,
independent limits, representation expansion, snapshots, and public delivery.
Link it from the agent instructions and PR template without adding checkboxes.
Clarify completed-observation ownership, digest representations, and exclusive
success/failure budget accounting. Preserve capacity fixtures and use byte
comparisons for large buffers to avoid diagnostic allocation blowups.
Passive page capture retained console entries and websocket frames with no
aggregate ceiling. Every sibling retention surface already enforces
WEB_RUNTIME_LIMITS.retainedEventBytes (8 MiB): the observation session's
pre-arm buffer and document-request identities, the browser runtime sources,
requests, and metadata, the V8 inspector's retained metadata, and this same
capture's script metadata, which has its own count-and-bytes budget. A page
that logs a few hundred megabytes of console text, or sends text frames whose
JSON shapes carry hundreds of thousands of keys, grew the capture without
limit until the process ran out of memory; the MCP response budget runs after
the handler, so the growth happened before any typed refusal could exist.
Admit each console entry and websocket frame against the same shared budget
before retaining it. A refused entry is counted through the completeness
tracker's dropped-events vocabulary, which already carries console_events and
websocket_frames kinds and was so far used only for late scripts, so the
inspection reports a truncated inventory with the dropped count instead of
silently overflowing. Console entry bytes measure the retained representation:
the entry's type, url, argument-type strings, and the text capture's retained
bytes, which the handler already computed per entry. A websocket frame retains
its shape summary rather than the payload, and the shape grows with the
payload's own keys, so the frame is charged the serialized size of the shape
it retains. beginAuthorizedFrame resets the counter together with the rest of
the per-frame state.
Out of scope: response metadata and agent hints, whose rows are fixed-shape
facts already bounded per response, and any new setting. The shared 8 MiB
constant is reused as-is.
Validation: RED then green in src/browser/CdpCaptureEvents.eventBudget.test.ts,
where the console case previously retained every oversized entry and the
websocket case retained the over-budget frame; 26 tests across the neighboring
console, websocket, coverage, and completeness suites pass. On this Windows
host the wider browser project has 26 pre-existing failures in three
Electron/WebBundle worker-spawn files, reproduced identically with this change
stashed (26 failed, 644 passed on the pristine base; 26 failed, 647 passed
with it on the same file set). tsc --noEmit over the changed set exits 0,
oxlint reports 0 errors, oxfmt --check is clean, and verify:module-boundaries
passes. No real browser session was driven on this host; the browser CI lane
owns that coverage.
Each retained entry now carries a conservative fixed overhead, and each captured console text value a wrapper cost, so a stream of minimal entries cannot admit millions of objects into the budget when shapes are disabled; a mixed-kind test locks that console entries and websocket frames spend one shared allowance.
overflow into the remediation and ended the reason with the session
continuation sentence, but the real-Ghidra boundary check still required
the old reason text, so every lane that runs verifyGhidraBoundaries failed
on real Ghidra.
* feat(environment): explain values an application derives from other fields
Prompt-built environments withheld creates whose responses carry values the
application derives deterministically, which the write model kept as
constants or could not match across calls:
- A composed string is literal text plus parts read from the fields other
rules write: a field's value, its slug, or a clock's calendar digits. A
create composes a string only when another entity of the type spells it
the same way and none spells it otherwise, and never from two sources
serving could tell apart; an update composes an unsent change from members
stored as sent and unchanged fields that also spell its value before the
call. A composed field no longer pins the members it reads.
- A trimmed clock format drops trailing fraction zeros, as Go spells
nanoseconds. It is learned from the type's values, a later create or a
later update, and spreads to the plan's other clocks unless a value shows
a fixed width.
- A large integer no entity of the type holds, among distinct ones of the
same length and kind, is a generated unique value, served as a seeded
integer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the derived-values verification
Gates, tests, write plans over 69 recorded captures and served write tasks on the baseline and on this branch.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): learn a create plan from every call
A later create the plan does not take is explained on its own and merged when both calls show the same fields, every field both rule they rule alike, and a field neither rules holds one value: members either call sent are accepted, only those every call sent are required, and an echoed member a call left out takes the value that call stored, kept in the body's defaults.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): sorted arrays, referenced entities and nested view keys
- An echo is `sorted` when the application stored the sent strings in
code-point order; serving stores them sorted, and a plain echo and a
sorted one agree in update and create plans.
- A `reference` is an object field holding fields of the entity a sent
member names, as the state holds it. The member is accepted with any
value; replay and serving look the entity up, and serving refuses a call
naming one the environment does not hold.
- A view names in `nested_fields` the keys it always shows of objects it
shows with fewer keys than an entity stores; a stored object it never
recorded renders with them. The view spelling learner moves to its own
module.
- Every later create, not only one the plan refuses, is merged into its
plan when it agrees, keeping the plan's rule order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep clock spellings, linked ids and word boundaries honest
- A later update's clocks take the plan's spelling, sibling-trimmed
clocks included, wherever the call's values fit it, so an update whose
later call trims only one of two clocks is served.
- A held value that ends its fraction in a zero keeps a create's clock
at a fixed width, whatever order the held spellings come in.
- A large integer that another field of the created entity also holds
is linked, as a repeated token is: it is never unique, and the create
is withheld as unexplained-change.
- Composed parts are delimited by whole characters through the shared
joinsBefore/joinsAfter, so an astral letter joins a match to its word.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(environment): keep a same-width fixed clock out of trimming
A held .123456780 beside a held .12345678 keeps the create's clock at
nine digits in either order; the cases fail on 8ce1127b5.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the create-calls verification
Gates, tests, write plans over 69 recorded captures and served write tasks on the derived-values baseline and on this branch.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): note the create-calls census on current main
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): check composed parts and clock fits before they shape a plan
- A composed string whose part has nothing to spell refuses the create
or update through the apply Result instead of keeping a stale value.
- A composition learned on a later call is rebuilt from each earlier
call's fields, so an earlier call that left the value otherwise
contradicts it.
- An update adopts its fitted clock spellings only after the
unknown-entity and contradiction checks, so a call on an entity an
unmodelled write left unknown no longer trims a served plan's clock.
- Sibling trimming on a later create also checks that call's fields,
so a sibling clock it shows with a trailing zero stays fixed-width.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(environment): split the derived-values view tests by rule
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep fixed-width, zone and generated-integer evidence through a plan
- A plan keeps the first value that showed each clock at a fixed width,
from held entities and every accepted create or update. No later fit
or sibling trim widens that field, and a later call that shows it
shorter is withheld.
- Two stamped clocks are one composed source only when they share a
zone offset. A held entity that holds the target field as anything
the parts do not spell, a non-string included, vetoes the
composition. The token index is built once, and only when a string is
left to compose.
- A generated integer that another field repeats as a number at any
depth is linked. Compare treats two integers of one JSON kind and
length under a unique rule as generated values.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep optional references across create calls and in answers
- A reference a later create added stays while further creates leave its
member out, and agrees with a later call that shows it again.
- A create's answer shows optional reference and echo fields only when the
call wrote them, whichever recorded call the answer came from.
- An update that re-sends the id of the entity a field already repeats keeps
the plan's reference instead of pinning the member.
- An id naming entities of one type in several collections teaches no
reference, and serving or replay refuses a call that names one.
- An echo is not promoted to sorted once a call stored its strings out of
code-point order as sent.
- Nested view keys compare as sets and keep the first recording's order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(setup): share registration prewarm host preparation
* fix(environment): retain write evidence and match trimmed response clocks
* fix(environment): withhold creates whose generated integer another string repeats
A unique integer whose digits show in another top-level string that no
template or composed rule rebuilds from it (an echoed serialText, or a
token like folder-<id>) withholds the create as unexplained-change,
since serving would seed the integer and keep the string. A templated
copy is still served.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): respect earlier clock widths and left-out members in later creates
- The create merge widens a clock to trimmed only against the value that
showed its width: a held entity or accepted call at a fixed width keeps it
untrimmed.
- A later create that left out a member the plan fills, with a default or a
reference's absence, while its observation shows that field otherwise is
unexplained-change, so the plan cannot serve the stale value.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): hold later creates and compare to the composed and integer forms
- A later create whose response spells a composed field otherwise than
the plan rebuilds it is withheld as unexplained-change.
- A unique field learned from a generated integer accepts only an
integer of the same length and kind from a later create, as serving
seeds one.
- Compare counts a composed field as equal when each side's response
spells it from its own fields, which compare under their own rules.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): preserve composed parts and unique generator families
* fix(environment): reject contradictory create references
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
Remove duplicate root lists and teardown deletion for directories already owned by createTestTempDirectory. Retain independent IDA workspaces, runtime closure, environment resets, permission recovery and explicit deletion assertions.
Validation: all nine affected suites passed (126 tests); installed Vitest cleanup ordering reviewed. Provider protocols use authored fixtures, not installed IDA or Ghidra.
Decode optional-header declarations once and pass detached directory words, locations and SizeOfHeaders to native consumers. Preserve executable machine admission, raw GPU/resource machine reporting, RVA mapping and certificate file-offset semantics.
Validation: fresh build/catalog, full static checks, 466 core/provider fixture tests, 122 workflow tests and 14 selected file-backed PE admission tests passed. Public CLI and actual stdio MCP used authored artifacts; installed external providers were not qualified. Added 17 structural cases and two GPU machine-policy controls. Eager directory decoding costs remain unmeasured.
Carry the command identity already parsed during registration inspection into maintenance selection. Keep activation, validity and public status output separate.
Validation: fresh build/catalog and full static checks; registration status, update maintenance and setup cleanup ownership suites passed (90 tests).
Return typed fetch outcomes with completed map coverage when acquisition is
cancelled or a response resource fails. Compose those facts with the captured
inspection as partial bundle Evidence, preserving exclusions, exact abort causes,
and cleanup priority without claiming unperformed script analysis.
Keep complete response-body SHA-256 and byte counts when map interpretation
fails, while preserving compact failure rows and capacity for later valid maps.
Reuse the read owner digest for successful text artifacts and preflight their
JSON representation before parsing. Historical failed rows remain importable.
Exercise controlled HTTP/stream boundaries, CLI SIGINT, and SDK cancellation
with later MCP Evidence retrieval. Share the public cancellation fixture and
use one Result API across consumers. Real Chromium and Windows CLI cancellation
were not verified.
* feat(environment): explain values an application derives from other fields
Prompt-built environments withheld creates whose responses carry values the
application derives deterministically, which the write model kept as
constants or could not match across calls:
- A composed string is literal text plus parts read from the fields other
rules write: a field's value, its slug, or a clock's calendar digits. A
create composes a string only when another entity of the type spells it
the same way and none spells it otherwise, and never from two sources
serving could tell apart; an update composes an unsent change from members
stored as sent and unchanged fields that also spell its value before the
call. A composed field no longer pins the members it reads.
- A trimmed clock format drops trailing fraction zeros, as Go spells
nanoseconds. It is learned from the type's values, a later create or a
later update, and spreads to the plan's other clocks unless a value shows
a fixed width.
- A large integer no entity of the type holds, among distinct ones of the
same length and kind, is a generated unique value, served as a seeded
integer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the derived-values verification
Gates, tests, write plans over 69 recorded captures and served write tasks on the baseline and on this branch.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep clock spellings, linked ids and word boundaries honest
- A later update's clocks take the plan's spelling, sibling-trimmed
clocks included, wherever the call's values fit it, so an update whose
later call trims only one of two clocks is served.
- A held value that ends its fraction in a zero keeps a create's clock
at a fixed width, whatever order the held spellings come in.
- A large integer that another field of the created entity also holds
is linked, as a repeated token is: it is never unique, and the create
is withheld as unexplained-change.
- Composed parts are delimited by whole characters through the shared
joinsBefore/joinsAfter, so an astral letter joins a match to its word.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(environment): keep a same-width fixed clock out of trimming
A held .123456780 beside a held .12345678 keeps the create's clock at
nine digits in either order; the cases fail on 8ce1127b5.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): check composed parts and clock fits before they shape a plan
- A composed string whose part has nothing to spell refuses the create
or update through the apply Result instead of keeping a stale value.
- A composition learned on a later call is rebuilt from each earlier
call's fields, so an earlier call that left the value otherwise
contradicts it.
- An update adopts its fitted clock spellings only after the
unknown-entity and contradiction checks, so a call on an entity an
unmodelled write left unknown no longer trims a served plan's clock.
- Sibling trimming on a later create also checks that call's fields,
so a sibling clock it shows with a trailing zero stays fixed-width.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(environment): split the derived-values view tests by rule
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep fixed-width, zone and generated-integer evidence through a plan
- A plan keeps the first value that showed each clock at a fixed width,
from held entities and every accepted create or update. No later fit
or sibling trim widens that field, and a later call that shows it
shorter is withheld.
- Two stamped clocks are one composed source only when they share a
zone offset. A held entity that holds the target field as anything
the parts do not spell, a non-string included, vetoes the
composition. The token index is built once, and only when a string is
left to compose.
- A generated integer that another field repeats as a number at any
depth is linked. Compare treats two integers of one JSON kind and
length under a unique rule as generated values.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(setup): share registration prewarm host preparation
* fix(environment): retain write evidence and match trimmed response clocks
* fix(environment): withhold creates whose generated integer another string repeats
A unique integer whose digits show in another top-level string that no
template or composed rule rebuilds from it (an echoed serialText, or a
token like folder-<id>) withholds the create as unexplained-change,
since serving would seed the integer and keep the string. A templated
copy is still served.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): hold later creates and compare to the composed and integer forms
- A later create whose response spells a composed field otherwise than
the plan rebuilds it is withheld as unexplained-change.
- A unique field learned from a generated integer accepts only an
integer of the same length and kind from a later create, as serving
seeds one.
- Compare counts a composed field as equal when each side's response
spells it from its own fields, which compare under their own rules.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): preserve composed parts and unique generator families
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
Give ordinary Evidence creation an explicit unknown-payload input contract
while retaining the strong JSON contract for producer helpers and cooperative
immutable creation. Keep schema admission, input snapshotting and identity
hashing with the Evidence owner.
Remove 32 generic JSON preparses that only fed ordinary Evidence construction.
Preserve semantic format validation, selected-source bindings, standalone
JSON boundaries, typed executions and scenario digest contracts. Invalid JSON
now reports its Evidence parameters/normalized_result/raw_result prefix.
Validation: fresh runtime/catalog build, 560 focused and README tests passed,
4 platform-specific cases skipped, full static/docs checks, test discovery,
duplication and diff checks. New regressions cover omitted semantic optionals,
raw-result defaults and nested undefined refusal without input mutation.
Installed providers, real browsers/Frida/JADX and Linux-only recovery remain
unverified; boundary providers are authored fixtures.
Carry the final artifact inspection's exact nested source Evidence through
AnalysisExecution. Derive links in the shared Evidence builder instead of
reparsing the result in CLI and MCP adapters. Retain validation for unknown
and historical Evidence inputs.
Move the shared source recorder into application ownership and migrate its
consumers. Record sources before parents; keep source recording failures
from publishing summaries. Decline snapshot entries whose source closure the
current entry format cannot represent, while retaining investigation Evidence.
Validation: actual ASAR regression failed before the change with missing
links and passes afterward; exact nested identity, source recorder refusal,
final-close cancellation and cleanup failure, historical authentication,
snapshot controls and public CLI/MCP workflows. Fresh runtime/catalog build,
full static/docs, discovery and duplication checks pass. Installed native
providers and platform-specific skipped cases remain unverified.
Return native admission issues and an explicit Hopper timeout result from
the configuration owner. Doctor consumes those parsed facts instead of raw
timeout text plus a separate issue list. Reuse a successful aggregate parse;
validate the individual timeout independently when other settings reject it.
Preserve aggregate error precedence, JEB isolation, omission, constraints,
causes and provider scope. Keep partial diagnostics when unrelated NUL,
loader arguments, language selection or timeout settings are invalid.
Validation: configuration and Doctor workflow regressions, mixed-invalid
settings, full static/docs checks. No installed provider was exercised.
Keep completed module and candidate identities, glue observations, and the
admitted tool profile when WABT validation or later inspection fails. Recover
stable reads from their existing owner when descriptor finalization fails;
mark unparsed glue explicitly and preserve validation uncertainty. Forward
partial Evidence through output/cleanup errors and preserve pre-abort reasons.
Exercise producer failures, partial acquisition, decoding, finalization and
cleanup retry with simulated owned WABT commands. Public CLI and stdio MCP
coverage follows the retained Evidence reference through get_evidence_bundle.
Real WABT 1.0.42 and the Windows shell-fixture lane were not verified.
* fix(il2cpp): accept arm64 module arrays loaded through adrp/add pairs
A Unity 2022.3 macOS Master (LTO) arm64 build keeps no static CodeRegistration, so the module-array fallback that only decoded x86-64 RIP-relative references reported code-registration-not-found.
The registration owner now also decodes aligned adrp + add pairs in executable ranges and keeps the same exact-module-set and single-reference rules.
A minimal Mach-O fixture failed on the previous reader (47 passed / 1 failed) and passes here with four fail-closed variants (48/48 binary tests).
* fix(il2cpp): name the matched instruction form in inferred module-array limits
The inferred-array limitation always described RIP-relative LEA/MOV, which is wrong for arm64 results.
The provider now names the adrp/add pair on arm64, and the tool contract and docs cover both forms and state that a located array alone does not finish linking a Master build.
The new arm64 provider case failed before the wording change (1 passed / 1 failed) and both cases pass now.
* fix(il2cpp): accept type tables loaded without a static MetadataRegistration
A Unity 2022.3.62f3 macOS Master build inlines MetadataRegistration as well as CodeRegistration, so linking stopped at metadata-registration-not-found after the module array was found. Registration now infers the only field linking reads, the Il2CppType table, from the one run of type-shaped pointers that covers every referenced type index and whose start is loaded by the existing RIP-relative or adrp/add forms; types_field is null and the Evidence is inferred. The new arm64 and x86-64 fixtures failed on the previous reader (52 passed / 2 failed). Four run-selection variants (interior reference, run extended before the loaded address, two loaded runs, run shorter than the referenced indices) keep metadata-registration-not-found; instruction decoding and record-shape checks are not covered by these fixtures.
* test(il2cpp): pin adrp page deltas, register pairing and type-record shape
Every adrp fixture targeted its own 4 KiB page, so dropping the page delta or its sign extension, the add register check, the type-kind bound or the type-definition bound left all binary tests passing. Fixtures now reach a later and an earlier page, pair an add with a different destination or source register, break the type run with an out-of-range kind or type-definition index, and bound type-table candidates when the metadata references few type indices. Each of those five source mutations now fails 1 to 3 of 85 binary and provider tests.
* refactor(il2cpp): share Il2CppType kind decoding and document type-table bounds
The type-table fallback decoded the Il2CppType kind and class/value-type kinds separately from the type namer; both now use the Types owner. Zero and unaligned words are rejected before mapping lookup (Master sample metadata-registration search 92 to 68 ms). Docs and the limitation state that types_count is an upper bound, that a run spanning mappings or a small referenced type range can fail, and credit Cpp2IL StrippedCodeRegSupport and the data-side MetadataRegistration searches of Il2CppDumper and Cpp2IL. Master sample output is unchanged (11,031 code addresses, 37,025 type rows).
* fix(il2cpp): refuse generic instances when the type table was inferred
An inferred type table has no static MetadataRegistration, so the
genericMethodTable it would anchor cannot be located. Report
generic-methods-unsupported instead of reading from a null field.
* fix(il2cpp): fail closed when an inferred type table is not unique
Adversarial review found that the type-table fallback could pick another
type-shaped pointer table when the real one was unloaded or excluded, could
replace a static MetadataRegistration whose counts matched, and accepted a
run cut short by a mapping end. Require exactly one long-enough run in the
image, ending inside its mapping and loaded by code; skip the fallback when a
record's two counts matched; accept only assigned Il2CppTypeEnum kinds; and
look records up through the indexed file mapping so many mappings cannot
multiply the scan. Docs and limitations now describe the run length as at
least every referenced type index rather than an upper bound.
* fix(il2cpp): keep the type-table fallback cheap and its refusals exact
Review follow-ups: check that an address can lie in a mapping before the
throwing indexed lookup, so words pointing into gaps or zero-fill no longer
build an exception each (64 MiB: 74 s to under 2 s); treat a run starting at
its mapping start like one reaching its end; stop at the second long-enough
run instead of a 64-candidate capacity error; ignore ADRP/ADD pairs on
register 31; and say whether a counted MetadataRegistration was unusable or
no code-loaded run was found. Docs no longer generalize from one build or
state tool history they do not source.
* fix(il2cpp): judge type-table slots by their loaded value
The run scan read stored words, so a dyld-bound slot's placeholder could
decide whether a run qualified. Confirm each slot through readSlot and give
a run bounded by a bound or unresolved slot an unknown extent, so the
placeholder no longer changes the choice.
* docs(il2cpp): keep the failure-reason table layout
Shorten the two changed meanings so the table keeps its column widths and the
diff shows only those rows.
* docs(il2cpp): keep the reason table at its formatted width
* fix(il2cpp): read type-table record fields through the loader too
The previous change confirmed table slots through readSlot but still read
each record's kind and definition index from stored bytes. Read them with
readUInt32 and readSlot, and treat a record field the loader binds or leaves
unresolved like an unresolved slot: the run it bounds has an unknown extent.
* perf(il2cpp): share image mappings across loader-aware reads
---------
Co-authored-by: morluto <[email protected]>
Separate typed manifest/capture composition from unknown-input loading.
Builds retain the admitted replay facts locally for credential warnings and
record counts instead of parsing the same capture again. Keep full captures
out of the compact replay source and preserve manifest-first authentication,
parent identity checks, failure precedence and cancellation points.
Validation: environment source/build/report/scenario/input-snapshot/state
workflows and public build CLI; fresh runtime/catalog build, full static/docs,
test discovery and duplication checks. No external clients or targets ran.
Use the existing named-contract retained-summary handler for pcap capture
inspection. Remove its duplicate validation, detail selection, preflight and
Evidence projection policy while preserving client inspection separately.
Validation: named pcap MCP schema/delivery, parent issue attribution and
workspace admission tests; fresh runtime/catalog build and full static/docs
checks. Native packet dissection and Linux-only retained-view tests were not
verified on this macOS host.
Return the validated descriptor and its original metadata from regular-file
admission. Propagate that contract through owned reads, hashes, snapshots,
artifact inventory and provider file workflows instead of replacing initial
facts with another stat. Keep arbitrary output-handle operations separate.
Preserve final descriptor/path identity proofs, borrowed-source verification,
BigInt precision, cancellation provenance and failed-close ownership. Remove
the obsolete source and owned-ZIP baseline stages and update their consumers
and regressions. Tie read completion faults to actual reads instead of the
retired duplicate stat; instrument real handles without fake handle wrappers.
Validation: fresh runtime/catalog build; focused source, lifecycle, filesystem,
CLI/MCP and authored provider-fixture tests; full static checks; docs checks;
test discovery; duplication check. Platform-specific cases remain skipped on
macOS. No installed provider or device behavior was verified.
The IPS JSON reader decodes \uXXXX escapes with String.fromCharCode and
returns raw slices verbatim, so an escaped unpaired surrogate reaches
Evidence as-is: malformed text reported under complete coverage, with no
refusal and no uncertainty. A crash report's procName or exception message
comes from the crashed process, so the escape is reachable input.
Refuse the string the way the parser already refuses other malformed
content. The check runs on the decoded value, so it covers escaped and
unescaped text alike, and a high surrogate followed by its low surrogate
escape stays valid text.
Validation: RED then green in src/native/apple/losslessJson.test.ts, where
the new case previously accepted the escaped surrogate; 311 tests across
the apple parser suites pass; tsc --noEmit over the changed set exits 0;
oxlint reports 0 errors; oxfmt --check is clean; verify:module-boundaries
passes.
Co-authored-by: morluto <[email protected]>
Flatten the prewarm cases so suite grouping does not exceed the function-size gate. Let createTestTempDirectory own cleanup instead of deleting the same roots from a second afterEach owner. Preserve all registration and warning assertions.
Validation: all four focused filesystem cases passed using temporary client configurations and injected prewarm results; no package runner or provider was launched.
Use the shared source owner for Mach-O and PyInstaller admission, descriptor and selected-path identity, hashing and cleanup. Retain lipo joining and reader-owned PyInstaller admission, caller cancellation and private PYZ leases. Centralize admission failure projection and remove the superseded PyInstaller wrapper.
Preserve physical identity verification before late hash-completion cancellation. Close an owned source before returning failed initial metadata acquisition and retain the exact handle when close fails. A focused regression failed twice before that fix and now passes.
Validation: fresh runtime build, 259 focused fixture tests, nine documentation checks and all local static/discovery/duplication gates passed. An earlier check also ran four system-lipo suite cases (three read-only native inspections) before the final source fix; those were excluded from subsequent fixture qualification.
* fix(hopper): keep installed apt dependencies at their versions during Linux setup
apt-get install upgrades every listed package that is already installed, so Linux setup could upgrade the system python3 and X11 libraries while installing Hopper. Runtime dependencies are now installed with --no-upgrade in a separate transaction before the Hopper archive, which may still upgrade. The setup approval plan lists both commands.
* fix(hopper): narrow the apt upgrade claim and cover split transaction failures
* fix(evidence): preserve JSON ownership through metadata updates
---------
Co-authored-by: morluto <[email protected]>
* fix(setup): materialize the pinned package-runner cache before the first client start
Package runners key their package cache by the requested spec, so the
entry installed for the @latest invocation that ran setup cannot serve
the pinned rea-agents@<version> registration. The first client start
installed that entry inside the client's MCP startup deadline and timed
out (three consecutive 30 s timeouts observed in one client log; a
controlled cold install with every tarball already cached took 22 s).
Setup now materializes the pinned entry once after applying
registrations: best-effort, bounded by a timeout, launched from a
neutral working directory, and non-fatal on failure. The setup result
carries the prewarm summary, interactive output prints a warning when
the step fails, and direct launcher registrations keep skipping it.
Fixes#7027
* fix(setup): gate prewarm to applied registrations and supervise the runner tree
Address review findings on the prewarm step:
- Gate the prewarm on at least one applied client registration
(configured_<client>), so an empty plan or a skill-only run never
installs into the package-runner cache; the step now only follows
registrations the user approved in this transaction.
- Replace execFileOutput with a supervised spawn: on timeout the whole
runner tree stops through cleanupWindowsProcessTree on Windows and a
process-group kill on POSIX, because killing only the direct child
leaves npm holding the inherited output pipes past the deadline.
- Emit started and completed progress events around the prewarm so an
interactive cold install no longer looks like a frozen setup.
* fix(setup): forward terminal interrupts to the detached prewarm runner
A detached POSIX package-runner group no longer receives the terminal's
Ctrl-C, so setup could exit while the runner kept installing in the
background. The supervised launch now registers SIGINT and SIGTERM
forwarding for the duration of the prewarm: the runner tree is stopped
through the same family cleanup as the timeout, and the default
termination is restored afterwards. An interrupted launch reports a
failure reason instead of a hang.
Transfer the inventoried ZIP descriptor after inventory reader cleanup, then
borrow it for materialization instead of reopening the pathname. Release the
exact owner before publishing output; preserve failed owners for cleanup retry
and completed inventory facts through cancellation or later tagged failures.
This uses the existing admitted-descriptor reader API without a third carrier
hash pass. Remove the superseded materialization wrapper.
Cover identical-member carrier replacement, directory/missing replacements,
late cancellation at both handoff and final close, mixed cleanup outcomes and
retry. Root-owner test seams preserve real output-handle cleanup. Compiled
CLI/MCP ZIP, archive and report regressions also run; no benchmark is claimed.
Keep observed empty JSON strings separate from missing fields in IPS process,
image, frame, register and OS-version facts. Preserve empty unlisted keys,
retain malformed UUID source text, and keep unknown architecture interpretation
separate from raw spelling. Classification excludes empty supporting fields.
Validate producer cases plus compiled CLI and stdio MCP delivery against the
same output contract. This is saved-report inspection, without target execution.
Publish Godot res:// declarations and PyInstaller CArchive/PYZ keys through
shared occurrence source_path while preserving safe logical extraction paths.
Consolidate PyInstaller file, directory and namespace projection through one
owner, and account for the additional retained source spelling before allocation.
Cover producer inventory, Windows separator conversion, PYZ module/data keys,
CLI extraction bytes and retained MCP inventory views. Format fixtures are
synthetic; real Godot/PyInstaller producer lanes were not run.
Compose one source owner for Godot PCK and Node SEA admission, descriptor identity, carrier hash binding and retained handle cleanup. Preserve format-specific parsing and member streams, drain their reader operations before source close, and leave borrowed descriptor release with its caller.
Validation: runtime build and 136 focused tests passed, including 17 real-descriptor integration cases for delayed admission, cached hash mismatch, close failure and retry, borrowed handles, admission cleanup cancellation and mixed peer outcomes. Tests use local authored carriers and controlled failure seams.
Separate raw SEA names from safe logical extraction paths. Accept empty asset
keys, retain absolute main paths and conflicting source spellings, and assign
deterministic paths through the shared artifact path registry. Keep duplicate
raw keys malformed and include sourcePath in owned entry equality.
Validate 102 focused regressions including compiled CLI and stdio MCP inventory
and extraction, the canonical SDK schema case, 9 documentation consistency
cases, cached build, typecheck/lint and formatting. A pinned Node-generated
preparation blob is verified in a synthetic ELF carrier; real injected native
executables and the optional Docker lane were not run.
Parse the original unframed module bytes for every build-info result. Remove the duplicate text facade and string-source branch while retaining observed text, framed bytes, malformed lines, quoting and replacement semantics.
Validation: 163 Go parser, image, provider and public CLI/MCP tests passed using local binary fixtures; build, static and documentation checks passed.
Require the immutable Evidence read APIs already implemented by production owners. Remove the detached-only compatibility fallbacks and forwarding helpers, and bind consumers directly to their owner. Preserve editable detached exports and existing snapshot semantics.
Use the shared canonical CLI error classifier for process commands instead of reinterpreting their projection. Validation: 172 focused application and process tests, build, static checks and documentation checks passed using local fixtures.
Specialize the buffer-backed directory driver for its actual ELF consumers and remove the unused PE branch. Share PE subject architecture interpretation at the existing machine owner, preserving unknowns for conflicting formats and unproven managed overrides.
Validation: runtime build and 108 focused service, native parser, GPU, and CLI/MCP tests passed using local fixtures. Live descriptor-backed PE readers remain supported.
Construct the canonical ToolResultDelivery through the native continuation policy factory. Move type and lifecycle spy consumers to the core owner while preserving explicit response budgets and the deferred runtime composition.
Validation: runtime build and 335 focused tests passed; 41 Linux-only firmware cases were skipped on macOS. Tests use authored local fixtures, not real external providers.
Preserve the verbatim direct CLI selection in caller_input_path, separate from canonical subject identity and provider/query parameters. Fresh publication uses the existing immutable metadata import path so provider auto-recording cannot discard request provenance. Replay returns the current selection without rewriting stored Evidence or partitioning the cache by alias. Historical records without the optional field still authenticate.
Use the domain semantic projection for ledger agreement, remove the duplicate projection and subject-only replacement names, and preserve request metadata at same-invocation summary delivery. Later retained-view questions do not inherit that context. Existing one-shot parameters.input_path remains for compatibility.
Validation: 149 focused tests, 28 public CLI/MCP cases, all canonical advertised schemas through the pinned SDK, and nine documentation tests passed across the affected checks. Build, typecheck/lint, and formatting passed. Two Windows-only replay cases were skipped on macOS. Native macOS and managed fixtures were exercised; no Hopper/Ghidra/IDA runtime was started.
* fix(android): report DEX static values of static final fields
Fixes#6397
* fix(android): request DEX static values only for class inspection
* fix(android): range-check wide DEX static values
* fix(android): move the bridge to version 7 after main took 6
---------
Co-authored-by: Abelo9996 <[email protected]>
* feat(artifacts): inventory and extract Node.js single executable application blobs
Adds a node-sea artifact reader that locates the NODE_SEA_BLOB payload in
Mach-O sections, ELF notes and PE RCDATA resources, decodes the blob
header, main script, optional snapshot and code cache, and named assets,
and extracts their stored bytes through the existing safe output tree.
The executable is never run. Resource-tree errors in a PE without a
NODE_SEA_BLOB name stay with native classification; truncated, unknown
or over-limit SEA declarations fail closed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe
* fix(artifacts): reuse PE resource facts and bound SEA asset tables
Move the bounded NODE_SEA_BLOB name probe into the shared PE resource
layout module so SEA admission uses the same header and section mapping
as the full parser; the layout reader now accepts a header prefix with
the file size. Bound SEA asset count and decoded name bytes before
records are created and report resource bounds as limit failures. Offer
the JavaScript next probe only for main scripts with a source suffix.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe
* fix(artifacts): make SEA metadata construction cancellable
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(targets): keep original DOS padding out of e_lfanew
A complete DOS header with a nonempty relocation table that ended before
e_lfanew treated the padding dword at 0x3c as a Windows declaration. Without
a recognized Windows signature, fall back to the validated DOS structure so
original DOS images are not refused as malformed Windows targets. Preserve
unresolved-candidate handling when no relocation table delimits the padding.
* test(ghidra): cover DOS padding and unresolved header candidates
* fix(targets): read Windows signatures before dismissing DOS padding
* docs(targets): reconcile DOS padding admission contract
Document that relocation-table padding inside a complete DOS header is DOS data, while an unread Windows signature beyond the current probe is requested before that rule applies.
Aligns docs/installation.md with docs/ghidra-dos.md after the MZ admission fix.
---------
Co-authored-by: dhruvac29 <[email protected]>
Retain the exact selected CLI path in one-shot provider Evidence while preserving canonical subject identity and provider inputs. The shared request-provenance boundary replaces the artifact-only special case across native, managed, and bundle-resource routes. Remove the redundant parameter fallback and make the private managed helper receive explicit Evidence parameters.
Validation: real macOS codesign fixtures through the compiled CLI retained app aliases for signature, plist, and absent asset inspections, with direct-target controls. Archive selection, managed direct analysis, and snapshot replay regressions passed; Windows-specific replay cases were skipped on macOS. Build, static, format, and docs checks passed. Snapshot-backed direct request provenance still follows its existing authenticated query contract.
Refs: #5479
Hash the observed database version with object-store definitions at the CDP boundary. Missing, invalid, or imprecise versions preserve store evidence with incomplete coverage and an explanation. Mark the revised projection so historical store-only hashes cannot create false changes or prove unchanged versions. Preserve comparable metadata and independent storage changes.
Remove the redundant storage comparability wrapper and use each fingerprint's parsed scope instead of interpreting its identity string.
Validation: producer-to-comparison regressions and a compiled CLI to stdio MCP comparison passed, including version-only changes, invalid versions, legacy projections, and complete database additions/removals. Static and docs checks passed. CDP responses were simulated; no real browser runtime was exercised.
Explain unreadable ELF note section names without discarding complete note coverage. Keep export and PE import rows when only version or DLL-name metadata is unresolved, and stop claiming those retained rows were unreported.
Use native byte equality for the 4 MiB ELF extended-numbering source-preservation checks; generic deep equality enumerated every buffer byte and exhausted the test worker heap. The full fixture and byte coverage remain.
Validation: focused ELF note, native export, and PE import regressions plus complete CLI/MCP adapter files passed under the existing 512 MiB heap limit. Build, typecheck/lint, formatting, and documentation checks passed.
* fix(ghidra): admit recordless MIPS-I legacy targets
MIPS-I headers carry a zero-value ISA field (EF_MIPS_ARCH_1) and predate the ABI flags record. The recordless legacy admission only accepted EF_MIPS_ARCH_32 and EF_MIPS_ARCH_32R2, so every MIPS-I target was refused before provider selection even when its ELF32/ET_EXEC shape matched the existing inferred-o32 lane.
Admit MIPS-I through the same recordless legacy path, pin the endian-matched 32-bit MIPS language, and use a distinct support-lane identity so profile-bound snapshots invalidate. Declared-ABI MIPS-I headers remain refused because that lane is verified for MIPS32r2 only. The session reports that the generic 32-bit language also decodes later MIPS32 instructions and that MIPS-I subset conformance is not established from the header.
Fixes#6933.
* fix(ghidra): bind MIPS profiles to their target
Refuse a committed MIPS lane or ELF metadata that was resolved for a different target, including the same-endian MIPS-I and MIPS32 legacy lanes that share a language pin. Report the committed caller-selected MIPS-I language in the session limitation instead of claiming the provider pin.
---------
Co-authored-by: dhruvac29 <[email protected]>
* feat(environment): report what an environment serves with an environment-coverage view
A new inspect_analysis_view kind over capture Evidence. It projects the
state an environment derives with the given template rules and reports
each tool as answered from state, replayed with its view reason, a served
write, or a withheld write with its reason, the records behind it and a
hint about what to record next. The hints cover every write reason, so a
new reason without one fails to compile.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): build an environment bundle in one command
rea environment-build <capture> [directory] inspects a HAR (or reads
capture Evidence), projects the environment manifest from the in-memory
Evidence, authenticates the pair, prepares the state and writes the bundle
environment-export writes. Every bundle file is byte-identical to the
three-step path for the same capture and options.
The report adds the capture and manifest identities, the coverage summary,
warnings (a loopback capture named after its file, credential-named body
members by pointer) and the next command. --dry-run writes nothing. The
default template rules are the four the published environments use.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(environment): cover environment-build and the coverage view
Domain tests check the hint table against the state view's reasons, the
summary, and the view against the environment-state view it summarizes.
Service tests build the fixture capture and compare every file with the
three-step path, a dry run, loopback naming and the refusals. A CLI test
compares a HAR built in one step with the three CLI steps byte for byte,
and an MCP test projects the view from every source kind.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(environment): build the real-agentenv lane's bundle with environment-build
The lane now runs the command users run: it builds the Grafana-shaped HAR
with environment-build and the packed checkout, checks the report, then
runs the bundle with agent-env as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): document environment-build and the coverage view
The agent environments guide opens with the one-step build: the report,
the hint for each withheld write reason, and what a bundle ships. The CLI
guide, the capture guide and the skill reference name the command and the
environment-coverage view.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): harden environment-build after review
- The credential scan walks with its own stack, so a deeply nested body
cannot crash the build, and it also reports credential-named header
names, query names, form body members and string arrays.
- The build checks SIGINT between stages: cancelled before the bundle
stage it writes nothing, during the write it names the written bundle.
- A dry run opens the package tarball, so it refuses what a build refuses.
- Repeated template rules are refused, a HAR saved as .json gets a hint,
file-derived names follow the bundle name rules, `localhost.` counts as
a loopback origin, and the next command is shell-quoted.
- --capture-timeout-ms passes through to the HAR inspection, and a
missing directory is refused before inspection.
- The no-observation hint covers updates and deletes of entities the state
never held; the coverage view states that it does not apply the
serve-time admission checks; the inspect_analysis_view description
names the view.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): state the build's conditions and record its verification
The guides now state when the one-step bundle equals the three-step one,
which headers inspection excludes, what the credential warning scans, how
cancellation behaves, and that the coverage view does not apply the
serve-time admission checks. The spec folder records the census, the end
to end runs, the gates and the review.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): dispatch a pending interrupt before each build stage check
The manifest, state and coverage stages run synchronously, so a SIGINT
that arrived during them was only dispatched once the bundle write had
begun. Each check now yields first, so an interrupt before the bundle
stage cancels the build with nothing written.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): scan scenario credentials, own the dry-run tarball and serialize Evidence once
A browser scenario's retained header values and the query parameters of its recorded URLs are scanned for credential
names as a HAR's are, through a review projection of its records. A dry run admits and releases the package tarball
through the export's regular-file scope, so a close it cannot confirm keeps its owner, and checks for cancellation
after that last await. Evidence is written into one exactly sized buffer instead of chunks joined by a copy.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* style(environment): format the build spec
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): scan every form occurrence and MIME-less scenario bodies
The build's credential scan collapsed form bodies with Object.fromEntries, so a
repeated field kept only its last occurrence: `api_key=secret&api_key=` warned
about nothing although the Evidence ships the value, and repeated values were
undercounted. Form fields are now scanned as ordered name/value pairs, as
headers and query strings already are.
The review projection of a capture_browser_scenario dropped a request body
recorded without a media type, so the scan never saw it. Review keeps such a
body's text with an empty media type; the manifest projection still reads only
bodies it can classify.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): scan structured HAR form params and the request side of unattributed scenario transactions
A HAR request recorded with postData.params and no text is scanned by those parameters, each occurrence counting, and
the review projection keeps the request side of a scenario transaction whose response cannot be attributed, so the
credential warning covers what evidence.json ships in both cases.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): validate build paths and read dry-run tarballs
* fix(environment): report bodies too large for the credential scan
Text bodies over the scan's 4 MiB limit, including a response decoded only as a prefix, are reported as a
credential-scan-incomplete warning by record and side, never by content, so an absent credential-named-members
warning no longer reads as a complete check. The spec and agent-environments guide also state the path validation and
dry-run tarball read that e52ae1cd added.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(verification): release cold source transform services
* fix(javascript): retain capture context along covered mutation paths
* fix(environment): admit serialized exports and scan HAR form fallbacks
* fix(environment): admit in-memory build inputs against heap headroom and scan SaveHar null-text forms
The inspected HAR's Evidence and the projected manifest are checked against live heap headroom before they are
serialized, with the same check the file route uses, so a capture too large to parse and seal is refused as
resource_constraint before anything is written. A request whose postData text is SaveHar's null is scanned by its
structured params.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): measure the credential scan limit in UTF-8 bytes
A body counts against the 4 MiB limit by its UTF-8 length, not its UTF-16 code units, so multibyte text over the
limit is reported as unscanned instead of parsed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(javascript): erase inline type imports before binding values
* fix(environment): parse credential form bodies by their media type
* fix(environment): review untyped retained form fields
* fix(environment): bound request comparisons and preserve write proofs
* fix(environment): keep write proofs and bundle review complete
* fix(environment): finalize entity overrides with simulated views
* test(environment): restore 0895c89b's untyped form tests and hint
Integrating main took main's credential scan and coverage view, which
dropped 0895c89b's tests and its body-unusable hint. Main's scan passes
the tests, now reading the scenario by event. The hint names the
response too, since extraction and serving also withhold a write as
body-unusable when its recorded response cannot be read.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(environment): scan a scenario content event recorded twice
Main's event scan reads both content events of a transaction whose
content the capture recorded twice; the test fails on 0895c89b, whose
review projection dropped that transaction's response. The spec names
event pointers among the first 20.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): retain contradictions and trim retired state
* fix(environment): distinguish mixed array order from changed values
* fix(environment): keep dependent create state pinned
* fix(environment): keep checking retired view observations
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Forward the file owner's completed stable-read hash and byte count through descriptor-close failure or cancellation in PE resources, certificates and imports, ELF notes and kernel modules, native exports, Apple IPS, and APK signature inspection. Preserve the primary failure, exact cancellation reason, and failed-close cleanup lease.
Validation: 32 acquisition-finalization regressions across eight providers and 30 existing APK provider, lifecycle, and identity tests passed. Existing native filesystem regressions and typecheck/lint also passed. No external analysis engines or Android device were used.
Keep malformed canonical devicetree phandles unresolved instead of falling back to legacy identities. Report the PE dynamic relocation offset and section at their exact source widths with Size-gated absence and zero preservation. Clarify classic kernel-module version coverage and byte-preserving string encoding.
Validation: 158 focused parser, service, filesystem, and finalization regressions; 14 CLI/MCP tests; build, typecheck/lint, formatting, and docs checks passed. Fixtures establish parser behavior; no external analysis engines were exercised.
The shared source-loading suite covers the former generic command and pre-exit lineage regression, plus package ownership, error-class identity, native module behavior, and preservation of a caller-owned compiler context. Remove the overlapping older fixture after validating those controls.
Reuse the shared immutable-result reader for Interface Builder while checking its current parent binding on every read. Accept truthful optional schema outputs and validate normalized JSON before freezing or caching it, preserving explicit undefined facts without caching them.
Move accumulator construction and inline admission policy into the domain. One prepared projection retains the same budget across admission and projection while preserving standalone defaults, custom budgets, and authentication order.
Keep ASAR header details and the Godot header parser module-local. Use the canonical lease types in Hopper consumers, including release-cache publication, while preserving acquisition and cleanup behavior.
* feat(browser): record when scenario requests were observed and how long they took
Request events carry observed_at, the wall-clock time the provider observed
the request start, and finished or failed events carry the browser's
elapsed_ms when it reports one. Both are optional, so earlier captures still
parse, and scenario comparisons leave them out as a committed built-in rule.
The real-Chrome network lane checks every request and finished response is
timed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): time browser scenario records so their state learns a write clock
Scenario records take startedDateTime from when each request was observed and
time from the browser's duration, as HAR entries are timed. A timed scenario
of the book session learns the same clock and serves the same writes as its
HAR; an untimed one keeps record order and learns no clock, as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): document scenario request times and timed scenario records
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): start scenario records at the browser's own request start
The provider observes a request up to tens of milliseconds after the browser
starts it while it reads other responses, so a record starting then and
ending after the browser's duration mixed two clocks. Finished and failed
events now carry the browser's started_at, records prefer it and fall back to
observed_at, and comparisons drop it too. Tests cover the fallback for a
request that never ended or ended twice, a partly untimed capture keeping
record order, comparing an untimed capture with a timed one, and the
real-Chrome lane checks every finished request carries the browser's start
and duration.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): document the browser start time, its fallback lag and older readers
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the scenario timing verification
Gates, the real-Chrome lane, prompt-recorded and re-recorded published
scenario captures built with and without their times, the stale published
environments left unrepublished, and the review.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(browser): compare timed completion events in the request-time comparison test
The comparison test's captures had no finished or failed events, so dropping
started_at and elapsed_ms from them went untested. The captures now end one
request and fail the other, the test asserts both carry times, compares them
shifted by seven seconds and against an untimed capture, and a control shows
a changed failure on a timed completion is still reported.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(firmware): inspect flattened devicetree blobs
Adds inspect-devicetree and inspect_devicetree for FDT v16/v17 blobs
without target execution or external dependencies. Results include
validated headers, reservations, a node/property tree with bounded raw
previews, and derived standard values and selected phandle references.
Parsing enforces file, tree, and decoded representation budgets and
rejects malformed input without partial success. Summary delivery
supports retained Evidence pagination, with tests covering parsing,
service validation, CLI delivery, and MCP access after the input is
removed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe
* fix(firmware): reject conflicting devicetree phandles and bound page summaries
A node whose phandle and linux,phandle disagree no longer resolves under
either value; the conflict is reported as a limitation. Finalization
failures after a completed read retain the file-snapshot-hash partial
observation, matching the Go provider. Devicetree view pages carry
fixed-size context and counts instead of repeating summary strings, so a
large bootargs no longer pushes every page over the MCP response budget.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(elf): inspect Linux kernel module metadata
Adds inspect-kernel-module and inspect_kernel_module for ELF32/ELF64
ET_REL Linux kernel modules, read statically without executing them.
Reports ordered .modinfo entries with duplicates preserved, __versions
symbol CRCs, the presence of .gnu.linkonce.this_module and __ksymtab,
.init.text and .exit.text sizes, and an appended module signature
trailer with its PKCS#7 byte range and SHA-256. gzip, xz and zstd
containers are reported unsupported with external decompression as the
next probe. Malformed headers and exhausted entry budgets return no
partial success.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe
* fix(elf): bound kernel module ksymtab listing and tighten contracts
Charge listed __ksymtab* sections against max_entries, omit section names
longer than 256 bytes with ksymtab_name_exceeds_limit, and aggregate the
non-file-backed limitation so repeated huge names cannot inflate Evidence.
Use fixedTupleSchema for the signature pad bytes with projection parity
tests, and assert the late-cancellation reason and retained observation.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe
* fix: resolve merge integration errors
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
Keep each admitted HTTP handler owned through Evidence writes and error rendering, including after its client disconnects. Close joins graceful transport shutdown and handler work before publishing completion, and propagates exact late failures while preserving ordinary request diagnostics.
Share lifecycle ownership across OpenAI, Anthropic, and Gemini fixtures while retaining protocol-specific parsing and responses. Exercise real disconnects, graceful concurrent and repeated close, ordinary 400s, and opaque late failures.
Use JSON.parse directly in package verification and retain one parsed Cursor recovery snapshot. Route native command source loading through the shared transform-service owner so completed command cleanup leaves no transform child.
Preserve JSONC validation and caller inputs. Cover the actual public source-loading lifecycle with an authored command and verified process lineage.
* fix(importer): reach package-main selection for trailing-slash directory references
posix.normalize preserves a trailing slash in the specifier, so normalized was sub/ while dirname(match) was sub; the resolvedThroughIndex comparison failed and selectPackageMain never ran for require("./sub/"). Compare against the slash-stripped directory so trailing-slash directory references take the same package-main path as the plain spelling. Boundary rows pair each trailing-slash request with the existing manifest fixtures and are validated against Node's own resolver in the same table.
Fixes#4189
* fix(importer): derive trailing-slash directory identity with path semantics
The lookbehind strip kept a trailing slash when the final component ended in a line terminator (require("./pkg\n/")), so dirname(match) still never matched and the trailing-slash path skipped package-main selection for those directories. Join the normalized path with "." instead: path semantics collapse any trailing slash run regardless of the preceding character, and behave identically to the strip on every other spelling. Boundary fixture gains a newline directory with a package.json, an index and a declared main so the index path (not the no-match branch) exercises the comparison.
Addresses Codex P1 on #4219
* fix(importer): normalize package-main targets with path semantics
---------
Co-authored-by: Hemang Varshney <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(pe): inspect TLS callbacks and load configuration
Adds inspect-pe-load-config and inspect_pe_load_config for PE32 and
PE32+ images, with artifact hashes and file ranges in the returned
evidence. Reports callback addresses and mappings, plus size-covered
security-cookie, SafeSEH, CFG, EH-continuation and XFG fields;
flag-derived mitigation claims are explicitly labeled inferred. Missing
directories remain null, and malformed storage or exhausted read budgets
return errors without partial success. Adds documentation and tests for
service validation, CLI/MCP access, malformed inputs and stable-file
handling.
x86 images that declare the legacy 64-byte load-config directory are
read to the structure's own Size when those bytes are file-backed, as
the Windows loader does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe
* fix(pe): retain snapshot facts and bound load-config section lookup
Keep the completed stable-read path, SHA-256 and byte count as a
file-snapshot-hash partial observation when descriptor close fails or is
cancelled. Report the checked Size span as structure_location and add it
to Evidence locations beside the declared directory range. Resolve
callback sections through a sorted segment index built once per image,
keeping the ambiguity failure. Pin every load-config field to offsets
transcribed from the PE/COFF spec and llvm COFF.h at its exact Size
boundary for PE32 and PE32+.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(environment): report what an environment serves with an environment-coverage view
A new inspect_analysis_view kind over capture Evidence. It projects the
state an environment derives with the given template rules and reports
each tool as answered from state, replayed with its view reason, a served
write, or a withheld write with its reason, the records behind it and a
hint about what to record next. The hints cover every write reason, so a
new reason without one fails to compile.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): build an environment bundle in one command
rea environment-build <capture> [directory] inspects a HAR (or reads
capture Evidence), projects the environment manifest from the in-memory
Evidence, authenticates the pair, prepares the state and writes the bundle
environment-export writes. Every bundle file is byte-identical to the
three-step path for the same capture and options.
The report adds the capture and manifest identities, the coverage summary,
warnings (a loopback capture named after its file, credential-named body
members by pointer) and the next command. --dry-run writes nothing. The
default template rules are the four the published environments use.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(environment): cover environment-build and the coverage view
Domain tests check the hint table against the state view's reasons, the
summary, and the view against the environment-state view it summarizes.
Service tests build the fixture capture and compare every file with the
three-step path, a dry run, loopback naming and the refusals. A CLI test
compares a HAR built in one step with the three CLI steps byte for byte,
and an MCP test projects the view from every source kind.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(environment): build the real-agentenv lane's bundle with environment-build
The lane now runs the command users run: it builds the Grafana-shaped HAR
with environment-build and the packed checkout, checks the report, then
runs the bundle with agent-env as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): document environment-build and the coverage view
The agent environments guide opens with the one-step build: the report,
the hint for each withheld write reason, and what a bundle ships. The CLI
guide, the capture guide and the skill reference name the command and the
environment-coverage view.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): harden environment-build after review
- The credential scan walks with its own stack, so a deeply nested body
cannot crash the build, and it also reports credential-named header
names, query names, form body members and string arrays.
- The build checks SIGINT between stages: cancelled before the bundle
stage it writes nothing, during the write it names the written bundle.
- A dry run opens the package tarball, so it refuses what a build refuses.
- Repeated template rules are refused, a HAR saved as .json gets a hint,
file-derived names follow the bundle name rules, `localhost.` counts as
a loopback origin, and the next command is shell-quoted.
- --capture-timeout-ms passes through to the HAR inspection, and a
missing directory is refused before inspection.
- The no-observation hint covers updates and deletes of entities the state
never held; the coverage view states that it does not apply the
serve-time admission checks; the inspect_analysis_view description
names the view.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): state the build's conditions and record its verification
The guides now state when the one-step bundle equals the three-step one,
which headers inspection excludes, what the credential warning scans, how
cancellation behaves, and that the coverage view does not apply the
serve-time admission checks. The spec folder records the census, the end
to end runs, the gates and the review.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): dispatch a pending interrupt before each build stage check
The manifest, state and coverage stages run synchronously, so a SIGINT
that arrived during them was only dispatched once the bundle write had
begun. Each check now yields first, so an interrupt before the bundle
stage cancels the build with nothing written.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(browser): send declared requests from a scenario page with a request step
A capture_browser_scenario step `request` sends one declared HTTP request
through the page's own fetch, with its cookies and session, the way the
application's frontend would, and reads the whole response so the capture
records the transaction like any other. The path must resolve to the start
URL's origin, and the page must be on that origin when the step runs.
Query values and header values may be literals or declared secrets, and a
header may read its value from a cookie or a local storage entry, which is
then redacted from later records like a declared secret. `expected_status`
fails the step on any other status. Scenarios without a request step are
unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(browser): verify request steps in a real browser through to a built environment
verify:browser:request records a write session against a fixture item API
that refuses writes without its CSRF cookie header and stored bearer
token. It checks that every request lands in its own step, that the page
values authenticated each write and appear in no record, header or body,
that an unexpected status fails the step and cancels the rest, and that
environment-build serves the recorded create, update and delete.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): export environment bundles over MCP
export_environment_bundle writes the bundle environment-build writes, from
capture Evidence retained in the session or one capture Evidence file,
into an absolute output directory that must not exist and whose parent
must exist, as extract_firmware and export_web_scripts require; it never
creates the parent or reuses a directory. It returns the same report,
whose schema now lives in the domain so the CLI and the tool share it, and
dry_run reports without writing. A loopback capture without a file needs a
name. The build's refusal messages no longer name CLI flags.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): scan scenario credentials, own the dry-run tarball and serialize Evidence once
A browser scenario's retained header values and the query parameters of its recorded URLs are scanned for credential
names as a HAR's are, through a review projection of its records. A dry run admits and releases the package tarball
through the export's regular-file scope, so a close it cannot confirm keeps its owner, and checks for cancellation
after that last await. Evidence is written into one exactly sized buffer instead of chunks joined by a copy.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skill): build agent environments from a prompt with request steps and the bundle tool
Add the environments reference to the skill and install it with setup: record
against a disposable instance, sign in through the UI, write with request
steps and read each write back, check coverage, re-record by withheld reason
and export the bundle. Route it from SKILL.md, name agent environments in the
skill description, and document the request step, the export tool and
recording writes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* refactor(browser): split the request lane and header resolution into smaller functions
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* style(environment): format the build spec
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): say when an untimed capture learns no write clock
Coverage of a capture whose records are not all timed, which includes every
browser scenario capture, now states that the state learns no clock, so an
update stamped with a server-set modification time stays unexplained-change
however often it is recorded.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skill): give agents a whole scenario shape with step timeouts
Agents guessed the scenario schema field by field and two hung on steps
without timeout_ms. The reference now starts from a complete scenario with a
timeout on every step, and says a scenario capture cannot serve updates the
application stamps with a modification time.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(browser): keep request steps on their origin and their page values redacted
- Follow no redirects: a redirect fails the step and nothing reaches the
location it names (checked in real Chrome), and check the page's origin
again in the page as the request is sent.
- Send nothing once the step's time has run out or it was cancelled while
header values were read; the in-page deadline is what remains of the step's.
- Keep every page value redacted under a repeated or colliding label, keep a
value's first marker when it is read again, and leave values shorter than
8 characters unredacted rather than rewriting ids, ports and hosts.
- Refuse a header name given twice, User-Agent (Chrome drops it) and line
breaks in literal values and prefixes; fail before sending when a secret or
page value holds them.
- Append query entries after the path's literal query without re-encoding it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): document request step limits and word bundle refusals for both surfaces
Document redirects, timeouts, page value redaction, HttpOnly cookie sources
and header refusals for request steps. Refusals shared by environment-build
and export_environment_bundle no longer name CLI flags, and the retained
loopback refusal no longer prints an empty file name. The skill reference
asks for read-backs after each write, shows the coverage view's source, names
package_tarball for unreleased builds and the browser path a scenario ships.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): scan every form occurrence and MIME-less scenario bodies
The build's credential scan collapsed form bodies with Object.fromEntries, so a
repeated field kept only its last occurrence: `api_key=secret&api_key=` warned
about nothing although the Evidence ships the value, and repeated values were
undercounted. Form fields are now scanned as ordered name/value pairs, as
headers and query strings already are.
The review projection of a capture_browser_scenario dropped a request body
recorded without a media type, so the scan never saw it. Review keeps such a
body's text with an empty media type; the manifest projection still reads only
bodies it can classify.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(browser): close request step edge cases from the second review
- Append query entries correctly after a path ending in "?", "&" or "#".
- Refuse 3xx expected statuses, which an unfollowed redirect can never
report, and say a redirected write may already have been applied.
- Redact a page value also as fetch sends it, without surrounding spaces
and tabs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): document page value body costs, redirects and MCP dry runs
Document that reading a page value shrinks the bodies a capture keeps under
the default allocation limit, that redirected writes may have landed, that
the MCP dry_run still checks its output directory and which refusals name
the tool, and keep the .har hint in the HAR-as-JSON refusal for both
surfaces.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the prompt-to-environment verification
Gates, the real-browser lanes, five environments built from prompts by an
agent on disposable VMs and evaluated with two models, and the two reviews.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(skill): count rollback failures from the managed skill files
The environments reference made the skill eight files, so the recovery tests now derive the counts they expect from
MANAGED_SKILL_FILES instead of fixing them at six and seven.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(adb): restore the portable Java float pattern a merge rewrote
A lint autofix during a merge unescaped the hyphens in javaFloatValuePattern, which is invalid in a v-mode character
class, so the advertised start_adb_activity schema failed to compile in every JS regex mode. The file now matches main.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(browser): show the request step inside a whole scenario
Every JSON example in the browser scenario guide is a capture_browser_scenario input the catalog test parses, so the
request step example now sits in a launch scenario instead of standing alone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): scan structured HAR form params and the request side of unattributed scenario transactions
A HAR request recorded with postData.params and no text is scanned by those parameters, each occurrence counting, and
the review projection keeps the request side of a scenario transaction whose response cannot be attributed, so the
credential warning covers what evidence.json ships in both cases.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): validate build paths and read dry-run tarballs
* fix(environment): report bodies too large for the credential scan
Text bodies over the scan's 4 MiB limit, including a response decoded only as a prefix, are reported as a
credential-scan-incomplete warning by record and side, never by content, so an absent credential-named-members
warning no longer reads as a complete check. The spec and agent-environments guide also state the path validation and
dry-run tarball read that e52ae1cd added.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(verification): release cold source transform services
* fix(javascript): retain capture context along covered mutation paths
* fix(environment): guard request dispatch and report export limits
* fix(environment): admit serialized exports and scan HAR form fallbacks
* fix(environment): admit in-memory build inputs against heap headroom and scan SaveHar null-text forms
The inspected HAR's Evidence and the projected manifest are checked against live heap headroom before they are
serialized, with the same check the file route uses, so a capture too large to parse and seal is refused as
resource_constraint before anything is written. A request whose postData text is SaveHar's null is scanned by its
structured params.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): measure the credential scan limit in UTF-8 bytes
A body counts against the 4 MiB limit by its UTF-8 length, not its UTF-16 code units, so multibyte text over the
limit is reported as unscanned instead of parsed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(browser): send request steps only before their deadline and keep page reads on the start origin
The page receives the step's absolute deadline and a one-second start window, so a busy page that runs the request
late, or after the step is cancelled, sends nothing; local storage is read only on the start origin; the response is
drained without keeping its body; an unparseable path is a field issue; retained Evidence is refused for heap headroom
before it is serialized; and the skill says how to reach server-assigned ids.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* style: format integrated export admission
* fix(javascript): erase inline type imports before binding values
* fix(browser): run the request lane under the shared verifier command owner
The browser request lane runs each CLI call through createPackageVerifierCommands on a private runtime root, so a
timeout, output overflow or failure stops the CLI and the browser it launched before the root is removed, and a failed
stop is reported with the root retained. The verification record states the lane's current counts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): scan scenario content by event and read bodies by their media type
A browser scenario's credential scan reads every retained network-content event, including a response attached
mid-flight and a transaction no record represents, with event pointers, and each transaction URL's query once; the
review record projection it replaces is removed. A body is parsed as a form or as JSON by its recorded media type,
falling back to its shape, so valid forms such as `password=x&` are scanned and sentences are not read as forms.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(environment): keep read-only POSTs out of the write list
A POST that repeats the latest read of its path, tunnels only read
operations, names a lookup in its path's last meaningful word, or renews
a session no longer counts as a state write: it is replayed, does not cut
initial windows, and does not end observation searches. A tool stays a
write when a related read line changes across one of its calls.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): take an entity an unplanned write made when a read first shows it
A successful item read of an entity the gate's copy never held, after a
write no built plan models, puts the entity into the copy instead of
failing the read, so later writes to it are checked as usual. An entity a
modelled write touched is never taken back.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): plan a create into the type its returned id is read under
A POST whose path names no state type becomes a create into the type
whose item read first names an id its response returned, when the id is
new to the state; serving creates in that read's collection. A successful
POST that returned an id a type's reads only show after it also ends that
type's initial window, so entities a capture created stay out of the
initial state.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): end a type's initial reads at the write its reads disagree across
When a type's reads disagree across a write under another root, the
type's initial window now ends at the earliest write within that change
and the state is extracted again, instead of leaving every view of the
type replayed. The cut composes with the creation cut; the earlier wins.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): replay request lines that show entities in another form
A read line that shows the same entities in another form, by an extra
query parameter that adds or drops members, or conflicts with a base
line on a shared entity, is a variant: it leaves the view's checks and
the entity observations, the view names it with the query keys that pick
it out, and a call matching those keys replays its recording. A filter
role whose name matches no field needs two agreeing values.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): specify and record the state-target changes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep a read-only POST a write when its evidence is incomplete
Body-level container operations are skipped like query ones, a lookup is named only
by the path's last literal segment and never alongside a write word, a tool with a
redirected or unanswered call is not reclassified, and the mutation veto is resolved
over all candidates at once so another read-only call cannot explain a read change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer a field selection only when the state shows it exactly
Serving tests a field selection before any variant key: a projection value a replayed
read gave, or one no checked line gave other than `*`, replays. A list sets aside a
line asking for some fields only before the identity proof, and a dropped variant's
membership change no longer rewrites the type.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): hold adopted entities and linked creates to what the recording shows
An adopted entity is charged against the view's budget like a created one. A linked
create holds only for its exemplar's path arguments: serving refuses others and the
gate counts such a call as unmodelled. A response's identity-named id links at any
length, but a short one only to a type read before the call.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the review fixes' measurements
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): release a discarded planning pass before planning again
A rewrite cut clears the first pass, so the budget rewinds to a checkpoint taken after
the write cutoffs and created ids before the state is extracted again. Rewrite spans are
charged and appended without spreading them into an argument list.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): link no create whose id an earlier read showed, and read only whole batches
A linked create is rejected when an earlier list of its type or read of its collection
showed the returned id. Body ids are walked without spreading children into an argument
list. An RPC call reads only when every operations entry names an operation of its own.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): let the gate skip exactly the reads serving replays
Serving's replay decision moves to the domain as replayReason, and the write gate uses it:
a read serving replays can neither approve nor veto a write, and a read of every field
beside a replayed selection is checked. A selection read missing a required field is
still skipped, since part of an entity can neither confirm nor check it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the second review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): read writes, links and creation cuts only from evidence that rules out other causes
- A read-only POST repeats only a read that finished before it began; the veto compares
the latest read finished before a call with the first begun after it, and counts a
recorded absence on one side as a change. `modify` and `replace` are write words.
- A redirected or unanswered write lets a later item read adopt the entity it may have made.
- A creation cut counts a read still running when the POST began as after it, and a list
read shows its listed entities' own ids, not those they nest.
- A linked create's item read must come before any other tool's write.
- Line shapes are charged to the budget as they are built.
- A `fields=*` list line is whole, not a field selection.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the third review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): tie linked creates to their returned ids and order state by the lines left
- A linked create is observed only in a read of an id its response returned,
envelopes included, and serving takes its recorded path values in either spelling.
- A list read shows a suffix-named identity (`projectId`) distinct across its entities.
- A read-only POST compares the read that finished last before it began.
- An entity's first sighting comes from the lines left after variant lines are dropped.
- The pairwise variant pass stops at a work limit, leaving every line checked.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the fourth review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep foreign keys, stale reads and double decoding out of write evidence
- A list read counts a suffix-named field as its entities' ids only when no
id-named field identifies them, so an `ownerId` beside `id` cuts no window.
- A read-only POST is compared with the read that finished last among those no
other write follows, so a consuming call stays a write.
- A linked create's source path values compare as spelled on both sides, so no
spelling is decoded twice.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the fifth review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): compare linked sources by position, decode path fields once and bound read and variant scans
- A linked create's source values compare segment by segment, so a template
naming two parameters alike cannot accept a changed first one.
- Serving fills a linked create's path fields with its arguments decoded once,
as the gate learned them.
- The read-only check reads each related line's records once per call, between
the nearest other writes.
- Member signatures are JSON arrays, so a name holding the separator still
conflicts; the variant work limit counts line pairs as well as entities.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the sixth review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): replay late and variant-only items, fill linked path fields from the exemplar and charge membership spans
- An item line first recorded after the initial window that names a new shaping
parameter and shows other members is a replayed variant, like an initial one.
- An item only a replayed line showed, which the state never started with,
answers with its own recording; one the state held and lost stays missing.
- A linked create fills its path fields from its exemplar's path decoded once,
whichever accepted spelling the call sends.
- Each membership change span is charged against the budget before it is kept.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the seventh review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): blame a failed read on the writes whose fields it disagrees on
A failed item read with no interfering unmodelled write contradicts its creates and
deletes, and an update only when a field the update wrote or its response showed
differs. A create is contradicted when a later read embeds it elsewhere and the state
does not. A create member is fixed to its recorded value when a constant it would
have to change derives from it: its text inside a constant, a referenced entity's name,
or an instant beside a sent date, matched by related field names.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): explain unsent update changes and nested clocks by what the recording shows
An update's unsent change can be a modified-by stamp beside a created-by counterpart, a
value derived from the one sent member stored nowhere (accepted only while the target
holds the recorded before-value), or a sent clock the call left in its own second.
Objects whose clocks all moved with the request are nested clocks, opaque tokens of a
type's own length are generated, and an enveloped body is learned as its one object.
A write takes only its recorded query, an echoed reference to a nested object and a
state word paired with other fields stay at their recorded values, and a slug is tried
before a token.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): order collections by whole lines and sort only arrays a read-back proves sorted
A collection's initial order comes from the first list line that shows every entity its
type's sightings do and applies no order. A view renders a string-array field sorted
only after a counted read-back shows the written values in an order that neither the
written order nor existing-values-first explains, and never once a read showed it
unsorted. Sort and direction parameters share one vocabulary and are never learned as
inert, and a create placed by disagreeing sorts or a random id falls back to last.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment): serve bulk writes at a collection and target item actions
A DELETE, PUT or PATCH at a collection, or a POST at its delete path, whose body names
held entities by id is applied to each named entity, and serving refuses it unless every
id is held. A POST to an entity's action path that answers nothing or the entity is an
update of it; it is served only when a recorded call found each scalar it sets already
at that value. Served bulk bodies advertise their items, and compare probes pass them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): decide late item lines against the lines left, charge replay keys and bisect rewrite cuts
- Item lines first recorded after the initial reads are decided once variants
are dropped, against the lines left: a new shaping parameter showing other
members, or a selection of some fields leaving members out, replays.
- Each replay key is charged against the budget before it is kept.
- A rewrite span's earliest write is found by bisection over the writes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the eighth review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): set aside response members a path mints anew on every read
A top-level UUID or clock that differs on every repeat of one path's successful reads,
with no write between, is left out of that path's item, list and document comparisons
and named in the view's per_response_members. Serving keeps the recorded value.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): count only reads finished before a linked create began as showing its id held
A list still running when the call began may show what the call made, as the
creation cut already decides, so it no longer refuses the link.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): count a list total nested in a paging object
An item total in a top-level object beside the list, such as pageInfo.totalRows, is a
counter on the same evidence as a top-level one: it must equal the number listed, and
one that differs marks the line partial. Serving and the gate render it from the state
through its JSON pointer and leave the object's other members as recorded.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): take a created entity's nested clocks from its read-back
After a create's generated values pass, its stored nested clocks take the instants the
first later read of the entity shows, as an update's already do, so a create whose
response and read spell the clock differently is confirmed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): import the compare tool definition as a type
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): describe the new write rules and trim repeated parsing
The environment and capture docs describe sorted arrays, per-response members, nested
counters, item actions, bulk writes, the new write rules and refusals, and their known
limits. `_order` is a direction only. Sorted-array retries and the per-response scan
parse only the reads they need, and identifier words come from one helper.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): confirm no update whose clocks or fields the evidence cannot show it writing
An unsent clock an update left in the second it ran keeps the update unconfirmed until
another call shows the clock stamped. A field the state lacks that the read and the
update's response both show is the update's change, so without a rule for it the read
confirms nothing. A modified-by name stamp brings its id sibling along, and created
entities are indexed by id when a failed read is checked for embeds.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): show pinned query and body values in the served catalog and apply bulk writes atomically
A pinned query parameter is required with its recorded value as the only choice, and a
call that leaves it out takes that value. A fixed body member shows its recorded value,
and an enveloped body its wrapping, in the schema, the description and the refusal.
Bulk writes go through the same query pin and update or remove their entities at once.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): specify and record the write-rule changes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep served creates' items missing, check same-type earlier reads, and bound late lines and read-only stretches
- An item only a replayed line showed replays only while no served create has
added to its type, so a created and deleted item stays missing.
- A linked create's earlier item-read veto checks only reads of the candidate's
type or collection that show the item, so another type's id does not hide it.
- An item read answered with JSON null shows no id to a creation cut.
- Late lines' member names are charged before they are kept.
- The read-only check precomputes nearest writes and passes once over each
write-free stretch per path, skipping lines whose reads all agree.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the ninth review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): link creates past empty item reads, sweep repeated reads once and keep only returned ids for creation cuts
- A linked create passes over a later item read naming its id whose body shows
no item, such as JSON null, and links the next one.
- Read-only repeated reads are found in one sweep: each path's reads wait by
end time until a call's start passes them, and a write clears them.
- A creation cut keeps, and charges, only the shown ids some creation returned.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the tenth review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): take bulk ids literally, check each recorded call's query once, and keep learned sorts open to contrary reads
Bulk writes take body ids as written, refuse a call naming one entity twice, size updates from the replacements
installed, generate each entity's values from that entity, and delete only with the members their exemplar sent
besides the ids. Comparison translates bulk body ids with the target collection's mapping.
Update rules echo a creation clock sent back unchanged, require every held clock, and refuse a nested clock once any
recorded call kept its object. A recorded call sent with another query than its exemplar's, including item actions,
documents and deletes, neither confirms nor teaches its plan, and only successful calls with that query relax a
create's guards.
A counted read listing a learned array unsorted drops the sort from its view, sort evidence comes only from the
entities the read covers, and a top-level counter whose name starts with a slash is named by its escaped pointer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the review fixes' checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(environment): compare variant lines through shared entities and parse each compared body once
The variant pass indexes a tool's lines by the entities they show and compares only lines that share one, charging
the index to the budget while it is held; the pairwise work limit still decides when a tool keeps every line checked.
Read-only decisions keep each response body's canonical digest, taken once while its parse is charged, across calls
and veto passes. The creation cut no longer parses reads at or after the write that ends the initial reads, and reads
the ids a body shows inside its lease. Adoption looks up a read's target only after an unplanned write before it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the eleventh review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(environment): list a type's collections only when the path's own holds no id, and parse variant base lines once
The write gate's item target lists the type's held collections, and the removed targets', only when the path's own
collection holds none of its ids, so a served item view's reads no longer walk every entity of the type. The variant
pass parses the base lines' queries once for all the variants they key, and not at all when it found none.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the twelfth review round's checks and the sweep's cost
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the 50-capture sweep's wall, CPU and peak memory
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): remove duplicate schema after main integration
* fix(environment): honor recorded bulk identity kinds
* fix(environment): retain scoped evidence and honor write defaults
* test(environment): keep SDK regressions independently scoped
* docs(environment): record scoped write verification and measured costs
* fix(environment): serve bulk writes whose response lists each entity they wrote
A bulk call answering an array with one element per entity its body names, in the body's order, is classified by
its elements, so the existing renderer serves it from the entities each call writes. Responses listing entities out
of order, with differing members or id spellings, wrapped elements or an envelope still refuse.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): measure the 50 captures at the scoped-envelope head
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): classify bulk responses and release verifier transforms
* fix(environment): classify and verify complete bulk responses
* fix(javascript): retain capture context along covered mutation paths
* test(environment): verify bulk responses through the real MCP SDK
* test(environment): separate bulk response proof regressions
* fix(environment): preserve request absence and generated value relationships
* fix(environment): verify complete nested clocks on creates
* fix(javascript): erase inline type imports before binding values
* fix(environment): share complete generated value verification
* fix(verifiers): load the Windows source graph only when needed
* fix(environment): validate write responses before stored clock restamps
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Co-authored-by: morluto <[email protected]>
The staging refusal told callers to increase max_output_files or max_output_bytes within its schema maximum. At the schema ceiling that recovery does not exist, so the refusal was unactionable exactly where the least room remains.
firmwareStagingBudgetRemediation now branches on the selected value against the schema maximum: below the ceiling it names the room and the control, at the ceiling it states that a further raise is unsupported and points at a smaller firmware byte range. The schema maxima move into named constants so the advice and the accepted range cannot drift apart.
Verified with a module test for both budgets at the ceiling and one below it (red on the previous single-form remediation), plus the existing firmware suites.
Migrate the remaining native verifier commands to shared ownership with explicit stdin contracts, command-before-fixture cleanup, and failure receipts. Preserve observed catalog counts and unknowns, retained artifacts, native assertions, and selected launch inputs.
Remove the obsolete raw exec export and share output persistence across native verifiers. Exercise actual entrypoints with authored failed preflights and acknowledged descendants, plus filesystem failures and opaque error preservation.
Move command and retained resource lifetimes into a shared verifier owner, keeping package workspace release as a separate dependency. Migrate Pi and Hermes verification to that owner while preserving open pipes, explicit EOF, diagnostics, and retained labs.
Require positive writable finalization before accepting EOF delivery. Cover premature close, opaque failures, late ownership delivery, mixed cleanup outcomes, exact retry, and real descendant cleanup with authored process boundaries.
Remove duplicate MCP defaults, the fixed CDP deadline message, and the canned Windows authority reason after verifying their only consumers were tests.
Pin independent expected budget facts, exercise active default and selected CDP deadlines, and verify exact authored native diagnostic propagation. Preserve malformed-input, cancellation, omission, and authority assertions. Verified 222 focused cases, 9 README cases, build, static checks, docs, discovery, and duplication at 3.58%.
Record erasure during definition indexing and reuse it across runtime references, captures, effects, module links, routes, Electron and Vite. Preserve source type links and emitted decorator and computed-key effects; keep actual runtime reads and escapes conservative.
Validation: 1,647 focused tests and 9 documentation tests passed, with build, static and documentation gates. The inherited reference-lifetimes stress file exhausts a 512 MiB worker on both unmodified 18451e479 and this change; that file remains unverified. TypeScript 7.0.2 emit was inspected for ES2022 and ES2018 without executing emitted code.
* feat(domain): parse and validate GameCube/Wii DOL headers
Add a pure parser for the 0x100-byte DOL header (7 text and 11 data
slots, BSS, entry point). Used slots must lie after the header and
inside the file and the 0x80000000..0x81800000 load window; text slots
are 4-byte aligned; sections may not overlap in memory or in the file;
the entry point must be inside a text section. BSS may overlap data
sections, as .sdata/.sdata2 do in shipped DOLs.
Refs #1741
* feat(binary): admit caller-selected GameCube/Wii DOL targets
* feat(ghidra): import GameCube/Wii DOL targets through a section-map pre-script
* test(ghidra): cover DOL launcher arguments, attestation and raw profile digests
* test(ghidra): add the real verify:ghidra:dol lane and DOL docs
* fix(ghidra): read only the DOL header and section ranges instead of the whole file
* test(ghidra): derive DOL lane addresses from readelf and admit Gekko from the install catalog
* fix(ghidra): attest DOL block permissions, space, overlay and load state
* fix(ghidra): keep load-image permissions optional for older inspections
* fix(contracts): accept DOL targets in binary_session output
* fix(ghidra): adapt the DOL lane to the current upstream address schema
Validate DOL load-image coordinates with the upstream observed-address
schema, drop a duplicate import left by the rebase, and re-pin the raw
profile digests to the unmodified upstream resolver.
* fix(ghidra): preserve bridge inventory symbol provenance
* fix(ghidra): align DOL verification with shared workflow contracts
* feat(domain): parse and validate GameCube/Wii DOL headers
Add a pure parser for the 0x100-byte DOL header (7 text and 11 data
slots, BSS, entry point). Used slots must lie after the header and
inside the file and the 0x80000000..0x81800000 load window; text slots
are 4-byte aligned; sections may not overlap in memory or in the file;
the entry point must be inside a text section. BSS may overlap data
sections, as .sdata/.sdata2 do in shipped DOLs.
Refs #1741
* feat(binary): admit caller-selected GameCube/Wii DOL targets
* feat(ghidra): import GameCube/Wii DOL targets through a section-map pre-script
* test(ghidra): cover DOL launcher arguments, attestation and raw profile digests
* test(ghidra): add the real verify:ghidra:dol lane and DOL docs
* fix(ghidra): read only the DOL header and section ranges instead of the whole file
* test(ghidra): derive DOL lane addresses from readelf and admit Gekko from the install catalog
* fix(ghidra): attest DOL block permissions, space, overlay and load state
* fix(ghidra): keep load-image permissions optional for older inspections
* fix(contracts): accept DOL targets in binary_session output
* fix(ghidra): adapt the DOL lane to the current upstream address schema
Validate DOL load-image coordinates with the upstream observed-address
schema, drop a duplicate import left by the rebase, and re-pin the raw
profile digests to the unmodified upstream resolver.
* docs(ghidra): qualify the DOL memory window
* refactor(ghidra): own DOL BSS ranges in the parsed header
* docs(ghidra): record real DOL workflows and task costs
* docs(ghidra): record final DOL integration verification
* fix(mcp): share the native procedure search row contract
* docs(ghidra): record public procedure search verification
---------
Co-authored-by: gensokyo-dev <[email protected]>
Join identity-verified PTY family cleanup and native exit before workspace removal, retaining failed cleanup capabilities for retry. Replace duplicate kill-and-reject loops with shared process ownership and deadlines.
Capture configuration before setup using owned file handles and compare raw bytes. Preserve interactive cancellation, terminal restoration, redirected output, and selected inputs; remove the test-only forwarding export. Verified 122 focused cases, 9 README cases, build, static checks, docs, discovery, and duplication at 3.58%.
Add lambdas names-filter selector cases to checkSelectors:
- names: ['start'] returns the matching lambda with resolved fields
- names: ['missing'] returns an empty page
Add a chunks no-match case:
- names: ['Xyzz'] returns an empty page for an absent chunk tag
Also clarify in docs/erlang-beam.md what names selects for each
collection: the four-character IFF tag for chunks, the resolved
function name (never null) for lambdas, and the decoded atom name
string for atoms.
Co-authored-by: Saiyam Jain <[email protected]>
* fix(ghidra): enforce the bridge request ceiling before writing
Hopper and Cutter both measure an oversized payload and refuse it before the
request reaches its bridge, using the shared REA_BRIDGE_MAX_REQUEST_BYTES
ceiling. Ghidra had no ceiling on either end: GhidraWire.request wrote the
stringified request straight to the socket, and the Java bridge reads request
lines with an unbounded readLine() after its bounded 64 KiB handshake.
Serialize once, measure the encoded payload, and refuse before the operation
is registered as pending and before any socket write, so an oversized request
never reaches the bridge. Carry the typed constraint as the cause and
re-attribute it in projectPrimarySessionError, so callers see the same
resource constraint Hopper and Cutter already report.
The Ghidra bridge reads no ceiling of its own, so its recovery text names that
only the REA client applies the setting instead of asking for a bridge restart.
Validation: src/ghidra/GhidraClientWire.test.ts (3 cases, red then green),
src/config/bridgeMaxRequestBytes.test.ts (16), src/ghidra/GhidraRequestQueue.test.ts
(25); tsc --noEmit over the changed set exits 0; oxlint reports 0 errors;
oxfmt --check clean; verify:module-boundaries passes. The two failures in the
wider src/config+src/ghidra+src/hopper+src/cutter sweep
(captureDecoderHeap, GhidraAnalysisSeeds symlink EPERM) reproduce identically
on pristine bded9c628 with this change stashed. The Ghidra fixture suite
cannot run on this Windows host: fakeGhidra.mjs renames bridge-endpoint.json
and hits EBUSY on 3 of 3 runs, so the fixture-level test was not added.
* fix(ghidra): preflight request bytes before encoding
---------
Co-authored-by: gensokyo-dev <[email protected]>
Tag close outcomes with Result so rejected undefined remains a failure. Preserve all hook attempts, retained resource owners, aggregate causes, and cleanup retry.
* feat(il2cpp): read generic containers on demand and render method spec names
Generic container indices and records are demand readers on the same metadata snapshot, so links that do not ask for generic instances allocate nothing. Type rendering keeps one graph walk with its workspace, depth and cycle guards; the C#-like names and the Il2CppDumper GetMethodSpecName names are two styles over it.
* feat(il2cpp): link generic method instances with admitted rows and kept issues
generic_instances links every genericMethodTable row through methodSpecs, genericInsts and genericMethodPointers, choosing the one CodeRegistration layout whose counts bound every row. The table is reserved against the report budget before any row is resolved; rows and issues are admitted before retention, and a contradicting row stays unknown with an address-backed issue instead of failing the link. Views page the rows by definition, state, address and name, unrequested and historical collections stay unknown, and Ghidra seeds fold instances after the definitions.
* feat(il2cpp): accept --generic-instances on the CLI and describe it in tool contracts
* docs(il2cpp): describe generic method instances and their limits
* fix(il2cpp): keep issued generic rows unknown and name their row
A method spec that contradicted the metadata still reported its pointer slot as code, while the schema and provider limitation promised unknown. Such rows now keep no address, every issue carries its genericMethodTable row, the domain validator requires unknown exactly on issued rows, and shared specs name one other row instead of all of them. The row-size lower bound moves to the metadata admission owner, the hex helper is shared, and budget wording says checked rather than reserved.
* test(il2cpp): reject ambiguous generic layouts and pin budget, yield and limit paths
* fix(il2cpp): keep unmapped generic slots per row and reject undeclared arguments
Layout selection no longer requires every genericMethodPointers slot to be
mapped; each row reads its own slot, so one unmapped slot leaves only that
row unknown with an issue. Method specs whose class or method argument count
differs from the owner's declared generic container are issued and unnamed.
Fixtures now declare the containers their generic rows fill.
* fix(il2cpp): page generic method issues instead of retaining them in summaries
Summaries keep generic_methods without its issues and count them in
counts.generic_method_issues; the generic_method_issues collection pages
them by rows, indices and fields, as code_gen_modules already does. Docs
also name the method_indices filter the request schema accepts.
* fix(il2cpp): read split generic arguments per slot and method containers on demand
Generic argument arrays may span adjacent loader ranges: admit them with
hasPointerArrayCoverage and read each slot through readPointer, instead of
requiring the whole array in one mapping.
Method generic container indices are read on demand from the method record,
like type containers, so no link path allocates a per-method column. An owner
whose container index is out of range now leaves the method spec unnamed with
an issue instead of naming it unchecked.
* fix(il2cpp): report generic entries past the address space at their row
A damaged methodSpecs or genericMethodPointers pointer could make a spec or
slot address exceed 64 bits. The issue kept that address, which the address
schema rejects, so inspection failed instead of leaving the row unknown.
Check the entry range first and attach the issue to the genericMethodTable
row (or its slot field).
* chore(il2cpp): remove unrelated formatting changes
* fix: restore adb float pattern and mobile fixture from main
The merge commit's lint-staged run rewrote these two main files: oxlint --fix
dropped the escape in [+\-], which is invalid under the RegExp v flag.
* fix(il2cpp): preserve unreadable generic method identities
* docs(il2cpp): record generic-instance workflow verification
* docs(il2cpp): qualify final integration checks
---------
Co-authored-by: gensokyo-dev <[email protected]>
FilesystemTreeView mapped root-relative dyld paths to host paths with path.join. On Windows a component such as ..\outside\x.dylib was normalized out of the analyzed root, so a file outside it was opened, parsed and hashed. Components containing a backslash or colon are now absent on Windows, Windows link text is split on backslashes, and every host path is checked to stay inside the root before it is opened.
* fix(process): preserve the caller abort reason in regex worker cancellations
Both cancellation outcomes of matchRegexValues, the pre-admission refusal
and the abort observed while the worker runs, now carry the caller's
signal.reason as the error cause. Hopper's regex search returns that result
unchanged, so cancelled regex search_procedures and search_strings results
regain the exact reason the caller supplied.
* test(process): assert the caller abort reason for cancelled regex matches
Cover the pre-cancelled refusal and an abort raised while the worker is
running, asserting the cause is the identical reason object.
* feat(apple): inspect IPS crash reports
Add inspect_apple_ips for caller-selected Apple .ips files. Parse the
first-line metadata object and, when bug_type is the string 309, the
crash body: process identity, exception and termination facts, threads,
usedImages, lossless integer registers, and documented classification
rules. Other bug types including 288 stackshots return no crash result.
Opened Mach-O and dSYM UUID joins remain unobserved.
Related to #4297
* fix(apple): keep IPS register literals exact
Use the arm64e fixture integer that matches 0x8527000193ed4944 instead
of an IEEE-754-rounded decimal, and count the lossless JSON example
length from the source text.
* fix(apple): budget IPS parsing and keep typed presence
Parse the IPS metadata and crash body through a projection of the
documented members. Members the report does not interpret are
syntax-checked without being materialized, and thread, frame, image and
the new max_registers budgets run before each row is parsed, so a large
selected file fails with a typed resource error instead of first
expanding every row. Fixed allowances bound JSON nesting, unlisted key
names and termination reasons.
Present values whose JSON type differs from the documented one are no
longer read as absence: they are listed in coverage.type_mismatches and
coverage becomes partial, including wrong-type or missing threads and
usedImages collections. osVersion keeps the producer dictionary or the
string form. Frame roles follow physical frame depth, so the top frame
of a non-faulting thread is current_instruction and inlined rows share
the following frame's role. SIGKILL classes compare the canonical hex
once, so 0xdeadd0cc no longer matches dead10cc. Invalid UTF-8 is refused
instead of being replaced, and JSON strings are decoded by copying
unescaped spans.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g
* fix(apple): bound integer text expansion and reconcile coverage
* refactor(apple): retain image metadata at its owning row
* refactor(apple): validate row capacity through reconciled coverage
* test(apple): verify resource recovery at the public error boundary
* docs(apple): record complete IPS task and corpus measurements
* docs(apple): record integrated checks and stdio corpus evidence
* docs(apple): record PE integration and baseline cleanup controls
* fix(native): bound IPS string decoding and preserve invalid identities
* refactor(native): use exact optional IPS source evidence
* docs(native): verify IPS escape capacity and complete-task costs
* test(native): verify IPS through production stdio MCP
* style(tests): format IPS advertised schema bindings
* fix(native): preserve unresolved IPS references and recorded frame semantics
* docs(native): record IPS reference review evidence
* fix(apple-ips): enforce JSON and report consistency
* docs(apple-ips): record final consistency verification
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
* fix(environment): keep read-only POSTs out of the write list
A POST that repeats the latest read of its path, tunnels only read
operations, names a lookup in its path's last meaningful word, or renews
a session no longer counts as a state write: it is replayed, does not cut
initial windows, and does not end observation searches. A tool stays a
write when a related read line changes across one of its calls.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): take an entity an unplanned write made when a read first shows it
A successful item read of an entity the gate's copy never held, after a
write no built plan models, puts the entity into the copy instead of
failing the read, so later writes to it are checked as usual. An entity a
modelled write touched is never taken back.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): plan a create into the type its returned id is read under
A POST whose path names no state type becomes a create into the type
whose item read first names an id its response returned, when the id is
new to the state; serving creates in that read's collection. A successful
POST that returned an id a type's reads only show after it also ends that
type's initial window, so entities a capture created stay out of the
initial state.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): end a type's initial reads at the write its reads disagree across
When a type's reads disagree across a write under another root, the
type's initial window now ends at the earliest write within that change
and the state is extracted again, instead of leaving every view of the
type replayed. The cut composes with the creation cut; the earlier wins.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): replay request lines that show entities in another form
A read line that shows the same entities in another form, by an extra
query parameter that adds or drops members, or conflicts with a base
line on a shared entity, is a variant: it leaves the view's checks and
the entity observations, the view names it with the query keys that pick
it out, and a call matching those keys replays its recording. A filter
role whose name matches no field needs two agreeing values.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): specify and record the state-target changes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep a read-only POST a write when its evidence is incomplete
Body-level container operations are skipped like query ones, a lookup is named only
by the path's last literal segment and never alongside a write word, a tool with a
redirected or unanswered call is not reclassified, and the mutation veto is resolved
over all candidates at once so another read-only call cannot explain a read change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): answer a field selection only when the state shows it exactly
Serving tests a field selection before any variant key: a projection value a replayed
read gave, or one no checked line gave other than `*`, replays. A list sets aside a
line asking for some fields only before the identity proof, and a dropped variant's
membership change no longer rewrites the type.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): hold adopted entities and linked creates to what the recording shows
An adopted entity is charged against the view's budget like a created one. A linked
create holds only for its exemplar's path arguments: serving refuses others and the
gate counts such a call as unmodelled. A response's identity-named id links at any
length, but a short one only to a type read before the call.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the review fixes' measurements
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): release a discarded planning pass before planning again
A rewrite cut clears the first pass, so the budget rewinds to a checkpoint taken after
the write cutoffs and created ids before the state is extracted again. Rewrite spans are
charged and appended without spreading them into an argument list.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): link no create whose id an earlier read showed, and read only whole batches
A linked create is rejected when an earlier list of its type or read of its collection
showed the returned id. Body ids are walked without spreading children into an argument
list. An RPC call reads only when every operations entry names an operation of its own.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): let the gate skip exactly the reads serving replays
Serving's replay decision moves to the domain as replayReason, and the write gate uses it:
a read serving replays can neither approve nor veto a write, and a read of every field
beside a replayed selection is checked. A selection read missing a required field is
still skipped, since part of an entity can neither confirm nor check it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the second review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): read writes, links and creation cuts only from evidence that rules out other causes
- A read-only POST repeats only a read that finished before it began; the veto compares
the latest read finished before a call with the first begun after it, and counts a
recorded absence on one side as a change. `modify` and `replace` are write words.
- A redirected or unanswered write lets a later item read adopt the entity it may have made.
- A creation cut counts a read still running when the POST began as after it, and a list
read shows its listed entities' own ids, not those they nest.
- A linked create's item read must come before any other tool's write.
- Line shapes are charged to the budget as they are built.
- A `fields=*` list line is whole, not a field selection.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the third review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): tie linked creates to their returned ids and order state by the lines left
- A linked create is observed only in a read of an id its response returned,
envelopes included, and serving takes its recorded path values in either spelling.
- A list read shows a suffix-named identity (`projectId`) distinct across its entities.
- A read-only POST compares the read that finished last before it began.
- An entity's first sighting comes from the lines left after variant lines are dropped.
- The pairwise variant pass stops at a work limit, leaving every line checked.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the fourth review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep foreign keys, stale reads and double decoding out of write evidence
- A list read counts a suffix-named field as its entities' ids only when no
id-named field identifies them, so an `ownerId` beside `id` cuts no window.
- A read-only POST is compared with the read that finished last among those no
other write follows, so a consuming call stays a write.
- A linked create's source path values compare as spelled on both sides, so no
spelling is decoded twice.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the fifth review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): compare linked sources by position, decode path fields once and bound read and variant scans
- A linked create's source values compare segment by segment, so a template
naming two parameters alike cannot accept a changed first one.
- Serving fills a linked create's path fields with its arguments decoded once,
as the gate learned them.
- The read-only check reads each related line's records once per call, between
the nearest other writes.
- Member signatures are JSON arrays, so a name holding the separator still
conflicts; the variant work limit counts line pairs as well as entities.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the sixth review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): replay late and variant-only items, fill linked path fields from the exemplar and charge membership spans
- An item line first recorded after the initial window that names a new shaping
parameter and shows other members is a replayed variant, like an initial one.
- An item only a replayed line showed, which the state never started with,
answers with its own recording; one the state held and lost stays missing.
- A linked create fills its path fields from its exemplar's path decoded once,
whichever accepted spelling the call sends.
- Each membership change span is charged against the budget before it is kept.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the seventh review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): decide late item lines against the lines left, charge replay keys and bisect rewrite cuts
- Item lines first recorded after the initial reads are decided once variants
are dropped, against the lines left: a new shaping parameter showing other
members, or a selection of some fields leaving members out, replays.
- Each replay key is charged against the budget before it is kept.
- A rewrite span's earliest write is found by bisection over the writes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the eighth review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): count only reads finished before a linked create began as showing its id held
A list still running when the call began may show what the call made, as the
creation cut already decides, so it no longer refuses the link.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep served creates' items missing, check same-type earlier reads, and bound late lines and read-only stretches
- An item only a replayed line showed replays only while no served create has
added to its type, so a created and deleted item stays missing.
- A linked create's earlier item-read veto checks only reads of the candidate's
type or collection that show the item, so another type's id does not hide it.
- An item read answered with JSON null shows no id to a creation cut.
- Late lines' member names are charged before they are kept.
- The read-only check precomputes nearest writes and passes once over each
write-free stretch per path, skipping lines whose reads all agree.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the ninth review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): link creates past empty item reads, sweep repeated reads once and keep only returned ids for creation cuts
- A linked create passes over a later item read naming its id whose body shows
no item, such as JSON null, and links the next one.
- Read-only repeated reads are found in one sweep: each path's reads wait by
end time until a call's start passes them, and a write clears them.
- A creation cut keeps, and charges, only the shown ids some creation returned.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the tenth review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(environment): compare variant lines through shared entities and parse each compared body once
The variant pass indexes a tool's lines by the entities they show and compares only lines that share one, charging
the index to the budget while it is held; the pairwise work limit still decides when a tool keeps every line checked.
Read-only decisions keep each response body's canonical digest, taken once while its parse is charged, across calls
and veto passes. The creation cut no longer parses reads at or after the write that ends the initial reads, and reads
the ids a body shows inside its lease. Adoption looks up a read's target only after an unplanned write before it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the eleventh review round's checks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* perf(environment): list a type's collections only when the path's own holds no id, and parse variant base lines once
The write gate's item target lists the type's held collections, and the removed targets', only when the path's own
collection holds none of its ids, so a served item view's reads no longer walk every entity of the type. The variant
pass parses the base lines' queries once for all the variants they key, and not at all when it found none.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record the twelfth review round's checks and the sweep's cost
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
* perf(artifacts): read stable files into one bounded buffer
* perf(artifacts): fill stable snapshots through direct descriptor reads
* docs(perf): qualify stable snapshot memory with real ELF and BEAM tasks
* test(artifacts): count descriptor close retries from admission
* test(artifacts): capture the retained descriptor after asynchronous admission
* docs(perf): record integrated qualification and cleanup fixture correction
CDP joins repeated response header field lines with LF. Only the Link projection split them, so a repeated Content-Security-Policy lost the first directive of each later policy, and a repeated Referrer-Policy reported no policy. Header normalization now keeps each field line, CSP parses every line and comma-separated policy, single-valued fields combine lines with a comma, and Link uses the shared lines.
fix(functions): name the rejected side and supplied kind in Evidence errors
Function comparison previously reported only that analyze_function/rea.analysis Evidence was required, leaving callers to discover which side was wrong and what was supplied. Include the side, its operation and predicate, and the expected pair in the input issue.
Co-authored-by: dhruvac29 <[email protected]>
Own operation results and exact rejections in withVerifierCleanup, including
undefined, null and independent cleanup failures. Remove the optional-primary
cleanup contract and migrate every caller, preserving returns, annotations,
signal restoration and dependent cleanup order.
Make Frida/JEB reporters fail on reasonless rejections and retain JEB fixture
files while provider cleanup is incomplete. Own the nested packaged WASM
verifier and expose its composite process/root cleanup capability for retry.
Update runnable docs and isolated fixture dependencies.
Validation: 547 focused cases in 20 files, eight isolated boundary unit checks,
static/build/docs/discovery gates and duplication check (3.58%). Node process,
SDK and descriptor cleanup are exercised; provider and packaging results in
orchestration tests are simulated. Pack/install subprocess ownership remains
with the existing shared package runner outside the nested-command change.
* fix(setup): report host-owned cleanup resources alongside the primary's
SetupHost.close documents that it reports the resources a setup host still
owns, but its contract returned only a string. runSetup therefore built the
ProviderCleanupError from the primary failure's cleanupResources alone, so a
resource the host failed to release reached the projection only inside a
free-text reason while the error asks the caller to review the reported local
resources.
Return a SetupCloseReport instead. closeSetupResources absorbs the structured
resources of any cause that reports them, through
ArtifactReaderFailure.cleanupObservation, and leaves opaque failures
message-only so no resource name is invented. runSetup then unions the host's
resources with the primary's before building the typed cleanup error.
Two boundary tests read the close result directly: the skill-cleanup case now
asserts the retained backup path in the report reason rather than in a bare
string, and the shared ownership helper takes an opaque close result because it
only ever awaits undefined from either host it is given.
Validation: RED then green in src/application/Setup.recovery.test.ts, where
the new case previously received only the primary resource; 116 tests across
the seven Setup and cleanup-ownership suites pass; tsc --noEmit over the
changed set, its setup callers, and both boundary tests exits 0; oxlint
reports 0 errors; oxfmt --check is clean; verify:module-boundaries passes.
The two failures in src/setupEnvironment.test.ts reproduce identically on
pristine bded9c628 with this change stashed.
* fix(setup): preserve cleanup ownership through host reports
* fix(setup): bind finalization reports to cleanup owners
* fix(setup): retain live resources in returned cleanup views
* fix(setup): preserve completed cleanup and exact installer resources
* fix(setup): retain delegated cleanup across authority changes
* fix(setup): compose callback and source cleanup authorities
* fix(setup): retain producer cleanup failure reasons
* fix(cli): render structured setup cleanup resources
* fix(setup): separate failure evidence from cleanup ownership
* fix(setup): normalize close failures and unify lease binding
* fix(setup): preserve direct lease resource attribution
---------
Co-authored-by: gensokyo-dev <[email protected]>
Remove the reconstruction-only request schema and consume the canonical parsed
JavaScript application input. Production already owns parsing at the service
boundary; direct test seams now use that same schema instead of a weaker path
contract. Preserve format, filesystem, source selection and lifecycle validation.
Add a NUL-path regression for canonical admission and typed input-error
projection. Verified 63 focused cases plus static, build, docs, discovery and
duplication checks; no target execution or real-provider claims.
Route application and provider-environment harnesses through the shared MCP
connection owner. Join accepted handshakes during shutdown, retain each failed
close for retry, preserve primary and independent cleanup failures, and release
provider files only after their session closes.
Surface BinarySession close Result errors in production and artifact fixture
teardown. Add real SDK and descriptor-boundary regressions for startup races,
concurrent cleanup, mixed failures and recovery. Use a provider-free plist for
descriptor ownership so installed deep engines cannot change the test outcome.
* perf(entropy): separate physical reads from report blocks
* test(entropy): type the injected positional short-read boundary
* test(entropy): retain full catalog transport capacity in task probe
* docs(perf): record real entropy task costs and limits
* docs(perf): record latest-main entropy qualification
* test(native): expect artifact_changed when a bound metadata target is lost
41e10b240 made a bound metadata target that fails the source boundary
(`path`) or disappears (`ENOENT`) report AnalysisArtifactChangedError, as
its comment and docs/testing.md state, but left the module test expecting
AnalysisInputError. The test now asserts the shipped contract, so main's
Test shard and Portable tests lanes pass again.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(package): ship the CLI process environment owner in launcher fixtures
c2caae88f moved launcher environment preparation into
scripts/lib/cli-process-environment.mjs, made scripts/rea.mjs import it and
made verifyPackagePack require it in the packed listing, and updated the
dispatcher fixtures, but two other fixtures still omit it:
- packageTarListing's fake tar listing, so every case stops at "package
omitted the CLI process environment owner" before its own assertion;
- deferredRuntime's copied compiled package, so the copied launcher fails
to resolve its import instead of reporting the corrupted catalog.
Both fixtures now include the module, as the package ships it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(native): expect XML plist validation in typed-value provenance
9f5dd56e2 records a successful `plutil -lint` of an XML plist source in
provenance, as docs/native-investigation.md states, but the macOS typed-value
CLI test still expected only the `file` step for both formats. XML sources now
expect the lint step; binary sources are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(cli): accept JavaScript source selections from a JSON array or file
Add --source-paths-json to analyze and analyze-javascript-application.
It takes an inline JSON array or a JSON file path of exact inventoried
paths and appends them after any --source-path values, so a long
selection no longer has to fit on one command line (cmd.exe through npx
on Windows stops at 8,191 characters). Entries stay artifact-relative and
go through the existing source_paths validation; the native lane rejects
the option like --source-path.
Refs #4987
* fix(cli): cancel --source-paths-json file reads with the command
Pass the command's abort signal through parseCliJsonInput to
readJsonInputFile, so SIGINT/SIGTERM stop a selection-file read instead
of waiting for it to finish. A cancelled read now propagates its abort
rather than being reported as a file read failure.
* test(cli): preserve ASAR identity for JSON file selections
* docs(cli): record source-selection task costs and identity verification
---------
Co-authored-by: KJLavender <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Give the launcher and shared command worker one lightweight owner for package-version binding, Windows empty-home handling and Android browser-cache defaults. Preserve per-command preparation and environment restoration, package the owner explicitly, and retain its package and JavaScript recovery CI scopes.
Validation: dispatcher/version/shared-session, home fallback, evaluator/MCP/process cleanup and CI scope tests passed. Functional archive/install/repack, installed static CLI analysis and MCP checks passed; no real Codex/model trial or Windows/Termux runtime qualification was performed.
Keep the maintained manual evaluator, fixture rubrics and semantic tests together under tests/evaluation/model-evals instead of shipping them in dist. Load the same typed owners from the manual runner and update the architecture and client guide. Grading/report contracts and executable logic are unchanged.
* feat(pe): inspect import and delay-load tables
Add inspect_pe_imports / inspect-pe-imports to read PE32/PE32+ import
and delay-load directories without executing the image. The report keeps
DLL names, named and ordinal functions, ILT/IAT RVAs, and a pefile
import hash of the standard import table. A missing directory is
observed absence, not proof the image never imported APIs.
* fix(pe): default PE import fixture options
Allow peImportImage() without an argument so CLI and filesystem tests
can use the KERNEL32 sample without repeating an empty options object.
* test(pe): keep import path cases on the dedicated MCP contract
inspect_pe_imports already rejects relative MCP paths in its own
boundary test. Leave the shared absolute-path matrix to tools whose
handler issue projection matches that suite's expected shape.
* fix(native): index virtual-to-file mapping lookups
mappedFileOffset scans every mapping twice per span, so a parser that
translates one span per table entry costs sections x entries. Add
fileMappingLookup, which builds an O(n log n) interval index once and
answers each span in O(log n) with the same accept, reject and
translation results. A differential test checks it against
mappedFileOffset over randomized overlapping mappings and zero fills.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g
* fix(pe): report partial import evidence and exact pefile hashes
- Withhold imphash with an explicit reason whenever the standard import
table is partial (unreadable names or thunks, missing tables), or when
pefile 2024.8.26 would hash a different row sequence: invalid or
truncated names, ordinal 0, descriptors without functions, tables its
bounds or repeat/spread heuristics discard, and its 8192-thunk import
symbol cap. Delay-load issues do not withhold it.
- Hash ordinal imports with pefile's complete ordlookup tables and its
ord<decimal> fallback instead of declining unmapped ordinals.
- Report a nonterminal descriptor whose FirstThunk is zero, and a
delay-load descriptor whose IAT or INT RVA is zero, as malformed_entry
partial coverage instead of a complete empty inventory.
- Directory evidence lists the file spans actually consumed for the
descriptor array and terminator; discontiguous mappings produce
separate spans and separate Evidence ranges.
- Carry exhausted limits as PeImportLimitFailure.limit and derive
recovery from it; the fixed 16 MiB retained-text ceiling reports a
fixed allowance instead of max_file_bytes advice.
- Translate RVAs through the indexed mapping lookup and yield with a
cancellation check every 1024 table entries.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g
* fix(pe): read only own ordinal tables for DLL names
DLL names such as constructor and __proto__ pass pefile's name rule but
resolved inherited object members, throwing before the ord<n> fallback.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g
* fix(native): preserve PE import facts and prove hash compatibility
* test(native): verify PE import workflows and measured costs
* refactor(native): narrow PE facts across the container union
* docs(native): record integrated PE import validation
* fix(native): bound every PE import RVA span
* docs(native): measure RVA guard completed workflows
* fix(pe): reconcile import coverage with retained rows
* fix(pe): retain partial imports at descriptor mapping gaps
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
When Smart App Control or an App Control policy refuses
rea-windows-x64.node, the loader reported the failure as the artifact
stage. Doctor and provider diagnostics then told the user to restore or
rebuild the native files, although their version and SHA-256 had
already matched the package manifest.
The loader now tags a failure of the module load itself, after the
manifest and digest checks pass, as the load stage. The reason keeps the
Windows error text and says that restoring the file cannot help, that
Ghidra and JADX need the native controls, that rea setup can still
register clients, and that REA must run where policy allows the file or
the maintainers can be asked about a signed build. Readiness keeps the
documented runtime_missing code for unloadable artifacts.
Setup does not depend on the native controls: with the addon
unavailable, the published 6.7.0 CLI still planned and applied the
claude_code registration, so no setup gate is changed.
Fixes#5723
Use exact recorded start facts for state extraction independently of derived clock formatting limits. Preserve millisecond interval policy, missing-time fallback, redacted-write cutoffs, and bounded timestamp retention; remove the superseded clock comparator.
Two source and two compiled CLI regressions reproduced post-write values being selected as initial state. State/clock/date, CLI/MCP view, and static checks passed after the fix.
Move the synthetic evaluator and its unchanged semantic tests out of the shipped runtime. Retain actual CLI and MCP Evidence separately from the synthetic nine-stage baseline in a versioned verifier artifact, execute its reported conformance checks, and assign the verifier to its CI lane.
Validation: evaluator/state/classifier source suites, compiled CLI/MCP views, verify:readiness, static checks, build and docs passed. Readiness observed coverage is static analysis; fixture runtime behavior and reconstruction closure were not executed.
Resolve the filesystem-selected manifest parent once before deriving script paths and reuse the shared containment rule. Preserve selected manifest spelling and capture metadata while retaining source integrity, symlink refusal and cleanup. Cover differing and identical lexical siblings through both artifact consumers and public CLI/MCP parity.
Move body-retention coverage accounting to the manifest request-line owner. Preserve source retention facts by ordinal and remove the competing eligibility rule from scenario projection.
Remove the redundant spelling gate after the shared intrinsic owner proves native constructor and source-base identity. Retain local-constructor and replaced-export uncertainty through public application analysis.
Consume the signature producer status before inferring operand-resolution limits. Preserve exact byte-match provenance while malformed paired signatures remain unknown through current and historical Evidence, CLI and MCP.
Retire the unused graph serialization facade and use one typed query implementation after canonical request admission. Migrate tests to the owning schemas while preserving graph commitments, Unicode transport, provenance, uncertainty, traversal and reconstruction controls.
* feat(javascript): link assignment right-hand sides to their writes
A declaration carries its value forward, but a later assignment to a
variable, a property or through a compound operator stopped the forward
trace at the right-hand-side read. The semantic IR now records one
syntactic assignment flow per simple assignment target (ranges, operator,
owning callable), and the graph projection connects each retained read
of the right-hand side to the write occurrence through a defines
relation. Compound assignments also consume the previous value of the
target; the written value itself stays unknown, keeping the boundary of
#4342 intact.
Fixes#6193
* fix(javascript): preserve computed key assignment ownership
* test(javascript): retain nested callback assignment ownership
* docs(javascript): record assignment influence verification
* test(mcp): isolate mocks and acknowledge cancellation boundaries
* test(javascript): retain CLI diagnostics and current-main verification
---------
Co-authored-by: gensokyo-dev <[email protected]>
Use the authoritative scenario schema directly in all 25 test consumers. Preserve unknown-input annotations and every lifecycle, permission, normalization, identity and cleanup assertion; retain the separate branded capture validation boundary.
Retire test-only coverage and verifier constructors while preserving the live authenticated reader and evaluation path. Derive fixture inputs from the canonical contract schema and author commitments with an independent canonical JSON hash. Remove redundant success assertions while retaining typed failures and semantic controls.
Remove the unused estimator export. Check storage and escaping through typed authentication refusals with fixed allowances, preserving bounded traversal and acceptance controls.
Remove test-only completion and historical manifest parsers, use authoritative completion schemas, and keep graph hashing with the validated manifest producer. Verify commitments with independent canonical JSON and SHA-256 checks through domain and public CLI workflows.
Remove the isolated package, replay and trust-gate modules with their synthetic-only tests. No application, CLI/MCP, verifier or package export consumes them. Keep active provider completion, managed conformance, fixture verification, readiness and reconstruction coverage paths.
Retain the selected catalog path with captured assetutil failures and malformed metadata. Map two explicit selected-path invalid-catalog diagnostics to the existing format error while preserving other failure classifications. Verify the observed diagnostic through the real CLI and stdio MCP using a truncated temporary catalog and an unchanged valid control.
Reject normalization omission counts that exceed unknown-size rendered frames at the shared capture parser. Preserve historical captures without the optional stage counter and expose the same constraint through CLI and MCP diagnostics.
Use the active in-scope materializer directly from the existing inventory fixture, with the same explicit resource-scope admission and cleanup owner. Keep staged snapshot mutation and extraction lifecycle regressions intact.
Remove the unintegrated memory-map and peripheral-attribution modules with their dedicated tests. No application, CLI/MCP contract, verifier or package export consumes them; the shipped firmware analysis and publication workflows remain unchanged.
Remove the undocumented name alias from list_names fallback and validate the canonical value rows with the existing strict policy. Keep record defaults in the public input contract and require explicit decoder budgets. Preserve provenance, partial coverage, source coordinates and typed failures in active application and MCP tests.
Pass the caller-selected input field to the shared managed parse-failure
owner. Derive filename and field from one comparison selection, reporting
leftPath or rightPath while ordinary inspection continues to report path.
Preserve typed input classification and completed-left partial Evidence.
Replace weaker direct malformed-input cases with a compiled CLI workflow
covering both sides, the healthy comparison, and sibling inspection.
Document the input-attribution contract.
Validation: the public regression reproduced the generic path issue;
focused CLI, comparison and shared-reader controls passed after repair.
No managed assembly or external analysis provider was executed.
Use one containing-procedure parser beside its declared schema for
navigation, address facets, and feature trace references. Keep the trace
cache and reference type bound to parsed facts, returning typed malformed
output before SDK projection instead of passing unchecked JSON onward.
Retain same-address search alternatives such as Kuna's distinct string
encodings. References identify target addresses, so observe and emit each
target once while retaining duplicate rows from the provider observation.
Remove address-only match collapse and redundant residual deduplication.
Document these semantics.
Validation: regression-first runs reproduced four malformed-resolution
failures and three alternative/reference-replay failures. Focused MCP and
consumer controls passed after repair. Real native engines were not run.
Retain relevant program facts in the shared native directory and consume them for mappings and relocations. Remove redundant table coordinates and presence flags. This preserves extended program counts so PT_DYNAMIC beyond the raw sentinel remains visible, while load and relocation admission keep their distinct policies.
Parse module metadata at the image reader that validates its framing. Consume those facts in the provider while preserving raw bytes, text uncertainty, source locations, parser semantics and the public report contract.
Remove the test-only selection/projection facade and its unused synthesized binding scan. Keep lexical and identity controls at their active owners, with combined behavior covered through EnhancedTools.
Successful worker transport can carry an explicit failed parser status
with no semantic projection. Preserve that producer fact through the
shared source-availability interpretation used by full and incremental
graph construction, rather than dropping the source's semantic scope.
Retain the module when capacity permits, per-family incomplete-module
unknowns, and unknown omitted extents. Preserve existing operational
failure reasons and limits without classifying syntax as resource loss.
Document this coverage behavior.
Validation: regression-first run had 3 expected failures; all 22 selected
projection and public CLI source-selection/HTML cases passed after repair.
No JavaScript target execution or live external provider was involved.
Pair a preceding low surrogate only with its adjacent high surrogate.
Unpaired code units must not make an earlier letter become the token's
neighbor. Keep UTF-16 offsets and source strings unchanged at the shared
delimiter owner used by rule discovery, copy checks, and replacement.
Cover valid astral letters, digits, emoji, and lone-surrogate controls,
plus create/read behavior through a CLI-started environment MCP server.
Validation: regression-first run had 4 expected failures; after the fix,
437 selected domain, application, and server-boundary tests passed.
Use the existing builtin argument split for the invalid-log-level doctor exemption, removing independent flag scanners from the launcher and reusable CLI test worker. Empty output values and doctor-looking command data now preserve configuration refusal precedence; shell completion stays transport data.
Check fresh and shared frontends with production argument parsing, retain both doctor readiness paths, and verify non-doctor requests stop before the real launcher imports command handlers.
Validate engine output in the worker and transport output at reply admission. Have the typed function projector consume admitted producer facts in the provider, removing the fused parse/project facade and discarded worker projection.
Preserve duplicate order, missing inferences and raw metadata. Keep malformed producer checks at the schema and cover the provider handoff with source identity, diagnostics and cleanup assertions.
Read screenshot identity from the selected frame-tree root instead of a
filtered inventory that could substitute a child. Reuse the frame record
parser for initial and event identities, rejecting malformed roots before
capture while preserving known origin refusals.
Share explicit origin-scope diagnostics across CDP workflows without
changing policy or conflating startup uncertainty, document replacement,
worker protection, and Inspector restrictions. Document the recovery and
make screenshot navigation fixture ordering explicit.
Validation: 577 focused checks passed across 13 files; one platform-specific
invalid-byte-filename case skipped. Build, typecheck, lint, formatting,
generated documentation checks, README conformance, and discovery passed.
No full suite or live browser/provider execution.
Carry exact target keys and canonical processor facts from Clang bundle and ELF metadata producers into inventory agreement checks. Remove the string-only handoff and downstream reparsing while keeping public spelling, selection, uncertainty and admission behavior.
Cover unrecognized targets, conflicting notes and identity retained before malformed notes through the provider boundary. Preserve distinct unselected-payload behavior for standalone ELF and known bundle targets.
Use one lossless Mach-O name projection for symbols and dependency paths, retaining undecodable bytes and partial binding facts. Keep referenced NIB string failures unknown and preserve their source nodes through the shared lazy projection. Valid Unicode, readable sibling metadata and weak-bind scope remain usable.
Exercise the producer and CLI/MCP consumers, and align date-overlap and object-capacity fixtures with their owning archive contracts without relaxing admission or omission assertions.
Remove the test-only Objective-C property facade that discarded completeness. Verify its remaining semantics through portable Mach-O metadata decoding, including retained partial properties and located issues.
Decode the single IDA text result directly while preserving native JSON and structured-content precedence. Update the stale SDK deadline assertion to check the busy tool through its diagnostic owner.
* feat(ghidra): bound one procedure's decompile without closing the session
Fixes#5340
* fix(ghidra): report decompile limit as a request timeout
Keeps BinarySession from marking every Ghidra operation unavailable after one procedure's decompile reaches the limit, and names the MCP tool-call deadline in the remediation.
* fix(ghidra): budget the decompile limit and report cancellation-closed sessions
Adds REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS to the derived Codex/Kimi tool deadline and forwards it through setup. Marks a remote failure that settles after caller cancellation so the timeout projection no longer promises session reuse.
* feat(ghidra)!: default analyze_function to a retained summary
analyze_function was the one analysis tool never wired into REA's
detail: summary|complete convention, so it always returned the complete
native dossier inline (0.2-10 MB, dominated by value-flow and
references). Agents on tool-result-capped harnesses never reached the
pseudocode.
Add `detail` to analyze_function, defaulting to summary: return a
compact dossier (procedure identity, pseudocode size, facet counts,
value-flow availability) and retain the complete dossier Evidence in
the session. Page pseudocode, references, assembly and value-flow
through normalized_result.parent_evidence_id with inspect_analysis_view,
which already projects every native facet. detail: complete returns the
full dossier inline as before. Delivery detail never reaches the
provider or Evidence identity.
BREAKING CHANGE: analyze_function now defaults to the compact summary
view instead of the complete dossier inline. Pass detail: "complete"
for the previous behavior.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* feat(cli): expose analyze_function detail on the function command
Wire `rea function` into the shared CLI detail-delivery convention
(--detail, --evidence_output) so the CLI and MCP adapters offer the same
summary | complete choice for analyze_function. Summary delivery writes
the complete dossier Evidence to a bundle and returns the compact
summary view; it fails before analysis when --evidence_output is
missing, so no provider work is wasted. The CLI default stays complete
because a one-shot command has no in-process Evidence retention.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* refactor(ghidra): harden analyze_function summary after independent review
FlatlineRoundtable cross-model review (6 lanes) converged on the summary
projection's availability semantics being underspecified. Document that
facet_availability is authoritative over raw pseudocode length, that
code_units are UTF-16 (the unit inspect_analysis_view pages by), and that
value_flow.truncated is meaningful only when value-flow is available.
Parenthesize the nullish-coalescing/comparison for clarity. No behavior
change; declined claims (value-flow "truncated collapse", unresolved_calls
dead code) were schema-impossible or already correct.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* test(ghidra): cover analyze_function detail wiring on both adapters
Close the roundtable's top convergent finding: the summary path had only
domain-projection coverage, not the server/CLI wiring where the breaking
behavior lives.
MCP server (registerEnhancedTools): the default delivers a retained
summary whose complete parent is recorded for paging; delivery detail is
stripped before the provider request; detail: complete returns the full
dossier inline; a registration without retention rejects a summary
request before any provider work.
CLI (rea function): the default returns the complete dossier; --detail
summary without --evidence_output fails before analysis; with the bundle
sink it returns the compact summary and writes the complete Evidence
bundle.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(ghidra): keep analyze_function delivery detail out of provider inputs
Address the five automated review findings on #5660.
P1: `detail` lived in the shared enhancedInputSchemas.analyze_function, so
inspect_native_api composed `{procedure, detail: "summary"}` and the strict
Ghidra analyze_function schema rejected it. Delivery detail is now owned by
the analyze_function MCP tool contract; the handler separates it before any
provider call, and executeFunctionAnalysisEvidence parses the provider input
only. The MCP regression test validates provider input exactly as the
production Ghidra client does and exercises inspect_native_api.
P2: the analyze_function summary Evidence now carries the parent's
relationship_coverage like native facet views, so partial or unknown
caller/callee discovery is not reported as complete.
P2: the summary reports the dossier's declared facet_availability, nulls the
counts of facets the provider declared unavailable, and lists each reason in
unknowns, so Kuna's empty references, comments and basic blocks are not
mistaken for observed absence.
P2: inspect-analysis-view evidence-file sources accept the Evidence bundle
that --evidence_output writes, selecting a record with source.evidence_id
when the bundle holds several; the CLI test reads the saved bundle back
through that route.
Tests that assert the complete dossier over public analyze_function now pass
detail complete, and the oversized-dossier test asserts the summary view it
previously expected to be unsupported.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(ghidra): keep omitted function facets unknown in the summary
The analyze_function summary coerced an omitted unresolved_calls record to
a count of 0 while the native facet view reports the same dossier as "not
recorded", and it carried no counts at all for the pageable
swift_string_literals facets. Count optional records as null when the
dossier omits them, name them in unknowns, and add nullable counts for both
Swift literal facets. The summary Evidence envelope also dropped the
function address: createAnalysisViewEvidence now derives one function-view
predicate for both the address location and relationship coverage.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(scripts): request complete analyze_function dossiers in real-provider checks
The real-provider check scripts call analyze_function over MCP and then
consume the complete dossier: requireFunctionDossier, functionDossierSchema,
.procedure/.pseudocode reads, operation assertions, compare_functions inputs
and the oversized-dossier rejection path. The summary default broke them.
Every such call site now passes detail: "complete". Provider-direct callers
are unchanged because detail is an MCP contract field; the admission-error
sites stay unchanged because provider failures return before the summary
branch.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(ghidra): keep unavailable value-flow counts unknown in the summary
summarizeDossier coerced the five value_flow_* counts to zero when
native_value_flow was null (IDA, Kuna) or declared unavailable (Ghidra),
so the summary reported empty collections while value_flow.available said
otherwise. The counts are now null unless value flow is available, with the
reason already named in unknowns.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(evidence): budget authentication headroom by file shape and serialized size
The evidence-file headroom reserved two parsed copies, calibrated for one
record, and charged strings at two bytes per character. Bundle
authentication additionally holds the schema-parsed bundle, the re-parsed
records and two JSON.stringify results, which expand control characters and
lone surrogates sixfold: a bundle whose raw_result is a million NUL
characters estimated at 2 MB parsed but needed 12 MB of serialized text.
The budget now has a parsed term and a serialized term per file shape, owned
by EvidenceAuthenticationHeadroom and shared by the analysis-view reader and
the bundle importer, which had no guard. Two transient peaks the budget did
not name are removed at their producers: the canonical digest hashes a large
leaf directly instead of concatenating it into its buffer, and the JSON
validator walks containers by cursor so a wide array no longer queues one
entry per member.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(mcp): route analyze_function summary replies to dossier consumers in docs and prompts
The MCP guide, the version-comparison and crash-triage prompt contracts,
the authored skill and the Kuna, IDA and function-matching docs still told
agents to pass analyze_function replies straight to compare_functions or
build_call_path. Under the summary default those consumers reject the reply.
They now name the retained-evidence reference for compare_functions, the
observations fields of match_native_functions, and detail: "complete" for
build_call_path, which accepts only inline dossiers.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* test(evidence): pin validator member order and keep interrupted validation yielding
Independent review of the cursor validator found the interrupted-validation
fixture relied on the old last-first walk: its over-deep head was reached
before the first cooperative yield. The padding now precedes the deep
element so the walk yields first, which is the invariant the test guards.
A new case pins first-defect-in-member-order reporting, skipped
non-enumerable keys no longer count toward the yield cadence, and the
import-headroom test loads the reader through a fresh module graph so it
holds under the services project's shared workers.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(ghidra): align deadline bounds and cancellation outcomes
* refactor(ghidra): shorten deadline recovery guidance
* fix(mcp): clarify the scope of inferred decompile deadlines
* test(ghidra): verify deadline workflows and document measured costs
Separate failure and registration cases by their distinct contracts.
Record actual Linux Ghidra, CLI and MCP evidence with measured context,
elapsed time, CPU and memory, and explicit remaining coverage limits.
* docs(ghidra): pin integration and distinguish baseline format failures
* fix(ghidra): own failure lifecycle wording in the provider
* test(ghidra): budget timeout fixture generation before allocation
* fix(analysis): preserve summary provenance and bound bundle guidance
* fix(ghidra): align capability and traversal deadline guidance
* style(config): align deadline guidance formatting
* docs(ghidra): record capability and traversal verification
* docs(analysis): use absolute paths for Evidence files
* docs(analysis): record completed function summary measurements
* docs(analysis): update main integration validation
---------
Co-authored-by: Syed Rishad <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Keep the JSON parse attempt and exact numeric source literals together through optional XML classification. Replace the probing parse and raw-string parser with readPlistJson, preserving duplicate-key behavior, signed zero, ambiguous number types, prototype omissions and failure precedence.
Project fresh results from private facts and preserve native command controls, captures and provenance. Verify parser reuse and real provider-boundary conversion choices, plus compiled CLI and MCP behavior with real plutil.
Reuse filesystem-aware path selection for resource-only plist sessions so directory symlinks and trailing separators retain their meaning. Parse dyld bind names once as lossless text or exact bytes, preserving partial binding facts and independent valid metadata without invented class identities. Keep coverage issues separate from known opcode applicability and exercise both producer and generated Mach-O consumers.
Restrict Windows A/W suffix and stdcall candidates to their applicable native format. Preserve original metadata and per-observation Evidence ownership. Exercise identity contradictions and wrong-role controls through the shared CLI and stdio MCP workflows.
Fixes#6926
Decode base64 JSON body bytes at the shape owner without replacement characters. Keep incomplete request correspondence unknown and use explicitly complete authored inventories for positive comparison controls. Repair semantic truncation and storage fixtures to satisfy their owning contracts without weakening assertions.
Keep Mach-O UUID interpretation at reconciliation, environment values at the details owner, and inventory scope admission at the public operation. Remove the obsolete UUID scalar, duplicate DSH dispatch, two intermediate inventory exports, and unused body-cutoff predicate.
Keep test resource scopes explicit and cover oversized bodies through the real comparison workflow. Correct stale DSH invalid-result assertions and model DMG cancellation with the command owner, retaining an opaque AbortError control and mount cleanup checks.
Verify fresh provider budget resolution without coupling inspection to probe counts, while preserving caller-selected positive timeouts and file bytes. Compare effective peer registrations before and after updates instead of rebuilding legacy OpenCode entries in a different representation.
Project the supervisor's compact stop flag to an explicit boolean only when an owned snapshot is available. Require confirmed false before treating SIGKILL as external or attaching cgroup kill evidence, so missing diagnostics remain unknown.
Cover known false, true and missing facts while preserving operation-scoped OOME precedence and real owner cleanup behavior.
Bind multi-owner shutdown errors to their live typed cleanup dependencies so retained aggregates narrow and clear as each resource is released. Keep unknown or already-resolved rejections conservatively incomplete.
Cover both release orders, retained MCP Evidence, late cancellation and retry through the SDK session shutdown boundary.
Carry descriptor outcomes, cancellation, retention and physical origin as named facts so wrappers cannot drop ownership context. Replace raw primary failure unwrapping with provenance facts across artifact consumers, and use Result for Kuna span reads including undefined rejections.
Exercise actual descriptors, rollback, cancellation and cleanup retry; update brittle snapshot and completion fixtures to use their producing boundaries.
* feat(ghidra): bound one procedure's decompile without closing the session
Fixes#5340
* fix(ghidra): report decompile limit as a request timeout
Keeps BinarySession from marking every Ghidra operation unavailable after one procedure's decompile reaches the limit, and names the MCP tool-call deadline in the remediation.
* fix(ghidra): budget the decompile limit and report cancellation-closed sessions
Adds REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS to the derived Codex/Kimi tool deadline and forwards it through setup. Marks a remote failure that settles after caller cancellation so the timeout projection no longer promises session reuse.
* fix(ghidra): align deadline bounds and cancellation outcomes
* refactor(ghidra): shorten deadline recovery guidance
* fix(mcp): clarify the scope of inferred decompile deadlines
* test(ghidra): verify deadline workflows and document measured costs
Separate failure and registration cases by their distinct contracts.
Record actual Linux Ghidra, CLI and MCP evidence with measured context,
elapsed time, CPU and memory, and explicit remaining coverage limits.
* docs(ghidra): pin integration and distinguish baseline format failures
* fix(ghidra): own failure lifecycle wording in the provider
* test(ghidra): budget timeout fixture generation before allocation
* fix(ghidra): align capability and traversal deadline guidance
* style(config): align deadline guidance formatting
* docs(ghidra): record capability and traversal verification
---------
Co-authored-by: gensokyo-dev <[email protected]>
Recognize baseline-dependent #JTD metadata at the shared layout owner before table sizing and decoding. Report unsupported partial metadata across all inspection workflows, preserve admitted PE/CLI and metadata header facts, and keep member identities and inventories unknown without the baseline.
A generated PE carrier reproduced complete coverage and a wrong standalone MethodDef token before the fix. Producer and actual CLI/MCP checks now cover all three inspections, normal metadata, retained comparison after source deletion, exact caller selection, source digests, unchanged bytes, and known header facts. Released .NET writer/reader sources establish framing applicability; natural PE occurrence and real CLR/Edit-and-Continue execution were not verified.
Reuse the existing direct-call returned-reference owner for immediate synchronous function results and feed proven loader facts into historical imports. Simple returned require retains its identity; mixed, opaque, and canonical shim alternatives retain explicit uncertainty rather than disappearing.
Focused semantic, Electron, and public CLI regressions passed, including CommonJS, ESM, async/generator, mutable argument, direct-loader, and source-provenance controls. No runtime target or Proxy evaluator was introduced.
Fixes#3535
Reuse the nonempty NUL-free local path contract before normalization in artifact commands, auxiliary inputs, and output destinations. Empty selections now fail at parsing instead of selecting the working directory; explicit relative paths preserve their target.
Public CLI checks cover primary and auxiliary empty paths, exact relative-file digest, explicit current-directory selection, symlink parent semantics, and environment export inputs. Correct the desktop fixture to assert the existing CLI validation envelope.
Classify lipo parse failures once for native and artifact consumers, retain valid read-only ELF ranges independently of optional section names, and map IL2CPP reader architecture through the existing CPU identity owner before Mach-O parsing.
Regression-first checks reproduced malformed lipo output, lost unnamed-section compiler paths, and the amd64/x86_64 adapter mismatch. Focused parser, artifact, Rust CLI/MCP, Go, and IL2CPP controls passed; authored fixtures do not establish real-provider behavior.
Fixes#5982Fixes#3397
Resolve application and managed workflow contracts through the canonical name-based resolver, removing catalog-position overloads and the obsolete local facade.
Use the existing retained-summary policy for eight offline registrations. Pass only validated delivery detail to that owner while preserving exact typed service inputs and validation, admission, logging, and Evidence recording order.
Align export-shape MCP parity coverage with the existing absolute-path contract using an absolute dot alias. Preserve exact caller-path parameters, canonical artifact parity, distinct Evidence identity, and the export comparison assertions.
Resolve declaring TypeDef tokens within their own source and reuse the retained nominal identities for pairing, absence, and aggregate coverage. Remove display-name membership interpretation while preserving partial known pairs and whole-inventory matching.
A generated managed fixture reproduced false additions and removals for distinct nested owners sharing a display name before this change. All 305 focused producer and public CLI-to-stdio-MCP checks passed, including comparisons after source removal, missing identities, and genuine whole-inventory changes. Clarify selected-scope semantics in the docs; no live managed runtime was used.
Select registration without skill installation in the synthetic-client recovery matrix. Preserve all four specific remediation assertions and verify the plan, failure reason, and absence of unrelated installation work.
Route MCP snapshot reads through the session's existing file-resource owner so cleanup remains visible, blocks replacement, and can be retried by close_binary. Keep reads inside the serialized open callback and remove superseded adapter resource plumbing.
Validate typed errors, exact descriptor recovery, unchanged source bytes, replay provenance, and same-connection lifecycle ordering. Focused snapshot, target cleanup, and MCP lifecycle checks passed with simulated providers; no live provider was exercised.
Fixes#6216
Classify selected-input read failures at the shared artifact boundary and remove duplicate phase-based translations from TShark and sigrok. Keep other read stages raw for their existing owners and retain cleanup resources until retry succeeds.
Extend the descriptor outcome matrix with post-run private-copy integrity failures, late cancellation, retained command output, failed cleanup, and successful retry.
Project admitted output facts beside the command owner and remove adapter-local copies. Preserve byte-count omission, explicit signal and exit facts, and caller-specific truncation and failure policies.
Update ADB timeout regressions to assert the existing prerequisite frame and original process cause, including captured output on both frames.
Derive aggregate selected-scope agreement from retained nominal type identities instead of requested spellings. Keep incomplete or historical selected scope unknown while preserving caller selections, whole-inventory distinction, paging and member absence behavior.
Cover equivalent requests, same-label nested owners, missing and partial identity facts, and complete empty selections. Exercise CLI inspection Evidence through stdio MCP comparison after removing its selected source file.
Keep the selected running JVM visible when its version is unsupported, while retaining missing-runtime fallback controls. Assert the native snapshot binding and composed workflow Evidence profiles separately, preserving their exact commitments through a rejected snapshot switch.
Preserve source code-point prefix endpoints in the shared marshal UTF-8 decoder, including separately encoded surrogate values. Keep native TextDecoder spans, full validation, byte identity and the existing 80-unit budget. Update the PyInstaller consumer without changing its text contract.
Validation: 207 relevant source tests;24 affected compiled CLI/MCP/filesystem CI tests; independent CPython3.11.2 compiler/marshal oracle and real argparse;18 equivalent performance tasks; build/check passed. Codex reviewed7c0f7abc6b without major findings.
CI bypass authorized for demonstrated baseline-only failures:101 source and179 built cases reproduced on actual tested main ba9c5bc68. Cancelled shard remainder is unverified. See PR body and discussion for scope and evidence.
Fixes#6476
Parse declarations before selecting values, admit safe integers once, and retain bounded raw numeric issues. Preserve display and package selection policies while distinguishing absent and malformed values.
Keep completed display facts in typed physical-size failures, share the existing diagnostic sample limit, and verify real CLI/MCP contracts with a strict fake ADB.
Parse MIME parameters once with strict quoted-value and duplicate handling,
and compare equivalent default binary profiles. Keep excluded HAR MIME
unknown, explicit framing independent, and nominal gRPC sanitization intact
across malformed or repeated declarations.
Add public CLI/MCP and native-header regressions with source identity and
partial-coverage assertions. Preserve upstream wire and schema admission.
Fixes#6836
Stop interactions and sampling before final observation, and stop the owned
target before failure-path rendering. Cache finalized terminal facts through
disposal and cleanup retry while preserving the original failure.
Version deferred terminal observation order for every normalization profile,
including disabled PID and port normalization. Add public CLI/MCP descendant,
partial-evidence and compatibility controls without replacing upstream owners.
Fixes#6443
Share parser-owned package and runtime facts across dependency consumers.
Preserve uncertain loader requests and explicit wrapper bindings without
inventing unbound require authority in known modules. Cover selected source
grammar through the public MCP boundary and retain ambient/type dependencies.
Open each selected directory before loading its scoped policy, retaining
partial directory failures and cleanup ownership. Preserve explicit caller
negation precedence and source diagnostics across inventory projections.
Clarify how version comparison retains container provenance without claiming
semantic drift. Use independent unknown source inputs to keep candidate-limit
fixtures meaningful as conditional branch evaluation becomes more precise.
Fixes#6891Fixes#6882Fixes#6883Fixes#6859
Validate the selected workspace lookup through stat before canonicalization,
preserving typed configuration causes and physical traversal semantics. Cover
readiness without writes and tie startup cleanup regressions to explicit
cancellation or timeout producer outcomes.
Fixes#6828
Omit absent inventory facts without erasing explicit unknowns. Compare canonical file occurrences by source path and digest while preserving complete container provenance in observations and Evidence.
Consolidate candidate-capacity fixtures around unresolved alternatives and update inventory coverage and source-occurrence oracles. Preserve resource, identity, relationship uncertainty, cancellation, and transfer regressions.
* fix(pe): accept WIN_CERTIFICATE padding counted in dwLength
* perf(pe): reuse decoded PKCS#7 ASN.1 tree
* style: format files inherited from main for CI
---------
Co-authored-by: morluto <[email protected]>
* fix(ghidra): report kernel OOM kills as a memory constraint
A kernel or container OOM killer ends Ghidra with SIGKILL and prints no
java.lang.OutOfMemoryError, so the failure fell through to the generic
execution_failure that points at `rea doctor`.
On Linux with cgroup v2, read the process's own memory.events oom_kill
count before launch and again after a SIGKILL. When it rose, project
resource_constraint (memory) with the group's memory.max, and advise on
the whole process tree (native decompiler processes included) instead of
a larger Java heap. Otherwise name the signal as a possible OOM kill.
Fixes#5859
* fix(ghidra): scope OOM-kill attribution to what the cgroup proves
Review follow-up for the SIGKILL projection:
- The process supervisor records stop_requested before it sends any
signal, and Ghidra diagnostics carry it, so a SIGKILL from REA's own
stop is never reported as an outside kill. Only process and protocol
failures qualify; launcher (start) failures can include REA's rollback.
- Word the memory constraint as an OOM kill in REA's cgroup or a child
group; the count is per cgroup, not per process tree.
- Keep the open_binary reopen guidance for an established session.
- Resolve the cgroup directory through the cgroup2 mount in
/proc/self/mountinfo, so subtree mounts are joined from their root,
and read kernel files to EOF within a budget.
- Report memory_max as "max" for an unlimited group and null only when
the limit could not be read.
The showcase illustration was a single light PNG, so it stayed a white
slab on GitHub's dark themes. Redraw it as a light and a dark SVG, and
swap the linked image in README.md and all 21 translations for a
<picture> with a prefers-color-scheme: dark source. Alt text and link
targets are unchanged. The PNG is deleted because nothing else uses it.
website/figures.md notes the pair.
🤖 Generated with Claude Code (Sonnet 5.5) 🦊
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
This reverts commit 56eca648d. The equivalent completed two-client measurement increased elapsed and CPU time, so compiler sharing has not established a benefit.
Separate malformed locators and fixed records from unsupported disk layouts or numeric representation limits at the ZIP tail owner. Preserve the classic 65535-entry fallback and existing downstream parsed facts.
Add public CLI and stdio MCP valid, damaged, restored and unsupported controls; document the bounded validation scope.
Fixes#6471
Keep typed search denial and acquired snapshot facts through cleanup and finalization. Record unfollowed symlink coverage at the snapshot owner, preserve physically enumerated parents, and retain uncertainty beneath omitted links without losing proven sibling deletions.
Update parsed omission contracts, public CLI/MCP regressions and documentation without introducing a new lifecycle or coverage framework.
Fixes#6446Fixes#6444
Coalesce equal parsed file definitions and reject conflicting same-name definitions before registry import resolution. Preserve field presence, known and opaque facts through one budgeted SDK parse while retaining original descriptor bytes and digest.
Keep unique files in their original order, reuse the existing admission traversal, and cover public CLI/MCP import, ordering, presence and source-provenance controls. Document REA ambiguity policy separately from protoc precedence.
Validate recorded-core program-header projections against the selected pyelftools architecture table after binding e_machine to the snapshot. Preserve numeric unknowns and reject names borrowed from another machine or inconsistent header bytes.
Add source-binding malformed controls and authored auxiliary-header carriers to the existing real CLI/MCP verification lane. Document pinned parser semantics separately from payload interpretation and real crash coverage.
Fixes#6538
Parse optional CDP worker relationships once for browser and Electron collectors. Keep absent ownership unavailable, malformed relationships invalid, and proven foreign targets outside the selected scope; retain existing origin and file authorization.
Add public CLI protocol fixtures and documentation for coverage and withheld details.
Fixes#6475
Select active constituent-dimension unknowns for an overall comparison claim while preserving comparison identity and exact scope for individual claims. Use the same selected facts for classification, citations, limitations and recommended probes; remove redundant aliases.
Cover stdio MCP registration, aggregate and independent claims, resolved latest revisions, source provenance and foreign comparison scopes.
Fixes#6879
* docs: add 21 missing src/ dirs to architecture.mermaid (#6901)
* docs: restore KUNA and AUX node declarations lost in merge
* docs: remove final conflict marker
The favicon tile (#20252b) is almost invisible on a dark browser tab
strip, and the social card opened on its own is light only. Add one
prefers-color-scheme: dark block to each SVG. Each edit is a pure
insertion, and the light render is 0 pixels different in magick and in
CairoSVG. CairoSVG ignores the block, so the PNG that link previews
fetch stays light. website/figures.md says so.
🤖 Generated with Claude Code (Sonnet 5.5) 🦊
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Require a real clock-field context for numeric time normalization and actual rendered write rules for generated strings. Preserve literal read, replay and missing-response values, and retain generated aliases and distinctness within each response.
Keep clock context separate from entity identity while traversing arrays. Add domain and public CLI controls for measurements, business strings, confirmed writes, missing responses and generated-value relationships, with documented inference limits.
Fixes#6878Fixes#6837
Validate message-local field numbers and fully qualified registry identities during existing descriptor admission before constructing the schema registry. Preserve repeated wire occurrences, enum aliases and independent scopes, with bounded name allocation and retained capture provenance through CLI/MCP refusals.
Fixes#6834
Keep staged recording names as relative file operands, including a literal dash. Recognize exact selected input-option refusal captions even when sigrok exits successfully or a caller key contains a newline; retain producer diagnostics and typed request failures across CLI/MCP.
Fixes#6875Fixes#6839
Resolve pointer and scalar facts through their shared loader-aware owners before registration search or type rendering. Preserve unresolved names and attributes, read count fields at their declared width, and admit declared zero-fill registration tails without accepting unmapped bytes.
Cover relocation overlap and adjacency, loader-bound fields, ABI padding, nullable names, and CLI/MCP behavior; document format and verification limits.
Fixes#6881Fixes#6873Fixes#6871
* fix(environment): match served items whose route repeats a separator
callSegments split the tool path template itself and dropped empty literal
segments, while the held collection is split by segmentsOf, which keeps an
internal empty segment. A captured item route such as /api//books/{id} was
stored under collection /api//books but searched with ["api","books"], so
collectionMatch rejected it and a served item read reported the extracted
entity as missing.
Split the call template through segmentsOf, the same owner used for the held
collection path, so a repeated separator is preserved on both sides and the
item answers from its extracted collection. Single-separator routes stay
distinct and param positions are unchanged.
Fixes#6847
* fix(environment): keep repeated-separator collections writable
Preserving empty literal segments in callSegments made createdCollection's
missing-argument guard reject a create whose route repeats a separator, such
as POST /api//books: the empty literal segment tripped the
segments.some(text.length === 0) check and answerCreate refused the valid
route. Restrict the guard to empty supplied parameter segments, so a missing
path argument is still refused while a repeated-separator collection stays
writable as well as readable.
---------
Co-authored-by: gensokyo-dev <[email protected]>
* feat(decompile): write function pseudocode to a directory and return a manifest
Add decompile_all and `rea decompile-all` so a caller can write one
provider pseudocode file per function, or a filtered set, and receive
only a small manifest. The result stays bounded when the program is
larger than a local model's tool-result cap.
Resume continues an export for the same target digest, provider,
analysis profile, and layout. Matching files are skipped, conflicting
files stay unchanged, and one function's failure does not stop the
rest. Cancellation leaves files already written. Requesting dwarf
layout reports that compilation-unit paths are unavailable.
Simulated-provider tests cover the manifest, resume, filters, limits,
cancellation, and the failure cap. MCP registration, CLI coverage,
schema validity, the product catalog, check:fast, and docs:check
passed. Real Ghidra was not exercised.
Refs #6090
* fix(decompile): keep unknown export identity unresumable and recover torn journals
Resume treated two missing target or profile digests as a match, so an
export of one binary could continue into another. Resume now requires both
digests, and a decompiler observation without the export's profile, or with
a different target, is recorded as profile_changed.
A crash during a journal append left a torn final line that made the export
unresumable. Text after the last newline is now treated as uncommitted and
truncated before the next append, and stale temporary files are removed.
An inventory row with an empty address aborted the whole export because its
failure record failed the journal schema. Such rows are now excluded and
counted in the limitations.
A limited resume retried the same recorded failures first and never reached
new functions. Functions not yet attempted now run before retries. File path
reservations come from every journal line, so an output_io record no longer
releases a path. The index is rebuilt from the journal in numeric address
order, and journal close and cleanup failures return a Result.
The cancellation test now cancels after a file is committed and checks that
file survives and is skipped on resume. Redundant snapshot-replay checks are
removed, the CLI limit uses the contract constant, and --snapshot no longer
claims decompile-all updates it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(decompile): refuse resume of a missing export and repair journals by byte offset
A second review round found that resume against a directory that does not
exist silently started a full new export and reported it as resumed. Resume
now requires an existing export directory.
The torn-line repair measured the committed journal from decoded text, so
damaged UTF-8 could move the truncation point past the end of the file. The
journal is now read as bytes and cut at the last newline byte, and an intact
journal is not truncated, so a concurrent append is kept.
A per-function observation for a different target is now recorded as
target_changed instead of profile_changed. A failure to remove a partly
created output directory is reported instead of hidden. The journal handle is
closed on every path again, including an unexpected throw. The path allocator
no longer gives up after 1,000 collisions and reuses a taken name.
Tests now use createTestTempDirectory, assert journal lines against the
journal schema, and cover resume of a missing directory, resume with an
unknown analysis profile, and a target-mismatched observation.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(decompile): keep a function's first file name across failed retries
Reserving every journal path stopped other addresses from taking a path, but
an address looked up only its latest record. After its file went missing and
a retry failed without a path, the next resume moved the function to a
hash-suffixed name and orphaned the original. Each address now keeps the
first path the journal gave it.
A matched commit still ran the decompiler but did not count toward limit.
The limit now counts every function taken on after skipping verified files.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(decompile): never follow a journal symlink and check procedure_info identity
Torn-line repair truncates the journal, and the journal was opened through
any symlink in the export directory, so repair could truncate a file outside
it. The journal must now be a regular file and is opened without following
symlinks where the platform supports it.
Body ranges from procedure_info were written without checking that the
observation belonged to this export or described the requested function. An
observation with a different provider, profile or target, or another entry
point, now leaves the body unavailable with that reason.
File, journal and index writes now use FileHandle.writeFile, which writes the
whole buffer instead of trusting one write call. The stale temporary sweep is
removed: a matching file name does not prove this export created the file.
The inverted-range test now uses one address space so it reaches the
inversion check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(decompile): bound returned caveats and keep verified checksums
Ghidra names each terminal call's address in its per-function caveats,
so decompile_all copied one new caveat per function into the returned
Evidence and the answer grew with the program. The returned Evidence
now keeps the eight most often observed distinct per-function caveats,
grouped by provider and text as they are rendered, plus a count of the
rest. Inventory caveats are unchanged, and other workflows are not
bounded. Each function keeps the caveats of the observations it used:
a written file in its header, now including procedure_info caveats,
and a failed attempt in its journal and index record. A procedure_info
observation for another target or profile is foreign and contributes
none.
A failure record for a function that was already written replaced the
record holding its checksum, so after a transient read error a resume
decompiled that function again. The journal now keeps the last
successful write separately from the latest attempt. Resume verifies
against that write and records it again once the file verifies.
A caller abort that arrives while the export finishes now returns
cancellation with the caller's reason instead of a success manifest,
as #6143 does for other late finalization. A publish, index or close
failure stays the primary error.
Also make the missing-journal resume error say what to do, and add
boundary tests: one runs decompile-all through the real CLI against a
fake Kuna to check flag mapping, the relative output directory,
filters and limit; one runs decompile_all through production MCP
registration to check that the caveat bound is applied.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(decompile): keep each function's complete caveats in the index
The returned Evidence keeps eight distinct per-function caveats and
counts the rest, pointing readers at the export. A written function's
file header truncates its caveats after 4,000 characters, and its
journal record kept none, so a large function with many terminal calls
could lose caveats from both the answer and the export.
Every journal record, written or failed, now keeps the complete caveats
of the observations its attempt used, and decompile-index.json carries
them. The header stays a readable summary and, when truncated, says
that the index keeps the complete list.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(decompile): report external functions instead of failing them
Ghidra classifies imports as external functions with an empty body and
returns null pseudocode for them, as its limitations state. decompile_all
recorded each one as an invalid_pseudocode failure ("not text"), so a
dynamically linked program reported every import as a decompiler failure:
34 of 116 functions in /usr/bin/xxd.
procedure_info owns the external classification. When it classifies a
function as external, the export now records status "external", counts it
in counts.external, writes no file and requests no pseudocode. The record
keeps the body fact as observed. The journal header pins the target,
provider and profile, so a resume skips a recorded external function
without asking the provider again; a limit below the external count still
leaves nothing remaining. A null pseudocode result for a function whose
classification is not external is reported as no_pseudocode.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(decompile): report finalization failures after a cancelled export
When the caller's abort stopped the publish loop, decompile_all returned
that cancellation before checking the index write and the journal close,
which had already run. A failure to finalize the export was hidden, so
the caller could not tell the saved index or journal was incomplete.
Publish failures stay primary, then index and close failures. A
cancellation counts as the caller's only when the caller's signal is
aborted, and is then reported after a successful finalization with the
caller's own reason. A provider's own cancellation, without a caller
abort, stays a publish failure like any other. Tests cover an index
failure and a close failure after the caller stops the loop, a stopped
loop with a successful finalization keeping the caller's reason, and a
provider cancellation staying primary over an index failure.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(decompile): remove a created output directory that cannot be secured
createPrivateDirectory ran mkdir and chmod as one step. When mkdir
succeeded and chmod failed, it returned an error before #create reached
its cleanup, leaving an empty directory behind. A fresh export then
refused the existing directory, and a resume refused it for having no
journal, so the caller had to remove it by hand.
mkdir and chmod are now separate. A mkdir failure created nothing, so
nothing is removed. A chmod failure after this call created the
directory removes it with a non-recursive rmdir: it is still empty, so a
path another process filled or replaced meanwhile is refused rather than
deleted, and that refusal is reported.
A filesystem boundary test fails chmod for the new root and checks the
directory is gone and a retry succeeds. A module test confirms that a
function written earlier and now classified as external still reports a
changed file at its path as output_exists.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* refactor(process): share the socket lease with owners other than Hopper
The socket lease that keeps two REA processes from opening the same Hopper
document lived only in HopperLease, with Hopper error messages built in.
A decompile export needs the same guarantee for its output directory.
OwnedLease now holds the lease mechanism unchanged: a listening local
socket that the operating system ends with its process, owner identity
answered by the live holder, and recovery of a stale socket serialized
through a recovery directory. Callers map a tagged LeaseFailure to their
own error. HopperLease keeps its exports and maps each failure to the
same HopperStartError message as before. On Windows the lease is a named
pipe, which needs no stale recovery.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* fix(decompile): hold the output directory per run and stop on repeated write failures
Two runs could resume the same output directory at once. Each read the
journal once, and the index each wrote reflected only its own view, so
the last writer could drop the other run's records. A run's torn-line
repair could also truncate the other run's append.
DecompileExport now holds a lease on the output directory, named by its
device and inode, from before the journal is read (or, for a new export,
from just after the directory is created and before its journal exists)
until the journal is closed. A second run is refused as a retryable
capacity constraint that reports the holder's process ID. The lease ends
with its process, so a crashed run leaves no lock to clear. The lease is
released on every open failure, and a failed release is reported.
An export also kept decompiling every remaining function when its files
had stopped being writable mid-run, for example on a full disk, failing
each one with output_io. After five consecutive functions whose files
could not be written or read, the export now stops with an output error
that carries the counts and the last failures. A verified file or any
other outcome ends a run of failures. Committed files and records stay,
the index is still written, and a later resume continues.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb
* docs(native): record directory export verification
Record matched real Ghidra outputs, bounded caveat and cleanup regressions,
actual catalog-inclusive context estimates, runtime measurements and coverage
limits for the pinned integration.
* fix(native): bound export summaries and reserve portable paths
Use one case-folded reservation owner for allocation and resumed journals.
Budget inline failures and provider caveats by encoded JSON bytes while
retaining complete records in the output index. Align the tool contract
with the collector order and verify SDK result-window behavior.
* docs(native): refresh final export measurements
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Describe the diagram as selected layer owners and dependency paths. Link the canonical composition factories and direct Frida/Cutter entry wiring, and add the omitted Kuna deep-provider node and its verified dependencies.
Documentation checks cover source links and generated facts; no exhaustive directory inventory or new provider capability is claimed.
Fixes#6901
Count each verified required obligation once across retained closure observations and validate their finite status/count rules against the supplied final ledger. Compare every retained semantic outcome variant across CLI/MCP instead of selecting the first pair, preserving all observations and Evidence.
Validation: evaluator regressions reject duplicate closure, contradictory intermediate status, and later outcome drift while retaining valid controls. The readiness verifier passes with the source-owned C fixture and actual CLI/stdio-MCP static JavaScript/Electron workflows. Variant sets do not establish equal repeat counts or run pairing; final-ledger consistency does not authenticate historical ledgers or proof freshness.
Fixes#6893Fixes#6894
Retain module-name displays observed before optional metadata failure so strict objdump framing can use those facts. Preserve uncertainty across name sections and report unknown completeness when unattributed producer warnings concern skipped custom metadata; keep exact warnings, core validation, WAT and artifact provenance.
Validation: focused source/process regressions and 61 real WABT1.0.42 CLI/MCP verifier cases pass. The unmodified official macOS arm64 archive matches its published SHA-256. Coverage includes outer/inner malformed name metadata, valid empty-map controls, stable core/interface facts and cleanup. No inspected WASM target was executed.
Fixes#6885
Resolve Go pointer descriptors and strings across complete, consistent file-backed spans, including PE headers and adjacent ELF load ranges. Reuse the canonical image-mapping selection for native exports. Preserve existing gaps, alias, zero-fill and capacity refusals.
Forward parsed zero-fill ownership through IL2CPP relocation tables, destinations and mapped-span checks. Distinguish missing spans from ambiguous ownership in the shared mapping failure: unresolved relocation targets may remain unknown, while ambiguous raw registration bytes cannot be reused as a resolved count.
Validation: parser, provider, service, native-export, IL2CPP and public CLI/MCP regressions cover PE32/PE32+ headers, split ELF descriptors/contents, relocation count conflicts, ordinary unresolved targets and unchanged inputs. Authored carriers establish static parsing, not Windows loading or actual legacy Go linker emission.
Fixes#6831Fixes#6830
Move the unchanged resolution contract into its domain owner and use it when
building navigation context. Preserve provider failures instead of guessing
absence from error text or malformed result shapes. Return null only for an
explicit valid negative resolution.
Remove the obsolete error-message matcher and partial name parser. Cover four
malformed result shapes, original error identity, valid positive/negative facts
and the typed MCP unreadable-output response. Original source failed six cases.
Validation: navigation source/MCP and advertised schema checks passed. Compact
and full generated catalogs are byte-identical to the parent. Static, docs,
discovery and duplication checks passed.
Reuse sameRegularFileState for artifact, native plist and entropy readback.
Remove private copies of identity/size/timestamp checks and consistently include
mode in the existing stable-state contract.
Keep initial identity-only artifact admission, byte-count checks, capacity and
error precedence, symlink choices, cancellation and descriptor cleanup intact.
Validation: stable-file, plist, artifact outcome, entropy and public CLI/MCP
lifecycle regressions passed; static and documentation checks passed.
Use the existing bounded thin-header decoder for loaded and embedded FAT
identities, thin-image resolution, and both prepared-copy readback paths.
Preserve raw unsigned CPU/subtype facts and endian handling.
Keep the initial eight-byte dispatch, FAT framing, selection and ranking,
source digests, error ordering, cancellation and cleanup authority local.
Validation: Mach-O container, Hopper image/copy lifecycle and file-offset close
boundary suites passed. No attached Hopper GUI or real-provider run performed.
Decode metadata coded indexes once into null, valid or invalid facts. Migrate
all existing consumers and remove parallel token and failure-reason decoders.
Keep required-null policy with each column, preserving tokens, reason text,
offsets, issue ordering and readable partial metadata.
Retain known table/row coordinates even when invalid; TypeRef regressions
preserve row-zero and out-of-range enclosing facts without granting validity.
Attribute naming consumes the same validated constructor coordinate.
Validation: metadata/member/native-boundary/signature source suites and public
CLI/MCP recovery workflows passed; static, docs and duplication checks passed.
Give accepted target IDs one owner for processor identity and canonical case,
while preserving exact target spelling for selection and Evidence. Use that
fact for payload/bundle comparison instead of splitting on a colon.
Keep feature differences unknown unless exact target keys agree. Cover legacy
plus-feature declarations in both directions and mixed processor case with
individually named physical-byte regressions. The original code failed twelve
of the twenty-one comparison cases; the updated matrix passes all cases.
Validation: GPU source, physical boundary, compiled CLI and lifecycle suites
passed. Static, docs, schema and duplication checks passed. No GPU execution.
Closes#6792.
ELF images with `PT_DYNAMIC` but no `SHT_DYNSYM` section incorrectly reported a complete, empty export inventory. The shared ELF directory reader now owns the dynamic-segment observation; CLI/MCP report `unsupported` with a reason. Existing symbol-table results remain intact. Read-only format correction; no new execution authority or public schema.
Validation on the current main integration:
- `npm run check`, test discovery, and 166 selected Linux tests pass, including actual CLI/stdio MCP cases and PE/IL2CPP/GPU/Rust reader consumers. Original author validation: 113 tests on macOS, including regressions that failed before the fix; test discovery passed.
- Real Debian libc (1,926,232 bytes) and libLLVM 14 (109,967,296 bytes), with only section-header-count/name-index bytes changed, reproduce the old false absence in both public adapters. The reviewed implementation returns unsupported in both adapters. Independent Python maps unchanged `PT_DYNAMIC` declarations and selected symbol bytes; original `readelf` agrees. The reduced files are not loaded or executed; `readelf -D` itself cannot enumerate these reduced layouts, and that failed oracle attempt is retained.
- Unmodified libc still returns all 3,025 exports, byte-identical complete Evidence and the same Evidence ID as the baseline. Resource-guarded public probes completed without stops or payload leftovers. No speed, memory or model-token improvement is claimed for this coverage fix.
Codex review completed with no findings. CI failed: all 61 observed failures (including 47 omitted from the aggregate report by canceled shards) reproduce with identical complete failure diagnostics on the exact CI base and merge trees. Baseline-only bypass is explicitly authorized for this maintenance session; canceled/skipped coverage remains unverified. See the final CI attribution comment. The original author's readiness declaration below is preserved.
## Author readiness
Human owner: @leeguooooo
- [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
- [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
- [x] I declare this PR ready for initial maintainer review.
Reuse the shared offset-instant owner for calendar validation and exact probe ordering. Preserve first-century years, arbitrary recorded fraction digits in the supported offset profile, ordinal ties and legacy native-Date fallback. Keep original clock spelling when conversion leaves the supported four-digit year range, and refuse unrepresentable generated clocks before committing state.
Validation: focused clock/comparison and public CLI regressions cover leap years, offset equivalence, precise DELETE/GET order, spelling classification and refusal atomicity. Replay/state compatibility checks pass. Numeric state intervals and generated-clock progression retain their documented millisecond limits.
Fixes#6835Fixes#6643
Retain each resource directory's type/name path and incoming entry ranges before metadata accounting. Empty branches keep their identity, and shared directory bytes retain each distinct incoming path. Validate the expanded facts in the shared service and advertise them through the canonical contract.
Validation: parser, service, filesystem and public CLI/MCP regressions cover numeric/named empty branches, shared directories and unchanged artifact/payload hashes. Controls use authored PE32/PE32+ files; Windows loader behavior remains unverified.
Fixes#6872
Correct the pinned HAR schema's missing object types and duration constraints at the shared decoder boundary. Preserve optional omission and the -1 sentinel without repairing producer measurements, and identify the owned corrections in decoder provenance.
Keep unsupported or redacted response encodings unavailable to replay and state extraction. Empty-response classification now uses the same body owner, preserving known empty content separately from unavailable content. Regenerate contract examples from decoded fixture bytes and the shared view service.
Validation: focused decoder, replay/state, CLI and MCP regressions, including unselected malformed records and confirmed write responses. Contract examples reproduce through MCP. Authored capture controls do not establish real-browser producer coverage.
Fixes#6890Fixes#6889
Set the repository jscpd ceiling to the requested 4%. Preserve the scan
inventory, token/line minima, ignore rules and report failure propagation.
Validation: npm run jscpd passed at 3.59% duplication; scoped format check passed.
Use a valid canonical embedded map in the decoder row-budget regression.
The old empty map fails version admission before it can reach the row limit.
Retain that malformed-map case separately and assert typed constraint metadata
for format, external-section and capacity failures from the real codec process.
Validation: source-map source suites and all sixteen physical decoder boundary
tests passed. Static, documentation and test-discovery checks passed.
Give raw and header-selected imports one owner for their twelve identical
semantic revision declarations. Preserve property order, parameter values,
omissions and all provider/language/processor override precedence.
Validation: Ghidra behavior and MIPS/RISC-V/PowerPC profile suites passed.
Seventy direct before/after cases retained identical Results, serialization,
parameter order and digests. No real Ghidra process verification was performed.
Pass hierarchy or connection identity from the producing call rather than
searching graph IDs containing caller-selected paths and authored IDs. Keep
connection omissions from making a retained hierarchy appear partial.
Replace mirrored ID-substring test oracles with exact endpoint relationships.
Cover marker-bearing paths and authored identities through domain, CLI/MCP,
summary and byte-retention boundaries.
Validation: affected source and compiled/public tests passed, including native
Foundation/ibtool fixture lanes; one Linux-only invalid-byte filename case
skipped on macOS. Static, docs and discovery checks passed.
Share incremental replacement UTF-8 decoding across web and JavaScript workers, bound content before complete-line allocation and handle split CRLF consistently. Keep named schemas, request versus checkpoint failures, worker reuse and stream cleanup authority in their servers. Cover real server recovery and deterministic byte boundaries.
Preserve load and resolve operations through semantic facts, worker transfer and graph Evidence. Only loads propagate executable ownership. Project ambient member writes and escapes through the shared path- and site-aware mutation owner; retain source literals, bases and uncertainty. Cover ownership and mutation boundaries through CLI and MCP.
Artifact comparison owns the inventory Evidence identities whose integrity mismatched. Residual unknowns consume those identities and static/runtime correlation keeps contradictory or unresolved findings separate from observed changes.
Adds a real-ASAR MCP comparison and correlation regression with ordinary-content controls. Validation is pending the centrally coordinated lane; single-file formatting and lint completed.
Have the function observation owner select aggregate dimensions before residual-unknown lookup. Retain independent dimension scope, unknown citations and recommended probes through the public MCP verifier.
Validation is pending the centrally coordinated lane; single-file formatting and lint completed.
Compare all CLI/MCP repeats within their operation, call kind and expected-success group. Reconcile closure decreases with unique required obligations in the supplied ledger, retaining malformed observations in findings and snapshots.
Validation is pending the centrally coordinated lane; single-file formatting and lint completed.
Include required-claim surfaces and reachable dependencies in ownership, verifier commitments, contradiction, package proof and summary checks. Preserve all applicable package outcomes instead of accepting any passing proof.
Adds authenticated synthetic scope cases and a shared public CLI/MCP regression workflow. Validation is pending the centrally coordinated lane; single-file formatting and lint completed.
Use completed codec input to establish legacy CCOB extents, preserving declaration/integrity failures independently. Reuse bounded input windows, charge actual reads and decoding attempts, and qualify recovery after incomplete decoding. Publish only established or qualified terminal extents so interruption retains completed declarations. Recognize bitcode representation in HIP bundles while leaving inner facts unsupported.
Fixes#6887Fixes#6880
Select legacy Atom when AtU8 is empty and skip zero-byte optional FunT. Preserve explicitly empty counted arrays, malformed nonempty failures, last-occurrence selection and original chunk provenance. Share structural fixtures across producer and public workflow regressions.
Fixes#6816Fixes#6874
Refuse compressed note sections with typed unsupported-target semantics before framing or decoding, preserving stored-range checks and physical descriptor-offset provenance. Flutter retains independent build IDs with partial coverage when compressed notes are skipped. Verify the existing shared alignment fix with prefixed notes across classes and table kinds.
Fixes#6869Closes#6563
Charge the whole-catalog admission guard by escaped decoded names, preserving raw-byte lookup identity and final worker reply limits. Keep UTF-16 inventory and detailed-schema results whose returned facts fit the documented allowance.
Fixes#6888
* feat(cli): add JSON Lines output for row-oriented results (#5487)
- Support opt-in JSON Lines format (--format jsonl) for row-oriented results and collection views with clear record boundaries
- Preserve stable source identities and stream records deterministically line-by-line
- Emit final coverage and limitation metadata as a distinct line with https://rea.dev/schemas/jsonl-coverage-v1.json to prevent confusion with data rows
- Distinguish error outputs with https://rea.dev/schemas/jsonl-error-v1.json
- Reject non-row-oriented results that lack record boundaries with an unreadable_output error
- Document JSON Lines streaming conventions in docs/cli.md
- Add boundary tests covering empty results, multiline strings, partial coverage, large fields, and non-row rejection
* fix(cli): preserve diagnostics, provenance, and nested collections in JSONL
* fix(cli): frame classified failures and reject unrepresented JSONL facets
---------
Co-authored-by: gensokyo-dev <[email protected]>
Share endpoint identity and repeated-name ambiguity across call and reference dimensions. Compare same-artifact procedure entries by canonical coordinates while retaining raw reference sites and source Evidence. Keep observed comparison status separate from reconstruction proof.
Fixes#6853
Separate common row constraints from established storage sizes. Preserve Q8_1 and K-layout uncertainty, plus numeric IDs and ambiguous textual identities for historically shifted IDs16–25. Keep zero-byte facts and identity limitations independently, and propagate producer facts through totals, cancellation and retained views. Pin released source evidence and document the compatibility cost.
Fixes#6848
Use original object-table UIDs for inventory, hierarchy and connector references. Retain authored identity fields as data, refuse forged reference aliases and select UIKit top-level collections without substituting unrelated roots.
BREAKING CHANGE: Keyed archives that previously used authored objectID or object-id graph identities now use decimal table UIDs. Authored values remain attributes; ibtool and native NIBArchive identities retain their existing source semantics.
Fixes#6849Fixes#6850
Retain producer traversal uncertainty through inventory, extraction, Interface Builder decoding and executable-root discovery. Keep known copied files and decoded facts with honest coverage; share omission normalization and bound omitted archive paths with their source reasons.
Fixes#6852Fixes#6825Fixes#6582
Reuse the shared one-shot finalizer for discovery and instrumentation, preserve finalization cancellation with completed observations, and bind Frida cleanup errors to exact owners so confirmed retry recovers the primary error.
BREAKING CHANGE: instrument-with-frida JSON now returns the canonical Evidence record directly. Read normalized_result.cleanup_error instead of the earlier top-level cleanup_error wrapper field.
Parse a named proof contract once and qualify exact obligation, owner, revision, fixture/case, and verifier/claim tuples. Preserve unavailable proof separately from applicable failed outcomes.
BREAKING CHANGE: ID-array reconstruction proof records no longer qualify for verified or ready ledgers. Producers must supply implementation and revision bindings with individual claim outcomes; ledger diagnostics now distinguish inapplicable, unavailable, and failed proof.
Replace ambiguous display-label sorting with exact relation assertions keyed by metadata tokens. Distinguish same-name methods and verify directed ownership, edge counts, properties and Evidence without depending on graph order.
Decode named comparison payloads and validate nested citations at one domain boundary. Preserve workflow-specific authentication, source closure policy and validation order; cover malformed payloads and citation boundaries.
Use one bound JSON selection set for inventory metadata, keeping raw-name byte
comparison and filtering inside SQLite while preserving caller projection order
and repetition. Large requests reach native result-column capacity attribution.
Separate unavailable row sources from generated-expression failures. Preserve
SQLite's reason and selected table/path, offer schema-only recovery, and avoid
claiming corruption or recommending unreadable columns.
Validated UTF-8/UTF-16 identity, native inspection/worker tests and CLI/MCP failure
and recovery workflows. The large CLI selection uses the existing framed CLI
fixture to avoid host argv limits. Source bytes and identity remain unchanged.
Fixes#6855Fixes#6604
Share AMDGPU triple grammar between bundle and note owners so modern target
spellings participate in processor/feature conflict detection. Propagate CUBIN
register conflicts into object, target and report coverage.
Normalize adjacent PTX dot keywords in the lexer, removing the downstream pointer
splitter. Parse post-type alignment while distinguishing parameter and pointee
layout, preserving names and byte provenance when layout remains unknown.
Validated production-boundary matrices and public CLI/MCP coverage workflows with
inert fixtures. Correct the HIP-v3 control's ELF processor declaration. Real GPU,
assembler and compiler-produced modern-note behavior remains unverified.
Fixes#6844Fixes#6823Fixes#6858Fixes#6868Fixes#6861
Read Apktool 3's producer string dialect before YAML can discard hashes or
reinterpret values. Preserve older YAML behavior and decode producer escapes once.
Select the exact root AndroidManifest.xml resource in REA's JADX bridge and
derive summaries once with namespace-aware XML parsing. Preserve decoded text,
summarize before its display cap, invalidate cached facts with the loaded session,
and remove superseded upstream manifest handlers. Keep the engine pinned to 0.7.1.
Validated Apktool parser/provider and public adapter controls; compiled the bridge
against the verified pinned JAR and ran its real Java regressions plus public CLI
and stdio MCP manifest parity checks. Real Apktool execution remains unverified;
the new manifest fixtures use plain XML. No Android application was executed.
Fixes#6804Fixes#6866Fixes#6863Fixes#6819
Use one marshal surrogatepass decoder for PYZ directories and Python bytecode.
Validate ordinary UTF-8 spans with the native fatal decoder, preserving BOMs,
lone surrogates, raw bytes and marshal reference slots.
Distinguish legacy full-pyc CArchive modules from modern marshal-only modules
before reconstructing headers. Share typed source/codec failures between stream
output and completion while retaining native-read drain and cleanup ownership.
Validated focused producer, reader, lifecycle, CLI and MCP regressions, plus
public inventory/extraction of CPython 3.14-generated marshal tables and code.
No packaged application was executed. Document original versus derived bytes.
Fixes#6800Fixes#6789
Keep validated PE padding in the selected Evidence range while consumed zlib bytes own compressed and inflate-ratio limits. Preserve typed resource failures and bounded allocation. Validated accepted padding, over-ratio and compressed-size controls through source, real CLI and stdio MCP workflows.
Use Float.valueOf grammar at shared admission and retain selected spelling through am --ef. Validated scientific notation, malformed controls and exact MCP forwarding with a source-owned ADB fixture; no device execution.
Fixes#6796
Separate AMDGPU processors from feature-qualified note identities. Read Apple hidden ARM64 table rows once and preserve visibility through artifact, dylib and fuse consumers. Keep Objective-C++ template commas within type attributes and incomplete metadata unknown.
Validated provider/parser and production property regressions, Electron CLI/MCP consumers, and native Apple lipo -hideARM64 output through the production slice reader and public artifact inspection. LLVM execution remains unverified.
Fixes#6806Fixes#6799Fixes#6801
Match table metadata by stored name bytes and fold only ASCII code units for relationships, reusing parsed facts without per-trigger queries. Preserve caller paths on structured worker limits and V8 heap failures.
Validated invalid UTF-8 collisions, distinct UTF-16 names and real worker transport refusal through CLI and stdio MCP. Controlled stderr covers OOM classification; actual memory exhaustion was not induced.
Fixes#6795Fixes#6607
Share embedded-map version admission and derive indexed overlap from represented mappings. Carry createRequire filename bases through worker facts and graph identity. Select factory export bindings at each bundler owner.
Validated accepted and malformed source maps through real CLI and stdio MCP codec workflows; verified loader and esbuild graph output against Node runtime controls. Rebased consumer checks passed.
Fixes#6803Fixes#6614Fixes#6798Fixes#6797
Reuse preflight-parsed container references for bounded decoder-copy normalization. Preserve original artifact bytes, scalar provenance and reference order. Verify three-byte offset and reference encodings through CLI and stdio MCP.
Fixes#6805
Retain warnings from validator, objdump and decoder through one diagnostic owner. Keep uninterpretable name bytes and explicit name-metadata coverage in complete results and summaries.
Fixes#6812
Reject duplicate decoded JSON names before object conversion. Use one checksummed zlib stream parse and retained PE FileAlignment to distinguish exact zero padding from trailing payload bytes. Exercise public CLI and stdio MCP.
Fixes#6814Fixes#6793
Share selected-file errno ownership with model inspection and codec result accounting with Rust. Verify actual descriptor exhaustion and unchanged-input recovery in an isolated child.
Fixes#6666
Resolve attribute display names from validated nesting and parsed member facts, admit composed names before allocation, and preserve unknown malformed chains. Keep ReadyToRun classification ahead of metadata hints while retaining each observed hint.
Fixes#6815Fixes#6810
Use one retained-path owner for module references and items. Preserve selected paths and genuine ambiguity across module kinds; remove the superseded singular helper.
Fixes#6790
Reuse the reader correction from PR #6705, and share the byte projection budget with output validation. Verify actual APK parsing through compiled CLI and stdio MCP at 0, 1, 2, 3, 64 and 65 descriptor bytes.
Co-authored-by: Ezra <[email protected]>
Fixes#6697
Read link targets as bytes before strict UTF-8 admission. Retain malformed targets as unreadable with source hex; preserve literal replacement characters and valid BOM-prefixed targets.
Fixes#6765
Validate supervised text once before version or capture interpretation. Exercise POSIX client probes through compiled CLI and stdio MCP; capture-summary native bounds remain host-specific.
Fixes#6775
* feat(cli): report version and runtime with an explicit output format
`rea --version --json` and `--version --format <format>` ignored the format
and printed only the bare version, so support reports gathered the runtime
and platform by hand.
A root --version request that selects a format now renders a documented
report: package name and version, JavaScript runtime and version, platform,
architecture, and whether the runtime satisfies the supported range. It
contains no paths, environment values, user or host names, and starts no
provider. Plain --version is unchanged, --help keeps precedence, and other
output controls keep Incur's plain version.
The runtime verdict uses a new shared supportsJavaScriptRuntime rule that
rea doctor now uses too, so both judge Bun by its Node.js compatibility
version. mcp doctor's private JSON/format renderer moves to cliOutput as
renderCliDocument and is shared with the version report.
Closes#5483
* refactor(errors): centralize input failure construction
Carry caller messages through the existing schema issue owner instead of constructing and discarding an intermediate input error. Remove forwarding schema helpers and migrate equivalent constructors across investigation, firmware, dylib, process, and screenshot workflows while preserving diagnostic paths and causes.
* docs(readme): add Roman Urdu edition (#6499)
* docs(readme): add Roman Urdu edition
Register ur-Latn with a pending website catalog and preserve English command examples.
Script-tag handling follows morluto's shared helper proposed in PR #2043 (c46f606c89).
* test(website): cover completed script-tag page metadata
* fix(process): parse native teardown relation sentinels
* fix(cli): make version report privacy checks environment-independent
The CLI test rejected any report containing the OS account name, so an
account named node, linux or rea failed a correct report. Check unique
caller-controlled HOME, cwd and environment markers instead; the strict
schema still rejects any additional field.
The CLI guide said --version with other output controls prints the plain
version, but invalid combinations such as --json with --token-limit report
their existing output-flag error first. Describe both outcomes and test them.
* refactor(nativeaot): share report facts across budget stages
Centralize parse-wide identity and coverage facts while keeping conservative admission values separate from measured rows and counters. Reuse the derived truncation reason and retain the final serialization bound.
Tighten the report-budget regression so unexpected parser finalization errors cannot be swallowed by its budget search.
* fix(adb): preserve empty properties and verbatim version names
* fix(artifacts): recognize empty Zip64 containers
Fixes#6540
* fix(erlang): accept uninterpreted BEAM alignment bytes
Fixes#6517
* fix(gpu): accept PTX entries without parameter lists
Fixes#6493
* fix(python): preserve signed code-object line numbers
Fixes#6490
* refactor(ghidra): centralize load image check recording
Share expected and observed check construction across MZ, COM, and raw-binary attestations. Preserve format-specific rules and source coordinates, and derive mapping callback types from the recording owner.
* fix(inspector): preserve script identity and parse-failure facts
Fixes#6535Fixes#6546
* fix(artifacts): recover malformed PYZ sizes without losing siblings
Fixes#6120
* fix(erlang): accept extended BEAM atom lengths
Fixes#6503
* refactor(erlang): reject overflowing BEAM atom lengths
* fix(gpu): preserve PTX and AMD target facts
Fixes#6469Fixes#6483
* refactor(targets): preserve ambiguous MZ header evidence
* fix(adb): retain refused activity launch reports
Parse complete nonzero Activity Manager reports while preserving other command failures and their captured output. Admit dollar signs only in the activity component name for nested Android classes.
Fixes#6528Fixes#6527
* fix(mcp): retain partial evidence through shared delivery
* fix(python): honor version-specific code equality
Fixes#6547
* fix(pyinstaller): preserve namespace declarations
Project PYZ namespace packages as metadata so their query paths do not claim output directories. This lets independent CArchive files retain the same logical path and remain inspectable and extractable.
Fixes#6539
* docs: clarify evidence ownership and verification guidance
* docs(adb): clarify emulator observation field
* docs(artifacts): clarify active-reader extraction scope
* fix(erlang): select the last repeated BEAM chunk
Fixes#6518
* fix(il2cpp): distinguish rank-one array types
Fixes#6534
* fix(python): align pyc and PYZ marshal decoding
Fixes#6560Fixes#6559
* fix(browser): preserve frame identity across captures (#4040)
* fix(browser): preserve capture evidence fidelity
* fix(browser): align frame identity comparisons
* fix(browser): retain historical frame evidence
* fix(electron): retain same-document script owners
* fix(electron): bind frame proof to tree reads
* fix(browser): retain malformed frame scope
* fix(browser): retain WebMCP event scope
* fix(browser): retain malformed timeline identity
* fix(browser): unify frame identity and scope
* fix(electron): prove enclosing frame ancestry
* fix(electron): separate ancestry from file policy
* fix(browser): preserve transient fragment semantics
* fix(browser): retry uncertain initial frame scope
* fix(browser): preserve runtime frame identity
* refactor(browser): separate independent capture fixes
* fix(electron): detect tree-first document replacement
* fix(electron): retain same-document tree authority
---------
Co-authored-by: eco-null <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(elf): resolve symbol versions from requirements
Fixes#6533
* fix(elf): follow loader version index precedence
VERDEF entries replace VERNEED names for shared indices, matching glibc lookup behavior.
* test(firmware): bind owned producers and exercise real limits
* fix(adb): preserve push target semantics
Fixes#6566Fixes#6567
* fix(cli): merge sibling output filter slices
Fixes#6542
* fix(browser): preserve Link context semantics
Fixes#6504Fixes#6505Fixes#6506
* fix(python): classify historical headers and signed flags
Fixes#6568, #6487
* fix(python): normalize legacy code flags for equality
Preserve CPython 3.7–3.10 constructor flag semantics in code keys.
* fix(environment): compare captured bodies by representation
Keep JSON, text, and retained bytes distinct through fidelity comparison. Compare binary bodies by bytes, including valid UTF-8 bytes against equivalent text, and keep empty-body compatibility explicit.
Fixes#6564
* docs: clarify derived values and comparison controls
* fix(environment): scope fidelity ID normalization
Reserve equal global ID pairs and stop normalizing contested pairings. Keep numeric measurements literal, scope keyed collection identities through learning and comparison, and normalize string references only where learning established the same field as an ID.
Fixes#6565Fixes#6550Fixes#6557
* test(process): print the progress frames when the sparse-child bound fails (#6427)
The sparse-child capture test asserted the frame bound with a bare boolean,
so a failure only printed "expected false to be true". The assertion now
carries the running progress details (elapsed time, frames, samples,
interactions) as its failure message. This only adds diagnostics to a
failure; it does not change what passes and does not fix the flake.
Refs #5889.
🤖 Generated with Claude Code (Sonnet 5.5) 🦊
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
* fix(il2cpp): name the metadata file when linking finds malformed metadata (#6425)
* fix(flutter): honor ELF note alignment when reading build IDs (#6423)
* fix(flutter): honor ELF note alignment when reading build IDs
* test(flutter): key shared note ranges by effective alignment
Cover a program and section declaration that share one note range: differing effective alignments (8 and 4) must both be read, while declarations that both normalize to four bytes (1 and 4) still share one read. 24-byte build IDs pad identically at both alignments, so only the range key decides the read count.
* refactor(firmware): centralize audited tool requirements
* fix(javascript): associate requested bindings only with resolved native addons (#6402)
* fix(javascript): associate requested bindings only with resolved native addons
* test(javascript): cover the exact native addon association
---------
Co-authored-by: morluto <[email protected]>
* fix(python): reject code keys and set members with unhashable consts (#6424)
* fix(python): reject code keys and set members with unhashable consts
* fix(python): refuse to hash a tuple that is still being read
* fix(python): hash a code object's co_consts tuple, not its item list
---------
Co-authored-by: morluto <[email protected]>
* fix(model): recognize named chat templates in summaries
Count retained template content rather than only the default key; keep names-only inventories distinct.
Fixes#6569
* fix(model): retain resolved GGUF alignment under cancellation
Publish alignment from the parser when explicit metadata validates it, or when metadata completion establishes the default. Preserve that fact through later parsing and hashing failures.
Fixes#6579
* fix(environment): reject out-of-range clock offsets
Reject invalid ISO offsets while preserving valid boundary values. Validate stored clock-format offsets through the same parser so malformed imported formats cannot reach rendering.
Fixes#6524
* fix(environment): preserve submillisecond insert ordering
Compare retained submillisecond components after milliseconds, so ordered collections recognize and place distinct timestamps correctly.
Fixes#6525
* fix(environment): restrict epoch normalization to clock fields
Require a time-field token boundary before treating numeric values as epoch clocks, so identifiers such as media format codes remain literal. Clarify recognized field forms and clock offset and precision semantics in the fidelity guide.
Fixes#6551
* fix(process): preserve sibling path identities
Keep aliases on selected paths at clear text delimiters so a longer sibling filename is not rewritten as the executable. Version the normalization contract and cover source and actual PTY behavior.
Fixes#6586
* fix(sigrok): retain recording suffix for autodetection
Copy recordings under a private filename that retains the caller-selected final suffix, and pass that filename to sigrok. This restores filename-based autodetection without exposing the source directory.
Fixes#6580
* test(go): assert unresolved MZ candidate boundary
* fix(pe): ignore unused export ordinal slots
Fixes#6577
* fix(macho): recognize arm64_32 binaries
Fixes#6581
* fix(go): honor amd64p32 pointer width
Fixes#6590
* fix(pe): accept BER indefinite CMS values
Fixes#6574
* fix(browser): reject overlapping indexed source maps
Fixes#6562
* fix(browser): resolve prefixed XHTML form actions
Fixes#6561
* fix(browser): preserve well-known path case
Fixes#6571
* docs(browser): clarify parser evidence contracts
* fix(gpu): accept percent-prefixed PTX entry names
Fixes#6592
* fix(gpu): accept PTX compute target aliases
Preserve the source spelling of NVIDIA's documented compute_xx target synonym.
Fixes#6494
* fix(gpu): parse legacy PTX body parameters
PTX 1.0 through 1.3 declares kernel parameters inside the entry body. Keep later-version parameter-list parsing unchanged.
Fixes#6594
* fix(gpu): keep unknown targets out of exact filters
Fixes#6599
* fix(gpu): resolve extended CUBIN symbol sections
Fixes#6600
* docs(firmware): document the Windows Unblob submodule checkout (#6464)
On Windows, the pinned Unblob commit cannot be checked out because 11
upstream fixture paths under tests/integration/ are invalid on NTFS.
Document the verified sparse-checkout workaround and point Windows
contributors to it from CONTRIBUTING.
Claude-Session: https://claude.ai/code/session_01LjhoPGpqAwHwcCgectzRsp
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(recovery): retain file outcomes and cleanup ownership
Use shared owned-file and directory primitives throughout Wakaru discovery,
snapshots, report parsing and publication. Preserve primary read failures and
completed fingerprints when final close fails or receives cancellation.
Retain exact cleanup capabilities before retiring workspaces, block new work
while cleanup is unresolved, and bind error status to the aggregate owner so
partial cleanup and later retry keep resource attribution current.
* fix(pe): preserve biased export ordinals
Accept the format-derived ordinal range for PE export slots.
* docs: clarify parser authority and compatibility evidence
* fix(python): preserve code-name identity
Fixes#6593
* fix(managed): validate metadata operands and locals declarations
Classify MemberRef operands from parsed signatures, validate signature table references and LocalVarSig blobs, and compare observed locals declaration presence. Keep partial CIL facts and transient-budget ownership at the method reader.
Fixes#6578Fixes#6572Fixes#6558Fixes#6513
* fix(python): preserve historical signed code fields
Keep signed co_stacksize and co_flags for historical magic entries whose constructors accepted them. Preserve magic 3410 with a constructor-revision limitation; reject negative values from 3411 onward.
Fixes#6487
* fix(python): compare code objects without inline caches
Fixes#6597
* fix(tests): stabilize process fixtures and malformed input coverage (#6570)
Preserve renamed-executable identity, complete-line readiness and bounded child lifetimes. Use genuinely malformed Inspector input and explicit ADB file-probe/push/hash facts while preserving cancellation, cleanup, Evidence and completeness assertions. Adopt current main Go fixtures and retain malformed COFF coverage.
Fixes#6556
Refs #6480
* test(apktool): own lifecycle fixtures and process observations
* refactor(mcp): reuse parsed Evidence tool parameters
* fix(runtime): align remaining Node security pins and setup guidance (#6316)
Derive exported Docker image versions from .nvmrc through generated package metadata. Align active setup translations with patched Node release floors and verify actual CLI exports, real Docker/agentenv, package and website workflows.
Eight built-test shards pass; three unrelated managed source failures reproduce on clean main and remain tracked in #6650.
Refs #6480
Co-authored-by: gensokyo-dev <[email protected]>
* fix(model): retain exact safetensors shape dimensions
Keep unsigned integer declarations exact until their owning field is interpreted. Bound actual offsets at conversion and validate shape products in declaration order, preserving large empty tensor shapes without hiding an earlier overflow.
Fixes#6627
* fix(python): read legacy TYPE_INT64 marshal values
Fixes#6609
* fix(apk): read constructed BER signer signatures
Share the bounded BER tree reader between PE certificates and APK v1 CMS, then accept constructed OCTET STRING signature values while retaining the declared signer and certificate facts. Embedded X.509 certificate decoding keeps its DER requirement.
Fixes#6603
* fix(adb): count Unicode requested permissions
Count requested-permission rows containing Unicode names, and keep counting subsequent rows in the same dumpsys section.
Fixes#6584
* fix(signals): reject missing sigrok channels
Recognize the exact selected-channel diagnostic even when sigrok-cli exits successfully, while preserving partial producer output in the invalid-input details. Cover both public JSON adapters and add the missing-channel case to the Linux real-tool verifier.
Fixes#6631
* fix(elf): reuse recognized machine architecture facts
Preserve the shared EM_HEXAGON classification in Go and Rust image reports.
Fixes#6629
* fix(asn1): decode four-byte BER lengths unsigned
Accumulate length octets arithmetically so a four-octet length with bit 31 set remains a positive unsigned value and is rejected by the containing-range check rather than becoming a negative length.
* fix(gpu): preserve parameter and target declarations
Decode observed V2 CUDA parameters, retain qualified PTX parameter names and entry resource counts, and preserve family and feature target declarations through selection and consistency checks.
Fixes#6624Fixes#6625Fixes#6626Fixes#6618Fixes#6613Fixes#6606
* fix(captures): preserve canonical TShark alias fields
TShark resolves selected field aliases before writing packet JSON. Keep the requested selector in Evidence while accepting the canonical field key produced for it.
Fixes#6595
* fix(qlog): mark records with unknown STREAM FIN incomplete
Keep known STREAM ranges and the unknown FIN value, while listing the source event among incomplete fact records so summary and retained views agree.
Fixes#6598
* fix(pcap): retain absolute HTTP request targets
Reuse parsed URI facts and preserve uncertainty when mixed request records cannot all be resolved from the producer output.
Fixes#6537
* fix(rust): preserve historical cargo-auditable feature facts
Accept observed feature arrays, preserve omitted values as unknown across producer revisions, and keep older retained reports compatible. Reuse bounded JSON sizing before graph expansion.
Fixes#6622
* fix(ptx): normalize parameter declarators once
Fixes#6606
* fix(managed): compare observed call targets
Compare aligned managed call edges by their reported declaring type and method name, keeping missing target facts unknown without comparing build-local tokens. Document the static comparison limits.
Fixes#6630
* fix(artifacts): compare PyInstaller declaration facts
Compare stable producer-tagged declarations independently of materialized payloads. Preserve missing facts as unknown without applying PyInstaller rules to foreign metadata or nested container entries.
Fixes#6484Fixes#6485
* test(managed): retain derived facts in cloned method fixtures
Clone caller-scoped call and field facts with the fixture methods. Keep known CIL changes alongside unavailable target metadata in the missing-row controls.
* refactor(apk): consume shared BER node facts
Remove the APK reader's pass-through children and content projections so format consumers read the shared BER owner's nodes directly. Keep only the encoded-span helper needed for exact issuer and certificate bytes, and describe CMS BER as this parser's structural owner.
* fix(adb): delimit permission section by indentation
* test(managed): preserve field-access owner identities
* fix(managed): share reference instruction semantics
Use the same parsed method/field operand predicate when producing member edges and comparing call and field shapes. Exclude ldtoken and operands without a value from both interpretations.
* test(managed): align source goldens with metadata validation
Fixes#6650
* fix(native): consolidate observed address validation
* fix(gdb): separate queued work from MI synchronization
Queued console followers have not dispatched MI records. Use active protocol requests when deciding whether the admitted close can verify and detach its inferior.
Fixes#6661
* fix(gdb): wait for the outer console command to finish
After a running result, queue a tokenized read-only MI query to establish that GDB has returned to its command loop. Keep collecting through intermediate inferior stops and retain the query reply as protocol Evidence.
Fixes#6660
* fix(erlang): retain admission cancellation reason
Preserve the caller's AbortSignal reason when BEAM inspection is cancelled before provider admission. Keep the production filesystem boundary and late-completion contracts covered.
Fixes#6658
* fix(desktop): probe GameMaker data filenames
Select conventional Linux and macOS game data files for the existing bounded FORM/GEN8 probe. Add service-boundary regressions and document the selected names.
Fixes#6659
* fix(harmony): exclude raw resources from manifests
Classify known resource paths once and keep a raw resource named module.json from changing the package model or appearing as a manifest. Add a filesystem-boundary regression and clarify the documented hint.
Fixes#6638
* fix(gpu): bound embedded section reads
Fixes#6657
* fix(gpu): retain resources across entry pragmas
Fixes#6651
* fix(reference-source): preserve raw POSIX filename identity
Mark directories with non-UTF-8 names unsupported without following replacement-character paths, while retaining readable sibling observations.
Fixes#6653
* fix(reference-imports): retain exact dynamic import targets
Use the shared exact-literal reader for dynamic imports, preserving expression-free templates and transparent TypeScript wrappers while leaving computed targets unknown.
Fixes#6654
* fix(reference-imports): retain exported import-equals dependencies
Route exported external import-equals declarations through the same relationship owner as plain declarations so their required target is not dropped.
Fixes#6655
* fix(managed): respect native module case semantics
Preserve the observed native format when comparing module identities so ELF case-only differences remain mismatches while unknown platform behavior stays unresolved.
Fixes#6652
* fix(il2cpp): reject duplicate method tokens per image
Reject duplicate MethodDef row identities within one owning image before publishing native addresses or Ghidra seeds, while retaining cross-image reuse and folded code addresses.
Fixes#6610
* refactor(reference-imports): share exact literal parsing
Route legacy nested import-expression handling through the same exact-literal owner as dynamic imports, removing a duplicate string-only check.
Fixes#6654
* fix(model): preserve optional metadata and tensor alignment
Fixes#6656Fixes#6637Fixes#6636
* fix(macho): infer legacy Intel simulator platforms
Fixes#6649
* fix(macho)!: preserve ambiguous chained import names
Keep the exact producer expression and report unknown symbol identity when chained-import text cannot distinguish a binding addend from a literal name suffix.
Fixes#6648
BREAKING CHANGE: inspect_macho import names can be null when identity is ambiguous. Use raw_name for the observed expression and name_status to distinguish it from an exact symbol name.
* fix(elf): retain raw export name bytes
Fixes#6647
* refactor(macho): separate dyld import and export parsing
Keep mode-specific inputs and parsed row types at their respective parser boundaries.
* fix(macho): preserve import identity in retained views
Use distinct row contracts for imports and exports, retaining ambiguous names and historical import defaults without adding import-only fields to export pages. Verify the SDK-advertised schemas and both facets through MCP.
* style(desktop): format GameMaker probe coverage
* chore(deps): update Vite security patch and dependency inventory (#6416)
Update Vite/PostCSS and refresh the lock-derived inventory while retaining TSRS platform selection and runtime dependency versions.
Source/static checks, installed-platform signatures, full/production audits, real CLI/MCP workflows and all four package platforms pass. Portable CI retains the independently reproduced main PTX fixture failure tracked in #6675; use the owner-authorized existing PR-only baseline exception. Refs #6480.
* fix(release): isolate npm publication from dependency execution (#6681)
Keep dependency execution in read-only release jobs and validate the original qualified archive with Node built-ins before publication. Refresh registry state on every publisher attempt; retain exact bytes/channel verification, v1 read-only recovery, and installed CLI/MCP checks before MCP publication.
Static/docs checks, artifact/registry/recovery tests and real package/rehearsal verification pass. All four hosted package platforms pass. Portable CI retains the independently reproduced main PTX failure in #6675 and fail-fast missing coverage; use the owner-authorized existing PR-only exception, with limitations documented in the PR. Refs #6480.
* refactor(json): consolidate representation size accounting
Share bounded UTF-8 and UTF-16 JSON measurements across state serving, write retention and source-map admission. Remove duplicate counters and distinguish retained state estimates from encoded bytes while preserving limits and exact refusal diagnostics.
* refactor(android): own search summaries at the producer boundary
Reuse validated class pages for inline pagination audits while retaining complete provider replies. Move retention into the shared Android workflow and remove the MCP-only service wrapper and application text reparser. Preserve unknown JSON metadata, late cancellation and retention failures.
* test: align boundary fixtures with producer contracts
Model GDB console completion, use real directory reads and cleanup for late-enumeration failures, and distinguish supported PTX pointer qualifiers from unknown declarations. Preserve original assertion and cleanup failures through scoped test finalizers.
* fix(rust): retain parsed defaults in cached Evidence views
Borrow sealed reports only when they match the parsed facts; otherwise cache the owned normalized snapshot. Keep historical feature omissions unknown through saved bundles and repeated retained pages without changing the source Evidence.
Fixes#6689
* fix(model): retain parsed headers when hashing is interrupted
Share partial observation ownership across GGUF and Safetensors. Preserve completed header facts through cancellation and MCP error delivery without claiming finalized digests or file identity.
Fixes#6684
* fix(gpu): preserve PTX parameter and target facts
Fixes#6664Fixes#6675Fixes#6680Fixes#6695
* fix(go): read inline build strings from ELF mappings
Fixes#6716
* fix(rust): scan read-only ELF load data
Fixes#6663
* fix(il2cpp): deduplicate consistent ELF registration aliases
Fixes#6715
* fix(il2cpp): track unresolved ELF relocation widths
Fixes#6701
* fix(artifacts): preserve raw directory name identity
Fixes#6665
* fix(desktop): retain unsupported filename coverage
Fixes#6665
* fix: preserve unknown historical module resolution
Fixes#6679Fixes#6678Fixes#6612
* fix: retain proven createRequire URL bases
Fixes#6612Fixes#6678
* fix(apple): preserve keyed archive table identity
Fixes#6662
* fix(plist): reject XML dictionary keys without values
Fixes#6682
* fix(apple): preserve NIBArchive special real values
Fixes#6583
* fix(signals): reject missing targets and invalid UTF-8
Fixes#6671Fixes#6693
* fix(beam): accept bytes after declared form
Fixes#6717
* fix(artifacts): compare source map target identity
Fixes#6706
* fix(pyinstaller): retain executable member metadata
Fixes#6702
* fix(pyinstaller): decode interned unicode names
Fixes#6676
* fix(pyinstaller): retain colliding archive observations
Keep source-distinct CArchive rows and decoded PYZ children in inventory. Refuse only when extraction reaches a destination conflict.
Fixes#6674
* fix(dotnet): preserve managed metadata comparison facts
Decode VARARG sentinels and allow omitted empty blob heaps while retaining failures for referenced missing data. Compare field and catch targets using owning parsed facts instead of build-local tokens.
Fixes#6720Fixes#6686Fixes#6713Fixes#6712
* fix(dotnet): keep TypeRef catch identity unknown
Fixes#6712
* test(apple): assert projected interface builder facts
* fix(dotnet): correct metadata comparison regressions
* fix(il2cpp): deduplicate aliased registration fields
Fixes#6715
* test(pyinstaller): match interned name producer encoding
Use Python 3.13 PYZ magic with the matching archive version and exercise FLAG_REF with TYPE_INTERNED.
Fixes#6676
* test(dotnet): split managed comparison blocks
* test(apple): isolate NIBArchive real projection group
* fix(parsers): correct Rust import and PTX alignment control
* fix: recognize aliased createRequire bindings
Fixes#6612Fixes#6678
* test(apple): retain NIBArchive limitation path
* fix(il2cpp): deduplicate aliased metadata registration
Fixes#6715
* test(managed): retain observed target changes across member remaps
* fix: track require.resolve references
Fixes#6678
* test(pyinstaller): encode PYZ list length correctly
Write the marshal list length as its raw little-endian count and surface inventory limitations when completeness regresses.
Fixes#6676
* fix(managed): retain unknown catch identity beside observed name changes
* fix: retain require.resolve source facts
Fixes#6678
* refactor(javascript): name retained require call facts explicitly
* fix: resolve selected require call members
Fixes#6678
* fix(javascript): keep unresolved created-loader bases unknown
* refactor(artifacts): centralize completed file outcomes
Project completed values and cancellation from the descriptor owner once across artifact consumers. Preserve completed void outcomes and format-specific validation and cleanup policies.
Keep later aborts from replacing captured APK and SQLite close failures, and retain the exact APK cancellation reason. Exercise real descriptors, public workflows and cleanup retries.
* fix(javascript): isolate package facts from host prototypes
Build frozen package field snapshots with a null prototype and restrict reference-source main lookup to own properties. Preserve declared null and empty values, index fallback, source identity and unavailable manifest evidence.
Compare inherited main and exports lookup with the native Node resolver, retain package observation coverage and verify CLI/MCP package workflows.
* fix(captures): advertise offline capture inspection without a browser provider (#6725)
* fix(parsers): preserve format boundaries and query occurrence identity
Normalize parsed facts at their owning boundaries across PE exports and certificates, ELF section names, GPU bundles, IL2CPP generic types, dyld slice selection, source maps, environment replay, and JavaScript mapped arguments. Preserve partial facts and explicit unsupported cases; clarify the corresponding documentation.
Validation: 831 affected tests passed with one existing skip across 23 files; build, typecheck/lint, documentation, formatting, and 2133-file test discovery passed. Format controls are synthetic; real Unity, GPU, and ARM-device execution is unverified.
Fixes#6709Fixes#6688Fixes#6719Fixes#6718Fixes#6704Fixes#6703Fixes#6692Fixes#6723Fixes#6687Fixes#6726Fixes#6696Fixes#6691Fixes#6727
* fix(process): require observed environment ownership tokens
Centralize run-token extraction at the environment observation boundary and require an own field. Build decoded OS environment records without host prototypes while retaining source names, empty values and last-assignment precedence.
Reject inherited matching tokens before cleanup signaling, preserve normal owned-group behavior, and verify live Linux process environments alongside native CLI/MCP capture workflows.
* fix(parsers): retain independent facts and boundary identity
Share bounded ELF section-name decoding across native and note readers; retain read-only ranges and coverage reasons when names are unknown. Preserve ELF symbol-version bytes, dyld-bound method uncertainty, partial gRPC-Web trailers, and per-parent model identity checks. Compare GPU signatures as bytes and require a PTX module header. Share selected-path errno classification across EVM and BEAM.
Validation: 397 shared-parser tests passed (one existing skip), 5 MCP schema tests passed, 14 EVM error-mapping tests passed, and 23 protobuf CLI/MCP matrix cases passed across focused runs. Build, typecheck/lint, docs, formatting, and 2134-file discovery passed. Full EVM provider workflow remains locally unverified on Darwin arm64; format controls are synthetic.
Fixes#6738Fixes#6737Fixes#6745Fixes#6740Fixes#6749Fixes#6747Fixes#6739
* fix(setup): preserve source keys across configuration boundaries
* fix(setup): keep the CLI-only skill choice offered under a client ID (#6401)
* fix(setup): keep the CLI-only skill choice offered under a client ID
* test(setup): cover the accept-all CLI-only skill selection
* fix(apple): report a read-denied selected archive as host access denial (#6479)
* fix(apple): report a read-denied selected archive as host access denial
inspect-keyed-archive reported a host read refusal on the caller-selected bundle entry as artifact_operation_failed/execution_failure with reason io, leaving EACCES or EPERM only inside message text. The caller selected that entry, so the refusal is a selection outcome with its own typed classification, system code and read-access recovery.
DirectoryArtifactReader now marks a refused entry open with DirectoryArtifactEntryAccessDenied, which retains the host system code and the selected entry path. KeyedArchiveReader, which owns the caller's selection, maps it to the shared AnalysisAccessDeniedError, and artifact failure translation keeps that typed error instead of replacing it with a generic io failure. Other readers and other reader failures are unchanged; every existing reason keeps its current projection.
Adds the access_denied projection coverage plus a real host refusal through the production reader and a non-permission control.
* fix(apple): carry cleanup ownership and cover raw refusals for a denied selection
Addresses the two review findings on the denied-selection classification:
- The typed host access denial now carries the reader's cleanup observation, so a denied open that left a resource owned does not project as fully cleaned.
- Adds coverage that starts from raw host EACCES/EPERM/EIO refusals with the real DirectoryArtifactReader open path, so the marker itself is exercised rather than constructed by the test. EIO stays an artifact failure.
* fix(native): report plist symlink loops and overlong paths as selection failures (#6371)
Co-authored-by: Abelo9996 <[email protected]>
* docs(cli): document opt-in Bash completion setup (#6250)
* fix(doctor): check only the current platform's Hopper launcher candidates (#6253)
* fix(doctor): check only the current platform's Hopper launcher candidates
Doctor built one launcher list for every platform, so on macOS it could
report a Linux-only path as a healthy Hopper ahead of a renamed bundle,
and setup then registered that path. Give per-platform automatic
candidates one owner that runtime configuration and doctor both read,
scan renamed macOS bundles and probe the Homebrew cask only on macOS,
and share runtime's renamed-bundle scan with the doctor host.
Fixes#6228
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(doctor): keep the renamed Hopper bundle scan asynchronous
Sharing runtime's synchronous scan made the doctor host read
~/Applications with readdirSync, which blocks the event loop on a slow
or network-mounted home. Give MacHopperPaths an asynchronous scan for
doctor beside runtime's lazy synchronous one, sharing the directory
filter and candidate builder, and check that both return the same
candidates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(doctor): model renamed launchers with runtime's POSIX joins
Since #5235, runtime builds macOS launcher paths with posix.join, so on
Windows the discovery table's native-join renamed launcher no longer
matched runtime's candidate. Model launchers with posix.join and keep
filesystem setup native, as MacHopperPaths.test.ts does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(entropy): name offset when the byte window starts past EOF (#6495)
Fixes#6482
Co-authored-by: Abelo9996 <[email protected]>
* fix(sqlite): report result-column capacity refusals as capacity (#6327)
A projection wider than SQLite's native result-column limit failed with the
generic "too many columns in result set" SQL error. The inspector could not
attribute that error to a generated column, so the worker reported "format"
and the parent blamed the caller's path as invalid_format even though the
selected database was readable.
Classify the refusal from SQLite's own column limit for the private
connection, carry the selected and engine counts through the worker reply,
and project it as a capacity resource constraint. The database stays
readable, no partial rows are returned, and the recovery names the column
counts and splitting the projection across calls.
* fix(web): refuse an existing script-export destination as invalid input (#6313)
* fix(web): refuse an existing script-export destination as invalid input
export_web_scripts refused an existing output directory or link correctly,
but reported it as artifact_operation_failed / execution_failure with no
field issue or recovery. SafeOutputTree.create raises that refusal as a
path failure before any output tree exists, and the export mapper wrapped
it as an artifact failure.
Attribute pre-admission path refusals to output_directory as an
AnalysisInputError, as environment export already does for its directory.
Both now share one helper owned beside SafeOutputTree; failures after the
tree is admitted keep their artifact semantics.
Fixes#6029
* fix(artifacts): type SafeOutputTree destination refusals
Admission conversion treated any cleanup-free path failure raised before
admission as a refusal of the selected directory, so an acquired-tree setup
failure, such as being unable to restrict a created root to 0700, blamed
valid output_directory or directory input.
SafeOutputTree.create now raises SafeOutputTreeDestinationRefusal for the
refusals of the selection itself: a relative or reserved root, an existing
root, and an accepted existing root that is not an empty directory. The shared
conversion accepts only that type, directly or wrapped by
SafeOutputTreeCreationFailure, so script and environment export no longer
track admission separately.
* fix(evm): reject repeated container keys in Solidity metadata (#6230)
* fix(evm): reject repeated container keys in Solidity metadata
* test(evm): check the repeat-key diagnostic for deeply nested keys
* docs(ci): add a repeatable static analysis example (#6496)
* docs(ci): add a repeatable static analysis example
* fix(ci): isolate example reports and repair the guide link
---------
Co-authored-by: gongfpp <[email protected]>
* fix(setup): retain unresolved environment facts when deriving budgets
* fix(parsers): preserve format evidence and caller input identity
Retain bounded FAT and offset-tagged PE observations through archive hashing,
classification, and content nodes. Preserve PyInstaller member paths according
to the observed carrier dialect and share APK v1 signing-name semantics.
Read SQLite's implicit sqlite_schema table without inventing a stored schema
declaration. Distinguish unsupported ELF numbering and decoded-name admission
from malformed data and fixed scan allowances. Preserve Sigrok basenames and
classify matching explicit input-format refusals at their owning boundary.
Validation: 554 distinct tests passed, 3 skipped across serial focused runs;
build, typecheck/lint, docs, formatting, and discovery (2141 files) passed.
Sigrok command-boundary simulators do not establish real-tool decoding.
Small repeated public PyInstaller CLI runs had equivalent inventory facts;
warm median elapsed 0.5431s before / 0.5426s after, with no speed claim.
Fixes#6733Fixes#6741Fixes#6722Fixes#6732Fixes#6736Fixes#6734Fixes#6764Fixes#6766
Refs #6739
* fix(parsers): respect container facts and real decoding boundaries
Use zip.js's parsed symlink fact for inventory and open refusal. Treat an
unmatched Godot trailer as an unconfirmed candidate while preserving failures
for confirmed malformed packs. Report entropy path-resolution failures at the
selected-input boundary.
Separate Go header discovery from inline string extents in ELF and Mach-O,
and apply the aggregate content allowance after validating real source ranges.
Normalize binary plist UID widths through the existing scalar-copy owner,
budget exact clone growth, preserve source identity, and refuse inexact UIDs.
Validation: 389 distinct tests passed, one Linux /proc test skipped on Darwin;
build, typecheck/lint, docs, single-thread formatting, and discovery (2142 files)
passed. CLI/MCP Go fixtures and public ZIP/Godot/keyed-archive workflows ran.
Real producer prevalence and external provider execution were not established.
Repeated ordinary keyed-archive CLI inventory facts and estimated reply tokens
were equal before/after. Warm median elapsed was 0.5166s / 0.5177s; no speed claim.
Fixes#6751Fixes#6763Fixes#6754Fixes#6767Fixes#6762Fixes#6779
* refactor(mcp): consolidate Evidence recording and operation progress
* fix(setup): reuse URL component admission for Hopper releases
* perf(cli): avoid loading analysis commands for version reports
---------
Co-authored-by: morluto <[email protected]>
Co-authored-by: Hifza Zafar <[email protected]>
Co-authored-by: Can <[email protected]>
Co-authored-by: eco-null <[email protected]>
Co-authored-by: Cassie_Fox <[email protected]>
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Co-authored-by: Wade <[email protected]>
Co-authored-by: RRFZ <[email protected]>
Co-authored-by: pablodiazjorge <[email protected]>
Co-authored-by: Ezra Brilliant <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Co-authored-by: GamerDubz <[email protected]>
Co-authored-by: raidnouredden-ux <[email protected]>
Co-authored-by: 吴佳浩Alben <[email protected]>
Co-authored-by: Abel Yagubyan <[email protected]>
Co-authored-by: Abelo9996 <[email protected]>
Co-authored-by: ShuaixinHuang <[email protected]>
Co-authored-by: Paritosh Lahre <[email protected]>
Co-authored-by: Dery Ferdika <[email protected]>
Co-authored-by: gongfpp <[email protected]>
Co-authored-by: gongfpp <[email protected]>
Preserve boundary facts and uncertainty through their consumers. Add focused production-boundary regressions and update affected documentation.
Fixes#6760
Preserve boundary facts and uncertainty through their consumers. Add focused production-boundary regressions and update affected documentation.
Fixes#6758
Preserve boundary facts and uncertainty through their consumers. Add focused production-boundary regressions and update affected documentation.
Fixes#6756Fixes#6752Fixes#6755
Preserve boundary facts and uncertainty through their consumers. Add focused production-boundary regressions and update affected documentation.
Fixes#6780
Preserve format and Evidence semantics at their owning boundaries, with focused regression coverage and documentation where applicable.
Fixes#6773Fixes#6786
Preserve format and Evidence semantics at their owning boundaries, with focused regression coverage and documentation where applicable.
Fixes#6785Fixes#6714Fixes#6735Fixes#6750
Preserve format and Evidence semantics at their owning boundaries, with focused regression coverage and documentation where applicable.
Fixes#6772Fixes#6787
Use zip.js's parsed symlink fact for inventory and open refusal. Treat an
unmatched Godot trailer as an unconfirmed candidate while preserving failures
for confirmed malformed packs. Report entropy path-resolution failures at the
selected-input boundary.
Separate Go header discovery from inline string extents in ELF and Mach-O,
and apply the aggregate content allowance after validating real source ranges.
Normalize binary plist UID widths through the existing scalar-copy owner,
budget exact clone growth, preserve source identity, and refuse inexact UIDs.
Validation: 389 distinct tests passed, one Linux /proc test skipped on Darwin;
build, typecheck/lint, docs, single-thread formatting, and discovery (2142 files)
passed. CLI/MCP Go fixtures and public ZIP/Godot/keyed-archive workflows ran.
Real producer prevalence and external provider execution were not established.
Repeated ordinary keyed-archive CLI inventory facts and estimated reply tokens
were equal before/after. Warm median elapsed was 0.5166s / 0.5177s; no speed claim.
Fixes#6751Fixes#6763Fixes#6754Fixes#6767Fixes#6762Fixes#6779
Retain bounded FAT and offset-tagged PE observations through archive hashing,
classification, and content nodes. Preserve PyInstaller member paths according
to the observed carrier dialect and share APK v1 signing-name semantics.
Read SQLite's implicit sqlite_schema table without inventing a stored schema
declaration. Distinguish unsupported ELF numbering and decoded-name admission
from malformed data and fixed scan allowances. Preserve Sigrok basenames and
classify matching explicit input-format refusals at their owning boundary.
Validation: 554 distinct tests passed, 3 skipped across serial focused runs;
build, typecheck/lint, docs, formatting, and discovery (2141 files) passed.
Sigrok command-boundary simulators do not establish real-tool decoding.
Small repeated public PyInstaller CLI runs had equivalent inventory facts;
warm median elapsed 0.5431s before / 0.5426s after, with no speed claim.
Fixes#6733Fixes#6741Fixes#6722Fixes#6732Fixes#6736Fixes#6734Fixes#6764Fixes#6766
Refs #6739
* docs(ci): add a repeatable static analysis example
* fix(ci): isolate example reports and repair the guide link
---------
Co-authored-by: gongfpp <[email protected]>
* fix(web): refuse an existing script-export destination as invalid input
export_web_scripts refused an existing output directory or link correctly,
but reported it as artifact_operation_failed / execution_failure with no
field issue or recovery. SafeOutputTree.create raises that refusal as a
path failure before any output tree exists, and the export mapper wrapped
it as an artifact failure.
Attribute pre-admission path refusals to output_directory as an
AnalysisInputError, as environment export already does for its directory.
Both now share one helper owned beside SafeOutputTree; failures after the
tree is admitted keep their artifact semantics.
Fixes#6029
* fix(artifacts): type SafeOutputTree destination refusals
Admission conversion treated any cleanup-free path failure raised before
admission as a refusal of the selected directory, so an acquired-tree setup
failure, such as being unable to restrict a created root to 0700, blamed
valid output_directory or directory input.
SafeOutputTree.create now raises SafeOutputTreeDestinationRefusal for the
refusals of the selection itself: a relative or reserved root, an existing
root, and an accepted existing root that is not an empty directory. The shared
conversion accepts only that type, directly or wrapped by
SafeOutputTreeCreationFailure, so script and environment export no longer
track admission separately.
A projection wider than SQLite's native result-column limit failed with the
generic "too many columns in result set" SQL error. The inspector could not
attribute that error to a generated column, so the worker reported "format"
and the parent blamed the caller's path as invalid_format even though the
selected database was readable.
Classify the refusal from SQLite's own column limit for the private
connection, carry the selected and engine counts through the worker reply,
and project it as a capacity resource constraint. The database stays
readable, no partial rows are returned, and the recovery names the column
counts and splitting the projection across calls.
* fix(doctor): check only the current platform's Hopper launcher candidates
Doctor built one launcher list for every platform, so on macOS it could
report a Linux-only path as a healthy Hopper ahead of a renamed bundle,
and setup then registered that path. Give per-platform automatic
candidates one owner that runtime configuration and doctor both read,
scan renamed macOS bundles and probe the Homebrew cask only on macOS,
and share runtime's renamed-bundle scan with the doctor host.
Fixes#6228
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(doctor): keep the renamed Hopper bundle scan asynchronous
Sharing runtime's synchronous scan made the doctor host read
~/Applications with readdirSync, which blocks the event loop on a slow
or network-mounted home. Give MacHopperPaths an asynchronous scan for
doctor beside runtime's lazy synchronous one, sharing the directory
filter and candidate builder, and check that both return the same
candidates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(doctor): model renamed launchers with runtime's POSIX joins
Since #5235, runtime builds macOS launcher paths with posix.join, so on
Windows the discovery table's native-join renamed launcher no longer
matched runtime's candidate. Model launchers with posix.join and keep
filesystem setup native, as MacHopperPaths.test.ts does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(apple): report a read-denied selected archive as host access denial
inspect-keyed-archive reported a host read refusal on the caller-selected bundle entry as artifact_operation_failed/execution_failure with reason io, leaving EACCES or EPERM only inside message text. The caller selected that entry, so the refusal is a selection outcome with its own typed classification, system code and read-access recovery.
DirectoryArtifactReader now marks a refused entry open with DirectoryArtifactEntryAccessDenied, which retains the host system code and the selected entry path. KeyedArchiveReader, which owns the caller's selection, maps it to the shared AnalysisAccessDeniedError, and artifact failure translation keeps that typed error instead of replacing it with a generic io failure. Other readers and other reader failures are unchanged; every existing reason keeps its current projection.
Adds the access_denied projection coverage plus a real host refusal through the production reader and a non-permission control.
* fix(apple): carry cleanup ownership and cover raw refusals for a denied selection
Addresses the two review findings on the denied-selection classification:
- The typed host access denial now carries the reader's cleanup observation, so a denied open that left a resource owned does not project as fully cleaned.
- Adds coverage that starts from raw host EACCES/EPERM/EIO refusals with the real DirectoryArtifactReader open path, so the marker itself is exercised rather than constructed by the test. EIO stays an artifact failure.
Centralize run-token extraction at the environment observation boundary and require an own field. Build decoded OS environment records without host prototypes while retaining source names, empty values and last-assignment precedence.
Reject inherited matching tokens before cleanup signaling, preserve normal owned-group behavior, and verify live Linux process environments alongside native CLI/MCP capture workflows.
Normalize parsed facts at their owning boundaries across PE exports and certificates, ELF section names, GPU bundles, IL2CPP generic types, dyld slice selection, source maps, environment replay, and JavaScript mapped arguments. Preserve partial facts and explicit unsupported cases; clarify the corresponding documentation.
Validation: 831 affected tests passed with one existing skip across 23 files; build, typecheck/lint, documentation, formatting, and 2133-file test discovery passed. Format controls are synthetic; real Unity, GPU, and ARM-device execution is unverified.
Fixes#6709Fixes#6688Fixes#6719Fixes#6718Fixes#6704Fixes#6703Fixes#6692Fixes#6723Fixes#6687Fixes#6726Fixes#6696Fixes#6691Fixes#6727
Build frozen package field snapshots with a null prototype and restrict reference-source main lookup to own properties. Preserve declared null and empty values, index fallback, source identity and unavailable manifest evidence.
Compare inherited main and exports lookup with the native Node resolver, retain package observation coverage and verify CLI/MCP package workflows.
Project completed values and cancellation from the descriptor owner once across artifact consumers. Preserve completed void outcomes and format-specific validation and cleanup policies.
Keep later aborts from replacing captured APK and SQLite close failures, and retain the exact APK cancellation reason. Exercise real descriptors, public workflows and cleanup retries.
Decode VARARG sentinels and allow omitted empty blob heaps while retaining failures for referenced missing data. Compare field and catch targets using owning parsed facts instead of build-local tokens.
Fixes#6720Fixes#6686Fixes#6713Fixes#6712
Share partial observation ownership across GGUF and Safetensors. Preserve completed header facts through cancellation and MCP error delivery without claiming finalized digests or file identity.
Fixes#6684
Borrow sealed reports only when they match the parsed facts; otherwise cache the owned normalized snapshot. Keep historical feature omissions unknown through saved bundles and repeated retained pages without changing the source Evidence.
Fixes#6689
Model GDB console completion, use real directory reads and cleanup for late-enumeration failures, and distinguish supported PTX pointer qualifiers from unknown declarations. Preserve original assertion and cleanup failures through scoped test finalizers.
Reuse validated class pages for inline pagination audits while retaining complete provider replies. Move retention into the shared Android workflow and remove the MCP-only service wrapper and application text reparser. Preserve unknown JSON metadata, late cancellation and retention failures.
Share bounded UTF-8 and UTF-16 JSON measurements across state serving, write retention and source-map admission. Remove duplicate counters and distinguish retained state estimates from encoded bytes while preserving limits and exact refusal diagnostics.
Keep dependency execution in read-only release jobs and validate the original qualified archive with Node built-ins before publication. Refresh registry state on every publisher attempt; retain exact bytes/channel verification, v1 read-only recovery, and installed CLI/MCP checks before MCP publication.
Static/docs checks, artifact/registry/recovery tests and real package/rehearsal verification pass. All four hosted package platforms pass. Portable CI retains the independently reproduced main PTX failure in #6675 and fail-fast missing coverage; use the owner-authorized existing PR-only exception, with limitations documented in the PR. Refs #6480.
Update Vite/PostCSS and refresh the lock-derived inventory while retaining TSRS platform selection and runtime dependency versions.
Source/static checks, installed-platform signatures, full/production audits, real CLI/MCP workflows and all four package platforms pass. Portable CI retains the independently reproduced main PTX fixture failure tracked in #6675; use the owner-authorized existing PR-only baseline exception. Refs #6480.
Use distinct row contracts for imports and exports, retaining ambiguous names and historical import defaults without adding import-only fields to export pages. Verify the SDK-advertised schemas and both facets through MCP.
Keep the exact producer expression and report unknown symbol identity when chained-import text cannot distinguish a binding addend from a literal name suffix.
Fixes#6648
BREAKING CHANGE: inspect_macho import names can be null when identity is ambiguous. Use raw_name for the observed expression and name_status to distinguish it from an exact symbol name.
Route legacy nested import-expression handling through the same exact-literal owner as dynamic imports, removing a duplicate string-only check.
Fixes#6654
Reject duplicate MethodDef row identities within one owning image before publishing native addresses or Ghidra seeds, while retaining cross-image reuse and folded code addresses.
Fixes#6610
Preserve the observed native format when comparing module identities so ELF case-only differences remain mismatches while unknown platform behavior stays unresolved.
Fixes#6652
Route exported external import-equals declarations through the same relationship owner as plain declarations so their required target is not dropped.
Fixes#6655
Use the shared exact-literal reader for dynamic imports, preserving expression-free templates and transparent TypeScript wrappers while leaving computed targets unknown.
Fixes#6654
Mark directories with non-UTF-8 names unsupported without following replacement-character paths, while retaining readable sibling observations.
Fixes#6653
Classify known resource paths once and keep a raw resource named module.json from changing the package model or appearing as a manifest. Add a filesystem-boundary regression and clarify the documented hint.
Fixes#6638
Select conventional Linux and macOS game data files for the existing bounded FORM/GEN8 probe. Add service-boundary regressions and document the selected names.
Fixes#6659
Preserve the caller's AbortSignal reason when BEAM inspection is cancelled before provider admission. Keep the production filesystem boundary and late-completion contracts covered.
Fixes#6658
After a running result, queue a tokenized read-only MI query to establish that GDB has returned to its command loop. Keep collecting through intermediate inferior stops and retain the query reply as protocol Evidence.
Fixes#6660
Queued console followers have not dispatched MI records. Use active protocol requests when deciding whether the admitted close can verify and detach its inferior.
Fixes#6661
Use the same parsed method/field operand predicate when producing member edges and comparing call and field shapes. Exclude ldtoken and operands without a value from both interpretations.
Remove the APK reader's pass-through children and content projections so format consumers read the shared BER owner's nodes directly. Keep only the encoded-span helper needed for exact issuer and certificate bytes, and describe CMS BER as this parser's structural owner.
Clone caller-scoped call and field facts with the fixture methods. Keep known CIL changes alongside unavailable target metadata in the missing-row controls.
Compare stable producer-tagged declarations independently of materialized payloads. Preserve missing facts as unknown without applying PyInstaller rules to foreign metadata or nested container entries.
Fixes#6484Fixes#6485
Compare aligned managed call edges by their reported declaring type and method name, keeping missing target facts unknown without comparing build-local tokens. Document the static comparison limits.
Fixes#6630
Keep known STREAM ranges and the unknown FIN value, while listing the source event among incomplete fact records so summary and retained views agree.
Fixes#6598
TShark resolves selected field aliases before writing packet JSON. Keep the requested selector in Evidence while accepting the canonical field key produced for it.
Fixes#6595
Decode observed V2 CUDA parameters, retain qualified PTX parameter names and entry resource counts, and preserve family and feature target declarations through selection and consistency checks.
Fixes#6624Fixes#6625Fixes#6626Fixes#6618Fixes#6613Fixes#6606
Accumulate length octets arithmetically so a four-octet length with bit 31 set remains a positive unsigned value and is rejected by the containing-range check rather than becoming a negative length.
Recognize the exact selected-channel diagnostic even when sigrok-cli exits successfully, while preserving partial producer output in the invalid-input details. Cover both public JSON adapters and add the missing-channel case to the Linux real-tool verifier.
Fixes#6631
Share the bounded BER tree reader between PE certificates and APK v1 CMS, then accept constructed OCTET STRING signature values while retaining the declared signer and certificate facts. Embedded X.509 certificate decoding keeps its DER requirement.
Fixes#6603
Keep unsigned integer declarations exact until their owning field is interpreted. Bound actual offsets at conversion and validate shape products in declaration order, preserving large empty tensor shapes without hiding an earlier overflow.
Fixes#6627
Derive exported Docker image versions from .nvmrc through generated package metadata. Align active setup translations with patched Node release floors and verify actual CLI exports, real Docker/agentenv, package and website workflows.
Eight built-test shards pass; three unrelated managed source failures reproduce on clean main and remain tracked in #6650.
Refs #6480
Co-authored-by: gensokyo-dev <[email protected]>
Preserve renamed-executable identity, complete-line readiness and bounded child lifetimes. Use genuinely malformed Inspector input and explicit ADB file-probe/push/hash facts while preserving cancellation, cleanup, Evidence and completeness assertions. Adopt current main Go fixtures and retain malformed COFF coverage.
Fixes#6556
Refs #6480
Keep signed co_stacksize and co_flags for historical magic entries whose constructors accepted them. Preserve magic 3410 with a constructor-revision limitation; reject negative values from 3411 onward.
Fixes#6487
Use shared owned-file and directory primitives throughout Wakaru discovery,
snapshots, report parsing and publication. Preserve primary read failures and
completed fingerprints when final close fails or receives cancellation.
Retain exact cleanup capabilities before retiring workspaces, block new work
while cleanup is unresolved, and bind error status to the aggregate owner so
partial cleanup and later retry keep resource attribution current.
On Windows, the pinned Unblob commit cannot be checked out because 11
upstream fixture paths under tests/integration/ are invalid on NTFS.
Document the verified sparse-checkout workaround and point Windows
contributors to it from CONTRIBUTING.
Claude-Session: https://claude.ai/code/session_01LjhoPGpqAwHwcCgectzRsp
Co-authored-by: Claude Opus 5.5 <[email protected]>
Copy recordings under a private filename that retains the caller-selected final suffix, and pass that filename to sigrok. This restores filename-based autodetection without exposing the source directory.
Fixes#6580
Keep aliases on selected paths at clear text delimiters so a longer sibling filename is not rewritten as the executable. Version the normalization contract and cover source and actual PTY behavior.
Fixes#6586
Require a time-field token boundary before treating numeric values as epoch clocks, so identifiers such as media format codes remain literal. Clarify recognized field forms and clock offset and precision semantics in the fidelity guide.
Fixes#6551
Reject invalid ISO offsets while preserving valid boundary values. Validate stored clock-format offsets through the same parser so malformed imported formats cannot reach rendering.
Fixes#6524
Publish alignment from the parser when explicit metadata validates it, or when metadata completion establishes the default. Preserve that fact through later parsing and hashing failures.
Fixes#6579
* fix(python): reject code keys and set members with unhashable consts
* fix(python): refuse to hash a tuple that is still being read
* fix(python): hash a code object's co_consts tuple, not its item list
---------
Co-authored-by: morluto <[email protected]>
* fix(flutter): honor ELF note alignment when reading build IDs
* test(flutter): key shared note ranges by effective alignment
Cover a program and section declaration that share one note range: differing effective alignments (8 and 4) must both be read, while declarations that both normalize to four bytes (1 and 4) still share one read. 24-byte build IDs pad identically at both alignments, so only the range key decides the read count.
The sparse-child capture test asserted the frame bound with a bare boolean,
so a failure only printed "expected false to be true". The assertion now
carries the running progress details (elapsed time, frames, samples,
interactions) as its failure message. This only adds diagnostics to a
failure; it does not change what passes and does not fix the flake.
Refs #5889.
🤖 Generated with Claude Code (Sonnet 5.5) 🦊
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Reserve equal global ID pairs and stop normalizing contested pairings. Keep numeric measurements literal, scope keyed collection identities through learning and comparison, and normalize string references only where learning established the same field as an ID.
Fixes#6565Fixes#6550Fixes#6557
Keep JSON, text, and retained bytes distinct through fidelity comparison. Compare binary bodies by bytes, including valid UTF-8 bytes against equivalent text, and keep empty-body compatibility explicit.
Fixes#6564
Project PYZ namespace packages as metadata so their query paths do not claim output directories. This lets independent CArchive files retain the same logical path and remain inspectable and extractable.
Fixes#6539
Parse complete nonzero Activity Manager reports while preserving other command failures and their captured output. Admit dollar signs only in the activity component name for nested Android classes.
Fixes#6528Fixes#6527
Share expected and observed check construction across MZ, COM, and raw-binary attestations. Preserve format-specific rules and source coordinates, and derive mapping callback types from the recording owner.
Centralize parse-wide identity and coverage facts while keeping conservative admission values separate from measured rows and counters. Reuse the derived truncation reason and retain the final serialization bound.
Tighten the report-budget regression so unexpected parser finalization errors cannot be swallowed by its budget search.
Carry caller messages through the existing schema issue owner instead of constructing and discarding an intermediate input error. Remove forwarding schema helpers and migrate equivalent constructors across investigation, firmware, dylib, process, and screenshot workflows while preserving diagnostic paths and causes.
Validate canonical dimension ownership, summary counts, and exact changed or unknown projections before investigation consumers publish derived Evidence. Reuse the shared dimension contract in selectors and keep historical source fixtures internally consistent.
Verify authentic malformed comparisons across all three MCP consumers while preserving valid reordered and unknown results and retained source Evidence.
* fix(javascript): report recovery inputs that collide on a case-insensitive filesystem
* test(javascript): cover a recovery input selected twice on any filesystem
* test(javascript): cover snapshot collision causes and unrelated write failures
Require Node.js ^22.23.4 || ^24.22.1 || >=26.12.1 across package engines, setup/doctor and the standalone installer. Validate patch boundaries and maintain runtime-independent listener identity, heap-option and HTTP schema-binding tests.
Closes#6283
Refs #6480
BREAKING CHANGE: Node versions below 22.23.4, 24.22.1 and 26.12.1 are no longer supported. Upgrade Node before installing or updating REA; the installer does not upgrade Node.
Co-authored-by: gensokyo-dev <[email protected]>
* fix(lifecycle): reject session work once close has started
Process shutdown closed the shared binary session without sealing it, so a
stdio tool call could open a provider after that close and leave the child
running. GDB ran a queued console command or a second close after the session
had started closing because the queued turn did not repeat admission checks.
Shutdown now refuses later target acquisition, and a queued GDB command or
duplicate close fails as unavailable once its turn finds the session closing
or gone.
Co-authored-by: morluto <[email protected]>
* fix(gdb): block console admission after cleanup starts
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
The EVM interface failure translator grouped the stable reader's input
limit with path and format refusals, so a carrier over the fixed 4 MiB
input allowance surfaced as a non-retryable invalid_request whose only
record of the allowance was issue text pointing at the input schema.
The same refusal now returns a non-retryable resource_constraint with
resource file-size and reported_limits.maximum_input_bytes, following
the WABT input translator, while path and format reader refusals stay
input errors. The reply-size write failure keeps its own branch.
Fixes#6053
capture-process recorded exit.code 0 for a target a scenario event killed
with SIGKILL or SIGTERM, beside the nonzero exit.signal. The result builder
and the retained observation each kept any nonnegative PTY exit code for an
exited capture without consulting the signal; only finalization checked it.
Project the exit once in captureExitObservation, whose normal exit code is
null when a nonzero signal ended the target, and use the same rule for
finalization. Normal exits keep their codes, including 0 and 3, and deadline
exits remain null.
Fixes#6445
`mcp doctor --format` and `--format=` reported VALIDATION_ERROR
"Invalid output format: missing", although docs/cli.md documents
MISSING_OUTPUT_VALUE for valued output flags without a value and
`capabilities` already returns it. Classify absent and empty values as
missing, share the existing missing-value message, and keep
VALIDATION_ERROR for a present but unsupported format.
Fixes#6455
Claude-Session: https://claude.ai/code/session_01LjhoPGpqAwHwcCgectzRsp
Co-authored-by: Claude Opus 5.5 <[email protected]>
Check the ARM/Thumb success marker with the runner-provided grep -F. The real Ghidra probe succeeded, but the previous rg dependency made the lane exit 127.
Validated with the actual workflow shell, producer-failure and missing/incorrect marker controls, 17 focused tests, and hosted Ghidra 12.1.4 / JDK 21 compilation and ARM/Thumb context checks.
Inject interrupts through imported projection and Evidence resolution boundaries so lexical service calls cannot bypass the test. Preserve exact normalized partial results, authenticated Evidence provenance and signal exit codes.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Require parsed seed facts in the pure trace projection and leave raw request defaults at the shared workflow boundary. Remove the redundant seed parse, unused raw-seed alias and duplicate seed-match binding.
Move omitted-versus-explicit defaults and full Evidence identity checks to compiled CLI/MCP acceptance. Reuse the owned MCP fixture and request files in the tests. Matching, native handoff, empty-literal, coverage and immutability checks remain covered; public schemas and successful outputs are unchanged.
Return expected source and semantic failures from the runtime input owner as tagged Results. Bind source schema validation to concrete schemas, preserve nested active-capture paths, and let unexpected interpretation and derivation failures reach the adapter boundary.
Remove broad protocol-error wrappers, duplicate active identity validation, and redundant JSON result parsing. Migrate parsed-fact consumers and retain CLI/MCP contracts. Relevant source and public-boundary tests pass; six injected implementation-fault controls fail on the prior revision. Live Electron behavior was not exercised.
Route CLI and MCP coverage requests through the shared strict workflow boundary before authenticating source Evidence. Preserve canonical advertised schemas, use integrity errors for malformed source bundles, and use the workflow operation for input diagnostics.
Remove the unused throwing domain facade and keep semantic fixture assertions on the parsed Result APIs. Validate compiled CLI and stdio MCP parity, strict request rejection, domain authority/time semantics, generated schemas, and unexpected failure propagation. No external providers execute in these checks.
Separate source corruption and selected-role errors from unexpected derivation failures. Normalize coordinates before body membership and conflict checks, reuse parsed requests, and account for outer observation arrays before validation copies.
Authenticate inventory requests once before family projection, remove obsolete schema aliases and raw facades, and preserve unresolved occurrence references from partial pages without fabricating content identities.
* fix(reconstruction): dedupe residual unknown ids with uniqueSorted
Current main calls uniqueBy, which is not defined, so typecheck fails.
Residual unknown ids are strings, and the adjacent lists already use
uniqueSorted.
Co-authored-by: morluto <[email protected]>
* feat(config): make the bridge request ceiling configurable
REA_BRIDGE_MAX_REQUEST_BYTES replaces the hard-coded 16 MiB Hopper and
Cutter request payload ceiling. The default stays 16777216 bytes. Values
outside 7464 to 268435456 bytes are configuration_invalid.
Part of #6176.
Co-authored-by: morluto <[email protected]>
* fix(cutter): keep cleanup ownership when a sent command's reply is oversized
An authenticated command may already have changed the Cutter document
when its reply exceeds the frame ceiling. Return the existing unknown
execution, with the ceiling diagnostic in the message, so receipt
cleanup still waits. A frame that overflows before the command is sent
remains a resource constraint.
Co-authored-by: morluto <[email protected]>
* style: format the bridge request ceiling sources
oxfmt --check failed on the five files that set and test
REA_BRIDGE_MAX_REQUEST_BYTES. Formatting only; an oversized Cutter
reply still stays an unknown execution.
Co-authored-by: morluto <[email protected]>
* fix(cutter): exclude the LF from the response frame budget
REA_BRIDGE_MAX_REQUEST_BYTES counts payload bytes. The Cutter response
reader and authenticated encoder were reserving one byte for the
terminating LF, so an exact-limit response was rejected. A reply that
exceeds the ceiling after the command was sent still stays an unknown
execution.
Co-authored-by: morluto <[email protected]>
* fix(bridge): measure escaped JSON before allocating the request
A command of U+0001 characters can expand sixfold. JSON.stringify of
that expansion can throw past Node's string ceiling before the payload
check. Count the escaped size within REA_BRIDGE_MAX_REQUEST_BYTES
first and refuse it as a resource constraint.
Co-authored-by: morluto <[email protected]>
* fix(config): derive the bridge request maximum from JSON expansion
A 256 MiB ceiling still lets a shorter U+0001 command expand past Node's
536870888-character string limit. The maximum is now
floor((536870888 - 296) / 7) = 76695798, the longest such command whose
authenticated frame fits in one string. Requests are still measured
before JSON.stringify.
Co-authored-by: morluto <[email protected]>
* fix(bridge): size the Cutter frame deadline and send the measured Hopper payload
The authenticated Cutter command is encoded after the challenge, so a
one-second read rejected an in-budget frame. That read now allows one
second per mebibyte of the ceiling and never waits longer than 25
seconds. Hello stays at one second. Hopper writes the payload it already
measured instead of serializing the same request again.
Co-authored-by: morluto <[email protected]>
* fix(config): bound the bridge request maximum by the serialized string
The ceiling already measures escaped JSON, so dividing Node's string limit by the seven-byte expansion rejected encoded payloads that still fit. The maximum is 536870887, one byte under that string ceiling, leaving room for the framing LF.
Co-authored-by: morluto <[email protected]>
* fix(hopper): serialize a bridge request when it reaches the wire
The queue kept each encoded frame, plus the caller's params, until Hopper finished the active request. A raised ceiling made a few waiting calls enough to exhaust the heap. The FIFO now keeps the params, and the frame is encoded only for the request being written.
Co-authored-by: morluto <[email protected]>
* fix(cutter): hash the bridge proof prefix and payload separately
An exact-limit Cutter frame plus the HMAC prefix exceeds Node's single-string ceiling, so the client threw before sending an admitted command. The prefix and payload are now separate HMAC updates, including the response proof.
Co-authored-by: morluto <[email protected]>
* fix(cutter): keep recovery fields when a status path exceeds the frame
A long current_file made the status reply miss the ceiling, and the fallback then dropped session_id and the completion receipts. The status now omits the path and version and still returns the session, connection proof, and up to 64 receipts. That required recovery frame is smaller than the 7464 floor.
Co-authored-by: morluto <[email protected]>
* fix(cutter): report an unknown path when status identity does not fit
A bounded status kept the new document generation and omitted the path, so the client filled in the descriptor's older path. The observed path is sent when it fits. When it does not, current_file is null and is not replaced with that stale path.
Co-authored-by: morluto <[email protected]>
* fix(cutter): count structured replies by their encoded JSON size
The response guard charged 16 bytes per primitive and two bytes per list slot, so a dense array under the configured ceiling was replaced with output_truncated. Numbers, booleans, null, and container punctuation now use their JSON lengths.
Co-authored-by: morluto <[email protected]>
* fix(cutter): stream authenticated responses in bounded chunks
A fitting reply was signed from retained raw bytes, escaped bytes, and a
joined frame, so a ceiling near 512 MiB could exhaust Cutter before send.
Measure and sign JSON pieces, then write the same frame in 64 KiB chunks.
Co-authored-by: morluto <[email protected]>
* fix(bridge): keep Cutter admission from stalling or hiding sessions
Read an unauthenticated frame off the accept loop. A command reply that
does not fit keeps completion and generation and reports an oversized path
as null. Discovery reports a ceiling mismatch instead of an absent session.
The maximum HMAC proof is opt-in, and both bridges reject non-ASCII digits.
Co-authored-by: morluto <[email protected]>
* fix(cutter): bound frame scans and client response copies
Search only newly read bytes for the framing LF. Unescape each socket
chunk into one response string, check the proof, and then parse it.
Co-authored-by: morluto <[email protected]>
* fix(cutter): keep completion, admission, and dense replies bounded
Report completed_connection only through connections that have
terminated. Cap connection threads and keep the listener up if one
fails to start. Batch scalar JSON and fail discovery when any ceiling
mismatches.
Co-authored-by: morluto <[email protected]>
* fix(cutter): hash requests in parts and scan escapes once
Update the client-proof prefix and request separately. Stop the response
decoder at the first quote or backslash instead of rescanning the tail.
Co-authored-by: morluto <[email protected]>
* fix(cutter): confirm live ceiling mismatches and omitted output
A stale descriptor from a previous ceiling is not a discovery mismatch
until its hello proves a live authenticated bridge. A response discarded
for the frame ceiling after dispatch reports output_truncated.
Co-authored-by: morluto <[email protected]>
* test(cutter): close the unauthenticated ceiling-mismatch fixture
The listener that fails the hello proof is destroyed after its challenge
so discovery cleanup does not wait on that socket.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
The registry fixture sent response headers before reading the upstream body,
then always wrote a 500 on error. A body failure hit ERR_HTTP_HEADERS_SENT,
which hid the real error and crashed the verifier with its helper still running.
The fixture now reads the body first, destroys a response that already started,
and the verifier rethrows the first fixture error.
Refs #6038.
🤖 Generated with Claude Code (Sonnet 5.5) 🦊
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
fix(managed): retain types after member limits
Admit type identity and a bounded recovery envelope before member decoding so callers can discover smaller selections after capacity exhaustion.
Fixes#5146
Co-authored-by: eco-null <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(native): preserve whitespace in otool load-command strings
otool prints dylib install names and rpaths as `<key> %s (offset %u)`
with exactly one separator space around the recorded string. The parser
trimmed the value and stripped the offset suffix with `\s+`, so a valid
install name such as " leading.dylib " became "leading.dylib" in both
the dependency inventory and the raw load-command fields.
Parse these offset-suffixed string fields by their exact format and
strip only the single-space offset suffix.
Fixes#3384
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(native): verify exact Mach-O install names through CLI and MCP
---------
Co-authored-by: Bhaaraadwaj <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
`start_xvfb` treated `--display-number 0` as omitted because it used the
option's truthiness, so an explicit `:0` was replaced by a random display
and its collision check ran against that random display instead.
Select the random display only when the option is absent, and add a
fixture-backed regression test covering explicit zero, an occupied
explicit zero, explicit one, and omission.
Fixes#5189
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(scripts): remove the ADB verifier's pushed probe file from the device
The verifier pushed `/data/local/tmp/rea-verify-<timestamp>.txt` for its
transfer roundtrip but never deleted it, so every successful run left a
probe behind while reporting that all checks passed.
After a successful no-overwrite push, the verifier now removes that exact
destination with the provider-reported adb client and confirms its absence
through `list_adb_directory`. Removal runs even when the pull fails, and a
removal failure is reported as a failed check instead of a pass.
`pull_adb_file` still retains device originals.
Fixes#5190
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(scripts): cover ADB probe cleanup after pull failure and leftover probes
Add verifier cases where the pull returns an error or throws after a
successful push, and where `adb shell rm` exits 0 but the listing still
shows the probe. Pull failures must still remove the probe, and a leftover
probe must fail the run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(setup): keep client and skill setup when the Hopper install fails
A failed Hopper package install returned from runSetup before any client
registration or skill install ran, so one optional provider's package
conflict (zlib-ng-compat on Arch and CachyOS) cost every other approved
change. Record the failure instead, apply the remaining plan without a
Hopper launcher, and report needs_human with the install's code and
remediation.
Fixes#5113
* fix(setup): keep the Hopper failure when a client write or the skill install fails too
* fix(setup): keep client and skill setup when the Hopper install fails
A failed Hopper package install returned from runSetup before any client
registration or skill install ran, so one optional provider's package
conflict (zlib-ng-compat on Arch and CachyOS) cost every other approved
change. Record the failure instead, apply the remaining plan without a
Hopper launcher, and report needs_human with the install's code and
remediation.
Fixes#5113
* fix(setup): keep the Hopper failure when a client write or the skill install fails too
---------
Co-authored-by: morluto <[email protected]>
* fix(apple): classify keyed-archive objects by declared superclasses
Interface Builder keyed archives record each object's superclass chain
in `$classes`, but the decoder classified objects by class-name
substrings alone. UINavigationController, UITabBarController and
UISearchController contain "control" but not "viewcontroller", so they
were reported as controls.
Classify keyed-archive objects by UIKit/AppKit view-controller, control
and view bases in their declared chain first, falling back to the name
heuristic only when the chain names none. The decoder now reads
`$classes` through one helper shared with NSArray detection.
Fixes#5218
* test(apple): cover each declared keyed-archive base with neutral class names
Neutral names classify only through the archived superclass chain, so each
UIKit and AppKit base entry and the control-before-view ordering now has a
case that fails without it.
* perf(apple): parse each keyed-archive class chain once
Objects sharing one class descriptor reparsed and scanned its whole
`$classes` array per object, so 20,000 objects sharing a 20,000-name chain
took about 16 seconds to decode. Cache each descriptor's chain as a set by
archive UID; the same archive decodes in about 3.5 seconds, matching main.
---------
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Pin admitted script identities and metadata before selected source reads. After the final CDP read, count unique additional retained IDs as dropped script inventory so incomplete captures stay unknown without extending source reads or inventing budget failures. Cover producer and comparison behavior through registered MCP tools.
Co-authored-by: gongfpp <[email protected]>
* feat(firmware): attribute hardware MMIO peripheral memory regions in native disassembly
- introduce hardwareMemoryMap domain model supporting standard ARM Cortex-M, RISC-V, MIPS, x86, and ARM64 physical memory region classifications (Code, SRAM, Peripheral MMIO, System Control Space, External Device)
- distinguish RAM/ROM and address-space segments from MMIO (isolate ARM External RAM / Device and MIPS kseg0/kseg1 uncached mirrors and boot ROM as non-MMIO)
- add hardwarePeripheralAttribution to map analyzed instruction references to hardware peripheral blocks and summarize peripheral register interaction profiles with numerical offset ordering
- expand ReaGhidraBridge.java decoder_context to capture ARM TMode execution register alongside MIPS ISA_MODE for native instruction inspection
- add focused unit and boundary tests across hardwareMemoryMap.test.ts, hardwarePeripheralAttribution.test.ts, GhidraFunctionValues.test.ts, and ghidraCompactReferencesMcp.test.ts verifying region bounds, MMIO isolation, numerical sorting, 32-bit/64-bit overflow rejection, ARM TMode MCP preservation, and bridge contract bindings
- make fake Ghidra installation host platform-aware on Windows in GhidraLanguageOverride.test.ts
* test(ghidra): inspect ARM and Thumb context in the Java bridge
---------
Co-authored-by: morluto <[email protected]>
* fix(hopper): keep macOS and Linux launcher paths POSIX
* fix(hopper): keep Linux and macOS bundle paths POSIX
Linux package archives and macOS app-bundle launcher paths describe the target host, not the host running the test. Build them with path.posix so exercising these installers from Windows produces the same paths as the target platform.
* fix(electron): follow Windows symlink targets with native separators
readlink returns backslash-separated targets on Windows. Splitting only on '/' kept a whole relative target as one component, so nested links were not followed and a relative target could pass the framework-root check without being walked. Split on both separators on Windows and add a regression for an escaping target.
* test(windows): align host-specific path and lifecycle expectations
Compare generated skill and provider-command paths with native joins, bind ADB device-path validation to an explicit platform, and prepare simulated POSIX Ghidra install roots on Windows. Skip POSIX-only Hopper lease, permission-bit, and symlink-parent cases, assert Windows' unsupported Hopper state instead, and cover the Windows Ghidra native-source admission step before snapshot replay. Launch Node child seams with SystemRoot available and expect Windows close-failure resource retention.
* test(windows): close review coverage gaps
---------
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(native): admit universal Mach-O files without a host slice
FAT Mach-O admission selected only the host's slice and rejected a valid
container whose slices are all foreign, so list-architectures returned
target_unavailable instead of the container's inventory. A thin foreign
image was already admitted.
Prefer the host's slice and otherwise select the first declared supported
slice. Remove the Ghidra installation probe's re-parse under other host
architectures, which existed only to work around the rejection.
Fixes#5404
* fix(native): select the first valid foreign FAT slice
Without a host slice, FAT selection took the first declared slice even when
it was too short for its Mach-O header, rejecting a container whose later
foreign slice is valid. The Ghidra decompiler probe then reported such a
foreign-only decompiler as unknown rather than incompatible. Select the first
foreign slice that can hold its header; a malformed host slice still fails.
* fix(native): select foreign FAT slices from every declaration
Foreign-slice fallback chose among architecture families after keeping only
each family's first declaration, so an undersized first arm64 subtype hid a
valid later arm64 subtype. Select from all declarations, and report the
deduplicated families separately. The host-slice rule is unchanged. Move the
FAT selection tests into their own block.
* fix(native): admit only unambiguous foreign FAT families
With no host slice, a family declared more than once, such as arm64 and
arm64e, has no provider-neutral choice: IDA analyzes the recorded slice while
Hopper prefers its own subtype. Select only families declared exactly once and
keep ambiguous foreign-only containers rejected, as on main.
* fix(ghidra): decide FAT decompiler compatibility from validated families
Slice selection now refuses a foreign-only FAT table that declares one family
more than once, such as arm64 and arm64e, and the decompiler probe reported that
refusal as unknown, so an x64 host accepted an arm64-only decompiler. When
selection fails, read the table's validated CPU families and report the
decompiler incompatible when they exclude the host. Selection and host
exclusion now have separate owners in binaryTarget.ts.
Authenticate source bundles through their shared owner and retain workspace lookup facts for evaluation. Preserve source integrity failures and precise caller constraints while allowing unexpected generated-output failures to propagate. Carry the typed closure result through the service and CLI without redundant JSON validation.
Validation: 242 focused tests, source checks, docs conformance, test discovery and duplication gate passed. Six public SDK regressions fail on the baseline. No analysis provider execution is claimed for the coverage workflow.
* fix(native): admit thin arm64_32 Mach-O targets for static inspection
Target admission mapped Mach-O CPU types only to x86, x86_64, arm and
arm64, so a thin arm64_32 object was rejected as "unsupported Mach-O
architecture" before list-architectures, inspect-macho or
inspect-signature could run, although the same slice inside a FAT file
was inventoried.
Recognize CPU_TYPE_ARM64_32 as its own Mach-O-only architecture with a
dedicated thin target variant, so ELF, PE and raw-binary paths cannot
carry it. Add its slice policy so 32-bit dispatch metadata refuses it
like other 32-bit Mach-O images instead of reporting a CPU mismatch.
Ghidra, Hopper and IDA have no verified arm64_32 lane and decline it
explicitly; Hopper's declined families now share one predicate.
Fixes#5713
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* ci(apple): run the thin arm64_32 CLI regression on macOS
The regression compiles an arm64_32 object with Apple clang, so it skips
on the Ubuntu jobs. List it in the macOS Apple artifact job and its
change scope so the job runs it and changes to it select that job.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(mcp): report Linux-only integrations as unsupported_host off their hosts
Session discovery folded the host check into each optional integration's
enabled flag, so on macOS or Windows the Linux-only packet, signal,
firmware, pwntools, EVM and Wakaru tools reported provider_missing.
Their execution guards refuse those hosts whatever is configured, so
installing a provider there could never make them available.
The server now records, per integration, when the host is outside its
verified host and no service is injected. Discovery checks that before
configuration and reports unsupported_host with the existing
remediation. Injected services and supported hosts behave as before.
Fixes#5730
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* refactor(mcp): share provider host checks with session discovery
Discovery restated each Linux-only integration's host rule in
createServer while its provider kept a separate execution check, so the
two could drift apart again. Each provider now exports its host
predicate, uses it in its execution guard, and composition hands the
same predicate to createServer for both the host restriction and the
enabled flag.
Observed host restrictions now also apply when an embedder supplies its
own availability policy, as provider load failures already do, because
no policy lets the default providers pass their host checks.
The MCP regression moves to tests/process-global and emulates darwin
arm64, linux arm64 and linux x64 through the real createServer, so CI on
Linux x64 exercises the unsupported-host branch. It also covers a
configured policy and the matching execution refusals.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(mcp): keep caller host restrictions beside observed ones
The observed host restriction replaced a configured policy's whole
hostUnsupported map. For an injected service the observed value is
false, so a caller's own restriction was lost and the tool was reported
available. Combine both per integration: either the caller or the
observed host can mark it unsupported.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(pcap): expect unsupported_host for packet tools off Linux
The host-independent PATH case asserted provider_missing on every host.
Off Linux the packet tools are unavailable because of the host, so
discovery now reports unsupported_host there; Linux still reports
provider_missing for an unusable PATH.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(reconstruction): dedupe residual unknown ids with uniqueSorted
Merged obligation candidates called uniqueBy, which is not defined, so
the project did not typecheck. String ids already use uniqueSorted.
Co-authored-by: morluto <[email protected]>
* docs(limits): describe how to design admission recovery
Add a short contributor guide for named defaults, settings, recovery
text, and the agent recovery ladder, and mirror that ladder into the
authored skill.
Co-authored-by: morluto <[email protected]>
* fix(hopper): pass distribution readers into install-host tests
systemLinuxHopperInstallHost now requires the caller's distribution
reader. The Linux install tests still passed only the environment, so
typecheck failed.
Co-authored-by: morluto <[email protected]>
* docs(limits): name the parser doctor shares with startup
Co-authored-by: morluto <[email protected]>
* test: align pcap, storage, and regex fixtures with current contracts
Source and portable CI failed on fixtures that still described the
previous pcap client identity, legacy storage fingerprints, and the
old regex deadline sentence.
Co-authored-by: morluto <[email protected]>
* docs: link repository experiments outside the built site
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Prevent polling readers from observing truncated readiness JSON in compiler fixtures. Retain deterministic cancellation controls and strengthen the real Swift compiler case to run native identity inspection, verify parent/group facts, and check stable identity across reads.
Validated all 32 Darwin tests with the real host toolchain. A delayed write reproduces the old JSON race; delayed atomic publication passes. No CI timeout or retry changes.
* test: prefer structured assertions over recovery prose
Lock regex, IDA, Hopper/Kuna, MCP budget, capture-heap, and pcap
failures on error codes and reported limits instead of full recovery
sentences.
Co-authored-by: morluto <[email protected]>
* test: keep decoder heap negatives off the Windows reserve
"512 MiB" also appears in the Bun Windows reserve sentence. The
negative check stays on "512 MiB (source setting)", which is the other
adapter's heap.
Co-authored-by: morluto <[email protected]>
* test: keep the one-retry cap and deadline recovery tokens
The lost-connection guidance must say once only, so an unlimited
"whenever it is safe" rewrite fails. Deadline recovery still has to
name set, restart, the floor, and the maximum constant.
Co-authored-by: morluto <[email protected]>
* test: keep heap, MCP, pcap, and preload recovery tokens
Codex still wanted the set/restart/floor/maximum heap guidance, MCP
default/floor/maximum bounds, Wireshark install guidance, and the
dropped-preload attribution limit as separate tokens.
Co-authored-by: morluto <[email protected]>
* test: keep shared-context and transport recovery tokens
Shared-context capture must still exclude requests without a known root
or descendant frame. Shutdown must end the REA process before starting
again, and a failed HTTP listen must offer another port or stopping the
process that holds it.
Co-authored-by: morluto <[email protected]>
* test: keep handoff, input-check, and budget format tokens
Skill and server guidance must still tell the user to restart the MCP
client. Process failures must still say to check the input files. Invalid
MCP phase budgets must still say positive decimal integer and ms.
Co-authored-by: morluto <[email protected]>
* test: bind budget floors and active deadline sources
The phase-budget minimum is the start of "from N to M ms", so a digit
inside the maximum no longer satisfies it. Capability text must contain
the active "N ms (source ...)" pair, not the default number and a heap
source mentioned apart.
Co-authored-by: morluto <[email protected]>
* test: bind heap MiB sources and pcap host tokens
A configured 256 MiB request must appear as "256 MiB (source setting)",
so a byte-sized 256 no longer passes. Unsupported pcap hosts must still
name Linux, util-linux, Windows, and owned Job Objects as separate tokens.
Co-authored-by: morluto <[email protected]>
* test: bind the selected decoder heap to MiB and source
The two-adapter limitation must contain "256 MiB (source setting)".
The exhaustion message binds that capacity as "256 MiB V8 old-generation
heap (source setting)", so a byte-sized 256 no longer passes.
Co-authored-by: morluto <[email protected]>
* test: bind doctor client seconds and prlimit absolute guidance
Doctor recovery must name startup_timeout_sec of the policy's seconds,
so a hardcoded 60 no longer passes while the policy stays 30. A relative
prlimit must say the value must be absolute and tell the user to check
REA_TSHARK_PRLIMIT_COMMAND.
Co-authored-by: morluto <[email protected]>
* test: assert integrity codes for rehashed evidence and coverage
Independently rehashed process Evidence and a tampered digest project
evidence_integrity_mismatch. An unsupported comparison operation stays
invalid_request. Unsupported inventory coverage status is an integrity
failure whose reason carries the coverage.status schema issue.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Resolve raw input at the application boundary and reuse its facts in both MCP admission paths. Keep cleanup, mutation retirement and execution ordering while retaining structured correction details for rejected commands.
Validation: 186 focused tests, source checks, docs conformance and duplication gate passed. The new compiled CLI regression fails on the main baseline. Provider process controls use deterministic fixtures; no real Rizin verification is claimed.
* fix(android): retain snapshot descriptors when close fails
Preserve the stable-file diagnosis or completed copy facts when an APK
or engine snapshot close rejects, and retry that exact owner before
workspace retirement.
Co-authored-by: morluto <[email protected]>
* fix(kuna): retain span descriptors when close fails
Preserve completed span text or the short-read diagnostic when close
rejects, and keep session close incomplete until that handle is released.
Co-authored-by: morluto <[email protected]>
* fix(android): attribute snapshot cleanup to the descriptor that failed
A finished copy can be nested under another descriptor's rejected close.
Walk that chain, keep the snapshot hash on the partial observation, and
name every descriptor that is still open.
Co-authored-by: morluto <[email protected]>
* fix(kuna): gate span admission until a retained close releases
Overlapping requests can pass the entry cleanup check before any lease
exists. Hold each span open through its close so the next open retries
that descriptor instead of admitting another one.
Co-authored-by: morluto <[email protected]>
* fix(android): preserve nested snapshot facts when both closes fail
Walk nested close failures so a finished copy and a changed-APK diagnosis
survive when the source descriptor also rejects close. Attribute cleanup to
each descriptor that failed, and classify a nested close cancellation.
Co-authored-by: morluto <[email protected]>
* fix(kuna): release recovered span leases before the next open
Serialize span admission and close without holding the queue across the byte
read. Recheck the caller signal after that wait, and drop a recovered lease
from the resource scope as well as the span map.
Co-authored-by: morluto <[email protected]>
* fix(android): retain a source descriptor when admission close fails
openRegularFile can reject after the descriptor opens. If that close also
fails, keep the owner on the snapshot cleanup map instead of dropping it
when adoption never runs.
Co-authored-by: morluto <[email protected]>
* fix(kuna): keep cancellation when a completed span close fails
Pass the caller signal into span finalization. If the read already finished
and close then rejects, report cancellation as the primary error and keep
the span text with the retained descriptor.
Co-authored-by: morluto <[email protected]>
* style: format Android and Kuna close-ownership changes
CI format:check rejected the four files touched by the close-ownership fixes.
Co-authored-by: morluto <[email protected]>
* fix(android): keep the snapshot hash inside a nested cancellation
A destination close that succeeds after abort throws RegularFileCancelled
with the finished copy. When the source close then rejects, recover that
copy so cancellation keeps the digest and file-snapshot-hash observation.
Co-authored-by: morluto <[email protected]>
* fix(android): keep the snapshot hash on a direct close cancellation
A finished copy whose descriptor closes successfully after abort is a
top-level RegularFileCancelled. Attach its digest and file-snapshot-hash
observation to that cancellation.
Co-authored-by: morluto <[email protected]>
* fix(kuna): preserve the caller abort reason after a successful span close
A completed read whose close then succeeds throws a cancellation wrapper.
Return AnalysisCancelledError with the caller's abort reason instead of
that wrapper.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Parse caller selections and authenticated Evidence at their owners, reuse the parsed function and comparison facts, and preserve unexpected implementation failures. Remove downstream error guessing and redundant validation wrappers.
Verify with 526 affected tests, source checks, docs conformance, test discovery and duplication checks. Seven public SDK regressions fail on the main baseline. No real-provider execution was performed.
fix: repair main after bulk merge
Restore typecheck after #3826 called uniqueBy, and align the red fixtures
with the merged pcap, browser, IDA, and Hopper contracts.
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(setup): offer Hopper installation only where setup can install it
On a host where automated Hopper installation is ineligible (Windows,
macOS below 12 or with an unknown version, or an unsupported or
unidentified Linux distribution), an unhealthy setup without a Hopper
launcher ended by advising "Rerun with --yes --install-hopper", and the
same run then refused that request. The eligibility rule existed twice
(the discovery proposal and the explicit-install gate, with separate macOS
parsers that disagreed on malformed versions), and the final advice
consulted neither.
Make setup discovery the single owner of Hopper install eligibility. The
explicit gate refuses with the unchanged copy through hopperInstallRefusal,
and the final advice offers --install-hopper only where the host is
eligible; ineligible hosts fall back to doctor remediation. hostRemediation
now checks only the Node.js runtime. An empty or unparseable macOS version
is now refused by the explicit gate instead of installing.
Fixes#3944
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore: drop merge-only formatter changes
* test: align inherited fixtures with current analysis contracts
* test(ida): assert search recovery instead of diagnostic prose
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(hopper): keep automated Linux setup off non-x86_64 hosts
Every pinned Linux Hopper package (deb, rpm, Arch) is x86_64-only, but
hopperSetupSupported was derived from os-release alone. Setup therefore
planned and confirmed an amd64 package install on arm64 Ubuntu 24.04 and
the other tested distributions.
Classify installer support with the host architecture where the
distribution is parsed, so setup planning, preflight, installation and
package verification share one decision. Setup now takes its Linux
distribution from the doctor host, as it already does for the macOS
version, and the supported-host remediation text has a single owner.
Fixes#3419
* fix(hopper): install Linux Hopper from the setup host's distribution facts
systemLinuxHopperInstallHost read os-release with process.arch on its own,
while setup planning used the doctor host's injected architecture. Both
agree in production, but a doctor host composed with another architecture
could plan one answer and install with another.
The install host now takes its distribution reader from the caller, and
systemSetupHost passes the doctor host's, so planning, preflight and
installation share one host-fact owner. The unused default host on
installLinuxHopper, the other ambient reader, is removed.
* fix(verify-package): require Hopper readiness only where Linux setup installs it
packageSetupSupport required doctor Hopper readiness on every Linux host
whose host check passed, including installer-untested distributions and,
after the architecture gate, arm64 hosts where setup never provides
Hopper. On Linux, readiness now also requires automated installer
applicability; macOS is unchanged.
This narrows the readiness assertion from #2973 on hosts outside the
installer matrix, such as Kali and ubuntu-24.04-arm.
---------
Co-authored-by: DavidFB-creator <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
* fix(ida): ignore released worker rows when verifying close
Upstream idb_list can observe the owning worker mid-teardown and return a
row whose identity is already cleared - empty session_id, owned false,
is_active false - while input_path still names the private copy. The
post-close check matched that row through its private-input arm and threw
"IDA worker remains registered after close.", failing a run whose analysis
had completed and whose idb_close was already acknowledged, and retaining
the private workspace.
Such a row establishes no live session, so it no longer blocks release. A
genuine peer on the same input keeps a session ID and is still reported,
and the session-ID arm is untouched.
Measured against IDA Pro 9.2 on Windows: 11 of 12 stock runs failed this
way; 6 of 6 passed with this change and retained no workspace.
Refs #3056
Co-Authored-By: Claude Opus 5 <[email protected]>
* fix(ida): suppress released rows only for worker backends
The blanked-identity exception reached a gui row as well, and a gui
session belongs to an IDA instance REA does not control, so a same-input
gui binding could stop blocking release. The observed teardown shape
covers workers only, so require backend "worker" before suppressing.
Adds a regression test keeping a blank-identity gui row on the same
private input reported as a blocker, and moves the three blanked-identity
tests into their own describe to stay within the per-block line limit.
Refs #3056
Co-Authored-By: Claude Opus 5 <[email protected]>
* refactor(ida): state the released-row predicate as equality
`owned` and `is_active` are strict booleans in databaseListSchema, so
`!== true` suggested a third possible value that cannot occur. Say what
the row actually asserts.
No behaviour change; the blanked-identity tests cover both directions.
Refs #3056
Co-Authored-By: Claude Opus 5 <[email protected]>
---------
Co-authored-by: Claude Opus 5 <[email protected]>
* refactor(pcap): give TShark selection and version admission one owner
The pcap and packet-capture families wrapped the same caller-supplied
TShark with diverging rules. inspect_pcap_* accepted an absolute
REA_TSHARK_COMMAND or tshark from PATH, while inspect_packet_* and the
server availability policy required an absolute REA_TSHARK_COMMAND and
parsed the version banner with a separate regex.
PcapConfiguration now owns the rule: selectWiresharkTool selects an
absolute environment path or the bare name on PATH and refuses relative
values; locateWiresharkTool resolves a PATH selection to the first
absolute PATH entry holding an executable file; parseWiresharkVersion
admits Wireshark 4.x banners for both providers. TsharkCaptureProvider
and the session availability policy consume these facts instead of
reinterpreting REA_TSHARK_COMMAND, so all four tools accept and refuse
the same configurations. inspect_packet_* still resolves, hashes and
rechecks the located executable, and records selection_source in its
tool Evidence.
Host support and resource bounds are unchanged; unifying them is the
next milestone in #3403.
Refs #3403
* fix(pcap): execute the TShark the shared locator selects
resolvePcapTools still returned bare tool names for PATH selections, so
the operating system could run a tool from a relative PATH entry while
the packet tools skipped that entry and selected a different install.
On POSIX hosts, resolve PATH selections through locateWiresharkTool and
run that absolute executable, failing with the existing tshark_missing or
capinfos_missing code when no absolute PATH entry holds the tool. Both
selections are validated before PATH is consulted, so a relative
environment value still reports its own configuration error. Windows
keeps PATH/PATHEXT resolution in the process launcher.
Refs #3403
* 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 <[email protected]>
* fix(pcap): share selected tool identity verification
* perf(pcap): retain compact executable identity
* fix(pcap): report missing selected tools clearly
* fix(pcap): read historical capture evidence
* fix(pcap): preserve executable read failures
* test(pcap): cover version refusal and packet-tool availability selection
Address review follow-ups on the shared TShark owner:
- Send a 3.x TShark banner and a non-Wireshark banner through the packet
provider, pinning the capability-unavailable reason for each and that
only the version probe ran before refusal.
- Cover the session availability rule for inspect_packet_capture over
MCP: a PATH-only install and an absolute REA_TSHARK_COMMAND are
available on Linux, a relative REA_TSHARK_COMMAND is refused even when
PATH holds TShark, and no absolute PATH entry reports provider_missing.
- Rename the locator test's missing absolute PATH entry so it no longer
shadows node:path relative.
Refs #3403
* fix(pcap): name the configured path when a selected tool is missing
Resolving the selected executable before launch replaced the runner's
"Cannot run selected tool <path>" refusal with a message that kept the
configured path only in diagnostics, so the public CLI no longer told the
caller which REA_TSHARK_COMMAND or REA_CAPINFOS_COMMAND value to fix and
the pcap CLI boundary test failed. Include the configured command in the
capability-unavailable message and assert it there.
Refs #3403
* test(captures): give the late-cancellation fixture a TShark selection source
The service fixture added on main in 719d56ebf predates the required tool.selection_source field, so its report failed output validation before reaching the late-cancellation path the test covers.
Refs #3403
* fix(pcap): report a probe's pending cleanup when a post-run check fails
When a tool probe returned with its owned process still awaiting cleanup,
a failed post-run executable re-read or identity check threw before run()
reported that cleanup. Probes have no snapshot to carry it, so callers
saw an ordinary error with cleanupIncomplete false while the provider
kept the process and refused the next request.
After the command returns, combine such a failure with the probe's
ProviderCleanupError (cancellation and other command failures keep their
own cleanup outcome). Classify request failures through one
analysisFailure owner shared by run() and executeAdmitted(), and let the
availability probe rethrow any cleanup-incomplete failure instead of
reporting the tool unavailable.
Refs #3403
* fix(pcap): keep a failed executable re-read's cleanup live with the probe's
The combined post-run failure was bound only to the probe's pending
process cleanup. When the executable re-read had also failed to close its
file, the classified failure owned that cleanup, but the combined error's
live cleanupResources omitted the file and could clear cleanupIncomplete
once the process stopped. Bind both the classified failure and the probe
cleanup as dependencies.
Refs #3403
---------
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
nm -j and dyld_info -exports end each row with LF. Splitting on \r?\n and
trimming rows treated a carriage return at the end of a symbol name as part
of the line terminator, so the distinct exports "trailing\r" and
"trailing" merged into one entry with the wrong address. Split rows on LF
only and keep trailing text in export rows.
Fixes#3390
Set disabled to false in generated OpenCode V2 registrations so a
lower-priority disabled entry cannot keep approved setup stale.
Cover layered JSON/JSONC refreshes, idempotent setup and doctor readiness,
exact backups, preserved lower settings, malformed-config refusal, and
the exclusion of disabled registrations from automatic maintenance.
Update the native V2 registration expectations.
Fixes#3318
* 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 <[email protected]>
* 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 <[email protected]>
Co-authored-by: morluto <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
---------
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* 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 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
---------
Co-authored-by: Claude Fable 5.1 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
* 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 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* style(pcap): format the capture ceiling test
oxfmt rejected the object literal wrapping in PcapNativeBound.test.ts.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
* 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 <[email protected]>
* refactor(errors): drop capability imports superseded by the shared helper
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
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.
* fix(javascript): report path data records as route candidates
A string `path` or `route` property was reported as an exact, observed
route endpoint whatever object held it, so data records such as
`{ type: "set", path: "updatedAt" }` became routes while a real
`app.get("/x")` stayed a low-confidence candidate.
Keep the property finding, but mark it as a candidate unless its object
has router fields (`component`, `element`, `Component`, `children`, or a
typed `_$` record), using the same shape check that already selects
automatic route roots. Route candidates get a route-specific limitation.
Fixes#6349
* fix(javascript): scan each route record's keys once
`#property` read the enclosing object's keys for every `path` or `route`
property, so an object repeating those keys was scanned quadratically.
Cache the record shape per object and share it with `#object`.
* fix(mcp): escalate transient failures before asking for a restart
Give dropped connections and startup failures one bounded retry, then a
scripted handoff to the user. Point timeout recovery at a named request
field or REA_* setting instead of telling agents those limits are fixed.
Co-authored-by: morluto <[email protected]>
* fix(process): stop unexpected command failures from recommending a retry
The catch can already have launched the target, and the projection stays
non-retryable. Tell the user to check the inputs and run doctor instead.
Co-authored-by: morluto <[email protected]>
* fix(mcp): limit retries to safe calls and recommended settings
A dropped connection may already have changed the target, and a bad
catalog rejects the same startup every time. Retry only a repeatable
call, and change a setting only when the remediation recommends it.
Co-authored-by: morluto <[email protected]>
* test(mcp): match recovery guidance across wrapped lines
Startup handoff capitalization and markdown wrapping still carry the
same sentences. Compare the prose with whitespace collapsed.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Document that the general source suite runs on Linux in CI and that Windows CI selects a curated lane, so Windows contributors compare failures against the base revision instead of attributing POSIX-only failures to their change.
Fixes#6308
* fix(hopper): retain file-offset reads when descriptor close fails
A rejected source close was classified as a source or access failure and
dropped the descriptor, so client close reported success. Keep the
completed snapshot or the earlier non-regular-source failure, report the
close as typed cleanup, and retry that exact handle.
Co-authored-by: morluto <[email protected]>
* fix(native): retain LLDB prelaunch cleanup ownership
Preparation failures were rewritten as an LLDB exit, and discovery
failures dropped the resolver supervisor. Preserve compiler facts and
the foreign helper marker, and retry only the tracer-owned resolver
before another trace is admitted.
Co-authored-by: morluto <[email protected]>
* fix(hopper): bind combined close failures to both owners
A descriptor close and a session close that fail together now stay incomplete until each owner resolves.
Co-authored-by: morluto <[email protected]>
* fix(native): retain resolver preparation cleanup ownership
LLDB discovery keeps compiler facts and the foreign helper marker when tool resolution fails during preparation.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Keep every competing-loader source on the shared stdio session and compare
three renderer outcomes with the compiled CLI. Retire the Early, Middle, and
Late shards, and seed their historical duration onto the consolidated file.
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Share clock rendering between analysis and local serving. Preserve exact sub-millisecond facts, bound learned format state per field, and charge retained output to the projection budget.
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Serve renamed create members and evidence-gated UUID regeneration. Preserve every copied text occurrence and withhold cross-field scalar UUID relations the independent generators cannot reproduce.
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Cursor Cloud can resolve an older Node.js before /usr/local/bin. Tell agents to prefix PATH so npm and node use the toolchain environment setup installs.
Co-authored-by: Cursor Agent <[email protected]>
* feat(mcp): make MCP startup phase budgets configurable
REA_MCP_INITIALIZE_BUDGET_MS, REA_MCP_TOOLS_LIST_BUDGET_MS,
REA_MCP_FIRST_CATALOG_BUDGET_MS, and REA_MCP_DOCTOR_DEADLINE_MS keep
today's defaults and refuse values outside 1 to 2147483647 ms. Codex's
30 second startup figure stays client guidance.
Co-authored-by: morluto <[email protected]>
* fix(mcp): enforce the first-catalog budget while phases are pending
The packaged startup probe applied each phase timeout on its own and
checked REA_MCP_FIRST_CATALOG_BUDGET_MS only after both requests
finished. A shorter first-catalog budget now clamps the in-flight
request and cancels it from one deadline measured at startup.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(captures): make the HAR/SAZ/qlog decoder heap configurable
REA_CAPTURE_DECODER_OLD_GENERATION_MIB keeps the 192 MiB default and
refuses values outside 8 to 16384 MiB instead of falling back.
Co-authored-by: morluto <[email protected]>
* fix(captures): defer decoder startup and share one parsed heap
Invalid REA_CAPTURE_DECODER_OLD_GENERATION_MIB threw while CLI commands
were registered, so doctor and help never ran. Build the capture service
only when inspect-web-network-capture runs.
Resolve the heap once in composition and pass that fact to each Node
adapter. Limitation and memory-failure reports use the selected adapter's
heap, so a decoder built with a different environment does not describe
another limit.
Mock captureDecoderProcessMemoryLimitBytes in the Node adapter test so a
Windows Bun host still exercises the unavailable-bound refusal.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Retain bodiless writes, proven omitted fields, identity-free projections and digit-group sequences. Preserve late projection provenance and current omission facts, and check absence statuses before replay shortcuts.
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
* feat(process): make the regex worker heap request configurable
REA_REGEX_WORKER_HEAP_REQUEST_MIB keeps the 128 MiB default and refuses
values outside 8 to 16384 MiB. A positive Node --max-old-space-size still
overrides the request and is not clamped by the setting.
Co-authored-by: morluto <[email protected]>
* fix(process): set the regex worker heap floor to the measured startup bound
On Node 22.23.4 a 5 MiB old-generation request aborts the regex worker
during startup, and 6 MiB starts it and returns a match. The setting floor
is that measured bound.
Co-authored-by: morluto <[email protected]>
* fix(process): raise the regex worker heap floor to 7 MiB
6 MiB passes configuration but returns ERR_WORKER_OUT_OF_MEMORY for a
trivial match on supported Linux Node 24 and Node 26.0.0. 7 MiB returned
that match on the measured 22, 24, and 26 lines. A clean-process test
runs the production worker at the floor.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Fixes#6212. Parse effective argfile syntax once, admit bounded regular files with retained cleanup/cancellation ownership, freeze validated bytes for subsequent helpers and analysis launch, and share existing private-runtime writing. Resolves the continuation, FIFO and mutable-source review findings.
Verified 97 related tests (3 explicit skips), full static/docs checks, two independent local Codex reviews and 12 real installed-JDK17 grammar/property controls. Real Ghidra, JDK25 and native Windows qualification remain unavailable. Used the explicitly authorized maintainer bypass: current GitHub CI remained queued and Greptile rerun was still in progress; author-readiness metadata was not fabricated.
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Fixes#6227. Forward caller cancellation to private-display selection and preserve its exact cause after joined selector finalization. Covers active, late-finalization, healthy and pre-aborted controls. Verified 3 x 14 source tests, 22 compiled process/CLI boundaries, full static checks and independent local Codex review. Greptile passed the current head. Used the explicitly authorized maintainer bypass: the current GitHub rerun remained queued and author-readiness metadata was not filled. Original-head code CI passed; no human-review attestation was manufactured.
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
* chore: restack environment fidelity onto main
Keep probe comparison, paging, and tied-create behavior on main after #5600. Capture parsing keeps field-path errors and records whether a probe kept request and response bodies.
Co-authored-by: morluto <[email protected]>
* fix(environment): keep served collection order with the fidelity checks
The tied-create collection order lives with the checks that match encoded parents. The write simulation reads field checks from that attribution, and taint uses the same segment match.
Co-authored-by: morluto <[email protected]>
* fix(environment): translate an object's keys only when it is keyed by learned ids
Differ.object mapped every key through the probe's id map, so a field named
like a learned id was compared with another field, and a real rename could
pass as a mapped key. Keys now translate only for an object keyed by
learned ids as learning reads one: a key both sides share needs every key
evidently an id, and otherwise each entry is an entity or each key evidently
an id. Any other object compares field by field.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
Learn supported filters, plain search, sorting and paging from retained list recordings and evaluate them through shared local-state workflows.
Preserve whole-membership admission, Evidence, query normalization, counter representation and omission. Use successful retry coverage consistently for confirmation and pending-write cleanup. Unsupported semantics remain replayed or unknown.
Validated 623 adjacent source tests, exact-head CI, repeated regressions, real retained GitHub CLI/MCP pagination, actual owned HTTP fixture sessions and Codex task/review runs. Third-party application-write fidelity and exhaustive scale/provider qualification remain unverified; details and measurements are recorded in the PR.
Refs #4933, #6262.
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
* feat(python): inspect CPython pyc headers and marshal code objects
Add inspect_python_pyc / inspect-python-pyc to read one explicit local
CPython 3.7–3.13 .pyc without executing bytecode or recovering source.
* chore(python): drop unused PythonPycHeader export
Knip reported the type as unused after inspect_python_pyc landed.
* fix(python): bound pyc expansion and decode marshal like CPython
- Count a nested code object reached through TYPE_REF once per copy the
report carries, against max_objects and max_file_bytes, before counting
or validating the tree
- Charge long digits to max_string_bytes, reject out-of-range and
unnormalized digits, build the value in one pass, and report integers
above 256 bits by bit length only
- Decode TYPE_UNICODE with surrogatepass semantics and reject other
ill-formed UTF-8
- Map only 3.11 magics 3494-3495 to the py311 layout
- Report non-finite float and complex parts as inf, -inf or nan
- Allow an empty co_filename
- Require bytes for co_linetable and co_exceptiontable
- Bind cleanup-failure errors to their retained descriptor leases
* fix(python): list pyc code objects once instead of a recursive tree
Report each distinct code object once in code_objects, top-level first,
with nested_code as indexes. A child shared through TYPE_REF is no longer
copied per reference, and the MCP output schema is no longer recursive.
Names repeated through TYPE_REF still count against max_file_bytes for
each copy the report carries.
* fix(python): bound shared pyc constants and reject malformed tables
- Cache byte-string digests so a buffer shared through TYPE_REF is
hashed once instead of on every reference.
- Charge every reported co_consts entry, including tables shared
through TYPE_REF, against max_objects before summaries are built.
- Resolve the inspect-python-pyc path with absoluteCallerPath so
link/../file.pyc reads the file the filesystem selects.
- Reject TYPE_NULL inside marshal sequences, as CPython does.
- Reject a co_localspluskinds length that does not match
co_localsplusnames in the parser and the report schema.
* fix(python): bound shared pyc names and read CPython marshal graphs
Charge each reported code-constant name, keep integer bit lengths on the
decoded value, and list code objects nested inside constant containers.
Accept self-referential lists and dicts, marshal version 0 text floats,
empty code names, the original string byte count, and Python set and dict
lengths. Pass the CLI cancellation signal into command cleanup.
Co-authored-by: morluto <[email protected]>
* fix(python): bound pyc key expansion and shared nested code
Charge set and dictionary keys before a shared tuple doubles them, list each
nested code object once per owner, and accept code objects as CPython keys.
Co-authored-by: morluto <[email protected]>
* fix(python): correct pyc equality, counts, and nested walks
Summarize code objects only after the marshal graph is complete, and
collect nested code with an iterative walk that shares wrapper results
instead of copying every descendant list. Keep tuple boundaries and the
original string bytes in equality keys, and reject negative CPython counts.
Co-authored-by: morluto <[email protected]>
* fix(python): bound shared pyc names and keep the first dict key
Charge every copied name-table entry against the object budget before
building the report array, including empty strings that add no bytes.
When dictionary keys compare equal, keep the first key object and
replace only its value, matching CPython.
Co-authored-by: morluto <[email protected]>
* fix(python): walk each pyc dictionary child list once
Keep a container's child array on the nested-code frame so a wide
dictionary is not copied on every step of the synchronous marshal walk.
Co-authored-by: morluto <[email protected]>
* fix(python): reuse stored pyc dictionary children when combining
The nested walk keeps each container's child list on its frame.
Combining nested code rebuilt that list, and the 8,000-key regression
still accepted the old per-key copy. Use the stored list, and require
that dictionary to finish well under the copy's runtime while still
reporting a nested code object.
Co-authored-by: morluto <[email protected]>
* fix(python): keep distinct pyc code objects and reject corrupt ones
Encode each name table's length and entry boundaries in the equality
key so a different name list cannot share a memo key. Walk tuple and
frozenset keys on an explicit stack so a long TYPE_REF chain does not
overflow. Treat code objects CPython rejects for an odd co_code or an
undersized local table as malformed instead of complete coverage.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Derive whole-collection membership per checked list line while preserving
recorded membership, counters, source Evidence and independent write claims.
Integrate current main without losing its shared path decoder. Keep later or
ambiguously positioned pages recorded rather than inventing earlier entities.
Validation: 543 focused/adjacent tests, nine README checks, complete static/docs
checks, repeated regression runs, and real retained GitHub-label CLI/MCP
load/reset workflows. An actual Codex session returned the correct facts;
its over-strict grader failure and same-trace semantic verification are recorded.
Evidence and limitations:
https://github.com/morluto/rea/pull/5787#issuecomment-6338446674
Tracking: #6262; feature scope: #4933.
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
The managed static provider refused a readable non-PE executable with
AnalysisCapabilityUnavailableError, the same type as its unimplemented
operation guard. inspect-managed-artifact, inspect-managed-members and
inspect-managed-native-boundaries therefore reported capability_unavailable
/ unsupported_provider and omitted the selected path.
Return AnalysisUnsupportedTargetError with the operation, path and observed
format instead. Unimplemented operations keep the capability error.
Fixes#6076
When a file:// URI contains leading slashes representing a UNC path (such as file:////remote.example.test/share/state.json), Node's fileURLToPath on Windows throws ERR_INVALID_FILE_URL_PATH because it expects a drive letter or Windows UNC format. This was caught and misreported as 'a file:// uri names a local absolute path' instead of the intended 'a file:// uri names a path on this host, not a network share'.
Testing UNC_PATH against url.pathname before calling fileURLToPath ensures that UNC paths are consistently recognized and refused as network shares across platforms.
Co-authored-by: ithis0623-png <[email protected]>
* feat(erlang): inspect BEAM IFF chunks and tables
Replay the Erlang BEAM inspection workflow, including table bounds,
OTP 28+ atom layouts, the shared absolute-path policy, and empty-path
CLI coverage, without carrying GitHub workflow edits.
Co-authored-by: morluto <[email protected]>
* fix(erlang): use publicInputStrictObject for BEAM tool input
inspect_erlang_beam advertised a public input built with bare
z.strictObject, so later shared input-policy changes would miss it.
Result and internal objects stay on their own constructors.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Remove the Latin-1 fallback and the assumed UTF-8 decoding of GeneralString so unsupported name encodings cannot silently corrupt issuer or subject text. Document the limitation and cover the T.61 acute-accent example through the public CLI.
Validation: 30 focused parser/CLI/MCP tests passed; npm run check and npm run docs:check passed. Three new parser regressions fail on the original decoder. Full suite and real historical certificates were not exercised.
* feat(pe): inspect Authenticode certificate tables
Add inspect_pe_certificates for IMAGE_DIRECTORY_ENTRY_SECURITY
WIN_CERTIFICATE entries and structurally decoded PKCS#7 signer
and X.509 identity fields. The workflow reads a stable PE image
without executing it or verifying signatures, chains, timestamps,
or Authenticode digest coverage.
* fix(pe): bound and validate certificate DER decoding
- Cap decoded DER values with a shared node budget so sibling fan-out
cannot exhaust memory before the PKCS#7 check
- Require SignedData version, encapContentInfo and a final signerInfos
SET in RFC 5652 order, and a signature OCTET STRING in each SignerInfo
- Decode OBJECT IDENTIFIER arcs as unbounded base-128 values, including
a multi-byte first combined arc
- Decode UniversalString names as UTF-32BE and reject invalid values
- Share PE header admission with the resource layout so a section table
that does not fit SizeOfHeaders is rejected
* docs: list PE certificates in the translated READMEs
* fix(pe): cap OBJECT IDENTIFIER length and arc size
A crafted type-2 certificate with a 262,144-byte contentType OID stalled
decodeOid for about 20 seconds, because one arc kept growing a BigInt
before the signedData check ran. decodeOid now rejects an OID longer than
256 bytes and an arc longer than 32 bytes before decoding it. Certificate
OIDs, including 2.25 UUID arcs, fit under both caps.
* fix(pe): reject truncated certificate tables and PKCS#7
SignedData now requires one SignerInfo with a non-empty signature and a
complete issuerAndSerialNumber. Certificate names share a 64 KiB decoded-byte
budget and are copied once. A security directory that ends in a short
WIN_CERTIFICATE header fails instead of reporting a complete table. The
certificate CLI forwards the caller abort signal into cleanup.
Co-authored-by: morluto <[email protected]>
* chore(ci): keep fork workflow files unchanged
The certificate restack follows current main. Workflow files stay at the
fork tip so this branch update does not rewrite .github/workflows.
Co-authored-by: morluto <[email protected]>
* fix(pe): charge serial and validity text to the decoded budget
Certificate and signer serial numbers and validity times now count against
the shared decoded-text budget before they are converted, so a crafted
certificate cannot push megabytes of text into the hashed evidence. The
budget error now names certificate text rather than only names.
Restore the CI workflow to match main: the CI_TIMING_TIER input, the
tier-named timing artifacts and their publication conditions, the Windows
typecheck, and the path and snapshot suites in the curated Windows lane.
* fix(pe): require certificate fields and preserve signer key ids
Reject X.509 records that omit subjectPublicKeyInfo or signatureValue,
return subjectKeyIdentifier bytes as bounded hex, and advertise an absolute
path in the PE certificate input schema.
Co-authored-by: morluto <[email protected]>
* fix(pe): reject extra PKCS#7 wrappers and invalid UTF-8 names
ContentInfo and its signedData wrapper no longer ignore trailing values, and
directory strings must round-trip as UTF-8 instead of becoming U+FFFD.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: morluto <[email protected]>
fix(ci): isolate packed Python syntax checks from ambient imports
Keep caller-local modules and site startup hooks from replacing the
standard-library AST/tar readers or executing during package validation.
Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
* feat(config): make the regex matching deadline configurable
REA_REGEX_MATCHING_DEADLINE_MS defaults to 5000 ms and refuses values
outside 1..2147483647. Recovery and capability text name the effective
deadline, its source, and how to change the setting.
Co-authored-by: morluto <[email protected]>
* fix(setup): preserve the regex matching deadline in registrations
Setup validated REA_REGEX_MATCHING_DEADLINE_MS but left it out of the
selected registration environment, so a caller override never reached
the launched server and matching fell back to 5000 ms.
Co-authored-by: morluto <[email protected]>
* test(config): restore a stubbed regex matching deadline on failure
Kuna deadline assertions now unstub the environment in finally, so a
thrown construction or expectation cannot leave the setting stubbed.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(native): read PE and ELF export tables
Add a host-independent export-table reader on the shared native
container layer. PE images report each used export-address-table slot
with its ordinal, one row per name, its address or forwarder text, and
the export directory identity; the PE image reader now exposes its data
directories. ELF images report defined default/protected global, weak
and unique .dynsym symbols with type, binding and .gnu.version_d
version (hidden versions kept distinct, unreadable version tables
"unknown"). Unreadable entries are counted in issues and make coverage
partial; an ELF without section headers is unsupported.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(native): inspect native export tables through CLI and MCP
Expose the reader as inspect_native_exports / inspect-native-exports:
one stable, digest-bound read of an explicit file with a caller-selected
input budget, typed failures for malformed, unsupported, oversized and
cancelled reads, and observed Evidence bound to the file path and
SHA-256. Mach-O inputs point to inspect_macho.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(native): describe portable PE and ELF export inventories
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): bound export inventories and read header-backed PE tables
Address review of the export-table reader:
- Charge every retained row and its name, forwarder and version text
against shared 262144-row and 16 MiB allowances before storing it, so
repeated long names or PE aliases cannot exhaust memory.
- Map PE RVAs through the headers as well as sections; the PE image
reader now exposes its SizeOfHeaders mapping.
- Keep ELF exports with version "unknown" when .gnu.version lies outside
the file, and report an empty version-definition name as unresolved.
- Reject a .dynsym without its mandatory null entry as malformed.
- Decode the ELF section directory once.
- Correct the docs on which gaps drop rows and which keep them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Wrapper substitution keyed contextBridge findings only by the inner
exposure call's range, so every helper invocation replaced the previous
one while unioning its members: the last key survived with every
invocation's members. Substituted members also listed only immediate
object keys, dropping nested paths that direct syntax retains.
Record substituted exposures per site and per world and key, and replace
the site's syntax observation once after traversal. Invocations exposing
one key at one site describe one API covering each alternative object; a
traced invocation whose key, object or world stays unresolved or guarded
keeps the site's own unresolved observation. Enumerate member paths
through resolved objects with the direct syntax depth bound, which also
retains the depth-limit unknown that substitution previously dropped.
Fixes#5871Fixes#5870
Co-authored-by: Claude Opus 5.5 <[email protected]>
Setup and doctor inspect every provider. With no GHIDRA_INSTALL_DIR and no
Ghidra launcher on PATH, the Ghidra inspection still ran the host `java` from
PATH. Its result was discarded because the configuration check already
rejects Ghidra, but its owned-process cleanup could still fail closed. In CI,
that cleanup reported cleanup_incomplete for an unrelated live process whose
/proc/<pid>/environ was unreadable, so `rea setup --client hermes` exited 1.
Run Java only for a selected installation, matching the existing rule for a
relative JAVA_HOME. Hermes setup tests now report the CLI JSON when an exit
code assertion fails.
Fixes#6200
Co-authored-by: Claude Opus 5.5 <[email protected]>
Electron's utilityProcess.fork(modulePath[, args][, options]) treats a
non-array object in the second argument as the options and ignores the
third. The static extractor only read the third argument, so
`fork("./worker.js", { serviceName })` lost the declared name while
reporting exact, complete coverage, and an object in the second slot let
an ignored third argument supply the name.
Move the optional-argv options selection that child_process already used
into the shared expression-shape owner and reuse it for utility
processes. Unproven argv shapes, spreads and identifier options remain
unknown with partial coverage; nullish options or a nullish serviceName
declare no name, matching Electron's runtime checks.
Fixes#6208
Co-authored-by: Claude Opus 5.5 <[email protected]>
The private-runtime ACL observer spent most of its time in PowerShell
machinery: Get-Acl module autoload, an Update-TypeData guard that updates the
session type table, and a Where-Object/ConvertTo-Json pipeline. On starved
hosted runners this pushed it past the 15s deadline, and one run never reached
the first script statement within 15s.
Read the descriptor through DirectorySecurity with SID-typed GetOwner and
GetAccessRules, check every compared identity is a SecurityIdentifier, keep
Get-Acl/Access/NTAccount/Translate out of the observer with a static check, and
build the JSON by string. Route the snapshot digest, readonly-children and
sharing-reader observers through the same cmdlet-free helper. Record process
uptime at script start, and give observers a 60s deadline that bounds a hung
child while covering cold powershell.exe start. Asserted facts are unchanged.
Fixes#6275
Co-authored-by: Claude Opus 5.5 <[email protected]>
Final filesystem observation reconstructed root admission from the initial
snapshot's root entries, so an initially absent root (no entry) whose ancestor
became a regular file failed capture with ENOTDIR after the child completed.
A returned initial snapshot now admits every selected root for the run. One
tagged path lookup classifies ENOENT and ENOTDIR as observed absence and other
unavailable lookups (EACCES, ELOOP, ...) as entry_unavailable omissions that
keep the root incomplete. Selected roots that are obstructed or denied before
launch still fail capture.
Fixes#6213
Co-authored-by: Claude Opus 5.5 <[email protected]>
project_managed_application_graph dropped the call_edges and
field_accesses that member Evidence already decodes, so the graph was a
containment tree that could not answer who calls a P/Invoke wrapper or
which methods touch a field, while reporting complete-within-inputs.
In-module call/callvirt/newobj/jmp sites now become one calls edge per
caller and target, and field loads, stores and address-takes become one
accesses edge per method and field, each with site and per-opcode counts
and member Evidence links. MemberRef and MethodSpec targets, unknown
targets, ldftn/ldvirtftn loads, recursive self-calls (the graph has no
self-loops), MemberRef fields and sites whose node is absent are counted
by reason in limitations, and the graph is partial with that
omitted_count. Add the provider-neutral accesses relation.
Graph Evidence projected before these counters existed still parses,
with null calls and field_accesses summaries.
Fixes#4056
Co-authored-by: Claude Opus 5.5 <[email protected]>
The shared TOML server-table editor kept comment lines immediately only
for the Codex dialect. For Grok Build it queued them with blank lines,
then discarded the queue when an REA table header followed or while it
skipped REA's table, so a comment introducing the stale REA table and a
trailing file note after it were lost on setup and uninstall.
Every comment line is now kept in place for both TOML dialects; only
blank lines that separated a removed REA table go with it. The Codex
wrapper's terminal line-break restoration moves into the shared editor,
so removing a final REA table keeps the file's last line break for
Grok Build too.
Fixes#6207
Co-authored-by: Claude Opus 5.5 <[email protected]>
A url-prefix runtime mapping decoded the whole pathname suffix at once, so
`child%2Fpart.js` became the nested artifact path `child/part.js` and produced
a medium-confidence location match and an inferred observed_as edge.
Decode each URL path segment separately at the mapping boundary. A segment
that decodes to contain a slash, like a malformed or non-UTF-8 escape, has no
artifact path, so the runtime script stays unknown with
no-authorized-location-mapping. Encoded spaces, Unicode, %25 and the literal
%5C backslash name keep matching.
File-root and URL-prefix mappings now share one tri-state translation
(outside, untranslatable, translated) for matching and load-state scope.
Static nodes beneath a prefix whose runtime script or worker could not be
translated stay unknown instead of becoming not-observed-in-capture.
Fixes#5878
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(cli): exclude declared sensitive values from parser diagnostics
Incur rejects malformed command tokens and output-flag values before the
web capture service applies --sensitive-value declarations, so its
diagnostics repeated declared literals.
Read declarations from the tokens Incur leaves for command parsing,
independently of parse success. Commands accepting the option get an
outermost middleware that excludes declarations from parse, validation
and thrown failures while keeping type, code and exit status. Rejected
--format/--token-limit/--token-offset values are replaced by a
literal-free value Incur still rejects. The option definition and the
Incur builtin-flag rules now have one owner each.
Fixes#6252
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(cli): redact whole rejected output-flag diagnostics
Incur builds its rejected --format/--token-limit/--token-offset message
from fixed text and the value, outside any middleware. Substituting only
the value left declarations that match the fixed text, such as "token"
in "--token-limit", in the diagnostic.
Report that first rejection at the serve boundary instead, redacting the
whole message as parser failures are, while keeping the UNKNOWN code and
Incur's toon or TTY rendering. Incur's message is now mirrored beside its
acceptance rule, and the value-substitution path is removed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(native): decode Swift nominal type declarations from __swift5_types
Fill swift_decls from the nominal type context descriptors that survive
stripping: kind (class, struct, enum, actor), own and qualified name,
module, generic flag, anonymous-context flag, metadata accessor and
descriptor location, with evidence citing the type record. Unresolved
parent contexts keep qualified_name null and the entry partial.
One shared reader walks each context once; class vtable recovery now
consumes its descriptors instead of re-reading the records, so a type
record that cannot be read keeps both facets partial. Remove the
never-populated swift_decls fields that descriptors do not carry.
Fixes#5341
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): bound Swift context walks and decode vtables in record order
Address review of the nominal-type reader:
- Cache intrinsic context depth (distance to the module) so a cached
parent cannot bypass the 32-context bound, and never cache a stop that
depends on where the walk began. Bound qualified names to 4096
characters so nesting cannot multiply name storage.
- Visit each descriptor as soon as its kind and name are read, so a
class vtable spends the record budget in record order instead of after
every later declaration.
- Report an accessor outside executable mapped code as null and keep the
declaration partial.
- Forward a name failure to vtable coverage only when the record is a
class or its kind is unknown.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Inspect selected ELF32/ELF64 PT_NOTE and SHT_NOTE declarations without executing
the artifact, retaining independent table identities, source ranges, descriptor
digests, GNU build-id/ABI fields and explicit coverage limitations.
Integrate mvanhorn's implementation and hardening with current main while
preserving byte-entropy registration and the Windows curated regression suite.
The maintainer follow-up uses ordinary merges/pushes, not history rewriting.
Executed validation: 96 focused/shared/docs tests, complete static/docs checks,
eleven real CLI/MCP pairs including GCC ELF32 and Clang big-endian PowerPC64,
and two actual Codex task traces checked against their Evidence. Full costs,
grader correction and verification limits are recorded in PR #4837.
Refs #6262.
build_call_path summed the out-degree of every reached function, so a
terminal goal's callees counted as explored even though the forward
search never expands the goal. Count explored edges and depth at the
expansion boundary inside the search, drop the post-hoc summary and the
now-unused out-degree helper, and reuse the schema-owned explored type.
Fixes#5906
Co-authored-by: Claude Opus 5.5 <[email protected]>
The NIBArchive value decoder read type tag 4 as true and 5 as false, the
reverse of what ibtool writes and AppKit's decoder returns, so every
compiled-nib boolean in decode-interface-builder output was inverted. The
test fixture encoder shared the reversed mapping, hiding it in round trips.
Name the value-type tags once and use them in both the read and skip
paths, fix the fixture encoder, pin raw tags in a unit test, and make
verify:interface-builder compare both polarities with ibtool --export.
Fixes#5541
Co-authored-by: Claude Opus 5.5 <[email protected]>
The DEHYDRATED_DATA COPY writer evaluated the output buffer before the
reservation that could replace it, so a COPY that grew the hydration
buffer wrote its payload into the superseded buffer. The overlay kept the
advanced length and hashed uninitialized bytes while the report claimed
complete recovery.
Reservation now returns a view of the retained buffer, and every writer
(COPY, zero fill, relative and absolute fixups) writes only through that
view, so no caller can hold a stale buffer across growth.
Fixes#5882
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(diagnostics): report a byte entropy profile for one local file
The diff adds the MCP tool inspect_byte_entropy and the CLI command
inspect-byte-entropy, both implemented by ByteEntropyService. One
absolute regular file is read offline into observed Evidence: file-wide
SHA-256 and Shannon entropy, then fixed blocks and adjacent runs for an
optional window, using a power-of-two block size from 256 to 65536
(default 4096), with high at 7.2 or above and low at 1.0 or below. A
window past EOF, a missing path, or a non-file is an input error; a size
or identity change during the read is an artifact-changed error; empty
input reports entropy 0; cancellation is honored before and during the
read. Summary detail retains the complete series and returns file
entropy, counts, and the ten longest runs for inspect_analysis_view,
while the registered effect remains read-only Evidence and the
diagnostics docs record the operation and its errors.
* fix(diagnostics): flag zero-size files as not shown empty
Generated files such as Linux /proc entries report size 0 while holding
content, so inspect_byte_entropy read nothing and returned an empty-looking
report. A file that reports size 0 now carries a limitation that the result
does not show the file is empty. The result stays a success because the
tool reads the declared size, which the file-stability checks depend on.
* fix(mcp): reject unsupported diagnostic summaries before inspection
* fix(ci): repair integrated checks and Hopper test baseline
* test(entropy): use shared MCP schema verification after merge
* fix(catalog): list inspect_byte_entropy under its own provider
Adding inspect_byte_entropy to BINARY_DIAGNOSTICS_TOOL_CONTRACTS put it
under pwntools-elf in the generated product catalog, because the
pwntools group took every binary diagnostics tool except
inspect_pe_resources. The service reports provider byte-entropy.
Add a BYTE_ENTROPY_PROVIDER identity beside PE_RESOURCES_PROVIDER, use
it in ByteEntropyService, give it its own catalog group, and name the
pwntools tools (inspect_binary_layout, inspect_recorded_crash)
explicitly so a new diagnostics tool is not attributed to pwntools by
default. The catalog test pins both groups.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KFTE4BHbGYzgf8Uhzcppdh
* fix(cli): resolve the byte-entropy path with absoluteCallerPath
main replaced the node:path resolve import in binaryDiagnosticsCommands.ts
with absoluteCallerPath (#4067), so the byte-entropy command's
resolve(args.path) no longer type-checks once the PR is merged with main.
Use absoluteCallerPath like the other commands, and add
src/filesystem/callerPath.ts matching main so the branch builds on its own.
* fix(diagnostics): retain entropy read cleanup ownership
* docs(diagnostics): explain entropy output capacity
* fix(diagnostics): preserve entropy cancellation outcomes
* fix(diagnostics): close the entropy input and warn on large reports
Public inspect_byte_entropy input now uses publicInputStrictObject before
detail is added, so unknown keys stay rejected on the advertised schema.
The tool description warns that complete output can become very large and
points callers to a shorter length or a larger block_size.
Co-authored-by: morluto <[email protected]>
* fix(diagnostics): keep byte-entropy registration mergeable with main
Register inspect_byte_entropy beside layout inspection and reject
unretained summaries before the file read, without editing workflows.
Co-authored-by: morluto <[email protected]>
* fix(diagnostics): register byte entropy with the shared tool options
Use toolRegistrationOptions so the handler typechecks on this branch and
on main, and still refuse unretained summaries before the file read.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(environment): render more read tools from the state and describe responses
Keep main's environment serving and fold in coverage for non-entity reads,
response summaries, and MCP tool annotations.
Squashed rebase of #5352 onto main.
* fix(environment): keep rendered documents and quoted samples inside their budgets
A recorded body of exponential numbers can render past 256 KiB and turn a
working read into a state size error, so that document stays a recorded replay.
Description samples are measured after JSON quoting, and the replay-fallback
catalog check uses a tool count the longer response descriptions still separate.
Co-authored-by: morluto <[email protected]>
* feat(environment): apply evidence-gated write effects to the served state
Serve write tools whose later reads confirm the effect, and keep main's
state-file ownership and part-wise reply measurement.
Squashed rebase of #5600 onto the rebased #5352 tip.
* fix(environment): bound and validate write state history
* feat(environment): render more read tools from the state and describe responses
Keep main's environment serving and fold in coverage for non-entity reads,
response summaries, and MCP tool annotations.
Squashed rebase of #5352 onto main.
* fix(environment): keep rendered documents and quoted samples inside their budgets
A recorded body of exponential numbers can render past 256 KiB and turn a
working read into a state size error, so that document stays a recorded replay.
Description samples are measured after JSON quoting, and the replay-fallback
catalog check uses a tool count the longer response descriptions still separate.
Co-authored-by: morluto <[email protected]>
* feat(environment): apply evidence-gated write effects to the served state
Serve write tools whose later reads confirm the effect, and keep main's
state-file ownership and part-wise reply measurement.
Squashed rebase of #5600 onto the rebased #5352 tip.
* fix(environment): bound and validate write state history
* fix(environment): bound additional collection history
* fix(environment): credit sent fields, negative sequences, and encoded targets
A later overwrite copied surviving derived fields into the sent-member
claim, so a members-only check rejected an update whose sent field still
matched. Serving floored sequences at zero and stored missing history as
zero, so a collection sequence learned from negative ranks skipped the
next value. An absence after a delete forgot the resolved collection, and
an encoded read could not observe an update spelled with another
collection encoding.
Regression tests cover each case.
Co-authored-by: morluto <[email protected]>
* test(environment): cover resolved encoded update identities
* fix: validate timed update observations
* fix(environment): share write overlap and check clocks on earlier calls
Observation search skips a read begun before the write ended. That
comparison now uses the same overlap as the check path.
A clock learned on a later call was not checked against earlier calls
that kept the field. Serving would stamp those calls too, so the plan
stays unserved. A clock learned earlier is still refused when a later
call does not advance it.
Regression tests cover an overlapping empty update and a published_at
clock that one recorded call leaves unchanged.
Co-authored-by: morluto <[email protected]>
* fix: preserve partial update evidence
* fix(environment): let an unmodelled write explain only the list members that differ
A failed whole-list read now reports which members alone account for the
difference. An unmodelled write excuses the read's writes only when it came
after each such member's last check and pending writes, so an older change
elsewhere no longer hides a newer write the list shows undone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 7b6aab9d7191e600051693f2c192afcfc743d083)
* fix(environment): explain added fields, tie creates to serving's collection, and bound write descriptions
An update explains a field the entity lacked before when the observing
view had shown the entity, so a server-filled field is not dropped. A
create is tied to the held collection serving picks for its path. Write
descriptions and body refusals quote member names and sample them within
a byte bound. The request body is indexed once per explanation, so copied
and slugged fields no longer rescan it.
Replays 07aca54e onto the restacked branch. Its rule that a read begun
before a write ended teaches nothing is already here (fb6785dc), so only
its test is kept.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): state how list reads weigh unmodelled writes, added update fields and quoted members
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* refactor(environment): keep the observing view helpers with the observations
shownBefore and the item-or-list fields check move to the observe module,
keeping the write effects within the line limit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): resolve unmodelled writes onto the held collection
An unmodelled path kept the request's collection spelling. An archive under
the plain spelling of a collection held encoded marked only that plain
collection unknown, so a later update was blamed for the archive's fields
and dropped. Name that entity the way a modelled write does.
Co-authored-by: morluto <[email protected]>
* fix(environment): confirm no write through volatile fields or overwritten replacements, and bound the served clock
A members-only check no longer compares volatile fields, which render
from the read itself. A document replacement credits the members it
wrote or removed at its object, so a later merge that overwrites them
takes its confirmation away. A recorded time past the range of a date
gives no served clock instead of throwing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): leave unknown entities unchecked by failed reads, volatile fields and unchecked calls
A failed item read that is no absence leaves an unknown entity unknown, so
a later usable read still checks its writes. A read showing an unknown
entity again checks no write on a volatile field. An update of an unknown
entity takes its recorded outcome and adds nothing to a built plan. A later
update that does not answer with its entity is observed only by a read
whose view shows every learned clock; without one it is unmodelled.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): fill a create's path fields from the collection serving selects
The gate built a later create's path fields from the recorded request path,
while serving fills them from the held collection it selects for that path,
so a read showing the other spelling's parent could confirm a create serving
cannot reproduce. Rules still learn from the recorded path.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): name unmodelled writes as modelled ones, count fields updates write, and keep later members' types
An unmodelled write below an item path names its entity through itemTarget,
and one at or above a collection resolves the held collection, as modelled
writes do, so an encoded spelling no longer hides the entity it changed.
An update's answer counts the fields its rules write, other than increments,
as available, so a field only the update writes no longer makes it
incomplete-entities. A member a later call first stores keeps that call's
JSON type in the body's kinds, which the body check falls back to.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): let a document replacement overwrite everything under its object
A replacement claims only the members it wrote or removed, but it now
overwrites every earlier effect under its object, so a merge that wrote
the object it replaces loses its confirmation.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): start the served clock from the records whose times a date can hold
A record timed past the range of a date no longer leaves the served clock
without a start, which the store read as zero and stamped writes in 1970;
the clock starts after the latest record a date can hold.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Authenticated capture Evidence whose operation is supported but whose
normalized_result fails its capture schema fell through to a generic
evidence.operation refusal, discarding the schema issues. Each recognized
operation now refuses with the projected schema issues beneath
evidence.normalized_result and retains the schema failure as the cause;
only an operation no capture schema owns blames evidence.operation.
The view Evidence parser had the same conflation: a wrong operation, a
wrong kind and a malformed payload of the requested kind all blamed
normalized_result.kind. It now reports role.operation, the kind, or the
payload's own field paths respectively.
Fixes#6119
Co-authored-by: Claude Opus 5.5 <[email protected]>
Electron and process scenario path admission caught every realpath or
stat failure as an invalid_value input issue, so a correct path beneath a
directory without search permission returned invalid_request with
schema-correction recovery instead of a host access denial.
Admission now classifies EACCES/EPERM from path resolution and metadata
reads as the existing AnalysisAccessDeniedError (selected path,
system_code, filesystem-read boundary). Missing, looping or otherwise
unresolvable paths and reachable files without execute permission keep
their input-error semantics. Electron reports invalid selections before a
denial on another field. Process PATH search still continues past a
denied directory; only an explicitly selected executable reports denial.
Fixes#6117
Co-authored-by: Claude Opus 5.5 <[email protected]>
Construct integer formats directly while preserving bounds, refinements, metadata and current shared schema owners. Keep Incur-dependent CLI numeric coercion declarations unchanged.
Verify canonical advertisements, 126 numeric boundary cases, 974 actual CLI parser cases, broad source tests and real public-artifact SDK workflows. Record 72 normal tasks and 12 separate owned-inspector heap diagnostics with whole-family resource bounds; distinguish SDK spans and asymmetric audit overhead. Generate numerical report sections from receipts and reject stale tables through documentation conformance checks.
Closes#6236. Current-head CI and Greptile pass; actionable review findings are addressed. Codex review was unavailable due repository usage limits.
* fix(ci): run the lockfile-pinned Skills CLI in package verification
The repository skill stage fetched [email protected] and its unlocked
dependencies from the npm registry on every run, taking 5-120 s and
intermittently exceeding the 120 s stage timeout on main and PRs.
Pin skills as a devDependency and run its locked bin directly.
Fixes#6234
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(ci): lock the Skills CLI outside the root dependency tree
skills 1.6+ requires Node >=22.20.0, so pinning it as a root
devDependency made `npm ci` fail under engine-strict on the supported
Node 22.19.x. Keep the root install unchanged and give the verifier its
own package.json and lockfile, installed with `npm ci --prefer-offline`
in the verification workspace. The lockfile still removes all registry
metadata resolution and pins every transitive dependency.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(native): let callers raise the Apple dispatch metadata input budget
inspect_native_dispatch_metadata refused any Mach-O over a fixed 64 MiB
before decoding, and its recovery said there was no override. Most large
macOS binaries exceed it.
max_input_bytes (CLI --max-input-bytes) now selects the budget, default
64 MiB and at most 1 GiB, through the shared input contract, the enhanced
workflow and the native provider. Results record input_admission with the
effective budget, its source and the retained bytes. A refusal names the
observed size, the budget and its source, and the budget to select or, past
1 GiB, a deep analysis provider.
Fixes#3917
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(cli): describe --max-input-bytes in inspect-native-dispatch-metadata help
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(native): cover max_input_bytes forwarding through CLI and MCP
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(pcap): report workspace allocation failures by their system cause
Pcap workspace allocation and snapshot writes mapped every filesystem
error, including ENOTDIR and EACCES from mkdtemp, to a file-size resource
constraint with null limits and worker file-size advice. Flutter's owned
snapshot storage had the same errno-blind mapping, while TShark and sigrok
carried duplicate copies of the correct split.
Move the private-workspace failure projection into one shared owner:
ENOSPC/EDQUOT/EFBIG stay file-size resource constraints with their system
code and path; descriptor exhaustion and every other failure become
provider workspace failures carrying stage, workspace path, failing path,
system code and cause-specific recovery.
Fixes#6123
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(flutter): locate owned storage failures by their workspace
mapOwnedIoFailure always passed a null workspace path, so a descriptor
write or read failure without its own errno path lost every location.
Thread the owned root through: allocation keeps null (no workspace yet),
snapshot failures use the allocated root, and package-reader system
errors propagate to the request owner, which maps them against the
snapshot root instead of mapping early without it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Authorize CDP targets with the caller's operation instead of a fixed
inspect_web_page label, and reuse the authorization owner's derived
origin scope. When a browser observation failure is attributed to a
different operation, keep its producer detail, remediation, cleanup,
diagnostics, partial observation and original cause rather than
rebuilding it from the reason alone. Both the CDP browser and CDP
Electron providers use the same domain-owned relabel.
Fixes#6122
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(process): record withheld unrelated Linux processes as unverified
The token sweep after an owned command (for example the Ghidra Java probe
run by setup and doctor) failed cleanup when an unrelated same-UID process
started during the sweep and Linux denied its /proc/<pid>/environ. Setgid or
setuid programs (mismatched real/saved IDs) and non-dumpable processes are
withheld by the kernel exactly like another account's processes, which were
already recorded as unverified.
Derive the kernel restriction from the same stat-bracketed procfs read that
already supplies the effective UID: all Uid/Gid IDs from status, and the
root ownership procfs reports for a non-dumpable process's environ. Keep that
fact through exit settlement. Related candidates still fail closed.
Fixes#6204
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(process): define caller credentials on hosts without geteuid
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(native): inspect a universal Mach-O slice in Go and Rust metadata readers
inspect_go_binary and inspect_rust_binary refused universal Mach-O files and
offered no slice selection, so hosts without lipo could not inspect them.
Both now accept architecture (x86, x86_64, arm, arm64). One selector reads
REA's own FAT table, validates the slice range, alignment and header CPU,
and returns the thin image with its macho-selected-image-v1 identity. The
universal file stays the artifact; results add selected_slice and keep
every source range a file offset. Missing, absent, duplicate or misplaced
selections fail on architecture and list the slices present. Subject
format mach-o-universal now has one owner shared by the services and the
retained Go/Rust views.
Fixes#4102
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(native): reject damaged universal files and self-contradicting slice reports
A FAT table whose slice could not be read was treated as a non-universal
file, so callers were told to omit architecture. Its reason now fails on
path. Go and Rust reports with a selected slice must be Mach-O and name the
slice's CPU family, through one shared agreement check. The CLI
architecture flag is described in help.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(artifacts): make ASAR header admission configurable
ASAR header/index admission was a fixed 16 MiB with no override, so
operators with large Electron indexes could only extract the archive.
REA_ASAR_MAX_HEADER_BYTES now sets it (default 16777216, maximum
268435456). One owner resolves the effective admission for the
classification probe, inventory, ASAR reader and Electron file scope,
including the Electron header cache budget. Startup and doctor validate
the setting; admission refuses an invalid value instead of falling back.
Recovery and error details name the effective value and its source.
Fixes#6176
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(artifacts): resolve ASAR header admission from the caller's environment
The admission read process.env, so composition roots that select their own
environment (createDirectAnalysis, createServer, CLI, optional observation
factories) could not raise or lower it, and an ambient value leaked into
callers that left it unset.
Nothing reads process.env now. ArtifactProvider and extraction resolve the
admission from their environment; JavaScript analysis parses it from the
environment its server or CLI caller passes and refuses an invalid value;
the Electron and V8 providers receive it from their factories. Inventory,
nested ASAR readers, classification probes and Electron page authorization
carry that one value. A header cached by Electron under a larger admission
is refused for a smaller one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Summary delivery with --evidence_output wrote the bundle, then let the
command's close/cancellation check replace the result. A SIGINT/SIGTERM
during staging cleanup after commit returned typed cancellation without
the receipt, and a persistent staging cleanup failure after commit was
rewrapped as a provider cleanup error that dropped published_path.
Every --evidence_output command now selects its view first and publishes
through publishEvidenceDelivery, the owner inspect-managed-members already
used. Post-commit cancellation keeps {path, bytes} under
details.diagnostics.evidence_bundle; incomplete cleanup keeps
details.published_path and bytes; a cleanup retry that succeeds returns
the normal receipt; pre-commit cancellation creates nothing and names
the command's operation.
Fixes#6095
Co-authored-by: Claude Opus 5.5 <[email protected]>
A captured script whose bytes matched no static digest was reported as
high-confidence unmatched even when no automatic or caller-declared
mapping applied to its location in any static layer. The matcher now
computes mapping presence once while locating candidates and shares one
no-candidate classification between the captured-source and source-free
branches, so an unmapped location stays unknown with
no-authorized-location-mapping and is not counted as unmatched.
Fixes#6061.
Co-authored-by: Claude Opus 5.5 <[email protected]>
The language catalog kept failed .ldefs closes in a module-global set that
only a later catalog read retried, and the provider returned a cleanup error
bound to no owner. Registry admission retries provider close before profile
resolution, so the retained handle was never retried and the original error
stayed incomplete forever.
Read definitions through the provider's caller-owned inspection scope with the
shared regular-file owner used by the seed and extension readers. A failed
close is retained as an exact lease in that scope, provider close and the next
catalog admission retry it, and the translated error is bound to the lease so
confirmed release reopens the same registry.
Fixes#6133
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(process): settle owned cleanup with the launcher's own exit proof
When owned process-group verification fails while the supervised
launcher is still mid-exit, the supervisor now waits up to the kill grace
for its own child-exit observation and verifies once more after it
arrives. Verification is not repeated when the exit was already observed
before cleanup started, so there is no new evidence and the original
failure is kept. Nothing is signaled based on the wait.
Fixes#6134
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(process): bound Hopper session cleanup attempts by exit evidence
The fake Hopper launcher exits after its shutdown acknowledgement. When
the supervisor first observes that exit during a failed verification, it
verifies once more, so the first close makes one or two attempts depending
on timing. The test now asserts that bound. Later session retries, made
after the exit was already observed, make exactly one attempt each.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Fixes#5148.
Add opt-in durable managed member summaries and authenticated paging while preserving complete default output, original Evidence, cleanup ownership and queued cancellation.
Verified with bounded real Newtonsoft/Redux/Rust tasks, independent oracles, 556 focused tests and the expanded domain gate; paired token estimates and latency tradeoffs are recorded in experiments/analysis-efficiency/managed-cli.md.
* fix(process): report the effective regex worker heap
Node gives --max-old-space-size, including NODE_OPTIONS, precedence over
the worker's 128 MiB resourceLimits request. Name that effective
old-generation heap and stop saying the request has no runtime override.
The matching deadline stays a fixed cap.
Co-authored-by: morluto <[email protected]>
* fix(process): keep SIGXCPU and SIGXFSZ retries uncertain
The signal does not prove the retained rlimit was exhausted, so an
unchanged retry may succeed. Project that retry as possible instead of
saying it fails the same way.
Co-authored-by: morluto <[email protected]>
* fix(process): leave percentage heap overrides unestablished
--max-old-space-size-percentage takes precedence over the absolute flag
in either order. Node computes that MiB from startup memory, which a
later read does not recover, so the effective heap stays unknown instead
of the 128 MiB request or an absolute flag.
Co-authored-by: morluto <[email protected]>
* fix(process): read launch NODE_OPTIONS, not process.env
A preload can rewrite process.env.NODE_OPTIONS after V8 has applied the
original flags, and those flags are not copied into execArgv. Use the
initial environment block when it can be read, and leave the effective
heap unestablished when that provenance cannot be recovered.
Co-authored-by: morluto <[email protected]>
* fix(process): leave env-file heap overrides unestablished
--env-file and --env-file-if-exists can supply NODE_OPTIONS when that
variable was absent from the initial environment. The file is not in
/proc/self/environ, so a missing entry is not the 128 MiB request.
An initial NODE_OPTIONS is still parsed, because Node does not let the
file replace it.
Co-authored-by: morluto <[email protected]>
* fix(process): leave --max-heap-size old generation unestablished
A positive --max-heap-size or --max_heap_size sets the total heap, not the
old generation, and can stay below the 128 MiB worker request when an
old-space flag is also present. The effective old generation stays
unestablished while that value is in effect. Zero or empty clears it.
Co-authored-by: morluto <[email protected]>
* fix(process): leave config-file heap options unestablished
--experimental-config-file and --experimental-default-config-file can
apply a heap percentage from a file that is not launch provenance. The
effective old generation stays unestablished when either flag is present,
including beside an absolute --max-old-space-size.
Co-authored-by: morluto <[email protected]>
* fix(process): read launch exec argv from the command line
process.execArgv can be cleared after V8 applies --max-old-space-size.
Linux recovers those tokens from /proc/self/cmdline and leaves the heap
unestablished when that vector cannot be separated from the program entry.
Heap probes compare pinned absolute limits instead of a percentage of host memory.
Co-authored-by: morluto <[email protected]>
* fix(process): leave a title-overwritten command line unestablished
process.title and --title replace /proc/self/cmdline with the title.
A buffer without a program entry is not an empty option list, so the
heap stays unknown instead of the 128 MiB request. The command-line
probe does not read /proc off Linux.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(electron): follow absolute symlinks that stay inside the framework
An absolute symlink target is now passed to relativeInside(root,
target), and a null result still ends the walk as an escape. When the
target lies inside the root, resolved is cleared and the root-relative
pieces are queued on pending, which replaces the path walked so far with
that absolute target. Later hops stay under the hop limit and the
existing lstat containment checks. Provider tests now cover an absolute
Versions/Current that stays on in-framework version B and matches the
relative bundle's resolved path and SHA-256, an absolute Current outside
the framework, and an absolute in-framework link whose next hop leaves
the framework; those last two still surface an escape error.
Fixes#3380
* test(electron): cover absolute framework symlink cycles
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(environment): render more read tools from the state and describe responses
Keep main's environment serving and fold in coverage for non-entity reads,
response summaries, and MCP tool annotations.
Squashed rebase of #5352 onto main.
* fix(environment): keep rendered documents and quoted samples inside their budgets
A recorded body of exponential numbers can render past 256 KiB and turn a
working read into a state size error, so that document stays a recorded replay.
Description samples are measured after JSON quoting, and the replay-fallback
catalog check uses a tool count the longer response descriptions still separate.
Co-authored-by: morluto <[email protected]>
* test(environment): pin the replay card this branch serves
The replay-only card test pinned main's 11,131-byte card. This branch's descriptions name each tool's recorded responses, so the card it serves is 12,818 bytes; the pin and its name follow.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Keep the 16 MiB ASAR header/index ceiling fixed and report that budget,
the lack of a runtime override, and that source selection does not bypass
header admission.
Refs #5910
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(source): retain CommonJS requires spelled as exact no-substitution templates
Admit expression-free template arguments at the CommonJS require gate and
read their single cooked quasi through the shared exact-literal reader.
Scope is the require path only: static ESM template declarations keep their
parse failures and dynamic import() keeps the explicit unknown/partial
<dynamic-import> relationship with no invented target.
Refs: #4382
* fix(source): admit TypeScript-wrapped exact require arguments via the shared literal reader
Address review: the require gate matched the outer AST node, so
runtime-transparent TS wrappers (as const, !, satisfies) around exact
strings or expression-free templates were still silently omitted even
though readExactJavaScriptLiteral unwraps them. Remove the gate and let
the shared exact-literal reader own argument exactness; computed
arguments remain silent and dynamic import() behavior is unchanged.
Refs: #4382
---------
Co-authored-by: Hemang Varshney <[email protected]>
* fix(adb): report a null installer declaration as an absent installer
Package Manager prints its String fields by concatenation, so a package
without a recorded installer shows `installerPackageName=null` in
`dumpsys package`. The projection treated only an empty value as absent
and forwarded the text "null" as a present installer identity, which a
consumer cannot tell apart from a package literally named "null" (#3943).
`versionName` is printed the same way and had the same gap.
The parser now maps the empty value and the `null` sentinel of both
String fields to the nullable result's null at the producing boundary;
a reported installer such as com.android.vending is kept unchanged.
Validation: parser unit tests for the sentinel, empty and missing
installer lines and the absent version name, plus a provider-level
regression through the inspect_adb_package operation; three of them fail
on main and pass here. The issue's four CLI controls through
`rea inspect-adb-package` against a stub adb return JSON null for the
sentinel, empty and missing cases and keep com.android.vending. Typecheck,
lint and module boundaries pass.
Closes#3943
* fix(adb): keep the null sentinel handling to the installer field
Review feedback on #4073: a manifest can declare the literal string
"null" as android:versionName, and dumpsys prints it exactly like an
absent value, so mapping versionName to null would turn a real version
into absence. Only installerPackageName, where no package can carry that
name, treats the printed null as the absent installer. The test for the
version name now pins the retained text.
* test(adb): establish permission inventory in null-installer fixture
Main now reports partial coverage when the requested-permission
inventory is not established (9534e42ec). Give the null-installer
fixture a requested permissions section so it still checks a
complete projection.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(environment): extract the initial state a capture's reads observed
Add the environment-state analysis view. From the parents the environment
manifest accepts, it extracts the data the capture's reads observed before
the first related write: entity types with an identity field (proved by a
path parameter or inferred from an identity name), entities with their
recorded values and provenance, documents for written non-entity paths, and
a render plan per read tool that is kept only when it reproduces the tool's
latest initial recordings from the state; every other read tool is a
recorded view with the reason. The Evidence is always inferred.
Response-body decoding moves to a domain owner shared with replay, exact
JSON parsing moves to the owner of the exact-number policy, the manifest and
state views share parent admission and record grouping, and
projectAnalysisView takes its accumulators as one object.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): bound state at redacted writes; infer identity only from items
A scenario write whose URL was redacted has no request line, so it was
dropped and reads after it counted as initial state. Such a successful
write now joins the timeline with an empty path, whose root relates to
every type, so it ends every type's state; a limitation counts them.
A list whose recordings were all empty passed the "every listed item
carries it" check vacuously and published an identity field nothing
showed; it now needs at least one item, else the view stays recorded
as no-identity.
A value that differed between two request lines of one item tool, such
as a query variant, was called volatile, and the gate then substituted
each recording's own value; only repeats of one request line now show
volatility, so differing lines go through fields-differ.
List membership is compared only while a list can still be the whole
collection, and merged entities are indexed by type and collection once,
so paged captures no longer cost pages times entities (8,000 pages of
40,000 entities: 5.7 s to 0.23 s).
Docs and the tool contract say scenario state keeps the capture's
redaction markers and how redacted writes bound it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep first sightings, one list pointer and linear field unions
Entities took the order of the first plan observed, and item plans run
before list plans, so a complete list read before its detail reads came
out reordered and lost "all" membership; each entity now keeps its
earliest recorded sighting.
A list plan reused the first recording's pointer for later recordings,
so a later response that was itself an entity wrapping a list was read
as that list; every recording must now locate the same list.
The field union scanned its ordered list for every key, quadratic in an
object's width; a set now answers membership.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep post-write values and partial lists out of the initial state
A value that changed between repeats of one request line was named
volatile whenever no write under the type's own root lay between, so a
write under another root (for example POST /api/dashboards/db before a
second GET /api/search) left its effect in the initial state. Any
recorded write between the two observations now leaves every tool of the
type recorded with reason fields-differ instead.
A list was published with membership "all" even when a total or count
envelope member exceeded the number it listed. Such a counter now makes
the membership "recorded".
Also drop a stale doc comment on ObservedEntity.order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): scope item-shaped and deleted written paths out of documents
State documents are held only for list-shaped paths a POST, PUT or PATCH
tool writes. Reads of item-shaped written paths and of paths that are only
deleted stay recorded views; the guide now says so and the spec defers them
to a later increment of #4933.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): count writes in flight when a value changes between reads
The volatility check compared write start positions with the two reads,
so a write already in flight when the earlier read started was missed and
its effect was named volatile. A write now counts when its interval
overlaps the span from the earlier read's start to the later read's end.
The check also ran once per changed field and scanned every write. It now
runs once per pair of observations, answered by a binary search over the
writes in timeline order and the latest end among each prefix.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): catch cross-tool changes across writes; read only item totals
A value that changed across a write was caught only between repeats of
one tool's request line, so a list read before a write and an item read
after it put the post-write value into the initial state. Changes are
now classified per entity in timeline order after observation: across
any recorded write, a change between repeats of a line or between two
lines of the type rewrites the type (every tool recorded as
fields-differ); with no write between, a repeat's change is volatile.
The classification lives in environmentStateMerge.ts.
Any numeric member whose name contained "total" or "count" counted as
an item total, so total_pages, account_id or page_count made complete
lists partial. Item totals are now members whose name tokens include
total or count and none of page, pages or per; a string of digits counts
as a number; and a set has_more, next or next-page token member also
marks the list partial.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep post-write observations out of initial state
* fix(environment): retain field provenance and bound state inference work
* feat(environment): serve tool calls from the environment state
`environment-serve --state <state.json>` serves an environment from the
initial state an environment-state view extracted. Read tools whose views
are state-backed answer from a live state store: item views render the held
entity into the recorded envelope, list views render the collection or the
recorded members, and document views return the held value; a missing entity
or one lacking a required field is refused, and requests the state cannot
answer fall back to their recordings with the reason. The /agentenv data
plane implements data/reset, data/add (a rea-state/v1 document as a data
part, base64 bytes, a file:// path or an https URL, validated and replaced
atomically) and data/get, and the card advertises those operations and an
intake declaration. Stage 1 is read-only: write tools are not served in
state mode. Without --state nothing changes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(environment): record verification after merging the state view's review fixes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(environment)!: serve the environment state by default
environment-serve derives the environment state at startup by running the
environment-state view in-process on the capture with the manifest's
template rules, so an environment comes with its state and a user supplies
nothing extra. The derived Evidence is the same one inspect-analysis-view
produces by hand. --state is removed; --replay-only serves exactly the
stateless replay of main. When the state cannot be derived, or its catalog
does not fit the discovery budget, the server starts in replay mode, logs
the reason and reports it in the stop summary's `state`. Exported bundles
serve the state by default too: export derives it the same way, so the
verifier's tool count and checked answer match what is served, and the
README describes the served mode.
BREAKING CHANGE: environment-serve answers read tools from the derived
state and does not serve write tools unless --replay-only is given; the
stop summary reports `state` instead of `state_evidence_id`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): bound data-plane replies, keep batch order, cancel intake on stop
- One /agentenv response is held to 64 MiB: replies are measured as they
are produced and one past the budget becomes a -32000 error stating it.
- Every message of a batch enters the arrival-order queue on arrival, so
another request's reset never lands between them.
- Stopping the server, or a caller leaving, cancels the active download
and answers queued calls `cancelled` without running them.
- file:// URIs name only this host: another host or a network share is
refused before anything is opened.
- A loaded view value may not give an entity another identity.
- List answers refuse a member lacking a field every recording carried,
as item answers do.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep bodies too deep for the state's nesting out of the state
A recorded body at the JSON depth limit was accepted by extraction, then
nested a few levels deeper as a document or entity value, so sealing the
state Evidence threw and environment-serve and environment-export stopped
instead of falling back. stateBodyJson, the one rule extraction and
serving share, now treats a body nesting deeper than the limit minus 16
levels as unusable, so its tool stays a recorded view.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): measure data-plane replies before building them
83fcc739 bounded one /agentenv response to 64 MiB, but it still
serialized each reply whole before measuring it, so a batch of data/get
calls on a large state built every refused reply in full only to drop
it. Each reply is now measured in bounded JSON parts (the shared
jsonParts encoder), its parts kept only while it fits; a reply past the
budget is counted for its exact reply_bytes but never assembled. A test
checks that a refused read is never handed to JSON.stringify whole and
that its reply_bytes is exact.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): release batch replies as they are encoded; judge exact numbers by value
answerBatch kept every settled reply in its pending array until the
batch ended, so a batch alternating data/add and data/get held each
loaded state alive; each reply now leaves the queue as it is encoded.
The exact-number policy judged a number by its recorded spelling, so a
state loaded with 1e20 came back from data/get as
100000000000000000000, which data/add refused, breaking the promise
that data/get output loads back unchanged. A number is exact only when
both its recorded spelling and the one serialization writes are safe,
so integers from 2^53 below 1e21 are refused in any spelling; replay
keeps such bodies as recorded text. A domain test checks that every
accepted number is accepted again once written back.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep room for refusals and framing in the reply budget
The response budget counted accepted replies only: a budget error that
replaced a reply, and a batch's brackets and commas, were not charged,
so near 64 MiB later refusals pushed the body past the advertised
limit. Every reply still to come now keeps room for its own budget
error, and a batch keeps room for its framing, so a refusal always fits
and the body never passes the budget. Reply ids and which messages are
answered come from one place shared by the replies and the reserve.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): refuse a loaded state that data/get would write past the intake limit
data/add measured only the document it was given, while data/get
writes the state back with the members a document may omit (types, the
state Evidence id, view fields and record citations). A document just
under 32 MiB could therefore load and be reported larger than any intake
accepts, breaking the data/get then data/add round trip. replace now
measures the state as data/get writes it, part by part, and refuses it
past the limit, leaving the state unchanged. The limit moves to the
state document module, so the application store can read it; the
intake re-exports it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): keep merged export checks on the shared test graph
Main's export tests installed module mocks for the whole services
project, which reuses one module graph. A tarball read failure then
missed its importer, and a one-shot heap mock could be taken by another
file. Read those through bindings the tests replace for one case.
Co-authored-by: morluto <[email protected]>
* fix(environment): keep a state file whose close failed owned until it closes
A file:// data/add that could not close its descriptor dropped the handle,
and an admission failure whose cleanup close failed dropped its owner, so
nothing could retry the close or report it.
The read now runs through withOwnedRegularFile, and a failed close is
retained in a server-lifetime ArtifactResourceScope. The next file read
retries it first and answers -32000 while it is still open; inline and
https documents still load. Closing the served environment retries it again
and rejects with the unfinished close.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(environment): attempt all state cleanup on close
* fix(environment): bound wide state JSON traversal
* fix(environment): scope cached state reply sizes to requests
* fix(domain): preserve JSON depth diagnostic guidance
---------
Co-authored-by: Edgar Arakelyan <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Fall back to a kind-based obligation title when a graph label is blank
A whitespace-only application observation or semantic node label passed through ?? as the obligation title, and the ledger schema (trimmed, min length 1) then rejected the whole ledger. Use trim() || so a blank label takes the generated title.
Closes#6126
* fix(mcp): name response-byte and startup ceilings in recovery
Name REA_MCP_MAX_RESPONSE_BYTES, its effective value, the 10485760 byte
floor, and process restart when the stdio response budget is hit, and
state that the setting does not raise Node's string ceiling. Name fixed
MCP startup phase budgets when those deadlines fire, and keep Codex's
30 second startup figure as client guidance.
Co-authored-by: morluto <[email protected]>
* fix(mcp): distinguish string ceilings and keep dossier recovery
Name REA's reserved string limit separately from Node's ceiling, restore
detail:summary discovery for an oversized complete evidence bundle, and
apply the doctor deadline sentence when binary_session itself times out.
Co-authored-by: morluto <[email protected]>
* fix(mcp): keep retained SDK errors inside the text budget
Quoted SDK validation diagnostics expand when recovery JSON is escaped
into MCP text. Shorten only that preview so the retained-evidence
reference still arrives as parseable error content.
Co-authored-by: morluto <[email protected]>
* fix(mcp): keep export advice when a summary exceeds the response budget
An oversized get_evidence_bundle summary no longer repeats detail: summary.
export_evidence_bundle still writes the retained bundle outside one MCP frame,
including when a larger byte budget cannot raise the string ceiling.
Co-authored-by: morluto <[email protected]>
* style(mcp): format the SDK diagnostic preview return
oxfmt rejected the fitted-preview ternary in EvidenceMcpServer.ts, which failed Static checks and the CI required gate.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(browser): name web-bundle budgets and recover web deadlines
Name the existing heap, per-script, and total web-bundle phases, and reject a per-script deadline above the total. Promote capture, CDP command, source-map decode, and source-map fetch deadlines at today's defaults. Name DevTools timeoutMs, and state that discovery, cleanup grace, byte budgets, and Electron hook retention have no runtime override.
Refs #5910
Co-authored-by: morluto <[email protected]>
* fix(browser): apply selected web deadlines to the owning phase
Keep HTTP and browser-socket discovery on the fixed discovery deadline so a
short command_timeout_ms starts at the handshake. Apply scenario
command_timeout_ms to launch startup and the primary connect session, and
keep service-worker restoration on the fixed 5000 ms grace. Omit
command_timeout_ms from recovery text when the operation does not accept it.
Co-authored-by: morluto <[email protected]>
* fix(browser): retain scenario commands that finish after their deadline
A timed-out scenario command stays owned so cleanup can close a late
browser or context and restore a mutation that lands after the deadline.
Child storage replies use that deadline, scenario recovery names
command_timeout_ms in the capture JSON, and the session test connection
implements beginCleanupGrace.
Co-authored-by: morluto <[email protected]>
* fix(browser): keep child command timeouts as the public failure
A nested command such as Page.enable that is delivered but never answered
was stored as protocol_error, hiding the command name, selected deadline,
and recovery. Store the typed timeout and assert that public error.
Co-authored-by: morluto <[email protected]>
* fix(browser): keep runtime cleanup on the fixed grace
Switch runtime restoration to the cleanup grace before the first send so a
short command_timeout_ms cannot fail an otherwise successful observation.
Stop advertising command_timeout_ms on list_browser_targets, which never
bounds a post-discovery command.
Co-authored-by: morluto <[email protected]>
* test(browser): prove a short command deadline cannot fail runtime cleanup
Drive restoration through the runtime fixture. A cleanup command that
outlives command_timeout_ms still succeeds, and a stalled restoration
names the fixed 5000 ms grace.
Co-authored-by: morluto <[email protected]>
* fix(browser): name the effective source-map decode deadline
codecBudgetLimitation reported a fixed 20-second deadline after a
caller selected decode_timeout_ms. Result and Evidence limitations
now name that effective millisecond deadline.
Co-authored-by: morluto <[email protected]>
* fix(browser): separate handshake and target-attach deadlines
A browser-level Target.attachToTarget no longer shares the WebSocket
handshake budget. Blocked service-worker attachment keeps discovery on
its fixed deadline and applies command_timeout_ms to the later handshake
and attach.
Co-authored-by: morluto <[email protected]>
* fix(browser): name the web-bundle resource each runtime exhausts
Bun ignores the V8 old-space flag. A Windows Job reports the process
commit limit, and other Bun hosts no longer suggest raising max_heap_mb.
Co-authored-by: morluto <[email protected]>
* fix(browser): cite the browser-socket discovery message budget
WebSocket discovery enforces the 64 MiB protocol budget. Timeout
recovery no longer advertises the 8 MiB HTTP document budget.
Co-authored-by: morluto <[email protected]>
* fix(browser): bound the blocked service-worker check before discovery
The first registration check has its own fixed 10s deadline. Discovery,
handshake, and Target.attachToTarget stay on their separate deadlines.
Co-authored-by: morluto <[email protected]>
* fix(browser): name the operand that stopped web-bundle admission
Host heap pressure no longer claims the fixed result budget is exhausted.
A Windows Bun allocation before the commit limit still names that limit.
Co-authored-by: morluto <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-22 05:47:38 +08:00
6247 changed files with 970210 additions and 241269 deletions
description:Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
description:Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA, and build agent environments (AgentEnv bundles) of web applications from a live instance or a capture. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
metadata:
version:"44"
version:"60"
---
# REA
Use REA when a claim depends on a shipped binary or package, decompilation,
passive application runtime evidence, or comparison with
requested runtime evidence, or comparison with
behavior not established by available source. For ordinary analysis of a
complete source repository, use normal repository tools and do not run REA
readiness or provider commands.
@@ -17,6 +17,9 @@ readiness or provider commands.
Installing this skill supplies instructions; it does not register the REA MCP
server, install analysis engines, or add tools to an already-running agent.
The exception is a Claude Code or Codex plugin install: the plugin bundles
this skill and registers its own `rea` server, outside the state that
`rea setup` and `rea doctor` manage.
If REA tools are available and their registration is not known to be stale,
proceed directly to the target. Do not run diagnostics or setup before every
@@ -32,39 +35,28 @@ original task after repair. Client approval, shell PATH, registration and engine
availability have different remedies; avoid repeating setup for all of them.
If the host times out without a REA result, do not repeat the unchanged call or
assume work stopped. Read the recovery guide's deadline distinction before retrying.
If the MCP connection drops, retry that call once only when it is safe to
repeat or known not to have started. If it may already have changed the
target, or it fails again, tell the user and ask them to restart the MCP
client. If REA could not start its connection, retry once, then tell the user
and ask them to restart the MCP client. Do not retry a startup failure that
returns the same unchanged result. For plugin installs, follow
| No REA tools in the active session | Inspect registration with `rea doctor --client codex --json`. A skill installation alone does not register MCP. |
| Missing, malformed or stale registration | Prepare a scoped setup plan; apply only authorized changes with backups. |
| Aligned registration, but no connected tools | Restart/reconnect the client. Doctor checks registration; it cannot establish the current agent's connection. Inspect the client's launch error if reconnection fails. |
| `MCP tool call requires approval, but approval policy is never` | Codex rejected the call before REA executed it. Do not install an engine or repeat setup to solve this client-policy decision. See the approval section below. |
| REA returns `provider_unavailable` or an unavailable tool entry | Read that operation's provider diagnostics or `binary_session.result.tool_availability`. Expand each group's tool names and inherit the shared client requirements unless overridden. Use the operation's availability, reason, and remediation; a complete tool catalog does not imply every operation is available. |
| `rea` is missing in a child shell, or Node is incompatible | Inspect the actual shell PATH and launcher/runtime. A prefix installation may be absent from a login shell's PATH; this does not establish a broken MCP registration. |
| No REA tools in the active session | If REA was installed as a client plugin, follow the plugin section below. Otherwise inspect registration with `rea doctor --client codex --json`. A skill installation alone does not register MCP. |
| Missing, malformed or stale registration | Prepare a scoped setup plan; apply only authorized changes with backups. |
| Aligned registration, but no connected tools | Restart/reconnect the client. Doctor checks registration; it cannot establish the current agent's connection. Inspect the client's launch error if reconnection fails. |
| MCP connection dropped or REA could not start | If the connection drops, retry that call once only when it is safe to repeat or known not to have started. If it may already have changed the target, or the same failure returns, tell the user REA's connection failed and ask them to restart the MCP client. If REA could not start its connection, retry once, then tell the user and ask them to restart the MCP client. Do not retry a startup failure that returns the same unchanged result. Run `rea doctor` when REA could not start or tools stay missing after restart. Doctor cannot restore a live connection by itself. |
| `MCP tool call requires approval, but approval policy is never` | Codex rejected the call before REA executed it. Do not install an engine or repeat setup to solve this client-policy decision. See the approval section below. |
| REA returns `provider_unavailable` or an unavailable tool entry | Read that operation's provider diagnostics or `binary_session.result.tool_availability`. Expand each group's tool names and inherit the shared client requirements unless overridden. Use the operation's availability, reason, and remediation; a complete tool catalog does not imply every operation is available. Request `{"detail": "complete"}` only for provider diagnostics. |
| `rea` is missing in a child shell, or Node is incompatible | Inspect the actual shell PATH and launcher/runtime. A prefix installation may be absent from a login shell's PATH; this does not establish a broken MCP registration. |
If the client cannot launch its shell/sandbox helper or read the installed skill,
inspect its launch diagnostic. For example, Codex can refuse helper aliases when
@@ -56,8 +61,13 @@ authorized CLI fallback. Do not silently broaden permissions or switch targets.
A returned REA `provider_timeout` instead means the **server/provider** reached
its deadline. Follow its `remediation.action`, reuse accessible partial Evidence,
and resolve reported incomplete cleanup before retrying. Raising only the
client deadline does not change this server budget. For static JavaScript,
and resolve reported incomplete cleanup before retrying. Change a request
field or a `REA_*` setting only when the returned remediation explicitly
recommends that change. Restart REA when that text says the value is read at
startup. A mention that a control does not raise the deadline is not a
recommendation to change it. Do not invent a setting the remediation does not
name. Raising only the client deadline does not change
and the exact retained Evidence ID to read selected facts without reanalysis.
The default MCP reply is already a summary of that dossier: its
`normalized_result.parent_evidence_id` is the retained ID to page, and its
null counts mark facets the provider did not record or declared unavailable.
Native facets also include procedure, pseudocode, assembly, basic blocks,
comments, callers/callees, references, unresolved calls, referenced strings and
names, the native API record, and value-flow summaries. Pseudocode offsets and limits count UTF-16
@@ -179,36 +356,84 @@ run, use `observe_native_calls` with explicit breakpoints and a bounded
`duration_ms`/`max_events`. It launches a new process under LLDB, so the target
runs and may change files or use the network. Hardened-runtime targets need the
`get-task-allow` entitlement.
Function breakpoints use exact LLDB runtime names: pass an `inspect_macho` export
or import `runtime_name` (`main`), not its symbol-table `name` (`_main`). REA does
not translate breakpoint names. Set `module` to the image
file name when selecting that image only; otherwise loaded system images also
match. Inspect retained locations and module paths before attributing a call.
Use `binary_session` with no arguments to check the open target, selected
provider, and alignment. Its default `result.tool_availability` carries a
complete inventory grouped by shared facts. Expand each group's`names` and
inherit the inventory-level client requirements unless a group overrides them;
availability, reasons, remediation, modes, and flags remain inline. The
`effects` and `annotations` arrays name true flags from fixed vocabularies, so an
omitted known flag is false. `tools/list` remains the canonical source for full
tool schemas when target or provider state changes.
provider, and alignment. Its default summary `result.tool_availability` carries
the complete tool inventory grouped by current status. Expand each group's
`names` and inherit the inventory-level client requirements unless a group
overrides them; availability, reasons, remediation, and modes remain inline.
Provider candidates keep availability and target-support status, code, and
reason. `omitted_fields` names what the summary leaves out; `{"detail":
"complete"}` adds each unavailable candidate's `remediation`, provider
diagnostics, candidate capability sets, and tool surface, `effects`, and
`annotations`. Those arrays name true flags from fixed
vocabularies, so an omitted known flag is false. `tools/list` remains the
canonical source for full tool schemas when target or provider state changes.
Provider capability groups carry provider identity and ordered limitations.
Prepend each set's `shared_limitations` to a group's `limitations` to recover the
operation's full ordered list. Candidate capabilities describe possible
providers. Top-level session capabilities describe the current route, and
operation's full ordered list. Candidate capabilities, returned with complete
detail, describe possible providers. Top-level session capabilities describe the current route, and
`analysis_provider_binding` identifies the selected provider.
## Continue editable Ghidra analysis across processes
For a task spanning agent sessions, check `binary_session` after selecting
Ghidra: the current provider's `capabilities[].shared_limitations` states whether
Program checkpoints are enabled or the session is ephemeral. Successful native
queries distinguish initial analysis from an actually restored packed Program;
do not infer that restoration happened merely because checkpoint mode is enabled.
In ephemeral mode, choose a private absolute directory and set
`REA_GHIDRA_CHECKPOINT_DIR` in the environment starting REA. Restart the MCP server
after changing that launch configuration; no current tool call or runtime
analysis-setting update enables it. Stock ELF/PE/Mach-O imports on Linux/macOS
are supported; see the [checkpoint setup and limitations](https://github.com/morluto/rea/blob/main/docs/installation.md#automatically-checkpoint-an-owned-ghidra-program).
With the setting enabled, normal opens restore compatible saved Programs
automatically. Initial completed analysis is saved, and `close_binary` saves
later metadata edits; no separate save/restore tool is needed. Repair a reported
save failure and retry close. Cancellation or process failure can lose edits
after the last successful save; unfinished initial analysis cannot resume.
Keep the same checkpoint configuration for later processes. Evidence/Unknown
history and immutable REA query snapshots have separate persistence workflows.
## Reuse caller-selected Ghidra analysis
When the caller supplies an expanded project, configure `REA_GHIDRA_PROJECT_PATH`
(the `.gpr` marker) and optionally `REA_GHIDRA_PROGRAM_NAME` (one root program),
then restart/reconnect before `open_binary`. CLI uses the same configuration.
REA opens only a fingerprint-bound owned copy with `-process -noanalysis -readOnly`;
original project files are not opened by the engine or saved. Database edits,
import overrides and repair scripts are unavailable in this mode. Treat original
analysis completeness/settings as unknown and prior edits as caller-project state.
The program's recorded import digest must match the target; changed project content
requires reopening for a fresh profile. See the
[setup and preparation guide](https://github.com/morluto/rea/blob/main/docs/installation.md#reuse-an-existing-ghidra-project).
## Ghidra startup deadline recovery
`provider_timeout` during the first Ghidra query can cover import, auto-analysis,
bridge startup, and readiness. The default 330,000 ms is configurable through
`REA_GHIDRA_STARTUP_TIMEOUT_MS`; a smaller query still needs the same cold import.
Preserve useful Evidence before closing the session or restarting REA. Check
`close_binary` and resolve reported cleanup failures. Set a larger startup budget
in the environment that launches REA, then restart REA and reconnect the MCP
client before reopening the same target. Closing and reopening a binary in the
existing server keeps its original configuration and starts import again.
Environment changes in an agent's shell do not update an already running MCP
server. If its launch configuration cannot be changed, report that constraint
and the exact retry setting instead of claiming a fixed technical limit.
When the error names `ghidra.startup_timeout_ms`, call
`update_analysis_settings` with `action: "set"` and a larger value within the
reported bounds. Resolve retained cleanup before the next startup attempt.
No REA restart is needed; an already accepted startup keeps its original
budget. Follow the error's session-specific restart guidance, and preserve useful Evidence before closing
and reopening the target when remediation requires that lifecycle step.
For a per-function timeout, update `ghidra.decompile_timeout_seconds` and retry
the same function on the existing Program. Explicit `null` removes that
per-function deadline. Inspection is optional when the error supplies the
setting and bounds. Runtime updates disappear when REA exits; environment
changes only seed future REA processes.
The client's tool-call deadline must allow startup plus the requested query;
server settings and progress notifications cannot override it. CLI analysis
@@ -225,6 +450,50 @@ Exported C and embedded resources remain useful independent evidence, but a
timeout does not establish complete analysis or absence of code. See the
[deadline and client guide](https://github.com/morluto/rea/blob/main/docs/mcp-contracts.md#ghidra-first-query-deadlines-and-recovery).
## Bridge request ceiling
Hopper and Cutter share `REA_BRIDGE_MAX_REQUEST_BYTES`. Unset, both sides use
16777216 bytes. The payload budget excludes the terminating LF. A decimal
integer from 7464 to 536870887 raises or lowers that ceiling. The floor fits
the largest bounded symbol-name command. A tight Cutter status still returns
its session id and up to 64 completion receipts. Its path is included when it
fits, and is unknown when it does not, so an older descriptor path is not
paired with the new generation. The maximum is the encoded payload, one byte under Node's string ceiling so
the framing LF still fits. Cutter measures, signs, and writes a fitting
response in bounded chunks. The bridge searches only the newly read bytes
for the framing LF. The client unescapes each socket chunk into one response
string before parsing it, so this ceiling does not keep the paged wire frame
and another concatenated copy. Invalid values are configuration errors and do not fall back to 16777216. Set the variable in the environment that starts REA, restart REA,
and start Hopper or Cutter from that same environment. An out-of-band bridge
without the setting stays at 16777216 bytes. The client reports that mismatch
during discovery only after the hello proves a live authenticated bridge, and
does not send a status request or the command. A stale descriptor is ignored.
Discovery fails if any such live ceiling differs, even when another live
session matches. A command response discarded for that ceiling stays an
unknown execution with `output_truncated`. A hello
that never sends its authenticated frame does not hold the accept loop.
At most 8 connection threads run; a further accept is closed, and a failure
to start a thread does not stop the listener. `completed_connection` is the
longest prefix of connections that have terminated. Dense scalar replies are
encoded in bounded batches.
The 25000 ms Cutter request deadline and the 256-candidate discovery budget
still have no runtime override.
## Selected native facts and call graphs
For a large `get_call_graph`, or repeated `procedure_address`,
`procedure_callees`, `procedure_callers` and `resolve_containing_procedure`
queries, select `detail: "summary"` when the first 64 recorded observations
suffice. Read further `native-observation` pages from the returned parent Evidence
ID. Graph message IDs index that page's exact-text dictionary; keep level,
source index, failed nodes, and partial/unknown relationship coverage.
Read referenced source context through `native-context` facets `overview`,
`analysis-profile`, `limitations` and `result-limitations`. Reuse inspected
context/profile values only for the same digest; until caveats are read,
interpretation beyond the returned facts remains unknown. Complete Evidence
remains retained and exportable. Small complete replies can cost fewer tokens
than additional context/page calls; count the completed task and discovery.
## Static memory and original file bytes
For Ghidra, `read_bytes` reads initialized analysis memory, including loader
@@ -252,6 +521,14 @@ loaded bytes, file mappings, relocations and the entry against the immutable
snapshot. Keep `mismatch` and `unsupported` explicit. Verified import covers the
reported static image, not runtime DOS or PC-98 hardware behavior.
For a large load-image report, select `detail: "summary"` and query its
`parent_evidence_id` with `inspect_analysis_view` kind `native-load-image`.
Choose a collection such as `relocations` or `mappings`, select only needed
fields, and page with offset/limit. Original indices preserve same-address
alternatives. Null collections remain unrecorded or inapplicable; paging does
not upgrade unsupported verification. CLI requires `--evidence_output` for
summary delivery so complete source Evidence remains accessible.
For Ghidra decompilation, `procedure_pseudo_code` also accepts `procedure: "file-offset:0x..."`; CLI `rea decompile <path> --file-offset=0x... --provider ghidra` resolves the source byte offset internally. An unresolved 0x-prefixed numeric procedure selector also falls back to a unique file mapping. Existing symbols and analysis addresses win; multiple mappings fail as ambiguous. Preserve the returned file-offset and address locations and raw selection observations. ELF virtual addresses are distinct from file offsets; never assume an arbitrary offset plus image base is an analysis address.
Complete function body ranges use inclusive ends and can contain gaps. Do not
@@ -295,6 +572,11 @@ names), coverage and per-collection counts. Page rows with
Native DMG traversal automatically uses a read-only mount on supported macOS
hosts, with an owned temporary mount directory and cleanup. It needs no approval
flag. Unsupported hosts or mount failures remain explicit limitations; do not
@@ -366,6 +658,8 @@ For headerless DOS COM, explicitly open with `format: "dos-com"` (CLI
analysis: the entry is `0x10100`, file offset zero, with imposed real-mode segment
context. PSP/stack/device state remains unmodeled. See [DOS guide](https://github.com/morluto/rea/blob/main/docs/ghidra-dos.md).
Use `inspect_byte_entropy` to triage high- and low-entropy ranges before extraction.
For other headerless images (embedded firmware without ELF/Mach-O/PE/MZ
headers), open with `format: "raw-binary"`, `architecture` (`x86`, `x86_64`,
`arm`, `arm64`, `6502`, or `m68k`), and `entry_point` (`0x` hex). File offset 0 is mapped at
@@ -378,9 +672,27 @@ semantics are unsupported. `m68k` requires an even entry and decodes the 68040
superset of the 68000 family; Amiga/Atari containers and vector-table entry
discovery are unsupported. Windows Ghidra P0 admits raw images only for `6502`. See [raw binary guide](https://github.com/morluto/rea/blob/main/docs/raw-binary.md).
Intel HEX, S-record, UF2 and DfuSe files are containers, not raw images:
`inspect_artifact` lists address regions (UF2 per familyID, DfuSe per element
with its alternate setting), and `extract_artifact` writes each region. Open an
extracted region as `raw-binary` with its start address as `entry_point`. UF2
`integrity_policy`. See [UF2 and DfuSe](https://github.com/morluto/rea/blob/main/docs/uf2-dfuse.md).
### Function annotations in Ghidra
Use `annotate_native_function` for one function name and/or entry comments, with at least one explicit change. Review its annotation readback and refreshed dossier inline. Changes are atomic and session-scoped; empty comments clear them, omitted fields preserve them. Later MCP calls observe edits until close. CLI `annotate-native-function` returns the updated analysis before discarding the session. Original executable bytes are unchanged; edits invalidate immutable snapshots. Windows P0 does not admit database mutations. For naming only, `set_address_name` / `set_addresses_names` also work with Ghidra: they name function entries or data addresses (primary labels) and return compact per-address success booleans instead of a dossier.
Fresh-import sessions admit annotations; existing-project reuse is read-only.
Use `annotate_native_function` for one function name and/or entry comments, with at least one explicit change. Review its annotation readback and refreshed dossier inline. Changes are atomic; empty comments clear them, omitted fields preserve them. Later MCP calls observe edits. Normal close saves them when owned Program checkpoint mode is enabled; ephemeral mode discards them. CLI `annotate-native-function` follows the same setting when closing after its updated result. Select the [checkpoint workflow](#continue-editable-ghidra-analysis-across-processes) before edits that must survive a later process. Original executable bytes are unchanged; edits invalidate immutable snapshots. Windows P0 does not admit database mutations. For naming only, `set_address_name` / `set_addresses_names` also work with Ghidra: they name function entries or data addresses (primary labels) and return compact per-address success booleans instead of a dossier. Ghidra rejects batch addresses that request the same name or a name another selected address holds; `raw_result.failures` gives each failure's reason.
For `compare_functions`, obtain `analyze_function` Evidence before and after the
edit while the MCP session is open; annotation Evidence is not a comparison
input. Export the needed Evidence before close if comparison will happen later.
Authenticated same-artifact coordinates preserve renamed entries as
`matched / same-entry`, with the canonical `entry_address` inline; name and
exact-text changes remain separate dimensions. This requires compatible
architecture, provider-owned mapping and selected-image commitments. Equal
| Which selected native entry is reached, with which integer argument registers? | `observe_native_calls` on the active Mach-O, with exact function/Objective-C breakpoints and a duration/event bound | macOS with LLDB and required debugger access. Launches a new owned process, stops at entries, records and continues; target execution may write files, show UI or use the network. It never attaches to an existing process. Inspect termination and partial coverage after the run. |
| What does caller-authored instrumentation observe? | `list_frida_devices` / `list_frida_processes` when discovery is needed; `start_frida_session`, `load_frida_script`, then status/unload/close | Requires compatible installed Frida bindings and selected device access; the real REA verifier covers Linux. Attach selects an existing PID; spawn selects a program and starts it paused until `resume_frida_session`. Scripts execute in the target and can change memory, files, network activity and behavior. This is not passive inspection. |
| Which debugger state or controlled execution explains the behavior? | `start_gdb_session` and `gdb_console`, or `start_rizin_debug_session` and `rizin_debug_command` | Persistent debugger workflows are Linux-only. GDB starts a debugger; console commands choose/load/attach/run targets. Rizin debug start launches the selected target and requires a durable `inferior_tty`. Both consoles are unrestricted and can change target memory/registers, run shell commands, write files and use the network. |
| How do terminal output, process lifetime and selected files differ between runs? | `capture_process_scenario`, then `compare_process_captures` with compatible recorded inputs | New captures require Linux or macOS and a working native PTY backend; macOS also needs `xcrun`/Swift for ownership inspection. Capture launches an owned command with ordinary user permissions. Observation paths select snapshots, not a filesystem sandbox. Comparing retained captures does not execute targets and is also available on Windows. |
| What does an existing desktop window show, or what changes after a declared UI action? | `observe_native_ui`; use `capture_native_ui_scenario` only for requested actions | macOS 14+ with required Screen Recording/Accessibility access. Select an existing PID/window. Observation does not launch or own the app; scenario actions can change its data or trigger network activity, and leave its state as-is. |
Close each persistent session with its matching tool. `close_frida_session`
unloads scripts and detaches; it does not kill the target. A still-paused spawn
is resumed only after instrumentation cleanup is confirmed. GDB and Rizin close
can detach a live inferior and let it continue only after verifying its durable
output terminal. When disposition or cleanup is unresolved, preserve the owned
session and reported recovery; do not assume that closing the debugger terminated
the target. Process capture and LLDB call observation instead own their launched
processes and report their termination/cleanup outcome. Cancellation is not proof
that cleanup completed.
Unrestricted GDB/Rizin commands and relevant start/close operations retire an
active binary-analysis session in the same MCP server. Close an active Ghidra
session explicitly first; after target effects settle, reopen the artifact before
new static analysis. A Cutter bridge command is likewise unrestricted and may
affect its GUI/target; it is not a passive native observation substitute.
To test a static hypothesis across two builds, retain compatible static
comparison Evidence and `compare_process_captures` Evidence. Supply explicit
finding/dimension mappings to `correlate_static_and_runtime`. It does not ingest
arbitrary Frida, LLDB or debugger traces, auto-match names, or prove causality
from cochange. Preserve those traces as their own Evidence and leave unobserved
behavior unknown. Record truncation, unmatched breakpoints, changed target
identity and cleanup uncertainty as limitations, rather than treating no retained
event as proof that the behavior never occurs.
Detailed contracts and prerequisites: [Frida](https://github.com/morluto/rea/blob/main/docs/frida-instrumentation.md),
@@ -22,10 +22,16 @@ Keep proof of ownership separate from validity, editability, and readiness. Malf
For lifecycle changes, follow [ownership and completion invariants](docs/tool-design.md#own-the-lifecycle) and select the applicable [lifecycle regressions](docs/testing.md#lifecycle-regressions). Include late handshakes, cancellation during final cleanup, mixed child outcomes, and cleanup retry where those stages exist; active-execution cancellation alone does not cover finalization.
Before adding complexity for a speculative or vanishingly unlikely race or security edge case, explain the concrete failure mode, likelihood, and complexity cost to the user and get their buy-in. Do not silently expand scope for theoretical robustness.
Assess brittleness in touched code during implementation, debugging, review, and maintenance. Fix the producing representation or ownership rule before adding downstream repairs. Normalize once at the owning boundary using format-aware parsers. Remove superseded representations, aliases, wrappers, and redundant tests after verifying affected consumers. Introduce abstractions only when they remove observed complexity.
When fixing a bug, inspect the producing boundary and affected consumers for duplicated interpretation or unclear ownership. Complete any directly related refactor in the same change and remove superseded code. Passing regression tests alone does not establish that the brittleness is resolved. If related cleanup remains, explain what remains and why.
After confirming a bug, search sibling adapters, consumers and similar workflows
for the same failure. Verify suspected matches, record working comparisons, and
group findings by root cause instead of filing duplicate reports.
When addressing a review thread, inspect the producing boundary and affected consumers for brittleness. Complete directly related refactoring in the same change. Before resolving the thread, explain the root cause, structural fix, validation, and any remaining cleanup.
Parse unknown boundary values and model expected failures with the tagged error algebra and `Result`. Preserve partial facts and meaningful failure reasons. Keep identity and lookup values separate from display formatting; preserve source values and explicit unknowns when normalization loses information. Absolute paths, file URLs, and HTTP paths have different semantics.
@@ -51,15 +57,18 @@ Optimize in this order:
3. CPU and memory. Avoid unnecessary work and copies; bound concurrency,
retained state and serialization growth.
For changes affecting these costs, measure representative completed tasks and
compare equivalent workflows before and after where available. Report token
usage, elapsed time, CPU time and peak process-family memory. Separate cold
Reserve performance measurements for changes that target performance or have a
to helper calls, imports, or representations alone do not require synthetic
benchmarks. When measurement is warranted, measure representative completed
tasks and compare equivalent workflows before and after where available. Report
token usage, elapsed time, CPU time and peak process-family memory. Separate cold
and warm runs, label estimates, and state unmeasured costs. Fewer bytes alone
do not establish fewer tokens or faster analysis.
## Development
Use [.nvmrc](.nvmrc) and `package.json#packageManager` for the pinned toolchain. Cursor Cloud may place older Node.js first on `PATH`; environment setup installs the pinned toolchain under `/usr/local/bin`.
Use [.nvmrc](.nvmrc) and `package.json#packageManager` for the pinned toolchain. Cursor Cloud may place older Node.js first on `PATH`; environment setup installs the pinned toolchain under `/usr/local/bin`. Prefix `PATH="/usr/local/bin:$PATH"` before `node` and `npm`.
-`npm ci`: install locked dependencies.
-`npm run build:cached`: build the runtime and packaged skill.
@@ -70,13 +79,24 @@ Use [.nvmrc](.nvmrc) and `package.json#packageManager` for the pinned toolchain.
Use relevant checks for the change. The complete local gate, `npm run check:pr`, is optional for broad changes or CI diagnosis. See [CONTRIBUTING.md](CONTRIBUTING.md) for development, generated-file ownership, and release conventions.
When changing installers or setup recovery, follow
[Checking installers and publisher downloads](docs/testing.md#checking-installers-and-publisher-downloads).
Coordinate expensive checks across agents. If a test unexpectedly consumes heavy CPU, stop its owned process family and profile a bounded representative case before retrying. Inspect production work, fixture generation, subprocesses, and repeated setup; preserve regression coverage when optimizing. Follow the [test resource guidance](docs/testing.md#developer-commands).
`docs/public/product-catalog.json`, `docs/verification/managed-conformance-*.json`, and `skills/` are ignored outputs. Edit source contracts and `.agents/skills/reverse-engineer-anything/`, then regenerate as needed. `.cache/mcp-tool-catalog.json` is test metadata; runtime builds and source checking do not consume it. Never commit binaries, provider project documents, credentials, `dist/`, `node_modules/`, or local planning artifacts.
## Tool and Test Changes
For tool design, follow [docs/tool-design.md](docs/tool-design.md): start from the analyst question, reuse existing contracts, preserve caller choices, and return useful evidence inline. Limits must follow real format, protocol, authority, or resource constraints. Account for representation expansion before allocation; derive completeness from examined coverage.
For test changes or provider claims, use [docs/testing.md](docs/testing.md). Prefer real public CLI/MCP workflows, then production-boundary integration, then producer goldens. Keep module tests for distinct semantics stronger workflows cannot reliably reproduce. Preserve malformed-input, cancellation, cleanup, permission, capacity, and target-identity coverage when pruning. Report unverified coverage explicitly; simulated providers do not establish real-provider behavior.
For user- or agent-facing tool problems, prioritize fixing the owning behavior,
contract, and actionable result or recovery at the point of use. Document the
result in product docs when explanation is needed. Add skill guidance only for
workflow judgment or context that the tool cannot reasonably convey; do not use
a skill as the primary repair for a discoverability or contract defect.
For test changes or provider claims, use [docs/testing.md](docs/testing.md). Select applicable [boundary regressions](docs/testing.md#boundary-regressions) for parser, inventory, snapshot, and projection fixes. Prefer real public CLI/MCP workflows, then production-boundary integration, then producer goldens. Keep module tests for distinct semantics stronger workflows cannot reliably reproduce. Preserve malformed-input, cancellation, cleanup, permission, capacity, and target-identity coverage when pruning. Report unverified coverage explicitly; simulated providers do not establish real-provider behavior.
Use Conventional Commit subjects and PR titles. Mark breaking changes with `!` or a `BREAKING CHANGE:` footer. Describe behavior changes, relevant validation, and real-provider verification scope.
- Build local agent environments from network captures, browser scenarios, or a prompt, then serve derived reads and evidence-gated writes and compare them with fresh application captures.
- Inspect more shipped formats, including GPU code objects, BEAM, Python bytecode, Unity IL2CPP metadata, Godot PCK, PyInstaller, Node.js SEA, devicetree, and additional PE/ELF tables.
- Keep large investigations focused with retained summaries, selected fact queries, configurable recovery limits, and row-oriented CLI exports. Complete Evidence remains available.
### ⚠ BREAKING CHANGES
- **Function analysis:**`analyze_function` now defaults to a retained summary. Set `detail: "complete"` for the previous inline dossier; the CLI retains its complete default and requires `--evidence_output` when selecting a summary. [#5660](https://github.com/morluto/rea/pull/5660). Thanks @CryptoJones, @gensokyo-dev, @RishTheFish21.
- **Accessibility:** Accept JSON numbers and booleans in node `value`, alongside strings and null. Update consumers that previously assumed text-only values. [cc90771](https://github.com/morluto/rea/commit/cc90771af14f045538651615147d8bf86b576549), [#6759](https://github.com/morluto/rea/pull/6759), [#6467](https://github.com/morluto/rea/issues/6467). Thanks @coygeek, @gensokyo-dev, @mvanhorn.
- **JSON Lines:**`--format jsonl` now emits object-collection rows followed by a `$rea: "jsonl/v1"`, `type: "metadata"` record; scalar selections and unsupported reports are refused. Parse control records separately, select a single supported collection when necessary, and use `--format json` for the previous whole-document output. See [JSON Lines framing](https://github.com/morluto/rea/blob/rea-agents-6.8.0/docs/cli.md#json-lines-record-output). [#5629](https://github.com/morluto/rea/pull/5629), [#5487](https://github.com/morluto/rea/issues/5487). Thanks @gensokyo-dev, @jayykernel, @vijaykiran06.
- **Interface Builder:** Keyed-archive graph identities use decimal table UIDs instead of authored `objectID`/`object-id`. Treat authored IDs as attributes and update persisted graph lookups; ibtool and native NIBArchive identities keep their source semantics. [6fbe67d](https://github.com/morluto/rea/commit/6fbe67d66e3d716db69929503a3a232a7ada0860).
- **Frida:**`instrument-with-frida` JSON returns the canonical Evidence directly. Read `normalized_result.cleanup_error` instead of the former top-level wrapper field. [48f700e](https://github.com/morluto/rea/commit/48f700ee35e0988ed973e5486dc9db33f2a5d944).
- **Reconstruction proof:** ID-array proof records no longer establish verified or ready ledgers. Supply implementation/revision bindings and individual claim outcomes; handle inapplicable, unavailable, and failed proof diagnostics. [f8b65e7](https://github.com/morluto/rea/commit/f8b65e78723b1e2169f94dd1226198ea3139707e).
- **Mach-O imports:** Handle a null import `name` when identity is ambiguous; use `raw_name` and `name_status` to distinguish observed expressions from exact symbols. [5d0d243](https://github.com/morluto/rea/commit/5d0d243a45c9132e1aff75d454ab53d86a74cc31), [169512a](https://github.com/morluto/rea/commit/169512a35bb015ff209168fb8bc1c9b31a2c678b).
- **Node.js:** Use a runtime satisfying `^22.23.4 || ^24.22.1 || >=26.12.1`; Node 23 and 25 are unsupported. Upgrade Node before installing or updating REA; the installer does not upgrade it. [#6315](https://github.com/morluto/rea/pull/6315), [#6283](https://github.com/morluto/rea/issues/6283). Thanks @GamerDubz, @gensokyo-dev, @ShikamaruNaaraa.
- **Browser storage:** Historical hashed storage remains readable, but comparisons stay unknown until both captures are recaptured in the new representation. [#6341](https://github.com/morluto/rea/pull/6341), [#6289](https://github.com/morluto/rea/issues/6289). Thanks @coygeek, @gongfpp.
- **Environment serving:**`environment-serve` serves derived state by default, including write tools whose effects are confirmed by recorded evidence. Use `--replay-only` for stateless replay of manifest tools. Read stop-summary `state` instead of `state_evidence_id`. [#5351](https://github.com/morluto/rea/pull/5351), [#6158](https://github.com/morluto/rea/pull/6158). Thanks @earakely-scale, @gensokyo-dev, @kaoru0822-kitauji.
- **MCP schemas:** Input advertisements default to compact. Set `REA_MCP_INPUT_SCHEMA_PROFILE=full` for complete advertised guidance and nested constraints. [#5975](https://github.com/morluto/rea/pull/5975). Thanks @gensokyo-dev.
- **Browser console arguments:** Accept null `argument_types` and `text_capture.status: "unavailable"` when the protocol argument inventory is omitted or malformed; preserve that uncertainty. [#6016](https://github.com/morluto/rea/pull/6016), [#5934](https://github.com/morluto/rea/issues/5934). Thanks @coygeek, @kaoru0822-kitauji.
- **Reconstruction summaries:**`summary.by_evidence_authority` is keyed by Evidence authority; read the former source-state counts from required `summary.by_source_state`. Regenerate ledgers whose closure digests depended on the old projection. [#5875](https://github.com/morluto/rea/pull/5875), [#5838](https://github.com/morluto/rea/issues/5838). Thanks @coygeek, @kaoru0822-kitauji.
- **Swift bindings:** Conformance `type_name` and `protocol_name` may be null; consume typed `type_binding`/`protocol_binding` evidence. Replace the former Objective-C-specific import-binding type/schema with `MachoImportBinding`. [d0067ba](https://github.com/morluto/rea/commit/d0067ba34a39ea47dd7e11bb2e6169bc0938cc3f).
- **WebSocket frames:** Accept null opcodes and preserve unknown frame facts; numeric opcodes are restricted to the four-bit protocol range. [0bc2b42](https://github.com/morluto/rea/commit/0bc2b42001f684beffcf47b70e507443b1a5d320).
- **Resource-only inspection:** Evidence subjects and executable digests may be null. Use `resource_root` and each resource's reported provenance instead of requiring an executable target. [6a6adf7](https://github.com/morluto/rea/commit/6a6adf703fa44100643947d20fbc670f86260b1b).
- **Browser timestamps:** Console and session-timeline timestamps may be null when the protocol supplies no finite/comparable time. Keep unknown time explicit in sorting and comparisons. [690fc51](https://github.com/morluto/rea/commit/690fc518540bae982038a74f47ef7781201785aa), [2a0410f](https://github.com/morluto/rea/commit/2a0410f1717b3126e8a95063d77dea66a597010a).
- **Reference sources:** Read failed root enumeration from `rootFailure` rather than a synthetic failed child at `"."`; preserve the top-level source-graph limitation. [6296fdf](https://github.com/morluto/rea/commit/6296fdf560f1ac4aa55985558edaa98300aacdbc).
- **Filesystem capture:** Accept `entry_unavailable` and `identity_changed` enumeration omission reasons in addition to the previous reason literals. [c85b44e](https://github.com/morluto/rea/commit/c85b44e1b14851c54e65327a4807f79ad5cd5a61). Thanks @rudrakshagarwal4200-ai.
- **APK locales:** Replace numeric legacy selectors such as `es-r419` with `es-419` or `b+es+419`. Two-letter regions remain supported. [29910d1](https://github.com/morluto/rea/commit/29910d16b98fe4b607a7aa15928789a73614b0b9). Thanks @mertkayacs.
- **XML plist scalars:** Recognize `$plist_type: "unknown"` and read `element`, `literal`, and `reason`; unresolved XML scalars use this wrapper instead of the former real/null unknown marker or malformed scalar prefixes. Supported extended UTC years preserve their timestamp. [3537e58](https://github.com/morluto/rea/commit/3537e58c1ab29309f06eabf50bc52adba9817a0c).
### Changes
- **Binary layouts:** Inspect ELF32 i386, little-endian ARM, and little- or big-endian MIPS `EXEC`/`DYN`/`REL` layouts on Linux x64 using a caller-supplied pwntools environment. [#7052](https://github.com/morluto/rea/pull/7052), [#6934](https://github.com/morluto/rea/issues/6934). Thanks @roblennon, @salmanalmutairi1.
- **Agent environments:** Project network captures and Evidence files into manifests, preserve template path pairs and positions, export AgentEnv bundles, and serve manifests as replay environments. [#4954](https://github.com/morluto/rea/pull/4954), [#5293](https://github.com/morluto/rea/pull/5293), [#5664](https://github.com/morluto/rea/pull/5664), [#4999](https://github.com/morluto/rea/pull/4999), [#4975](https://github.com/morluto/rea/pull/4975). Thanks @earakely-scale, @gensokyo-dev, @kaoru0822-kitauji.
- **Capture to environment:** Build environments from browser scenarios or a prompt, extract the initial observed state, and compare the environment with application probe captures. [#5182](https://github.com/morluto/rea/pull/5182), [#6946](https://github.com/morluto/rea/pull/6946), [#5319](https://github.com/morluto/rea/pull/5319), [#5699](https://github.com/morluto/rea/pull/5699). Thanks @earakely-scale, @gensokyo-dev, @kaoru0822-kitauji.
- **State-backed tools:** Render read filters, search, sorting and paging from state; apply confirmed write effects with broader state targets and recording-based write admission. [#5352](https://github.com/morluto/rea/pull/5352), [293832b](https://github.com/morluto/rea/commit/293832bafc9a054914ec514fe906dd4936c7a372), [#5800](https://github.com/morluto/rea/pull/5800), [#5787](https://github.com/morluto/rea/pull/5787), [#5600](https://github.com/morluto/rea/pull/5600), [5375ff0](https://github.com/morluto/rea/commit/5375ff0f9aadb4fd0d46333815aa0431e3df1010), [#6169](https://github.com/morluto/rea/pull/6169), [#6385](https://github.com/morluto/rea/pull/6385), [#6897](https://github.com/morluto/rea/pull/6897). Thanks @earakely-scale, @gensokyo-dev, @kaoru0822-kitauji.
- **State learning:** Model application-assigned values, preserve view-specific clock spellings, learn write clocks from scenario timing, derive fields, and learn creates across sorted arrays and referenced entities. [7ec9b6e](https://github.com/morluto/rea/commit/7ec9b6e56e1b3762285dca881db6d900a389ab9c), [#6136](https://github.com/morluto/rea/pull/6136), [957bcfa](https://github.com/morluto/rea/commit/957bcfa149c1a0593bb0aa2466cc56d1d4f62e7d), [#6138](https://github.com/morluto/rea/pull/6138), [#6967](https://github.com/morluto/rea/pull/6967), [#7025](https://github.com/morluto/rea/pull/7025), [#7034](https://github.com/morluto/rea/pull/7034). Thanks @earakely-scale.
- **Recorded writes:** Serve creates whose compatible read-backs add omitted fields or express timestamps at a different precision, and whole-object updates that regenerate observed slugs or modification times. Conflicting observations still withhold writes. State views and bundles carrying the new read-back data need REA 6.8.0 or later to serve. [#7080](https://github.com/morluto/rea/pull/7080). Thanks @earakely-scale.
- **Native investigations:** Page native inventory summaries, match functions across builds, export pseudocode to resumable directories, and select objdump disassembly by symbol. [#5378](https://github.com/morluto/rea/pull/5378), [#5390](https://github.com/morluto/rea/pull/5390), [#6170](https://github.com/morluto/rea/pull/6170), [#5171](https://github.com/morluto/rea/pull/5171), [#4249](https://github.com/morluto/rea/issues/4249), [#5343](https://github.com/morluto/rea/issues/5343), [#5159](https://github.com/morluto/rea/issues/5159). Thanks @coygeek, @CryptoJones, @gensokyo-dev, @kaoru0822-kitauji.
- **Engine workflows:** Bound a single Ghidra decompilation without closing its session, analyze GameCube/Wii DOL executables, and inspect static CUDA/HIP code objects. [#5662](https://github.com/morluto/rea/pull/5662), [#6240](https://github.com/morluto/rea/pull/6240), [#5813](https://github.com/morluto/rea/pull/5813), [#5340](https://github.com/morluto/rea/issues/5340), [#5345](https://github.com/morluto/rea/issues/5345). Thanks @arielfikru, @coygeek, @gensokyo-dev, @RishTheFish21.
- **PE/ELF:** Inspect export tables without an engine, PE imports/delay-loads, Authenticode certificates, TLS callbacks/load configuration, ELF notes, Linux kernel module metadata, and Zstandard section-name tables. [#6278](https://github.com/morluto/rea/pull/6278), [#6509](https://github.com/morluto/rea/pull/6509), [#4830](https://github.com/morluto/rea/pull/4830), [#7008](https://github.com/morluto/rea/pull/7008), [#4837](https://github.com/morluto/rea/pull/4837), [#7010](https://github.com/morluto/rea/pull/7010), [#6987](https://github.com/morluto/rea/pull/6987), [#4057](https://github.com/morluto/rea/issues/4057). Thanks @gensokyo-dev, @kaoru0822-kitauji, @mvanhorn, @Rinkia, @ShuaixinHuang.
- **APK output:** Set `string_tables_only` or CLI `--string-tables-only` to omit duplicate primary strings and avoid their output-capacity charge. [7c458f0](https://github.com/morluto/rea/commit/7c458f04b9155707c8ba406f8b455b10016f1b99), [#4990](https://github.com/morluto/rea/pull/4990), [#4982](https://github.com/morluto/rea/issues/4982). Thanks @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0.
- **Retained analysis:** Reuse successful validation of authenticated immutable native/imported-JavaScript parents and captures. Query retained Go build identity, Rust dependencies, JavaScript source-to-bundle mappings, and native export facts without repeating full output. [8f7d12e](https://github.com/morluto/rea/commit/8f7d12e30d63f3d3b4cfe2a90336362841438f56), [#4962](https://github.com/morluto/rea/pull/4962), [#5349](https://github.com/morluto/rea/pull/5349), [#5524](https://github.com/morluto/rea/pull/5524), [#5815](https://github.com/morluto/rea/pull/5815), [#6128](https://github.com/morluto/rea/pull/6128), [#6907](https://github.com/morluto/rea/pull/6907), [#4810](https://github.com/morluto/rea/issues/4810), [#5499](https://github.com/morluto/rea/issues/5499), [#5802](https://github.com/morluto/rea/issues/5802), [#6112](https://github.com/morluto/rea/issues/6112), [#6900](https://github.com/morluto/rea/issues/6900). Thanks @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0.
- **Compact results:** Use retained Frida/GDB/objdump summaries, omit redundant Ghidra raw results, and defer the analysis runtime during MCP catalog discovery. [#5661](https://github.com/morluto/rea/pull/5661), [#5614](https://github.com/morluto/rea/pull/5614), [#5355](https://github.com/morluto/rea/pull/5355), [#4424](https://github.com/morluto/rea/pull/4424), [#5640](https://github.com/morluto/rea/issues/5640), [#5584](https://github.com/morluto/rea/issues/5584), [#5337](https://github.com/morluto/rea/issues/5337). Thanks @coygeek, @gensokyo-dev, @kaoru0822-kitauji, @leeguooooo.
- **Analysis work:** Reuse Hopper literal-string records, JavaScript workers and captured prefix selections, Interface Builder validation and plist scalar schemas; bound stable reads to one snapshot buffer and avoid decoding omitted GGUF strings. [#5017](https://github.com/morluto/rea/pull/5017), [#5819](https://github.com/morluto/rea/pull/5819), [31de177](https://github.com/morluto/rea/commit/31de177404461d122761d8243abe4fd24564f4da), [#5885](https://github.com/morluto/rea/pull/5885), [85e38ac](https://github.com/morluto/rea/commit/85e38ac199dff15e8c43343d9170db7e0e46d284), [#6966](https://github.com/morluto/rea/pull/6966), [#5781](https://github.com/morluto/rea/pull/5781), [#5010](https://github.com/morluto/rea/issues/5010), [#5855](https://github.com/morluto/rea/issues/5855), [#6954](https://github.com/morluto/rea/issues/6954), [#5774](https://github.com/morluto/rea/issues/5774). Thanks @gensokyo-dev, @kaoru0822-kitauji.
- **Native scheduling:** Batch modern IDA callee lookups, schedule decompilation reads on demand, index NativeAOT type closure, and retain call-path ancestry once per visited node. [0c6fed9](https://github.com/morluto/rea/commit/0c6fed90b74c75a9fd23d1ec1b510a7274f85887), [06fe7ee](https://github.com/morluto/rea/commit/06fe7eecbc8c387414b4c1aa0d88ad6314f54f0c), [ce61680](https://github.com/morluto/rea/commit/ce61680c04c95a9cdfad5335632a99542af26d37), [5e4ec0f](https://github.com/morluto/rea/commit/5e4ec0fa1b678aa3bf20704ec9110224ba8c1676).
- **Selected processing:** Prepare CLI positional schemas only for the selected command, separate entropy reads from report resolution, prune typed-array reference escapes, index state-write cutoffs, decode only selected replay query values, and skip payload text during WAT framing. [315b234](https://github.com/morluto/rea/commit/315b23403f0819967878cee418c3f9cb4b1a69d9), [#6950](https://github.com/morluto/rea/pull/6950), [#5139](https://github.com/morluto/rea/pull/5139), [#5350](https://github.com/morluto/rea/pull/5350), [96ea32b](https://github.com/morluto/rea/commit/96ea32b7e4e28d3c9cd79a1de4b8e1ba3b4244e9), [70835d3](https://github.com/morluto/rea/commit/70835d3d5f030deadf606e1c79c70a60883e50c9), [#5766](https://github.com/morluto/rea/pull/5766), [#6947](https://github.com/morluto/rea/issues/6947), [#5760](https://github.com/morluto/rea/issues/5760). Thanks @galleonli, @gensokyo-dev, @kaoru0822-kitauji.
### Fixes
- **IPS report fields:** Preserve the recorded terminating process and PID, termination flags, parent PID, and build/platform metadata. [#7079](https://github.com/morluto/rea/pull/7079), [#7078](https://github.com/morluto/rea/issues/7078). Thanks @rodricksz4h5.
- **Ghidra language selection:** Accept stock language IDs containing `LEBE` or slash-separated processor names, and report conflicting DOL language overrides as configuration errors naming `REA_GHIDRA_LANGUAGE_ID`. [#7070](https://github.com/morluto/rea/pull/7070), [#7072](https://github.com/morluto/rea/issues/7072), [#7081](https://github.com/morluto/rea/pull/7081), [#7082](https://github.com/morluto/rea/issues/7082). Thanks @Adolanium.
- **Setup and Hopper:** Preserve the Linux embedding runtime, supported x86_64 host restriction, readiness-based provider choices, orderly demo-helper shutdown, and installed apt dependency versions. Keep cleanup and skill publication ownership through retries. [#5041](https://github.com/morluto/rea/pull/5041), [#3422](https://github.com/morluto/rea/pull/3422), [f5d1012](https://github.com/morluto/rea/commit/f5d1012fabfe99af82523c3078ce9d93eb3780c1), [#4966](https://github.com/morluto/rea/pull/4966), [#6974](https://github.com/morluto/rea/pull/6974), [211e353](https://github.com/morluto/rea/commit/211e3533b34f81ae0910ad4f53f2a3d3c8587d3e), [#6920](https://github.com/morluto/rea/pull/6920), [#5015](https://github.com/morluto/rea/issues/5015), [#3419](https://github.com/morluto/rea/issues/3419), [#4921](https://github.com/morluto/rea/issues/4921), [#6975](https://github.com/morluto/rea/issues/6975). Thanks @Abel-I-Akhigbe, @Adolanium, @DavidFB-creator, @DeryFerd, @gensokyo-dev, @kaoru0822-kitauji, @sx4im.
- **Client setup:** Treat empty APPDATA as unset, distinguish MSIX Claude Desktop process views, preserve aliased Hermes server tables, reuse client dialect/alias bindings, and materialize the pinned package-runner cache before the first start. [#5126](https://github.com/morluto/rea/pull/5126), [#4903](https://github.com/morluto/rea/pull/4903), [#3785](https://github.com/morluto/rea/pull/3785), [b7eeded](https://github.com/morluto/rea/commit/b7eededf5af5061a834f93f57f2c888acf870344), [#7032](https://github.com/morluto/rea/pull/7032), [#5104](https://github.com/morluto/rea/issues/5104), [#4278](https://github.com/morluto/rea/issues/4278), [#3320](https://github.com/morluto/rea/issues/3320), [#7027](https://github.com/morluto/rea/issues/7027). Thanks @CmsChase, @coygeek, @myso-kr, @pablodiazjorge, @vaidehi-snk, @wxj006007.
- **Target admission:** Preserve thin arm64_32 Mach-O support, distinguish JVM identity before FAT admission, validate FAT format/CPU identity, preserve original DOS header padding, and reject NULs in local objdump/rizin paths. [#5788](https://github.com/morluto/rea/pull/5788), [6843dbe](https://github.com/morluto/rea/commit/6843dbea9bce4709ee2e8bf46c38f0bbf640716b), [497037e](https://github.com/morluto/rea/commit/497037e6e3975cbb37cfd89ea0ad300faa2a4769), [#7013](https://github.com/morluto/rea/pull/7013), [#3600](https://github.com/morluto/rea/pull/3600), [#5713](https://github.com/morluto/rea/issues/5713), [#6510](https://github.com/morluto/rea/issues/6510). Thanks @coygeek, @dhruvac29, @kaoru0822-kitauji, @Lew1sWong, @qorvr.
- **Mach-O and dylibs:** Preserve container framing, facts when dyld rejects a missing UUID, verified-signature snapshot identity, unsigned diagnostics, absolute paths inside the selected root, legacy platform rules, lazy-load payloads, and reasons for rejecting present dylibs. [cf3288d](https://github.com/morluto/rea/commit/cf3288d8cf7a09685acf89103b36c32359be56fa), [2b263a7](https://github.com/morluto/rea/commit/2b263a7fe9e9d4c185c7d6af6b4e67b21a73af30), [eb7acc7](https://github.com/morluto/rea/commit/eb7acc70bf06f119e8639e30a78081d5ba82b0bf), [1502cfb](https://github.com/morluto/rea/commit/1502cfbc8ca0d1d1d86eebf334018dfb418bbac0), [90062c0](https://github.com/morluto/rea/commit/90062c092228dea9d33fadc0d7e1b968105b8b2c), [#5650](https://github.com/morluto/rea/pull/5650), [3b67b36](https://github.com/morluto/rea/commit/3b67b362aa2cd79eac5af57d0965e81a7d0b8c2b), [#5551](https://github.com/morluto/rea/issues/5551). Thanks @coygeek.
- **Native facts:** Admit direct dispatch-metadata providers; preserve record-local Objective-C omissions, snapshot profiles, class-import binding identity, stripped class labels, version-like names, and qualified Swift protocol identities. [50b6975](https://github.com/morluto/rea/commit/50b6975d861069e2c6cfa202780621449f863802), [9eec57b](https://github.com/morluto/rea/commit/9eec57b469087fe9bff3d29dcc02f47074159e91), [0ba4d6f](https://github.com/morluto/rea/commit/0ba4d6f43ad610dacc861a1548be1a5c4712cd61), [4d7de00](https://github.com/morluto/rea/commit/4d7de00d4cc9a95ec2b7a3123b04e01db2daafa0), [22c84ce](https://github.com/morluto/rea/commit/22c84ce64aa6c7601aa81af86d4117b5a4c2feac), [4852c83](https://github.com/morluto/rea/commit/4852c830af1e3b9df437235b7a8fa8970a6d1c59), [aa9043f](https://github.com/morluto/rea/commit/aa9043f6b61c453e4735795cfe6a559bf6b6cc16).
- **Archives and files:** Complete positional ZIP reads, preserve bounded ASAR signatures, report stable symlink loops as resolution failures, retain target applicability, and report unknown traversal coverage. Retain reported integrity contradictions for members present in only one compared artifact. [#4414](https://github.com/morluto/rea/pull/4414), [e8da7e1](https://github.com/morluto/rea/commit/e8da7e1edd075feec0e6beedec93d5645fed8125), [#5571](https://github.com/morluto/rea/pull/5571), [#5763](https://github.com/morluto/rea/pull/5763), [#5867](https://github.com/morluto/rea/pull/5867), [#5521](https://github.com/morluto/rea/issues/5521), [#5731](https://github.com/morluto/rea/issues/5731), [#5837](https://github.com/morluto/rea/issues/5837), [#7076](https://github.com/morluto/rea/pull/7076). Thanks @Afloat16, @coygeek, @jdotc1, @kaoru0822-kitauji, @WenyuChiou.
- **Extraction and file ownership:** Restrict destination-case recovery to owned collisions, retain physical-read failures through late cancellation/cleanup, preserve cleanup capabilities for retry, and retain independent firmware extraction failures. [932fbb7](https://github.com/morluto/rea/commit/932fbb7d84a9a995b9aee3c1be7da05a8c4d7bbb), [9499667](https://github.com/morluto/rea/commit/949966712e68d8857a50a165ecddf04c2d7e6d3b), [56679dd](https://github.com/morluto/rea/commit/56679dd2ae31dda58e181f8bf99775014f7bb82b), [b3c912a](https://github.com/morluto/rea/commit/b3c912ab79ad147a37b6cfbe1552a1a5eecec629), [7298bd0](https://github.com/morluto/rea/commit/7298bd08d134860b84b4073f6703021916a5a4c5).
- **Processes and cancellation:** Retain partial samples and shutdown gaps, unresolved descendants and caller abort reasons; bind cleanup errors to their owners, propagate comparison contradictions, and report no normal exit code for signalled targets. [ba866a2](https://github.com/morluto/rea/commit/ba866a26ecbe179a717e746cbc0d1605ae616ad5), [#5270](https://github.com/morluto/rea/pull/5270), [#6144](https://github.com/morluto/rea/pull/6144), [a721c42](https://github.com/morluto/rea/commit/a721c42c07dc674c32ab939627355b8a1bee97a5), [#6298](https://github.com/morluto/rea/pull/6298), [#6486](https://github.com/morluto/rea/pull/6486), [#5935](https://github.com/morluto/rea/issues/5935), [#5944](https://github.com/morluto/rea/issues/5944), [#6100](https://github.com/morluto/rea/issues/6100), [#6101](https://github.com/morluto/rea/issues/6101), [#6110](https://github.com/morluto/rea/issues/6110), [#6113](https://github.com/morluto/rea/issues/6113), [#6121](https://github.com/morluto/rea/issues/6121), [#6131](https://github.com/morluto/rea/issues/6131), [#6132](https://github.com/morluto/rea/issues/6132), [#6218](https://github.com/morluto/rea/issues/6218), [#6445](https://github.com/morluto/rea/issues/6445). Thanks @agorevski, @coygeek, @gongfpp, @kwakhyun, @mhamzasakhi.
- **Provider cleanup:** Preserve Windows native handles until close is confirmed, native/Inspector/Electron cleanup ownership, Kuna probe identity and cleanup, IDA registration configuration, and Hopper preparation ownership. [#5772](https://github.com/morluto/rea/pull/5772), [87ab054](https://github.com/morluto/rea/commit/87ab054ea384b3fcbae2998b1f94685edcd5f624), [ff78165](https://github.com/morluto/rea/commit/ff78165978de9dbcf9fa16c2acf74bf85a69dc44), [04ae07a](https://github.com/morluto/rea/commit/04ae07ab8c9ae1a6697b6ad15489bbde515da8b5), [dd0015a](https://github.com/morluto/rea/commit/dd0015aaf156c5701ef9598bcc6c885e6b8da803), [b231c28](https://github.com/morluto/rea/commit/b231c28cf64c2f799ada99343f8f21e0e6acb15e), [#4505](https://github.com/morluto/rea/issues/4505), [#5616](https://github.com/morluto/rea/issues/5616).
- **Provider diagnostics:** Give Ghidra restart recovery, stable Homebrew aliases, unsupported-host results for Linux-only integrations, correct IDA regex/readiness handling, and clear diagnostics when Windows refuses a verified native addon. [#5356](https://github.com/morluto/rea/pull/5356), [7c6142e](https://github.com/morluto/rea/commit/7c6142efdc19a42c148d0e8c96b69d18e63c7f87), [#5793](https://github.com/morluto/rea/pull/5793), [#4862](https://github.com/morluto/rea/pull/4862), [99ccfb6](https://github.com/morluto/rea/commit/99ccfb6e709ac4bc0afe176315280abde9ebcb9f), [#6832](https://github.com/morluto/rea/pull/6832), [#5730](https://github.com/morluto/rea/issues/5730), [#4860](https://github.com/morluto/rea/issues/4860), [#5723](https://github.com/morluto/rea/issues/5723). Thanks @coygeek, @dark13b, @gensokyo-dev, @jgt87, @jpmorris41488, @Lew1sWong.
- **MCP results:** Preserve protocol envelopes on oversized errors, enforce and advertise absolute Evidence paths, retain typed output-filter errors and selected JSON paths, and give client/provider timeout recovery. Compact advertisements now normalize reported enum and union shapes for strict gateway translation. [562dab0](https://github.com/morluto/rea/commit/562dab00c5cbf76a9b2695bad93792eccdc9a49d), [3cf6206](https://github.com/morluto/rea/commit/3cf6206614e65b5ebb6284a5709090dfb8f9aaee), [0e83d92](https://github.com/morluto/rea/commit/0e83d92c583ae36afe00b2c23dd2a0f1d025cbca), [4714c7e](https://github.com/morluto/rea/commit/4714c7eaa3d1d8b6a5df0aff586e844b9d9ab06d), [1bfa60a](https://github.com/morluto/rea/commit/1bfa60ac20e1a344bcf3132c08d15b6860b05c61), [#5608](https://github.com/morluto/rea/pull/5608), [#3806](https://github.com/morluto/rea/pull/3806), [#5598](https://github.com/morluto/rea/issues/5598), [#3800](https://github.com/morluto/rea/issues/3800). Thanks @gensokyo-dev, @Hahaa909, @wang1xiang.
- **Browser lifecycles:** Retain historical outcomes through cleanup, stop late navigation before session release, stop expired work, observe aborted fetch settlement, and mark later scenario actions skipped after failure or cancellation. [2e5171e](https://github.com/morluto/rea/commit/2e5171e391b83f8a467d0861fd452c1f78621fd8), [599fdac](https://github.com/morluto/rea/commit/599fdac029912f96c511f859660483405d2de26f), [b2fad36](https://github.com/morluto/rea/commit/b2fad362f2b37f81dc9a6f784f4521e4ca24d41e), [13a8640](https://github.com/morluto/rea/commit/13a864020414577006dce58d7fdaa877ddd0cb52), [#6023](https://github.com/morluto/rea/pull/6023), [#5943](https://github.com/morluto/rea/issues/5943). Thanks @coygeek, @kaoru0822-kitauji.
- **Electron observations:** Preserve listener callback ownership and count registrations by source callsite; block trash calls in the active hook, retain window handles through cancelled selection, and preserve cancellation during finalization. [dfe8378](https://github.com/morluto/rea/commit/dfe8378cf5a727e3d93d47a5986233175c6ef898), [5064007](https://github.com/morluto/rea/commit/506400748eb1d6c674f0cf51330073929c45f939), [816a9dc](https://github.com/morluto/rea/commit/816a9dc98886e6d824cd8ae88b68731b161d5dda), [1707d17](https://github.com/morluto/rea/commit/1707d171d750955bf6387e028e378183b360ffe3), [4e2a566](https://github.com/morluto/rea/commit/4e2a566d6c7e053ecbbee1b684311ed31ea163bf).
- **JavaScript source maps:** Keep unmapped and missing mappings unknown, preserve partial indexed-map facts and section paths, propagate preparation capability errors, and retain observed worker failures through cancellation. [a4dbb36](https://github.com/morluto/rea/commit/a4dbb36d8ea83e9f94dd289ac1a2d220d2c72c9b), [2b2e362](https://github.com/morluto/rea/commit/2b2e362a00fc5ba2f93fa6c62e2d8780072d1b4b), [498a667](https://github.com/morluto/rea/commit/498a667234b0140c1cfa43c819b35454ab11d14e), [#6149](https://github.com/morluto/rea/pull/6149), [#6214](https://github.com/morluto/rea/pull/6214), [f01dcca](https://github.com/morluto/rea/commit/f01dccab77f712fb4ac936d0a6e92a70d12f0789), [#6082](https://github.com/morluto/rea/issues/6082), [#6061](https://github.com/morluto/rea/issues/6061). Thanks @coygeek, @diyasharma05, @kaoru0822-kitauji.
- **JavaScript semantics:** Preserve overflowing timer uncertainty, unavailable-scope comparisons, directory-form URL/ESM path intent, and artifact confinement. Replaced globals lose built-in effects; erased imports no longer become runtime bindings. Treat a known truthy `fork()``silent` option as piped stdio unless a string/array `stdio` override takes precedence; unresolved options stay dynamic. [#3456](https://github.com/morluto/rea/pull/3456), [8d198d6](https://github.com/morluto/rea/commit/8d198d6ec707402d75276f516e0090d71767b7e4), [#4646](https://github.com/morluto/rea/pull/4646), [#2700](https://github.com/morluto/rea/pull/2700), [#4565](https://github.com/morluto/rea/pull/4565), [#6994](https://github.com/morluto/rea/pull/6994), [#3400](https://github.com/morluto/rea/issues/3400), [#3882](https://github.com/morluto/rea/issues/3882), [#3367](https://github.com/morluto/rea/issues/3367), [#7083](https://github.com/morluto/rea/pull/7083), [#7084](https://github.com/morluto/rea/issues/7084). Thanks @Adolanium, @amyanger, @coygeek, @gensokyo-dev, @HawkForge, @rohitg00, @viniciusdsandrade.
- **JavaScript routes and imports:** Treat path data as route candidates, preserve route flag and lexical RPC identities, package-main trailing-slash selection, catch aliases, and factory parameter ownership. [#6360](https://github.com/morluto/rea/pull/6360), [38ecc02](https://github.com/morluto/rea/commit/38ecc0248e1993d25a2f7dd350024484cce797bb), [#4219](https://github.com/morluto/rea/pull/4219), [21097b1](https://github.com/morluto/rea/commit/21097b192b1ab94647e3446b1a2a4dd00e924d5e), [f774dc4](https://github.com/morluto/rea/commit/f774dc420b3befe2b8e68971b4116da2d62fc800), [#6349](https://github.com/morluto/rea/issues/6349), [#4189](https://github.com/morluto/rea/issues/4189). Thanks @coygeek, @hemv-857, @kaoru0822-kitauji, @Nikhi00718.
- **Managed/IL2CPP:** Preserve extension-only scope uncertainty and type discovery after member limits, nested names, ReadyToRun/declaration facts, selected type scope, valid shared-suffix strings, and truncated IL2CPP metadata prefixes. [#5674](https://github.com/morluto/rea/pull/5674), [#5312](https://github.com/morluto/rea/pull/5312), [87fa1b6](https://github.com/morluto/rea/commit/87fa1b613d2b6bda1722855a00f1390f27731b64), [bc2d494](https://github.com/morluto/rea/commit/bc2d494030efd1ccabaeaa0bd49226aa87c378a2), [1f0eb22](https://github.com/morluto/rea/commit/1f0eb228e50908ee2a2aaa72d329178ae1a2cdbd), [4c5fae5](https://github.com/morluto/rea/commit/4c5fae5179dd54c4a0d427cd2a115b9c887e3841), [b3aadaf](https://github.com/morluto/rea/commit/b3aadaf474e59b33e5ff465236008a67eccefb87), [#5467](https://github.com/morluto/rea/issues/5467), [#5146](https://github.com/morluto/rea/issues/5146). Thanks @coygeek, @eco-null, @elricfd, @umi008.
- **Format boundaries:** Preserve independent facts across malformed inputs in model, GPU, WASM, Rust, Android, and SQLite inspection; report optional metadata warnings without discarding core results. [eb41f35](https://github.com/morluto/rea/commit/eb41f358cf83d5626f09de68332f9718f9aa6482), [#5789](https://github.com/morluto/rea/pull/5789), [00a9e95](https://github.com/morluto/rea/commit/00a9e95de91a4bd4d2a41f8aee704a53fa575750), [fc63fe8](https://github.com/morluto/rea/commit/fc63fe8682654086261540dcf3c59afc36c7d58f), [39a91d4](https://github.com/morluto/rea/commit/39a91d4db96ca13224c25936330ea84fe78970e5), [6c6e2ad](https://github.com/morluto/rea/commit/6c6e2ad62b20f810b54f5a567e66fc55241514b2), [929a0a7](https://github.com/morluto/rea/commit/929a0a76c870d9991e015dde58308fd9df0280b8), [d137a4c](https://github.com/morluto/rea/commit/d137a4cdde0049de967f1d5cd429ed9e1f7d0a10), [ef475fc](https://github.com/morluto/rea/commit/ef475fcf8b0601c61052b7091568ba574546417d), [#5749](https://github.com/morluto/rea/issues/5749). Thanks @coygeek, @gensokyo-dev.
- **Capture decoding:** Preserve malformed protobuf trailer facts and wire/schema identity, HTTP/2 URI and UTF-8 failure facts, WebMCP property coverage, and offline capture availability without a browser provider. Report invalid record ordinals as selection errors with actual bounds where available, and trim surrounding SP/HTAB from gRPC-web trailer values while preserving internal whitespace. [9cf7a7c](https://github.com/morluto/rea/commit/9cf7a7cb0d3c72a1121fde280cb11bf911af8055), [b3c3b21](https://github.com/morluto/rea/commit/b3c3b21e43cab4e71b460d08829d8c6e4e9b9c21), [4cb1f1d](https://github.com/morluto/rea/commit/4cb1f1d820df4498ee464bcf72a5f47d40cf8dab), [51e5938](https://github.com/morluto/rea/commit/51e593877642ab42fb5bf25812f4a4110ee7ac26), [d23be5c](https://github.com/morluto/rea/commit/d23be5c0898c39c9f0c54e2e0b43ee746d92d316), [#6725](https://github.com/morluto/rea/pull/6725), [#3947](https://github.com/morluto/rea/issues/3947), [#7067](https://github.com/morluto/rea/pull/7067), [#7068](https://github.com/morluto/rea/issues/7068), [#7069](https://github.com/morluto/rea/pull/7069), [#7071](https://github.com/morluto/rea/issues/7071). Thanks @Adolanium, @coygeek, @Sriram-PR.
- **Evidence and errors:** Authenticate ledger Evidence before committing; preserve parent-schema diagnostics, canonical correction details and owned cleanup outcomes; identify the rejected function-Evidence side/kind. Fixed allowances now name their actual limits. [0407bbf](https://github.com/morluto/rea/commit/0407bbfbe65e8fa475e8bb0c78c30170e2c2f55e), [#6361](https://github.com/morluto/rea/pull/6361), [1629266](https://github.com/morluto/rea/commit/1629266e3c08c90fed6bba5383528af7986a12b7), [#6969](https://github.com/morluto/rea/pull/6969), [#5860](https://github.com/morluto/rea/pull/5860), [#6034](https://github.com/morluto/rea/issues/6034), [#5932](https://github.com/morluto/rea/issues/5932), [#5847](https://github.com/morluto/rea/issues/5847). Thanks @AyoubElleuch, @coygeek, @dhruvac29, @kaoru0822-kitauji.
- **ADB and firmware recovery:** Report an unparsed push checksum as unverified, normalize absent installer-package sentinels, describe extraction-relative Unblob links, and avoid recommending a budget increase at its fixed ceiling. [#6010](https://github.com/morluto/rea/pull/6010), [#4073](https://github.com/morluto/rea/pull/4073), [#4573](https://github.com/morluto/rea/pull/4573), [#6971](https://github.com/morluto/rea/pull/6971), [#5924](https://github.com/morluto/rea/issues/5924), [#4175](https://github.com/morluto/rea/issues/4175). Thanks @coygeek, @Ganesh2325, @kaoru0822-kitauji, @Rinkia, @XiaoBinGan, @yankikucuk.
- **CLI formatting:** Share Markdown report formatting, exclude inherited row values, and preserve arrays of empty objects as JSON. Render Markdown table headers and cells literally while keeping intraword underscores readable. [b784912](https://github.com/morluto/rea/commit/b784912811fd10ac9dc425696dc0e9af632afa34), [#6984](https://github.com/morluto/rea/pull/6984), [#7058](https://github.com/morluto/rea/pull/7058), [#7057](https://github.com/morluto/rea/issues/7057), [#7073](https://github.com/morluto/rea/pull/7073). Thanks @Plastic-time, @ShuaixinHuang, @viniciusdsandrade.
- **WASM and APK metadata:** Preserve ambiguous WASM identity and partial facts, glue URL query/fragment text across line terminators, APK signer identity, and raw issuer identity when comparison rules are unknown. [af8f0ba](https://github.com/morluto/rea/commit/af8f0badf64fd3546392b6aa4625777904b2f051), [#6802](https://github.com/morluto/rea/pull/6802), [f783f0f](https://github.com/morluto/rea/commit/f783f0f1e82472cdddd4de2156d974f729e332a6), [115a78f](https://github.com/morluto/rea/commit/115a78fe241276c34917b496621954c5f29bfc15), [#6639](https://github.com/morluto/rea/issues/6639). Thanks @coygeek, @gensokyo-dev, @gongfpp.
- **Source identity:** Preserve Next route spelling, module grammar and loader request identity, directory package-main behavior, accessor effects, exact source targets, and resolver-discovery versus module-loading distinctions. [ddd7ac7](https://github.com/morluto/rea/commit/ddd7ac7b846b48191ead1f7dcd0e3bd735468fa8), [9974512](https://github.com/morluto/rea/commit/99745126009b15060d71c38809418858a07d5afa), [f2e5fd1](https://github.com/morluto/rea/commit/f2e5fd148a85fa737302dd194a33172c4561093e), [baf1678](https://github.com/morluto/rea/commit/baf16784af3592534687052431c145daef55cfd1), [6e324b2](https://github.com/morluto/rea/commit/6e324b224405f2befd953234f6dc210cf72ad50a), [75daf0c](https://github.com/morluto/rea/commit/75daf0c7df108f04ab41f34aec1a31d4a7ceb322), [f91b7f8](https://github.com/morluto/rea/commit/f91b7f8009c9af27a5e0148e3b07d20bfe3b7a2a).
- **Graphs and selected facts:** Preserve JavaScript graph omissions/version facts, Electron native-loader facts, web JSON identity/coverage, module-path selection, feature-trace qualifications, selected native paths, endpoint context, and mixed Swift demangler outcomes. [30db499](https://github.com/morluto/rea/commit/30db499db310f073d9bf3d3acfd77985e1cb7e2e), [5a7bddb](https://github.com/morluto/rea/commit/5a7bddb3aeb0345854e76b79d529aed1c40e2054), [79805f2](https://github.com/morluto/rea/commit/79805f244a1ec0850f3109929168a9c8ebb1b908), [f719dc4](https://github.com/morluto/rea/commit/f719dc46c51a55b3926eec9659d06e7dfb95132b), [7784fdb](https://github.com/morluto/rea/commit/7784fdbad78f8b384d513f0e7dae8e983cd48ef8), [76500f1](https://github.com/morluto/rea/commit/76500f12b54e07b1ea700a403b2593890d63b42a), [676d29f](https://github.com/morluto/rea/commit/676d29f9eef794cea668f5f09f0ff510c848a513), [edf7b3d](https://github.com/morluto/rea/commit/edf7b3d32c3248f221e27f7ce5a3d34e8590285b), [93de7bc](https://github.com/morluto/rea/commit/93de7bc7e324a761162a31625b838ba0d4e3824d).
- **Managed boundaries:** Honor native platform symbol lookup, consume resolved IL2CPP ELF declarations, and attribute malformed comparison inputs to the supplied argument. [64fa1db](https://github.com/morluto/rea/commit/64fa1db1309218283df19f3ee4dba61f5fbd2a67), [87745af](https://github.com/morluto/rea/commit/87745af52ab0ccd3a172c2534c516d41b0095035), [96ad94f](https://github.com/morluto/rea/commit/96ad94f69eda5d68c7335ff775b6927b1376fd2b).
- **Provider and worker recovery:** Preserve output loss after successful command exit, physical refusal diagnostics, bounded worker control framing, JavaScript worker-phase/budget attribution, admitted ambiguity choices, and selected EVM limiter availability. Scope objdump debug-link limitations to DWARF requests and keep process-capture preparation nonexecuting. [e639edf](https://github.com/morluto/rea/commit/e639edf6204c05285d96840478926592daf95876), [171b33b](https://github.com/morluto/rea/commit/171b33b2c4defc6cbea49545bedf4272b72d1ea2), [34563fc](https://github.com/morluto/rea/commit/34563fcc9bd92af30668820dcac5635e27181094), [3f9357b](https://github.com/morluto/rea/commit/3f9357b5990d166a38440f56f439452266650093), [cec8f1e](https://github.com/morluto/rea/commit/cec8f1e8f305d6332272457a6ee419d3c5fb6359), [a74d859](https://github.com/morluto/rea/commit/a74d859a19122fb8f1282b3632d320997b1caae0), [c284168](https://github.com/morluto/rea/commit/c284168c4b1cd214ce7f504d43da28528d82d603), [4fb936f](https://github.com/morluto/rea/commit/4fb936fe86bb7a07f94f819ae8510301f2ef69c9).
- **Setup and limits:** Retain unknown environment-budget facts, selected-skill/maintenance uncertainty, Hopper URL admission and bookmark verification, and listener failure recovery that matches the system cause. [a718037](https://github.com/morluto/rea/commit/a7180370332350bc0f998fbd643888221759570e), [d1fbebb](https://github.com/morluto/rea/commit/d1fbebb578538dc94fd5e376e6e1959218efa835), [60accad](https://github.com/morluto/rea/commit/60accad2de8f2862fe91901502fe26cc5390fb36), [fd7da77](https://github.com/morluto/rea/commit/fd7da77fc02dcfaf7d1bc9f2fb240d71cdd27c59), [0c440bf](https://github.com/morluto/rea/commit/0c440bfc07f3cb343e2e836d27cccaed539cfe9c).
- **State and Evidence:** Retain export tarball ownership and SQLite primary outcomes through cleanup retry, count emitted SQLite schema names across encodings, scope display refresh to matching snapshot bindings, and report retained-result validation failures consistently. Reconstruction uses one effective completion scope and kind-based labels when names are blank. [ab58edc](https://github.com/morluto/rea/commit/ab58edcdf50b4bd687900ee2dc362ecc5c3dce15), [1006714](https://github.com/morluto/rea/commit/100671427105791a5c5913a918b63a394f6bc83e), [a47788a](https://github.com/morluto/rea/commit/a47788a6a4abb067273f954275a9d0a269adf1e7), [8aa40e9](https://github.com/morluto/rea/commit/8aa40e9a61155cff8b6f3c138c601bf805b20653), [b8c1b45](https://github.com/morluto/rea/commit/b8c1b45316882beeab4a9de49381d94e131fab0f), [a35b933](https://github.com/morluto/rea/commit/a35b9335a0124674ee5e3abe8c075f1fff6b4fa6), [#6137](https://github.com/morluto/rea/pull/6137), [#6126](https://github.com/morluto/rea/issues/6126). Thanks @carlogiovannineri-rgb, @mohithhhh.
- **Capture coverage:** Name browser bundle budgets and deadline recovery, separate domain readiness from worker attribution, attribute rendered-frame omissions to their producing stage, share offline-capture host admission with discovery, preserve selected signal-file semantics, and retain short Flutter GNU build IDs. [#6036](https://github.com/morluto/rea/pull/6036), [ba78133](https://github.com/morluto/rea/commit/ba781330d6d28b22d15498e63c144fb40e086156), [5934387](https://github.com/morluto/rea/commit/5934387f4185129efe9dc8b7338d8bc3fa5b25bd), [ad629b2](https://github.com/morluto/rea/commit/ad629b2e31c3a9495b27179360b508fa021fe86a), [928ba76](https://github.com/morluto/rea/commit/928ba76882bfd216dde8ed79118789b2c3aad4f9), [678c4cf](https://github.com/morluto/rea/commit/678c4cfa68e2fee12f620075e12dc87d9b1195b0).
- **Additional boundary facts:** Retain parser format/query occurrence identity and independent malformed-input facts, unresolved nested Swift identifiers, and completed signing-capture coverage; describe crash documents without assuming a provider. [5f702dd](https://github.com/morluto/rea/commit/5f702dd6ccd9e8e505e224854b91678439c3b41f), [99d2d7a](https://github.com/morluto/rea/commit/99d2d7ad22bcbd780919760008aba6a88e7d1077), [90e433d](https://github.com/morluto/rea/commit/90e433df06b3f83417176b8db1b0a905f9df1d3a), [#5548](https://github.com/morluto/rea/pull/5548), [ee12d51](https://github.com/morluto/rea/commit/ee12d517a25d34853d40e31fc039c7d1576c60e1), [#5538](https://github.com/morluto/rea/issues/5538). Thanks @coygeek, @ShuaixinHuang.
- **Documentation:** Add the Romanian README. [#7026](https://github.com/morluto/rea/pull/7026). Thanks @An3treiu.
Full contribution record: [1740 commits and 497 merged PRs](https://github.com/morluto/rea/blob/rea-agents-6.8.0/docs/releases/6.8.0.contributions.json).
@@ -78,7 +180,7 @@ Full contribution record: [4 commits and 1 merged PR](https://github.com/morluto
### ⚠ BREAKING CHANGES
- **Saved Evidence:** Evidence and analysis snapshots now require explicit nullable `relationship_coverage` in their identity commitments. Artifact inventories require scoped coverage and explicit `integrity_contradictions`; obsolete `coverage_status` and prose-based coverage inference are removed. Regenerate old Evidence/snapshots and inventory observations with this release: saved files are not migrated and omitted facts are not filled with guessed defaults. ([db8128d](https://github.com/morluto/rea/commit/db8128dc0d1c4f731f3d19877fa00d25cf17f657), [0405c9d](https://github.com/morluto/rea/commit/0405c9d361de6d904dfa16cc0b6d8350407f2c9b), [a9588be](https://github.com/morluto/rea/commit/a9588be4ebdd20eeda975cb63ffcdb1cd868b340)).
- **Saved Evidence:** Evidence and analysis snapshots now require explicit nullable `relationship_coverage` in their identity commitments. Earlier records whose digest omitted null coverage are incompatible even when the serialized field is explicitly null. Artifact inventories require scoped coverage and explicit `integrity_contradictions`; obsolete `coverage_status` and prose-based coverage inference are removed. Regenerate old Evidence/snapshots and inventory observations with this release: saved files are not migrated and omitted facts are not filled with guessed defaults. ([db8128d](https://github.com/morluto/rea/commit/db8128dc0d1c4f731f3d19877fa00d25cf17f657), [0405c9d](https://github.com/morluto/rea/commit/0405c9d361de6d904dfa16cc0b6d8350407f2c9b), [a9588be](https://github.com/morluto/rea/commit/a9588be4ebdd20eeda975cb63ffcdb1cd868b340)).
- **Browser capture results:** Consumers must retain resolved capture limits, event families and omission facts. Set desired budgets through the supported capture options; omitted caller inputs still select defaults, but stored observations must report the resolved values. Admitted content reads queue behind bounded concurrency. ([57a23f0](https://github.com/morluto/rea/commit/57a23f0a2da0fdc3835e203718ded8acea5a34e6)).
- **Android version codes:**`inspect_adb_package.version_code` is a decimal string rather than a JSON number. Read it as a string, or use `BigInt` for numeric comparison. ([#3485](https://github.com/morluto/rea/pull/3485), [morluto/rea#3368](https://github.com/morluto/rea/issues/3368), [7666a48](https://github.com/morluto/rea/commit/7666a482b69dc7cabdbac3bbaf89a43e32300192)). Thanks @coygeek, @kaoru0822-kitauji.
- **Native UI failures:** Failed or cancelled native UI scenarios now return typed errors with the collected report in `partial_observation`. Handle the error path and read that report instead of expecting a successful result containing failed steps. ([d909919](https://github.com/morluto/rea/commit/d9099190ab3a819bd3a9fd62a8714cef398016d5)).
@@ -2581,7 +2683,7 @@ Read the [6.1 to 6.2 migration guide](docs/migration-6.2.md) before upgrading. R
[Quick start](#quick-start) · [How REA works](#how-rea-works) · [What you can analyze](#what-you-can-analyze) · [Showcases](#showcases) · [FAQ](#faq) · [Documentation](#documentation)
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Join the REA community on Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
[Quick start](#quick-start) · [How REA works](#how-rea-works) · [What you can analyze](#what-you-can-analyze) · [Showcases](#showcases) · [Build RL environments](#build-rl-environments) · [FAQ](#faq) · [Documentation](#documentation)
<code>npx rea-agents setup</code>
@@ -62,7 +64,7 @@ See a feature in an app that you want in your own product? Ask your agent to inv
REA connects your agent to tools for inspecting native binaries, JavaScript and Electron apps, .NET assemblies, and websites. You can also use the same tools from your terminal. Analysis runs locally, and results include the evidence and limitations behind each conclusion.
Setup registers REA with your agent and installs matching workflow instructions. Native analysis can use an existing Hopper, Ghidra, or IDA installation; setup can optionally install Hopper with approval. Static JavaScript analysis needs no native analysis engine.
Setup registers REA with your agent and installs matching workflow instructions. Native analysis can use an existing Hopper, Ghidra, IDA or Kuna installation; setup can optionally install Hopper with approval. Static JavaScript analysis needs no native analysis engine.
> **[Visit the REA website](https://rea.tools/)** for setup instructions, illustrated guides, and real case studies.
@@ -83,12 +85,15 @@ npx rea-agents setup
Choose your agents, review the proposed changes, and approve them. Setup adds
REA's MCP server and matching workflow instructions, with backups of existing
configuration. Restart your agent afterward.
configuration. Restart your agent afterward. Setup records the selected runtime and REA
entry script directly. If acquired through a package runner, keep its cache entry
available. Run package acquisition from a trusted directory outside the target
project. Installation and recovery details are in the setup guide below.
Setup supports Claude Code, Codex, Cursor, Gemini CLI, Grok Build and
[other agents](docs/installation.md#supported-agents). See
[installation and setup](docs/installation.md) for provider configuration and
manual MCP registration.
manual MCP registration and Claude Code/Codex plugin installation.
MCP uses compact input schemas by default to reduce context overhead. Set
`REA_MCP_INPUT_SCHEMA_PROFILE=full` in your MCP registration when you need the
@@ -96,6 +101,12 @@ complete advertised field guidance and nested validation details. Both profiles
keep the full tool set and canonical server validation. See
"Compact input schema profile" in the MCP contracts guide.
Complete output schemas are advertised by default. If your client caps the
complete `tools/list` frame, set `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` to
bound each advertised output schema, or `off` to omit output schemas entirely.
Both settings keep canonical server-side validation and complete structured
results. See "Output schemas" in the MCP contracts guide.
### Ask your agent
```text
@@ -177,13 +188,13 @@ CLI commands use the same workflows.
## What you can analyze
REA requires Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+, plus npm.
REA requires Node.js 22.x (>=22.23.4), 24.x (>=24.22.1), or 26.12.1+, plus npm.
Additional tools and host support depend on the target:
| Target | What REA returns | Requirements and guide |
@@ -191,6 +202,13 @@ Additional tools and host support depend on the target:
| IL2CPP metadata | Unity global-metadata.dat assembly images, type and method names and index ranges | Built-in static decoding of versions 24, 27, 29, 31 and 39; no Unity required |
| Godot PCK | Archive inventory and extraction, original header/directory facts and selected content identities | Built-in static formats 2, 3 and 4; no Godot required |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](docs/pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](docs/pe-imports.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](docs/elf-notes.md) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](docs/binary-hardening.md) |
| WASM artifact | Validation, sections, exact import/export WAT forms and decoded WAT | Caller-supplied WABT 1.0.42; `inspect_wasm_artifact` |
| EVM bytecode | Dispatch selectors, byte offsets, inferred arguments and mutability | Local raw/hex carrier on Linux x64; [offline EVM guide](docs/evm-bytecode.md) |
| SQLite databases | Tables, columns, indexes, views, triggers and selected ordinary-table rows | Local database/WAL snapshot; Node.js 24.x >=24.15 or 26+ with native SQLite limits |
@@ -200,6 +218,7 @@ Additional tools and host support depend on the target:
| Saved network captures | Requests, responses, exposed payloads and source locations | HAR or Fiddler Everywhere SAZ; mitmdump on Linux for native mitmproxy; [capture guide](docs/web-network-captures.md) |
| Android APKs | Manifest declarations, classes, decompiled methods and references | Headless JADX and a full JDK on Linux/macOS/Windows x64; [Android guide](docs/android-analysis.md) |
| JEB projects | Engine-backed project units, artifact digests and type/method pseudo-code | A caller-started JEB client serving MCP, selected with REA_JEB_MCP_URL; [JEB guide](docs/jeb-analysis.md) |
| Android resources | Decoded manifest, string tables, locales and version facts | Caller-supplied Apktool launcher; [resource guide](docs/apktool-resource-analysis.md) |
@@ -237,7 +256,12 @@ for features added since the latest npm release.
## Showcases
[](https://rea.tools/showcase/)
Record a web app session, turn its API into tools, and export an environment an AI agent can use. Run it with AgentEnv, then add tasks and reward checks for RL training or evaluation.
[Read the guide](https://rea.tools/guides/rl-environments/) ·
[Watch the video](https://x.com/reatools/status/2112574822584918075?s=20)
If you've used REA on something interesting, we'd love to see it. Share your
case in an [issue](https://github.com/morluto/rea/issues) or a
[pull request](https://github.com/morluto/rea/pulls), including the target,
your question, how REA helped, and what you found.
## Build RL environments
Give an AI agent a local environment to practice tasks such as finding an issue or creating a folder, without running the original app. Use it for reinforcement learning (RL) or to test how well an agent completes tasks.
REA turns recorded API requests and responses into tools the agent can call. The environment uses the recorded data and supports changes only when the recording confirms their effects.
With REA set up in your agent, replace the example URL and folder tasks below with your own:
```text
Use REA to build an RL environment from my test web app at
http://127.0.0.1:3000.
Cover listing, creating, renaming, and deleting folders. You may create and
remove test folders. Leave existing data unchanged.
Save it in a folder named my-env. Check which operations work and tell me
how to run it.
```
<details>
<summary>Use the command line instead</summary>
Use these commands if you already have a file containing the app’s recorded browser requests and responses. Running the result also requires AgentEnv and Docker.
```sh
rea environment-build ./session.har ./my-env --name my-env --json
agent-env run ./my-env
```
</details>
REA exports a runnable environment with a basic test to check that it starts and responds. Use it with your RL framework to define task goals, add reward checks, and run training.
[Build an environment](https://rea.tools/guides/rl-environments/) ·
@@ -10,7 +10,7 @@ Do not disclose vulnerabilities, proof-of-concept exploits, credentials,
private binaries, Hopper documents, or private Ghidra projects in a public
issue.
Use **Report a vulnerability** in the repository's **Security** tab to submit the report privately. Do not open a public issue to request a private contact channel.
Use **Report a vulnerability** in the repository's **Security** tab as the preferred way to submit the report privately. If GitHub private reporting is unavailable to you, email [[email protected]](mailto:[email protected]). Do not open a public issue to request a private contact channel.
Include the affected REA version, operating system/distribution, selected
provider and version, impact, reproduction conditions, and any suggested
| npm dependencies | [Complete inventory](licenses/npm-dependencies.txt) of every entry in `package-lock.json`, including transitive, optional platform, development, and website dependencies. [Runtime notices](licenses/npm-runtime-notices.txt) preserve the copyright, license, and NOTICE documents from integrity-verified release archives, plus the MIT notice for Rolldown's generated runtime helpers. Development dependencies are inventoried but are not all distributed in REA's npm package. |
| pefile ordinal tables | Import-hash ordinal names are derived from [pefile 2024.8.26](https://github.com/erocarrera/pefile/tree/4b3b1e2e568a88d4f1897d694d684f23d9e270c4), copyright 2004–2024 Ero Carrera. Preserve the [MIT license](licenses/pefile-2024.8.26-LICENSE.txt). The Python package is not a runtime dependency. |
| UF2 family names | UF2 family IDs and short names are copied from [microsoft/uf2 `utils/uf2families.json`](https://github.com/microsoft/uf2/blob/cf3bd7f9c274b949528f8cd6a19eef2502a687d2/utils/uf2families.json), copyright Microsoft Corporation. Preserve the [MIT license](licenses/microsoft-uf2-MIT.txt). |
| NativeAOT parser | REA's derived parser uses the MIT-licensed [ghidra-nativeaot](https://github.com/Washi1337/ghidra-nativeaot/tree/effeb734fc570c32650f88b159608979dc7b423e) work of Washi and the NativeAOT layout definitions in [dotnet/runtime](https://github.com/dotnet/runtime/tree/a3b2d40328be0be3f8dd43f0e5cc925b8827b044). Preserve [Washi's MIT notice](licenses/nativeaot-parser-MIT.txt) and the [.NET Foundation MIT notice](licenses/dotnet-runtime-MIT.txt). |
| EVM analysis | `[email protected]`, MIT, copyright 2023 Maxim Andreev. The published npm archive omits its license document; REA retains the [original upstream license](third_party/evmole/LICENSE) and [provenance](third_party/evmole/README.md). It is also included in the runtime notices. |
| Windows native addon | The release addon is compiled with MinGW-w64 and statically links GCC runtime libraries. Preserve the [MinGW-w64 runtime notices](licenses/MinGW-w64-runtime.txt), [GPLv3 text](licenses/GPL-3.0.txt), and [GCC Runtime Library Exception 3.1](licenses/GCC-Runtime-Library-Exception-3.1.txt). The exception governs eligible compiled combinations; it does not relicense the runtime itself under MIT. The Node-API build headers are from Node 22.19.0; its [original license and component notices](licenses/Node-22.19.0-LICENSE.txt) are retained. Node itself is supplied by the user. Snapshot provenance and release-toolchain review requirements are in [licenses/README.md](licenses/README.md). |
@@ -52,27 +54,27 @@ REA's adapters do not grant, replace, or waive these licenses. The upstream
executables and their dependency graphs are supplied separately. Git submodules
pin inspection sources; they are not included in the npm package.
| Project | License and source | REA relationship |
| Ghidra | [Apache-2.0](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/LICENSE), with the government-work and component qualifications in its [NOTICE](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/NOTICE) | Bring your own Ghidra 12.1.4. Copies of its [LICENSE](licenses/ghidra-12.1.4-LICENSE.txt), [NOTICE](licenses/ghidra-12.1.4-NOTICE.txt), and [DISCLAIMER](licenses/ghidra-12.1.4-DISCLAIMER.txt) are retained for reference. |
| JADX | [Apache-2.0](https://github.com/skylot/jadx/blob/v1.5.6/LICENSE) | The audited jadx-headless-mcp source uses JADX 1.5.6 for Android decompilation; the engine JAR is separately supplied. |
| Kuna | [Apache-2.0](https://github.com/Noelo-Lab/kuna/blob/v1.833/LICENSE), with its [NOTICE](https://github.com/Noelo-Lab/kuna/blob/v1.833/NOTICE) | Bring your own `kuna`; REA verified 1.833 and never downloads, installs or bundles it. REA invokes it as a separate process. |
| pyelftools | [Public-domain dedication and component notices](https://github.com/eliben/pyelftools/blob/v0.33/LICENSE) | 0.33; includes separately licensed bundled material, including Construct. |
| Unicorn | [GPL-2.0 and component notices](https://github.com/unicorn-engine/unicorn/blob/2.1.2/COPYING) | 2.1.2; used by the caller-supplied ELF analysis profile for instruction emulation. |
| GNU GDB | [GPL-3.0-or-later](https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/COPYING) | Caller-supplied debugger for the optional pwndbg core-file workflow. |
| LLDB / LLVM | [Apache-2.0 with LLVM exceptions](https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT) | Caller-supplied debugger; the installed distribution's component and historical notices also apply. |
| Ghidra | [Apache-2.0](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/LICENSE), with the government-work and component qualifications in its [NOTICE](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/NOTICE) | Bring your own Ghidra; see [supported versions](docs/installation.md#ghidra). Copies of the 12.1.4 [LICENSE](licenses/ghidra-12.1.4-LICENSE.txt), [NOTICE](licenses/ghidra-12.1.4-NOTICE.txt), and [DISCLAIMER](licenses/ghidra-12.1.4-DISCLAIMER.txt) are retained for reference. |
| JADX | [Apache-2.0](https://github.com/skylot/jadx/blob/v1.5.6/LICENSE) | The audited jadx-headless-mcp source uses JADX 1.5.6 for Android decompilation; the engine JAR is separately supplied. |
| Kuna | [Apache-2.0](https://github.com/Noelo-Lab/kuna/blob/v1.990/LICENSE), with its [NOTICE](https://github.com/Noelo-Lab/kuna/blob/v1.990/NOTICE) | Bring your own `kuna`; REA verified 1.990 and never downloads, installs or bundles it. REA invokes it as a separate process. |
| pyelftools | [Public-domain dedication and component notices](https://github.com/eliben/pyelftools/blob/v0.33/LICENSE) | 0.33; includes separately licensed bundled material, including Construct. |
| Unicorn | [GPL-2.0 and component notices](https://github.com/unicorn-engine/unicorn/blob/2.1.2/COPYING) | 2.1.2; used by the caller-supplied ELF analysis profile for instruction emulation. |
| GNU GDB | [GPL-3.0-or-later](https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/COPYING) | Caller-supplied debugger for the optional pwndbg core-file workflow. |
| LLDB / LLVM | [Apache-2.0 with LLVM exceptions](https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT) | Caller-supplied debugger; the installed distribution's component and historical notices also apply. |
case"list_classes"->"Pages of case-sensitive matching top-level class names; offset and next_offset are inventory positions, limit bounds matching rows. Omitted or empty query inventories all names.";
case"get_class_summary"->"Parsed member metadata for one exact class name; no source generation.";
case"get_class_summary"->"Parsed member metadata for one exact class name; no source generation. Set dex_static_values to include encoded static final field values.";
case"get_method_body"->"Source for an exact class and stable same-name overload index; static initializers use smali to preserve folded field initialization.";
default->"Complete static using-node inventory for an exact class or selected method overload; one node may contain multiple invoke instructions, and source lines are unresolved.";
tryfail("pid-not-application","LaunchServices does not expose an application for the selected PID; this does not establish that the process is absent")
}
guardletcanonicalPointer=realpath(url.path,nil)else{tryfail("target-mismatch","Running executable path cannot be resolved")}
guardleturl=app.executableURLelse{
tryfail("application-executable-unavailable","The selected application does not expose an executable URL; select a PID with an identifiable application executable")
}
guardletcanonicalPointer=realpath(url.path,nil)else{tryfail("executable-path-unavailable","Running executable path cannot be resolved")}
raiseLayoutFailure("format",f"ELF symbol table {index} links to invalid string table {h.sh_link}.")
strings=all_sections[h.sh_link]
@@ -294,6 +319,7 @@ def inspect_elf(path, cache):
"Relocatable relocation targets with SHN_UNDEF or inactive SHT_NULL headers retain their reported section index/offset as unknown; they do not establish an associated target section.",
"GOT/PLT are derived upstream convenience maps; PLT inference can emulate selected instructions in Unicorn. They do not establish target runtime behavior; aliases may collapse and warnings may indicate incomplete coverage. Their completeness is unknown.",
"Mitigations are upstream static heuristics, not runtime protection. ET_DYN does not prove an executable; absent canary symbols do not prove every function unprotected.",
"Kernel configuration/version discovery is not performed; these memory-content scans do not supply the reported structural, linkage or mitigation facts.",
"This profile inspects ELF EXEC/DYN/REL layout only. It does not analyze recorded cores, resolve loaded libraries, launch the target as a host process or start a debugger.",
"Symbol/relocation inventories reflect original section tables. A sectionless image can still report dynamic dependency names; missing tables do not prove absence of dynamic symbols or relocations.",
*(["Absent section-name table: section-dependent RELRO/canary heuristics may be incomplete; protection coverage is unknown."]ifall_sectionsandsection_name_index==0else[]),
| `pull_adb_package` | read | Complete split APK set from `pm path`, each file digested |
| `pull_adb_file` | read | One absolute device path pulled and digested |
@@ -103,6 +117,15 @@ existing analysis families directly, for example
| `start_adb_activity` | **mutates device** | Starts an intent (`am start -a` with optional URI, component, and up to 16 typed extras); preserves the activity manager's verdict |
| `stop_adb_app` | **mutates device** | Force-stops every process of a package, ending a dynamic-analysis run |
Screen capture validates the PNG signature, IHDR framing, dimensions, and IHDR
checksum before publishing. It does not validate encoding fields or decode and
validate image contents; that limitation is retained in Evidence.
When `push_adb_file` receives an existing directory, adb places the local file
inside it under the local basename; Evidence reports that effective file path
while retaining the caller's directory path in the input parameters. The
private read-only staging copy preserves the source file's executable bits.
For `inspect_adb_window`, `focus_contexts` retains each display header, its exact
display ID when recognized, and every focus declaration with its original line.
Declarations without a display header are explicitly `unscoped`. A missing
| `no-state-target` | The write could not be linked to a modelled target. Check the path template and recorded views; record missing reads. Some route forms may not be supported. |
| `no-call` | No recorded call of this tool was modelled, for example because every call failed. Record a successful call. |
| `body-unusable` | The retained request or response cannot be modelled. Record a successful call with a complete supported request and reproducible response. |
| `no-observation` | Nothing showed the entity or document this write changed. Read a created entity back by id right after the write; for an update or delete the state must already hold the entity, so read it before the write or record the create that made it. |
| `unexplained-change` | The write changed fields it did not send, in a way no rule explains. A second, different call of the same tool can show the pattern. |
| `contradicted` | A later read disagreed with the modelled effect. Check the read at contradicted_by. |
| `side-effect` | The write changed something besides its own entity, which the environment does not model. |
| `unconfirmed` | No later read showed the effect. Read the entity and its list after the write. |
| `incomplete-entities` | The write is confirmed, but the views that show its entity cannot render it in full. Read the written entity through every view that lists it. |
| `unknown-clock` | The learned rules generate timestamps, but the capture establishes no global served clock start. Record usable start times and completed durations for every request. |
The bundle ships `evidence.json`, the whole capture Evidence, response bodies
included. Inspection excludes the values of the standard authentication headers
(`Authorization`, `Cookie` and `Set-Cookie` among them) and URL userinfo; a
custom key header such as `X-Api-Key`, a token in the query string and tokens
an application returns in bodies remain. The environment views refuse captures
inspected with `--sensitive-value`, so scrub a capture before building it. The
report's `credential-named-members` warning counts recorded header names, query
parameter names and JSON or form body members whose names look like
credentials (whole name segments such as `token`, `secret`, `password`,
`api_key`, `authorization` or `session`) and hold a value, and lists the first
20 by record and pointer, never by value. A body is read as a form or as JSON by
its recorded media type, or by its shape when it has none or one naming
neither. A browser scenario is
scanned by event (`event 4 response /token` names
`normalized_result.events.items[4]`): every retained request and response
content event, including a response whose request began before the browser was
attached and a transaction no record represents, and each transaction URL's
query once. Bodies larger than 4 MiB of UTF-8 text are not
scanned; `credential-scan-incomplete` counts them and lists up to 20 record or
event pointers, including base64 bodies whose decoded scan was cut. An empty name
warning does not establish that these bodies were checked. A browser scenario's
saved values (`save` and `save_response`) named like credentials but not marked
`secret: true` are recorded unredacted; `credential-named-saved-values` lists up
to 20 of them by parameter pointer. Names do not establish sensitivity,
so the build never refuses on them. Capture
Evidence also records the capture file's absolute path (`subject.local_path`,
`parameters.capture_path`, `locations`, `artifact.path`), and the bundle
carries it: build from a neutral folder when that path should not ship.
Over MCP, `export_environment_bundle` builds the same bundle and returns the
same report. Its `source` is capture Evidence the session retained
(`{"kind": "retained-evidence", "evidence_id"}`), such as a
`capture_browser_scenario` or `inspect_web_network_capture` result, or a
capture Evidence file (`{"kind": "evidence-file", "path"}`); inspect a HAR with
`inspect_web_network_capture` first. Retained Evidence is written into the
bundle as `--format json` writes it, so the bundle is the one
`environment-build` writes from that file. `output_directory` must be an
absolute path that does not exist yet, whose parent does; the tool creates it
and refuses an existing directory, empty or not. `name`, `template_rules`,
`package_tarball` and `dry_run` work as `--name`, `--template-rule`,
`--package-tarball` and `--dry-run` do, and retained Evidence has no file to
name the environment after, so a loopback or IP origin needs `name`. Unlike
`--dry-run`, `dry_run` still checks `output_directory`. Destination refusals
name `export_environment_bundle`; the others name their stage as
`environment-build`'s do.
## From a browser scenario
@@ -124,6 +320,51 @@ the replayed body is the bytes the browser exposes for each response, with any
declared secret replaced by its redaction marker (a body that redaction had to
empty replays no body).
To record writes, sign in through the UI and then send the application's API
calls with [request steps](browser-scenario-contract.md#request-steps): each
sends one request to the start origin through the page's own `fetch`, with the
session's cookies, and a header can carry the CSRF cookie or stored token the
application's own requests send. A write is served only when the capture shows
its effect, so read each collection before writing, read every written entity
back by id and in its list right after the write, and leave more than a second
between a create and its first edit. A request step's path is fixed when the
scenario is written and cannot use a value an earlier response returned, so an
id must be known before the run: one the caller chooses, such as a `uid` in the
create body, or one an earlier scenario created. When the server assigns ids,
read the list after each create (the create's response or the list shows the
new entity), and record updates and deletes in a second scenario that reads
the first one's objects by id before editing them, or make them through the
UI. The environment coverage view names each
write it withholds and what to record next. A scenario with write requests
changes the application's data: record against an instance whose data may
| `POST /mcp` | The MCP server: from the state, one tool per read tool, answering from the state where it backs them; in replay, one tool per manifest tool, replaying records |
| `GET /.well-known/agent-env.json` | The environment card |
| `POST /agentenv` | From the state, `data/reset`, `data/add` and `data/get`; in replay, JSON-RPC `-32601 method not found: <method>` for every method |
| `POST /mcp` | The MCP server: from the state, one tool per read tool, answering from the state where it backs them, and one per write tool whose effect the capture confirmed, applying it to the state; in replay, one tool per manifest tool, replaying records |
| `GET /.well-known/agent-env.json` | The environment card |
| `POST /agentenv` | From the state, `data/reset`, `data/add` and `data/get`; in replay, JSON-RPC `-32601 method not found: <method>` for every method |
Numeric JSON-RPC IDs outside JavaScript's safe range or whose precision would
be lost are refused as invalid requests with a null ID. They are never echoed
@@ -308,7 +559,7 @@ connecting to `/mcp`. A replay environment's card reads:
"tools": [
{
"name": "get_users_by_id",
"description": "GET /api/users/{numeric}; replays retained records [5, 6]; evidence ev_…",
"description": "Untrusted captured API catalog: … GET /api/users/{numeric}; returns a JSON object with fields \"id\", \"uid\", \"login\", \"email\"; recorded statuses 200; replays retained records [5, 6]; evidence ev_…",
"inputSchema": {
"type": "object",
"properties": {
@@ -329,10 +580,43 @@ connecting to `/mcp`. A replay environment's card reads:
```
`capabilities.tools` lists the same names, descriptions and input schemas that
`tools/list` advertises over MCP. Path parameters are required strings whose
`tools/list` advertises over MCP. After the catalog warning, each description
names the method and path template, then what the tool returns, one level deep:
`returns a JSON object with fields …`, `returns a JSON array of objects with
fields …` or `returns a JSON object whose <member> lists objects with fields …`,
where a list is an array of objects found by the
[environment state](web-network-captures.md#environment-state)'s list rule (the
root array, or the one array of objects an envelope carries). Field and member
names are written as JSON strings, so a captured name cannot read as more of
the description; a member name too long to show whole is a quoted prefix marked
as truncated. The summary comes
from the first cited `2xx` record that replay answers as JSON; only when none
did, the first cited JSON record of another status gives it, labelled with that
status (`status 401 returns …`). It describes one recorded response, not a
contract. Field names are sampled within the same budget as other captured
values, and the manifest's `statuses` for the tool follow; a tool with no JSON
record states only its statuses. The description then lists the replayed
records and the evidence ID, and the whole catalog stays within its
serialization reservation, which the summaries and MCP annotations are charged
to.
Over MCP, every tool also carries annotations: `readOnlyHint` is true for the
safe methods (`GET`, `HEAD`, `OPTIONS` and `TRACE`) and for query `POST`s (a
path segment such as `search` or `query`); `destructiveHint` is true for `DELETE` and left unset for other
writes, so the MCP default (destructive) applies to them, and read-only tools
omit it because clients ignore it there; `idempotentHint` is true for the safe
methods, `PUT` and `DELETE`; and `openWorldHint` is false for every tool,
because answers come from the capture rather than the captured application. The
hints describe the captured method; replay itself changes nothing. The card
keeps the `name`, `description` and `inputSchema` of the echo example's tool
shape and carries no annotations.
Path parameters are required strings whose
description names the segment index, the templating rule and the observed
values (sampled within the list budget above; the manifest keeps them all);
query parameter names are optional strings; write methods accept an
query parameter names accept an optional string, number or boolean (a number or
boolean is sent as its JSON text) or array of strings (for repeated values in
captured order); write methods accept an
optional `body` object described by the recorded top-level key types. Each
input keeps its own argument: a query name equal to a path parameter's name is
advertised as `query_<name>`, a query name longer than 64 characters as
@@ -344,8 +628,11 @@ In replay, `extensions`, `operations` and `children_environments` are fixed to
`null`, `[]` and `null`: a replay environment declares no extensions, no
data-plane operations and no children. Served
[from the state](#serve-from-the-state), the card declares the three data-plane
operations and one intake extension instead, leaves write tools out and
describes each tool as answering from the state where the state backs it.
operations and one intake extension instead, and the catalog holds the read
tools and only the write tools whose effect the capture confirmed: a read tool
is described as answering from the state where the state backs it, and a
served write tool by the effect it applies to the state and the quoted body members it
accepts.
## Serve from the state
@@ -368,7 +655,8 @@ A state that is unavailable does not stop the server: it logs a warning naming
the error `code` and message before it binds, then serves replay. The state is
unavailable when it cannot be derived, such as from a
`capture_browser_scenario` capture that selected header values but no response
bodies or over the view's budget, and when its catalog under the served name
bodies or over the view's budget, when `data/get` would write it past the 32 MiB
`data/add` intake limit, and when its catalog under the served name
exceeds the [discovery reservation](#what-a-manifest-is). That check runs
first, so no state is derived for it, and replay is then admitted exactly as
with `--replay-only`. `--replay-only`
@@ -385,24 +673,124 @@ member reports which applied:
Each read tool answers according to its view in the state:
| `item` | The entity the path arguments name, rendered into the recorded envelope of `envelope_ordinal`. Arguments match as replay matches them: each collection segment equals the held collection's segment as written or after percent-decoding, and the identity is the last argument percent-decoded, then as written; values are never re-split on `/`. Any entity the state holds answers, including one `data/add` loaded; query arguments only decide `replay.exact_match`. An entity the state does not hold, or one lacking a field every recording carried, is a tool error; a body is never invented. |
| `list` | When the arguments name one of the lines the view checked exactly (its latest recordings before the first related write; every path and query value equal to the recording's, and every recorded query parameter supplied), that recorded envelope with its array rendered from the state: every entity of the collection in state order for `membership: "all"`, or the recorded members still held. A member lacking a field every recording carried makes the call a tool error rather than a partial list. |
| `document` | When the arguments name the document's checked line exactly, the state's value for it; a tool error when the state holds none. |
| `recorded` | The recording, as replay answers it. |
| `item` | The entity the path arguments name, rendered into the recorded envelope of `envelope_ordinal`, whose `per_response_members` (such as a request id), like a list's or a document's, keep their recorded values. Arguments match as replay matches them: each collection segment equals the held collection's segment as written or after percent-decoding, and the identity is the last argument percent-decoded, then as written; values are never re-split on `/`. Any entity the state holds answers, including one `data/add` loaded; query arguments only decide `replay.exact_match`, except that a call matching one of the view's `projected_query_keys`, or on a view with `projected_record_ordinals` asking for some fields only with a value other than `*` no checked line gave or one a replayed read gave, replays its recording. An entity the state does not hold answers the tool's latest recorded 400, 404 or 410 response with `state.view``missing`, or is a tool error when none was recorded; one lacking a field every recording carried is a tool error, unless a read showed the application leaves that field out; a body is never invented. |
| `list` | When the arguments name one of the lines the view checked exactly (its latest recordings before the first related write; every path and query value equal to the recording's, and every recorded query parameter supplied), that recorded envelope with its array rendered from the state: every entity of the collection in state order for `membership: "all"` or a line `all_record_ordinals` names, or the recorded members still held. A call with no query argument answers from such a line of the collection its path arguments name that only pages its list, with `replay.exact_match` false. On a line the view's `query` answers, and for a call whose every query argument has a role there (an argument-less call aside), the answer is the query evaluated over the state: filtered, searched, sorted and paged, with counters at the number listed and `query.totals` at the number matched, and `state.membership: "query"`; such a call answers on a query or whole line of its collection with `replay.exact_match` false. A value an `inert` parameter never recorded, a parameter without a role, a call matching one of the view's `projected_query_keys`, or a field selection the checked lines do not show (as for `item`), replays its recording. A member lacking a field every recording carried makes the call a tool error rather than a partial list. |
| `document` | When the arguments name the document's checked line exactly, the state's value for it; a tool error when the state holds none. |
| `recorded` | The recording, as replay answers it. |
A read with repeated query values uses replay, because the state query model
does not infer repeated-parameter semantics. A repeated-query write is refused
before it can change state for the same reason.
List counters retain their recorded number or digit-string representation.
Changing a digit-string counter preserves its observed zero-padding width;
query-total inference remains limited to canonical decimal counts. Path matching
uses the same segment parser as extraction, preserving empty internal segments.
An item or list view that always spells a clock field otherwise than the
entity's stored value names that spelling in `clock_formats`, and a value it
never recorded, such as a created entity's clock, is rendered in it: a list that
writes `+00:00` where item reads write `Z` lists a new entity the same way.
A view that names a field in `sorted_arrays` lists that string array sorted, as
a read-back showed the application does, while the state keeps it in the order
it was written, so a write's own answer echoes it. If a later counted read
contradicts that ordering, the view stays recorded and its earlier reads no
longer confirm served writes. A view that names a field in `nested_fields`
always showed that field's objects with the same keys, in any order, and fewer
than an entity stores, such
as a list showing a parent without the parent's own nested type; a stored
object it never recorded, such as a created entity's parent, is rendered with
those keys. Members a view names in
`per_response_members`, such as a request id each response mints, keep the
value its recording carried. A list's `counters` and `query.totals` are
top-level members or members one level down in a paging object
(`/pageInfo/totalRows`); either is set to the number listed or matched, and
members beside it such as `isLastPage` stay as recorded.
A list or document call that names no checked line, including a line recorded
after a write or with other query values, answers from the recording. Every
answer has the replay shape plus `state`: `{"source": "state", "view": …}`
naming the entity, collection or document path, or `{"source": "recording",
"reason": …}` with the view's reason, `no-view` or `unrecorded-request`. A
"reason": …}` with the view's reason, `no-view`, `unrecorded-request`, `page-exceeded` (a call naming a page of a whole list that the collection has outgrown), `projection` (a call to an item or list tool with replayed reads asking for a selection of fields the checked lines do not show, or leaving out a projection parameter every checked line names) or `variant` (a call matching another `projected_query_keys` key: a parameter, or a value of one, only lines showing the entities in another representation named, or leaving out a parameter every other line named). A `projection` or `variant` answer is a recording, so it does not show served writes. A
state answer carries the base record's status, content type and `replay`
provenance with `truncated: false`; one whose JSON would exceed 256 KiB is
refused as a `resource_constraint` tool error rather than cut. Serving is
read-only: `POST`, `PUT`, `PATCH` and `DELETE` tools, other than query `POST`s,
are not served, so nothing a call does changes the state. Query `POST`s stay
served and answer from their recordings with the reason `query-body`.
refused as a `resource_constraint` tool error rather than cut. Extraction does
not state-back a document whose rendered JSON exceeds 256 KiB; that read stays
recorded (`rendered-oversize`) and replays the captured body. Query `POST`s,
and `POST` tools the capture shows only reading (a lookup answering as a `GET`
did, an RPC tunnel naming read operations, a session renewal), stay served and
answer from their recordings with the reason `query-body`; they are not write
tools.
`HEAD`, `OPTIONS` and `TRACE` tools also replay their captured responses; their
state views report `method-replay` and coverage counts them as recorded reads.
A line establishes whole-collection membership only from the beginning:
`offset`, `start`, `_start` and `page` must be zero when present. Positive or
malformed positions do not by themselves establish whole membership. A separate
query model may describe supported paging; an empty later page cannot start
returning earlier entities merely because it was empty in the capture.
`POST`, `PUT`, `PATCH` and `DELETE` tools are served only when the state's
[write plan](web-network-captures.md#environment-state) for them is served:
the capture's later reads confirmed the effect the plan models. Other write
tools are not served, so no call claims a change it does not make; each one's
plan states why in its `reason`. A served write changes the live state, which
| `create` | Adds an entity to the collection the path names, built from the observed entity with each ruled field taken from the call or generated, at the plan's `insert` position. A plan tied to its exemplar's collection writes only there. |
| `update` | Sets the sent members on the entity the path names, constants for members accepted only with their recorded value, and the clock, nested clock, increment, slug, stamp, derived, composed, reference, unique and renewed changes the plan models. |
| `update` (action) | A `POST`, `PUT` or `PATCH` to an item path plus an action segment (`/{type}/{id}/archive`) sets, on the entity before the segment, each scalar the recorded calls showed the action set (`set`), or flips each boolean they showed it flipping both ways (`toggle`), with any sent members; it is served only when a recorded call found what it sets already set, or flipped back what it toggles. |
| `delete` | Removes the entity the path names. |
| bulk `update` or `delete` | A `DELETE`, `PUT` or `PATCH` at a collection, or a `POST` to it plus `delete`, `remove` or `destroy`, applies its effect to every entity the body names, as recorded: an array of ids or of objects carrying the id field, at the root or as the body's one array member (`{"IDs": […], "Read": true}`). |
| `document` | Merges the body into the plan's document object, or replaces it for `PUT`. |
An update's unsent changes follow the plan's rules. A `held-clock` is a clock
the client sends back as it read it and the application stamps anew (Ghost's
`updated_at`): the call must send the entity's value and the field takes the
write's clock. A `nested-clock` sets the clocks inside an object the entity
holds, in that object's recorded format. A `stamp` writes the acting user
the entity already names under its `created…by` counterpart into a
`modified…by` or `updated…by` field. A `derived` field takes a value showing
the one sent member's text that the entity stores nowhere (a field Ghost
derives from `html`), only with that member's recorded value. An action's
`set` rule sets a scalar to the value the recorded calls left it at, and its
`toggle` rule flips a boolean, refusing a call on an entity that holds none
there (Miniflux's `PUT /v1/entries/{id}/bookmark` stars and unstars).
Its answer has the status and content type of the plan's recorded response,
with the written entity or its identity rendered in, no body, or the recorded
body. A call whose body is not an object, sends a member the capture did not
show stored, sends a fixed member with another value or a member of another
JSON type, or lacks a required member is a tool error naming the accepted
members; so is a call sending query parameters other than the exemplar's (they
may change what the body does), a body not wrapped in the exemplar's envelope
(Ghost's `{"posts": [{…}]}`), a bulk call naming an id the state does not hold,
a `held-clock` member sent with another value than the entity holds, a call
sending a `derived` rule's member to an entity that does not hold the value the
recorded call found, a nested clock on an entity holding no such object, and a
rendered answer missing a field the recorded response carried, and an update or delete of an entity the state does not hold answers
the recorded 400, 404 or 410 of the tool or of an item view of the type, if
any. A served delete of a type whose item view has a `deleted_flag` (the
member, `deleted`, `isDeleted` or `is_deleted`, its reads set `true` on a
record the application soft-deleted, as EspoCRM does) keeps the entity until
`data/reset` or `data/add`, within the size `data/add` loads, so a read of it by
id answers its last fields with that flag `true`. A refused call leaves the
state unchanged. Values a write generates are
deterministic: the clock starts at `write_clock` and advances one second per
served write, sequences take one past both the largest held value and the
largest they generated, and UUIDs and tokens derive from the state Evidence
id, tool, field and write count, skipping held values. If `write_clock` is null,
plans needing `clock`, `held-clock`, or `nested-clock` retain their learned rules
but are withheld with `unknown-clock`; other confirmed plans can still be served.
`data/reset` and
`data/add` restart the clock, counter and sequences, so the same calls after a
reset give the same state. Answers carry `state: {"source": "state", "view":
"create" | "update" | "delete", …}` or `"document-write"`; a bulk answer adds
`ids`, every entity it wrote in body order, with `id` naming the first.
The data plane serves three JSON-RPC methods:
@@ -436,18 +824,29 @@ names its category in `error.data.reason`: `not-found`, `permission`,
downloads, and `cancelled` for a download the server's stop or its caller's
departure ended.
Served creates also bound additional collection history since the last reset
or `data/add` to 1 MiB of deterministic accounting: UTF-8 bytes for each new
collection spelling and its possible sequence keys, plus a 64-byte allowance
per collection and potential sequence-key record. The score bounds identifier
payload and a fixed record allowance; it is not a measurement or guarantee of
process memory. Collections
already present in the Evidence or loaded state are part of the state itself;
the store indexes their unique spellings so they remain known after deletion,
but they do not consume this additional-history budget. A create that would
exceed the budget is refused before the state changes. `data/reset` or `data/add`
clears generated history; if the same calls would hit the limit again, use
fewer distinct collection paths. The 1 MiB score ceiling is 1/32 of the
separate 32 MiB state-intake ceiling, reserving a small, explicit allowance
for identifiers retained beyond the loaded state. A loaded-state path that is
later deleted and then introduced again may consume the additional-history
budget when the store must retain it beyond the source entity.
Stopping the server cancels every pending call, and a caller that leaves before
its reply cancels its own: a download in progress ends, and calls not yet run
are answered `-32000` with `error.data.reason``cancelled` without running. A
cancelled call leaves the state unchanged, and shutdown never waits for a
download.
A `file://` state file whose close fails stays owned by the server, and
the `data/add` gets `-32000`. The next `file://` load retries that close first
and, while it still fails, answers `-32000` without reading another file;
inline and `https://` documents still load. Closing the server retries it once
more and fails with the unfinished close rather than dropping the descriptor.
One `/agentenv` response carries at most 64 MiB of replies, twice the 32 MiB
a loaded document may hold: room for a `data/get` of a loaded state, while a
batch of reads cannot multiply a large state into one body. Replies are
@@ -483,13 +882,17 @@ id are unique; `view_fields` is optional and names item or list views of its
type. Views render their values over the entity's fields, so a view's values
must not set the type's identity field to a value whose text differs from the
entity's `id`. Spell a collection as requests spell it, as `data/get` reports it: list
calls find entities under the checked line's recorded path. Each document
calls find entities under the checked line's recorded path. State collection
Each `--probe` is capture Evidence (`inspect-web-network-capture` of a HAR, or `capture-browser-scenario`) of one call
sequence made against the live application, seeded as it was for the capture and reset before the sequence. The command
makes no network call: whoever records the probes drives the application with its own credentials. For each probe it
resets the environment's state, takes the requests in the order they were made (record order when any lacks a start time), matches each to the manifest tool of the
same method whose path template fits with the most literal segments (a format-rule parameter must fit its format once
percent-decoded). A path argument is the raw segment, which the environment decodes once.
It answers the call as the served environment would, and compares that answer with the application's.
A re-seeded application holds the same entities under other ids, so the comparison maps them. Objects in the two answers
to one call pair by a natural key, a string field unique on each side that both share, such as a name or a slug; their
identifier fields (`id`, `uuid`, `guid`, and names ending in `_id`, `Id`, `ID`, `_uuid` or `Uuid`), and those of two single
answers such as a create's, map the live value to the environment's, including a create that returns only the new id.
Ids are learned only from an answer the environment
gave that call itself, from its state or a recording of that very call, request line and body, never from another call's
recording replayed in its place and never from a recording whose body the probe omitted. The answer's own `id`, `uuid` or `guid` also maps within the collection the call names, since
two collections can reuse a live id, and an equal pair stays in that collection. A live id in a later request's path, query or body is translated before the
environment answers, a path parameter by the collection the live path names (a nested collection keeps its parent values, each decoded once, and a literal route segment stays), except a query value that pages a list, such as `limit`
or `page`. Each call is checked against the served tool's input schema first, as the MCP server checks it. After mapping, two clock values compare by format only (ISO-8601 strings, and epoch numbers under
`at`, `time`, `date`, `timestamp`, `created`, `updated` or `modified` field names, including snake-case and camel-case forms).
Numeric arrays retain that containing field's clock context through nested
arrays; an object's own fields establish their own context. Numeric values at
the body root, in root arrays, or under measurement fields remain literal.
Generated values normalize only where this confirmed, served write's entity
response rules identify generation. A UUID identity rule must still compare two
UUIDs, and a `unique` rule two UUIDs or two generated integers (10 to 20
digits) of one JSON kind and length; a `renewed` rule applies only at its
recorded pointers,
with `*` matching actual array items. Token/sequence strings retain the existing
format check: equal length of at least eight, the same character classes, and
a digit. Numeric sequences continue to compare literally unless an ID mapping
applies. String shape and names such as `token` or `sku` alone do not establish
generation. Read and replay responses without these facts stay literal except
for supported clocks and learned IDs. A missing write target replays an absence
response and does not inherit the successful entity response's generation rules.
Admitted generated values must also preserve aliases and distinct identities
within that response. UUID identity uses the generator's case-insensitive
matching; token and sequence strings retain their literal identity. This
correspondence ends with the answer and is not inferred across calls.
A `composed` response field must spell the value its parts build from that
response's own fields. Its parts then compare by their rendered text or their
source fields' clock and ID rules. A coherent archive URL can therefore differ
only in normalized clock digits without reporting a value difference; an
incoherent URL still differs even when both sides repeat the same stale text.
Normalized compositions count under `generated`; source fields are counted once.
The report counts each kind of normalised
value; the generation rules remain model-derived facts, rather than new
observations of the application.
ISO clock offsets are accepted only within RFC 3339's `00`–`23` hour and `00`–`59` minute ranges.
Probe ordering retains every recorded fractional-second digit in ISO timestamps
with a four-digit year, `T`, and a `Z` or `±HH:MM` suffix. Equal instants use ordinal order. Other native-Date
spellings keep their existing millisecond interpretation; an absent or unreadable
start time makes the whole probe use record order. Original capture timestamps
remain unchanged. Clock-format comparison and respelling recognize four-digit
ISO years `0000`–`0099` as well as later years, with normal calendar validation.
Respelling preserves the original value when the target zone would require a year
outside `0000`–`9999`; generated writes refuse a clock outside that format.
Capture-state start ordering uses the same exact recorded timestamp facts,
including minute-only times and fractions longer than nine digits. Recorded
spellings may use `T` or a space, `Z` or an extended/compact offset, or no zone
(interpreted as UTC). Equal starts use ordinal order; a missing or unreadable
start makes the capture use record order. Request interval and overlap checks
still use millisecond coordinates, and generated-clock progression compares
milliseconds. Derived ISO clock formats still require seconds and at most nine
fractional digits.
Comparison keeps response representations distinct: JSON values compare structurally, text compares as text, and a
retained base64 body compares by bytes (including with UTF-8 text carrying those same bytes), never by its base64
spelling. ID mappings apply at identifier fields, `ids`/`uuids`/`guids` lists, an entity's own identity within its
collection, and a string field learning confirmed as a state-held ID. ID mappings leave other numeric and string values unchanged. A
global conflict involving an equal pair is not used to normalize either pair; the report counts it as an ID conflict.
A step agrees when the statuses and the normalised bodies are equal. The report gives agreement overall, per probe and
per tool, `agreement_ignoring_order` for steps that differ only in item order, and each step's differences, at most 20,
with a JSON pointer and both values cut short. Each step reports `differences_total` before that cap,
`differences_omitted`, and `differences_truncated` (true when entries were omitted). Agreement and categories use the
complete comparison. Omitted entries are not retained, so `--full-output` cannot recover them; inspect the original
capture and probe Evidence to investigate beyond the returned pointers.
| `missing-field` | The application's object has a member the environment's lacks |
| `extra-field` | The environment's object has a member the application's lacks |
| `value` | Values differ, or one is an object or array and the other is not |
| `length` | Arrays differ in length, or not every item pairs |
| `order` | Paired items come in a different order |
| `clock-format` | Both are clocks, in different formats |
| `refused` | The environment refused the call |
| `not-served` | The request's tool is one the environment does not offer, such as a withheld write |
| `no-tool` | No manifest tool matches the request |
| `unmapped-id` | The application answered with success and the environment did not, for a path id the comparison never mapped |
What a probe did not observe is not counted as the application's behaviour. A record without a usable request line,
such as a redacted URL or a transaction the capture cannot attribute, is not replayed, nor is a request to an origin different
from the application's (`other-origin`: the probe's origin with the most JSON-like records, as the manifest
chooses its own), and a request with no recorded
answer is made, since it may have changed the application, but not compared: each probe lists them under `unobserved` with the reason, and the summary counts them. A live
body the probe did not record compares by status only and is counted as `unobserved_bodies`, unless the answer never
carries one (204, 205, 304, `HEAD`). A browser probe captured without `capture.network.response_body` is refused, as
is one that makes POST, PUT or PATCH calls without `capture.network.request_body`.
Agreement covers only the calls the probes make, after the mapping the answers allow. Clocks compared by format
and fields normalized under explicit generation rules can still differ in content, and an equal answer after a write shows the state matched for that
read, not that the write had no other effect.
## Compatibility verification
`npm run verify:agentenv:environment` is the hosted `real-agentenv` lane
and [string escape owner](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.j.yaml/src/main/java/brut/yaml/YamlStringEscapeUtils.java#L55-L116).
- **The manifest is the decoded text**, bounded at 512 KiB and reported as
truncated when larger.
- **Strings are projected verbatim.** Escaped entities stay escaped, and
@@ -167,6 +180,11 @@ Multi-locale and table-only projection were also verified through real CLI and
stdio MCP workflows on Linux with Apktool 3.0.3/JDK 21 against NewPipe 0.29.1,
ApiDemos 6.0.18 and Appium Settings 8.2.1. Independent XML projection checks,
exact facts/Evidence parity and bounded task costs are recorded in the
[shared-decode and table-only checkpoint](../experiments/analysis-efficiency/apktool-string-tables.md).
[shared-decode and table-only checkpoint](https://github.com/morluto/rea/blob/42e1a17d6e6c0487f17be0bae248553c4caba7f7/experiments/analysis-efficiency/apktool-string-tables.md).
This qualifies selected resource facts, not application execution or independent
binary resource-decoder completeness.
If the selected APK changes while its private snapshot is copied, acquisition
returns `artifact_changed`; request-schema correction cannot recover a stable
snapshot. Launcher spawn permission denial is distinct from a missing launcher,
and configuration diagnostics retain the selected command and system error.
@@ -235,7 +254,7 @@ A `conditional` image is still traced, because it is an in-root candidate
that may load. Every edge below it has `loader_conditional: true`, and a
request that reuses it by install name is `conditional` too. An in-root dylib
with no platform load command remains unknown unless the root is an executable
with observed macOS platform and the candidate’s CPU type is x86_64, i386, or arm64. In that macOS context,
with observed macOS platform and the candidate's CPU type is x86_64, i386, or arm64. In that macOS context,
[Apple's dyld](https://github.com/apple-oss-distributions/dyld/blob/fd8d0c4d52320ebf64db34f3cb280310d905c5ae/mach_o/UnsafeHeader.cpp#L1137-L1145) supplies an implicit macOS platform to legacy dylibs on those CPU
types. The candidate records `platform_basis: "macos-dyld-implicit"`; the
reported slice fields `platform: null` and `platforms: []` remain the parsed
MAIN["src/main.ts + src/main/<br/>MCP server adapter: stdio default, streamable HTTP opt-in<br/>config, session, process lifetime"]
@@ -6,7 +8,7 @@ graph TD
end
subgraph "Production Composition"
COMPOSITION["src/composition/<br/>fresh typed provider and session factories<br/>one-shot binding and independent optional loading<br/>static WASM/EVM descriptors: named contracts, factories, retryable lifetimes"]
COMPOSITION["src/composition/<br/>fresh typed provider and session factories<br/>one-shot binding and independent optional loading<br/>typed capability bindings: construction, availability and retryable lifetimes<br/>pure capability declarations feed provider catalogs"]
end
subgraph "Adapters"
@@ -19,22 +21,29 @@ graph TD
subgraph "Application Layer"
APP["src/application/<br/>Shared CLI/MCP workflows<br/>enhanced analysis and investigations"]
SESSION["SessionProviderRouter + BinarySession<br/>one immutable deep binding per target<br/>disjoint auxiliary routing"]
REGISTRY["AnalysisProviderRegistry<br/>sorted deep candidates<br/>deterministic selection, no fallback"]
REGISTRY["AnalysisProviderRegistry<br/>sorted deep candidates<br/>deterministic selection, no fallback<br/>ProviderInspectionScope: prerequisite observation lifetime"]
RECORDS["src/application/investigation/<br/>composed InvestigationRecords + narrow ports<br/>EvidenceLedger and Unknown ownership"]
SNAPSHOT["BinarySessionRecords + AnalysisSnapshotCache<br/>binary target/profile snapshots<br/>compatibility facade and post-commit notifications"]
LIFETIMES["CapabilityScope<br/>host-owned service close capabilities<br/>released peers removed; failed cleanup retained for retry"]
SNAPSHOT["BinarySessionRecords + AnalysisSnapshotCache<br/>binary target/profile snapshots and session file ownership<br/>record coordination and post-commit notifications"]
ANDROID_TOOLS["src/adb/ + src/apktool/ + src/jeb/<br/>BYO device, resource-decoding and JEB adapters<br/>owned adb and Apktool processes; JEB engine client over MCP"]
CAPTURE_TOOLS["src/captures/ + src/pcap/ + src/signals/ + src/sqlite/<br/>BYO tshark, pcap, sigrok and SQLite child inspection<br/>selected captures, recordings and databases; owned processes"]
<descid="desc">Three illustrations. A brick-breaker screen with a speaker on each side stands for the DX-Ball sound-pan showcase. A browser window joined by a wire to a clipboard stands for the Notion clipboard-bridge showcase. A ring of 24 dots with one orange dot around a dark centre stands for the TH04 bullet-ring showcase.</desc>
<descid="desc">Three illustrations. A brick-breaker screen with a speaker on each side stands for the DX-Ball sound-pan showcase. A browser window joined by a wire to a clipboard stands for the Notion clipboard-bridge showcase. A ring of 24 dots with one orange dot around a dark centre stands for the TH04 bullet-ring showcase.</desc>
an `__RESTRICT` segment, `LC_CODE_SIGNATURE` presence, encryption from the
existing encryption reader, the arm64e subtype, and a stack-protector import.
`hardened_runtime` is `unknown` with `next_probe``inspect_signature`.
CodeDirectory flags stay with that tool.
The report states presence of build-time features. It does not state
exploitability. `inspect_binary_layout` is unchanged.
The default read budget is 64 MiB (`max_file_bytes`, ceiling 512 MiB). A changed
file is refused. A malformed header, a Mach-O segment or encryption command that fails admission, or a program, section,
or segment payload declared outside the file returns no partial report, except that one bad slice in a universal Mach-O leaves the other slices. A symbol table or
symbol name over its budget leaves the import rows `unknown` and keeps the
header rows. A GNU property note over its budget, or a compressed note section
that may hold one, leaves the CET and BTI/PAC rows `unknown` and keeps the header
rows. A dynamic-table budget still fails the ELF report. The image is never
@@ -12,16 +12,30 @@ For request/response bodies and traffic debugging, see
[Fiddler Everywhere captures and MCP](web-network-captures.md#reading-fiddler-captures-saz).
Inspect native SAZ or HAR 1.2 offline with REA, or connect Everywhere's separate MCP server.
HTTP discovery and browser WebSocket inputs require an explicit port. An
explicit default `:80` remains valid and is retained in the canonical endpoint;
omitting the port remains invalid. HTTP `/json/list` discovery preserves usable
siblings when optional metadata is malformed: attachment becomes unknown and
row/field diagnostics appear in `limitations`. Target identity and socket
authority are validated independently before selection and connection.
## Shipped surfaces
- `list_browser_targets` / `rea list-browser-targets` discovers all eligible page targets at the endpoint by default, with an optional exact-origin filter.
- `inspect_web_page` / `rea inspect-web-page` captures DOM structure, accessibility nodes, scripts, resources, safe response/DOM metadata, attach-window network and console metadata, WebSocket frame sizes, workers, quota, and optionally selected storage key names, redacted storage-content fingerprints, or script sources.
- `analyze_web_bundle` / `rea analyze-web-bundle` parses selected script artifacts without execution and derives chunk edges, route and endpoint candidates, vendor fingerprints, static WebMCP declarations, and optionally source-map/original-source evidence.
- `observe_web_session` / `rea observe-web-session` arms an observation window of the requested duration for an external user action and records ordered reload, SPA navigation, redirect, failure, lifecycle, and target-termination metadata.
- `discover_webmcp_tools` / `rea discover-webmcp-tools` uses the experimental CDP WebMCP domain to return every in-scope registration and its complete observed `input_schema` declaration and canonical `input_schema_sha256` digest inline. Declarations are untrusted JSON data, including declared types, required fields, enums, examples and reference URLs. REA does not resolve schema references. These fields replace `input_schema_shape`. Registrations with the same name and URL in distinct observed frames retain separate identities. `tool_key` includes the observed frame ID; it is not a stable identity across navigation or browser sessions. Same-frame registration updates replace that owner only, and removal leaves other owners intact. Declared `readOnly`, `untrustedContent`, `consequential`, and `autosubmit` annotation hints are reported per registration: `true`/`false` are declared values, `null` means the declaration did not provide the hint in that capture, and a missing `consequential` key means the hint was not recorded, either in a historical capture or because the observed annotation was invalid. Consult discovery completeness and exclusions to distinguish these cases. Malformed annotation values or registration stacks retain the registration with incomplete coverage and an `invalid_protocol_value` exclusion. Other malformed protocol payloads can fail discovery explicitly. REA never exposes `WebMCP.invokeTool`.
- `discover_webmcp_tools` / `rea discover-webmcp-tools` uses the experimental CDP WebMCP domain to return observed in-scope registrations and their complete observed `input_schema` declaration and canonical `input_schema_sha256` digest inline. Declarations are untrusted JSON data, including declared types, required fields, enums, examples and reference URLs. REA does not resolve schema references. An omitted optional `inputSchema` is a valid parameterless registration, represented by null schema and digest. These fields replace `input_schema_shape`. Registrations with the same name and URL in distinct observed frames retain separate identities. `tool_key` includes the observed frame ID; it is not a stable identity across navigation or browser sessions. Same-frame registration updates replace that owner only, and removal leaves other owners intact. Declared `readOnly`, `untrustedContent`, `consequential`, and `autosubmit` annotation hints are reported per registration: `true`/`false` are declared values, `null` means the declaration did not provide the hint in that capture, and a missing `consequential` key means the hint was not recorded, either in a historical capture or because the observed annotation was invalid. Consult discovery completeness and exclusions to distinguish these cases. Malformed annotation values or registration stacks retain the registration with incomplete coverage and an `invalid_protocol_value` exclusion. Other malformed protocol payloads can fail discovery explicitly. REA never exposes `WebMCP.invokeTool`.
- `compare_web_captures` / `rea compare-web-captures` compares passive captures or exact-step-aligned browser scenarios. Scenario results commit reproducible literal normalization and expose alignment failures plus artifact-level action, screenshot, DOM, accessibility, URL, history, storage, and event differences. Missing or truncated evidence is never treated as equivalence.
For a compact route query, select `detail: "summary"` and a
`view: {kind: "javascript-routes", collection: "routes", limit: 20}`. The
| Renamed WebMCP tool keys/frame IDs with one matching name, owner origin, and frame URL | Compare the registration content; local IDs do not prove change |
| Duplicate WebMCP owners with the same name, origin, and frame URL | Unknown correspondence; complete group count differences still prove change |
| Matching script inventory keys with retained source digests | Compare the retained SHA-256 values even without a producer hash |
| One corresponding script source is unavailable, or neither source nor producer hash is available | Source equality remains unknown; inventory changes and other proven differences survive |
| A partial HTTP response group is a subset of the complete peer | Unknown; omitted repeated responses can explain the difference |
| An observed HTTP response fingerprint is absent from the complete peer group | Changed; omissions cannot explain that retained response |
| A WebSocket connection or frame inventory is incomplete | Do not align retained payloads by position |
Storage fingerprints decode supported CDP primitive/JSON values before hashing.
Debugger descriptions, previews, and remote references do not represent storage
content. Actual JSON properties with those names remain content. Reference-only
objects, unsupported values, and opaque cookie partition identities remain
incomplete. Previously exported fingerprints are not recomputed; recapture both
sides when comparing captures produced across this decoder change.
IndexedDB schema fingerprints with `projection_version: 2` include the observed
database version and object-store definitions. A missing, malformed, or imprecise
version leaves the schema fingerprint incomplete while retaining observed store
facts. Historical fingerprints without this marker covered object stores only:
matching historical hashes cannot establish unchanged database versions, and a
historical hash is not compared with a version-bearing hash. Recapture both
sides for a complete IndexedDB version comparison; other proven storage changes
remain reportable.
Script comparison groups observations by reported URL and compares unordered
content samples. Producer inventory keys include optional metadata, so a changed
key cannot by itself prove script removal or addition. Missing module, length,
language or source-hash facts remain unknown; shared known values and complete
retained source digests can still prove a group change. Partial inventories use
the same omission rule as repeated HTTP samples. WebMCP dropped-tool counts also
prevent equality, and two incomplete owner inventories cannot prove tool
correspondence merely from their different retained registrations.
- `capture_web_screenshot` / `rea capture-web-screenshot` returns a content-addressed visible-viewport PNG.
- `compare_web_screenshots` / `rea compare-web-screenshots` performs bounded local PNG pixel comparison without OCR or external services.
- The CLI comparison commands accept each capture, screenshot artifact or normalization policy as inline JSON or a JSON file path. Use files for screenshot artifacts and scenario captures, whose embedded PNG bytes can exceed the host's command-line length limit.
@@ -90,6 +159,11 @@ preserve these authority and lifecycle differences. They share one exclusion
vocabulary (`BrowserExclusionReason`): every denial reason maps onto it, even
where a wire schema keeps a narrower historical bucket.
Passive page and Inspector capture enable the `Debugger` domain. A target's
`debugger` statement can then pause execution; these observation paths do not
suppress pauses with `Debugger.setSkipAllPauses` or resume the target. Their
lack of explicit pause commands does not guarantee uninterrupted execution.
CDP attachment is not a guarantee that the browser preserves all state.
Chromium can clear another client's `navigator.platform` emulation when REA's
page session disconnects, even if that session sent no emulation commands.
@@ -260,7 +334,11 @@ read as registration state only after domain availability is confirmed.
Discovery is attach-only: the command needs a reachable loopback CDP endpoint
and a target ID from `list_browser_targets`, and it never navigates, reloads,
or evaluates the page. Observe the page in the state you want to measure;
registered tools reflect the document as loaded.
registered tools reflect what the protocol delivered. If an in-scope subframe
already exists when discovery attaches, `webmcp_tools` remains attach-limited:
Chromium replay may omit its older registrations. Later live registrations
retain their frame ownership but do not establish recovery of those missing
descriptors. REA does not reload, evaluate, or invoke tools to fill that gap.
Accessibility names/descriptions, console primitive text, JSON body shapes, WebSocket shapes, script content, and storage key names are selected independently:
@@ -302,6 +380,20 @@ are reported in the result.
Call `list_browser_targets` first because target IDs are browser-instance-specific. REA rechecks the selected target's current type and origin immediately before attaching. Without an origin filter, discovery lists all eligible pages; an individual capture defaults to the selected page's current origin.
REA establishes a main-frame origin boundary before enabling Runtime, Debugger, or Network observation, rechecks it before document capture, and validates it again afterward. If the main frame navigates during final capture, REA discards the mixed result and returns `target_changed`.
An observed navigation also takes precedence over failures from stale
document reads, such as a script ID invalidated by reload; cancellation remains
cancellation. Retry after navigation settles, or refresh target discovery if
the selected page was replaced. Origin-scope refusals from page inspection,
runtime observation, screenshots, and WebMCP discovery explain the permitted
HTTP(S) origin scope and direct the caller to select a permitted document or
explicitly revise `allowed_origins`. Page capture also preserves an observed
main-frame scope violation when a later document read fails. An unresolved
initial main frame does not establish an origin violation; its bounded startup
retries retain a distinct refusal.
Screenshot capture requires a valid main-frame identity. Malformed identity
produces a protocol failure; a readable child frame cannot substitute for the
main frame.
The web-session timeline uses CDP monotonic seconds where the event producer
provides a comparable timestamp; absent or invalid timestamps are `null` rather
@@ -312,34 +404,78 @@ contextual evidence with `timestamp: null`, accompanied by a limitation stating
that it does not establish activity in the selected window. Main-frame scope
enforcement still applies to contextual navigation events. Network redirect
events retain their paired prior-hop response context when the timestamped
next-request event falls inside the window. The pre-arm timeline buffer is bounded
next-request event falls inside the window. A redirect row's optional
`redirect_response` contains the prior response `url`, CDP-reported `status`,
and `scope`. The URL has transport credentials removed; URL and status are
withheld unless its origin is approved. Missing or malformed response URLs have
null scope and add `invalid_protocol_value` timeline coverage; a valid URL with an
unsupported protocol has `unsupported` scope. Outside-policy and unsupported
responses mark the timeline as policy-filtered with `disallowed_origin` or
`unsupported_url`. Missing status remains null rather than zero. A supplied
non-numeric status is also null and records `invalid_protocol_value` coverage;
the other valid response facts remain available. Historical rows may omit this
field; omission does not establish that no prior response existed. Response
headers and bodies are discarded. The row timestamp belongs to the next request,
not an exact prior response time. The pre-arm timeline buffer is bounded
by original CDP message bytes. If clock sampling completes after that budget is
exhausted, the operation returns `payload_limit` instead of a partial window.
Cancellation and clock-request failures retain their own outcomes.
Cancellation and clock-request failures retain their own outcomes. Session
`load_failed` rows require a previously observed main-frame `Document` request;
subresources, child-frame requests and failures without admitted request ownership
are excluded. Redirects reuse their request identity, and completed/failed requests
release it. Active request identity metadata uses the existing 8 MiB retained-event
allowance; exhaustion fails the observation explicitly instead of guessing scope.
## Data minimization
The Link projection parses repeated CDP header field lines independently and
retains their declarations in order, including derived untrusted agent hints.
Commas inside URI targets or quoted parameters remain part of those values.
Response metadata parses repeated CDP header field lines independently. The
Link projection retains their declarations in order. `csp.directives` lists the
directives of every CSP policy in order, across field lines and comma-separated
policies, without recording policy boundaries, so a directive name can repeat
across policies. As in CSP3, a repeated name within one policy is ignored.
Single-valued policy fields combine their lines as one comma-separated value,
as RFC 9110 specifies. Each link keeps its target in `href` and
its relationship context in `context_url` with a separate `context_scope`;
absent `anchor` uses the response URL, while a relative `anchor` resolves
against that URL. Anchored links remain in the projection, but a `link_rel`
agent hint is emitted only when the resolved context matches the response URL
before userinfo credentials are removed. Following [RFC 8288](https://www.rfc-editor.org/rfc/rfc8288.html),
the `rel`, `anchor`, and `type` parameters use their first occurrence, and
quoted-pair escapes are decoded as specified by [RFC 9110 §5.6.4](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.4).
The context fields are optional when reading older saved captures. Commas
inside URI targets or quoted parameters remain part of those values.
Well-known-resource hints require an exact supported pathname; path case is
preserved because a case variant may identify a different HTTP resource.
COOP and COEP projections retain the exact declared token after validating the
complete Structured Field Item, including parameters such as `report-to`.
These are declarations, not an assessment of the document's effective policy;
absent, malformed or non-token items project as `null`.
Console entries, WebSocket frame summaries, and selected request/response JSON
shape outputs share one 8 MiB retained-observation allowance per capture. Shape
inference admits expanded path bytes before allocating those paths, including
escaped and Unicode property names. Replacing or removing a request's shapes
refunds their retained charges. A refused body shape preserves network metadata
and any captured peer shape with `resource_budget_exhausted` coverage; a refused
WebSocket frame is counted as dropped rather than reclassified as non-JSON text.
The allowance has no runtime override. Protocol messages and JSON input parsing
remain subject to their separate transport admission; this output allowance does
not bound the whole process heap.
REA retains local observation data and removes only URL userinfo credentials, explicitly declared scenario secrets, and values from structured credential/storage fields:
- URL observations preserve the complete local URL, including query values, fragments, duplicate parameters, and their order. Only username/password userinfo is removed; malformed original URL strings remain available with `origin: null`.
- DOM snapshots retain node types, node names, value lengths, and attribute names, but not text or attribute values.
- Accessibility structure and roles are retained by default, while names and descriptions require `include_accessibility_text: true`.
- DOM snapshots retain node types, node names, value lengths, and attribute names, but not text or attribute values.`dom.node_value_length_unit` is `utf16-code-units` for web observations and applies to every node's `node_value_length`; it counts JavaScript string code units, not Unicode code points or UTF-8 bytes. Electron observations declare `utf8-bytes` instead. Historical captures without the unit leave it unknown; reading them does not infer a unit or convert their stored lengths.
- Supported DOM URL attributes are projected separately as resolved destinations. Empty `action` on HTML or XHTML forms and empty `formaction` on HTML or XHTML submit controls resolve to the document URL; other relative URL attributes resolve against the captured document base.
- Accessibility structure and reported roles are retained by default, while names and descriptions require `include_accessibility_text: true`. The browser protocol may omit an individual role; that role remains unknown for comparison. Other observed accessibility differences can still establish a change.
- Network observations retain method, status, MIME type, size, type, initiator stack location, and complete URLs with userinfo credentials removed. Redirects reported by CDP are attached as ordered `redirects` on the one request ID, with the prior request URL, CDP response URL, method/type, response status/MIME/encoded length, request timestamp, and the next `requestWillBeSent` event timestamp. CDP delivers `redirectResponse` with that next request event, so the latter is an event boundary rather than an exact response timestamp. Redirect response headers and bodies are discarded; hops outside the selected origins are discarded with the request chain. Headers are discarded after an allowlisted projection of length/encoding, structured CSP/Link/policy fields, and untrusted agent hints. Cookies and authorization headers are never retained.
- Request/response bodies are not requested or parsed by default. When selected, only allowed-origin JSON media types are read, converted immediately to complete value-free typed paths, types and observation counts, and discarded. Each path segment is either `{ "kind": "property", "name": "raw property name" }` or `{ "kind": "array-element" }`; array-element types include primitives and nested arrays. Literal `*` properties remain distinct from array elements. `Network.getResponseBody` is never sent unless JSON body shape capture is selected. Comparison uses body shapes only when both captures selected them, and compares request/response shapes only where both observations provide those facts. Unavailable or truncated selected coverage makes equivalence unknown; observed differences still report changed.
- Request/response bodies are not requested or parsed by default. When selected, only allowed-origin JSON media types are read, converted immediately to complete value-free typed paths, types and observation counts, and discarded. Each path segment is either `{ "kind": "property", "name": "raw property name" }` or `{ "kind": "array-element" }`; array-element types include primitives and nested arrays. Literal `*` properties remain distinct from array elements. `Network.getResponseBody` is never sent unless JSON body shape capture is selected. A JSON-typed request body that CDP reports through `hasPostData` but omits from `postData`, or that cannot be parsed as JSON, makes `json_body_shapes` unavailable. That request's `body_shapes.status` is `partial`, not `included`, when only its response shape was captured. Base64 response bytes must be valid UTF-8 before JSON interpretation; invalid bytes leave the response shape unavailable and preserve any observed request shape and network metadata. Producer-decoded strings and genuine U+FFFD characters retain their reported meaning. Comparison uses body shapes only when both captures selected them, and compares request/response shapes only where both observations provide those facts. Unavailable or truncated selected coverage makes equivalence unknown; observed differences still report changed.
For normalized passive inputs with complete request inventories on both sides,
request additions and removals remain observable even when selected body shapes,
response metadata, or WebSocket coverage are unavailable. HTTP request and
WebSocket inventory coverage are evaluated separately; complete HTTP coverage
cannot prove a missing WebSocket connection. Limited request inventory still
prevents request-absence claims. This comparison rule does not make live CDP's
cannot prove a missing WebSocket connection. A missing request in a limited inventory cannot prove absence; a retained
requestabsent from a complete peer can prove a change. This comparison rule does not make live CDP's
attach-limited inventory complete or establish body-shape equality.
An unavailable HTTP resource type also leaves request identity incomplete:
observations with the same method and URL remain possible matches across types,
@@ -354,13 +490,17 @@ REA retains local observation data and removes only URL userinfo credentials, ex
uses `websocket_connections` and `websocket_frames`. Drops in other event kinds
and the aggregate total do not by themselves limit HTTP inventory. Proven
Repeated identical HTTP content samples establish multiplicity changes only
with complete request inventories; limited inventories retain known content
comparisons without interpreting duplicate sample counts as changes.
Repeated HTTP samples retain multiplicity. A partial group that is contained
in a complete peer cannot prove modification, even when the omitted response
had different content. A retained sample absent from the complete peer can
prove modification. Two partial groups cannot establish response
correspondence from their different retained samples alone.
WebSocket stream inventories and connection counts likewise require complete
WebSocket coverage. Redirect resource types use only shared known metadata:
WebSocket coverage. Retained frame positions are compared only with complete
connection/frame inventories; matching direction/opcode sequences alone do
not prove alignment after drops. Redirect resource types use only shared known metadata:
an omitted type leaves equality unknown rather than proving a modification.
- Console observations with a stack source retain call type, argument types, the CDP `Runtime.Timestamp` in milliseconds since Unix epoch, and source location. An absent or invalid timestamp is `null` and makes `console_events` incomplete; an observed zero remains zero. Argument types are listed by CDP argument position. An absent or non-array argument inventory is `null`, and a malformed argument keeps its position as `unknown`; either makes `console_events` incomplete, while an observed empty inventory remains `[]`. A malformed stack or top stack frame is not replaced by its caller: the console event is excluded, and a request initiator location or WebMCP `registration_source` is `null`; each is reported as `invalid_protocol_value`. This epoch-millisecond clock is separate from the monotonic-second timestamps in `observe-web-session`. When console text capture is selected, already-delivered primitive values are retained verbatim with their original `argument_index`; objects, getters, and remote properties are never expanded. Text capture is `unavailable` when the argument inventory is unknown.
- Console observations with a stack source retain call type, argument types, the CDP `Runtime.Timestamp` in milliseconds since Unix epoch, and source location. An absent or invalid timestamp is `null` and makes `console_events` incomplete; an observed zero remains zero. Argument types are listed by CDP argument position. An absent or non-array argument inventory is `null`, and a malformed argument keeps its position as `unknown`; either makes `console_events` incomplete, while an observed empty inventory remains `[]`. A malformed stack or top stack frame is not replaced by its caller: the console event is excluded, and a request initiator location or WebMCP `registration_source` is `null`; each is reported as `invalid_protocol_value`. Source locations are CDP's zero-based lines and UTF-16 columns. A negative column, which Chrome reports on the first line of some inline scripts, is kept as reported and does not locate a column in the source URL; an invalid line or a non-integer column is `null` (WebMCP `registration_source` is `null`) and is reported as `invalid_protocol_value`. This epoch-millisecond clock is separate from the monotonic-second timestamps in `observe-web-session`. When console text capture is selected, already-delivered primitive values are retained verbatim with their original `argument_index`; objects, getters, and remote properties are never expanded. Text capture is `unavailable` when the argument inventory is unknown.
- WebSocket observations retain direction, opcode, and payload byte length. A missing or malformed CDP opcode is returned as `null`, with opcode-dependent payload byte length and shape also `null`, and marks `websocket_frames` incomplete. Missing or wrong-typed payload data is not treated as empty; its byte length is `null`, and a text payload shape is unknown. Numeric opcode `0` and an observed empty payload remain distinct from missing data. Opcode is nullable in the public inspection schema, so clients must handle unknown protocol values. When shape capture is selected, text messages are classified as text or complete value-free JSON shape; binary bytes, hashes, prefixes, and raw payloads are never retained.
- Storage observations always redact values. Key names, IndexedDB names, and cache names require `include_storage_keys`; stable content fingerprints require the additional `include_storage_fingerprints` selection. Cookie fingerprints cover cookies applicable to the current main-frame URL, including its path, rather than every cookie path on that origin. Other storage remains scoped to the origin. Cache fingerprints hash all decoded response bytes, including bodies above 64 KiB, subject to the [64 MiB per-message CDP limit](#request-boundary). Missing or undecodable cache bodies and partial IndexedDB remote objects make fingerprint coverage incomplete, so identical observations remain `unknown`.
- Unavailable or malformed storage inventories mark `storage_keys` unavailable, even without fingerprints. Comparisons suppress inferred key additions and removals while preserving modifications proven by complete fingerprints on both sides. Previously exported captures are not retroactively corrected; recapture them to obtain these coverage markers.
@@ -370,6 +510,11 @@ Frames, resources, scripts, events, and workers outside the selected origin scop
Retained workers include validated opener-target and parent-frame IDs when CDP
provides them; these relationships do not expand the exact-origin boundary or
prove static module ownership.
Workers without either relationship are withheld with an `unattributed_target`
exclusion and `workers` in `unavailable_sections`: a same-origin URL does not
establish which page owns a worker. A supplied relationship to another target or
frame remains `out_of_target_scope`. Missing ownership also remains unknown when
the worker URL is empty; withheld target details are not returned.
## Completeness and limits
@@ -384,8 +529,17 @@ validation and resource bounds. Omitted script metadata makes its affected
section explicitly truncated.
Disallowed-origin entries are filtered before retention.
Script metadata and selected source reads use one fixed admitted-script snapshot.
New in-scope script IDs still retained when the final CDP read completes are
counted in `scripts.total` and `completeness.dropped_events.scripts`, with
`scripts` marked truncated; they do not extend the source-read pass. Repeated
late events for the same ID count once. The original snapshot is not replaced
or reduced by later updates to an existing ID. These omissions do not imply
that a source byte or time budget was exhausted.
Selected script-source collection shares a 32 MiB aggregate UTF-8 retention
budget and a 20-second deadline across the capture. An oversized source is
budget and the `command_timeout_ms` deadline (default 20000 ms) across the
capture. The byte budget has no runtime override. An oversized source is
omitted while later smaller sources may still fit. When the deadline expires,
remaining source reads are skipped. Script metadata, per-item omission reasons,
and `script_sources` completeness remain available. A single CDP response can
@@ -393,19 +547,25 @@ still be materialized up to the separate 64 MiB transport limit before source
admission; the retention budget does not bound that transient allocation.
Bundle parsing and semantic inspection run sequentially in an owned child,
without executing captured code. `max_heap_mb` defaults to 1024 MiB V8
old-space; `analysis_timeout_ms` defaults to 30000 per script, including startup
and transfer. A failed or timed-out script becomes an explicit unknown while
completed findings and later scripts remain available. `total_analysis_timeout_ms`
defaults to 300000 across script analysis; expiry returns a typed timeout with
partial Evidence and identifies unstarted scripts. These controls are also
available as CLI flags with hyphenated names. Capture and optional source-map
fetching have separate budgets.
without executing captured code. These are separate phases. `max_heap_mb`
defaults to 1024 MiB V8 old-space (range 128..16384). `analysis_timeout_ms`
defaults to 30000 per script, including startup and transfer.
`total_analysis_timeout_ms` defaults to 300000 across script analysis. A
per-script deadline above the effective total is rejected. A failed or
timed-out script becomes an explicit unknown while completed findings and later
scripts remain available. Total-deadline expiry returns a typed timeout with
partial Evidence and identifies unstarted scripts. `source_map_fetch_timeout_ms`
defaults to 30000 and is an item limitation, not that analysis timeout. These
controls are also available as CLI flags with hyphenated names. Worker protocol
and result byte budgets have no runtime override.
One web-bundle capture/analysis is admitted per REA process at a time. Completed
script results share an 8 MiB serialized transfer budget, with host heap
reservation checked before decoding and validating another result.
Exhausted result capacity stops further script launches and returns a typed
When the host reservation is what reaches zero, the failure names that heap
headroom and the remaining fixed budget. When the 8 MiB allowance itself is
exhausted, the failure names that fixed budget and says it has no runtime
override. Either stop ends further script launches and returns a typed
resource failure with completed facts and explicit unstarted-script unknowns.
Worker control frames are limited to 64 KiB. Cleanup must confirm child termination
before releasing its private files; failed cleanup retains its exact owner and
@@ -415,8 +575,10 @@ facts, including cancellation during final cleanup.
The worker heap setting is not a process-family RSS limit. Node enforces V8
old-space flags; Bun ignores them. Windows Bun instead uses an owned process
commit limit with a 512 MiB runtime reserve; other Bun hosts have no enforced
memory limit. These bounds do not establish an aggregate host-memory limit
across other REA operations or external processes.
memory limit. An allocation failure before that Windows limit is reported as
reached still names the configured limit and says it was not reached. These
bounds do not establish an aggregate host-memory limit across other REA
operations or external processes.
Resource-tree inventories distinguish a valid empty `resources` array from a
missing or malformed collection. Invalid resource entries are omitted with
@@ -429,13 +591,31 @@ An explicitly empty MIME type is valid. The optional content size may be absent;
an invalid supplied size remains unknown with incomplete resource coverage while
the resource's other facts are retained. Zero and nonnegative fractional sizes
are preserved.
`resource_key` remains the exact observation identity used by script
reconciliation. Capture comparison groups resources by URL, origin, type and
MIME type, then compares known sizes within a group. If a matched group contains
an unknown size, comparison reports unknown rather than treating the observation
as a resource addition or removal; complete inventory coverage still establishes
when an entire group is absent.
CDP discovery has a 10-second deadline. Each WebSocket handshake and command
response has its own 20-second deadline, separate from the caller-selected
observation window. A stalled handshake or command reports `provider_timeout`;
caller cancellation still reports cancellation. WebSocket messages remain subject
to the [64 MiB limit](#request-boundary). REA does not impose a message-count or
pending-command quota. On cancellation, it closes only its own WebSocket,
CDP discovery has a 10-second response deadline. HTTP discovery documents
use an 8 MiB budget. Browser-socket discovery (`Browser.getVersion`,
`Target.getTargets`) uses the 64 MiB protocol message budget. Neither budget
nor the discovery deadline has a runtime override. `list_browser_targets` accepts no
`command_timeout_ms`: HTTP discovery and browser-socket discovery
(`Browser.getVersion`, `Target.getTargets`) stay on that fixed deadline, and
the socket closes without a later command. Other CDP tools use
`command_timeout_ms` (default 20000) for each WebSocket handshake and command
response, separate from the caller-selected observation window, discovery, and
the 5000 ms cleanup grace. A browser-level socket's handshake and the following
`Target.attachToTarget` each receive that full deadline; they do not share one
budget. A stalled handshake or command reports
`provider_timeout` and names `command_timeout_ms` only when that operation
accepts it; caller cancellation still reports cancellation. WebSocket messages
remain subject to the [64 MiB limit](#request-boundary), which has no runtime
override. REA does
not impose a message-count or pending-command quota. On cancellation, it closes
only its own WebSocket,
waiting up to one second before terminating it. Malformed unsolicited events
and malformed replies to pending commands poison the connection so later
commands fail closed. A correlated reply's session ID must match the command's
@@ -444,22 +624,63 @@ rejects it as unknown; other command rejections retain their reported failure.
Network and console coverage starts only after REA attaches and enables the relevant CDP domains. `prior_activity_available` is always `false`; absence from these arrays is not evidence that an event never occurred. Scripts are different: scripts the page parsed before REA attached are still reported under `scripts`. A page that loaded before attach can therefore list its scripts while `network.requests` lacks the requests that fetched them. If CDP reuses a request ID without supplying redirect metadata, REA retains the predecessor observation and marks network coverage incomplete.
Source maps are not fetched by inspection. Bundle analysis fetches them only when `fetch_source_maps` is selected, with exact-origin redirect checks and no credentials, cookies, or referrer. Each fetch operation has a 30-second deadline and a 64 MiB aggregate streamed-response budget across redirects. A deadline or byte-limit failure remains explicitly unavailable and makes bundle completeness partial. Source-map decoding also bounds raw resolved identities to 64 MiB and aggregate inline evidence to 32 MiB. Failed maps preserve their failure context when it fits; if another context cannot fit, collection stops with the full requested count, the returned processed count, and an explicit unknown-results limitation. Retained mappings and original sources are returned inline. See [source-location limits](web-source-location.md) for the structural decoder bounds. Original source artifacts with `.ts`, `.tsx`, `.mts`, or `.cts` suffixes carry `text/typescript`; other source names retain `text/javascript`.
Source maps are not fetched by inspection. Bundle analysis fetches them only when `fetch_source_maps` is selected, with exact-origin redirect checks and no credentials, cookies, or referrer. Each fetch operation uses `source_map_fetch_timeout_ms` (default 30000) and a 64 MiB aggregate streamed-response budget across redirects. The byte budget has no runtime override. A fetch overrun is an item limitation, not a thrown analysis timeout. A deadline or byte-limit failure remains explicitly unavailable and makes bundle completeness partial. Source-map decoding also bounds raw resolved identities to 64 MiB and aggregate inline evidence to 32 MiB. Failed maps preserve their failure context when it fits; if another context cannot fit, collection stops with the full requested count, the returned processed count, and an explicit unknown-results limitation. Retained mappings and original sources are returned inline. See [source-location limits](web-source-location.md) for the structural decoder bounds. Original source artifacts with `.ts`, `.tsx`, `.mts`, or `.cts` suffixes carry `text/typescript`; other source names retain `text/javascript`.
source-map items retain the script's `declared_url` separately from the observed
`effective_url`. successful maps use the latter as their source-resolution base.
failed fetches retain the last response URL observed within the approved origins;
`effective_url` is `null` when no response was observed. query parameters are
A fully read response that fails UTF-8, JSON, mapping, or structural validation
retains `artifact: null`, empty original-source, module-edge, and mapping
arrays, and an explicit failure limitation. Invalid content is `invalid`;
decoder or structural capacity refusal is `fetch_failed`. Its optional
`response_identity` records the complete body's SHA-256 digest, byte count,
and `fetch-response-body-bytes` representation without retaining malformed
text or charging that text to the 32 MiB output budget. These are bytes
delivered by Fetch after HTTP content decoding and before UTF-8 decoding, not
HTTP wire bytes. The identity is absent when the body was not fully read, and
older observations may omit it. A response whose text cannot fit the existing
output budget is likewise `fetch_failed` with text omitted and completed-body
identity; JSON parsing and source-map decoding are not attempted. Successful and partial maps keep their
full text artifacts. Their original text, including an initial BOM or XSSI
line, remains distinct from the parsing view that removes that framing.
Collection reads the body once and does not raise its response or output limits.
When fetching is selected, source-map `excluded` facts preserve the capture
ledger's reasons and counts for declarations rejected before download. No
disallowed URL details are retained or fetched. `requested` and `processed`
count the admitted fetch requests; exclusions are counted separately. Excluded
declarations make the collection unavailable, or partial when maps were retained.
An empty collection with no declarations remains `not_requested`.
If the caller cancels while later source maps are being fetched, the failure's
partial Evidence retains the captured page and every completed map item. Its
`normalized_result.kind` is `web-bundle-acquisition` and
`analysis_status` is `not_performed`: no script-analysis result is claimed.
`source_maps.requested` remains the admitted request count,
`source_maps.processed` counts completed items, and any unprocessed requests
have an explicit unknown-results limitation. Capture exclusions remain in the
same collection. The cancellation error keeps the caller's exact reason;
an already observed response resource or cleanup failure keeps its own error
category and cleanup owner.
Source-map items retain the script's `declared_url` separately from the observed
`effective_url`. Successful maps use the latter as their source-resolution base.
Failed fetches retain the last response URL observed within the approved origins;
`effective_url` is `null` when no response was observed. Query parameters are
preserved, and both URLs count toward the inline evidence budget.
Current fetches emit a string or null; historical observations may omit
`effective_url` without changing their stored content or Evidence identity.
An observed URL must fit before its response body is read. If it cannot fit,
remaining map results are explicitly unknown; failed cancellation retains its
cleanup context and resource constraint.
Indexed maps whose decoded mappings cross the next section's starting position
are invalid under [ECMA-426](https://tc39.es/ecma426/2024/), rather than complete
maps with clipped mapping rows. Fetch collection and source tracing share this
validation.
## Non-goals and threat model
The browser provider deliberately does not expose generic CDP commands, `Runtime.evaluate`, WebMCP invocation, navigation, input, downloads, page closure, or browser closure. Screenshot capture is the sole pixel surface and returns an image only when requested. REA disables the domains it enabled, detaches the target session, and closes only its own WebSocket.
Discovery and attachment errors remain primary if closing REA's socket also
fails. The cleanup failure is reported alongside the original command failure;
it cannot replace the operation diagnostic.
This feature is not a browser sandbox or network containment mechanism. The attached page and browser continue running with their existing privileges and may make external requests independently of REA. CDP gives deep access to the selected browser profile, so use a dedicated profile, include only origins you intend to inspect, and treat other same-user processes as outside this boundary.
@@ -9,23 +9,74 @@ liveness deadlines. `INPUT_JSON` may be inline JSON or a JSON file path.
The minimal request contains only browser launch/connect selection, `start_url`,
and at least one `actions` entry. Launch `headless` defaults to `true` and can
be set to `false` where the host supports a visible browser. Navigations follow
the destinations named by the scenario and the page's ordinary browser requests;
there is no separate origin allowlist or request-interception layer. Environment
be set to `false` where the host supports a visible browser. Launched Chromium
keeps its sandbox; on hosts that cannot provide one, such as root in a
container, set `REA_BROWSER_NO_SANDBOX=true` for the REA process. Termux on
Android always launches without it. Navigations follow
the destinations named by the scenario and the page's ordinary browser requests,
and [request steps](#request-steps) send only to the start URL's origin;
there is no separate origin allowlist. Request steps use a temporary dispatch
gate for cancellation; ordinary page requests retain their browser behavior. Environment
settings (including service workers blocked by default), empty storage, and a
final sanitized URL capture are supplied by default. Set
`environment.service_workers` to `allow` when the application requires them.
The connected service-worker policy owner retains its exact CDP transport
through discovery, attachment and restoration. Failed admission preserves its
primary diagnostic when socket cleanup also fails; later policy cleanup retries
that retained transport and clears cleanup uncertainty only after close succeeds.
For attached browsers, `block` rejects captures when the selected page or its
frames are controlled by a service worker or already have a registration,
including after navigation. Existing caller registrations are never removed.
including after navigation. Policy checks bracket each artifact capture, including
the final step. If an external worker takes control while artifacts are being
captured, REA rejects the capture and retains the completed artifacts in
`details.diagnostics.collected_artifacts`; they do not establish policy-compliant
completion. Existing caller registrations are never removed.
Navigation can run destination code before a newly discovered worker is rejected;
that rejection does not roll back browser effects. REA removes its owned
future-document scripts, restores live frame methods, then disconnects.
Add secret declarations, storage seeds, and additional artifact or event
capture only when needed. Action, secret, and storage counts are not capped.
Duration, action, and navigation timeouts remain fixed provider-owned liveness
limits. `capture.limits` controls retained event and network-content budgets;
it does not change those liveness deadlines.
capture only when needed. Missing environment-backed secrets fail as input errors before browser acquisition, naming the declaration and required variable. An explicitly set empty string is a resolved value. Action, secret, and storage counts are not capped.
Duration, action, and navigation timeouts remain separate from CDP command
deadlines. `command_timeout_ms` (default 20000; omit it to keep that default
and leave it out of Evidence) bounds launch startup, the connect-over-CDP
handshake, target lookup, environment commands, and storage CDP commands,
including replies from attached child targets. It also bounds the initial
`start_url` navigation through the load event; a redirect or replacement
navigation cannot leave startup waiting indefinitely. Set it in the scenario JSON.
`capture-browser-scenario` has no `--command-timeout-ms` option.
It does not change action `timeout_ms`, `wait_for_timeout.duration_ms`, or
action navigation. Service-worker restoration keeps a fixed 5000 ms grace and does
not inherit a raised `command_timeout_ms`. CDP discovery stays on its own
fixed deadline. When service workers are blocked, the registration check
before discovery has its own fixed 10000 ms deadline; `command_timeout_ms`
does not apply because discovery has not started. The discovery deadline
covers endpoint discovery only. The following WebSocket handshake and
`Target.attachToTarget` each use `command_timeout_ms`. `capture.limits` controls retained event and network-content
budgets; it does not change those liveness deadlines.
## Validate a scenario before running it
`validate-browser-scenario INPUT_JSON` checks one scenario against the same
`browserScenarioSchema` refinement that `capture_browser_scenario` admits, and
stops there. `INPUT_JSON` may be inline JSON, a JSON file path, or `-` for
standard input.
```bash
rea validate-browser-scenario ./browser-scenario.json --json
```
A valid scenario reports `{"valid":true,"actions":<count>,"secrets":<count>}`
and exits 0. An invalid one exits 1 and reports the same field paths capture
would report, so a correction made here is the correction capture needs:
duplicate or reserved step IDs, duplicate secret identities and
- A `request` step saves with `save: [{name, json_pointer, secret}]`, from its
own response. A `click` or `press` step saves with `save_response: {method,
path, save}`, from a newly observed matching response while the step waits.
The recorder watches before the action and excludes requests already running,
such as an earlier autosave. Method/path matching does not prove that the UI
caused the request: use a path that distinguishes the intended operation from
background polling. Multiple observed matches before binding fail as ambiguous;
requests starting after this observation window cannot be ruled out. It matches the method and a path on the
start origin as the browser spells it, so a path may name `My List` or
`My%20List` (`{*}` matches one segment, and the query is ignored), and fails
the step unless a 2xx JSON response arrives before its `timeout_ms`; one still
being read when the step times out or is cancelled saves nothing. A pointer
that names no string or number fails the step, so later steps never send a
missing value, as does a response over 4,000,000 characters: a request step
stops reading and aborts its fetch there. A UI step reads a body only when
its decoded size is bounded at 12,000,000 bytes or less: its received size,
or the declared Content-Length of one served from cache, times 1032 for each
gzip or deflate coding, the most DEFLATE expands. Other codings and unknown
or zero sizes are refused before materializing the body; decoded text over
the character limit also fails. The UI action may already have applied.
- `{saved:name}` sends a saved value in a request step's `path`, including its
query, and in a `goto` destination's `url`, percent-encoded; a destination's
structured `query` values are sent as declared. A saved value that makes a
path segment `.` or `..`, alone or with the text beside it (including `%2e`),
fails the step, since the URL would drop it. In a request step's `json` body,
a string that is only `{saved:name}` becomes the saved value with the JSON
type it was saved with, so a saved number id is sent as a number, and
`{saved:name}` inside a longer string is filled as text. A header value or
Basic credentials part sends one as `{"source": "saved", "name"}`. Other
braces, such as `{id}`, are sent as written, and so is a percent-encoded
`%7Bsaved:name%7D` anywhere but a `goto` URL, whose parsed form spells the
braces that way.
- Validation refuses a saved value that no earlier step saves. A later save of
the same name replaces the value. An integer past 2^53, which JSON parsing
would round to another number, and a number that is not finite, such as
`1e400`, negative zero, or a value that underflows to zero, such as `1e-400`, fail the step
that saves it.
- A scenario keeps at most 4,000,000 characters of saved values at once,
counting distinct retained redaction spellings, including trimmed variants,
of values saved as secret. Replacing a binding releases its live value
charge, while secret spellings remain protected for the scenario lifetime.
A save past the budget fails before changing the binding.
- A value saved with `secret: true` is redacted however short, as a declared
secret is, as `[REDACTED:page:<step_id>.<name>]`. The selected source
response is protected before retaining its body or header values, including
when only headers were requested. If that body cannot be read safely, its
body and header values remain unavailable. Before publishing the capture,
the selected source transaction's earlier URL, request headers and request
body are also protected using the admitted values. Unresolved source values
withhold that transaction's affected metadata/content with an explicit
limitation. Before publication, retained events are rechecked
with the final marked-response values, including immediate follow-up requests
and their response bodies, console/page errors, WebSocket text and event URL/name fields. Learned redaction facts are independent of source-identity readiness. Unknown source values withhold only the selected
source transaction. Other saved values, such as ids, stay visible, since
environments model them. The environment build warns about saved values
named like credentials that are not marked secret
(`credential-named-saved-values`).
- Page-derived cookie/local-storage values and constructed Basic credentials
remain redacted only from observations made after they were read/built.
They do not retroactively rewrite earlier facts. The final event projection
borrows only declared and response-marked patterns; promotion of an already
held page value to an explicit response secret preserves its first marker.
Projection state is session-local, references admitted pattern/byte owners,
and is invalidated on admission or response classification changes.
- Screenshots are withheld as `missing` from the start when the scenario plans
any response save marked `secret: true`, page-derived or Basic authentication
header values, or whenever nonempty sensitive values
are already known. Raw pixels cannot be safely text-redacted, so the provider
does not request pixels in those cases. This also protects earlier steps and
partial failures without retaining an image to scrub later. Other requested
artifacts remain available; affected screenshot/aggregate completeness is
partial and is not eligible for equality.
- Saved URLs and expanded request JSON are admitted before building their expanded strings or
serializing. Its allowance is the smaller of Node's string limit and
one sixteenth of current V8 available heap bytes, allowing for the expanded
tree, serialization, Playwright transport and browser-side copies. Size
accounting includes JSON escaping and repeated references; each distinct
saved value's size is measured once per request. An oversized request fails
before browser dispatch; the complete request envelope, including repeated
header values, is checked as well. URL encoding uses a conservative
nine-characters-per-UTF-16-unit allowance before encoding a saved value.
Reduce the payload/repetitions, or raise the parent
Node heap and restart; this is a conservative allocation estimate, not a
measurement or guarantee of browser/process-family memory.
- Evidence records a request step's empty `save` list as absent, so a scenario
that saves nothing records the same parameters and `scenario_sha256` as
before saved values existed, and REA versions without saved values read its
virtualizes AppData writes. Once it has saved its configuration, it reads
the package-private copy
@@ -51,6 +237,11 @@ in the file the installed app reads:
private copy when it exists. Before the package has saved one, they use the
`%APPDATA%` file it still reads; with neither file, setup creates the
private copy.
- If `%LOCALAPPDATA%\Claude-Data\claude_pzs8sxrjxfjjc` exists, setup, doctor
and uninstall report an unresolved location. Migrated MSIX installations
have been observed reading `claude_desktop_config.json` in this directory.
Quit Claude Desktop and verify its active configuration before registering
manually. See [#8968](https://github.com/morluto/rea/issues/8968).
- If `%LOCALAPPDATA%\Packages` holds a `Claude_<publisher ID>` package from
another publisher, an `AnthropicPBC.Claude_<publisher ID>` package, or cannot
be read, REA cannot establish which file Claude
@@ -73,12 +264,17 @@ to rerun it.
A process that the MSIX app started, such as a terminal in Claude Desktop's
Code tab, sees the package's private files at their `%APPDATA%` paths even
though it reports no package identity. Setup compares one of those files at
both paths. When it finds that redirected view, the app is running, so setup
and uninstall refuse to change Claude Desktop's configuration; rerun them from
a terminal that Claude Desktop did not start. If the package has no private
file to compare, setup and uninstall cannot rule that out and also refuse; open
Claude Desktop once if it has never run, quit it fully, then rerun. Doctor
reads the same file from either kind of process.
both paths: first one from the package's private `Claude` folder, and when
that folder has none (some installations keep writing the real
`%APPDATA%\Claude`), one from the package's other private roaming folders,
which the package redirects in the same way. When it finds that redirected
view, the app is running, so setup and uninstall refuse to change Claude
Desktop's configuration; rerun them from a terminal that Claude Desktop did not
start. If the package keeps no private roaming file at all, setup and
uninstall cannot rule that out and also refuse. With Claude Desktop fully
quit, add REA's entry by hand to the file setup names, which is the one the
app reads, or remove it from both the private copy and the `%APPDATA%` file.
Doctor reads the same file from either kind of process.
The same process also writes everything under `%APPDATA%` and
`%LOCALAPPDATA%` into the package's private storage, where other apps never
@@ -131,14 +327,25 @@ and Claude Code reports that autocompact is thrashing. To avoid this:
`mcpServers.rea` entry in `~/.claude.json` (`$CLAUDE_CONFIG_DIR/.claude.json`
when that variable is set), then restart Claude Code. Setup keeps this
override.
- To remove entire tool families from the upfront catalog, run
`rea setup --client claude_code --tool-families <ids>` with the families the
workflow needs (see [the advertising scope contract](mcp-contracts.md#tool-family-advertising-scope)).
Out-of-scope tools are not advertised and their calls fail with the SDK's
disabled-tool error, so widen the selection when a task needs another
family. Setup writes the scope into the entry's `env` as
`REA_MCP_TOOL_FAMILIES`; rerun setup with a different selection to refresh
it, then restart Claude Code.
The sizes above come from REA's own `tools/list`. A live Claude Code session
against DeepSeek's API is unverified.
For a client whose stdio reader rejects the complete catalog frame, add
`"REA_MCP_OUTPUT_SCHEMA_PROFILE": "off"` to REA's `env` object and restart the
client. This omits only advertised output schemas; REA still validates complete
structured results against its canonical contracts. See [MCP output schema
For a client whose stdio reader rejects the complete catalog frame, set
`"REA_MCP_OUTPUT_SCHEMA_PROFILE": "compact"` to bound every advertised output
schema, or `"off"` to omit them, in REA's `env` object, then restart the client.
Both keep canonical result validation and complete structured results; `compact`
keeps a widened root shape and `off` advertises nothing. On catalogs large
enough that full input schemas alone approach the frame limit, also keep the
input profile at its `compact` default. See [MCP output schema
profiles](mcp-contracts.md#output-schemas).
## Kimi Code
@@ -154,8 +361,16 @@ startup; an explicit Ghidra startup timeout of 2700000 ms produces a 2820000 ms
tool timeout. The startup allowance follows an explicitly selected Ghidra or
Hopper provider, and otherwise uses the larger known Ghidra/Hopper deadline.
An explicit `REA_JEB_MCP_TIMEOUT_MS` deadline, shared by catalog discovery and
each tools/call, also contributes to that maximum. For `ida` or `auto` selection, a valid IDA registration's parsed
each tools/call, also contributes to that maximum. An explicit `REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS` is added to the Ghidra startup allowance, because a single-decompile call can import the target and then decompile up to that limit; setup forwards it into the registration. For `ida` or `auto` selection, a valid IDA registration's parsed
`timeoutMs` also contributes to that maximum; its default is 300000 ms.
The derived value is a single-decompile estimate, not a deadline for an
unbounded batch. For example, startup 330 seconds plus two 600-second
decompiles and 120 seconds of headroom needs an estimated 1650 seconds,
before any additional listing or output work. Select the client tool deadline
for the intended workload, split `batch_decompile`, use `decompile_all`
limit/resume, or reduce `trace_native_values``max_functions`. The trace can
decompile several functions serially; its depth and graph budgets do not
make the single-decompile allowance a whole-traversal deadline. Existing positive caller deadlines are preserved.
Setup forwards these explicitly selected REA deadlines and the input schema
profile into the registration; unrelated process values are omitted. An
explicit `REA_JADX_OPERATION_TIMEOUT_MS` is forwarded with the JADX
@@ -163,6 +378,21 @@ registration but does not contribute to the derived tool timeout; raise
`toolTimeoutMs` to cover a longer selected JADX deadline.
This headroom is a practical default, not a guarantee for every analysis
duration. Setup preserves positive timeout values already in the `rea` entry.
Setup also forwards explicitly selected input/output schema profiles,
`REA_MCP_MAX_RESPONSE_BYTES`, `REA_MCP_MAX_INPUT_ELEMENTS`, the Windows ChangeTime settle settings `REA_WINDOWS_CHANGE_TIME_DRIFT_MS`, `REA_WINDOWS_CHANGE_TIME_SETTLE_POLL_MS`, and `REA_WINDOWS_CHANGE_TIME_SETTLE_MAX_POLLS`, provider paths and supported Ghidra, JADX and Kuna
settings. It also forwards explicit executable and resource selections for ADB,
apktool, packet capture, GDB/Rizin/objdump, Cutter, WABT, Wakaru, firmware and
signal tools, browser inspection, and pwntools/pwndbg. This includes their
supported prlimit overrides, Darwin Swift compiler/SDK/module-cache selections,
and compile timeout, logging level and explicitly configured reference-secret
patterns. A provider being unavailable does not discard its selected paths;
doctor reports its readiness separately. Explicit selections override discovered
defaults, including when setup runs again with a changed selection. Unrelated
environment variables, process ownership tokens and one-shot target paths are not
copied. For an explicitly configured startup target, see the
REA resolves existing ancestor directories before checking physical containment,
including symlinks and junctions, even when the override directory does not exist.
An override beneath a symlinked default home therefore uses the default home's
sticky selection. If `~/.hermes/link` points outside the default home,
`HERMES_HOME=~/.hermes/link/new` instead uses that override root, whether or not
`new` exists. Previewed destinations retain the configured path spelling.
An unreadable path or dangling alias leaves the home unresolved; repair that path
before configuring Hermes. Inspect setup's previewed destinations before applying
changes.
An explicit `HERMES_HOME` pointing to `profiles/<name>` selects that named
profile directly, bypassing sticky selection. An invalid, missing or deleted
sticky profile is reported instead of writing into the default home; repair the
profile or run `hermes profile use default` before retrying. It also remains an
unresolved skill selection, so setup cannot report a successful empty installation.
REA follows `HERMES_HOME` whitespace trimming, environment-variable expansion,
and home expansion before resolving that profile. Setup previews the resolved
configuration and skill destinations; doctor and uninstall use those same paths.
@@ -302,10 +687,12 @@ installs its skill copy there.
## Factory Droid
For [Factory Droid](https://docs.factory.com/harness/mcp), setup writes
`mcpServers.rea` into the user-level `~/.factory/mcp.json`. Entries carry
`command`, `args`, and `env`. Droid discovers personal skills from
`~/.factory/skills` ([skills](https://docs.factory.com/harness/skills)), so
setup installs its skill copy there.
`mcpServers.rea` into the user-level `~/.factory/mcp.json` (the
`FACTORY_HOME_OVERRIDE` override replaces the home that contains `.factory`).
Entries carry `command`, `args`, and `env`. Droid discovers personal skills
from `~/.factory/skills` ([skills](https://docs.factory.com/harness/skills),
following `FACTORY_HOME_OVERRIDE` when set), so setup installs its skill copy
there.
## Trae
@@ -322,6 +709,18 @@ The user-level MCP paths follow the editor's VS Code layout. Trae's official
documentation describes the raw JSON editor without specifying those paths;
loading this registration in Trae remains unverified.
## CodeBuddy
For CodeBuddy, setup writes `mcpServers.rea` into the global
`~/.codebuddy/mcp.json` (a standard `mcpServers` JSON file at the user home
root on every platform). Entries carry `command`, `args`, and `env`; stdio is
the default transport. CodeBuddy discovers personal skills from
`~/.codebuddy/skills`, so setup installs its skill copy there.
The global MCP path is a plain JSON file; setup preserves every other server
and top-level key when it upserts `rea`. Loading this registration, the
bundled skill, and real REA tool calls through CodeBuddy are verified.
## WorkBuddy
For [WorkBuddy](https://www.workbuddy.cn/docs/workbuddy/From-Beginner-to-Expert-Guide/Function-Description/MCP-Guide), setup writes `mcpServers.rea` into the
@@ -353,14 +752,43 @@ the fallback file untouched. Existing REA environment overrides are preserved
when refreshing its command. A malformed or unreadable fallback file fails setup instead of
being silently suppressed; repair or remove it and rerun. Uninstall removes an
REA-owned `rea` entry from both the native table and the fallback, backing up
each changed file, so an emptied native table cannot load a leftover fallback
registration; other fallback servers, settings and a foreign `rea` entry are
kept. ZCode disables a
each changed file. If removing the last native server would activate a retained
fallback server, uninstall refuses before changing either file; review the
fallback configuration before retrying. Other fallback servers, settings and a
foreign `rea` entry are kept. ZCode disables a
server with `enable: false` on its entry; doctor reports such a registration
as stale, and rerunning setup removes the flag to repair it. Native configuration
uses strict JSON; setup refuses comments, trailing commas and a BOM rather than
writing a registration ZCode cannot load.
## FreeBuff
For FreeBuff, `rea setup --client freebuff` writes `mcpServers.rea` into the
discovery, and ownership behavior only. Automatic applicability requires the
FreeBuff profile directory even when the shared MCP file exists.
When uninstall cannot establish FreeBuff or another shared-storage reader from
product discovery, a malformed or unreadable shared MCP file is retained
unchanged and does not stop other REA removals, including `--purge-data`. When
FreeBuff or ZCode product applicability is established or unresolved, the same
malformed shared file stops uninstall before removals. See
[Diagnose, update, and remove](installation.md#diagnose-update-and-remove).
FreeBuff setup support requires `[email protected]` or later (including the
current npm `latest` release). Older versions reject
`rea setup --client freebuff` as an unsupported client. See the
[installation guide](installation.md#supported-agents) for client availability.
## DeepSeek Harness
DeepSeek Harness registers MCP clients through a Cordis YAML patch sequence
@@ -411,6 +839,103 @@ Harness integration, with live DeepSeek API compatibility remaining unverified.
See the [Harness MCP guide](https://github.com/deepseek-ai/deepseek-harness/blob/639ed015397290b3745d163aafe02ffee4aa3f84/packages/mcp/mcp-client/README.md)
for its native configuration contract.
<a id="llamacpp"></a>
## llama.cpp
Use REA from llama.cpp's local Web UI through `llama-server`'s native stdio
MCP support. REA supplies the analysis tools; llama.cpp runs your chosen model.
It is not a replacement for a native analysis provider such as Ghidra or Hopper.
Install REA in a persistent location so the configuration below can use a stable
entrypoint. For a project-local installation, run this in your chosen directory:
```bash
npm install --save-exact rea-agents
```
Or install it globally:
```bash
npm install --global rea-agents
```
See [package runners and global installation](installation.md#package-runners-and-global-installation)
for runtime requirements and installation options. Running `npx rea-agents setup`
alone does not create either installation; do not use its temporary package-cache path.
Use a llama.cpp build whose `llama-server --help` lists
`--mcp-servers-config`; older builds without that option cannot use this route.
The configuration below uses an already installed REA package, so tool discovery
does not depend on downloading an npm package during server startup.
and [renderer usage notes](https://github.com/earendil-works/pi/blob/b45e0ebcc5ae2fc42a6f2f23104d10e656e6bedb/packages/codemode/README.md#declarations-for-the-model).
<a id="llamacpp"></a>
## llama.cpp
The [manual stdio connection](client-configuration.md#llamacpp) was checked on
macOS arm64 with Node.js 24.18.0, llama-server b11715 (`9d1878a43`), and the
published `[email protected]` package. `GET /tools` exposed 207 REA tools without
enabling llama.cpp's built-in tools. With the source checkout at `9e372c6`, all
207 forwarded names, descriptions and input schemas matched a direct MCP client.
Calls through `POST /tools` returned session metadata and JavaScript analysis
Evidence for a fixture at a Unicode path. A missing target returned REA's input
error, and a subsequent session call still succeeded. The owned llama-server
process exited successfully after the checks. No native analysis engine or
Windows host was exercised.
A local Qwen3-1.7B Q8_0 run selected the JavaScript analysis tool and received its
Evidence, but its follow-up answer reached the output-token limit. Other small
Qwen3 attempts returned proposed calls as ordinary JSON text rather than tool
calls. These runs do not establish dependable model-driven investigations.
A request with the full catalog and task history exceeded a 16,384-token context;
select tools for the task instead of assuming that discovery implies model fit.
The checks exercised HTTP and stdio, not browser interaction with the Web UI.
## End-to-end, integration and golden evidence
The optional `verify:qwen-client`, `verify:pi-client`, and `verify:hermes-client`
@@ -48,6 +87,14 @@ skill setting; Hermes discovers it through the selected `HERMES_HOME`.
Set `REA_VERIFY_HERMES_STICKY_PROFILE=1` to exercise native Hermes selection of
a named sticky profile with `HERMES_HOME` still pointing to its root.
For Qwen on POSIX, set `REA_VERIFY_QWEN_REDUCED_PATH=1` and select its JavaScript
entry point with `REA_VERIFY_QWEN_COMMAND` to test a restricted client environment.
Setup retains the caller's `PATH`; the native Qwen `chat` or `call` run then uses
an isolated `PATH` containing only Bash for the shell-tool fixture. The lane
checks that `node`, `npm` and `npx` cannot be found and uses the generated REA
registration unchanged. Its receipt records `reducedPath`, `clientPath` and
`registrationCommand`; the option alone does not establish a passing native run.
The Qwen lane also supports native Windows (verified with Qwen Code 0.25.1 and
Node 24.19.0 for both `chat` and `call`). With Node 24.18.0, the same `call`
journey reached full Evidence readback but the client aborted during exit with
@@ -149,6 +196,12 @@ REA_VERIFY_COPILOT_MODEL=gpt-4.1 REA_VERIFY_COPILOT_SCHEMA_PROFILE=compact npm r
REA_VERIFY_COPILOT_MODEL=gpt-4.1 REA_VERIFY_COPILOT_SCHEMA_PROFILE=compact npm run verify:copilot-client -- call
```
The verifier explicitly writes either selected profile into its disposable
registration and compares the input schemas forwarded by Copilot with that
profile's advertised schemas. A run must not infer `full` from an unset setting:
REA's runtime default is now `compact`. The recorded client results above remain
scoped to their executed revisions; this verifier correction is not a new client run.
Both native workflows passed with the complete tool inventory. The verifier selects the
profile only in its disposable Copilot registration and records it in the
receipt. Compact advertisements can reduce nested validation structure; the
@@ -452,6 +505,11 @@ real local Codex CLI with:
npm run verify:agent
```
The verifier's scenarios, transcript scoring, fixture rubrics, and report gates
are development verification code under `tests/evaluation/model-evals/`. The
source-owned evaluator runs alongside the compiled REA tools; it is excluded
from the shipped runtime package.
The lane runs scenarios sequentially in standalone Codex (`--no-daemon`) with a
disposable HOME and CODEX_HOME under `~/.cache/rea-agent-evaluations/`. Set
`REA_AGENT_EVAL_ROOT` to choose another parent directory outside the OS temporary
@@ -475,10 +533,16 @@ is removed after confirmed process cleanup even with
`REA_AGENT_EVAL_KEEP_FIXTURES=true`. No user configuration,
rules, plugins or history is copied; shell snapshots and additional agents are
| [Domain definitions](https://github.com/morluto/rea/blob/main/src/domain/analysisSettingDefinitions.ts) and [types](https://github.com/morluto/rea/blob/main/src/domain/analysisSettings.ts) | Exact setting IDs, value types, shared bounds/defaults, units, provider/operation associations, application points, and structured limit metadata. Numeric constants stay with their domain owner. |
| [Launch parser](https://github.com/morluto/rea/blob/main/src/config/analysisSettings.ts) | Parse selected launch values once, preserving an invalid value as a typed configuration failure rather than substituting a default. |
| [AnalysisSettings](https://github.com/morluto/rea/blob/main/src/application/AnalysisSettings.ts) | Own one runtime's launch baseline, immutable override revisions, atomic updates, inspection, and accepted-operation snapshots. It does not own provider lifecycle or cleanup. |
| [Composition](https://github.com/morluto/rea/blob/main/src/main/eagerRuntime.ts) and [typed configuration views](https://github.com/morluto/rea/blob/main/src/config/runtimeAnalysisSettings.ts) | Inject the same settings reader into providers and workflows; translate overrides into the configuration each consumer understands. |
| Provider or resource owner | Resolve the effective budget, enforce it at the applicable boundary, and attach the actual value and known source to failures. |
| [MCP registration boundary](https://github.com/morluto/rea/blob/main/src/server/EvidenceMcpServerCore.ts) | Enter the operation's settings scope before invoking its handler. |
| [Settings contracts](https://github.com/morluto/rea/blob/main/src/contracts/analysisSettingsContracts.ts) and [tool registration](https://github.com/morluto/rea/blob/main/src/server/registerAnalysisSettingsTools.ts) | Validate named requests and deliver inspection/update results without requiring an open target or healthy analysis provider. |
The normal path is: composition creates the store, an MCP handler captures its
current revision, and providers read that snapshot when resolving budgets.
If an owner rejects work, it reports the effective limit through the shared
error projection. A subsequent settings update publishes a new revision for
later handlers. It does not reach into the failed operation or retry it.
### Accepted-operation snapshots
`AnalysisSettings.run()` installs the current immutable snapshot in
`AsyncLocalStorage`. Its `read` function returns that snapshot inside the
operation and the latest revision outside it. Providers depend on the reader
contract rather than on the mutable store or an ambient environment lookup.
Acceptance here is entry into the registered handler's settings scope, not
arrival of bytes at the MCP transport. Work queued or awaited within that scope
keeps its snapshot. Deferred server initialization must still dispatch through
@@ -53,7 +53,7 @@ Every collection supports `fields`, `offset` and `limit`; its stable row ID is
always included. Selection changes delivery, not source inspection. Pages keep
source coverage and parent Evidence links. Page exhaustion does not establish
absence in a partial source. Complete Evidence remains accessible through
`get_evidence` and Evidence bundle export.
`get_evidence_bundle` and Evidence bundle export.
The summary's `probe_issues` counts files with reported issues and their retained
issue entries. It previews at most two such files, including stable `file_id`,
@@ -97,17 +97,27 @@ same bundle. Export writes only the caller-selected output file.
| Unity Mono | A managed assembly below `*_Data/Managed` or `Contents/Resources/Data/Managed` has an exact UnityEngine assembly reference. |
| Unity IL2CPP | A recognized IL2CPP metadata magic (with the raw version when complete) coexists with a conventionally named native `GameAssembly.dll`, `GameAssembly.so`, `GameAssembly.dylib` or `libil2cpp.so` image. Build correspondence remains unknown. |
| Godot | A `GDPC` header at file start or a validated executable trailer offset. |
| GameMaker | A bounded `FORM` container starts with a `GEN8` chunk. |
| GameMaker | A bounded `FORM` container starts with a `GEN8` chunk. The scan probes conventional `data.win`, `game.unx`, and `game.ios` data files for that header. |
| Unreal | `Content/Paks` path conventions or an IoStore TOC signature; arbitrary `.pak` filenames are insufficient. |
| Electron | A `resources/app.asar` or `resources/default_app.asar` path convention; archive validity is unverified. |
When the four-byte metadata magic is present but version bytes are incomplete,
the fact retains `raw_version: null`; it does not offer an IL2CPP metadata
inspection probe. A complete magic/version prefix with supported raw version 29,
31 or 39 offers an
`inspect_il2cpp_metadata` follow-up at its admitted file path, including when no
native library was found. This probe does not establish full metadata validity
or correspondence to a native binary. See [IL2CPP metadata](il2cpp-metadata.md).
inspection probe. A complete magic/version prefix with supported raw version
24, 27, 29, 31 or 39 offers an`inspect_il2cpp_metadata` follow-up at its
admitted file path, including when no native library was found. This probe does
not establish full metadata validity or correspondence to a native binary;
version 24 inspection still applies only to supported header layouts. See
[IL2CPP metadata](il2cpp-metadata.md).
In a managed directory with a Unity Mono witness, `Assembly-CSharp.dll` and
`Assembly-CSharp-firstpass.dll` get an `inspect_managed_members` probe listed
first; project-named asmdef assemblies keep only the ordinary managed probes.
The same probe is offered for those names in `Managed/` of a
`<Product>_BackUpThisFolder_ButDontShipItWithYourGame` folder (or when that
folder is the root); whether its IL matches the shipped IL2CPP code is unknown.
Point the scan at the game folder: a root that is the `<Product>_Data` or
`Managed` folder itself has no Mono witness path and gets no member probe.
Candidates are derived or inferred from these facts and do not prove loading or
execution. BepInEx, MelonLoader, Easy Anti-Cheat and BattlEye labels identify
@@ -134,11 +144,13 @@ observations, not an atomic directory snapshot. Unread bytes remain unknown.
## Resource bounds and reuse
Defaults are 20,000 examined entries and 8 MiB of total probe reads. Callers may
select up to 100,000 entries and 64 MiB. A separate 32 MiB conservative
representation budget bounds retained paths and facts before growth. Limits
and failed probes produce partial coverage. Traversal and reads are serial.
Each reader admits one inspection at a time; overlapping requests receive a
retryable capacity refusal without cancelling the admitted caller.
select up to 100,000 entries and 64 MiB. A separate 32 MiB representation
budget bounds retained rows, paths and facts before growth. Each retained path
is charged at the larger of its UTF-16 heap size and its escaped JSON size, so
typical macOS bundle paths fit about 20,000 rows, and control-character paths
fewer. Limits and failed probes produce partial coverage. Traversal and reads
are serial. Each reader admits one inspection at a time; overlapping requests
receive a retryable capacity refusal without cancelling the admitted caller.
Unity reference decoding stops after a witness in each `Data/Managed` directory;
other assemblies' references remain unexamined, while their headers are still
and [compression producer](https://github.com/dotnet/runtime/blob/v8.0.22/src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs).
Loaded 100 of 6247 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.