arborist/docs/tickets/ticket-000041-citation-aliases-table.md
russell@unturf.com 6f1dbed82e
docs: refresh stale citation-alias counts (40 → 54 → 74) across 4 surfaces
Stale-marker check on #000038 surfaced the same drift pattern as
yesterday's earlier sweeps: documentation got snapshotted at
multiple points during the 2026-05-10 alias-substitution sprint
and four surfaces ended up disagreeing about the live row count.

Live count via ``arborist --shards-dir SHARDS_DIR alias citation
list | jq length``: **74** rows.

Stale snapshots refreshed:

- ``docs/tickets/ticket-000041-citation-aliases-table.md`` line 3
  (status header): "40 rows live by 2026-05-10" → "74 rows live
  as of 2026-05-10" with the 40 → 54 → 74 progression noted.
- ``docs/tickets/ticket-000041-citation-aliases-table.md`` line 205
  (§7 close-out): "54 rows live" → "74 rows live" + progression.
- ``docs/tickets/ticket-000038-phase-4-content-acquisition.md``
  line 3 (status header): "(54 rows)" → "(74 rows live as of
  2026-05-10; count grew 40 → 54 → 74 across the day)".
- ``docs/tickets/ticket-000038-phase-4-content-acquisition.md``
  §7 line: "54 rows live (decision_by=...)" → "74 rows live as
  of 2026-05-10".
- ``docs/tickets/ticket-000038-phase-4-content-acquisition.md``
  references line 274: "(40 → 54 rows live)" → "(40 → 54 → 74
  rows live across 2026-05-10)".
