Imported from ctoforaday/special-circumstances (
plugins/frank-exchange-of-views/skills/research-protocol/SKILL.md). Install upstream withnpx skills add ctoforaday/special-circumstances --skill research-protocol. Copyright stays with the author.
research-protocol
Research that survives an adversary.
Protocol
- BEFORE searching, YOU MUST formulate 3–5 frontier hypotheses — what would be true if each candidate answer were right — and record each one as a LINE OF INQUIRY on the record — the approach, and what would be true if it paid off; searches then test hypotheses instead of wandering. On the record rather than in a file, because a hypothesis red cannot rule
too-thinorout-of-scopeis one nobody can contest — and the opening hypotheses are the ones that shape the entire run. - During research, YOU MUST search to saturation: stop only when new searches return already-seen sources (typically 20–30 searches for a deep topic).
- During research, YOU MUST spend at least one search in five hunting disconfirming evidence against your current position. This is a drafting floor, not the verification: it keeps confirmation bias out of the draft; systematic disconfirmation is red's entire job.
- During writing, YOU MUST add every citation with the TOOL, against the exact sentence it backs — never by hand. The tool fetches the source once into the run cache, then splices an INVISIBLE, IMMORTAL
<!--cite:c-…-->anchor at that sentence; assembly weaves the anchors into the visible[^N]footnotes and composes the## Bibliography. A hand-typed[^label]is not a citation: nothing backs it, the claim counter does not see it, and the unbacked-citations detector flags it. An unreachable source is unusable — the cite is rejected, and the log is where you report it. - The bibliography is BOTH sides'. A red CORROBORATION — a source red went and found for a
claim blue made — mints an anchor and joins the footnotes the same way when it SUPPORTS the
claim. A reader cares that the text has appropriate references, not which seat inserted them.
A
refutesorabsentreading is not a reference backing the sentence and is never spliced: it is red finding the text unsupported, which is a defect, and it goes to the board as a finding — a PASS is refused until one is raised for it. - An anchor is part of the text you edit. The report as the tool renders it prints every
<!--fx:…-->,<!--cite:…-->and<!--proof:…-->as it is; quote the span as printed and copy each token into the replacement like any other character. A quote that stops just short of the anchor on the sentence it rewrites is refused, and the refusal names the token to carry. The anchor is never lost, and an edit that moves the words under one REOPENS it — the reference stands, its referent moved, so a verification of it is stale rather than refuted. - AFTER drafting, every claim MUST trace to a source a skeptic can follow; unverifiable claims are labeled as such, not laundered into fact.
- For PDF-only sources, a scanned PDF is read by the run's cached source read itself, and that reading is what a citation can locate a page in; YOU MUST read it there before grading down on a lossy fetch. For arXiv figures and tables,
arxiv-latexgives the exact LaTeX. A claim capped at "unable to corroborate" without trying these is an incomplete audit.
The exchange is TOOL-MEDIATED
Everything the two sides exchange — findings, closures, citations, proofs, revisions, lines of inquiry, disputes, log entries, opinions — is an event on the record, written through a verb that can refuse it, and read back through a projection. This is the governing clause of the protocol, not a storage preference: a hand-written file is an exchange nothing validated, and a fact recovered from a filename or a prose substring is one only pretending to be mediated. Both fail the same way — by returning a plausible zero, which reads exactly like a clean board. See [[facts-are-fields]].
The report set is the instructive non-exception: prose, because its audience is human. But
every point of argument in it carries a tool-placed anchor — cite: where a source backs
a claim, fx: where red challenged, proof: where a computation settles it — and dropping
one is a hard refusal. Write for the reader; put what the machinery depends on in a field.
The run directory
The tool is the read path. Where a line below says RECORD, that artifact has no
authoritative file — read it with show <name> and never from disk — show is a GROUP, so show --help lists every projection.
research/<date>_<slug>/
├── records/ # THE RECORD — the source of truth: records/record.db, the run's one store
│ # (MAY live outside the run entirely; a `.records-elsewhere` note appears
│ # here instead. Nothing changes for a seat, because a seat reads the
│ # record with `show <name>` and never from disk — which is the
│ # point: a run can be configured so that is the ONLY way, and then a
│ # missing verb has to surface as friction instead of a workaround)
├── README.md # the run's front door: verdict, gaps, and what each document holds
├── report.md # THE RESEARCH — verdict, Catechism, foundations, analysis, risks, open questions
├── docket.md # the board: every gap and how it closed, blue's manifest, red's spot-checks
├── debate.md # the transcript, epoch by epoch, and the bench's terminal disposition
├── judgments.md # motions — every contested question and how it was ruled
├── lines-of-inquiry.md # the directions: each line's fate, the path it took, its ruling and appeal
├── evidence.md # the computations, with script, output and sha256
├── run.md # friction, the record's invariant check, and cost
├── CHANGELOG.md # this report's own provenance: revisions, retired claims, repairs
├── report.html # the same set with real tabs and cross-document links — one file, no server
│ # (ALL of the above are assembled LAST, from the record, by `assemble`. The
│ # set exists because 70–76% of the single file was about the run and the
│ # research it was commissioned for was a quarter of the file.)
├── inputs/PINNED.md # the evidence base, pinned: repo HEAD at launch + cited corpora's commit/revision
├── blue/
│ # (the opening hypotheses are LINES OF INQUIRY on the record, not a file — read
│ # read them as the `lines-of-inquiry` projection. A hypothesis in a file is one red
│ # cannot rule too-thin or out-of-scope, and the opening ones shape the
│ # whole run)
│ ├── report.md # written by the synthesizer at synthesis, then frozen into the record
│ │ # by `ingest`, which deletes it. From then the report is read
│ │ # through the tool, and every change goes through the `edit` verb
│ └── candidates/ # best-of-N lane drafts, one method each, preserved (authored)
└── cost.md # measured tokens + dollars per seat-sitting (feov-record cost)
RECORD — no file at all; read through the tool. Every projection, what each is for, and the
verb that WRITES each one are in your role's help — `manual` prints every page of it in one call,
at the start of a sitting — which is generated from the command tree
and cannot disagree with it. A catalogue here would be a second copy that can.
run this first, and again before you stop: your work — everything open to you, each item
saying whether it is what blocks you closing, plus whether the sitting may close at all.
`complete: true` with items still open means the gates are satisfied, NOT that you are done.
trajectories/ journal.jsonl (the HARNESS's lifecycle record, tracked)
+ agent-transcripts.tar.gz (gitignored)
setup lays down directories and no file a seat fills. A stub is not an artifact: a
placeholder reads as the real thing. Measured in the 2026-08-05 run, stubs for the transcript and
the citation list finished at 36 and 46 bytes while the record held 122 events. In B7, setup still
stubbed report.md and blue/report.md: the synthesizer copied its draft onto the first, and
ingest froze the second — 40 bytes of heading — as the report's base. Anything under RECORD above
has no file at all — read it with show <name>.
blue/report.md is the file the synthesizer writes and then FREEZES: the freeze records its
text as the base of the record and DELETES the file (#709). From then it too is a projection —
there is no blue/report.md to open; read the report through the tool and change it only
through the tool's edit path, each change an event the report is replayed from. It cannot be
raw-written or bypassed, which is the point: the report a seat reads and the report the record holds
are the same bytes, by construction.
Termination is the record's, and the standing practice is stop-and-resume: the chair's dispatch next says who sits; the run ends when nobody is ready — PASS
permitted (VERIFIED), or every open material gap at its limit, ruled by the bench and remanded
(CEILING) — or when the run reaches its epoch limit with parties still ready (CEILING, the limit
named as the reason). The bounds are the run's terms, recorded at setup: the exchanges a gap gets before
impasse (k-max), the floor of the gaps a lens may mint (mint-budget), which the record raises
with the report's size in each lens's unit — citations, proofs, claims or prose paragraphs (plans/roundless.md §III.B.2) — and
the chair sittings the run gets (max-epochs, default 12). One stop is the engine's own: a dispatch
plan identical for three chair sittings in a row — the same parties readied against the same
head for the same reasons — is a loop nothing on the board is moving, and the engine ends the run
UNVERIFIED naming the stuck parties and the head. Red owns PASS/FAIL — is it defensible. The bench
owns the stopping judgment — is it close enough, the one call that weighs remaining defect
against remaining cost, and the only terminal value (economy) that otherwise has no organ. It
reads the telemetry projection — the series, never a snapshot — and files a reasoned,
cost-stated opinion; the operator acts on it,
stopping a run past its value and resuming for the honest UNVERIFIED
assembly — cache replay makes the stop ~$0 (measured). Stopping is not passing: the verdict
stays UNVERIFIED with the open count stated. Automatic severity-floor termination was evaluated
and REJECTED (run-4 report §1): it automates the one call that belongs to judgment. NEVER
change models on the resume.
All artifacts are git-tracked; nothing is summarized away. The payload is the file; the envelope is the handle — no large content travels through agent return values.
Reading the corpus — two access modes, never confused
There is no search index, and there are two access modes:
- Full read for the report — red reads blue's report whole, in context, every sitting. A snippet NEVER substitutes: a decontextualized quote is how audits go blind. This clause outranks any token saving.
- Leaf-node fetch for verification — a citation is checked against its source, never against a
summary. For a source BLUE CITED, read the exact bytes blue read from the run cache
(a cache hit, so you audit the same artifact, not a page that may have drifted since). For a source you discover yourself, pull it verbatim (Bash
curl, or the run's cached source read for a PDF). WebFetch is not used: it returns a summary, not the source.
To find text inside the run's own artifacts, use Grep — the terms you want are the terms you
already have, and a lexical match over a known file beats a ranked guess over a corpus.
Harness contract (one referenceable paragraph — three seats re-derived this at token cost)
The Workflow script's log() is operator-console-EPHEMERAL: it persists nowhere. The
transcript directory's journal.jsonl is the HARNESS's lifecycle record — started/result
events only, never script logs. Per-agent API transcripts are agent-*.jsonl (the cost
audit's input). Durable in-run state lives ONLY in the run directory (git-tracked run files) or
in envelopes; anything else evaporates with the session. Tool footguns with live recurrences:
Grep's count mode counts LINES, not occurrences (anchor patterns when counting); quoted
heredocs can eat backslashes (prefer the Write tool for scripts); the Read tool caps ~25k
tokens — a full-document read over that cap is consecutive whole windows, which satisfies the
full-re-read MUST without a confidence discount.
Report structure
What a run hands over is a SET, not a file (see references/report_template.md), and every document
in it opens with a link bar to the others.
report.md is the research: verdict stamp (the word alone — its argument opens Read this
first) → the Catechism (references/catechism_template.md — the worth-our-time decision,
adapted from Heilmeier) → analytical core (foundations / analysis / risk matrix graded
likelihood × impact × complexity, including risk-accepted items with rationale) → the three
inquiry areas → open questions, left open by this run (authored by blue into the report's ## Open questions, audited by red every sitting, lifted verbatim) →
footnotes (with access dates; volatility noted for living sources).
The debate's own documents are beside it, one per audience: docket.md (the board in
full), debate.md (the transcript), judgments.md (motions and rulings), lines-of-inquiry.md
(the directions and the path each took), evidence.md (the computations), run.md (friction, record verification, cost), CHANGELOG.md (the report's own
revisions and withdrawn claims). Nothing is summarized away by the split — the union is the
directory, indexed by README.md.
THE CITATION AND PROOF LAYERS ARE WOVEN PER DOCUMENT. A footnote definition cannot cross a file
boundary, so each document numbers and defines the references it actually carries; proof numbers
are run-wide, so P3 is the same computation wherever it is cited.
The log
A subagent's only voice is its return value — so capability complaints travel in the envelope.
- AFTER any task where a missing tool, denied permission, or missing capability impeded you, YOU MUST report it in the envelope's
logfield: name the capability and what you would have done with it. - AFTER any task where the material did not fit the shape you were given — a template section that made no sense for the topic, a protocol step that fought the work, an envelope field you had nothing honest to put in, content with no home — YOU MUST report the misfit as friction: name the template/step/field and what shape the work actually wanted.
- YOU MUST NOT silently work around a missing capability — the workaround destroys the signal that would get you retooled.
- The log is on the RECORD through each role's
logverb — every seat WRITES it, and the read is the OPERATOR's, on the operator's own surface and not on yours, because a missing capability is a report to the human who can retool the seat, not material for the debate; capture reconciles every envelope'slogfield against it, and the self-improvement loop consumes it. Complaints are how the system learns what its agents actually need.