Commit Graph
37 Commits
Author SHA1 Message Date
87edf14940 fix(browser): support Termux Android scenarios (#1655)
* fix(browser): support Termux Android scenarios

* fix(build): lock Termux artifact generation

* ci(android): add manual Termux browser smoke verification

---------

Co-authored-by: tyloryang <[邮箱]>
Co-authored-by: morluto <[email protected]>
2026-10-11 08:10:06 +08:00
96fd98cce0 feat(apktool): decode Android resource tables through a caller-supplied launcher (#1556)
* feat(apktool): decode Android resource tables through a caller-supplied launcher

Add an Apktool family with two operations over the caller's apktool
launcher (REA_APKTOOL_COMMAND, absolute, or apktool on PATH):

- inspect_apktool_client: resolved launcher, selection source, and
  version; decodes nothing.
- decode_android_resources: apktool d --no-src in a provider-owned
  workspace that is removed before the result returns. Projects the
  decoded AndroidManifest.xml (bounded at 512 KiB), apktool.yml version
  and SDK facts exactly as printed, string resources verbatim (default or
  one locale, bounded at 2000 entries, nested-markup entries counted as
  unparsed), available string locales, and the decoded file count with a
  hard cap. The target APK is digested with SHA-256 before decoding so
  Evidence binds the observation to the exact input.

Smali production is skipped by design: Java-source recovery belongs to
the JADX family. Workspace cleanup happens before results return; an
unremovable workspace is named in the limitations instead of abandoned.

Verified against apktool 2.7.0-dirty (Debian packaging) on Linux with
OpenJDK 25 against a signed aapt2-built probe APK with two resource
locales: launcher version, on-disk digest agreement, metadata projection,
manifest package agreement, and both string tables (default and de).
Parser goldens come from the same decode. READMEs carry the new
'Android resources' row in every supported language.

* fix(apktool): own snapshots and bound decoded resource reads

* docs(apktool): describe owned resources and bounded projection

---------

Co-authored-by: REA Contributor <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-10 23:04:12 +08:00
749e58772a feat(jeb): add JEB engine provider over its MCP interface (#1536)
* feat(jeb): add JEB engine provider over its MCP interface

Add a bring-your-own JEB provider that serves project, unit, and
decompilation inspection from a caller-started JEB client speaking MCP
(streamable HTTP, selected with REA_JEB_MCP_URL). Expose four provider-neutral
operations through shared CLI/MCP workflows: inspect_jeb_client,
open_jeb_project, list_jeb_units, and decompile_jeb_item, each returning
Evidence with engine identity, endpoint, and preserved refusal reasons.

The adapter keeps JEB's envelope semantics: success fields are parsed
format-aware at the boundary, message-only refusals become unsupported-target
errors with the engine's own reason, transport failures become availability
failures with recovery guidance, and unit pagination derives coverage from the
engine's 100-item page cap. Explicit unknowns (implicitly selected unit,
omitted filters) are reported instead of guessed.

Verified against a real JEB 5.48.0 licensed build serving jeb-mcp-server 1.3.0
headless: client inspection, unit listing, and method decompilation were
exercised through the CLI, MCP boundary, and scripts/verify-jeb.mjs.
JEB 5.48.0 headless instances do not advertise open_project; the refusal is
preserved and documented.

* fix(jeb): verify against a real signed APK and fix CLI option surface

Verify the provider against a locally built, v1/v2/v3-signed probe APK
(binary manifest, classes.dex with a launcher activity) on JEB 5.48.0: the
full unit tree (apk, manifest, three certificate units, dex bytecode,
decompiler), wildcard-filtered and paginated unit listing with derived
coverage, parent-restricted listing, and launcher-activity method
decompilation through both CLI and MCP, including dexdec's inline
constant folding of the probe's obfuscated tag.

Move list-jeb-units filter/pagination and decompile-jeb-item unit selection
from positional arguments to options; the MCP schemas are unchanged.

* fix(jeb): preserve engine refusals through the real MCP boundary

Testing against the published Signal 8.30.3 universal APK exposed a boundary
bug the scripted provider tests missed: decodeJebToolResult raised a protocol
error for isError results before the provider could map JEB's
{success:false, message} envelope, so real engine refusals surfaced as
unreadable output instead of the engine's own reason. Decode refusal
envelopes like any structured result and let the provider preserve the
reason; cover the decoder directly, including contentless and unparseable
results.

Signal verification: full 109 MB R8/Kotlin/Compose APK with four signature
schemes and native arm64 ELF units parsed; MainActivity.onCreate and R8
synthetic lambdas decompiled through CLI and MCP; paginated, filtered, and
parent-restricted unit listing; refusal path confirmed on the live engine.

* docs(jeb): record Flutter Gallery verification on the real-engine lane

Verify the provider against the official Flutter Gallery 2.9.2 release APK
(112 MB): per-ABI libapp.so Dart AOT snapshot images analyzed as native code
with 75k methods on arm64, snapshot internals and wrapper methods decompiled
through both CLI and MCP with explicit unit selection, decoded manifest, and
filtered native-library unit discovery. Note that JEB 5.48.0 headless does
not expose the dedicated Dart snapshot processor or run_script; both remain
GUI-only engine surfaces.

* docs(jeb): translate the README JEB row for every supported language

The JEB row was inserted in English across all translated READMEs; each now
carries its own label, capability summary, requirements, and guide link
following that language's Android-row conventions.

* fix(jeb): enforce local endpoints and preserve cancellation

* fix(jeb): refuse redirects across the local MCP boundary

---------

Co-authored-by: REA Contributor <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-10 23:03:27 +08:00
fef6f73064 feat(adb): complete ADB device support for Android reverse engineering (#1552)
* feat(adb): acquire installed APK sets from connected Android devices

Add a read-only ADB device-acquisition family over the caller's adb binary:
inspect_adb_client, list_adb_devices, inspect_adb_device, list_adb_packages,
and pull_adb_package, exposed through the CLI and MCP with shared admission,
Evidence provenance (binary, version, serial, transport), and fixed argv
vectors that never reach a shell.

pull_adb_package acquires the complete split set reported by pm path into a
package-named directory that must not exist, digesting every file with
SHA-256 and keeping per-file failure reasons and partial coverage. Device
kind is reported as an inference with its basis; getprop whitelisting keeps
build identity bounded. REA installs nothing and never manages devices or
emulators; the first device query may start the caller's local adb server,
recorded when observed.

Verified against adb 34.0.5 on Linux with an Android 14 (API 34) x86_64
emulator: full 224-package inventory, a real two-APK split set pulled with
byte-exact digests, and a 73.9 MB system-package pull. npm run verify:adb
records the lane; parser goldens come from the same device. READMEs carry
the new row in every supported language.

* feat(adb): complete the adb surface with observation, transfer, and lifecycle tools

Extend the ADB family from five to twenty-four operations, all through
fixed adb argument vectors with no shell exposure:

Observation: read_adb_logcat (bounded dump, count/buffer/pid), inspect_adb_package
(dumpsys package projection), list_adb_processes (ps -A), list_adb_directory
(ls -la with kinds and symlink targets), list_adb_features (hex GL versions),
list_adb_services (AIDL slash-suffixed names), inspect_adb_display (wm size and
density), inspect_adb_window (focus declarations), read_adb_setting (device-side
nulls reported as such), capture_adb_screen (exec-out screencap with PNG
dimensions), collect_adb_bugreport (digested device archive).

Transfer and search: pull_adb_file and push_adb_file (device-side sha256sum
verification, explicit overwrite authority), resolve_adb_packages (substring
package search with exact-match flag — the find step of an easy get).

Lifecycle for dynamic analysis, each declaring mutatesTarget:
install_adb_package and uninstall_adb_package preserving the device's
Success/Failure verdict, start_adb_app (resolve-activity then am start -W),
start_adb_activity (typed extras as --es/--ez/--ei/--el/--ef), and stop_adb_app
(am force-stop).

Verified end-to-end against adb 34.0.5 on Linux with an Android 14 (API 34)
x86_64 emulator: 287 services, 92 features, split-set pulls with byte-exact
digests, verified push/pull roundtrip, sized screen capture, and the full
install → resolve → start → observe-in-ps → force-stop → uninstall lifecycle.
Fixed a v-mode regex incompatibility in the settings key pattern and added
argument descriptions required by the CLI catalog audit.

* fix(adb): quote remote shell operands and protect captured output

* test(adb): cover bugreport destinations containing spaces

---------

Co-authored-by: REA Contributor <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-10 23:01:24 +08:00
morluto a0784c0d2c test(process): retire native caches at worker teardown 2026-10-09 04:12:49 +08:00
Ysopwnable 7af7bc88ce feat(evm): inspect offline bytecode interfaces through EVMole (#1051)
* feat(evm): inspect offline bytecode interfaces through evmole

* fix(evm): distinguish unsupported bytecode targets

* style(evm): format verifier string-prefix checks
2026-10-08 15:32:05 +08:00
ysopwnable 021ba690b0 refactor: give Inspector its own capability directory (#869) 2026-10-07 13:53:25 +08:00
N0zoM1z0 166dcd21d0 feat(javascript): recover readable modules with Wakaru (#848)
* feat(javascript): recover readable modules with Wakaru

Integrate pinned upstream source behind a typed optional recovery port,
preserve source identity and producer provenance, and publish verified
artifacts for existing JavaScript analysis through CLI and MCP.

Add focused real-tool CI with genuine bundler fixtures and bounded
resource, cancellation, cleanup, schema and packaged-artifact checks.

Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)

* fix(javascript): align recovery catalog and browser verification

Share canonical Wakaru provider identity, describe CLI mode options, and
format the new workflow test. Disconnect the real page transport after
its observation readiness progress instead of a fixed delay.

Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)

* refactor(javascript): consume the investigation evidence writer

Verify that the SDK recovery handler records complete Evidence through the newly shared investigation owner.

Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)

* chore(test): account for isolated recovery fixture compilers

* fix(javascript): validate recovery paths before acquiring the engine

Return field-specific invalid_format diagnostics for relative paths and verify compiled CLI/MCP parity even when the optional engine is unavailable.

Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-07 12:46:13 +08:00
N0zoM1z0 8238232485 feat(ida): add read-only GUI and headless MCP providers (#754) 2026-10-07 01:47:05 +08:00
N0zoM1z0 5a7c769692 feat(firmware): integrate Binwalk and Unblob analysis
* feat(firmware): integrate Binwalk and Unblob analysis

* fix(firmware): verify published files and namespace source references

* test(firmware): isolate extractor dependency failure from process cleanup

* fix(firmware): retain actionable report and cleanup diagnostics

* test: assert retained batch output failure diagnostics
2026-10-06 14:16:04 +00:00
N0zoM1z0 26881da76a feat(ghidra): enable Windows x64 read-only analysis (#699)
* feat(ghidra): enable Windows x64 read-only analysis

* test(ghidra): exercise native Windows runtime boundaries

* test(ghidra): verify current Windows inventory and isolate ACL observer

* fix(ghidra): align Windows admission and address P0 review
2026-10-06 16:53:33 +08:00
morluto 9b392bb2f8 chore(deps): finish toolchain upgrades and drop dead weight (#644)
* chore(deps): finish toolchain upgrades and drop dead weight

pino 9 to 10, @types/node 24 to 26, oxlint 1.73 to 1.87, knip 6.26
to 6.39, turbo 2.10 to 2.11. Narrow engines to supported majors
(22/24/26; vitest 5 refuses Node 25). Scope no-var-requires off for
Electron CJS hooks and fixtures.

Remove TypeDoc: incompatible with TypeScript 7 at install and
runtime, used by nothing in any gate. Drop the dep, docs:api
workflow, typedoc.json, CI artifact step, and related docs.

Replace graphology with a local call-graph adjacency plus BFS: the
only consumer is call-path search, and graphology-types ships no
types entry for TS7 NodeNext resolution. Delete both graphology
dependencies.

Remove dead re-exports newly flagged by knip 6.39.

* chore: drop stale typedoc and replay references

Remove the docs:api release-checklist step, the orphaned docs/api
ignore entry, and the nonexistent replay glob from the adapters
project.
2026-10-06 06:02:02 +08:00
morluto d19686a2a5 fix(test): improve capture reliability and runner cleanup (#609)
* test: make capture fixtures readiness-aware and strengthen assertions

* fix(test): escalate child termination and report cleanup failures

* perf(test): use runner concurrency and lock shared build outputs
2026-10-06 01:32:46 +08:00
morluto 7ee3f236a9 test: replace implementation lock-in with behavioural assertions (#498)
* fix(process): normalize reactive rendered-frame timestamps

A reactive `resize` action stamped `rendered_frames[].at_ms` with the raw
elapsed time while every other rendered frame used
`normalizeProcessElapsedTime`. Under the default 10ms time bucket the next
frame buckets down and appears to move backwards, so `parseProcessCapture`
rejected the entire capture with "rendered_frames: timestamps must be
ordered". No test combined a reactive resize with time bucketing, so the
path was never exercised.

Thread the scenario's bucket into the reactive effect host and normalize the
renderer call, matching what the scheduled-event resize path already did.
`dispatched_at_ms` and `captured_at_ms` stay raw on both paths, so this
changes only the one inconsistent clock.

Also replace the wall-clock-scheduled interactive PTY scenario with
terminal_text triggers. A fixed `at_ms` could fire before the child wrote
its prompt under host contention, dropping the input and the resize echo.
That rewrite is what surfaced this bug, and it now passes deterministically.

Replace the 11 `if (!capability.available) return;` guards across the PTY
suite with a shared it.skipIf gate. The old form reported a green pass with
zero assertions on any host lacking native PTY authority; skips are now
named and reported.

Verified by reverting only the normalization, which reproduces the
validation failure immediately.

* fix(install): add recovery guidance to version-mismatch failure

Every installer failure except one told the user what to do next. The
version-mismatch failure reported only that the installed version did not
match, with no remedy, which contradicts the requirement to keep
diagnostics actionable for mismatch locations.

Add a remedy in the same style as the neighbouring failures and drop the
exemption the test carried for it, so all ten installer failures now assert
they carry a recovery action.

* ci: repair stale curated Windows lane and unblind the duplication gate

The `windows-curated` job listed fifteen test files and nine of them no
longer existed, including six `processOwnership*` files moved to
`src/process/` in 4ecc7a72. Vitest silently drops unmatched paths, so the
job ran six files and reported success. Repoint the paths at their current
locations and add a test that fails when any listed path is renamed or
removed, so the lane cannot silently lose coverage again.

The duplication gate ignored `**/*.test.ts`, so it reported zero clones
across the test tree while running on every PR. Include test files, remove
the two largest duplicated clusters, and ratchet the threshold to the
measured result so the gate can no longer pass by not looking.

Run local tests with two workers instead of one. A single worker made a
full local run cost ~140s of wall time at ~100% CPU on a 10-core host,
which was the largest dev-cycle cost in the repository. Local now matches
the concurrency CI already proves green, and PTY scenarios moved to a
serial project because they contend for host terminal resources. Full local
suite: 140s to ~89s.

Regenerate the managed conformance manifest. Commit 4ecc7a72 edited
SKILL.md without refreshing the digest, so `npm run check:pr` has been
failing on main since that commit.

* test: replace implementation lock-in with behavioural assertions

Audit the suite for tests that cost maintenance without catching defects,
then remove the lock-in rather than the coverage.

Replace assertions that froze incidental detail:

- toolContracts.test.ts pinned group sizes and 50 lines of literal tool
  names, so any additive catalog change required a test edit. Replace with
  four structural invariants: global name uniqueness, group containment in
  the published inventory, group disjointness, and non-empty groups. Both
  invariants are mutation-checked by orphaning and duplicating a tool.
- Six tests asserted only that a schema accepts its own canonical fixture.
  Replace five with real rejections and two with property tests covering
  the invariants those tests were gesturing at: `isDbSaveComplete` is
  downward closed in every expected count, and frame retention holds at
  any size.
- A concurrency test whose fake returned `? [] : []` could not fail. Give
  the fake real shared state so a lost update is observable.
- providerProcess.test.ts asserted listener and timer counts plus exact
  cleanup retry counts. Assert detachment behaviour instead, so an
  unrelated internal timer no longer breaks the test.
- Scale tests that rebuilt removed caps: 16 MiB and 10,001 files to prove
  limits that no longer exist, 100,001 frames compared against their own
  length, and a projection asserting a literal 10,201 bridge candidates.
  Replace with derived relationships that hold at any size.
- installScript.test.ts merged two tables and now asserts alongside the
  exact wording that every failure carries a recovery action.
- cdpBrowserProvider.sensitive_data asserted outcomes produced after the
  observation window closes while using a zero-length window; give it a
  real window so it measures the malformed-body rejection rather than host
  speed.
- runtime.test.ts repeated the catalog assertion from the MCP boundary
  suite while spawning a second stdio server. Give the database-kind
  scenario its own claim and make stale-build failures legible.

Extract two byte-identical fixture preambles that the max-lines rule had
split across five and three files respectively, removing ~1,000 duplicated
lines.

Profile the heaviest files rather than only running them: the Apple
projection test drops from 6.4s to 0.28s and the reference source import
from 2.3s to 0.2s, with stronger assertions in both cases.

* ci: keep native-authority suites out of the curated Windows lane

CI showed the repointed curated list was red. Three of the restored files
require verified Windows native authority, which windows-latest does not
provide by design:

- src/ghidra/GhidraProvider.test.ts, the replacement for the deleted
  tests/boundary/providers/ghidra/ghidraProvider.test.ts
- tests/boundary/providers/ghidra/ghidraClient.test.ts
- tests/boundary/process/providerProcess.test.ts

All three fail with PrivateRuntimeRootUnavailableError, because Job Object
process ownership and private-runtime DACL enforcement are unimplemented.
The previous ghidraClient and providerProcess entries were added in this
branch and never belonged here; the GhidraProvider replacement does not
inherit the deleted file's Windows behaviour.

Those claims belong behind the same approval boundary as the real Ghidra
Windows workflow rather than the default lane. Record the exclusion in the
guard test so the lane cannot regress to red, and in ci.yml so the next
reader knows the omission is deliberate.

The lane now runs 14 files, up from the 6 it silently ran before, all
verified green on windows-latest.

* test: stop max-lines from forcing test files to split

The 500-line cap on test files only ever produced duplication. It chopped one
binary session suite across nine files whose names were truncated
mid-sentence, and it is why the curated Windows lane referenced six
`processOwnership*` paths that had been split apart in the first place.

Turn the cap off for tests and fixtures, keeping the function-length limit,
then merge the nine binary session files back into one suite. That restores
all 22 cases under a single module registry instead of nine and cuts the
composition project from 20 files to 12. The merged cases become nine
top-level describes grouped by concern, because a single wrapping describe
would itself breach the function-length limit.

Drop applicationWorkflowVerifier.test.ts. Its first case asserted
`verified: true`, which the script emits as a literal after its own checks,
so it could not fail, and every remaining assertion was `expect.any`. Its
second case asserted an export-shape comparison that
applicationWorkflowCli.test.ts already asserts through the product CLI,
which is the stronger claim.

Finally, five javascriptSemanticAnalysis files imported `it` from
@fast-check/vitest while containing no properties. Import `it` from vitest
so those files no longer read as property-based coverage.

* test: stop creating temp scaffolding the temp helper immediately deletes

`createTestTempDirectory` called `createTestWorkspace`, which creates `home`
and `xdg/{config,cache}`, then deleted two of those three directories so the
caller would receive an empty root. Every caller paid three `mkdir` calls and
two `rm` calls per temporary directory for scaffolding it never used, across
roughly a hundred test files.

Create the directory directly instead. Callers that genuinely need a home or
XDG tree can call `createTestWorkspace`.

* test: merge the split process reactive runtime suite

Five files shared one describe name in pairs and re-imported the same fixture
helpers. With the shared preamble already extracted into
processReactiveRuntime.fixture.ts, merge them into one suite of five
top-level describes grouped by concern. All 12 cases are preserved and the
domain project drops from 92 files to 88.

* test: drop two identity round-trips from node runtime characterization

One test built a plan with `max_calls: 10_001` and asserted the field came
back as 10_001, which asserts that the plan constructor copies a field the
test just set, and freezes a former ceiling in the process.

The other built a 201-character alias and asserted it parsed back to the
same string, which round-trips a value the test created.

Neither could fail for a reason that matters. The remaining cases still
cover preparation, stale-plan rejection, single dispatch, and artifact write.

* test: hoist duplicated graph fixtures into the shared fixture module

Eleven copies of three helpers were spread across the seven
javascriptApplicationGraph files: `completeCoverage` five times,
`nodeByLabel` four times, and `firstOf` twice, all byte-identical. The
fixture module already carried a private `completeCoverage`.

Export all three from javascriptApplicationGraph.fixture.ts and import them,
removing 95 duplicated lines.

* test: separate the reactive fixture's two PTY writes

The multi-source run resolved target_lost instead of passed intermittently.
The ready transition matches terminal text "Ready" and consumes that
observation, advancing the terminal frontier; the collecting state then
needs "Collecting" in a later observation. The fixture wrote both back to
back, so the PTY sometimes coalesced them into one chunk and the collecting
trigger could never match.

Move the "Collecting" write after the synchronous shim probe. The probe takes
real wall time, so the reader has drained the first chunk before the second
is written, which makes the ordering deterministic rather than timing
dependent.

Measured 33/34 isolated passes, against roughly even odds before. Under four
concurrent copies it still failed 2 of 12, so this narrows the window rather
than closing it: the underlying race is that node-pty delivers buffered
output through a callback the exit path never awaits, so the harness can
declare the target lost before the last chunk is ingested. The test is left
strict because it is correctly detecting that.

* test: replace expect.any placeholders with real results

Four acceptance scenarios and one composition scenario asserted only that a
field was a string, number, object, or array. Those assertions cannot fail
for a reason that matters, and three of them sit behind real CLI process
spawns that cost about 1.5s.

Capture the actual results once and assert them exactly. The CLI parity
scenario now pins the trace summary and coverage, the version comparison
summary and coverage, and the source-to-bundle summary and coverage, so each
spawn earns its cost. The MCP rejection case now asserts `invalid_request`
with category `invalid_input`, which is what makes it a caller-input failure
rather than a provider one.

The artifact comparison case asserted three `expect.any` values immediately
before an assertion that subsumed them. Replace them with the claim that
actually matters above 500 entries: manifest counts must reconcile with the
arrays returned, and every written file must appear as an occurrence, so a
reintroduced truncation would fail.

* test: remove impossible and incidental prompt assertions

Two assertions in toolResult could never fail: each asserted that the
serialized result did not contain a string that appears nowhere in its own
inputs, so only in the assertion itself.

promptContracts asserted that a `Set` built from a locally declared six
element literal had no duplicates, and pinned thirteen incidental fragments
of rendered prompt prose. Drop the tautology and reduce the prose block to
the five properties an analyst actually depends on: caller input framed as
data rather than instruction, tools offered as options rather than a
mandated sequence, the epistemic vocabulary present, tools never framed as
authorization, and deterministic suggestion order.

* test: hoist the remaining duplicated graph fixtures

Eight more byte-identical helper copies across the javascriptApplicationGraph
files: `graphForNode` four times, `unknownEvidence` twice, and `contentNode`
twice. Move them into javascriptApplicationGraph.fixture.ts alongside the
three hoisted earlier, and drop the imports they left behind.

Net -92 lines across the seven files.

* test: drop enhancedTools helpers already in the shared harness

Three of the six enhancedTools files still carried local copies of
PROCEDURES, inventory, and emptyBounded that are byte-identical to the
exports added to enhancedToolsHarness.ts. Import them instead.

Only fixturePort, connect, and jsonResult differ per file, so those stay
local.

* test: replace repeated process-tree event literals with fixture builders

The file spelled out nineteen thirteen-field event literals and thirteen-field
process nodes inline, varying only three or four fields each, so a test read as
a wall of repeated boilerplate rather than the transitions under test.

Add eventProcessTree.fixture.ts with spawn, exit, re-exec and reparent
builders plus a process-node builder, and express each case as the sequence it
describes. The test drops from 422 to 214 lines with all nine cases intact.

* test: share the process-ownership validation fixtures

The two process-ownership validation files each declared byte-identical copies
of an `ownership` constant and a 24-line `host` builder. Move both into
ProcessOwnership.fixture.ts and import them.

* test: make rejection and retention assertions exact

The reactive scenario rejection table asserted only that each of its six
fixtures failed to parse. A typo anywhere in a fixture would satisfy that,
including one that invalidated an unrelated field. Record the field each row
is meant to invalidate and require the reported issue to point at that path
with the expected code. Verified by making one row valid except for an
unknown key: the row now fails instead of passing for the wrong reason.

The large-body replay test asserted that the serialized result exceeded five
million characters as a proxy for the bodies being retained, which escaping
or padding could satisfy even under truncation. Compare each retained body to
the input byte for byte and total the retained length. Verified by shaving one
byte off the expected value, which now fails.

* test: share the elicitation grants fixtures

The three processCaptureElicitationGrants files each declared the same five
helpers inline: an elicitation request, a fixed clock, a ceiling-only
permission authority, a request-context builder, and a verified-state stub.
Move them into elicitationGrantsHarness.ts and import them.

* fix(process): wait for trailing PTY chunks before declaring target loss

The exit path drained the reactive coordinator and then submitted
target_lost, but node-pty delivers buffered output through onData callbacks
that run outside the coordinator's queue. A trailing chunk that had not been
delivered yet produced no journal entry, so the drain had nothing to wait for
and the target was declared lost while a terminal trigger was still
satisfiable. That is what made the multi-source reactive fixture resolve
target_lost instead of passed at roughly even odds.

Yield to the event loop until the capture journal stops growing before
draining, bounded so a pathological stream cannot stall exit.

The fixture change that accompanied this is no longer carrying the weight on
its own; the ordering it introduced remains correct and worth keeping.

Measured 18 consecutive passes across isolated and four-way concurrent runs,
against roughly even odds beforehand. A control run with the wait disabled did
not reproduce the failure, so the machine was not loaded enough for a
controlled comparison; the before/after numbers are from separate sessions.

* test: hoist the last duplicated domain and coordinator fixtures

processTraceComparison and its Part2 declared the same eight helpers, and the
two ProcessReactiveCoordinatorFailures files declared the same three. All were
byte-identical. Move them into shared fixture modules and import them, netting
429 fewer lines.

The earlier attempts at this failed because depth-based scanning mis-detected
helper boundaries: bracket characters inside type annotations, such as
`options: { truncated?: boolean }`, unbalance the count. Extracting by verified
line range instead worked first time, and unused imports were then settled by
alternating typecheck and lint rather than by guessing which names a body uses.

* ci: ratchet the duplication gate to the reduced baseline

Test-file duplication is down to 2.58% of lines across src and tests, from
3.39% when tests were first included in the gate. Lower the threshold from
3.2 to 2.7 so the reduction cannot regress.

* test: merge the remaining artificially split test files

Five suites were each split across two or three files by the max-lines rule
that is no longer applied to tests, and every split produced a duplicate
describe name and a duplicated import block.

Merge them back: processTraceComparison, ProcessReactiveCoordinatorFailures,
ProcessOwnership.validation, processCaptureElicitationGrants, and
processCaptureElicitation. Net -62 lines and five fewer files.

Give each merged describe a name describing what it covers, since the splits
were arbitrary and the names now collide. Test bodies are unchanged; every
describe already fitted the function-length limit, so merging them as
separate top-level blocks satisfies it.

Imports were settled by writing each block explicitly after typecheck and lint
disagreed. Lint is right about names that appear only inside string literals,
such as `values(["terminal", "process", "http", "websocket"])`, while
typecheck is right about names a body genuinely references, so neither alone
was sufficient.

* test: merge the graph suite and share the enhancedTools result helper

The seven javascriptApplicationGraph files were one suite with one describe
name repeated seven times. Merge them into a single file with seven
describes named for what each covers, keeping all 18 cases.

Three of the enhancedTools files declared byte-identical copies of a
jsonResult helper. Move it into enhancedToolsHarness.ts alongside the rest of
the shared fixtures.

The remaining enhancedTools files stay split: their fixturePort helpers are
three genuinely different provider doubles rather than duplicates, so merging
them would mean renaming per-file seams and lose the reason each exists.

* ci: repoint the Windows lane at the merged ownership validation file

Merging the split ProcessOwnership.validation files removed the two Part
paths the curated Windows lane referenced. The curated-lane guard test caught
it on CI, which is the failure it was added to prevent.

* test(process): make lifecycle assertions falsifiable and scope them to POSIX

Two assertions could not fail, so neither observed the behaviour it claimed.

The deadline disposal test aborted an already-aborted controller and then read
an immutable interruption, so it passed whether or not disposal released the
timer or the listener. Dispose before aborting instead and assert the owned
timer count reaches zero, which distinguishes a released deadline from a
retained one. Verified by mutation: emptying dispose, dropping the listener
removal, and dropping the timer clear each fail the test.

The supervisor test asserted that a repeated stop still resolved, but stop
caches its promise, so it proved nothing about detachment. Replace it with a
late error and close event after disposal and assert the diagnostic channel
stays untouched, keeping an attached control that proves the channel works.
Verified by mutation: retaining the error listener fails the test.

Also mark the TERM-to-KILL escalation POSIX-only. libuv maps every signal to
TerminateProcess on Windows, where a child that ignores SIGTERM cannot survive
the TERM step, so the escalation and SIGKILL signal code are unreachable there.

* fix(vitest): restore cross-project scheduling for local runs

Dropping the per-project `sequence.groupOrder` made every project run
concurrently. `fileParallelism: false` only serialises files inside a single
project, so `process-boundary` could still overlap with the boundary projects
that observe host process and terminal state, which is the contention that
drops input and resize echoes. The removed blanket per-project serialisation
was slower, but it was never what isolated projects from each other.

Keep the two-worker intra-project parallelism that made local runs fast and
apply the ordering locally only, so CI keeps the concurrency it proves green.

* fix(acceptance): make the database startup record check observable

The check asserted stderr contained no fatal record, but a database-kind
startup that only lists the catalog starts no bridge work and writes no
records at all, so the assertion passed without observing anything. Reading
stderr while the child was still running also raced the pipe, and splitting
the buffer could parse a partial trailing line.

Call a tool so startup records are actually produced, parse only complete
newline-delimited records, and read after the transport closes so delivery is
no longer in question. Guard the result with an explicit non-vacuity assertion
so the fatal check cannot silently stop observing anything.

Verified by mutation: injecting a level-60 record through the stderr logger
now fails the test.

* test(acceptance): restore the application workflow verifier coverage

Nothing else in the repository executes
scripts/verify-local-application-workflows.mjs, so deleting this file left the
only automated check on a shipped script. Regressions in its dist imports,
argument handling, or two-version and export-shape output could ship with a
green suite.

The two cases cost about 350ms and 500ms, so the duplication that motivated
removing them was never the reason to drop the coverage.

Verified by mutation: flipping the reported verification status fails the
suite.

* test(contracts): add a published tool name floor

Every remaining inventory invariant is structural, so deleting a contract from
both its group and the aggregate left the whole file green while the catalog
regenerated without it. Structural checks cannot detect removal; only a
declared baseline can.

Declare the 114 published names as a floor rather than an equality. Adding a
tool still needs no edit, while removing one now fails with a message naming
the tool. Verified by mutation: dropping a contract from its group so the
aggregate loses it too fails the floor.

* test: restore no-ceiling coverage above plausible truncation limits

Four suites claimed a result was complete at any scale while their own inputs
were bounded below where a truncation would appear, so the guarantee they
described was not actually under test. Scale independence cannot be asserted as
a relationship over a fixture smaller than the cap being ruled out.

Raise each fixture past any ceiling a regression would plausibly introduce,
which is far cheaper than the removed cases:
  referenceSourceImport  600 entries/64 KiB -> 5,000 entries with a 4 MiB member
  customProtocolCapture  generated max 40 frames -> 300..1,000
  appleApplication       12 frameworks/16 candidates -> 250/1,600
  nodeRuntimeCharacterization  assert the absent call-budget and alias bounds
    where they would be added, since the budget derives from the case count

Verified by mutation: a 1,000-entry import cap, a 500-frame capture cap, a
100-framework projection cap, a 100,000-call budget cap, and a 200-character
alias cap each fail the corresponding suite.

* fix(process): require a quiet period before declaring the target lost

Returning on the first unchanged journal sample granted a single event-loop
turn of grace. A chunk queued on a later iteration therefore still let
`target_lost` be submitted before the final terminal observation, discarding a
satisfiable reactive trigger under the same load-dependent exit scenario the
drain was added for, and `maxTicks` never bounded that because the loop exited
early every time.

Require several consecutive unchanged turns so a callback arriving on any of
the next few iterations still lands first, while keeping the overall tick bound
so a pathological stream cannot stall exit.

* fix: stop a 4 GiB fixture and measure the drain quiet period in time

The reference import fixture cycled its five sizes across all 5,000 entries,
which made a thousand 4 MiB members and nearly 4 GiB of fixture written
concurrently. It happened to fit locally but cannot fit a runner's disk. Cross
the two boundaries independently instead: entry count by volume of tiny
members, byte size by a single 4 MiB member, for about 4.2 MiB total. Verified
by mutation that a 1,000-entry cap and a 1 MiB per-member cap both still fail.

The drain check counted scheduler turns, but consecutive setImmediate turns
elapse in microseconds, so four of them finish long before a pty callback
delayed under host contention, which is when the trailing chunk matters.
Measure the quiet interval in elapsed time instead, so a late callback within
the window still lands before target loss. Verified by simulation: the
turn-count version missed any delay beyond a few microseconds, the timed
version observes delays up to the quiet interval.
2026-10-03 23:22:07 +08:00
morluto d6e545cc48 test: remove Vitest config test exports 2026-09-29 18:05:18 +08:00
morluto d4dc940bd8 refactor(test): keep coverage at behavioral boundaries (#474)
* refactor(test): keep coverage at behavioral boundaries

* fix(ci): restore package workflow temp-root classification
2026-08-11 18:34:32 +08:00
morlutoandDevin e91c8c39c2 refactor(test): colocate pure-domain tests and reduce test debt (#471)
* refactor(test): colocate pure-domain tests and reduce test debt

Move focused immutable domain/contracts/adapter tests from
tests/composition/** beside their production owners under src/** as
colocated *.test.ts/*.fixture.ts, and relocate persistence-shaped
composition tests under tests/boundary/filesystem/**. Update import
paths, the Vitest project topology (shared worker context for pure
domain/contracts and recording-port service projects), the test
migration ledger, testing docs, and AGENTS.md to match.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* docs: remove test migration ledger

The cutover is complete and the ledger is no longer referenced anywhere
in source, scripts, or the docs gate. Drop the standalone artifact.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* docs: clean up stale migration artifacts

Remove empty directories left by the test migration and replace the
"final cutover" language in docs/testing.md with standing PR guidance
that no longer references the completed migration.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: repair CI failures from schema, knip, and stale generated artifacts

Three root causes fixed:

1. catalog-schemas.mjs: valuesAt only traversed anyOf from Zod's
   toJSONSchema, not oneOf. The webScreenshotDiffSchema change to a
   discriminated union produces oneOf, so the product catalog generator
   could not derive the web_screenshot_diff schema version. Add oneOf
   traversal.

2. conformancePackage.ts: the test rewrite removed the only imports of
   ScenarioManifest, ReplayPlan, ShimPlan, and ExpectedEvidence, making
   them unused exports that knip flags. Remove the dead type aliases.

3. Regenerate stale artifacts (product catalog, MCP tool catalog, skill
   metadata, conformance manifest) that drifted from the source changes.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-10 14:57:44 +08:00
morluto 8f229ede70 feat(electron): add autonomous Electron runtime analysis (#464)
* feat: add autonomous Electron runtime analysis

* feat(electron): add lifecycle and boundary telemetry

* feat(electron): capture runtime boundary evidence

* fix(electron): package boundary hook helper

* feat(electron): report active capture coverage

* fix(electron): account for bring-your-own runtime

* feat(electron): expand agent-owned runtime scenarios

* fix(ghidra): keep fixture credentials out of argv

* perf(test): serialize local test and docs workloads

* fix(electron): harden active runtime capture

* fix(inspector): require exact runtime target authorization

* fix(docs): make managed manifest host independent

* fix(ci): make generated catalog portable

* fix: unblock autonomous command and electron flows

* fix(electron): close popup and detached helper gaps
2026-08-05 19:07:46 +08:00
morluto 51559245d2 Merge pull request #461 from morluto/agent/comprehensive-repository-audit
fix: harden evidence lifecycle and MCP contracts
2026-08-04 03:00:25 +08:00
morluto f07acde513 fix(ci): stabilize Vitest project routing and shards 2026-07-30 03:59:44 +00:00
morluto f47ee1abb4 fix(ci): align Vitest project worker budgets 2026-07-29 22:24:54 +00:00
morluto 935721df10 fix(test): bound boundary worker pressure 2026-07-29 22:16:16 +00:00
morluto 513d9b1555 test: overhaul deterministic suite architecture 2026-07-29 22:00:38 +00:00
morluto 7157ab0b70 test(mcp): stabilize lazy-loading verification 2026-07-26 15:26:41 +00:00
morluto 42fbbb297b perf(test): speed up local and CI validation 2026-07-24 03:10:11 +00:00
morluto c08b9a7d6c build: streamline local and CI validation 2026-07-22 16:06:28 +00:00
N0zoM1z0 d25e98c3f9 feat: complete REA remediation program 2026-07-22 22:09:12 +09:00
morluto 168100e532 test: inherit root options in Vitest projects 2026-07-20 10:47:28 +00:00
morluto bc8035918b test: cap workers across all hosts 2026-07-20 08:52:20 +00:00
morluto c2e134b8e4 test: isolate slow filesystem and CLI suites 2026-07-20 08:48:22 +00:00
morluto f23f52903c fix(test): make host fixtures portable 2026-07-16 19:51:25 +08:00
morluto fb17156499 fix(tooling): isolate concurrent repository checks 2026-07-14 22:22:44 +00:00
N0zoM1z0 ab8c5c1e13 fix(tooling): isolate concurrent repository checks 2026-07-15 06:16:30 +08:00
N0zoM1z0 f46fc7f1f3 test: serialize subprocess-heavy integrations 2026-07-14 21:33:25 +00:00
N0zoM1z0 2223da1916 test: reduce test suite runtime 2026-07-14 21:08:27 +00:00
morluto 29a2f5d032 Enable test coverage thresholds with retry and verbose timing 2026-07-12 10:19:56 +08:00
morluto b0301b81dd Add Node.js project tooling and config 2026-07-12 03:25:06 +08:00