Lossless Changelog
component

Citation Assembly Hardening + Avalanche Goes Firm-Private

Three reproducible citation-assembly bugs squashed.

Michael StatonMusic Editoraugmented with Claude Code (Opus 4.7)
fix(citation-assembly): cascading-replacement, inline-with-colon, and
                        hyphen-in-citation-id bugs
fix(css): TOC heading no longer stranded by page-break-inside: avoid
            on a too-tall nested <ol>
move(brand): Avalanche brand bundle → io/avalanche/ firm-private,
              symlinked back from templates/brand-configs/
new(skill): context-v/skills/manage-memo-citations/SKILL.md
new(skill): context-v/skills/adhoc-edits-workflow/SKILL.md
new(script): scripts/fix_citation_positioning.py
bump(submodule): io/avalanche → ChromaDB v0.0.11

Three reproducible citation-assembly bugs found during the ChromaDB
v0.0.11 build. Each had a single-line root cause and a single-line
fix; the trick was teasing them apart.

* src/agents/citation_assembly.py — cascading-replacement bug. The
  prior renumber_inline_citations used a sequential str.replace loop:
  [^notes1] → [^1] could then be caught by the next rule [^1] → [^3],
  collapsing many distinct citations to a single number. Replaced
  with a single-pass callable-based re.sub so each marker is visited
  exactly once.

* src/agents/citation_assembly.py — inline-citation-with-colon bug.
  The prior `(?!:)` negative lookahead excluded end-of-sentence
  inline citations like "claims [^X]:" as if they were definitions,
  leaving them as orphan markers. The replacer now distinguishes
  definitions structurally — must be `[^X]:` at the start of a line
  followed by a space — from inline citations.

* src/agents/citation_assembly.py — hyphen-in-citation-id bug. The
  citation ID regex was `[a-zA-Z0-9_]+`, which silently dropped
  semantic IDs like `[^github-2018]`, `[^cursor-2026]`,
  `[^mongodb-fy26]`. Widened to `[a-zA-Z0-9_-]+` across all five
  places the pattern appears.

* templates/base-style.css — WeasyPrint was stranding the "Table of
  Contents" heading on a page by itself with the list pushed to the
  next page. Root cause: the markdown TOC is a nested <ol>, so the
  outermost <li> (containing every subsection underneath) is taller
  than a full page, and `page-break-inside: avoid` on `li` prevented
  it from breaking. Added a more-specific override
  (`#table-of-contents + ol ... { page-break-inside: auto }`) so the
  TOC list flows immediately after the heading.

* templates/brand-configs/brand-avalanche-config.yaml is now a
  symlink to ../../io/avalanche/configs/brand-avalanche-config.yaml.
  Matches the dark-matter / hypernova convention. Canonical trademark
  assets live in io/avalanche/configs/trademarks/ and the brand
  config references them by local path; the duplicates in
  templates/trademarks/trademark__Avalanche--*.{png,webp} are removed.

* context-v/skills/manage-memo-citations/SKILL.md — codifies the
  "foundational citations live in 1-research/, not in section files"
  rule, with a theme→file mapping table, the semantic-ID-over-numeric
  convention, and a recovery playbook for when orphans appear.

* context-v/skills/adhoc-edits-workflow/SKILL.md — codifies the
  four-step ad-hoc edit pipeline (research → sections → assemble →
  export), with six anti-patterns captured from this session
  (editing the final draft directly, adding prose before the citation
  exists, hand-typed numeric IDs, skipping assembly before export).

* scripts/fix_citation_positioning.py — applies two house-style
  rules: inline citations belong AFTER terminal punctuation, not
  before; and same-citation-id repeats in one paragraph collapse to
  a single marker at the end of the paragraph. Obsidian-compliant
  single-space-before-bracket throughout.

Also included:
* io/avalanche submodule pointer advanced to the ChromaDB v0.0.11 commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Changelog — 2026-05-26_01

What broke and what got fixed

The ChromaDB v0.0.11 build surfaced three bugs in citation_assembly.py that had been silently masking each other. The cascading-replacement loop turned distinct citations into duplicates; the citation-with-colon exclusion misread legitimate inline citations as definitions; the hyphen-rejecting regex silently dropped any semantic ID that wasn't purely alphanumeric. Run the assembler on a memo with semantic IDs like [^chroma-site] and you'd see fewer citations in the output than in the input — and not in a way that traced back cleanly to one cause.

Each fix is small in isolation. The lesson is in the diagnosis order: once the regex was widened to accept hyphens, the cascading-replacement behavior became visible because there were now more distinct IDs to collapse, and the inline-with-colon issue became visible because the definitions vs. inline distinction had to be rebuilt. Fix one, surface the next.

The WeasyPrint TOC issue is unrelated but landed in the same commit because it surfaced in the same export run. A nested <ol> whose outermost <li> is taller than a page can't honor page-break-inside: avoid, and WeasyPrint deals with that by stranding the heading rather than overriding the rule. The fix is one selector specific enough to win over the global rule.

Avalanche goes firm-private

The brand config and trademark assets for Avalanche have been moved into io/avalanche/configs/ and io/avalanche/configs/trademarks/, following the same firm-private pattern as dark-matter, hypernova, emerge, and alpha-partners. The orchestrator-side templates/brand-configs/brand-avalanche-config.yaml is now a symlink pointing back into the submodule; the asset paths inside the YAML reference the canonical copies under io/avalanche/configs/trademarks/.

This is the standard form for firms whose visual identity counts as their IP. The orchestrator can read the symlinked YAML at export time without holding the assets itself.

Two new skills

The citation rules and the ad-hoc edit pipeline aren't novel — they've been operative on every memo run for weeks — but they hadn't been written down. Two context-v/skills/ entries fix that. The next session that opens a section file to "just add a citation real quick" can read manage-memo-citations/SKILL.md first and skip the foundational-vs-section-citation footgun.

What didn't change

  • No outline YAML, no template structure, no agent prompt.

  • The assembler's interface is unchanged; only the renumbering pass and the regex character class moved.

  • No client-private content described here. The io/avalanche submodule pointer bump is a one-liner, not a paragraph.

What's next

  • Run the assembler over a memo that historically had the cascading-replacement signature (avalanche v0.0.10 → v0.0.11) and diff citation counts to confirm.

  • The new skills will get exercised on the next memo run; expect at least one anti-pattern to need a follow-up addition.

References

  • src/agents/citation_assembly.py — the changed file

  • templates/base-style.css — the TOC override

  • context-v/skills/manage-memo-citations/SKILL.md

  • context-v/skills/adhoc-edits-workflow/SKILL.md

  • scripts/fix_citation_positioning.py

  • apps/memopop-orchestrator/CLAUDE.md §"Changelog scope" — why the avalanche pointer bump is a one-liner, not a paragraph