* refactor: prune internal seams and preserve configuration ownership
* fix(setup): keep YAML aliases and inherited registration settings on refresh
- Reuse an unchanged YAML collection only while every alias in it still
resolves after the edit; otherwise rebuild it so the alias becomes its
value. A sibling anchor replaced or moved in the same refresh no longer
leaves an unresolved alias in Hermes or Goose output.
- Keep a connection selector whose existing value already chooses stdio
(for example an inherited Hermes transport: stdio) instead of superseding
it; remote selectors are still replaced. Explicit stdio selectors are
therefore no longer rewritten (Kimi expectation updated).
- When the whole Hermes registration is inherited through a merge,
override it with that merge and write only the changed fields.
* fix(setup): edit registration fields whose YAML key is an alias
A key written as an alias to a string scalar (for example *field with
field: &field env) was treated as a source-only key, so any change to
its value was refused in Hermes and Goose. Resolve such keys to their
string, keep the aliased spelling while its anchor survives the edit,
and write the plain key when the same edit rebuilds the anchor.
* test(process): bound cleanup-boundary waits and always release gates
Both cancellation tests awaited a bare started promise and released
their gates only on success, so a missed boundary waited for the outer
deadline and could leave cleanup parked. Use awaitAcknowledgement and
release and join the cleanup in finally.
* fix(setup): repair only aliases an edit leaves unresolved
Replace the pre-edit alias survival guess, which expanded aliases to
sibling anchors that survive unchanged, with an exact repair: record
what each alias in the replaced value resolves to, write the edit, then
replace only aliases left without a preceding anchor by that value.
- Keep an aliased key's source position, so its anchor stays ahead of
later aliases.
- Treat an aliased key as its string only when no other key in the map
resolves to the same string; an ambiguous one stays guarded.
- Override an inherited Hermes registration only when the requested
entry keeps every inherited field, so an explicit replacement can drop
remote selectors.
* fix(setup): repair aliases whose binding an edit changes, keeping comments
An alias whose own anchor was removed could resolve to an earlier anchor
with the same name and silently change value. Record the node each alias
resolved to and repair any alias that no longer resolves to that node.
Carry the alias's comments and spacing onto the repaired value.
* test(setup): split the Hermes anchor and merge refresh cases
* fix(setup): refuse removing an overridden key its merge would restore
The merge-retention check skipped keys the mapping also overrides, so
dropping such a key (for example a remote url during an explicit stdio
replacement) let the retained merge restore it. Check removal before
skipping overridden keys, so the edit is refused with the existing
message instead of producing a registration that cannot be applied.
* refactor(cli): run one-shot commands through one shared owner
scripts/rea.mjs and the test CLI session worker each implemented the
one-shot command sequence: argument normalization, output admission,
configuration errors, version documents, the Amp scope refusal, and
result delivery. The worker copy had drifted: it never rendered
formatted version reports or the Amp refusal.
Move the sequence into src/cliOneShot.ts. Both callers supply a host
for their streams, exit and resource ownership. The launcher keeps
closed-pipe status, worker shutdown and process exit; the session
worker captures output per command.
The cliOutput bundle entry is superseded and removed. The dispatcher
test now replaces only the command runtime, so output admission runs
production code. A new parity test compares the file session with a
fresh process.
* test(cli): bundle the one-shot entry in the runtime bundle fixture
* perf(tests): run plain CLI commands on the file's shared process
`cli.run` in `cliTest` files started a fresh compiled CLI for every
command, about 0.45s and 260 MB each before any work. The file-scoped
session already existed, but callers had to opt in through
`runFileCli` or `sharedCliRun`, so 1,300 call sites still spawned.
Make the fixture choose: `cli.run` uses the shared process when it
reports the command exactly as a fresh process would, and starts one
otherwise (stdin, explicit deadlines, MCP and MCP doctor routing,
NODE_OPTIONS, or enabled logging). `freshProcess: true` keeps
explicit process-level checks. The session worker now presents the
launcher's entrypoint, so registration identity matches a fresh
process.
`runFileCli` and `sharedCliRun` are superseded and removed; their
call sites use `cli.run`, and paired fresh/shared checks use
`freshProcess`.
* style(tests): format the managed member summary call site
* test(cli): give each shared-session command an empty stdin
The shared CLI process reads session frames from its own stdin. A
command that reads stdin without supplied input, such as
`compare-wasm-interfaces - --json`, read that pipe and hung, where a
fresh process receives EOF. The worker now presents an ended, empty
stdin to each command and restores its own afterwards.
The parity test compared the routed path with itself after cli.run
began choosing the session. It now compares a fresh process with the
session directly, including the `mcp add` refusal and a command
reading absent stdin.
* test(cli): keep default command logging and launcher checks on fresh processes
environment-serve and environment-build log at info when REA_LOG_LEVEL is
unset, through a logger that writes to stderr directly. The shared CLI
process captures only process.stderr writes, so routed runs lost those
lines. Declare the default-logging commands beside the command names,
have infoLogger read that set, and start a fresh process for them.
The log-level precedence checks compared the shared process with itself
after routing; their fresh side now passes freshProcess: true.
Explicit deadlines at or below the project deadline only shortened it:
a literal 30-second value also disabled the coverage scaling, which timed
out wide-capacity regressions on main. Workloads above the built
deadline use the long-running tag. Deadlines derived from an operation
budget remain.
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(il2cpp): read generic containers on demand and render method spec names
Generic container indices and records are demand readers on the same metadata snapshot, so links that do not ask for generic instances allocate nothing. Type rendering keeps one graph walk with its workspace, depth and cycle guards; the C#-like names and the Il2CppDumper GetMethodSpecName names are two styles over it.
* feat(il2cpp): link generic method instances with admitted rows and kept issues
generic_instances links every genericMethodTable row through methodSpecs, genericInsts and genericMethodPointers, choosing the one CodeRegistration layout whose counts bound every row. The table is reserved against the report budget before any row is resolved; rows and issues are admitted before retention, and a contradicting row stays unknown with an address-backed issue instead of failing the link. Views page the rows by definition, state, address and name, unrequested and historical collections stay unknown, and Ghidra seeds fold instances after the definitions.
* feat(il2cpp): accept --generic-instances on the CLI and describe it in tool contracts
* docs(il2cpp): describe generic method instances and their limits
* fix(il2cpp): keep issued generic rows unknown and name their row
A method spec that contradicted the metadata still reported its pointer slot as code, while the schema and provider limitation promised unknown. Such rows now keep no address, every issue carries its genericMethodTable row, the domain validator requires unknown exactly on issued rows, and shared specs name one other row instead of all of them. The row-size lower bound moves to the metadata admission owner, the hex helper is shared, and budget wording says checked rather than reserved.
* test(il2cpp): reject ambiguous generic layouts and pin budget, yield and limit paths
* fix(il2cpp): keep unmapped generic slots per row and reject undeclared arguments
Layout selection no longer requires every genericMethodPointers slot to be
mapped; each row reads its own slot, so one unmapped slot leaves only that
row unknown with an issue. Method specs whose class or method argument count
differs from the owner's declared generic container are issued and unnamed.
Fixtures now declare the containers their generic rows fill.
* fix(il2cpp): page generic method issues instead of retaining them in summaries
Summaries keep generic_methods without its issues and count them in
counts.generic_method_issues; the generic_method_issues collection pages
them by rows, indices and fields, as code_gen_modules already does. Docs
also name the method_indices filter the request schema accepts.
* fix(il2cpp): read split generic arguments per slot and method containers on demand
Generic argument arrays may span adjacent loader ranges: admit them with
hasPointerArrayCoverage and read each slot through readPointer, instead of
requiring the whole array in one mapping.
Method generic container indices are read on demand from the method record,
like type containers, so no link path allocates a per-method column. An owner
whose container index is out of range now leaves the method spec unnamed with
an issue instead of naming it unchecked.
* fix(il2cpp): report generic entries past the address space at their row
A damaged methodSpecs or genericMethodPointers pointer could make a spec or
slot address exceed 64 bits. The issue kept that address, which the address
schema rejects, so inspection failed instead of leaving the row unknown.
Check the entry range first and attach the issue to the genericMethodTable
row (or its slot field).
* chore(il2cpp): remove unrelated formatting changes
* fix: restore adb float pattern and mobile fixture from main
The merge commit's lint-staged run rewrote these two main files: oxlint --fix
dropped the escape in [+\-], which is invalid under the RegExp v flag.
* fix(il2cpp): preserve unreadable generic method identities
* docs(il2cpp): record generic-instance workflow verification
* docs(il2cpp): qualify final integration checks
---------
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Resolve pointer and scalar facts through their shared loader-aware owners before registration search or type rendering. Preserve unresolved names and attributes, read count fields at their declared width, and admit declared zero-fill registration tails without accepting unmapped bytes.
Cover relocation overlap and adjacency, loader-bound fields, ABI padding, nullable names, and CLI/MCP behavior; document format and verification limits.
Fixes#6881Fixes#6873Fixes#6871
Normalize parsed facts at their owning boundaries across PE exports and certificates, ELF section names, GPU bundles, IL2CPP generic types, dyld slice selection, source maps, environment replay, and JavaScript mapped arguments. Preserve partial facts and explicit unsupported cases; clarify the corresponding documentation.
Validation: 831 affected tests passed with one existing skip across 23 files; build, typecheck/lint, documentation, formatting, and 2133-file test discovery passed. Format controls are synthetic; real Unity, GPU, and ARM-device execution is unverified.
Fixes#6709Fixes#6688Fixes#6719Fixes#6718Fixes#6704Fixes#6703Fixes#6692Fixes#6723Fixes#6687Fixes#6726Fixes#6696Fixes#6691Fixes#6727
Go, IL2CPP, dylib resolution, and application-workflow tests were still
starting a compiled CLI for every command. They now reuse one process
per file. Warm focused runs dropped from 40s, 46s, 35s, and 21s to
14s, 21s, 20s, and 2s.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Validate the fixed 16-byte version-39 records before using their count to select the serialized index width. Reject contradictory count and byte extents while preserving valid zero, one and 300-record controls.
Validation: 89 focused reader/provider/compiled CLI and stdio MCP tests; check:ci; seven reproduced CLI and SDK-validated MCP cases with unchanged fixture hashes. Version-specific source and independent parser evidence distinguish fixed records from compressed indices. Synthetic metadata only; no real-game or target-execution qualification.
Fixes#5751
Link IL2CPP metadata names to native addresses and types through shared CLI/MCP workflows, with bounded queries and atomic Ghidra seed export.
Preserve the original implementation by 8rulerstar and follow-up correctness, resource, provenance, and CI dependency repairs. Verified against real Windows/Android artifacts, independent references, actual Ghidra, and focused CLI/MCP tests.
Fixes#5112.
* feat(il2cpp): read member names, string literals and metadata versions 24 and 27
Adds field, property, event and parameter names with their owners, managed string literals, and the version 24 and 27 header layouts to inspect_il2cpp_metadata, with filtered pages for the new collections. Member decoding yields between row batches like the other row readers.
* fix(il2cpp): bound metadata expansion and query parameter owners
* test(il2cpp): cover nonempty property and event tables
---------
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
* feat(il2cpp): inventory IL2CPP global-metadata images, types and methods
Add inspect_il2cpp_metadata (CLI inspect-il2cpp-metadata), a read-only
in-process decoder for unencrypted Unity global-metadata.dat version 39.
It reports header sections, serialized index widths, images, type
definitions (nesting from the nestedTypes section) and method names.
Unrecognized magic and other versions are unsupported; damaged headers,
overlapping sections, inconsistent record sizes, out-of-range indices and
invalid strings fail with a reason code and no partial success. MCP
detail summary retains the complete Evidence and pages rows through
inspect_analysis_view kind il2cpp-metadata.
Adds an opt-in verify:il2cpp:metadata -- --metadata PATH check.
* feat(il2cpp): deliver metadata summaries by default over MCP
A 112,232-method file produced a 10.5 MB complete response, above the
10 MiB stdio budget. Default MCP delivery is now the retained summary;
detail complete stays available, and the one-shot CLI requests it.
* fix(il2cpp): reject shifted strings, nesting cycles and method owner drift
Review fuzzing showed that moving the strings section offset by one byte
silently renamed every image, type and method. Name indices must now start
a string, which also bounds total decoding by the section size.
Nesting cycles and methods whose declaring type disagrees with the owning
type range are rejected instead of returned. Adds service, shutdown and
index-width coverage, and moves environment-specific measurements out of
the public guide and tool description.
* feat(il2cpp): decode global-metadata versions 29 and 31
Unity 2022.3 writes version 29 or 31 metadata: a 256-byte header of
(offset, size) pairs, fixed 4-byte indices, type records with
elementTypeIndex, and method records that gained returnParameterToken in
version 31. Record counts come from section sizes and must be whole.
Sections report a null count for these versions and index widths report
fixed-width-version. Image, type and method rows keep the same shape, and
the string, nesting and method-ownership checks apply to every version.
* fix(il2cpp): bound report size before serializing and tighten coverage
Rows share decoded names, so a 130 KB file could expand into gigabytes of
JSON before the 64 MiB budget check, ending in an internal RangeError. The
provider now refuses from a lower bound on the serialized rows first.
The decoder also rejects non-empty sections inside the header, methods
outside their declaring type's range and overlapping image type ranges, and
keeps a leading byte order mark in names. Layouts now carry one indexing
mode instead of two coupled flags, and the tool description explains
nested_type_start.
* fix(il2cpp): refuse oversized metadata before decoding rows
Address review findings on the IL2CPP metadata inventory:
- check a lower bound from header row counts before any row object is
allocated, so a large rejected file no longer builds every row first;
- validate sealed parent Evidence and count its rows once per report
instead of on every inspect_analysis_view page;
- decode version 39 fixtures with 2- and 4-byte type-definition,
generic-container and parameter indices.
* docs(il2cpp): attach the decoder comment to readIl2cppMetadata
* fix(il2cpp): deliver complete rows by default like the other inspection tools
inspect_il2cpp_metadata defaulted to detail summary, the only inspection
tool to do so. Omitted detail now returns every row inline, as
inspect_artifact, decode_interface_builder and the capture tools do;
summary stays one argument away and the guide, CLI reference and skill
reference say to select it first for large games, whose complete MCP
delivery is refused at the stdio budget with retained Evidence.
* test(il2cpp): cover narrow-width -1 sentinels and fix the skill's default
An empty image stores its type start as the all-ones value of a one- or
two-byte index; the decoder reads it as -1, but no fixture exercised it,
so removing either conversion passed the suite. The fixture can now append
an empty image, and both widths decode it. The skill still described a
summary default; it now says to select summary first, as the guide does.
* docs(il2cpp): request summary explicitly in the summary example
* chore(il2cpp): hash the verifier metadata through owned file resources
* fix(il2cpp): count escaped name length without building the escaped string
* fix(il2cpp): keep the verifier's check failure and small-table last page
* feat(domain): count JSON string length without building the escaped string
* fix(il2cpp): keep descriptor cleanup owners and share the JSON length count
* test(domain): give the backslash case real backslashes
* fix(il2cpp): select the CLI path and finish the command like the other tools
Follow main's absoluteCallerPath (#4067) and pass the command signal to finishAnalysisCommand.
* fix(il2cpp): select the verifier input path like the CLI
Use absoluteCallerPath (#4067) so a path with a symlink followed by .. names the same file the CLI and MCP inspect.
* feat(il2cpp): add compact composable metadata queries
Retain source-indexed Evidence for filtered batch and field queries. Bound representation growth before serialization, yield decoding for cancellation, and share CLI Evidence publication with desktop inspection. Verify public Unity metadata against independent byte and managed-DLL oracles and exercise actual stdio SDK workflows.
* fix(il2cpp): retain the validated metadata header magic
* test(il2cpp): remove obsolete execution wording exception
* fix(artifacts): preserve export selection and completed source facts
---------
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>