OKUSOKO runs an investigation the way a careful researcher would, only relentlessly and with the receipts stapled on. It keeps a queue of open questions, spends each cycle on exactly one of them, reads what it finds, and refuses to record anything it cannot quote from bytes it has stored. Every so often it stops looking at claims and looks at the shape of what it has collected instead — and that second, slower reading is what generates its own next questions.
a fast loop that gathers ground truth, a slow loop that reads the landscape
Takes one question, searches, reads three sources, extracts claims, verifies every quote against stored bytes, and decides whether the question is answered. This is where facts enter the system.
Ignores individual claims and reads the whole store for structure: things that co-occur, clusters, years that bulge, contradictions, and gaps. What it notices becomes new questions, ranked above routine work — so the system's own observations steer the next searches.
click any stage — the same ten run every cycle, in this order
A ResearchSpace is, underneath, a queue of open questions — the frontier. Each cycle takes exactly one. Highest priority wins; ties go to the older question.
Priority comes from where the question was born: the primary question you wrote is 1.0; a secondary question you attached — another information vector the loop must keep in view — is 0.95; a question the system raised itself after noticing a pattern is 0.85; a routine breakdown of a bigger question is 0.7; a “nobody has asked about this entity yet” gap question is 0.6.
One deliberate twist: if the last question worked was pattern-born, the next pick is forced to be something else. Otherwise the system's own theorising crowds out plain coverage of the ground.
The first time a primary or secondary question is selected, the director splits it into sub-questions and puts them all on the frontier.
A cycle can work more than one question (cycle_width, default 1): the picks alternate pattern-born and routine, discovery runs for each in parallel, and extraction, indexing and reflection follow on one thread — breadth per cycle, without racing the store.
one question becomes three queries becomes three documents — everything else is discarded on purpose
A single query finds a single kind of page. Splitting into precise, broad and archival means one search reaches the specific record, the overview, and the primary document — three different layers of the web at once.
Fetching is the expensive step, in time and in tokens. Ranking with the director seeing all candidates side by side is far cheaper than reading three bad pages, and it can drop obvious junk outright.
Pure novelty chases weirdness and quality collapses. Pure quality re-reads the same encyclopaedia forever. Bands of three give quality the first word and novelty the second.
every claim points at a byte range in a hashed snapshot — checked in code, not by judgement
Every claim is labelled fact, inference, hypothesis or speculation, and the database enforces it. The point is not to filter speculation out — it's to stop speculation from silently becoming fact as it gets passed along.
The quote is hunted in the snapshot bytes: exact, then punctuation-tolerant, then normalised alignment, then fuzzy at 0.9. Anything that can't be located is flagged. In the Dulce trial run, 96% of 676 claims verified — and the 4% are visible, not hidden.
a PDF you upload skips search and triage — everything after that is identical
The PDF becomes one snapshot (so quotes verify the ordinary way) and each page is recorded as a span over it. Pages with a thin or missing text layer — scans, photocopies, handwriting — are rendered and transcribed from the scan first (the OCR rung; such pages are marked). Every chunk is then extracted — no per-cycle budget, because you asked for this document.
A summarizer role writes a 2–4 sentence digest, tags and dates for each page, then a document-level digest with type, author, date and the period it covers. Each page shows how many claims it contributed, and the document keeps per-stage timings (read, OCR, extract, summarize, advance) — the Documents view shows the live stage and progress while a job runs.
Index → resolve → skeptic → corroborate → survey → interrogate, exactly as a ground cycle would. New entities join the graph, dated claims join the Chronoscope, and any patterns become frontier questions.
six roles inside Okusoko, each with one job
Splits the seed question, writes the three search queries, ranks candidates, and decides when a question is answered. The only role that sets direction.
Reads a chunk, returns claims with epistemic labels, quotes and entities. A parse failure returns nothing rather than stub claims, so what is counted is what was read.
Re-reviews live claims every cycle and moves them between provisional, corroborated, contested and retracted.
Answers one narrow question over and over: do these two claims assert the same fact? Gates both corroboration and contradiction.
Digests each uploaded page (summary, tags, dates) and then the whole document (type, author, date, period). Read-only: it never creates claims.
Writes a small scraper for a domain the ladder keeps failing on. The scraper is validated, stored in the space, and becomes rung 1 for that domain forever after.
Turns text into vectors for novelty, retrieval and clustering — runs inside the worker, so indexing never waits on a remote endpoint.
five detectors, run over everything, every third cycle
Entity pairs that keep turning up in the same passage across different sources — not dominated by one document, and only the strongest sixty kept, so the view stays readable.
Greedy cosine grouping over claim vectors — a knot of claims all saying related things is itself a finding at a higher scale.
Entities that connect otherwise-separate communities of the corroborated entity graph — high betweenness relative to their few links. Informationally valuable, never evidence of anything by itself.
Years that attract disproportionate claim mass. Where the corpus bulges in time.
Claims that share a resolved entity but assert conflicting attributes — dates, counts, money — plus anything with refuting evidence. Cross-source only, and judge-gated, or the view fills with noise.
Entities the corpus keeps mentioning that no open question addresses. Absence, detected.
Documents the corpus cites but does not hold — a deposition, memo, or case file named by two or more independent sources with no matching source in the space. The most actionable kind: the question it births is 'go get it'.
Entities whose identity needs confirming: a name seen once in OCR'd handwriting ('Agino' for Aquino), two spellings one edit apart, or one label pooling several people's surnames. The question it births is 'who is this, really?'
Two entities that never share a corroborated passage yet keep sharing associates, organisations, places and years — an undocumented relationship may sit between them. Structural inference, scored against the within-source shuffle, never a node; interrogation turns it into the search that would document it.
Two entities that may be one — a nickname, a one-edit surname, a shared identifier, shared neighbours beyond chance — with the evidence for and against on the card. Nothing merges until you rule; a ruling holds through every resolve after it.
Strong patterns are handed back to the director, which writes new frontier questions about them — deduped against what's already open, and entered at priority 0.85 so they get worked before routine decomposition.
the store is the terrain; a surface is a continuous reading of it — four built in, more to come
A surface is the whole store aggregated along one axis into cells, each carrying claim mass (density), verified share (coverage), distinct sources, status mix and a gap score. Time cells are years; place cells are place entities; actor cells are people and organisations; document cells are sources. Every axis also has a summary: how many claims could be placed on it at all, how many cells, the loudest gap.
Gaps in surfaces are a better thing to interrogate than raw entity counts: a hole in the time surface between two busy years, an actor only one source speaks for, a place that carries claims but has no location, a source that was acquired and yielded nothing. These are emitted as gap patterns and become frontier questions — the system asking about the shape of what it knows, not just its contents.
every question lineage and every entity is a trajectory with a clock — the loop measures how each one moves
Information is mass arriving over time: verified claims weigh 1, unverified .4, corroborated ×1.5. Per cycle a thread has velocity (mass gained, smoothed), acceleration, and momentum = velocity × √mass × a bonus for independent sources. Coherence is the cosine between successive cycle centroids — speeding up in the same direction is acceleration with high coherence; speed with low coherence is churn.
Shannon bits over two distributions: which sources a thread's claims come from (low = a monoculture, one document speaks for it) and which entities they name (low = focused, high = diffuse). Per claim, surprise = −log₂ of how often its entity combination occurs; a bridge is a high-surprise claim joining two well-established entities that almost never co-occur — something between surfaces.
Flags are additive, not a ranking: accelerating, dormant, focused, diffuse, coherent, monoculture, woke up, bridging. The quiet focused pocket is listed because it is quiet. Momentum tells you where the information is running; entropy tells you how wide the road is.
three measured conditions — none of them an opinion
Every question closed. Nothing left to ask.
The cycle cap you set, or max_cycles (12 by default).
Three cycles running where each brought fewer than 0.5 new claims per source AND mean novelty under 0.15. Diminishing returns, measured.
the tray inside an investigation — every view reads the same store
Two things are always present: the Inspector — click any claim anywhere and its full evidence chain opens in the right rail — and the floating ask and note pair at the foot of the stage: one steps into the conversation with the record, the other opens a note already listening. Slash commands in the Ask composer drive the worker.
The instruments at your fingertips — one card per view with what it tells you, what it currently sees in this corpus, the latest break it found, and a mark when something changed since you last looked. Breaks (the moments what is known actually changed) name the instrument that found them.
Count tiles, the verified-evidence ring, epistemic and status mix, per-cycle information-gain sparklines — and “what the investigation noticed”: the questions the pattern loop asked on its own.
Prose written by the writer role from cited claims only. Every citation marker renders as a chip that opens the Inspector on the exact quote. Versioned, refreshed after runs and uploads.
The coverage instrument: density, coverage and gap per cell along the four axes. Click a cell to jump to the matching view.
The momentum-vs-entropy scatter per thread — which inquiries are hot, which churn without converging, which went quiet while focused.
Your margin, all in one place — a quick note on the whole investigation, and every note inked on a claim, a source, an entity or a break. Dictate with the mic; Whisper settles the text.
The question queue, grouped by state, each with its origin — why is the system asking this? Attach your own secondary questions at priority 0.95.
Every claim, filterable by status, epistemic type and text. Horizon-aware.
The bibliography, with a date-coverage strip and click-to-correct publication dates — the column the time machine stands on. Horizon-aware.
Ingested files, expandable to page-by-page digests with OCR, rotation and handwriting flags.
The source-to-source graph: solid arrows are explicit citations, dashed links are kinship (shared phrasing); hulls group lineages. Horizon-aware.
Drag-drop PDFs and text. Duplicates are caught before upload; a live queue shows each ingestion stage as it runs.
Paste URLs for the acquisition ladder, or issue a search that fans out into batched acquisition jobs — one digest email when the batch lands.
The constellation: a force graph sized by mentions, colored by kind, with community hulls, per-entity dossiers, an ego-focus mode and a time scrubber.
What the slow loop found — clusters, co-occurrences, temporal bunching, gaps — each with its supporting members.
Judge-confirmed conflicts, laid out side by side.
The zoomable evidence timeline: swimlanes by source, entity or epistemic type; green arcs corroborate, red arcs contradict; a density strip finds the core period. Horizon-aware.
One account retold over the years, with a strength score per telling — flags stories that got louder without gaining a single new independent lineage.
Geocoded entities on a basemap, co-mention arcs, place dossiers, time scrubber.
Then versus now — the comparison the horizon slider exists for. Detailed in the next section.
Every phase the worker executed, newest first, with token costs.
The queue: queued, running, done, failed.
Roles (admin / editor / viewer), invites, visibility, and an append-only event feed of who did what.
what did the record look like as of a date — or as known to one investigator?
The horizon system stands on one load-bearing column — published_at on every source — and a hard rule set designed to keep hindsight from leaking. Views that cannot honestly filter show an explicit full-knowledge banner instead of pretending to: filtering some numbers while others stay full-corpus is hindsight leaking through the arithmetic.
Toggle the horizon strip and Findings, Sources, Lineage and Chronoscope filter to sources published inside it. Findings recomputes each claim's source count within the horizon and suppresses stored status chips — a “corroborated” badge earned in 2005 must not decorate a claim viewed from 1991. Undated sources are never silently included or excluded: they are quarantined, and counted, visibly.
Name an investigator in the strip and mark sources “known to” them from the Sources view, with an optional reason logged to the event feed. A marked source is in-horizon regardless of publication date — including undated ones, which escape quarantine. The horizon stops being a calendar and becomes a person's knowledge state.
What was already in the record at the horizon (today's corroborated conclusions whose independent lineages all sat inside it, recomputed over in-horizon links only), what surfaced only later — ranked by the lag between event and record — which contradictions only became visible later, and what came after.
rsx horizon materializes a fork that honestly contains only the record as of the date: out-of-horizon sources removed, every full-knowledge judgment dropped — statuses reset, patterns cleared, corroboration links and narrative deleted — then recomputed from scratch inside the horizon. The fork records what was kept, cut and quarantined, and verify passes on it.
With the strip active every ask is horizon-constrained automatically: retrieval is restricted to in-horizon claims and quotes — a claim citing both a 1975 and a 2005 source surfaces only its 1975 quotes — and a deterministic year rule plus a judge pass strips hindsight sentences from the answer. The Inspector shows how many were stripped, with the text on hover, and the answer always carries a leak warning: Okusoko itself still knows the future, even when its citations don't.
the #1 foreseeable mistake — the horizon gates the assertion axis (when things entered the record), not the eventaxis (when things happened). “The record as of 1991” still describes events from 1975 — it just describes them using only what had been written down by 1991. Keeping those two time dimensions distinct is the whole discipline.
the arc the demo space actually ran — the 1975–79 Rio Arriba mutilation wave
One question creates the space. Config worth knowing lives in researchspace.json: chunk size, sources per cycle, cycle budget, pattern cadence.
The director decomposes the seed on first contact, then the ten stages cycle. When the pattern loop first fires, watch Overview's “what the investigation noticed” — questions with a pattern origin are the system asking things you didn't.
Web sources get you the retellings; the case lives in scanned documents. Drag the district office reports and Valdez's memos into Upload and watch the stage queue run — dedup, read, OCR, extract, summarize, document, advance. Then correct any inferred publication dates in Sources, backfill the rest, and let kinship work out who copied whom.
Findings filtered to contested shows where sources disagree. Lineage shows a dozen “independent” accounts are one wire story wearing costumes — which is exactly why the corroboration count didn't move. Chronoscope finds the core period; Entities puts Valdez at the center of ranchers, labs and reporters; Drift flags the tellings that grew stronger over the years with no new lineage behind them.
The Ask view does the same. The answer comes back cited, and every citation chip opens the Inspector on the exact verified quote.
Flip the horizon strip to 1979-06. Findings drops to what had actually been written down mid-wave; corroboration chips vanish (they were earned later); the quarantine badge counts the undated clippings. Open Horizon Diff to see which of today's conclusions were already fully supported inside 1979 — and which only became sayable years later. Ask the Gomez question again with the strip active and read the answer a 1979 investigator could have given, hindsight sentences stripped and counted.
The rigorous fork is a real space — runnable, forkable, verifiable — that honestly contains only the 1979 record. Hand the .rsx to a collaborator and their verify tells them, mechanically, whether to trust it.
python3 -m rsx.cli <command> <space> — the hosted product drives the same engine
eleven swap points, each with a contract you can honour without touching anything else
Each socket below is a place where the pipeline currently makes a decision with a simple rule. The contract line is what any replacement must take and return; satisfy it and the rest of the loop neither knows nor cares what's inside. Sockets marked markov-shaped are the ones where state-transition methods fit naturally — because the thing being decided already has states and moves between them.
now — A priority sort with an anti-starvation interleave. No learning: a question's expected yield never updates.
contract · (open questions, history) → one question
now — One call to the director returns three queries: precise, broad, archival. Nothing in the space's own graph informs them.
contract · question → string[]
now — The director ranks candidates comparatively, then novelty reorders within bands of three. Purely local — no notion of a source's standing in a wider graph.
contract · (question, candidates[]) → order + drop set
now — Cosine similarity to the question, top 40. Chunks are scored independently, which ignores that relevance runs in streaks down a page.
contract · (chunks[], question) → keep set
now — Pairwise name similarity with first-name compatibility and generic-surname guards. Effectively single-link clustering.
contract · mentions[] → canonical groups
now — The skeptic plus cross-source counting. Every source is treated as equally trustworthy, and status moves are decided one claim at a time.
contract · (claims, evidence, sources) → status ∈ {provisional, corroborated, contested, retracted}
now — Eight hand-written detectors: co-occurrence, bridge entities (spectral modularity communities + Brandes betweenness), cosine clusters, temporal mass, entity-attribute contradictions, gaps, cited-but-unheld documents, suspect entities needing identity confirmation. Each returns a strength score.
contract · store → patterns(kind, scale, strength, signature)
now — The director reads patterns above a strength floor and writes new questions, deduped against what's already open.
contract · patterns[] → new questions[]
now — Hand-written rules per axis: holes between busy years, thin coverage, single-source actors, unlocated places, silent or weakly verified documents. Each returns a strength.
contract · surface cells → gap patterns(cell, description, strength)
now — Mass-weighted velocity with an EMA, acceleration as its difference, momentum = v·√mass·source bonus, Shannon entropy over sources and entities, surprise from co-occurrence frequency, coherence from embedding centroids. Additive flags, no ranking-out.
contract · thread claims by cycle → series + flags (accelerating, dormant, focused, …)
now — Two hard floors over a three-cycle window: new-claims-per-source and mean novelty. Simple and honest, but it can only see backwards.
contract · metrics window → stop reason | null
a finished investigation exports whole — claims, snapshots, lineage
A ResearchSpace exports to a .rsx package: sorted JSONL tables, the raw snapshots, and a hashed manifest. Someone else can import it, run verify to re-check every quote against the bytes, fork it — which records your space as parent lineage — and merge findings back. Conflicting claims are never auto-resolved; they land in a merge report for a human to settle. That's the part that makes this closer to version control than to a chatbot.