- ``docs/TICKETS.md`` line 70 (#000041 row): "54 rows live" →
  "74 rows live as of 2026-05-10 (count grew 40 → 54 → 74; ...)".
- ``docs/TICKETS.md`` line 73 (#000038 row): "(54 rows in #000041
  + 13 rows in #000042)" → "(74 rows in #000041 + 13 rows in
  #000042)".
- ``CLAUDE.md`` line 145: "40 fox-decided citation-aliases" →
  "74 fox-decided citation-aliases" + a note about the live-count
  command for future readers ("alias counts grow as fox adds
  substitutions — ``arborist alias citation list | jq length``
  for live count").

The refreshed numbers will themselves go stale next time fox
adds rows. Each surface now also notes the live-count
mechanism so future readers can check current state without
having to refresh the doc — same pattern as today's earlier
"track Phase 3 fire rate via the audit-line tail" closure
note. The CLAUDE.md note specifically tells future readers
where to look up the live count.

Term-aliases count (13 rows in #000042) was verified live and
matches all surfaces — no refresh needed there.

#000038 phases otherwise clean: §7 close-out + §8 residue
already accurately reflect current state (92/92 records resolve;
4 residue items genuinely-open). No phase status drift to fix.

Hygiene: docs-only commit, no code touched. No test impact.
2026-05-10 10:41:07 -04:00

10 KiB
Raw Blame History

Ticket #000041 — Citation-aliases table

Status: closed · mechanism landed 2026-05-09; 74 rows live as of 2026-05-10 (fox decisions across pillars I/II/III/V/VI/VII/IX; count grew 40 → 54 → 74 across the day as fox added substitutions; check live via arborist --shards-dir SHARDS_DIR alias citation list); driver of the 92/92 (100%) claim-pack warrant resolution under #000031 Opened: 2026-05-09 Scope: Add an arborist citation_aliases table that maps a claim-pack record's original source_reference string to a substitute citation (a different textbook with comparable content), read at warrant-resolve time. Closes the gap surfaced in #000038 §3.2: when a cited textbook is proprietary or unavailable, fox decides on a PD substitute (e.g., Hilbert-Ackermann 1928 instead of Mendelson 1997), and the warrant resolver looks up the aliased citation when matching against shard textbooks. Audience: future blackops shifts running warrant-promotion on records whose original cite isn't ingestable. Hard constraint: alias decisions are auditable. Each alias row carries a decision_at timestamp + decision_by identifier (free-string, e.g. "fox 2026-05-12"); silent fabrication of substitute citations is not allowed.


1. Problem statement

#000038 Phase 4 surfaces 9 cited textbooks not yet in the shard cluster. Some are proprietary (Mendelson, Enderton, Stanley, Brualdi, Knuth, Goldstein) and licensing decisions take time. When a textbook stays out, the records that cite it stay at ANCHOR-WARRANTED.

Sometimes a comparable PD work exists. #000038 §3.1 documents:

  • Mendelson + Enderton — Hilbert-Ackermann 1928 Principles of Mathematical Logic covers the same ground; PD by age.
  • Goldstein — Newton's Principia (already ingested) is a substitute for the classical mechanics axiom citations that point to Goldstein for "Newton's first law".

For these, the warrant resolver could light up if it knew about the substitution. But silently rewriting the claim-pack record's source_reference field would break audit traceability. The right answer is an explicit alias table.

2. Design choices

2.1 Schema

CREATE TABLE IF NOT EXISTS citation_aliases (
    original_ref       TEXT NOT NULL,        -- exact source_reference string
    substitute_ref     TEXT NOT NULL,        -- replacement citation string
    substitute_authors TEXT NOT NULL,        -- JSON array
    substitute_title   TEXT NOT NULL,
    decision_at        INTEGER NOT NULL,     -- unix timestamp
    decision_by        TEXT NOT NULL,        -- e.g., "fox 2026-05-12"
    decision_rationale TEXT,                 -- e.g., "Hilbert-Ackermann 1928 covers same propositional-logic axioms"
    PRIMARY KEY (original_ref, substitute_ref)
);

Stored at the shards-cluster level (one alias table per shard or one global at ~/.arborist/aliases.db?). The global path keeps mesh-syncing simpler — peers can share the alias table without per-shard duplication.

2.2 Resolver wiring

The resolver currently:

  1. Reads claim-pack record → parses source_reference
  2. Walks shards, matches title + author against the cited work
  3. Writes derivations row binding record → surface chunk

With aliases:

  1. Reads claim-pack record → parses original source_reference
  2. Looks up original source_reference in citation_aliases
  3. If alias found, also tries the substitute citation alongside the original (resolver tries both; either may produce a match)
  4. When the alias matches, the derivations row's process_id carries "warrant-resolver-v1+alias" — distinguishes alias-resolved chains in the audit trail
  5. Existing chain-of-custody render tail stays the same

2.3 Alias scope — per-record or per-citation-string?

A. Per exact source_reference string. Keys on the literal string. Brittle to whitespace / punctuation changes; safe to share.

B. Per (author last name, normalized title) tuple. Looser match; could catch "by Mendelson" and "Elliott Mendelson 1997" as the same entry. Risk: over-matching unrelated records.

C. Per (citation_id, alias_id) opaque IDs. Requires content-addressed citation IDs (we don't have those today).

→ A for now. Strings ARE the data fox writes the alias against. Key-collision is acceptable — the resolver tries both the original AND the alias; if the original happens to match some other shard, both chains land.

2.4 CLI surface

arborist alias add ORIGINAL --substitute SUB --by FOX [--rationale RAT]
arborist alias list [--filter X]
arborist alias remove ORIGINAL --substitute SUB
arborist warrant-resolve --shards-dir X --use-aliases [--write]

The --use-aliases flag opt-in is fail-closed: aliases don't apply unless the operator explicitly turns them on for a resolve run. Audit trails distinguish alias-resolved derivations.

3. Recommendation

Implement when at least one PD-substitute decision lands from #000038. Today the only candidate is Hilbert-Ackermann 1928 → Mendelson + Enderton (~13 records), which is substantial. The Goldstein → Newton substitution is also viable for ~5 pillar VI records.

Without those substitutions, the table sits empty and the implementation has no real test cases. Park until #000038 surfaces the first substitution decision.

4. Implementation sketch

4.1 Schema migration

Add to arborist/store.py:

def _ensure_citation_aliases_schema(conn):
    conn.execute(SCHEMA_CITATION_ALIASES)
    conn.execute(IDX_CITATION_ALIASES_BY_AUTHOR)

4.2 Resolver integration

In arborist/qa/warrant_resolver.py:warrant_resolve:

for record_root, record_root_data in claim_pack_records:
    citations = parse_citation(source_ref)
    if use_aliases:
        for alias_row in fetch_aliases(source_ref):
            citations.append(parse_alias_substitute(alias_row))
    # … existing resolve loop …

4.3 CLI subcommands

def _cmd_alias_add(args):
    conn = connect(args.aliases_db)
    conn.execute(
        "INSERT INTO citation_aliases (...) VALUES (...)",
        (args.original, args.substitute, ...)
    )

5. Hard constraints (re-stated)

  1. No silent fabrication. Every alias row has decision_at + decision_by. The CLI refuses to add aliases without those fields populated.
  2. Opt-in by default. arborist warrant-resolve does NOT use aliases unless --use-aliases is passed; aliases are a curator's tool, not a default behavior.
  3. Audit trail preserves origin. Derivations rows from alias-resolved chains carry a distinct process_id ("warrant-resolver-v1+alias" proposed) so a future audit can tell which records are evidence-warranted under their original citation vs. under a fox-approved substitute.
  4. Same fail-closed honesty as #000031. When NEITHER the original nor the alias resolves, the record stays at ANCHOR-WARRANTED. No half-credit.

6. Open questions

  • Does the process_id discrimination break the existing PK on derivations(core_root, src_root, process_id)? No — alias-resolved chains have a different process_id, which means an alias chain CAN coexist with an original chain for the same (core_root, src_root) pair. Probably the desired behavior; either chain validates.

  • Mesh sync for aliases? Aliases aren't deterministic (they're fox decisions); shouldn't auto-propagate across peers without each peer's curator approval. Defer mesh semantics; treat the alias table as local-only initially.

  • Per-pillar alias tables? Sometimes a substitution is per-pillar (Hilbert-Ackermann → Mendelson works for pillar I logic but not for any other). The schema's loose; operators can encode pillar-specific aliases via the decision_rationale field for now. Promote to a schema field if the pattern hardens.

7. Status

Closed · 74 rows live as of 2026-05-10 across pillars I/II/III/V/VI/VII/IX (count grew 40 → 54 → 74 across the day as fox decided more substitutions; check live count via arborist --shards-dir SHARDS_DIR alias citation list). All rows carry decision_by="fox 2026-05-10" (or earlier-dated decision) and free-text decision_rationale.

Substrate substitutions registered (cited proprietary work → ingested PD/CC/MIT/GFDL substrate):

Pillar Original cite Substitute(s)
I Mendelson Introduction to Mathematical Logic Russell IMP 1919 + De Morgan FNL 1839 + Boole Laws of Thought 1854 + Russell PoM 1903 + SF-LF
I Enderton A Mathematical Introduction to Logic Russell IMP + De Morgan FNL + Russell PoM + SF-LF
II Jech Set Theory Cantor 1915 Contributions + Russell PoM + Whitehead-Russell PM Vol 1
III Landau Foundations of Analysis Russell IMP + Dedekind 1888 Essays + Peano 1889 Arithmetices Principia + SF-LF
III Gödel 1931 (× 2 variant strings) Peano + Russell IMP + Dedekind + SF-LF
V Kolmogorov Foundations of Probability Grinstead-Snell Introduction to Probability + Laplace Philosophical Essay
VI Goldstein Classical Mechanics (× 2 variant strings) Newton's Principia (Motte)
VII Stanley/Brualdi/Knuth combinatorics (× 14 compound source_refs) Bogart CTGD + Levin Discrete Math + Keller-Trotter Applied Combinatorics
IX Barendregt Lambda Calculus PLFA (Wadler/Kokke/Siek) + SF-LF
IX Dummit-Foote Abstract Algebra Judson AGT
IX Böhm-Jacopini 1966 SF-LF (Imp.v sequence/conditional/while)

Distribution by substrate (most-cited substrates):

  • SF-LF Pierce et al.: 6 alias rows (Mendelson, Enderton, Landau, 2× Gödel variants, Böhm-Jacopini)
  • Russell IMP 1919: 4 (Mendelson, Enderton, Landau, 2× Gödel variants)
  • Newton Principia: 2 (Goldstein × 2 variants)
  • Bogart CTGD: 14 (pillar VII compound source_refs, full strings)
  • Levin: 14 (same pillar VII rows, second substitute)
  • Keller-Trotter Applied Combinatorics: 14 (same pillar VII rows, third substitute — added after KT 38-min crawl-delay BFS landed)
  • Cantor 1915, Russell PoM, Dedekind, Peano, PLFA, Judson, Whitehead-Russell PM, De Morgan FNL, Boole, Laplace, Grinstead-Snell: 1-3 rows each

Each row in ~/.arborist/shards/000.db:citation_aliases carries: decision_at (unix timestamp), decision_by ("fox 2026-05-10" across this batch), decision_rationale (free text — caveats, peer-vs-primary distinction, partial-coverage warnings).

Listed via arborist alias citation list. Full audit trail preserved across audit_events chain. Re-running arborist warrant-resolve --use-aliases --write after any new alias is registered is the way to extend coverage.