Skip to content

Consolidate remaining BOM stripping after the pnpm reader failures were fixed #905

Description

[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: discussion #560 register.

Kind: bug (with a refactor fix). Source: new finding; review "CRLF/BOM/indent policy" (Part 4.4), register E64.

Problem

No module owns "a leading UTF-8 BOM is encoding, not content". Each reader decides for itself, on main @ 9c43dfc:

Proof by execution. I ran a throwaway unit test twice on 9c43dfc with identical results. It used one pnpm 9 lock with one left-pad@1.3.0 entry, once plain and once with a \u{feff} prefix:

reader (consumer) plain BOM
PnpmLock::entries (inventory, hosted rewrite) 1 entry 1 entry
inventory_project_diagnosed 1 entry 1 entry
PnpmLock::is_pnpm_lock (VEX discovery) true false: "not a pnpm lockfile" diag
sniff_lock_grammar / detect_npm_lock_flavor (vendored router) V9 / Pnpm Err vendor_lockfile_version_unsupported: "has no lockfileVersion in its head … re-lock with pnpm >= 9"
lock_version_major (hosted trust gate) 9 None
workspace::top_level_key("trustLockfile: false") trustLockfile \u{feff}trustLockfile
workspace_lockfile_dir("lockfileDir: ../x") ../x ../x
formats::yarn::is_berry_lock (control) true true

So a single BOM lock gets four answers inside formats::pnpm: it is readable, not a pnpm lock, unversioned, and unsupported. pnpm itself reads it (see #903).

Symptoms

Impact: medium. Each new reader repeats the decision, and every one that forgets it is a new bug. Three bug-hunt issues have hit this in different ecosystems so far.

Proposed change

  1. Add formats::text with split_bom(&str) -> (&str, &str) (one BOM, as parse_json_manifest pins down) and strip_bom. Delete utils::serde::strip_bom, gradle::dsl::strip_bom, formats::yarn::strip_bom and cargo_manifest::split_bom, and re-point their callers.
  2. In formats::pnpm: have head_lock_version, lock_versions, is_pnpm_lock_text and may_need_store_flag read strip_bom(text). Have the pnpm-workspace.yaml splices call top_level_key on a BOM-stripped first line, re-adding the BOM on write. Then make governing_root::workspace_lockfile_dir a top_level_key caller, which deletes its private key-prefix grammar.
  3. Re-point the inline strip_prefix('\u{feff}') / trim_start_matches('\u{feff}') sites to the helper, file by file. Any site that keeps "any number of BOMs" must justify it in a comment.

Size and scope

Acceptance criteria

Dependencies


Backlog review — 2026-10-08

Priority: P1 → P3. The functional pnpm BOM-reader failures were fixed by #909. The remaining broad strip_bom consolidation is maintenance; keep active PR #1117.

The title now describes the remaining scope after the partial fixes. The original report is preserved above for historical context.

Activity

  1. added
    bugSomething isn't working
    arch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)
    on Oct 6, 2026
  2. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Shares root cause with #903, #904: the formats::pnpm lock and workspace readers (head_lock_version, lock_versions, is_pnpm_lock_text, workspace::top_level_key) match a column-0 literal and never skip a leading UTF-8 BOM. Will be fixed together.


    Generated by Claude Code

  3. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (with #903, #904; shared root cause: pnpm lock/workspace readers don't skip a leading UTF-8 BOM). Branch: agent/fix-pnpm-bom-readers. Claim-ID: 2026-10-06T01:20:29Z-3fcf3e


    Generated by Claude Code

  4. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Fix in progress: #909 (steps 1 and 2 of the proposed change; step 3 stays a follow-up).


    Generated by Claude Code

  5. mikolalysenko commented on Oct 8, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue for the architecture refactor routine (highest leverage: step 3, the inline BOM strips onto formats::text. It is the top-scoring candidate that lies entirely in files no open PR changes). Branch: arch-refactor/905-bom-sites. Claim-ID: 2026-10-08T07:55:58Z-1460c5

    The earlier fixer claim (2026-10-06T01:24Z) ended with #909, which merged steps 1 and 2 and left step 3 as a follow-up.

    This slice covers the production sites in files no open PR touches: the hosted and shared requirements lexers, manifest/operations.rs, hosted/npm_manifest.rs, gradle/dsl.rs, policy/socket_yml.rs and the CLI's hosted Pipenv remedy. It also adds a one-sided guard test, so a new file can't spell out its own BOM handling. The sites that sit in open-PR files stay on the guard's pending list for a later slice.


    Generated by Claude Code

  6. 2 remaining items

  7. changed the title [-]pnpm lock and workspace readers don't skip a leading BOM, because BOM handling has no shared helper (4 named copies, ~50 inline strips)[/-] [+]Consolidate remaining BOM stripping after the pnpm reader failures were fixed[/+] on Oct 8, 2026
  8. mikolalysenko commented on Oct 8, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue for the architecture refactor routine (highest leverage: step 3, slice 2: the inline BOM strips in the 11 PENDING_INLINE_BOMS files that no open PR changes now, onto formats::text). Branch: arch-refactor/905-bom-sites-2. Claim-ID: 2026-10-08T18:56:38Z-46a945

    The previous claim (2026-10-08T07:59Z) ended with #1117, which merged as slice 1.


    Generated by Claude Code

  9. mikolalysenko commented on Oct 8, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1160.


    Generated by Claude Code

  10. mikolalysenko commented on Oct 8, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue for the architecture refactor routine (highest leverage: step 3, slice 3: the inline BOM strips in the 4 PENDING_INLINE_BOMS files that no open PR changes, redirect/vlt.rs, vendor/go_mod_edit.rs, vendor/jvm/gradle.rs and vendor/lock_inventory/pypi.rs, onto formats::text). Branch: arch-refactor/905-bom-sites-3. Claim-ID: 2026-10-08T23:59:38Z-061a4b


    Generated by Claude Code

  11. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1191.


    Generated by Claude Code

  12. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue for the architecture refactor routine (highest leverage: step 3, slice 4: the inline BOM strips in the 7 PENDING_INLINE_BOMS files that no open PR changes now, crawlers/npm_crawler.rs, formats/pnpm/lines.rs, hosted/governing_root.rs, patch/redirect/npmrc.rs, patch/redirect/upstream/npm.rs, vex/discover/npm.rs and vex/discover/pypi_other.rs, onto formats::text). Branch: arch-refactor/905-bom-sites-4. Claim-ID: 2026-10-09T13:56:35Z-7070bc

    The remaining 4 files (redirect/mod.rs, upstream/pypi.rs, vendor/yarn_classic_lock.rs, vex/discover/yarn.rs) wait on the open PRs that change them.


    Generated by Claude Code

  13. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1277.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:claimedagent:triagedarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)bugSomething isn't workingpm:pnpmpnpmpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions