Syntology Docs

Architecture overview

Evidence base: live checkout reads of ARCHITECTURE.md (2026-09-01 review), README.md, wrangler.toml, Dockerfile.api, public/llms.txt, and the serving/ingestion entrypoints. Counts and schedules drift; treat numbers as snapshots unless you re-run the cited commands.

Topology

Cloudflare Worker (worker.js) owns syntology.ai: static assets from public/ (wrangler.toml [assets], run_worker_first = true so security headers apply), billing/session, trial OAuth, MCP reverse proxy, graph-agent proxy to App Runner, and several fixed Cypher reads against Aura’s HTTP Query API (pulse, slice, neighborhood, atlas-api, code-library).

AWS App Runner runs uvicorn main:app --workers 1 from Dockerfile.api: FastAPI surface for /papers/* (local corpus; empty in container by design), thin /graph/<template> endpoints, /graph/ask (NL pipeline), draft/commit agent, reviewer-bot, and mounted /mcp (mcp_server.py). Neo4j via syntology_api_reader. Bedrock via task role (not static keys in the image).

Neo4j Aura Professional is the single graph. Writers on the Mac use syntology_loader through WriteLedger (+ advisory lock file under groundwork/19_structured_provenance/). Web tier uses syntology_web over HTTP Query API only.

Local canebrake writer: acquisition → GROBID → parse → citation resolve → methods extraction → load; enrichment backfills; code harvest lane; QC suite; ~10 launchd jobs. Ops dashboard on localhost (read-only collectors).

CF Worker ──X-Syntology-Internal-Key──► App Runner ──reader──► Neo4j Aura
                                              ▲
Local Mac ──WriteLedger / syntology_loader────┘

Data flow (stage sketch)

Directory-to-directory transforms. Stage numbers through 06 are sequential; from 07 onward numbers are often write-order, not flow position.

PhaseScripts (examples)Reads → writes
Acquireacquire_*.py, comprehensive_paper_acquisition.py, 05/07/10/11venues/arXiv/OR → metadata + PDFs
Extract01_grobid_extract / 09_binned_parse, 02_parse_tei, 02b_backfill_from_s2PDFs → TEI → parsed JSON (+ review queue)
Cite / review04 / 04c, 03_openreview_syncparsed → *_cited*, reviews_synced.json
Orchestrate06_process_new_papers.pydiscovery JSON → full chain (subprocess per stage)
Methodsrun_methods_full_corpus.py, apply_name_shape_veto.pycited → extraction results
Loadload_papers_into_neo4j.py (+ other load_*.py)cited + methods → Neo4j (MERGE, review gate, ledger)
Enrich15–22_*, embeddingsgraph / corpus backfills
Code lanediscover → classify → harvest → probe → roles → verification ladderGitHub + graph → CodeSample, IMPLEMENTS / HAS_*_IMPL
Servemain.py + query_engine/graph → API/MCP responses

Exit-code contract (everywhere): 0 clean · 1 findings · 2 drift · 3 unaccepted exclusions · 4 unresolved partials. 06 treats 3/4 as “refusing to overclaim; continue.”

Provenance discipline

  • Derived files: stamp via provenance.py (R1).
  • Filters account for exclusions (R3); review queue gates load (R4).
  • Bulk graph writes: WriteLedger + --intent (no placeholders).
  • Edges carry provenance; CodeSample.verification_level must be backed by evidence on the node or readers treat as 0.
  • Reviews: raw per-reviewer rows; never averaged; no reviewer identity.

Identity spaces

Three disjoint paper identity spaces coexist: arXiv-keyed nodes, S2-keyed citation stubs (s2_only), and venue-native ids stored in arxiv_id (CVF, ACL, …). arxiv_ids.is_arxiv_id() is the shared definition of “usable as an arXiv id.”