XAML $max_tree_bytes truncates real-world files from ~80 KB — measured on a 14,056-file production repo, ~50 files affected #222
Labels
No labels
code-review
correctness
dos
performance
security
severity/high
severity/low
severity/medium
tech-debt
Kind/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
h-dv/code-index#222
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found in production use, not in a fixture. Reported from a 14,056-file WPF repository with 30,461 XAML field symbols.
Measured
Every affected file reports exactly
extract.truncated_tree, neverextract.node_limit_reached, across six probes.parse_errors: 0— this is not a parse failure and not host give-up. It is the extractor bounding its own walk, writing what it had, and declaring it, which is the departure from "budget exhaustion is a refusal" that #80 explicitly permits.ScaleBackgroundLayerModels.xamlwndKommissionierenForBA.xamlwndProduktionsKontrollplanElectronics.xamlwndAbstempelnBDE.xamlxRichTextBoxDevExpr.xamlwndShopItem.xamlThe cut sits between ~78 and ~90 KB of source. Reported count was 42, correctly identified by the reporter as a FLOOR rather than a total — the 43rd is truncated too, the index was mid schema-upgrade rebuild (5,041 of 14,056 files carrying the invalidation sentinel), and 44 candidates are ≥97 KB with 58 ≥78 KB. Expect it to settle in the low-to-mid 50s.
The files affected are the ones that matter most in that repo:
Generic.xaml(246.6 KB),BaseTheme.Net.xaml(221.2 KB),wndBeleg.xaml(160.3 KB),BaseTheme.xaml(133.3 KB),AiChatControl.xaml(124.3 KB) — the theme dictionaries and the main controls.The mechanism, and it is NOT node count
tests/packages/xaml.extractor.wat:$max_tree_bytesbounds the serialized tree-sitter tree ($tl, the length the host passes alongside$tp), not source bytes and not node count:A serialized tree is several times its source, which is exactly why a 320,000-byte budget bites at ~80 KB of XAML, and why markup density moves the threshold. The reporter's inference that it was "node-count driven" is the one budget that is NOT firing —
$max_factsreports a different code that never appeared in any probe.Why this is worth fixing rather than documenting
For a truncated file, symbols past the cut are missing from every structural tool:
file_outline,search_symbols,find_references,change_impact. The disclosure is honest and it is per-file, so a reader who checks is not misled — but the reader has to check, and the affected set is the largest and most-referenced files in the repository.It also compounds with the XAML use-channel gap: the package declares
resolver = ["bridge_source"]with both bridges pointing outward, so nothing emits a ref targeting a XAML symbol andref_counton anx:Namefield is structurally unmeasured (30,461 symbols inkinds_without_use_channel). On a truncated large file, a "this style is unused" conclusion is unsupported twice — the symbol may not be recorded at all, and even where it is, its zero was never measured against anything.What the fix must weigh
The budget is deliberately one line — the extractor's own comment says the globals exist "so that one line is the whole knob", and
plugin_pkg.rs::xaml_extractorrewrites exactly those two lines to make a budget fire on a small file. So the edit is trivial and the consequences are not:Generic.xamlfull ofx:Namemay exceed$max_facts = 4000once the tree budget stops cutting first, turningtruncated_treeintonode_limit_reachedon the same files. Raising one budget alone would look like a regression to anyone watching the diagnostic rather than the outcome.$bp > 196608is a hard guard on the fact output buffer, checked every iteration of the scan.(memory 8)— 512 KiB initial linear memory, withsrc_offsetat 262,144, shared between source, tree and fact buffer.tests/packages/xaml/is packed, so this is a package version bump (0.1.0→ next), a re-record oftests/packages/xaml.digest, and a release note — an operator who does not re-pin gets a baredigest_mismatchand no other explanation.xaml_package_e2e.rs::the_shipped_package_is_the_artifact_that_was_recordedstays RED until the digests are re-recorded.What a fix must prove
$max_factsmeasured, not reasoned about: state which budget binds first at 100 KB, 250 KB and 1.3 MB of real markup.Mutations
$max_tree_bytesto 320000 → the "large file completes" assertion must go RED.the_shipped_package_is_the_artifact_that_was_recordedmust go RED (it already does; confirm it still does after the bump).Related: the fact-budget follow-up, and the XAML use-channel question (a
bridge_source-only package meansref_countonx:Namefields is structurally unmeasured).$max_factsis the next wall behind #222 — raising the tree budget alone will turntruncated_treeintonode_limit_reachedon the same files #223Fixed in
ea74dbc(packagede.h-dv.xaml0.2.0). Landed together with #223, because fixing either alone would have moved the wall rather than removed it.The measurement, which is the deliverable
Bisected through the real grammar, real worker and real parent validator — tree length recovered by bisecting the budget itself (the smallest budget at which
truncated_treedoes not fire IS the tree length).<b/>fillerThree results worth keeping:
29 + name(ref) and34 + name(symbol) to the byte on three unrelated shapes. That licenses arithmetic assertions rather than fitted constants.320,000cut at ~64 KB here and ~80 KB on the reporter's sparser files. The mechanism in this issue is confirmed exactly.What shipped
All three budgets now derive from
FILE_SIZE_LIMIT_BYTES(2 MiB — the largest file the indexer hands any extractor), not from the corpus of the day, which was this issue's actual root cause.$max_tree_bytes$max_facts$max_fact_bytes196608Every production size in this report now completes. At 2 MiB of dense markup: 29,845 facts, no diagnostic.
Two things this issue got wrong about the memory model
Both were mine, and both mattered:
$max_tree_bytescosts no memory at all. The host serialises the whole tree into guest memory and grows it to fit before callingextract. The budget bounds work (fuel), not bytes. This issue's claim that it was "the largest single consumer" of memory was simply false.(memory 8)= 512 KiB was never the ceiling. The real one isMEMORY_CEILING_BYTES= 64 MiB. What 512 KiB actually bounded was the fact buffer, because it sits belowsrc_offset— so the knob for the third guard wassrc_offsetall along. A one-line move, not the streaming rewrite this issue contemplated.Generic.xamlscale is reachable. Streaming is not needed.One finding worth more than the budgets
With the byte guard deleted as a mutation, 38,000 facts wrote 271,384 bytes past
src_offset, over the source. It survived only because the walk is forward and the overwritten prefix had already been read. That guard is a memory boundary, not a budget — the new layout keeps 59,334 bytes of margin above the worst-case overshoot.Operator impact
Digest moved to
sha256:7b572f5cc32ffcd5af550900a451bbef37e255804776d7e3e3e71d2dd7d0aa79; the release asset isde.h-dv.xaml-0.2.0.cip. Re-pin, or get a baredigest_mismatch.extraction_identitymoved too, so every project with this package enabled re-extracts its.xamlfiles once — which is the point.Documented in the v0.27.0 release notes as its own upgrade section. Note v0.27.0 is tagged but not yet published — blocked on an unrelated windows-gnu archive fault — so the asset is not downloadable yet.
Eight mutations run, all RED. Closing.
$max_factsis the next wall behind #222 — raising the tree budget alone will turntruncated_treeintonode_limit_reachedon the same files #223