docs/tickets: open #000041 — citation-aliases table
Design ticket for the alias mechanism proposed in #000038 §3.2 option (b). 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 alongside the original. Schema (`arborist citation_aliases`) carries decision_at + decision_by + decision_rationale per row so the substitution is auditable. Opt-in via `--use-aliases` on warrant-resolve; alias-resolved chains carry distinct process_id ("warrant-resolver-v1+alias") so audit can tell substituted chains from original ones. Recommend parking until first PD-substitute decision lands in #000038. Today the only candidate is Hilbert-Ackermann 1928 → Mendelson + Enderton (~13 records), which is substantial. Goldstein → Newton's Principia for ~5 pillar VI records is also viable. Sibling design to #000042 term-aliases (the other vocabulary- mismatch follow-up surfaced by the Hilbert "incidence" vs "connection" gap from #000040 §6). Implementation deferred until trigger fires.
This commit is contained in:
parent
b9e5bbdb13
commit
2487b1c05c
2 changed files with 216 additions and 1 deletions
|
|
@ -61,6 +61,7 @@ Newest first. Update on every open/close.
|
|||
|
||||
| ID | Title | Status | Opened | Directive |
|
||||
|----------|------------------------------------------------|-----------------------|------------|-----------|
|
||||
| #000041 | Citation-aliases table (PD substitutes for proprietary cites) | open · awaiting first PD-substitute decision from #000038 | 2026-05-09 | — |
|
||||
| #000040 | Phase 5 resolver fix — phrase + content-token cascade (Hilbert terminology mismatch surfaced) | closed · cascade landed 2026-05-09; lift blocked by 1902-vs-modern vocab; follow-up #000042 | 2026-05-09 | — |
|
||||
| #000039 | Optional `sqlite-vec` retrieval backend (A/B vs FTS5, hybrid not replacement) | open · awaiting go/no-go (doc-only Phase 0) | 2026-05-09 | — |
|
||||
| #000038 | Phase 4 content acquisition — proprietary textbook license decisions for warrant coverage | open · awaiting go/no-go | 2026-05-09 | — |
|
||||
|
|
@ -104,4 +105,4 @@ Newest first. Update on every open/close.
|
|||
|
||||
## Next ID
|
||||
|
||||
`000041`
|
||||
`000042`
|
||||
|
|
|
|||
214
docs/tickets/ticket-000041-citation-aliases-table.md
Normal file
214
docs/tickets/ticket-000041-citation-aliases-table.md
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
# Ticket #000041 — Citation-aliases table
|
||||
|
||||
**Status:** open · awaiting go/no-go (depends on #000038 PD-substitute decisions)
|
||||
**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
|
||||
|
||||
Open · awaiting fox's first PD-substitute decision under
|
||||
`#000038` §3.1. Implementation triggered by that decision;
|
||||
estimated size when triggered:
|
||||
|
||||
- Schema migration: ~30 LOC + 1 migration test.
|
||||
- Resolver integration: ~50 LOC.
|
||||
- CLI subcommands (add / list / remove): ~80 LOC + 5 unit tests.
|
||||
- Documentation update.
|
||||
|
||||
Total: ~160 LOC + ~6 tests when triggered.
|
||||
Loading…
Add table
Add a link
Reference in a new issue