Commit Graph
6613 Commits
Author SHA1 Message Date
Nianze Wu 93ce13e76f fix(desktop): read SQLite state despite metadata name collisions
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)
2026-11-06 16:21:02 +00:00
gensokyo-devandgensokyo-dev 98c589e420 perf(tests): reuse bounded CLI workers for native matching errors (#9163)
Co-authored-by: gensokyo-dev <[email protected]>
2026-11-07 00:08:27 +08:00
gensokyo-dev bacc1e3846 perf(mcp)!: defer retained views in progressive catalogs (#9151)
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.
2026-11-06 22:44:56 +08:00
ShuaixinHuang cfa7ebb020 fix(environment): ignore unsafe paging bounds (#9145) 2026-11-06 22:18:25 +08:00
ShuaixinHuang 772d6098f0 fix(environment): validate JSON pointer array indexes (#9147) 2026-11-06 22:17:31 +08:00
Beqa fbe7532fab fix(managed): surface capacity limits in member comparison
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)
2026-11-06 13:23:13 +00:00
gensokyo-dev 1480a7c72e docs(firmware): qualify component comparison workflows (#9143) 2026-11-06 21:15:58 +08:00
RRFZ 55ee36809d fix(javascript): apply package type scope to application sources (#7883)
* 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.
2026-11-06 20:49:41 +08:00
2SinceJulyand2SinceJuly 6c08320654 feat(process): add a non-executing dry run to capture_process_scenario (#8469)
* 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]>
2026-11-06 20:49:20 +08:00
Entrpi 81f9f71f6d feat(rizin): select the decoder and map address for one-shot commands (#8991)
* 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.
2026-11-06 20:48:59 +08:00
Harsh Asalkar 7e9cf83fca feat(mcp): guide agents from argument-name mistakes in procedure and search tools (#2146) (#8649)
* 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.
2026-11-06 20:48:37 +08:00
ShuaixinHuang ee2f5b965a fix(desktop): bound exported task array-index conversion (#9139) 2026-11-06 20:48:17 +08:00
gensokyo-dev 5303199163 feat(api-contract): inspect local GraphQL SDL and field references (#9140)
* feat(api-contract): inspect local GraphQL SDL and field references

* fix(api-contract): retain caller metadata and bound selection work
2026-11-06 20:16:31 +08:00
Saim Shafique 14d2c51d85 fix(managed): decode only selected types in member inspection (#9044)
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)
2026-11-06 12:05:09 +00:00
Christopher Pruijsen abdbaa544a fix(rizin): report the output cap instead of a provider signal (#9033)
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)
2026-11-06 11:20:43 +00:00
ShuaixinHuang f3d6f45315 fix(desktop): preserve exported SQLite text encoding (#9123)
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)
2026-11-06 10:50:12 +00:00
gensokyo-devandgensokyo-dev fe27f96720 test(ci): restore client registration and doctor checks (#9128)
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]>
2026-11-06 18:15:25 +08:00
xin zhuang aefd903adb fix(desktop): preserve nullable SQLite row identity (#8999)
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)
2026-11-06 09:54:08 +00:00
gensokyo-dev 985b9eae96 feat(browser): import saved Playwright v10 trace evidence (#9127)
* feat(browser): import saved Playwright v10 trace evidence

* fix(browser): preserve original Playwright ZIP member names
2026-11-06 17:52:09 +08:00
Rishikesh Gowda K K 9044b6fb36 fix(kuna): mark project-dependent operations unavailable on export failure (#8748)
* fix(kuna): mark project-dependent operations unavailable on export failure

* fix(providers): preserve sibling operations in capability error copies

* fix(kuna): propagate sibling unavailability on export fallback

* fix(binary): preserve observed sibling recovery instructions across fallback observations

* fix(binary): build sibling unavailability from structured reasons without rewriting display text

* test(kuna): synchronize fallback test on settled export workspace retirement

* test(kuna): release and join export synchronization barrier across exit paths

* test(kuna): synchronize fallback test on observed export process exit

* test(kuna): gate fallback completion on settled export cache build

* test(kuna): preserve build promise chain and restore spy after teardown
2026-11-06 17:22:02 +08:00
996977edf2 feat(cli): expose caller-selected reference-source path exclusions (#2630)
* 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]>
2026-11-06 17:21:28 +08:00
김준호andmorluto f1bfa1cba2 fix(cli): honor the selected format for unknown mcp subcommands (#7802)
* 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]>
2026-11-06 17:20:56 +08:00
gensokyo-devandgensokyo-dev afc9cfe9ff perf(tests): reuse bounded CLI workers in mobile archive checks (#9116)
Co-authored-by: gensokyo-dev <[email protected]>
2026-11-06 17:09:46 +08:00
gensokyo-dev 39827ec717 docs(mcp): document verified FastMCP catalog recovery (#9113)
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)
2026-11-06 08:22:26 +00:00
gensokyo-dev 3e0599a949 fix(javascript): preserve loader identity across terminal origins (#9107)
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)
2026-11-06 07:59:03 +00:00
Hasan ZayniandClaude Sonnet 5.5 1cc979c104 feat(ghidra): admit DOS MZ targets on Windows P0 (#7144)
* 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]>
2026-11-06 15:39:03 +08:00
Xiangfang Chen fb841a9596 feat(setup): support CodeBuddy as a setup client (#2492)
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.
2026-11-06 15:38:47 +08:00
Jay-Alexander Elliot 1902fcb13d fix(cli): load Evidence record pairs from JSON files (#5129)
* fix(cli): read explicit Evidence record pairs from JSON files

* test(cli): isolate record-pair file regression

* style(cli): format record-pair file regression

* test(cli): reuse comparison fixture for record-pair file input

* style(cli): format reused record-pair test invocation

* test(cli): keep paired-input coverage within lint function limit
2026-11-06 15:38:30 +08:00
吴佳浩Alben fe9797b487 feat(python): qualify a recognized 3.14/3.15 pyc header (#8927)
* 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.
2026-11-06 15:36:22 +08:00
吴佳浩Alben 99bcd3e4e0 fix(javascript): report empty Electron coverage instead of silent zeroes (#8332)
* 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.
2026-11-06 15:15:43 +08:00
Canandeco-null 85775b635b test(ghidra): verify installation metadata budget through doctor (#6690)
* fix(ghidra): name installation metadata limit

* test(reference): return byte-preserving entry names

* fix(javascript): coalesce projected source facts

* test: repair isolation and format fixtures

* fix(evidence): retain rebound snapshot proof

Carry immutable member proof across caller and target path rebinding so low-headroom error projection reuses completed Evidence without cloning its payload.

---------

Co-authored-by: eco-null <[email protected]>
2026-11-06 15:15:23 +08:00
Canandeco-null 7ad3d7928b test(search): cover Unicode literal syntax and astral case folding (#6685)
* fix(search): fold Unicode literal matches

* test(ida): require Unicode literal case folding

* test(reference): return byte-preserving entry names

* fix(javascript): coalesce projected source facts

* test: repair isolation and format fixtures

* fix(evidence): retain rebound snapshot proof

Carry immutable member proof across caller and target path rebinding so low-headroom error projection reuses completed Evidence without cloning its payload.

---------

Co-authored-by: eco-null <[email protected]>
2026-11-06 15:15:03 +08:00
Canandeco-null 1d4e9f31c3 test(cli): cover production doctor output option ordering (#6683)
* fix(cli): retain production doctor output routing

* test(reference): return byte-preserving entry names

* fix(javascript): coalesce projected source facts

* test: repair isolation and format fixtures

* fix(evidence): retain rebound snapshot proof

Carry immutable member proof across caller and target path rebinding so low-headroom error projection reuses completed Evidence without cloning its payload.

---------

Co-authored-by: eco-null <[email protected]>
2026-11-06 15:14:43 +08:00
Jay-Alexander Elliot dc9f329764 test(ghidra): verify PSP VFPU prefix and vector decoding (#8614)
* test(psp): add source-owned VFPU prefix and vadd sequence

* test(psp): assert VFPU prefix and consumer decode facts

* test(psp): verify VFPU prefixes and consumers through public Ghidra MCP

* docs(psp): delimit VFPU prefix/consumer conformance coverage

* test(psp): preserve unknown when Ghidra omits high-pcode data

* style(psp): apply pinned oxfmt output to verify-real-ghidra-mips.mjs

* style(psp): apply pinned oxfmt output to psp-fixture.mjs
2026-11-06 15:14:24 +08:00
吴佳浩Alben 172f248884 fix(javascript): emit no fs acquisition for a rejected literal mode (#8624)
* 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.
2026-11-06 15:14:03 +08:00
gensokyo-dev 91d73a113e feat(contracts): inventory offline OpenAPI operation declarations (#9104)
* feat(contracts): inventory offline OpenAPI operation declarations

* docs(contracts): link fixture provenance outside the docs site

* fix(contracts): describe the shared API document byte allowance

* fix(api-contract): preserve safe diagnostics and completed source facts
2026-11-06 15:13:51 +08:00
RRFZ 42aaef8bdb fix(cli): require absolute ADB device paths for file pull and push (#9048)
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.
2026-11-06 15:13:43 +08:00
Luis Araya 3959b68d91 test(rust): assert non-retryable compiler-hash capacity in CLI and MCP (#5504) (#7520) 2026-11-06 15:13:24 +08:00
吴佳浩Alben 1c0a02d4a4 fix(evidence): bound malformed file field diagnostics (#8557)
* 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.
2026-11-06 14:53:25 +08:00
e69f27fba0 fix(web-capture): bound the mitmproxy headline facet index (#2876)
* 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]>
2026-11-06 14:53:14 +08:00
gongfppandgongfpp 08bc52104a fix(cli): preserve Unicode edge whitespace in Markdown tables (#8915)
* fix(cli): preserve Unicode edge whitespace in Markdown tables

* fix(cli): bound Markdown whitespace encoding to linear time

---------

Co-authored-by: gongfpp <[email protected]>
2026-11-06 14:53:03 +08:00
RRFZ 6430f395a8 fix(cli): reject non-decimal pyc and BEAM limit options (#9060)
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.
2026-11-06 14:52:53 +08:00
Christopher Pruijsen c9797c5dd5 fix(native): keep inspect_macho usable when dyld_info refuses the slice (#9034)
* 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
2026-11-06 14:52:42 +08:00
hhangandhhang d77442228d fix(desktop): align exported XML formatting whitespace (#9020)
* fix(desktop): align exported XML formatting whitespace

* test(process): align symlink comparison snapshots

* test(boundary): align inherited CLI and MCP fixtures

---------

Co-authored-by: hhang <[email protected]>
2026-11-06 14:52:32 +08:00
Hasan ZayniandClaude Sonnet 5.5 6571be631a fix(decompile): keep the output failure streak across external functions (#7924)
* 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]>
2026-11-06 14:51:47 +08:00
ShuaixinHuang f624c3b8e3 test(desktop): make Python output fixtures portable (#9066)
* 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
2026-11-06 14:41:44 +08:00
ShuaixinHuang d3a4fc87cd fix(cli): stop completion after the option terminator (#9064)
* 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
2026-11-06 14:37:29 +08:00
ShuaixinHuang 6993e4ac73 fix(managed): explain single-type member capacity exhaustion (#9065)
* test(process): align symlink fixture with filesystem checkpoints

* fix(managed): explain single-type member capacity exhaustion

* 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(managed): count selected types in capacity guidance
2026-11-06 14:37:16 +08:00
RRFZ a74ce4512c fix(cli): reject non-decimal desktop application limits (#9061)
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.
2026-11-06 14:34:07 +08:00
RRFZ 415ddb74c0 fix(cli): publish the GPU inventory digest format in the CLI schema (#9062)
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.
2026-11-06 14:33:56 +08:00