Skip to content

Build hygiene: unrendered bcf-0001 root + invalid XML on titles containing '<' #31

Description

@bmorphism

Two build-hygiene findings surfaced while building the forest from current branches (forester 4.3.1) and validating the output with an independent strict XML parser. Both are pre-existing on main (not introduced by any open PR) and are stable across PR #25 and #30.

1. Forest root bcf-0001 is never rendered → dangling transclusions

forester build emits no output/bcf-0001.xml, yet bcf-0001 is referenced via \transclude (e.g. from mat-0001 and the generated index). This is a genuine /referential-integrity hole: the root of the forest has no page.

Evidence (identical in #25 and #30 builds):

transclude  mat-0001 -> bcf-0001   (target not in rendered output)
transclude  index    -> bcf-0001   (target not in rendered output)

Suggested fix: ensure the root tree is rendered (give bcf-0001 a body / confirm it isn't filtered out of output/), or point the root config at a tree that is rendered.

2. Titles containing < produce invalid XML

When a tree title contains a literal <, forester escapes the element text correctly (&lt;) but leaves the text="…" attribute unescaped, yielding non-well-formed XML. A strict parser (Python xml.etree) rejects these; ~0.7% of machine-generated unstable-* trees fail this way.

Example (unstable-17488.xml):

<fr:title text="Immediate (Distance < 2.0)">Immediate (Distance &lt; 2.0)</fr:title>

The raw < in text="…" is invalid. Authored bcf/bci/horse trees are unaffected (none currently have < in titles): 88/88 (PR #25) and 103/103 (PR #30) core trees are well-formed. This is a latent trap — any future title with < will emit invalid XML at build time.

Suggested fix: escape </&/" in the text= attribute (upstream forester), or a post-build sanitizer pass over output/*.xml.

Reproduce

./forester build bci-forest.toml            # forester 4.3.1
# 1) ls output/bcf-0001.xml  -> missing
# 2) python3 -c "import xml.etree.ElementTree as ET,glob; \
#      [print('BAD',f) for f in glob.glob('output/*.xml') \
#       if (lambda: (ET.parse(f),False)[1] if True else None)]" 2>&1 | head

Context: found during a functorial-diff review of #25/#30 (the dangling cocycle is conserved across both — neither PR fixes or worsens it).

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions