{"path":"research/corpus-graph/readers/2026-10-11-pages-what-does-not-fit-architecture-reference-as-a-reader-returned-it.md","content":"# What does not fit on the page that describes the built system, as a reader returned it (2026-10-11)\n\nA read-only reader (Sonnet), a helper of the app with a shell and the search by meaning. It is part of the\nreading of the pages written for visitors, for what does not fit, described in\n[../reading-for-every-kind-of-insight.md](../reading-for-every-kind-of-insight.md) § 7b. It was given the brief in\n[../dissonance-reading-brief.md](../dissonance-reading-brief.md), everything the founder has settled in one file (135 items), and these 1 notes:\n\n- `docs/architecture-reference.md`\n\nIt made 183 tool calls in about 18 minutes and used 349,000 tokens. Its report is below, unchanged but for one thing: in finding F13 the address of the unlisted page is replaced by words in square brackets, since that address is not to be named in this folder (the word-for-word report is kept privately). And since 2026-10-11, when the page's operations half was made private on the founder's word, the reader's quotes of machine detail from that page are replaced the same way. Nothing in it is kept or acted on until the\nquoted words have been checked by code and the passages opened; what was done with it is in § 7b of the note above.\n\n---\n\n1 of 1 notes read in full (docs/architecture-reference.md, 604 lines); about 75 checkable statements listed. Each was checked against the code and the other notes, and F1 to F15 are the ones that did not fit.\n\n### F1\ntarget: docs/architecture-reference.md:26\ntarget_words: \"Relationship detection + scheme classification** — supports/attacks/qualifies/reframes/decomposes_into + Walton's 96 argument schemes\"\nolder: deliberus/extraction/schemes.py:3\nolder_words: \"Walton's catalogue runs to 96 schemes. This module holds a subset of them:\"\nkind: number-or-date\nwhy: The note says the relationship pass classifies against Walton's 96 argument schemes. The code says its module holds only a subset, and `len(ArgumentScheme)` is 46 members in 11 clusters. A reader who takes 96 as the classifier's menu, for example when judging how often \"does not fit\" should fire or how much of the catalogue is covered, works from a menu twice the real size.\nstands: other, by the code\nstrength: medium\n\n### F2\ntarget: docs/architecture-reference.md:29\ntarget_words: \"6. **Self-evaluation** — per-claim and per-relationship checks with an `issue` and a `suggested_fix` on each\"\nolder: deliberus/extraction/self_eval.py:1\nolder_words: \"Pass 4: Self-Evaluation — quality check the pipeline's own output.\"\nkind: word-two-senses\nwhy: The note numbers contested concepts as pass 4, self-evaluation as 6 and embed plus auto-connect as 8. The code, the Temporal workflow and temporal-extraction-architecture.md call self-evaluation Pass 4, embedding Pass 5 and auto-connect Pass 6. A reader who meets \"Pass 6\" in a code comment or workflow step name and looks it up in this note lands on self-evaluation, not auto-connect.\nstands: other, by the code\nstrength: medium\n\n### F3\ntarget: docs/architecture-reference.md:33\ntarget_words: \"Decompose-created children get the same detection in authored mode. NOT yet on the Temporal/PDF path.\"\nolder: deliberus/temporal/activities.py:460\nolder_words: \"2026-08-25, ending the divergence where the Temporal/PDF path had NEITHER\"\nkind: state\nwhy: The note says the post-store weighing pass is not yet on the Temporal/PDF path. The code runs the shared post-store layer, weighing included, as a Temporal activity wired on 2026-08-25. The worker registers it, and tests/test_pipeline_congruence.py fails if an orchestrator drops it. A PDF extraction today gets weighing detection, so an operator reading the note would believe a gap exists that was closed.\nstands: other, by the code\nstrength: strong\n\n### F4\ntarget: docs/architecture-reference.md:41\ntarget_words: \"Direct FalkorDB via falkordb-py (scoped to `deliberus_extraction` — NEVER write to other graphs).\"\nolder: docs/architecture-reference.md:100\nolder_words: \"A **blob** is a separate FalkorDB graph plus a separate Postgres database on the same servers, both\"\nkind: counter-case\nwhy: Line 41 says the pipeline's graph client is scoped to deliberus_extraction and must never write to other graphs. By design (blob.py, store.py `_blob_graph_name`), a process started with DELIBERUS_BLOB writes to the separate graph deliberus_blob_<name>, and community input for members does exactly that. The two fit only if \"other graphs\" means graphs outside the app's own, which a reader of line 41 could not know.\nstands: other, by the code\nstrength: medium\n\n### F5\ntarget: docs/architecture-reference.md:57\ntarget_words: \"A `print` is not a logger and is invisible in production. A bare `except Exception: return []` is a hidden capability-degradation bomb.\"\nolder: deliberus/extraction/cq_generation.py:184\nolder_words: \"print(f\"    ⚠ CQ generation failed for edge {idx}: {e}\")\"\nkind: rule-and-practice\nwhy: The note says any try-block wrapping an LLM call that falls back to an empty result must call logger.exception, because a print is invisible in production. It says this applies to every extraction-pipeline helper. CQ generation returns [] for a failed edge after the print quoted, and scout.py:112 does the same for a failed section. When a section's scout call fails in production, claims are silently lost and the log names no cause.\nstands: target, by nothing I found\nstrength: strong\n\n### F6\ntarget: docs/architecture-reference.md:59\ntarget_words: \"Known gap (Jul 6, 2026, dogfood F5, OPEN): `/extract/stream` (the inline path) writes NO forensics row — its failures are invisible to this table.\"\nolder: docs/research/dogfood-run-1-friction-log.md:67\nolder_words: \"Port cleanup-at-start + forensics to the stream path** — **SHIPPED, live-verified end-to-end** (attempt rows\"\nkind: state\nwhy: The note says the stream path writes no extraction_attempts row, the port is pending, and the item is OPEN. The friction log records the port as shipped and live-verified. The code in deliberus/api.py (about line 1124) calls save_attempt_started in the stream path and marks the row failed on a crash. A reader would add forensics that already exists, or distrust a table that does cover the stream path.\nstands: other, by the code\nstrength: strong\n\n### F7\ntarget: docs/architecture-reference.md:61\ntarget_words: \"All source text MUST be escaped (`{{ → { {`) before passing to instructor. See `_escape_jinja()` in `scout.py` and `focused.py`.\"\nolder: deliberus/extraction/client.py:167\nolder_words: \"Escape {{ }} patterns that instructor interprets as Jinja templates.\"\nkind: number-or-date\nwhy: The note points to `_escape_jinja()` in scout.py and focused.py. No function of that name exists anywhere. Both files import the public `escape_jinja` from client.py, where it is defined. A reader who searches for the named function finds nothing and may rewrite the escape, or conclude escaping is missing.\nstands: other, by the code\nstrength: medium\n\n### F8\ntarget: docs/architecture-reference.md:93\ntarget_words: \"Auto-connect costs are capped at `new_claims × top_k(5)` LLM calls regardless of graph size — linear in new claims, not in graph density.\"\nolder: docs/research/auto-connect-upgrade.md:224\nolder_words: \"cosine 182 candidates (35.2s), concept_route 21 (0.1s), structural kinship 244 (0.7s) — merged 435\"\nkind: overtaken\nwhy: The note says auto-connect makes at most new_claims times five model calls, whatever the graph looks like. Since the 2026-08-20 upgrade, concept-route and structural-kinship candidates come on top of the top-five cosine ones: 435 merged pairs from 182 cosine ones on one source. Each pair costs one or two calls, and the partner cap and pair ceiling are off unless set (production sets neither). One source was measured at 749 pairs, about $47 at list prices (auto-connect-upgrade.md:146).\nstands: other, by a measurement\nstrength: strong\n\n### F9\ntarget: docs/architecture-reference.md:157\ntarget_words: \"Blocks access to [the inside services, listed with their ports].\"\nolder: docs/architecture-reference.md:87\nolder_words: \"[a line giving the database's address and the command that reaches it]\"\nkind: number-or-date\nwhy: Line 157 gives the Postgres port as 5433. Lines 83 and 87 of the same note, and config/deploy.yml (`port: \"5432:5432\"`), give 5432. The SSRF check itself tests address ranges and not ports. Anyone checking by port number that the defence covers the shared Postgres would check the wrong one.\nstands: other, by the code\nstrength: strong\n\n### F10\ntarget: docs/architecture-reference.md:158\ntarget_words: \"**Cypher injection whitelist**: `VALID_RELATIONSHIP_TYPES` set in `store.py` — only SUPPORTS/ATTACKS/QUALIFIES/REFRAMES/DECOMPOSES_INTO accepted.\"\nolder: deliberus/graph/store.py:660\nolder_words: \"# (DEFINES, DECOMPOSES_INTO) carries no inference and stays unlabelled. Before\"\nkind: number-or-date\nwhy: The note says exactly five relationship types pass the whitelist. The code's set at store.py:626 also holds DEFINES, and the store logic treats DEFINES as an ordinary stored structural edge. Someone auditing the injection surface from this note would miss a sixth accepted type.\nstands: other, by the code\nstrength: medium\n\n### F11\ntarget: docs/architecture-reference.md:185\ntarget_words: \"**Public endpoints**: All GET routes (claims, extractions, concepts, graph stats, docs).\"\nolder: docs/architecture-reference.md:190\nolder_words: \"owner-only `/me` settings page → `GET /auth/claim-link` (401 anon, 403 non-owner) mints a 15-min HMAC-signed token\"\nkind: counter-case\nwhy: Line 185 says all GET routes are public. The same note documents GET /auth/claim-link answering 401 to the anonymous and 403 to non-owners, and owner-only /api/admin/* GETs that answer 404. The code gates GET /claims/{id}/my-vote on sign-in. A crawler, agent or test that treats every GET as anonymous-safe gets 401 or 404 on these.\nstands: other, by the code\nstrength: medium\n\n### F12\ntarget: docs/architecture-reference.md:216\ntarget_words: \"settings: only a signed-in reader is named, no IP and no user agent, no query strings, and ninety\"\nolder: deliberus/reading.py:83\nolder_words: \"Delete reads older than the retention window. Returns how many went.\"\nkind: rule-and-practice\nwhy: The note lists ninety days of retention (continued on line 217) as one of four bounds on the read log that are \"refusals rather than settings\". reading.py defines `prune()` to enforce it, but nothing in the repository calls it, and the admin endpoint reports retention_days 90. Named readers' paths therefore accumulate past ninety days with no sign anywhere, a guard that never fires.\nstands: other, by the code\nstrength: strong\n\n### F13\ntarget: docs/architecture-reference.md:304\ntarget_words: \"they are never added to `robots.txt`, where a Disallow line would advertise the path, and never named in this directory, which is publicly served.\"\nolder: docs/architecture-reference.md:305\nolder_words: \"**The data is generated, not typed**: `scripts/build_borderland_maps.py` projects a blob's own data files\"\nkind: rule-and-practice\nwhy: Line 304 says unlisted pages are never named in this publicly served directory, so their path is not advertised. Line 305, in the same note, names the unlisted community page's builder, output file and test by the community's name, and other served docs name it too. The [page's path] that the noindex header and the missing robots.txt line protect is thus readable from the served docs. The code's own docstring says \"unlisted is not private\", so this may be accepted.\nstands: cannot tell, by nothing I found\nstrength: medium\n\n### F14\ntarget: docs/architecture-reference.md:309\ntarget_words: \"This is still a guidance/state layer, not yet full transitive mother-claim strength handling.\"\nolder: deliberus/graph/badge_inputs.py:316\nolder_words: \"The full badge with the decomposition channel composed RECURSIVELY.\"\nkind: overtaken\nwhy: The note says decomposition guidance is only a state layer, without transitive strength through mother claims. Since 2026-08-22 `compute_badge_recursive` composes the decomposition channel recursively: examined children enter their parent at their own computed badge, to depth 8. The note's own hinge route (line 419) and requirement route (line 416) rely on this. The sentence carries no date or mark, so a reader would think a parent ignores its grandchildren.\nstands: other, by the code\nstrength: medium\n\n### F15\ntarget: docs/architecture-reference.md:602\ntarget_words: \"Transcribed (168KB). Raw output at `data/voice_memos/Robin + Martin; Deliberus.txt`. Analysis pending — massive primary source\"\nolder: TODO.md:3835\nolder_words: \"Remaining 5+ hrs is Whisper hallucination from quiet audio. No new Deliberus design insights beyond existing docs.\"\nkind: overtaken\nwhy: The note presents the 5.5-hour Robin and Martin transcript as a massive primary source awaiting analysis. TODO.md records that it was reviewed: only the first ~12 minutes are real content and the remaining hours are Whisper hallucination from quiet audio. A reader who plans analysis of 168KB of transcript would spend effort on text that is mostly invented.\nstands: other, by a measurement\nstrength: strong\n\nNoticed: Smaller drifts I did not report as pairs. (a) L45 cites `client.py:178` for `_CAPACITY_MARKERS`; it is now line 184. (b) L161 says LLM-invoking POST routes are limited to 3–10/minute and that the \"1/minute\" note was stale, but `generate-titles` and `embeddings/generate` still carry 1/minute. (c) L83 lists three Postgres tables, while the code defines about twelve. (d) L544 says \"marked as nobody's\", which is the wording AG12 replaced with \"a claim nobody asserts\". (e) L300 describes the 2026-08-19 landing as current, and only L303 says it is hidden (`showDerivation = false`). (f) L71 gives the previous pipeline as 46 claims, while the cited first-pipeline-run-analysis.md says 44. (g) L75 says \"No JSON files on disk\", but deliberus/cli.py writes per-pass JSON. (h) L457 says the graph is reached \"via Graphiti MCP\", while L41 says direct falkordb-py. (i) L49's \"only three of the 25 call sites store\" which model served is partly overtaken by the shared judgment record (Z1, live 2026-10-06), though the doc cross-references still repeat it. (j) L43's correction mark refers to \"separate keys and isolated billing accounts\" above it, but nothing above mentions them.\n"}