{
  "audit_version": 1,
  "audit_date": "2026-05-29",
  "generated_against_triage_summary": {
    "passes_now": 308,
    "error_contract_ok": 94,
    "fails_needs_feature": 0,
    "fails_needs_error_path": 0
  },
  "scope": "Sweep of crates/panache-parser/src/parser/yaml/events.rs for projector logic that compensates for scanner / parse_stream gaps. Each site is classified by ownership target. Predictions are inspection-only; the next planning round confirms regressions empirically.",
  "classifications": {
    "PARSER_GAP": "Scanner mis-tokenizes. Fix target: scanner.rs.",
    "CST_FIDELITY_BUG": "Scanner OK; the parser collapses tokens into a node shape that loses a structural distinction. Fix target: parser.rs (parse_stream).",
    "LEGITIMATE_PROJECTION": "Within the skill's stated projector remit (scalar cooking, folding, event reconstruction from a correct CST).",
    "ACKNOWLEDGED_DEBT": "Listed in SKILL.md or scanner-rewrite.md as residual cutover work.",
    "RESOLVED": "Confirmed dead at audit time; helper deleted with no behavior change."
  },
  "sites": [
    {
      "id": "flow_kv_split",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 593,
      "summary": "Hand-rolled char_indices loop with single/double-quote tracking to locate a `:` key-indicator inside a flow-sequence item's text. Reached after project_flow_sequence_items_cst assembles item_text from selected tokens.",
      "classification": "CST_FIDELITY_BUG",
      "ownership_target": "parser.rs",
      "rationale": "Confirmed load-bearing: ablation regresses QF4Y (implicit flow-map entry inside `[...]`). The scanner emits YAML_COLON tokens for the boundary, but parse_stream doesn't surface flow-map-entry structure inside YAML_FLOW_SEQUENCE_ITEM, so the projector has to re-locate the `:` in the item's joined text. Promoting flow-seq items to carry explicit YAML_FLOW_MAP_ENTRY children (or YAML_KEY/YAML_VALUE tokens) lets the projector walk structure instead of re-tokenizing text. Same pattern as decompose_scalar (projector joins-then-reparses), but here the structural information genuinely isn't in the CST at the item level.",
      "predicted_regressions_if_ablated": ["QF4Y", "87E4", "L9U5", "LQZ7", "9MMW", "6BFJ", "X38W"],
      "seed_case_ids_in_comments": ["87E4", "L9U5", "LQZ7", "9MMW", "6BFJ", "X38W"],
      "depends_on": [],
      "blocks": [],
      "fix_size_estimate": "M",
      "test_strategy": "Parser-crate CST snapshot for QF4Y / 87E4 verifying YAML_FLOW_SEQUENCE_ITEM gains KEY/VALUE children; projection-parity for 9MMW (JSON-like key adjacency, the special case at l.624-628)."
    },
    {
      "id": "simple_flow_sequence_items",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 1040,
      "summary": "Comma-split a `[...]` flow-sequence text into items with quote-nesting tracking. Three callers: doc-level descendant-search fallback, explicit-key flow-seq projection, and project_block_map_entry_value text fallback.",
      "classification": "RESOLVED",
      "resolution": "Dead at all three call sites. Ablated by making the function return None, full yaml suite stayed green (17/17). The structured flow-collection paths (`project_flow_collection_node`, `project_flow_collection_node_with_anchor`) cover every allowlisted case. Helper and all three call sites removed.",
      "ownership_target": null,
      "predicted_regressions_if_ablated": [],
      "seed_case_ids_in_comments": [],
      "depends_on": [],
      "blocks": [],
      "fix_size_estimate": "S",
      "test_strategy": "n/a — removed."
    },
    {
      "id": "fold_block_scalar_raw",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 1348,
      "summary": "137-line YAML 1.2 §8.1.3 block-scalar folding pipeline: parse indicator, infer content_indent from raw text, strip prefix, fold/chomp.",
      "classification": {
        "folding_and_chomping": "LEGITIMATE_PROJECTION",
        "content_indent_inference": "CST_FIDELITY_BUG"
      },
      "ownership_target": "parser.rs",
      "rationale": "Fold/chomp rules (l.1436-1483 plus fold_greater_lines) are spec-defined post-processing of already-correct content and belong in the projector. The content-indent inference at l.1372-1380 walks the raw body text counting leading spaces; the scanner already tracks the block-scalar's content indent via the indent stack when it emits the body tokens. Attaching a content_indent attribute to the YAML_BLOCK_SCALAR node (via parse_stream reading a scanner-tracked value) lets fold_block_scalar_raw stop scanning lines to recompute it.",
      "predicted_regressions_if_ablated": ["T26H", "JEF9/01", "JEF9/02", "Y79Y/001", "R4YG", "MJS9", "DK3J", "7T8X"],
      "seed_case_ids_in_comments": ["T26H", "JEF9/01", "JEF9/02", "Y79Y/001", "R4YG", "MJS9", "DK3J", "7T8X"],
      "depends_on": [],
      "blocks": [],
      "fix_size_estimate": "L",
      "test_strategy": "Attach content_indent to YAML_BLOCK_SCALAR; CST snapshots for T26H, JEF9/01 to verify the attribute; projection parity for the eight seed cases to verify folding output unchanged."
    },
    {
      "id": "decompose_scalar",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 2715,
      "summary": "Peel leading `&anchor` and `!tag` prefixes off a scalar's text via string scanning; returns (anchor, long_tag, rest). Called from ~12 sites in events.rs to recover node properties from scalar text.",
      "classification": "LEGITIMATE_PROJECTION",
      "ownership_target": null,
      "rationale": "Not a scanner gap. Ablation by returning (None, None, text.trim()) regresses JS2J — but the JS2J CST already has YAML_ANCHOR as a dedicated token. The 'fusion' is **self-inflicted by the projector**: `project_block_map_entry_value` (and several other call sites) join YAML_SCALAR + YAML_ANCHOR + WHITESPACE tokens into a single `value_text` string, then peel the anchor back off via decompose_scalar. The scanner is fine; the projector chose a join-then-reparse pattern over a token-walking pattern. Refactoring the call sites to walk tokens and extract anchor/tag directly would let decompose_scalar die, but that's a projector-style choice, not a parser gap — within the projector's stated remit per SKILL.md.",
      "predicted_regressions_if_ablated": ["JS2J"],
      "seed_case_ids_in_comments": [],
      "depends_on": [],
      "blocks": [],
      "fix_size_estimate": "L",
      "test_strategy": "Follow-up refactor (not required for the audit conclusion): walk tokens in each scalar-projection site; remove decompose_scalar."
    },
    {
      "id": "rebuild_anchor_alias_with_trailing_colon",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 3029,
      "summary": "W5VH workaround: when a block-map-value's leading YAML_SCALAR starts with `&` or `*` and is immediately followed by a YAML_BLOCK_MAP with an empty first-entry key, rebuild the anchor/alias name to include the trailing colon.",
      "classification": "RESOLVED",
      "resolution": "Dead code at audit time. The scanner already emits YAML_ANCHOR / YAML_ALIAS tokens that include the trailing `:` (verified by inspecting the CST for W5VH directly: `YAML_ANCHOR@3..16 \"&:@*!$\\\"<foo>:\"`). The comment naming a scanner bug was stale; scan_anchor_name in scanner.rs:1285 already breaks only on whitespace and flow indicators, so `:` is consumed as a name char. Helper and its sole call site removed; yaml_allowlist_projected_event_parity passes unchanged.",
      "ownership_target": null,
      "predicted_regressions_if_ablated": [],
      "seed_case_ids_in_comments": ["W5VH"],
      "depends_on": [],
      "blocks": [],
      "fix_size_estimate": "S",
      "test_strategy": "n/a — removed."
    },
    {
      "id": "residual_splice_26dv",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 3117,
      "summary": "26DV / BU8L / 6JWB residual-splice path inside project_block_map_entry_value (and a parallel path inside project_block_sequence_items): when a value/item had a nested YAML_BLOCK_MAP and the leading YAML_SCALAR carried fused node properties + residual text, peel the properties and splice the residual as the first entry's empty key.",
      "classification": "RESOLVED",
      "resolution": "Dead code at audit time on both splice paths (block-map-value and block-sequence-item). CST probes for all three seed cases show the scanner already emits YAML_ANCHOR / YAML_ALIAS / YAML_TAG as dedicated tokens, and the nested YAML_BLOCK_MAP's first entry has a real (non-empty) key, so the splice condition (`!residual.is_empty() && first_entry_empty`) never fires. Ablated both branches under `if false`, ran the full yaml suite (17/17 pass), then deleted the branches and the now-unused `project_spliced_nested_map` helper. `extract_leading_node_properties` still has one caller (`map_open_event_for_value`) that discards `residual` — its return type can be simplified as a follow-up.",
      "ownership_target": null,
      "predicted_regressions_if_ablated": [],
      "seed_case_ids_in_comments": ["26DV", "BU8L", "6JWB"],
      "depends_on": [],
      "blocks": [],
      "fix_size_estimate": "S",
      "test_strategy": "n/a — removed."
    },
    {
      "id": "extract_tagged_block_scalar",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 1141,
      "summary": "Fallback that re-reads a fused `<node-properties> <block-scalar>` value (e.g. `folded: !foo >1\\n value`, M5C3 / Z67P) when the bare block-scalar detector misses it because the tag is embedded in the scalar token.",
      "classification": "RESOLVED",
      "resolution": "Dead code at audit time. CST probes for M5C3 and Z67P show the scanner now emits `YAML_TAG` as a dedicated sibling token of the block-scalar `YAML_SCALAR`, so `extract_block_scalar_body` (the primary path) already handles them — it walks the YAML_TAG sibling and splices it into the prefix. Ablation under `if false` left the full yaml suite green (17/17). Helper and call site removed; `fold_block_scalar_raw` doc-comment trimmed to drop the stale [`extract_tagged_block_scalar`] reference.",
      "ownership_target": null,
      "predicted_regressions_if_ablated": [],
      "seed_case_ids_in_comments": ["M5C3", "Z67P"],
      "depends_on": [],
      "blocks": [],
      "fix_size_estimate": "S",
      "test_strategy": "n/a — removed."
    },
    {
      "id": "absorb_anchor_or_tag_text_fallback",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 2555,
      "summary": "Inside absorb_anchor_or_tag, a YAML_SCALAR arm falls back to text-prefix detection (`trimmed.strip_prefix('&')`, `trimmed.starts_with('!')`) when the token is a scalar rather than a dedicated YAML_ANCHOR / YAML_TAG.",
      "classification": "RESOLVED",
      "resolution": "Dead. The scanner emits YAML_ANCHOR / YAML_TAG / YAML_ALIAS as dedicated tokens for every case in the allowlist, so the YAML_SCALAR arm never fires. Ablation under the `_ => {}` catch-all left the full yaml suite green (17/17). The YAML_TAG arm still has a residual `strip_prefix('&')` check on YAML_TAG tokens whose text starts with `&` — also suspect, but out of scope for this commit; tracked as follow-up.",
      "ownership_target": null,
      "predicted_regressions_if_ablated": [],
      "seed_case_ids_in_comments": [],
      "depends_on": [],
      "blocks": [],
      "fix_size_estimate": "S",
      "test_strategy": "n/a — removed."
    },
    {
      "id": "find_block_scalar_kv_split",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 2202,
      "summary": "Locate a `:` key-indicator inside a block-context scalar text, honoring a leading quoted body. Called from project_block_sequence_items for inline-map sequence items (`- key: value`).",
      "classification": "RESOLVED",
      "resolution": "Dead. parse_stream already wraps inline-map sequence items in a nested YAML_BLOCK_MAP child, so the fallback structured branch handles every allowlisted case. Ablation by returning None left the yaml suite green (17/17). Helper, call site, and a stale comment referencing decompose_scalar tag recovery removed.",
      "ownership_target": null,
      "predicted_regressions_if_ablated": [],
      "seed_case_ids_in_comments": [],
      "depends_on": [],
      "blocks": [],
      "fix_size_estimate": "S",
      "test_strategy": "n/a — removed."
    },
    {
      "id": "fold_plain_document_lines",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 483,
      "summary": "Fold a plain-scalar document's lines (raw.split('\\n'), join with spaces) when the document body is a single multi-line plain scalar.",
      "classification": "RESOLVED",
      "ownership_target": "n/a",
      "resolution": "Helper still present but the framing was wrong — the scanner already emits multi-line plain scalars as a single token (`Token::Scalar(Plain)` spanning the whole body). Nothing is re-stitched. Local-version folding logic moved into `cooking::cook_plain`; `fold_plain_document_lines` now delegates."
    },
    {
      "id": "collect_doc_scalar_text_with_newlines",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 457,
      "summary": "Concatenate document-level YAML_SCALAR / YAML_ANCHOR / YAML_ALIAS / NEWLINE token text in order, skipping directive lines.",
      "classification": "RESOLVED",
      "ownership_target": "n/a",
      "resolution": "Renamed to `collect_doc_scalar_source`. Not a re-stitch — the scanner already emits scalars as single tokens. This collector aggregates raw token *source* (scalar + adjacent anchor/alias/newline tokens) for the multi-line-quoted projection path; cooking is delegated to `super::cooking`."
    },
    {
      "id": "collect_value_scalar_text_with_newlines",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 3366,
      "summary": "Block-map-value-bounded version of the doc-level scalar source collector.",
      "classification": "RESOLVED",
      "ownership_target": "n/a",
      "resolution": "Renamed to `collect_value_scalar_source`; same scope as above (aggregates raw source for multi-line quoted projection; cooking delegated to `super::cooking`)."
    },
    {
      "id": "quoted_val_event_multi_line",
      "file": "crates/panache-parser/src/parser/yaml/events.rs",
      "anchor_line": 700,
      "summary": "Wrap a multi-line quoted scalar's cooked value in the yaml-test-suite `=VAL '` / `=VAL \"` event prefix with `escape_for_event` applied.",
      "classification": "RESOLVED",
      "ownership_target": "n/a",
      "resolution": "Now a thin event-formatting wrapper. The fold/decode pipeline (`fold_quoted_inner`, `strip_quoted_wrapper`, `decode_double_quoted_inner`) was extracted to `cooking.rs` as `cook_single_quoted_multi_line` / `cook_double_quoted_multi_line`; this helper just dispatches to them and adds the event prefix."
    }
  ],
  "sequencing": [
    "flow_kv_split",
    "fold_block_scalar_raw"
  ],
  "sequencing_rationale": "Scanner-side anchor/alias/tag emission fixes go first (rebuild_anchor_alias_with_trailing_colon, residual_splice_26dv, extract_tagged_block_scalar) because they are the smallest scanner state-machine changes and they unblock the most downstream projector workarounds (absorb_anchor_or_tag_text_fallback, decompose_scalar). After those land, the projector's anchor/tag/alias text-peel paths can be confirmed dead via ablation. Then move to parse_stream shape work: flow_kv_split (add KEY/VALUE children to YAML_FLOW_SEQUENCE_ITEM), which unblocks the simple_flow_sequence_items dead-code confirmation, then find_block_scalar_kv_split (add implicit-map structure to YAML_BLOCK_SEQUENCE_ITEM). Block-scalar content-indent attachment (fold_block_scalar_raw_indent_inference) goes last because it touches the YAML_BLOCK_SCALAR node shape that the earlier fixes also use, and the cooking portion of fold_block_scalar_raw stays in events.rs regardless.",
  "open_questions": [
    "How much of decompose_scalar's remaining work is the multi-line-scalar acknowledged debt vs. genuinely single-line residuals? Settled by ablation after the three scanner fixes land.",
    "Does simple_flow_sequence_items have any live callers after the YAML_FLOW_SEQUENCE structured branch took precedence? Settled by ablation; expected dead.",
    "find_block_scalar_kv_split — is the right fix a new YAML_BLOCK_MAP_ENTRY inside YAML_BLOCK_SEQUENCE_ITEM, or a flatter shape with KEY/VALUE tokens? Needs a parse_stream design decision in its own planning round.",
    "For fold_block_scalar_raw content-indent attachment — does the scanner currently surface content_indent anywhere usable, or does it need a new field on the block-scalar token? Check scanner.rs's block-scalar emission state."
  ],
  "what_this_audit_does_not_cover": [
    "Ablation runs to confirm predicted regressions.",
    "Edits to scanner.rs, parser.rs, validator.rs, events.rs.",
    "New SyntaxKind variants.",
    "Changes to triage.json, allowlist.txt, or any fixture."
  ]
}
