* feat(managed): decode ldstr user strings in member inspection
inspect_managed_members reported every ldstr operand as a bare 0x70 token,
so an analyst could see that a method loads a literal but not which one.
Answering "where is this path or key used" required a second tool.
Read the optional #US stream and add a top-level user_strings inventory:
each distinct token loaded by an inspected method body, once, in token
order, with its #US file offset and decoded UTF-16 value. Instruction
anchors and normalized CIL digests keep their build-local tokens. A missing
#US stream or malformed item is reported against its token as partial
coverage. Evidence recorded before this field reads user_strings as null.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): collect user strings only from ldstr
A malformed ldtoken can carry a 0x70 token, which the operand decoder
classifies as a string operand. Only ldstr loads a literal, so collect
user strings by opcode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): admit user-string tokens and failures before collecting them
Charge each distinct ldstr token to the decode budget before adding it to
the collection set, and admit each #US failure before retaining it. A
missing #US stream is now one stream-level issue rather than one per
token.
Document why the #US terminal flag is not validated: Mono and Roslyn
assemblies disagree with ECMA-335 II.24.2.4 and the runtime ignores it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(managed): describe missing #US reporting and the unvalidated flag byte
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(managed): cover #US items that run past the stream end
An odd length whose payload crosses the end of #US, and a two-byte
length prefix that starts on the last #US byte, are both rejected as
"User string leaves #US" beside a readable literal. The test fails if
that bounds check is removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): report reserved #US length prefixes as string failures
The compressed-length reader shared by #Blob and #US hard-coded
invalid-blob and "Metadata blob" for a reserved prefix, so a malformed
user string was published as a blob failure. The caller now names the
item it reads: #Blob keeps its code and message, and #US reports
invalid-string.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): append member inspection issues without argument spreading
inspectManagedMembersBytes gathered every producer's issues with
issues.push(...list), passing one argument per issue. A large malformed
input, such as hundreds of thousands of distinct bad ldstr operands or
MemberRef rows, overflows the stack with an untyped RangeError instead of
returning partial coverage. All six sites now use
appendManagedParseIssues, which appends one issue at a time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): classify truncated #US and #Blob length prefixes by item
A two- or four-byte compressed-length prefix cut off by the end of the
artifact went through the generic range check and was reported as
invalid-row. The prefix reader now uses the item's own code for both
reserved and truncated prefixes: invalid-string for #US, invalid-blob for
#Blob. The unused generic range helper is removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): retain user-string terminal flag conformance issues
* test(managed): write ECMA-335 #US terminal flags and cover flag issues
d5b7b0704 reports a #US terminal flag that differs from ECMA-335
II.24.2.4. The fixture heap always wrote 0, so its non-Latin-1 literals
now read as nonconforming. The fixture writes the spec flag, and a new
test flips one flag each way ("ok" set to 1, "it's" set to 0) and checks
that both literals remain beside one issue each.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): classify ldstr operands before collapsing a missing #US
With no #US stream, every collected ldstr operand was counted as a #US
reference and reported only as missing-stream, while the same non-0x70
operand got invalid-heap-index when the stream existed. parseUserStrings
now owns which operands are user-string tokens: any other operand is
invalid-heap-index either way, and only 0x70 tokens count toward the
missing-stream issue. The heap reader drops its duplicate kind check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): state user-string limits in member comparisons
compare_managed_members ignored user_strings, so a method whose ldstr
literal changed at the same token, or legacy Evidence that never examined
literals, could read as unchanged with no caveat. The comparison now adds
an unknown limitation when either side predates user strings and a
not-compared limitation when literals were observed. ExportedType and
user-string inventories share one uncomparedInventory rule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(managed): advertise ldstr literals in member inspection help
The MCP tool description, the type_names input, and the CLI command and
--type help now say that inspect_managed_members returns the #US string
literals that ldstr loads, and that a type selection limits them to the
literals the selected types' method bodies load.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): read the empty #US entry at offset 0
ECMA-335 II.24.2.4 makes the first #US entry the empty blob: a zero
length with no terminal flag byte. ldstr 0x70000000 loads it, but the
odd-length check rejected it as invalid-string, dropping the literal and
making coverage partial. Offset 0 with length 0 now reads as "".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): reserve #US offset 0 and admit literals before scanning
Offset 0 is the reserved empty #US entry, so a nonempty item there is now
invalid-string instead of a published literal. The literal's projection
is reserved before the terminal-flag scan, so an inadmissible item fails
at the budget without reading every code unit. The scan's loop index no
longer shadows the token's heap index.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): report #US length prefixes wider than ECMA-335 assigns
compressedLength accepted an overlong encoding, such as 0x80 0x05 for a
5-byte item, without comment. It now reports whether the width is the
one ECMA-335 II.23.2 assigns, and the #US reader reports a non-minimal
prefix as an invalid-string conformance issue beside the readable
literal, like the terminal flag. readMetadataUserString returns every
issue it observes. #Blob behavior is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): check the #US length width before the empty entry
The offset-0 empty entry returned "" before the length-width check, so an
overlong empty entry such as 0x80 0x00 read as complete. The width issue
is now collected first and returned with the empty literal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): admit #US width issues only for returned literals
The overlong-prefix issue was reserved before the fatal shape checks, so
an item that then failed, such as 0x80 0x00 away from offset 0, leaked
512 bytes of budget per token. The width issue is now built and admitted
only on paths that return the literal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): keep #US issues out of member metadata status
User-string issues, including terminal-flag and length-prefix
conformance differences, marked metadata.status partial, so
compare_managed_members turned one-sided methods and fields into unknown
even though every member row was examined. #US issues now make coverage
partial while metadata.status describes the member inventory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): give user strings their own coverage
#US issues entered the member coverage, so every consumer that reads it
as member coverage (application graph member facts, comparison status)
reported examined members as partial over a literal conformance
difference. user_strings is now { state, literals, issues }: #US issues
live there and never change the member coverage or metadata.status.
parseUserStrings returns that inventory directly.
compare_managed_members inspects with user strings skipped, since it
never reads literals, so a large literal cannot exhaust the decode budget
of a comparison. Its limitations state once that literals are not
compared, and the ExportedType limitations return to their main form.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): mark user strings partial when a body is not fully decoded
With user strings owning their coverage, an inspected method body that
was partial, malformed, or truncated no longer reached their state, so a
literal after the decode stopped was missing from a "complete" inventory.
parseUserStrings now marks the inventory partial unless every inspected
body was fully decoded, keeping the literals it did observe.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): mark user strings partial when a method row is omitted
A MethodDef row that failed before its body was read was recorded as an
issue and omitted, so its ldstr instructions were never scanned, yet the
user-string inventory could still read as complete. parseUserStrings now
takes the parseMethods result and stays partial whenever a method row
failed, as well as when an inspected body was not fully decoded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): count only method rows that may hide a selected body
Any parseMethods issue marked user strings partial, including an
unreadable name on a method outside the type selection, so a fully
scanned selection read as incomplete. parseMethods now reads each row's
owner first and reports unscannedRows: rows that produced no method while
their owner is selected or unknown. parseUserStrings uses that count.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): treat unreadably named types as possibly selected
Under a type_names selection, a type whose name could not be read may be
a requested type, but its methods were skipped without counting toward
unscannedRows, so an unexamined body left user strings complete. A
method whose owner's name is unreadable now counts as possibly selected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): judge user-string scans by CIL decoding, not body status
body.status is malformed for an invalid exception section even when
every instruction decoded, so user strings read as partial although every
ldstr was examined. The body reader now reports whether its CIL was fully
scanned, independent of exception-region validity, and parseMethods folds
unscanned bodies and unproduced rows into one unscannedMethods count that
parseUserStrings uses instead of reinterpreting body.status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): stop deduplicating user-string issues
The inventory passed its issues through uniqueManagedParseIssues, which
serializes every issue into an unbudgeted map: about 42 MB of heap and
113 ms for 125,000 issues on Node 24. The issues are unique by
construction (one failure or per-offset conformance issues per distinct
token, one missing-stream issue), so the inventory now returns them, and
its literals, without copies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): preserve selected string coverage unknowns
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Charge fixed row bookkeeping once per table and row within an admitted
metadata layout. Repeated owner lookups no longer permanently consume
capacity for temporary cursors; distinct rows and failed admissions retain
the existing representation limit and typed failure behavior.
Verify repeated local attribute-constructor lookups through public CLI
metadata/member inspection and preserve the remaining capacity guards.
Closes#1878
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
* refactor(domain): split error taxonomy into thematic modules
Move each error family out of domain/errors.ts into its own module
(base, core, provider selection/adapter, browser observation,
artifact, evidence, unknown registry, hopper, configuration) and
relocate AnalysisErrorProjection to analysisErrorProjection.
Migrate all 162 importers to the owning modules and delete
errors.ts. No re-export shims; behavior is unchanged.
* refactor(runtime): name all silent catch causes without changing diagnostics
Bind cause in every bare catch and unhandled rejection across src/.
Real failures preserve the cause for logging; best-effort cleanup
carries an explicit comment. Caller-visible and Evidence strings are
byte-identical to before.