D-04: add _make_bibkey(paper) — ConcatSurnames+Year, ≤3 authors full list,
>3 authors FirstEtAlYear. Applied in ingest_paper when paper.bibkey is
None. ON CONFLICT now fills NULL bibkeys via COALESCE(papers.bibkey,
EXCLUDED.bibkey) while preserving manually-set values.
D-05b: add codex/__main__.py so `python -m codex` dispatches to the CLI.
D-05c: fix test_ingest.py:75 — rename var to raw_api_citations (the raw
OpenAlex payload with citing_id=openalex_id) and add regression
assertion: citing_id written to DB must equal paper.id (DOI), not
paper.openalex_id.
D-05a (proxy): add TestEntryPoint.test_python_m_codex_help — runs
`sys.executable -m codex --help` via subprocess, covering the
non-uv-run entry path. 253 tests green, ruff+mypy clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes audit N-02: docs/adr/ADR-F13-synthesis-engine.md was missing,
blocking R-34. Documents spike result (GO, Opus APPROVE 2 passes),
four-stage lead taxonomy, grounding discipline, three-level
zero-fact-leak invariant, and falsification-gate in _parse_conjecture_output.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs exposed during first batch ingest run after D-03 fix:
1. `.txt` files were silently skipped ("Unbekannter Dateityp") — added
plain-text read path alongside .tex and .pdf.
2. After D-03, OpenAlex resolves arXiv IDs → paper.id uses the DOI
(https://doi.org/…) but fetch_citations emits citing_id=openalex_id
(https://openalex.org/W…). FK constraint citations_citing_id_fkey
→ papers.id violated. Fix: rewrite citing_id to paper.id after
fetching from OpenAlex.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FastMCP defaults to DNS rebinding protection (allowed_hosts=localhost only),
which rejects requests from the Mac (192.168.178.82) to the Jetson
(192.168.178.103:8765). Security is already provided by Bearer token auth
in _TokenAuth middleware and UFW restricting port 8765 to trusted LAN IPs,
making host-header validation redundant in this deployment context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
F-13 settings (leads_dir, synthesis_llm_*, synthesis_top_k,
synthesis_min_grounded_ratio, synthesis_gap_min_coverage) appeared twice
due to independent commits on F-13 and F-14 branches. Kept the second
(more complete) block; removed the first.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Documents MCP server setup for Claude Code (.mcp.json) and Claude Desktop
(claude_desktop_config.json), HTTP transport config, and available tools table.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Lead.__post_init__: raises ValueError on empty provenance list
- find_gaps: no-chunk path now carries topic-marker Provenance instead of []
- _update_index: docstring corrected (rebuilds, not append-only)
- test_model: test_lead_empty_provenance_raises covers the new invariant
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace 3-gram substring match with:
- _last_sentence(): only the last sentence before a citation is checked
(prevents a hallucinated paragraph grounding via a phrase at its end)
- _content_words(): stopwords removed from both claim and chunk before
n-gram comparison (prevents bypass via "the discrete conformal map")
- Content-5-gram: require 5 consecutive non-stopword tokens from last
sentence to appear in the chunk's content-word stream
- Claims with <5 content words in last sentence → ungrounded by default
Add adversarial tests: stopword-3-gram bypass → grounded=False;
legitimate content-5-gram → grounded=True.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add _detect_conflicts() using adversative keyword heuristic (but,
however, in contrast, …) between chunks of different bibkeys; results
populate CompileReport.conflicts and appear in wiki/log.md.
Also add CompileReport.quarantined field (used by Fix 2).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_inject_cross_refs temporarily replaces backtick spans with null-byte
placeholders before injecting [[slug]] cross-refs, then restores them.
This prevents concept titles inside `code` from being rewritten.
Add tests for inline-code protection and full-list cross-ref injection.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
compile_concept now accepts chunks as a positional parameter; compile_all
retrieves chunks once and passes them directly, avoiding double-retrieve.
Also separates all_concepts (full list) from compile_concepts (filtered),
so cross-ref injection always uses the complete concept list regardless of
--concept filter (Fix 6 + Fix 8).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_parse_claims now skips matches where bibkey contains spaces or starts
with "http", preventing Markdown hyperlinks like [text](https://...) from
being misidentified as citations. Add two tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wrap llm.generate() in compile_concept with a broad try/except;
log a warning and return an empty ConceptPage (no crash) when the
LLM endpoint is unreachable. Add logging module import.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- mathpix.py + figures.py: wrap fitz page-loop in try/finally so
doc.close() is guaranteed even on exception (no file-handle leak)
- ingest.py: DELETE FROM formulas/figures WHERE paper_id before re-insert
so --rich re-ingest is idempotent (BIGSERIAL has no natural UNIQUE key;
ON CONFLICT DO NOTHING was a no-op)
Gate: 158 passed, ruff clean, mypy clean
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
codex-py hatte kein .claude/ — F-xx-Worker liefen mit nackten globalen Settings
und wurden bei jedem uv/ruff/mypy/pytest/git-Befehl geprompted. settings.json
spiegelt den knowledge-base-Stil (acceptEdits + harter deny-Block), zugeschnitten
auf den Python/uv-Stack + podman (DB) + ollama. settings.local.json gitignored.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Implement all CLI commands as specified in F-07: ingest, search, and discover subcommands (leads, citing, cited-by, cocited), and provenance subcommands (scan, add-link, links, bib).
- Add ask command stub that exits with code 1 (not yet implemented).
- Implement comprehensive tests with mocked dependencies (no real DB/API calls).
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Review-Gate finding: GROBID emits type="arXiv" (camel-case), not "arxiv".
XPath literal match silently returned empty strings for all real responses.
Fix: iterate idno elements and compare .lower() == "arxiv".
Test fixture updated to reflect real GROBID output (type="arXiv").
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bare Typer() with no commands raised RuntimeError at runtime.
Callback makes the entry point callable without exposing F-07 logic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>