Skip to content

Cover-page subtitle SHA tracks outer-builder HEAD, not the content ref #95

Description

@asluk

Symptom

The cover-page subtitle v<version> (<sha>) reads the short HEAD of the outer repo checkout that ran doc_build, regardless of which content ref is actually being rendered. When the outer HEAD happens to match the content ref (e.g., a regular build_docs.yml checkout of the same ref it builds), the subtitle looks correct. As soon as outer HEAD diverges from the content ref, the subtitle is wrong on every produced PDF.

The most common case this surfaces in is --diff <from> <to>, where all three rendered PDFs (before / after / diff) get the same outer-HEAD SHA on the cover. Notably this includes after_<to_short>.pdf — the file we typically rename to aousd_core_spec_<version>_<to_short>.pdf and ship as the standalone draft candidate. So the bug isn't cosmetic to the visual diff PDF; it taints the headline spec artifact too.

Concrete repro from a recent core-spec-wg run:

File Filename SHA Cover-page SHA
aousd_core_spec_1.1.0_e98ceb3.pdf (regular build, outer HEAD = e98ceb3) e98ceb3 e98ceb3 ✓
aousd_core_spec_1.1.0_60e060e.pdf (after-render from --diff, outer HEAD = 37cccfc) 60e060e 37cccfc ✗
aousd_core_spec_1.1.0_diff_e98ceb3_to_60e060e.pdf (diff render, same workflow) e98ceb3→60e060e 37cccfc ✗

In other words: filenames are correct because generate_combined_diff resolves from_ref / to_ref through commit_hash(ref, repo_root, short=True) (doc_builder.py:654-655), but the cover-page subtitle never sees those.

Repro

git checkout <some_branch_with_HEAD=A>
pixi run main build --diff <ref_with_SHA=B> <ref_with_SHA=C>
# All three of build/diff_from/before_*.pdf, build/diff_to/after_*.pdf,
# build/diff/diff_*.pdf show "v<ver> (<short of A>)" on the cover.
# Expected: before -> short of B, after -> short of C, diff -> some
# representation of B->C.

Root cause

_render_combined is the only renderer; it's called three times in diff mode (doc_builder.py:259-282), all on the outer builder. It computes the subtitle by calling self.get_subtitle(spec):

# doc_builder.py:308-327
def _render_combined(self, args, combined, filename, *, ..., output_dir=None, from_pretty=None, to_pretty=None):
    ...
    spec = self.get_metadata_defaults_file()
    subtitle = self.get_subtitle(spec)

get_subtitle reads HEAD of the outer repo, ignoring whatever ref the combined markdown was actually flattened from:

# doc_builder.py:917-922
def get_subtitle(self, defaults_file_path: Path):
    with open(defaults_file_path, \"r\") as f:
        spec_data = yaml.load(f, Loader=yaml.SafeLoader)
        commit = git_utils.commit_hash(\"HEAD\", self.get_repo_root(), short=True)
        subtitle = f\"v{spec_data['metadata']['version']} ({commit})\"
    return subtitle

_build_combined_for_ref correctly creates an inner builder rooted at the ref's worktree (doc_builder.py:638-647), but that inner builder is only used for the markdown flatten/preprocess step. The PDF render that produces the cover still runs on the outer builder.

This is the same shape as the prior #90 bug (parent args not forwarded to per-ref renders) that #85 fixed.

Suggested fix

Thread a per-render subtitle through _render_combined so each render gets the SHA of its own content ref:

  • before -> from_ref
  • after -> to_ref
  • diff -> to_ref for the headline SHA (or a <from_short> -> <to_short> form, since from_pretty / to_pretty are already passed through)
  • regular build -> unchanged (defaults to current behavior of reading outer HEAD)

Two reasonable shapes:

  1. Optional subtitle: Optional[str] = None parameter on _render_combined; get_subtitle retained as the default when caller doesn't pass one. Diff path computes the right value per render.
  2. Optional subtitle_ref: Optional[str] = None parameter; get_subtitle(spec, ref=subtitle_ref or \"HEAD\"). Slightly more invasive on get_subtitle but keeps the formatting logic in one place.

Either is fine; #2 keeps the subtitle string format ("v{version} ({short})") authoritative in get_subtitle.

Impact

Any consumer of the diff workflow (e.g., core-spec-wg's build_diff.yml) ends up with wrong SHAs on:

  • the diff PDF cover (visible to anyone scanning red/green deltas), AND
  • the after-render PDF cover (visible on the file we ship as the standalone candidate spec, since that's what we rename and distribute)

Filename and rendered content are correct; only the embedded subtitle is wrong. Because the after-render is what people use as the de facto authoritative artifact, the misleading SHA propagates anywhere that PDF is shared.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions