Design note · 11 Sep 2026 · accepted, not yet implemented

The site is a pipeline with a jagged edge, not a corpus with some gaps.

A layer is not one of five fixed stages. It is a node in a versioned processing graph — a step, a revision, a feature, an open question, a candidate output — that runs against a paper and produces a new version of it. Layers stay visible as work proceeds rather than collapsing into a current state. The site's job is to show that graph, in general and applied to every paper, and to say honestly which cells are current, stale, absent, or impossible.

Status accepted and implemented, except §A layer is a proposal, which is proposed Plan #23 — this note is its design Frames #18 · #19 · #20 Declared in pipeline/layers.yaml Depends on runs/README.md · verification/*/provenance.json · mappings/*.json

Two separations

General from particular. Methods, formats, docs, agents, schema and standards describe the machinery and show no paper's output. A paper's pages show that paper's output and explain no machinery.

Layer from view. A layer is a unit of work with inputs, outputs and dependencies. A view — graph, list, table, marked document, comparison — is a way of looking at one. Views are shared vocabulary: a new layer usually reuses existing views, occasionally earns a new one.

Everything else follows. /extract is a layer's output for one paper, not a page. /agents is the general half of the induction layer. /standards is reference material about external formats, sitting outside the pipeline and connected to the layers that target them. An Introduction sits outside both, for the reader arriving cold.

The pipeline

Left to right by dependency. Dashed borders are corpus-scope layers — schema and vocabulary decisions that apply to every paper and, when they change, make every dependent paper-instance stale. Filled boxes are artifacts rather than steps.

Source
paperJATS or PDF, cached
claim-formatcorpus · schema
relation-vocabcorpus · question · #19 #20
Induction
results-readerstep · candidate
caption-readerstep · candidate
structure-readerstep · candidate
reconcilerstep
edge-inferencestep
Tree
claim treeartifact · versioned
stance / alternativesfeature · revises tree
relation re-typingrevision · from #19
Measures
abstract↔claimsstep · leaf
verificationstep
coverage spansstep
adjudicationstep · judgement
marked paperartifact · leaf
Interchange
mira exportstep · branch
oxa exportstep · branch
dg exportstep · from oxa
formats reportartifact · leaf
gap reportartifact · leaf
paper-scope layer corpus-scope layer artifact

The shape matters more than the contents. Induction fans in — three candidates reconciled to one. Interchange fans out — one tree into three encodings that never rejoin. Coverage is the only layer that reads the paper again after induction, which is why it is the one that can find what everything else missed. And the two corpus-scope nodes on the left have edges into almost everything on the right, which is why a schema decision is never a small change.

Five kinds of layer

The kind is not decoration: it determines whether a layer can be re-run mechanically, and what its output supersedes.

KindProducesRe-runnableExample
stepAn artifact from declared inputsYes, mechanicallycoverage spans, MIRA export
candidateOne of several parallel outputs, kept rather than collapsedYes — and worth re-running with a different modeleach of the three readers; a prompt variant
judgementA decision stored so it can be disputedNo — recomputing destroys the recordcoverage adjudication, review gate
featureA new capability that revises existing artifactsOnce, then it is part of the schemastance and alternatives
questionA decision about meaning, then a re-typing of what it governsThe decision no; the propagation yes#19 dissociates-with, #20 epistemic

#19 is the case that proves the model. "Is dissociates-with an opposition?" has a general half — the definition, the decision, the declaration in the exporter — and a per-paper half: which of this paper's relations are affected, and what changed when the answer landed. It is a layer with 65 instances across 10 papers, it sits upstream of every export, and when it resolves, every export downstream of it is stale. Exactly like a processing step. That is why it belongs in the same structure as one, and why a new paper arriving later gets the same question asked of it automatically.

A layer is a proposal proposed · not built

Everything above describes layers that exist. This describes how one comes to exist, and how an existing one is corrected — which turn out to be the same act.

A declaration is a proposal. It says a step should exist and what it does. It goes up as a page — the question, the mechanics, a worked example, what it would change, what it feeds — and it is discussed, improved, and at some point accepted. Adding a step to the pipeline and correcting an existing one are one operation: a new version of a declaration.

This generalises something the repository already does by hand. Every schema change so far — stance, the relation vocabulary, the epistemic question — has been an issue, then an argument, then an edit. That is a proposal being accepted, without a place to stand or a record of who accepted it.

What judges a declaration

Three things, and the third is what makes the pipeline worth building.

EvidenceAsks
the logicIs the question worth asking, and is it the right question?
the codeDoes the mechanism answer the question it claims to?
the outputsWhat did the runs actually produce, across the papers they ran on?

The third is why an output approval matters beyond itself. Reading Gädeke's claim tree v3 and approving it is evidence about the claim-tree declaration, not only about Gädeke. A declaration accepted on argument alone is a guess; one accepted with approved outputs behind it has been tried. So the two kinds of approval are not parallel — the per-paper ones accumulate into the corpus-level one.

Two subjects, one operation

Approval applies to a version. What differs is whose version.

SubjectScopeAsksRecorded in
declarationcorpusShould this step exist, and is this what it should do?runs/approvals.jsonl
outputpaperIs what this run produced right?runs/<paper>/approvals.jsonl

Keeping these apart is not pedantry. They were conflated once already, under one word review, and the result was two things in the repository with the same name doing different jobs.

The comments are the issue

A proposal needs somewhere for people to argue with it, and a static site cannot hold a comment thread. It does not need to: the layer's GitHub issue is the thread. relation-vocab already declares issue: 19 and its page links it. The field simply stops being something only question layers carry, and the page shows the discussion's state rather than only its address.

The consequence with teeth

If accepting a declaration revision changes what a layer does, every run of that layer is stale. That follows from the propagation rule already built — but only if the declaration is itself a hashed input to every run, and today it is not: reads hashes scripts and prompts, not pipeline/layers.yaml.

Closing that makes revising a definition turn the matrix amber for every paper the layer touches, which is exactly what should happen and currently does not. It is a small change and the sharpest test of whether the model is real.

Lifecycle

proposed → accepted → superseded. The open: true flag on the question layers is a crude form of it, and replication — declared, with no runner and no runs — is simply a proposed layer that nobody has accepted or tried. Saying so is better than an empty column with no explanation.

This note is an instance of what it describes The proposal to make layers into proposals is itself a proposal: a declaration, put up as a page, open to argument, and not yet accepted. Under the model it describes it would carry status: proposed and an issue for its comments — which is what it has. That it can describe itself without strain is the best evidence available that the model holds.

State, not presence

A cell is not a tick. The interesting states are the two the site cannot currently express — stale, and impossible — and the jagged edge is the normal condition, not a defect to hide.

The five states

current ran, and every declared input still hashes to what the run recorded.

stale ran, but an input changed since. The output is still there and no longer trustworthy.

absent never run. The site shows what would run it, and what that depends on.

n/a declared impossible, with a reason. Artiushin's verification is image inspection; Kammer's needs a specialist fMRI pipeline.

open a question-layer with no decision yet. Downstream is provisional by definition.

Propagation

Staleness flows along dependency edges. Re-type a relation and every export is stale; regenerate the exports and the formats report is stale; change a prompt and every run that used it is stale.

This is the rule that would have caught the three drifts found this week: Gädeke's OXA measuring 89 of 93 relations after the stance work, the standards page publishing 57% after the exporter changed, and a verification banner describing a run superseded by the audit.

None of those were noticed by anyone looking at the page. All three were computable.

Paperinducttreeabs↔clverifyspansadjudmarkedstancemiraoxadgformats
Gädeke
Kolb
Wengert
Rozak·
Headley
Ejdrup
Scheller
Bouyeure
Artiushin·
Kammer·
current stale absent· not applicable

Drawn from the artifacts on disk today. It is mostly honest already — what it cannot yet say is stale, and every drift we found this week would have shown as an amber column.

Where versions come from

This was the open question. The answer is not a hand-written changelog and not git log — it is the run record, which three parts of the repository already keep independently and none of them share.

Already built, three times

runs/<paper>/manifest.json — per role: prompt path, prompt hash, model, output, output hash. Its README already states the rule: "if the hash of a prompt file no longer matches what a run recorded, that run was produced by a different prompt than the one now in the tree."

verification/<paper>/provenance.json — argv, interpreter, data commit, every file opened with a hash, exit code.

mappings/<paper>.json — a sha per verdict, discarded rather than applied when the sentence it judged has changed.

Plus byte-stable exports, where git diff is the staleness check. Four mechanisms, one problem, no shared vocabulary.

One ledger instead

Every layer run appends a record. Inputs and outputs by path and hash; the declared dependency edges come from the layer definition, not from the record.

Version history is the ledger sorted by date. Staleness is re-hashing the inputs. Propagation is a walk over the dependency graph. The changelog line is the one hand-written field, written at the moment of the change by whoever ran the layer rather than reconstructed from a diff months later.

Git stays the audit trail underneath. The ledger is the index into it.

runs/gadeke-2026-guilt-insula/ledger.jsonl
{"layer":"stance", "v":3, "ran":"2026-09-10T18:22Z", "kind":"feature",
 "note":"Six alternatives authored from the controls' closing clauses; false contradiction replaced with qualifies.",
 "in": [{"path":"claims/gadeke.../*.md","sha":"a41c…"}, {"path":"docs/claim-format.md","sha":"9be0…"}],
 "out":[{"path":"claims/gadeke.../alt-*.md","sha":"77d2…"}],
 "by": "claude-opus-5"}
{"layer":"mira-export", "v":7, "ran":"2026-09-11T08:04Z", "kind":"step",
 "note":"Regenerated after stance; 6 nodes gain haak:stance.",
 "in": [{"path":"claims/gadeke.../*.md","sha":"a41c…"}, {"path":"scripts/export_mira.py","sha":"5f31…"}],
 "out":[{"path":"exports/gadeke….mira.jsonld","sha":"c0a8…"}],
 "by": "scripts/export_mira.py"}

The second record's first input is the first record's output hash. That single fact is what makes the whole graph checkable: change the claims and a41c… no longer matches, so the export is stale, so the formats report that consumed it is stale, and the site can say so without anyone remembering to.

One addressable cell

The matrix is not a picture of the corpus. It is a view over the ledger, and every cell in it is a row that can be fetched, rendered, compared or run.

A cell is (paper, layer, version). A view is how you look at it. Everything the site does is one of four verbs on that tuple: render it, fetch it, compare two of them, or run the layer that produces one.

VerbCallGives
render/papers/<paper>/<layer>[@v]/<view>The page. @v omitted means current; the view defaults to the layer's own.
fetch/data/<paper>/<layer>[@v].jsonThe same cell as data, pre-rendered in a static build. This is what makes a candidate exportable rather than merely visible.
compare/compare?a=…&b=…Two cells side by side: two versions of one layer, or two layers of one paper — mira against oxa, draft against final, v2 against v3.
runPOST /_run {paper, layer}Dev mode only. Runs the layer and its unmet dependencies, appends to the ledger. The published build shows the same command as copy-and-run text.

A layer page is then a query with the paper unbound, and a paper page a query with the layer unbound. The matrix is the query with both unbound — which is why it is worth building as a real view over the ledger rather than as a table someone maintains. Useful queries fall out of it: every stale cell, every paper missing coverage, every instance of one question across ten papers, everything a given prompt version produced.

Two consequences worth stating. Candidates become first-class for free — a second prompt variant is a version like any other, addressable, fetchable and comparable against the one that shipped, instead of being overwritten by it. And a cell that cannot be rendered can still be described: absent cells carry the command and the dependency chain, so the jagged edge is navigable rather than blank.

Views are a shared vocabulary

Six views cover every layer we have. A new layer picks from them; earning a new view should be rare and deliberate.

ViewShowsUsed by
graphShape — the deductive spine, what hangs off whattree, stance, verification status over the tree, relation re-typing
listThe text. The only form in which every claim can be readevery layer
tableColumns you sort and scanverification, coverage gaps, formats, candidates
documentThe paper itself, marked in placecoverage, abstract↔claims
comparisonTwo encodings of one thing, side by sideformats, draft ⇄ final, reproduced ⇄ published, before ⇄ after a revision
overlapSet relations between parallel candidatesthe three readers; prompt variants

Comparison is the view the pipeline model makes newly important. If a layer produces a new version, the interesting thing is rarely the new state alone — it is the diff. What did the stance layer change? What did re-typing 65 relations change? A revision layer with no before-and-after view is just a silent edit.

Pages

SurfaceScopeHolds
/ (introduction)outsideWhat this is, why panel-level claims, what the prototype shows and does not. For the reader arriving cold — including eLife.
/pipelinegeneralThe graph above. Every layer's definition, kind, dependencies, views, and the command that runs it. The corpus matrix under it.
/pipeline/<layer>generalOne layer in general: what it asks, what it produces, its state across all ten papers, and what it found corpus-wide.
/papersparticularTen papers with their fill state visible — the jagged edge as the index's main information.
/papers/<paper>particularThis paper's instance of the pipeline. Layer navigation, each layer's own structure and views, provenance on every generated thing, and its version history from the ledger.
/standardsreferenceWhat we assume and know about MIRA, OXA, CiTO, Discourse Graphs and our own schema. Outside the pipeline, linked from the layers that target each format.
/docsreferenceThe CLI and the machinery. Already correct; unchanged.

Note what disappears: /extract, /agents, /layers, /formats, /formats/mira and /findings are all either a layer in general or a layer applied to a paper. None of their content is lost; all of it moves to the altitude it belongs at.

Order of work

Deliberately: the model and its data before any page is moved. Rearranging pages first would just relocate the drift.

  1. Declare the layers. One file — id, title, question, kind, scope, depends_on, produces, views, command. The pipeline graph and every page below read from it.
  2. Write the ledger. Backfill from what exists: runs/, verification/, mappings/, and the export files. Every layer's runner appends to it from then on.
  3. Compute state. Hash inputs, compare to the ledger, walk the graph. Output goes into corpus-facts.json beside the layer detection already there.
  4. Build /pipeline — the graph, the per-layer pages, the matrix with all five states. This subsumes /layers and both /formats pages.
  5. Rebuild the paper page around layer navigation, with views and provenance, absorbing /extract and the Gädeke half of /agents.
  6. Strip the general pages of per-paper content: /agents, /standards.
  7. Add dev-mode run and the compare view, once cells are addressable.
  8. Write the introduction last, when there is a settled thing to introduce.
Revised 11 Sep, after five steps The plan first said build at a parallel prefix and swap when it is better. That is the mechanism this rebuild exists to reverse: every step adds a surface and defers the removal to a step that keeps receding. Five steps in, the nav had gone from eight items to ten and no route had been deleted. The rule is now no new route without deleting one, and the target is four nav items and roughly six routes. A deletion still waits for its replacement to exist — that is a sequencing constraint, not a deferral.

Decided

Answers to the five questions the last draft left open, recorded here so the implementation does not have to re-ask them.

QuestionDecisionImplication
A · running Local dev mode runs layers; the published build shows copy-and-run. Two behaviours from one cell definition. The command string is in the layer declaration either way.
B · grouping Groups are real. Induction groups five runs. A group is a layer with children, not a display convention — the same shape will be wanted for binding related questions into one parent, as sub-issues.
C · candidates First class, exportable and comparable. Drives the addressing above: a candidate is a version, reachable by the same call as any other.
D · naming versions / views / layers. A layer runs and produces a version; a view renders one. "Step" stays informal, for a layer whose kind is mechanical.
E · questions #19 becomes a layer after the issue is examined. The reading happens in the issue; the decision and its propagation become a declared layer with per-paper state, so a new paper gets the question asked of it automatically.