Skip to content

Epic: the structure tree still reads levels and addresses from display labels, so changing how a heading looks can change the reported structure #552

Description

@willhea

What's wrong

DeltaTrack works out part of a bill's structure by reading back the heading text it displays. Whether a heading is a division, a title or an account, and which node a passage belongs to, can depend on how that heading happens to be printed. So a change that should only affect how a heading looks can change the structure the report shows, and no test notices. This epic groups the issues that remove that coupling.

Some background, for anyone new to this:

  • The structure tree and its levels. Both pipelines (XML, read from GPO's tagged file, and PDF, read from the printed page) produce a flat list of nodes. src/deltatrack/structure_tree.py rebuilds them into a nested tree. Each tree node has a level: division (the "Division A" of an omnibus), title (TITLE I), major (a department), agency, account (the named pot of money), section, and a few others. docs/bill-structure.md lists them. The tree drives the Sections list in the Full bill view and the tree field of the canonical diff JSON (the published output contract).
  • Display label vs match path. Each node carries two heading chains. display_path is the breadcrumb a reader sees, e.g. Division A: Military Construction… › TITLE I › DEPARTMENT OF DEFENSE. match_path is a normalised twin the diff uses to pair a provision in one version with the same provision in the other. The display form is meant to be free to change. The match path is not.
  • Why reading structure from a display string is fragile. A display label is written for readers. Epic: the viewer doesn't present a bill the way GPO prints it, so readers can't line it up with the published text #778 (the epic to show bills the way GPO prints them) will change many labels on purpose, for example Division A: Header to GPO's DIVISION A—Header. Any code that recovers structure by pattern-matching the label breaks silently when that happens.

#468 (closed 2026-08-04: changing how a division was displayed rewired which sections the diff compared) was the first case. It was fixed by giving each node a division_key built from the source alongside the label. That fix is the pattern for the rest.

What's still coupled (checked 2026-10-05 on develop, d3935ee)

Why it matters

Sub-issues (all open, checked 2026-10-05)

Issue What it is Status
#557 No test checks that a display-only change leaves the tree unchanged Ready to start. Ship with #471
#471 A division's level is read from its display label Ready to start. Latent until #66 lands
#470 A container that adopts a headerless provision takes its level, so agencies are reported as accounts Needs a maintainer's choice of fix
#521 Headings with no text of their own reach no node Fix in #739, a draft PR that keeps every XML heading in the breadcrumb. It's stacked on #736 and on #734 (the open PDF-headings PR). Its 2026-09-30 review asks that a heading not become the parent of sections it doesn't introduce (SEC. 421 of 118-hr-4366 v2 lands under SPENDING REDUCTION ACCOUNT)
#518 Sections printed without a number share one empty address Not addressed

Order of work, and what it unblocks in #778

  1. No test checks that a display-only label change leaves the structure tree unchanged #557 and A division's level is read back from its display label, so showing divisions GPO-style would strip it in both pipelines #471 together, in one PR. The gate is red today because of A division's level is read back from its display label, so showing divisions GPO-style would strip it in both pipelines #471, so it can't land green without the fix. This unblocks Division labels read "Division A: Header" instead of GPO's "DIVISION A—HEADER", in both pipelines #66.
  2. An agency or title that holds a headerless provision takes that provision's level, so it is reported as an account #470. Together with No test checks that a display-only label change leaves the structure tree unchanged #557, this unblocks XML reports show headings in the source file's inconsistent casing, not GPO's per-level casing #53.
  3. Headings with no text of their own (provision groups, agency containers) are missing from the XML reader's breadcrumbs #521 lands with Show every heading the XML tags in its breadcrumb, and measure the ledger on each kind of bill that appropriates #739. Sections printed without a section number share one empty address, so a bill with no divisions can't tell them apart #518 is independent.

Done when

  • Every sub-issue is closed or explicitly deferred with a reason.
  • The tree's division level comes from the source in both pipelines, not from the label text.
  • A render-invariance gate runs in CI and was shown red before it went green.
  • No container's level is overwritten by a provision it adopts.
  • Decide (ADR or a comment here) how containers get a level when the source can't type them. The PDF pipeline feeds the same tree builder without XML tags, so the contract has to cover it.

Scope

Where to start

Unverified

History

Filed 2026-08-06 after a review found #470, #471, #518 and #521 to be one mechanism. The tree was first built (9a35800, 2026-06-29) to make a diff navigable, and nesting by breadcrumb was the quickest way to get a hierarchy on screen. Levels were later read as structure without the model being redefined for that.

Activity

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

    epicContainer of sub-issues; tracked on the Roadmap, not the working board

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions