{"path":"research/corpus-graph/readers/2026-10-11-pages-open-question-architecture-reference-as-a-reader-returned-it.md","content":"# What a reader would tell the founder about the page that describes the built system (the open question) (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 the open question, 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[../open-reading-brief.md](../open-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 66 tool calls in about 10 minutes and used 228,000 tokens. Its report is below, unchanged but for one thing: where the reader wrote out the address of the unlisted page, words in square brackets stand in its place, 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, lines 1-604).\n\n### O1\nrests_on: docs/architecture-reference.md:12 \"the 8-pass extraction pipeline · shared data architecture and the Darwin port map ·\"\nrests_on: docs/architecture-reference.md:14 \"visualisation · the FastAPI route inventory · the FB scraper · Claimify · the graph database · voice\"\nrests_on: docs/architecture-reference.md:350 \"superseded on claim pages 2026-09-20, component kept\"\nremark: The Contents line still describes the file as it was extracted on 2026-08-29. It promises a \"Darwin port map\" that is only a bullet list. It leaves out three sections added since: Blobs (line 98), Compartments, read scope and the admin surface (line 194), and The map view (line 322). It still lists graph visualisation, which line 350 says the map view superseded. CLAUDE.md lines 222-225 copy the same list, so a session deciding from that pointer whether to open this file will not learn that admin, compartments and the map view are here. The file is now about 13,800 words against the \"~14,300 tokens\" CLAUDE.md states.\nso: Rebuild the Contents (and the CLAUDE.md pointer) from the headings, and add a test that compares the two.\nsure: yes, compared the headings with the Contents line and CLAUDE.md:220-227.\n\n### O2\nrests_on: docs/architecture-reference.md:23 \"with instructor + Pydantic + Gemini 3 Flash, orchestrated by Temporal:\"\nrests_on: docs/architecture-reference.md:27 \"via Temporal parallel activities\"\nrests_on: docs/architecture-reference.md:368 \"`POST /extract/stream` — same pipeline with real-time SSE progress events (parallel Pass 3a+3b)\"\nrests_on: docs/architecture-reference.md:33 \"NOT yet on the Temporal/PDF path.\"\nremark: The numbered list is not the pipeline that runs. The Temporal workflow runs a pass labelled 2c_implicit (workflows.py:198) between extraction and relationships. It writes the 34 `implicit` claims and appears in the list nowhere; line 134 mentions its activity only as a registration incident. The code and line 368 call the parallel pair 3a and 3b, while the list numbers them 3 and 4. `detect_implicit_premises` is called only from the Temporal activity, and the weighing pass is stream-only (line 33). So the two paths run different passes, and community extractions, which use the stream path (line 241), get no implicit-premise pass.\nso: Say beside each pass which path runs it, and add 2c, the one pass that writes premises nobody stated.\nsure: yes. Read workflows.py and activities.py, and searched for every caller of detect_implicit_premises.\n\n### O3\nrests_on: docs/architecture-reference.md:43 \"allt ovan om separata nycklar och isolerade fakturakonton är HISTORIA\"\nrests_on: docs/architecture-reference.md:43 \"Faktureringens läge hör inte hemma i ett publicerat dokument\"\nrests_on: docs/architecture-reference.md:300 \"(paused note while the LLM key is unfinanced)\"\nrests_on: docs/architecture-reference.md:45 \"on the free tier (20 calls per day and model) the chain roughly triples the calls available\"\nremark: The only Swedish passage corrects something \"above\" it, namely separate keys and isolated billing accounts. Nothing above it on this page says that, because the extraction from CLAUDE.md left the correction without its target. It also says billing status does not belong in a published document, yet this file says the system runs on the free tier (line 45) and that the key is \"unfinanced\" (line 300). A visitor reading the page sees an unexplained correction in another language, and a rule the page itself breaks.\nso: Delete the orphaned correction and reword lines 45 and 300 to carry no billing state, or say the rule covers only invoices.\nsure: yes. Read lines 1-43 for any mention of keys or accounts and found none.\n\n### O4\nrests_on: docs/architecture-reference.md:45 \"All extraction passes MUST route through `llm_call` — never `client.chat.completions.create` directly.\"\nrests_on: docs/architecture-reference.md:49 \"PDF reading, transcription and embeddings call their providers directly and pass through none of the three.\"\nrests_on: docs/architecture-reference.md:41 \"(scoped to `deliberus_extraction` — NEVER write to other graphs)\"\nremark: Two of the file's absolutes are falsified by its own later text. \"All extraction passes MUST route through llm_call\" sits beside the PDF scout, which uses `call_genai_with_pdf`. That function has three plain retries and none of the model chain, the router or the claude engine (client.py:72-125). So the \"chain triples the calls\" arithmetic on line 45 does not apply to PDFs. \"NEVER write to other graphs\" predates blobs, which write to `deliberus_blob_<name>` (line 98). Its intent is other projects' graphs; as worded it names a noun, not a use (see J2 and J3 on over-broad rules).\nso: Reword the graph rule to name the use, and mark the PDF path as outside the fallback chain wherever the quota arithmetic is given.\nsure: yes for the PDF path (read the code). Fairly for the graph rule's intent, which I inferred.\n\n### O5\nrests_on: docs/architecture-reference.md:59 \"(Jul 6, 2026, dogfood F5, OPEN): `/extract/stream` (the inline path) writes NO forensics row\"\nremark: The line is marked OPEN with \"Port pending\", but the stream path now writes the row. api.py's `_generate_inner` calls `save_attempt_started` and marks the attempt failed on an exception. The dogfood-run-1 friction log (line 67) lists F5 and F7 as shipped and live-verified. TODO.md line 1095 lists the same stale sentence in CLAUDE.md among \"repairs of fact for a fresh session\", but not this file. A reader of this page believes failures on the path community extractions use are invisible when they are recorded.\nso: Correct this line and its cleanup-at-start claim, and add this file to the repair list in TODO.md.\nsure: yes. Read api.py:1105-1130, the friction log and TODO.md.\n\n### O6\nrests_on: docs/architecture-reference.md:49 \"no call starts once the day's recorded spending reaches `DELIBERUS_ROUTER_DAILY_CAP_USD`\"\nrests_on: docs/architecture-reference.md:49 \"with the model that served it and its cost in `~/.deliberus/router/usage.jsonl`\"\nrests_on: docs/architecture-reference.md:142 \"2026-09-19 alone saw seven deploys\"\nremark: The cap's ledger is a file under the home directory, and the production container's user has none. CLAUDE.md says it is made with --no-create-home and only /app/data is writable. deploy.yml sets DELIBERUS_JUDGMENTS_DIR to /app/data for exactly that reason and sets nothing for DELIBERUS_ROUTER_DIR. With the second switch on, `_reserve`, `spent_today` and `router_dir().mkdir` would raise before the first call. Pointing it at /app/data would work, but a deploy empties it, and seven deploys happened on one day. So the $2.00 daily cap would restart with every container.\nso: Before the engine is switched on in production, give the ledger a path that survives a deploy (or put it in the database), and say so in this paragraph.\nsure: fairly. Read router_backend.py:156-213 and 338-360, the Dockerfile and deploy.yml. I did not run it in a container.\n\n### O7\nrests_on: docs/architecture-reference.md:157 \"URL validation with DNS resolution + private IP range rejection + redirect-following with per-hop validation.\"\nrests_on: docs/architecture-reference.md:264 \"(an allowlist checked once is not checked at all when the first response is a 302)\"\nrests_on: docs/architecture-reference.md:265 \"This matters more here than on most deployments: the app's host is also the\"\nremark: The page learned the lesson that a check must be repeated at every hop, but not its sibling. `_validate_url` resolves the hostname with getaddrinfo, and then `httpx.get(url)` resolves it again independently (api.py:608-632 and 682-693). A name that answers a public address the first time and an inside address the second passes. The page itself says [where the app runs], and that \"owner-only is not the defence\". Any signed-in Google user can submit a URL once extraction is un-paused; deploy.yml currently pauses it. The word \"rebind\" appears nowhere in docs/.\nso: Before un-pausing extraction, make the fetch connect to the address that was validated, and record that beside the redirect lesson.\nsure: fairly. Read the code and searched docs/ for \"rebind\". I did not exploit or test it.\n\n### O8\nrests_on: docs/architecture-reference.md:83 \"Tables: `extractions` (JSONB + `input_type` + `pdf_data` BYTEA + `pdf_filename` + `owner_id`)\"\nrests_on: docs/architecture-reference.md:49 \"But only three of the 25 call sites store it\"\nremark: The file never mentions `judgments.py`, the write-once record of every model call (ledger Z1/Z11, live since 2026-10-06), though CLAUDE.md carries its gotcha. The storage section names three Postgres tables. The code defines at least ten: deliberation_drafts, sameness_answers, parts_answers, step_answers, synthesis_artifacts, blob_memberships and read_events, plus the judgment tables. The three review pages write his labelling answers into some of them. Line 49's \"only three of the 25 call sites store it\" is about the served model on outputs. Read with no mention of the record, it suggests nobody knows which model answered, when the record holds exactly that.\nso: Add a short section on the judgment record and a complete table list, so a session touching storage finds them here.\nsure: yes for the omissions (rg of the file for \"judgments\" returned nothing; table names from rg for __tablename__). Fairly on how line 49 reads.\n\n### O9\nrests_on: docs/architecture-reference.md:296 \"any page under `[id]`, `[slug]`, or `[...path]` MUST use `$effect(() => { data.param; ... })` to reload data when the route param changes\"\nrests_on: docs/architecture-reference.md:119 \"[a clause on the owner allow-list and the home connection]\"\nrests_on: docs/architecture-reference.md:190 \"any new top-level route must be added to the `spa_fallback` decorator list in `deliberus/api.py`\"\nremark: The header says rules that must fire first stayed in CLAUDE.md and \"check there first\". Seven are duplicated there. But at least three imperative rules exist only in this reactive file, and I found no test holding them: the $effect rule for [id] pages, user_email on every notification call, and new top-level routes entering `spa_fallback`. The /admin anti-scanner trap at line 276 is a fourth, held only by an api.py docstring. Each fails quietly: stale claim data after navigation, a push to the owner's phone for his own actions, a 404 that appears only in the built app. By contrast `get_graph()` (line 149) has a test.\nso: Move those rules into the resident gotchas, or give each a test that reads the code, as was done for the others.\nsure: fairly. I searched CLAUDE.md and tests/ for each phrase and found none, but a test under other wording may exist.\n\n### O10\nrests_on: docs/architecture-reference.md:218 \"Wired into the scope middleware, so it covers the whole site.\"\nrests_on: docs/architecture-reference.md:118 \"Real doc reads notify from `/api/docs/{path}` with logged-in user identity when present.\"\nrests_on: docs/architecture-reference.md:286 \"The members do not see the read log — a decision, stated in\"\nremark: The read log is introduced under compartments and community governance. But the middleware also records signed-in reads of the public commons (api.py:430, `_record_read(..., \"public\")`): sign-in email, path and time, kept 90 days. Line 118 adds an owner push naming who read which doc. I found no notice at sign-in or in the about page saying any of this. Line 286 calls the members' not seeing the log a decision \"stated in\" a docstring. It is his ruling of 2026-09-27 (\"Read log should only be visible to me\", shared-sensemaking-what-deliberus-holds-and-owes.md:220), which the page could cite. The docstring itself says nobody in a blob consented.\nso: Decide whether signed-in readers, in the commons and in compartments, are told what is recorded about them, and cite the ruling here.\nsure: fairly. Read api.py:420-460 and reading.py, and searched web/src and docs/ for a notice.\n\n### O11\nrests_on: docs/architecture-reference.md:265 \"This matters more here than on most deployments: the app's host is also the\"\nrests_on: docs/architecture-reference.md:119 \"[a clause on the owner allow-list and the home connection]\"\nrests_on: docs/architecture-reference.md:87 \"[a line giving the database's address and the command that reaches it]\"\nremark: This file moved from CLAUDE.md, which is not served, into docs/, which line 43 says is served openly. In moving it became the most infrastructure-revealing page on the site. It states [what else the production host does and how one store is exposed]. It gives [ports, names, a command line, how the owner is told apart, who shares the connection, and the sign-in provider's project], and a history of holes found and fixed (a regex bypass, a path traversal to CLAUDE.md). None of it is a secret, but together it is a guide. Line 157 also says Postgres is on 5433, against 5432 here and in deploy.yml.\nso: Decide once whether operations detail belongs on the public site, and if not, split this file into a public architecture half and a private operations half.\nsure: a guess about whether he intends this. Fairly on the facts: I read the lines, checked deploy.yml, and found no 5433 anywhere in the code.\n\n### O12\nrests_on: docs/architecture-reference.md:304 \"and never named in this directory, which is publicly served\"\nrests_on: docs/architecture-reference.md:305 \"`scripts/build_borderland_maps.py` projects a blob's own data files (the ones `scripts/store_hand_extraction.py` stores)\"\nremark: Line 304 sets the rule that unlisted pages are never named in the served docs. The next line names the unlisted community page by its working files (build_borderland_maps.py, borderlandMaps.ts, test_borderland_maps.py), and six other notes say \"borderland\" too. The literal path [the page's address] is printed nowhere in docs/, so the rule holds only in letter. The page's own header says unlisted is not private, so the harm is small. But the rule is stated as a rule, and it is already broken in seven places.\nso: Rename those files to something neutral, or loosen the rule to what it can actually keep.\nsure: a guess about whether it matters. I checked rg for [the page's address] in docs/ (none) and counted seven docs naming borderland.\n\n### O13\nrests_on: docs/architecture-reference.md:527 \"The six other kinds join a claim to something else: EXTRACTED_FROM (6,244, claim to source),\"\nrests_on: docs/architecture-reference.md:494 \"| Source | 35 | where claims were taken from |\"\nremark: I ran a read-only count on the live graph. Of the 6,244 EXTRACTED_FROM links, 4,052 run from cq_positive and cq_negative claims, the possible answers nobody gave. The rest: 2,050 from extracted claims, 84 from reports, 35 from human claims and 23 from implicit ones. So about two thirds of the arrows named \"extracted from\" lead to a source that never said the claim. The table calls Source \"where claims were taken from\" and the edge \"claim to source\". This is the same hazard the page warns of for machine_step. Any reader that follows the edge to say what a source holds picks up scaffolding unless it filters by kind.\nso: Label the edge honestly in this page and the glossary (or rename it), and list its readers in a test as was done for machine_step readers.\nsure: yes for the counts (ro_query, 2026-10-11). A guess that any reader currently misuses it.\n\n### O14\nrests_on: docs/architecture-reference.md:536 \"a strength, the machine's reason, who or what drew it, whether a person refused it\"\nrests_on: docs/architecture-reference.md:331 \"is link strength; what a claim hangs on and where an answer would count are meant for emphasis\"\nrests_on: docs/architecture-reference.md:336 \"written as marked parts (`provenance='split'`, `confirmed` left unset, the verdict on the edge)\"\nremark: As a description of what is built, the page is right. But the section is dated 2026-10-10 and lists `strength` and `confirmed` as plain fields of a link. It says the map picks its boxes by link strength, with none of the \"ruled, not built\" marks it puts on other pending matters. On 2026-09-28 he ruled Q31 (a link's strength is a reading, never a stored value; 174 public links carry a flat 0.7) and Q32 (retire `confirmed`). The build items are open (TODO.md:1312), and claim_map.py:37-65 still sorts by the stored number. A session reading here to build a feature will build on both.\nso: Put a one-line \"ruled to become a reading, not yet built\" beside each of these places.\nsure: fairly. Compared with settled items Q31 and Q32, claim_map.py and TODO.md.\n\n### O15\nrests_on: docs/architecture-reference.md:175 \"count drifts upward — trust `pytest` over this line). Coverage: SSRF (all private IP ranges)\"\nrests_on: docs/architecture-reference.md:45 \"`_CAPACITY_MARKERS` in `client.py:178`\"\nremark: Small rendering and drift faults. The bold on line 175 opens and never closes (one `**` on the line), so the public page prints literal asterisks. The figure says \"800+ tests\" while pytest collects 2,600. Line 45 cites client.py:178 for `_CAPACITY_MARKERS`, which is now at 184. Lines 26 and 27 keep stray \"}\" marks from a bracket in the original that renders as noise. Line numbers in a reference rot where symbol names would not.\nso: Fix the asterisk and the braces, and cite symbols instead of line numbers.\nsure: yes. Counted the asterisks, ran pytest --collect-only, and read client.py.\n\nLooked for and did not find: any mention of DNS rebinding or address pinning anywhere in docs/; any notice to signed-in readers that their reads are logged (searched web/src and docs/); any mention of judgments.py or the judgment tables in this file; a test holding the $effect, spa_fallback or notification user_email rules; the literal path [the page's address] printed in docs/; a ruling that closes the raw-atom gap in line 25 (it is recorded as open B11 in session26-open-rulings.md:46, so not raised).\n"}