# 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

```sql
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`:
```python
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`:

```python
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

```python
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.
