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.
10 KiB
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:
- Reads claim-pack record → parses
source_reference - Walks shards, matches title + author against the cited work
- Writes derivations row binding record → surface chunk
With aliases:
- Reads claim-pack record → parses original
source_reference - Looks up original
source_referenceincitation_aliases - If alias found, also tries the substitute citation alongside the original (resolver tries both; either may produce a match)
- When the alias matches, the derivations row's
process_idcarries"warrant-resolver-v1+alias"— distinguishes alias-resolved chains in the audit trail - 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)
- No silent fabrication. Every alias row has
decision_at+decision_by. The CLI refuses to add aliases without those fields populated. - Opt-in by default.
arborist warrant-resolvedoes NOT use aliases unless--use-aliasesis passed; aliases are a curator's tool, not a default behavior. - 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. - 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_iddiscrimination break the existing PK onderivations(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_rationalefield 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.