diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 6aa0068..0000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,88 +0,0 @@
-name: CI
-
-on:
- push:
- branches: [main]
- pull_request:
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- check:
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- matrix:
- # PULL REQUESTS RUN ONE INTERPRETER; MAIN RUNS BOTH. Conditional, so here
- # is why, because the next reader will ask.
- #
- # 3.13 is what every developer machine and every verification clone uses.
- # That makes 3.12 the ONLY environment in which pyproject.toml's
- # `requires-python = ">=3.12"` is tested at all. Dropping it entirely
- # would leave that declaration an untested claim carried forward on
- # nobody's authority -- which is the shape of the most expensive failure
- # in the project this template came from: an inherited criterion used
- # unexamined across TEN experiments before anyone asked what it tested.
- # See met-000-EXAMPLE in docs/inherited-measurements.yaml.
- #
- # KEEPING 3.12 ON `main` means the claim is still tested before anything
- # lands, while a PR iteration pays for one interpreter instead of two.
- #
- # ONE JOB WITH A CONDITIONAL MATRIX, not two jobs with `if:`. Two jobs
- # would need the whole step list twice -- install, lint, format, types,
- # tests, and every comment explaining them -- so each future edit would
- # have to be made in both copies and would eventually be made in one. The
- # cost of this form is that the expression is dense; the cost of the
- # other is a duplicate that drifts. `fromJSON` is needed because a matrix
- # dimension must be a real list, and a GitHub expression cannot produce
- # one literally.
- # Folded scalar: every continuation line sits at the SAME indent, so YAML
- # folds the newlines to spaces and Actions receives one expression.
- # Indenting them further would make YAML keep the newlines literally.
- python-version: >-
- ${{ github.event_name == 'pull_request'
- && fromJSON('["3.13"]')
- || fromJSON('["3.12", "3.13"]') }}
-
- steps:
- - uses: actions/checkout@v4
-
- - uses: actions/setup-python@v5
- with:
- python-version: ${{ matrix.python-version }}
- cache: pip
-
- # Project dependencies are deliberately absent from this file: they belong
- # in pyproject.toml, so that a local environment and CI install the same
- # set and neither can drift ahead of the other. Add optional extras to this
- # line only when CI needs an extra a user does not -- and when you do, say
- # in a comment WHICH TEST would otherwise skip, because a step installed
- # for no stated reason is one nobody can later remove.
- - name: Install
- run: |
- python -m pip install --upgrade pip
- pip install -e ".[dev]"
-
- - name: Lint
- run: ruff check src tests
-
- - name: Format check
- run: ruff format --check src tests
-
- # Type checker. Blocking, not advisory: a type error is a failure.
- - name: Types
- run: mypy
-
- # The suite. Includes the spikes/ import ban and the no-reference-checkout
- # guard, which is why those rules can be stated as enforced rather than
- # intended. Note that the reference clones are git-ignored and absent here,
- # so any code that reached into one would fail at this step.
- #
- # `-rs` REPORTS SKIPS. A test that needs an instrument the runner does not
- # have -- a GPU, a renderer, a licensed solver -- should skip with a reason
- # naming what is left unverified, and that reason should be visible in the
- # log. A silent skip is a gap in the evidence that looks like a pass.
- - name: Tests
- run: pytest -rs
diff --git a/.github/workflows/report.yml b/.github/workflows/report.yml
index f6fabd3..ec4df59 100644
--- a/.github/workflows/report.yml
+++ b/.github/workflows/report.yml
@@ -1,10 +1,19 @@
-# The report build. SEPARATE from ci.yml on purpose: that workflow is the code
-# gate and runs a two-version matrix, and there is no reason to install a TeX
-# distribution twice or to couple a prose change to the test suite.
+# The report build, and THE ONLY WORKFLOW IN THIS REPOSITORY. The template shipped
+# a second one, ci.yml -- a two-interpreter matrix running ruff, mypy and pytest
+# -- and it was deleted at instantiation (fc-001 in docs/state.yaml): this
+# repository has no library, no test suite and no pyproject.toml, so that job
+# could not even reach its first step, and a workflow that is red for reasons
+# unrelated to the report is one people learn to override.
#
-# Triggers mirror ci.yml rather than firing on literally every push, so a branch
-# with an open pull request is not built twice for the same commit. Every change
-# in this repository reaches main through a PR, so this covers every change.
+# WHAT THAT COSTS, said here rather than left to be discovered: this is now the
+# whole automated surface. Nothing checks that docs/state.yaml and
+# docs/inherited-measurements.yaml are parseable YAML, and they are the record.
+# See od-004, which is open and blocked only on the question of adding a
+# dependency.
+#
+# Triggers do not fire on literally every push, so a branch with an open pull
+# request is not built twice for the same commit. Every change in this repository
+# reaches main through a PR, so this covers every change.
#
# WHY THE REPORT IS COMPILED IN CI AT ALL. The report is where a measurement
# becomes a claim, and a document that only ever builds on the author's machine
@@ -59,10 +68,10 @@ jobs:
biber \
latexmk
- # No language runtime step, and that is the point: report/figures/*.pdf are
- # committed, so the document compiles from a clean checkout with a TeX
- # installation alone. See report/figures/README.md for why the figures are
- # committed rather than regenerated here.
+ # No language runtime step, and that is the point -- doubly so here, where
+ # there is no language runtime to install. Generated figures would be
+ # committed rather than built on the runner; there are none today. See
+ # report/figures/README.md.
- name: Build
run: make -C report
diff --git a/.gitignore b/.gitignore
index 5378da7..b0761c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,19 @@
-# Python
-__pycache__/
-*.pyc
-.venv/
-venv/
-.pytest_cache/
-.mypy_cache/
-*.egg-info/
-build/
-dist/
+# Reference clones, disposable. Pinned checkouts of the repositories this report
+# makes claims ABOUT are cloned here and read BY PEOPLE. This is the mechanism
+# the claims-verification pass runs against: every figure in report/draft.md is a
+# claim about `visgraf/bio-3d-vision` or its predecessors, and "verifiable at
+# source" means a checkout at the SHA pinned in docs/inherited-measurements.yaml.
+#
+# THE ENFORCEMENT THAT USED TO SIT BESIDE THIS IS GONE, and that is worth saying
+# rather than leaving to be discovered. The template guarded this directory with
+# a test asserting that no code reads from it. This repository has no code, so
+# the guard had nothing to guard and was deleted with the rest of the suite. The
+# convention survives on nobody's authority but this comment. If a figure script
+# ever lands here, restore the guard with it -- see report/figures/README.md.
+.reference/
+
+# LaTeX build artifacts are ignored in report/.gitignore, beside the build that
+# makes them.
# macOS
.DS_Store
@@ -16,11 +22,12 @@ dist/
.vscode/
.idea/
-# Reference clones, disposable. Pinned checkouts of the repositories this
-# project reads from are cloned here and read BY PEOPLE, never by code:
-# tests/test_scaffold.py enforces that. They are absent in CI, so any code path
-# that reached into one would pass locally and fail everywhere else.
-.reference/
-
-# Run artifacts
-results/
+# Python. Nothing in this repository is Python -- there is no package, no test
+# suite and no pyproject.toml. These stay because a one-off script written in
+# report/figures/ would otherwise drop caches into the tree, and because the
+# virtualenv left over from the template instantiation is still on disk.
+__pycache__/
+*.pyc
+.venv/
+.pytest_cache/
+.mypy_cache/
diff --git a/CLAUDE.md b/CLAUDE.md
index b573d97..f41bc8d 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -18,18 +18,28 @@ types and review catch it. Here the characteristic failure is a **wrong belief**
by a number that was computed correctly and means something else. No test
catches that. Every rule below exists to prevent one.
-**FILL THIS IN, then delete this instruction.** One paragraph: what is being
-investigated, what the predecessors are if there are any, and — the part that
-does the work — **what failure this repository exists to not repeat.** Naming
-that failure is what makes clause 1 of the definition of done specific rather
-than pious.
-
-> *Example, from the instance this template came from:*
->
-> `bio-3d-vision` is a research project on active 3D vision — successor to
-> `visgraf/active-stereo` and `visgraf/bioeye`. The predecessors built good
-> components. Neither ever ran the loop those components were for. That is the
-> failure this repository exists to not repeat.
+`method-notes` is a **document-only** repository. It holds one technical
+report — *Personal Notes on a Methodology for Research and Development in
+Applied Computational Mathematics* — its markdown draft, and the
+claims-verification pass over that draft. There is no library, no experiment,
+and no measurement apparatus here, and there is not going to be one.
+
+The report is **about another project**: `visgraf/bio-3d-vision`, and behind it
+`visgraf/active-stereo` and `visgraf/bioeye`. It reports what a methodology cost
+and caught over five days of work in those repositories.
+
+**What failure this repository exists to not repeat.** The source project's own
+technical report went into its first draft with eighteen load-bearing claims and
+came out of verification with seven standing as written. Nothing in it was
+invented — every claim traced to something real in the record. Three were wrong
+on the figure and eight were true only in the band, arm or metric they came
+from. **This report is a document whose entire content is claims about that
+record, written by someone who has read it and believes them.** It is therefore
+exposed to exactly the failure it describes, at a higher rate than the document
+it describes, because it has no experiments of its own to be corrected by. Every
+number in `report/draft.md` is inherited and unverified until read at source.
+That is the failure this repository exists to not repeat, and §8.4 of the draft
+takes the recursion as a subject rather than a disclaimer.
Nothing here is established yet. See `docs/state.yaml` for what actually runs.
@@ -76,71 +86,96 @@ it has been tried and settled, it is a **foreclosure**: `docs/state.yaml` under
> `docs/adr/` and say what they hold that a foreclosure entry does not.
**Every iteration regenerates the same artifact beside the previous one's** —
-same inputs, same seed, written next to its predecessor rather than over it. You
-compare against the last one by looking. An iteration that produces nothing to
-put beside the last is not an iteration.
+same inputs, written next to its predecessor rather than over it. You compare
+against the last one by looking. An iteration that produces nothing to put
+beside the last is not an iteration.
> *Why:* overwriting is how a regression becomes invisible; a difference nobody
> can see is a difference nobody reports.
-
-**`spikes/` is where you try things.** Git-tracked, and that is the whole
-ceremony: no decision record, no findings entry, no definition of done, and
-no tests required.
-Nothing in `src/` or `experiments/` may import from it, and CI enforces that. A
-spike's only permitted output is **a decision or a deletion** — write what you
-learned into `docs/state.yaml` and delete it, or delete it. See
-`spikes/README.md`, and note the trigger there: **a question written about twice
-without being tested goes to a spike.**
-> *Why:* in the predecessor a step was written up twice and never started,
-> accumulating five open questions, because writing a document was the only
-> sanctioned response to not knowing; in the instance `spikes/` shipped on day
-> one with its rules and its enforcement test and was never used once.
-
-## Domain invariants
-
-**EMPTY ON PURPOSE. FILL IT IN, or delete it and record why.**
-
-This is the section that does not generalise, and it is the section that does the
-most work. It holds the facts of *your* domain that a plausible sentence can
-violate without any test noticing: units, coordinate frames, sign conventions,
-what a positive number means, what the null hypothesis is, which quantities are
-commensurable, what "the same scene" means.
-
-**The rule for what goes here:** an invariant belongs in this file only if it
-cannot be declared at the place the value is produced. Prefer the code — see
-*Where authority lives* below, which is not optional and which constrains this
-section.
-
-
-
----
-
-
-A filled example, from the instance this template came from.
-Delete this block once yours is written.
-
-> **Two stimulus sources, and they are not peers.**
-> - *One rendered source:* Blender. When a result needs an image, it comes from
-> there. Adding a second renderer needs an ADR.
-> - *One analytic fixture:* closed-form, milliseconds, no monocular depth cues.
-> It is a **test fixture, not a scene family** — it exists so tests are fast and
-> exact. The moment a finding rests on it, it has been misused. Do not grow it
-> into a corpus; 42% of the predecessor's library was stimulus infrastructure.
>
-> **Depth is not one quantity.** The loop's estimator returns planar `z` in the
-> rectified frame *at the measuring fixation*, which is fixation-dependent, so
-> fusing it across fixations fuses different quantities. Convert to range from
-> the cyclopean origin before fusing.
-
-**Note the shape of both.** Each names a thing that is easy to state wrongly and
-says which side is right. The second goes further and names **the operation that
-silently produces a wrong number if you get it backwards** — that clause is worth
-more than the rest of the entry, because it is the one that turns an invariant
-into something a reviewer can check a diff against.
+> *What this means here, where the artifact is prose.* `report/draft.md` is
+> tracked, and it is typeset into `report/main.tex` in the same pull request, so
+> the two revisions sit side by side in the history and the sentence that changed
+> on its way into LaTeX is visible as a change rather than as a fact. A
+> verification pass regenerates the count table in `report/claims-verified.md`
+> beside the previous count, never over it. A second pass that overwrites the
+> first destroys the only evidence of whether the prose got better.
+
+**`spikes/` is not part of this repository, and its absence was decided rather
+than defaulted.** The template ships a low-ceremony lane; this repository has no
+lane to be low-ceremony *about*, because it runs nothing. The exploratory work a
+spike would hold is a discarded draft section, and that already has a home in
+`report/`.
+> *Why the absence is recorded instead of silent:* the source instance shipped
+> `spikes/` on day one with its rules and its enforcement test and used it zero
+> times in thirteen experiments, and §6 of the draft argues that a project with
+> strong ceremony structurally will not reach for its informal lane.
+> Instantiating the directory here would have reproduced the exact failure the
+> report identifies, and its emptiness would then have been read as evidence for
+> the argument rather than as a consequence of it. See `fc-004` in
+> `docs/state.yaml`.
-**Note also what is *not* in the example:** the units of any particular return
-value. Those are declared at the function that returns them, never here.
+## Domain invariants
-
+This section holds the facts of this project's subject matter that a plausible
+sentence can violate without anything noticing. **The template's rule for what
+goes here — prefer to declare it at the place the value is produced — has no
+force in this repository, because no value is produced here.** There is no
+function with a docstring to carry a unit. Every number arrives from somewhere
+else already formed, which means this section is the *only* place these can live,
+and the *Where authority lives* section below is inert. That inversion is the
+main structural consequence of being document-only, and it makes this section
+load-bearing rather than supplementary.
+
+**Four repositories, and claims about them are not interchangeable.**
+
+| | what it is | what claims about it look like |
+|---|---|---|
+| `visgraf/bioeye` | predecessor; closed the accumulation loop, not the perception–action loop | qualitative, mostly |
+| `visgraf/active-stereo` | predecessor; verified geometry, a decision record, the loop never ran | the low-ceremony-lane failure, the constitution clause that was false |
+| `visgraf/bio-3d-vision` | **the instance.** Thirteen experiments, two ledgers, a report and its verification pass | *every count in the draft* |
+| `visgraf/math-ai-method` | **the template**, which this repository was instantiated from | what it ships, what it fixed |
+
+**The instance is not the template, and the sentence that conflates them is the
+one to watch.** A count about thirteen experiments is a fact about
+`bio-3d-vision`. A claim about what a rule fixes is a fact about
+`math-ai-method`. They are routinely stated in adjacent sentences, and the
+template's own front page has already been caught attributing an instance count
+wrongly — see `docs/spec-defects.md` §3. When a figure appears, the question is
+always *which repository is this true of*, and the answer belongs in the
+sentence.
+
+**Counts are taken from the set, never from the highest identifier.** The
+instance ran experiments `exp001`–`exp005` and `exp007`–`exp014`. That is
+**thirteen**, not fourteen: `exp006` was specified and deliberately not run.
+Any count of experiments, foreclosures, or ledger entries is computed by
+enumerating the things and counting them, and the enumeration goes in the entry
+in `docs/inherited-measurements.yaml` beside the number.
+
+**A denominator is part of a figure, not context for it.** Two counts of the
+instance's verification pass are in circulation — *22 claims checked* and
+*eighteen load-bearing claims* — and they agree on 3 corrected and 8
+under-qualified while disagreeing on the total and on the confirmed count. They
+are reconcilable if "load-bearing" names a subset, and **neither passage says
+so.** Do not quote either figure without its denominator and the word that
+selects it. See `mn-001` and `mn-002` in `docs/inherited-measurements.yaml`;
+they are recorded as two entries precisely because prose cannot hold a
+disagreement and two data can.
+
+**`report/draft.md` is the prose of record; `report/main.tex` is a derivative.**
+When the two disagree about a number, the draft is not automatically right — but
+the disagreement is always a defect, and it is *the* defect this arrangement
+exists to expose. **The operation that silently produces a wrong number here is
+typesetting**: a qualifier dropped, a digit transposed, or a scope clause that
+did not survive being turned into a sentence is invisible in either document
+alone and obvious between them. Never edit one without diffing the other in the
+same pull request.
+
+**Every figure in this report is `inherited` and none will ever be `measured`,
+with one exception.** The exception is the verification pass over this report's
+own claims: its tally is produced here, and it is the only number this
+repository will ever own. Do not let the exception widen — a figure recomputed
+by reading a source is still inherited, because the conditions it was produced
+under are still not this repository's.
## Definition of done
@@ -151,8 +186,17 @@ A task is done when all of these hold:
> *Why this clause is first:* the predecessor's five conditions were all about
> artifact quality, so a perfectly-tested component could be added to a loop
> that had never run — and was.
-2. New behaviour has a test, and the suite passes.
-3. Units, frames and conventions are stated where the code states them (below).
+2. **Every claim it adds to the prose is traceable.** There is no test suite
+ here and clause 2 cannot be about one. Its replacement is the standard the
+ draft is held to: a new factual sentence names which repository it is true
+ of, and any figure in it has an entry in
+ `docs/inherited-measurements.yaml` — written at the same time, not later.
+ > *Why the substitution and not a deletion:* clause 2 existed so that new
+ > behaviour could not arrive unchecked. New prose is the only thing that
+ > arrives here, and the unchecked version of it is this project's entire
+ > failure mode.
+3. Conventions are stated in `## Domain invariants` above, because there is no
+ code to state them at. See the note there.
4. `docs/state.yaml` is updated if any of it changed.
5. The diff contains nothing that was not asked for.
@@ -162,6 +206,20 @@ project is not moving and that is worth saying out loud.
## Where authority lives
+**INERT IN THIS REPOSITORY, AND KEPT SO THAT ITS INERTNESS IS DELIBERATE.** The
+rule below is the template's best rule and it needs a function to attach to.
+There is none here. Authority over the facts in this project lives in the
+**pinned reference checkouts** named in `docs/inherited-measurements.yaml`: the
+source, not the sentence. The practical translation is that a number's authority
+is a file and a line in another repository at a stated SHA, and the ledger entry
+is the docstring's stand-in — with the important difference that a docstring sits
+beside the code it describes and can be checked against it in one glance, while a
+ledger entry can drift from its source silently. **That is a real weakening, not
+a clean substitution.** The claims-verification pass is what compensates for it,
+and it is the reason that pass is not optional here.
+
+The original rule, kept because it is what the substitution is measured against:
+
**Units, frames and conventions are declared at the function that returns the
value, in its docstring — not here.** This file does not restate them,
deliberately.
@@ -173,19 +231,25 @@ deliberately.
> a governance file has nothing to fail against. A docstring beside the return
> statement is checkable against the code under it.
-So: this file governs process. Code governs facts about code. When they
-disagree, the code is the finding and this file is the bug.
+So: this file governs process. In the template, code governs facts about code.
+Here, **the pinned sources govern facts about the world, and this file governs
+how a claim about them may be written.** When the prose and a source disagree,
+the source is the finding and the prose is the bug.
## Working agreement
-- **Branch.** `feat/…`, `fix/…`, `exp/…`, `docs/…`, `spike/…`. Never commit to
- `main`.
+- **Branch.** `docs/…`, `report/…`, `fix/…`. Never commit to `main`. (`exp/…`
+ and `spike/…` are dropped: this repository has neither.)
- **Never commit or push unless asked.** Never force-push or rewrite history.
> *Why the asking is explicit:* a specification that names a branch but never
> asks for a commit leaves the work sitting uncommitted and costs a round
> trip. See `docs/spec-defects.md`.
-- **Never weaken, skip, or delete a failing test to get green**, and never move a
- tolerance to make a number pass. A failing test is a result.
+- **Never soften a sentence to make a claim survive verification.** This is the
+ document-only form of *never weaken a failing test to get green*, and it is the
+ one that will actually be tempting here: the cheapest way to clear an
+ UNDER-QUALIFIED verdict is to blur the sentence until nothing can contradict
+ it. A claim that cannot fail is worth less than one that failed. Correct the
+ figure, add the condition, or drop the sentence.
- **Never add a dependency without asking.**
- **Say when a number is measured and when it is assumed.** An assumed input
that goes unnamed is the defect; the assumption itself is not.
@@ -206,9 +270,13 @@ method, with its provenance.
## Commands
```bash
-pytest -q # suite
-ruff check src tests # lint
-ruff format src tests # format
-mypy src # types
make -C report # build the report PDF (needs a TeX installation)
```
+
+**That is the whole list, and it is one line.** No suite, no linter, no type
+checker — there is no code for them to run against, and `pyproject.toml` was
+deleted rather than kept as an empty promise. The build is the only automated
+check this repository has, and `.github/workflows/report.yml` is the only
+workflow. Everything else that could go wrong here goes wrong in prose, and the
+only instrument for that is `report/claims-verified.md`, run by a person against
+a pinned checkout.
diff --git a/README.md b/README.md
index 6c9ca8f..322fb8a 100644
--- a/README.md
+++ b/README.md
@@ -1,134 +1,95 @@
-# math-ai-method
+# method-notes
-**A template for doing research with AI collaborators — not for building
-software with them.**
+**One technical report, its draft, and the pass that checks its claims.**
-The distinction is the point, and it should be legible in every file here.
+*Personal Notes on a Methodology for Research and Development in Applied
+Computational Mathematics* — Luiz Velho (IMPA), with Claude (Anthropic) as
+collaborator. The notes describe a methodology for research with AI
+collaborators, drawn from one project run over five days, and they argue that
+research has a different characteristic failure from software:
-> In software, the characteristic failure is a **bug**: the artifact does the
-> wrong thing, and tests, types and review catch it.
+> In software the failure is a **bug** — the artifact does the wrong thing, and
+> tests, types and review catch it.
>
-> In research, the characteristic failure is a **wrong belief**: a claim that is
-> well-formed, plausible, internally consistent, and supported by a number that
-> was computed correctly and means something else.
->
-> **No test catches that.** Every rule in this template exists to prevent one.
-
-That is why this looks different from an AI-for-developers guide. A guide for
-developers optimises for producing correct artifacts quickly. This optimises for
-never believing something that is not so — which means most of what is here is
-machinery for making claims **falsifiable, scoped, and attributable to a source
-you can open.**
-
-**Every rule that ships carries the observed failure that motivated it**, in a
-sentence, right next to the rule. That is deliberate: a rule without its reason
-gets simplified away by the next person who reads it, and looks like ceremony
-right up until the day it would have saved them.
-
-## Where this came from, and what that limits
-
-This is extracted from **one instance**: one researcher, one field, one project,
-**five days** — [`visgraf/bio-3d-vision`](https://github.com/visgraf/bio-3d-vision)
-at commit `5f8e39f`. Thirteen experiments, a decision ledger, a measurement
-ledger, a technical report and a verification pass over its claims.
-
-**One instance is not a validated methodology, and this document does not claim
-to be one.** What it claims is narrower and, I think, more useful: these rules
-were *exercised*, several of them failed in a specific way that is recorded here,
-and the fixes are the parts marked as new. Where a component was designed and
-never used, the file says so rather than presenting it as tested practice — see
-`spikes/README.md` and the roles table in `docs/workflow.md`, both of which
-document their own non-use.
-
-Adopt it, argue with it, and record what breaks.
+> In research the failure is a **wrong belief** — a claim that is well-formed,
+> plausible, internally consistent, and supported by a number that was computed
+> correctly and means something else. **No test catches that.**
-## What is in it
+## What is here
| | |
|---|---|
-| [`CLAUDE.md`](CLAUDE.md) | **The constitution.** Read at the start of every session; normative. Contains an empty, marked `## Domain invariants` section with a filled example beside it — that section is yours and it is the one that does not generalise. |
-| [`docs/workflow.md`](docs/workflow.md) | **The four-station loop** — User → Chat → User → Code → Chat — and its rules: every specification carries a falsifier, states the repository state it assumes, and is reviewed in-repo rather than from excerpts. Also the devil's advocate pass, and the surfaces table with its obituary. |
-| [`docs/spec-defects.md`](docs/spec-defects.md) | **Re-read before writing a specification.** Ways a specification has actually gone wrong here, with what each cost. |
-| [`docs/state.yaml`](docs/state.yaml) | **The decision ledger.** Schema and headers, no entries. Written by Code, read by Chat, never the reverse. Splits *position* (derivable from the ledger) from *plan* (intent, from Chat, not established here). Carries a **required stopping rule**. |
-| [`docs/inherited-measurements.yaml`](docs/inherited-measurements.yaml) | **The measurement ledger.** Every number with its provenance and its status — measured here, inherited, or a recorded gap. Its `methods` section carries the most expensive lesson in the template, as a worked example. |
-| [`experiments/exp000_example/`](experiments/exp000_example/) | **The shape of an experiment**: five files, empty, in the order they are written. The README explains why the order is load-bearing. |
-| [`spikes/`](spikes/) | **The low-ceremony lane**, with a trigger for when to use it — because in the instance it shipped with rules and enforcement and was used zero times. |
-| [`report/`](report/) | Scaffold that compiles from a clean checkout, plus [`claims-verified.md`](report/claims-verified.md): the practice of checking every claim in a report back against its source. |
-| [`tests/test_scaffold.py`](tests/test_scaffold.py) | **The enforcement.** What makes "CI enforces that" a true sentence rather than an intention. |
-| [`.github/workflows/`](.github/workflows/) | The gate, and the report build. |
-
-## Starting from it
-
-1. **Use it as a GitHub template** (or clone it), then take these five steps
- before any research work.
-2. **Rename the package.** `src/research/`, `name` in `pyproject.toml`, and
- `PACKAGE` in `tests/test_scaffold.py` move together.
-3. **Fill in `CLAUDE.md`'s two blanks:** *What this is* — including the failure
- this project exists to not repeat — and **`## Domain invariants`**, which
- holds the facts of your field that a plausible sentence can violate without
- any test noticing. Read the filled example beside it first. If your domain
- genuinely has none, delete the section and say why.
-4. **Answer `docs/state.yaml`'s stopping rule.** "none yet" is a legal answer and
- it is the answer the instance had for five days without anyone having to type
- it. Typing it is the point.
-5. **Clone your reference repositories into `.reference/`**, pinned to a SHA.
- It is git-ignored, and `tests/test_scaffold.py` enforces that code never reads
- from it — references are read by people.
-6. **Write the first specification.** `docs/workflow.md` says what one must
- contain; `docs/spec-defects.md` says how they have gone wrong.
-
-Then, at the point where you first want to run something:
-`experiments/exp000_example/README.md`, and commit the preregistration alone.
-
-## The parts that are new
-
-Five rules here were **not** in the source instance. Each fixes something that
-went wrong in it, and each is marked in the file that carries it:
-
-1. **Methods are entered at first use, not first suspicion**
- (`docs/inherited-measurements.yaml`). Carried *numbers* were labelled from day
- two; carried *methods* only at experiment twelve, by which time an unexamined
- statistical bar had governed ten experiments and turned out not to test what
- it was read as testing.
-2. **`docs/spec-defects.md` exists at all.** Decisions get foreclosures,
- measurements get provenance, badly-written specifications got nothing.
-3. **The devil's advocate pass** (`docs/workflow.md`). The loop had no move for
- *argue the opposite and see what holds*. Its second trigger is the
- counterintuitive one: a run of **clean** iterations, since a run of failures
- gets scrutiny for free.
-4. **The spike trigger** (`spikes/README.md`): a question written about twice
- without being tested goes to a spike. Adding the directory was not enough — it
- was used zero times.
-5. **The repeated-claim check** (`report/claims-verified.md`): grep for repeated
- figures across sections, because verification runs per passage and repetition
- crosses passages.
- **This one has fired**, on the provenance paragraph of this README — the only
- place in the template where a rule is caught working rather than argued for.
- Read it under *"This check has fired once, on this template's own prose"* in
- [`report/claims-verified.md`](report/claims-verified.md).
-
-And one required field: **a stopping rule** in `docs/state.yaml`, because the
-instance ran five days without one after being told in week one.
-
-## What this template does not do
-
-- It does not check your reasoning. Nothing here can tell you that a correct
- measurement is answering a different question than the one you asked. It can
- only make sure that when someone finally notices, the conditions are still
- attached to the number.
-- It does not scale itself down. The ceremony here was written for a project
- where a wrong belief was the expensive outcome. If that is not your situation,
- take less of it — but take the reasons with you, since they are what tell you
- which parts you are giving up.
-- It is Python-shaped in its scaffolding (`pyproject.toml`, the test guards, the
- CI matrix) because the instance was. The rules are not; the plumbing is.
-
-## Licence and lineage
-
-Extracted from [`visgraf/bio-3d-vision`](https://github.com/visgraf/bio-3d-vision)
-at `5f8e39f84a235bf53ace5789e739f0d51377344a`. Its predecessors —
-`visgraf/active-stereo` and `visgraf/bioeye` — supply several of the observed
-failures cited above; where a rule's reason begins "the predecessor", that is
-which repository it means.
-
-No `LICENSE` file yet. Adding one is the maintainer's call, not the scaffold's.
+| [`report/draft.md`](report/draft.md) | **The prose of record.** Eight sections and an appendix, drafted on the conversational surface and assembled by hand. This is the document. |
+| [`report/claims-verified.md`](report/claims-verified.md) | **The verification pass — not yet run.** Every factual claim in the draft, read back against its source and marked CONFIRMED / CORRECTED / UNDER-QUALIFIED / UNSUPPORTED / PARTIAL. |
+| [`report/main.tex`](report/main.tex) | The typeset form. Still the scaffold; typesetting is gated on the pass above. |
+| [`docs/inherited-measurements.yaml`](docs/inherited-measurements.yaml) | **The measurement ledger**, and the spine of the repository — see below. |
+| [`docs/state.yaml`](docs/state.yaml) | **The decision ledger.** Where the project is, what is closed and why, what is open. |
+| [`CLAUDE.md`](CLAUDE.md) | The constitution. Read at the start of every session; normative. |
+| [`docs/workflow.md`](docs/workflow.md) | The four-station loop and its rules. |
+| [`docs/spec-defects.md`](docs/spec-defects.md) | Re-read before writing a specification. |
+
+Build the PDF with `make -C report` (needs a TeX installation). That is the only
+command this repository has.
+
+## The thing to understand before reading anything else
+
+**This report is about another project, so every figure in it is inherited.**
+Nothing was measured here. The counts in the draft — thirteen experiments, ten
+governed by an unexamined criterion, 45 of 96 nulls reading differently, eighteen
+load-bearing claims of which seven stood — are all claims about
+[`visgraf/bio-3d-vision`](https://github.com/visgraf/bio-3d-vision) at
+`5f8e39f`, or about its two predecessors, or about the template.
+
+That makes this repository an unusually exposed instance of the failure its own
+subject matter is about: **a document whose entire content is claims about a
+record, written by people who have read that record and believe them.** It has no
+experiments of its own to be corrected by. The only instrument it has is the
+verification pass, and the only thing it will ever measure is that pass's own
+tally.
+
+**That pass has not been run.** The sources are not yet cloned (`gap-001`), no
+claim has been resolved to a file and a line (`od-001`), and until then
+Appendix A's closing promise — *every claim these notes make about what happened
+can be checked* — is a statement about what is possible, not about what has been
+done. Read the draft accordingly.
+
+> **A worked example of why the ledger exists, found in this repository's own
+> files.** The instance's verification tally appears twice here: as *22 claims,
+> 10 confirmed* in `report/claims-verified.md`, and as *eighteen load-bearing
+> claims, seven stood* in `report/draft.md`. They agree exactly on 3 corrected
+> and 8 under-qualified. They are reconcilable if "load-bearing" names a subset —
+> and **neither passage says so.** As two paragraphs in two files they had
+> coexisted without contradiction, because a paragraph cannot contradict a
+> paragraph. As `mn-001` and `mn-002` in the measurement ledger they are a
+> disagreement you trip over. Resolving them is `od-002`.
+
+## Provenance
+
+Instantiated from the template
+[`visgraf/math-ai-method`](https://github.com/visgraf/math-ai-method) at commit
+`81a638e`, then adapted to a document-only shape: no library, no experiments, no
+test suite, no Python. What was deleted and what was kept is recorded with its
+reasoning in the `foreclosures` block of
+[`docs/state.yaml`](docs/state.yaml) — `fc-001` through `fc-006` — rather than
+left to be inferred from an absence.
+
+Two of those decisions are worth flagging on the front page, because the obvious
+call went the other way:
+
+- **The decision ledger was kept.** Its argument never depended on there being
+ experiments — it exists so a later session can tell *we decided against this*
+ from *nobody thought of it*, and editorial decisions have that property too.
+ Adapting the template generated six foreclosures, five open decisions and a
+ stopping rule before any report work began.
+- **The measurement ledger was kept, and is load-bearing.** A report in which
+ every figure is inherited is this file's ideal case, not a category error: a
+ count in a repository at a pinned SHA is exactly recheckable in a way a
+ physical measurement is not.
+
+The four repositories this report makes claims about, and the rule that claims
+about them are never interchangeable, are in `CLAUDE.md` under **Domain
+invariants**. That distinction is not pedantry — the template's own front page
+was caught attributing an instance count wrongly, which is `docs/spec-defects.md`
+§3.
+
+No `LICENSE` file yet. Adding one is the maintainer's call.
diff --git a/docs/inherited-measurements.yaml b/docs/inherited-measurements.yaml
index 3cef4a8..e130584 100644
--- a/docs/inherited-measurements.yaml
+++ b/docs/inherited-measurements.yaml
@@ -22,9 +22,29 @@
# measurement as its own entry and set the old one's `status` to `superseded`,
# with `superseded_by`. The disagreement is the information.
#
-# TEMPLATE. Schema and rules, no entries. The `methods` section below ships with
-# one worked example, and that example is the most expensive lesson in this
-# template — read it before writing your first experiment.
+# WHY THIS FILE IS THE SPINE OF A DOCUMENT-ONLY REPOSITORY, AND NOT A CATEGORY
+# ERROR. This repository runs nothing. It holds one report ABOUT another project,
+# which means EVERY figure in report/draft.md is `inherited` by the definition
+# above — measured elsewhere, under conditions this repository has not adopted,
+# a prior rather than a fact about this project. The template's own framing has
+# `inherited` as the awkward minority status carried alongside a project's real
+# measurements. Here it is the default and `measured` is the exception, and the
+# file is closer to its own ideal than it would be in a project with experiments:
+# a count in a repository at a pinned SHA is EXACTLY recheckable, in a way a
+# physical measurement under recreated conditions never is.
+#
+# The one thing this repository will ever measure is the tally of its own
+# claims-verification pass (report/claims-verified.md). That number, and nothing
+# else, may ever carry `status: measured`.
+#
+# WHAT IS AWKWARD ABOUT THE SCHEMA HERE, said plainly rather than worked around:
+# `units`, `n`, `conditions` and `provenance.environment` are shaped for a
+# physical measurement. For "thirteen experiments" the units are "experiments",
+# `n` is meaningless, and `environment` is null and always will be. They are
+# filled honestly below rather than deleted, because the fields that DO carry
+# weight here — `statement`, `value`, `provenance.source`, `status`, `notes` —
+# are the same fields, and forking the schema to tidy four of them would cost
+# more than the noise does.
# ==============================================================================
schema_version: 1
@@ -51,14 +71,60 @@ record:
#
# Note the distinction from `measured_at_sha` on an entry, which is the commit
# the RUN was made at, as recorded by the source itself.
-sources: []
-# - key:
-# repo:
-# sha:
-# role: >-
-# What it contributes. A source that contributes NO measurements still
-# belongs here, with its absence recorded as a gap — "we read it and it had
-# nothing" and "we never read it" are different facts.
+sources:
+
+ - key: instance
+ repo: visgraf/bio-3d-vision
+ sha: 5f8e39f84a235bf53ace5789e739f0d51377344a
+ role: >-
+ THE SUBJECT. Nearly every figure in report/draft.md is a claim about this
+ repository — thirteen experiments, their pre-registrations, runners, raw
+ results, verdicts and findings, two ledgers, a technical report and its
+ claims-verification pass. The draft calls it "the instance". Resolving a
+ figure means opening a file in a checkout of this repository at this SHA.
+ checkout_status: >-
+ NOT CLONED. No file in this repository has been resolved against it yet.
+ See gap-001, which is what makes that absence citable instead of merely
+ being the case.
+
+ - key: template
+ repo: visgraf/math-ai-method
+ sha: null
+ sha_short: 81a638e
+ role: >-
+ What this repository was instantiated FROM, and a subject of §7 of the
+ draft. Claims about what the template ships, and what it fixed relative to
+ the instance, are claims about this source and NOT about `instance`. See
+ the four-repository table in CLAUDE.md's domain invariants; conflating the
+ two is the specific error the template's own front page was caught making.
+ note: >-
+ `sha` IS NULL ON PURPOSE AND THIS ENTRY IS THEREFORE NOT YET COMPLIANT
+ with the rule above it, which requires a full 40-character SHA. The
+ instantiation was recorded to this session as the abbreviated `81a638e`
+ and nothing here can expand it without the repository. Recorded as an
+ abbreviation in its own field rather than written into `sha`, because a
+ 7-character prefix in a field that promises 40 is the shape of a citation
+ that looks resolvable and is not. Expand it at the first clone.
+
+ - key: active-stereo
+ repo: visgraf/active-stereo
+ sha: null
+ role: >-
+ Predecessor. A full implementation with verified geometry and a decision
+ record, whose loop never ran. The draft cites it for two things: the
+ failure that motivated the low-ceremony lane, and the constitution clause
+ that was false and talked a reviewer out of verifying.
+ checkout_status: NOT CLONED. Contributes no resolved measurement. See gap-001.
+
+ - key: bioeye
+ repo: visgraf/bioeye
+ sha: null
+ role: >-
+ Predecessor. A thin vertical slice that closed the accumulation loop and
+ not the perception-action loop. Listed with NO measurements attributed to
+ it, which is a fact and not an oversight: "we read it and it had nothing"
+ and "we never read it" are different, and this is the second.
+ checkout_status: NOT CLONED. Contributes no resolved measurement. See gap-001.
schema:
id: Stable. Never reused, never renumbered. Cite this.
@@ -102,7 +168,83 @@ schema:
# docs/state.yaml, which governs every id space in the record, and names the
# assumption that makes sequential allocation safe.
# ------------------------------------------------------------------------------
-values: []
+# THE PREFIX FOR THIS PROJECT IS `mn-` (method-notes).
+#
+# WHAT IS AND IS NOT HERE. This ledger is SEEDED, not complete. It holds the
+# figures this repository has actually had occasion to handle — which, so far,
+# means the pair below, found while adapting the template and recorded because
+# finding them is what settled the question of whether this file belongs here at
+# all. THE CENSUS OF EVERY FIGURE IN report/draft.md HAS NOT BEEN DONE. It is the
+# claims-verification pass's job, it is od-001, and its absence is gap-002.
+# Do not read a short `values` list as "the draft has few figures".
+values:
+
+ - id: mn-001
+ statement: >-
+ Claims checked in the claims-verification pass over the instance's
+ technical report, as stated by the TEMPLATE.
+ value: {checked: 22, confirmed: 10, corrected: 3, under_qualified: 8, unsupported: 0}
+ units: claims
+ n: null
+ conditions: >-
+ One verification pass, over the first draft of the technical report in
+ `instance`. The five verdicts are those in report/claims-verified.md.
+ provenance:
+ cited_as: >-
+ "22 claims were checked: 10 confirmed, 3 corrected, 8 under-qualified,
+ 0 unsupported"
+ source: report/claims-verified.md, in this repository, carried verbatim from `template`
+ experiment: null
+ run_id: null
+ measured_at_sha: null
+ environment: null
+ status: inherited
+ notes: >-
+ TWO THINGS TO CARRY WITH THIS VALUE. First, the four stated verdicts sum to
+ 21 and the total is 22; PARTIAL is a fifth verdict in the same table and is
+ not stated, so the sum is reconcilable by assuming exactly one PARTIAL —
+ AND THAT IS AN ASSUMPTION, not something the passage says. Second, it
+ disagrees with mn-002 on the total and on the confirmed count while
+ agreeing exactly on corrected (3) and under-qualified (8). Neither entry is
+ known to be wrong. Do not quote either without resolving them at source.
+
+ - id: mn-002
+ statement: >-
+ Claims checked in the claims-verification pass over the instance's
+ technical report, as stated by the DRAFT of this report.
+ value: {load_bearing: 18, stood_as_written: 7, wrong_on_the_figure: 3, true_only_in_condition: 8}
+ units: claims
+ n: null
+ conditions: >-
+ Same pass as mn-001, restricted by the qualifier "load-bearing", which is
+ met-002 and is nowhere defined.
+ provenance:
+ cited_as: >-
+ "Of eighteen load-bearing claims in the first draft of the project's
+ technical report, seven stood as written." (report/draft.md:870-871);
+ also "corrected eleven of eighteen claims" (report/draft.md:1219)
+ source: report/draft.md, in this repository
+ experiment: null
+ run_id: null
+ measured_at_sha: null
+ environment: null
+ status: inherited
+ notes: >-
+ THIS PAIR IS THE ARGUMENT FOR KEEPING THIS FILE, so it is worth stating
+ exactly. 18 = 7 + 3 + 8 and is internally consistent. Against mn-001 it
+ shares the 3 and the 8 and differs on the total (18 vs 22) and the
+ confirmed count (7 vs 10). THE RECONCILIATION THAT FITS: 22 claims were
+ checked, of which 18 were load-bearing; 10 confirmed overall, 7 confirmed
+ among the load-bearing. It fits every number. NEITHER PASSAGE SAYS IT.
+ That is precisely an UNDER-QUALIFIED defect — a figure true of the subset
+ it came from, carried into a sentence that does not name the subset —
+ occurring in the two documents that ship the remedy, about the pass that
+ invented the category. As prose in two files it could sit there
+ indefinitely, because a paragraph cannot contradict a paragraph. As two
+ entries with two values it is a disagreement a reader trips over.
+ DO NOT "FIX" THIS BY EDITING EITHER SENTENCE. Resolve it at source (od-002)
+ and add the resolution as a new entry.
+
# - id: -001
# statement:
# value:
@@ -202,6 +344,65 @@ methods:
first person to wonder finds the wondering already half done.
status: examined_at_experiment_twelve
+ # --- THIS REPOSITORY'S OWN CARRIED METHODS ----------------------------------
+
+ - id: met-001
+ what: >-
+ The verdict taxonomy that scores every claim in this report:
+ CONFIRMED / CORRECTED / UNDER-QUALIFIED / UNSUPPORTED / PARTIAL, with
+ UNDER-QUALIFIED defined as "true in the band, arm, metric or stimulus it
+ came from, and unsupported as written".
+ kind: carried
+ from: >-
+ `template`, in report/claims-verified.md, which carried it from the
+ instance's own verification pass. It arrived with this repository at
+ instantiation and has never been examined here.
+ re_derived_here: >-
+ NO. Entered BEFORE first use rather than at it — the pass has not run yet.
+ That is STRICTER than the rule this section states and is not required by
+ it; it costs four lines and it is entered now because the whole lesson of
+ met-000 is that the cheap moment to write `kind: carried` is before anyone
+ has a reason to.
+ to_re_derive: >-
+ Nothing to derive; it is a taxonomy, not a quantity. What it needs is a
+ statement of the boundary between UNDER-QUALIFIED and CORRECTED, because
+ that boundary decides the headline number this repository will publish
+ about itself. A claim whose figure is right and whose scope is missing can
+ be scored either way by a reasonable reader, and mn-001's tally is a
+ 3-and-8 split across exactly that line.
+ why_it_matters_here: >-
+ It passes the test at the top of this section: if this taxonomy changed,
+ the one number this repository will ever own — its own claims tally —
+ would change. It is therefore a method, and it governs everything.
+ status: carried_unexamined
+
+ - id: met-002
+ what: >-
+ The selection criterion "load-bearing", which decides WHICH claims in a
+ report get counted in the denominator of a verification tally.
+ kind: carried
+ from: >-
+ report/draft.md, which uses it in the sentence that carries the report's
+ central argument ("Of eighteen load-bearing claims ... seven stood as
+ written"). No definition accompanies it anywhere in this repository.
+ re_derived_here: >-
+ NO, AND IT IS ALREADY COSTING SOMETHING. This undefined criterion is the
+ most likely explanation of the mn-001 / mn-002 disagreement: 22 claims
+ checked against 18 "load-bearing" ones. An undefined selector on a
+ denominator is invisible, because a ratio quoted without it still reads as
+ a complete sentence.
+ to_re_derive: >-
+ Write down the rule that separates a load-bearing claim from a checked one,
+ BEFORE recounting anything. A criterion chosen after seeing which one
+ reconciles the two figures is the same defect as an unexamined carried
+ method with a different unit on it. Then apply it and record the count as a
+ new entry, superseding neither mn-001 nor mn-002.
+ what_the_entry_would_have_cost_on_day_one: >-
+ One line: `load-bearing: `. The draft's strongest single
+ sentence — the asymmetry that §5 says justifies the whole method — rests on
+ this word.
+ status: carried_unexamined
+
# ------------------------------------------------------------------------------
# GAPS
#
@@ -213,7 +414,45 @@ methods:
# matter; a gap is closed only by a measurement, or by a foreclosure that says
# in writing what is being given up.
# ------------------------------------------------------------------------------
-gaps: []
+gaps:
+
+ - id: gap-001
+ statement: >-
+ No source repository has been cloned or read. Every `sources` entry is
+ marked NOT CLONED, and no figure in report/draft.md has been resolved to a
+ file and a line in the record it claims to come from.
+ why_it_is_load_bearing: >-
+ It bounds every factual claim the report makes. The draft's own status
+ block says its claims about the source project have not been through a
+ verification pass, and Appendix A's closing sentence — "Every claim these
+ notes make about what happened can be checked in the third" — is at present
+ a statement about what is POSSIBLE, not about what has been done. mn-001
+ and mn-002 cannot be reconciled while this gap is open. It is the single
+ gap that stands between this repository and its deliverable.
+ what_would_close_it: >-
+ Clone `visgraf/bio-3d-vision` at 5f8e39f into .reference/, and
+ `visgraf/math-ai-method` at the expanded SHA beside it, then run the pass
+ in report/claims-verified.md. Nothing here needs an instrument; it needs
+ two clones and a reading.
+
+ - id: gap-002
+ statement: >-
+ No census of the figures in report/draft.md has been taken. The number of
+ distinct factual claims in the document is unknown.
+ why_it_is_load_bearing: >-
+ It bounds the completeness of this ledger, and therefore how much a short
+ `values` list means. Two entries here are not evidence that the draft
+ contains two figures; they are evidence that two were handled. It also
+ bounds the repeated-claim check in report/claims-verified.md, which cannot
+ be run over a set nobody has enumerated — and that check is the one the
+ template records as having fired.
+ what_would_close_it: >-
+ The mechanical grep in report/claims-verified.md, run over draft.md, with
+ every hit triaged into a claim or discarded as a section number. NOTE THAT
+ THE SHIPPED COMMAND DOES NOT WORK AS WRITTEN on this document: run against
+ draft.md it returns section numbers ("4.1", "8.6") and its `uniq -f2`
+ field-counting collapses the result to a single line. See od-003.
+
# - id: gap-001
# statement:
# why_it_is_load_bearing: >-
diff --git a/docs/state.yaml b/docs/state.yaml
index 857e973..5100d5c 100644
--- a/docs/state.yaml
+++ b/docs/state.yaml
@@ -8,12 +8,30 @@
# Update it in the same commit as the change it describes. If it disagrees with
# the repository, the repository is right and this file is a bug.
#
-# TEMPLATE. Every block below carries its schema and its rules and NO ENTRIES.
-# The headers are the part that generalises; delete none of them. Fill the lists.
+# DOES A DOCUMENT-ONLY PROJECT HAVE A DECISION LEDGER? YES, AND THE ARGUMENT FOR
+# ONE NEVER DEPENDED ON THERE BEING EXPERIMENTS. The ledger exists so a later
+# session can tell "we decided against this" from "nobody thought of it" — two
+# states that look identical in a tree and are not the same thing. Editorial
+# decisions have exactly that property, and arguably more strongly: a deleted
+# directory leaves no trace at all, whereas an abandoned code path usually leaves
+# an import. The template's own instantiation into this repository deleted six
+# things, and without `foreclosures` below there would be nowhere to say why —
+# a future reader would find a repository that simply never had tests.
+#
+# THE TEST THIS FILE PASSED: the adaptation task that produced this repository
+# generated six foreclosures, five open decisions and a stopping rule ON ITS OWN,
+# before any report work started. A file that fills up during its own
+# instantiation is not ceremony.
+#
+# WHAT DOES NOT TRANSFER, marked rather than deleted: `falsifier_verdicts` and
+# `constraints` are still empty and may stay that way. See their headers.
+#
+# The blocks below carry the template's schemas and rules. Entries are this
+# repository's own.
# ==============================================================================
schema_version: 1
-updated_at: null # ISO date. Set it when you first write an entry.
+updated_at: 2026-09-08 # ISO date. Set it when you first write an entry.
# ------------------------------------------------------------------------------
# HOW IDS ARE ALLOCATED
@@ -70,6 +88,8 @@ updated_at: null # ISO date. Set it when you first write an entry.
# always fires is one a cold session learns to skip. Staleness is tested per
# half in `sequence`, and each half gets the test it can actually pass.
updated_by_commit: null # the repository state this file describes
+# Null on the commit that introduces it, necessarily: no commit can name itself.
+# Set it to the adaptation commit on the next change to this file.
# ------------------------------------------------------------------------------
# SEQUENCE — where this project is, and what it plans next
@@ -117,13 +137,25 @@ sequence:
# - a gate: "the loop must run before any further instrument work"
# - "none yet", with the date it was last considered
stopping_rule:
- rule: none yet
- decided_at: null # commit or date. `null` while `rule` is "none yet".
- last_considered: null # ISO date. Bump it even when the answer is unchanged.
+ rule: >-
+ A gate, not a budget. THE REPORT IS NOT TYPESET UNTIL THE
+ CLAIMS-VERIFICATION PASS HAS RUN AGAINST A CLONED CHECKOUT (gap-001), and
+ it is not published until every claim in report/draft.md is either
+ CONFIRMED or carries its correction in the sentence. If the pass cannot be
+ run — if the source repositories are unavailable at the pinned SHAs — the
+ work stops and the reason is recorded here, rather than the document
+ shipping with its figures unresolved.
+ decided_at: 2026-09-08
+ last_considered: 2026-09-08
note: >-
- REPLACE THIS. If the answer stays "none yet" for more than a few
- iterations, that is itself a finding about the project and belongs in the
- record, not in this comment.
+ TYPED AT INSTANTIATION RATHER THAN LEFT AT "none yet", which the template
+ says is a legal answer and which the source instance left standing for five
+ days after being told in week one. It was easy to type here for a reason
+ worth recording: a document-only project has a natural terminal condition
+ that a research project does not, because the deliverable is finite and its
+ correctness criterion is already written down in report/claims-verified.md.
+ Do not read the ease of filling this field as evidence that the template's
+ complaint was overblown.
staleness:
note: >-
@@ -174,11 +206,25 @@ sequence:
RECORD EACH RUN HERE, INCLUDING THE ONES THAT FAIL, and keep the previous
record beside the correction rather than overwriting it. The defect is
worth more than the fix.
- complete: [] # step numbers
+ complete: [1]
deferred: [] # - {step: , until: }
- next: null # the lowest step that is neither complete nor deferred
- evidence: {} # :
- out_of_sequence: []
+ next: 2
+ evidence:
+ 1: >-
+ fc-001 through fc-006 in this file, and the adaptation commit that
+ carries them. Read against clause (1) above: step 1's title is "Adapt the
+ template to a document-only shape", and each foreclosure names one thing
+ deleted or kept AND WHY — not merely that it happened.
+ out_of_sequence:
+ - what: >-
+ Two inherited measurements (mn-001, mn-002) and two carried methods
+ (met-001, met-002) were entered in docs/inherited-measurements.yaml.
+ note: >-
+ Recorded here so no id in that file is orphaned. They were not the task
+ and are not step 1's output: they came out of READING the draft closely
+ enough to decide whether the measurement ledger belonged in this
+ repository. The met- entries are required by CLAUDE.md's first-use
+ rule; the mn- pair is the evidence that settled fc-006.
# out_of_sequence holds work that was really done and is not one of the
# numbered steps. Entries are {what, note}. IT IS NOT A REMAINDER BIN: its
# job is that a cold session finding a verdict id or a measurement id in this
@@ -194,7 +240,7 @@ sequence:
pins it. Treat it as a prior, exactly as an `inherited` entry in
docs/inherited-measurements.yaml is treated — usable, and an assumption
whenever it is relied on.
- amended: null # how many times, and over how many iterations
+ amended: 0 # how many times, and over how many iterations
staleness:
last_amended_commit: null
rule: >-
@@ -218,7 +264,50 @@ sequence:
One clause is not answerable from the repository at all: whether the
maintainer has taken an amendment that was never recorded here. No marker
can close that, and it is exactly what `source: chat` is warning about.
- stages: []
+ stages:
+ - stage: 1
+ name: Instantiate
+ steps:
+ - n: 1
+ title: Adapt the template from a research shape to a document-only one
+ altitude: infra
+ status: complete
+ outcome: fc-001..fc-006, od-001..od-005, gap-001, gap-002
+ - stage: 2
+ name: Verify
+ steps:
+ - n: 2
+ title: >-
+ Clone the sources at their pinned SHAs and run the
+ claims-verification pass over report/draft.md
+ altitude: exp
+ status: next
+ outcome: null
+ - n: 3
+ title: Reconcile mn-001 against mn-002 and define "load-bearing" (met-002)
+ altitude: exp
+ status: planned
+ outcome: null
+ - stage: 3
+ name: Typeset
+ steps:
+ - n: 4
+ title: >-
+ Typeset the verified draft into report/main.tex, in the same pull
+ request as the draft revision it came from
+ altitude: impl
+ status: planned
+ outcome: null
+ - n: 5
+ title: Section 8.6, to be written by the first author
+ altitude: impl
+ status: planned
+ outcome: null
+ # THE STANDING OF THIS BLOCK IS UNCHANGED AND WORTH RE-READING: `source: chat`
+ # means nothing here established this plan. Steps 2-5 are read off the draft's
+ # own status block and Appendix A, which is INFERENCE FROM THE DOCUMENT, not
+ # an amendment taken from the maintainer. If the maintainer's plan differs,
+ # this half is wrong and the repository will not contradict it.
# - stage:
# name:
# steps:
@@ -242,11 +331,29 @@ sequence:
# what stops "what does this repository do" from being answered by reading src/.
# ------------------------------------------------------------------------------
runs:
- summary: null
- entry_points: []
- # - call:
- # does:
- # cost:
+ summary: >-
+ ONE, and it builds a document. There is no library, no test suite and no
+ experiment runner in this repository; `pyproject.toml` and the Python CI gate
+ were deleted at instantiation (fc-001, fc-002). A session asking "what does
+ this repository do" is answered by this block in full.
+ entry_points:
+ - call: make -C report
+ does: >-
+ Compiles report/main.tex to report/main.pdf with latexmk and biber.
+ Builds from a clean checkout with a TeX installation alone.
+ cost: >-
+ NOT MEASURED HERE. The TeX install step in .github/workflows/report.yml
+ carries an inherited figure from the template — 43.7 s, 142 MB, 119
+ packages on a GitHub runner, first run — which is a measurement of the
+ APT INSTALL, not of the compile, and was made against the template's
+ package set rather than this one. Treat it as a prior.
+ - call: the claims-verification pass
+ does: >-
+ Not a command. A person, reading report/draft.md against cloned
+ checkouts, filling report/claims-verified.md. It is the only instrument
+ this repository has and it is not automatable — see od-003 for what
+ happened to the one part of it that was.
+ cost: unmeasured
# ------------------------------------------------------------------------------
# LAST MEASURED NUMBERS
@@ -258,6 +365,13 @@ runs:
# docs/inherited-measurements.yaml with status `inherited`, and the new
# measurement is added as its own entry.
# ------------------------------------------------------------------------------
+# NOTHING HERE YET, AND ONLY ONE THING WILL EVER BE ELIGIBLE. This repository
+# runs nothing, so it measures nothing — except the tally of its own
+# claims-verification pass, which is produced here and is the one number it will
+# own. Every other figure it handles is `inherited` and stays that way, including
+# a figure re-read at source: re-reading resolves a citation, it does not
+# reproduce a measurement under this repository's conditions, because this
+# repository has none.
measurements: []
# - ids: []
# commit:
@@ -283,7 +397,95 @@ measurements: []
# impl implementation. Local, reversible, no interface moves.
# infra tooling, CI, layout. Reversible, affects no result.
# ------------------------------------------------------------------------------
-open_decisions: []
+open_decisions:
+
+ - id: od-001
+ altitude: exp
+ status: open
+ title: The claims-verification pass over report/draft.md has not been run.
+ the_question: >-
+ Which of the draft's factual claims about the source project survive being
+ read at source, and at what rate. The draft's own status block declares
+ this open; §8.4 rests the document's claim to be evidence rather than a
+ closed loop on the pass having fired.
+ what_would_decide_it: >-
+ Clone the sources (gap-001), run the pass in report/claims-verified.md,
+ fill its count table. DECLARED IN ADVANCE, per the field's own rule: the
+ tally is recorded whatever it says, and a rate of 0 UNSUPPORTED with a
+ substantial UNDER-QUALIFIED count would REPRODUCE the instance's result
+ rather than contradict it. A pass that returns everything CONFIRMED is the
+ suspicious outcome, not the good one, and would prompt asking whether the
+ claims were selected to be safe.
+ depends_on: [gap-001]
+
+ - id: od-002
+ altitude: exp
+ status: open
+ title: mn-001 and mn-002 give different totals for the same verification pass.
+ the_question: >-
+ Is the instance's pass 22 claims or 18? Both figures are in circulation in
+ this repository, they agree on 3 corrected and 8 under-qualified, and they
+ disagree on the total and the confirmed count.
+ what_would_decide_it: >-
+ Open the instance's claims-verification file at 5f8e39f and count. THE
+ CRITERION IS DECLARED FIRST AND IS met-002: define "load-bearing" before
+ counting, not after seeing which definition reconciles the two. Record the
+ result as a NEW entry; supersede neither.
+ depends_on: [gap-001, met-002]
+
+ - id: od-003
+ altitude: infra
+ status: open
+ title: >-
+ The repeated-claim grep in report/claims-verified.md does not work on this
+ repository's prose.
+ the_question: >-
+ The template ships a mechanical check — extract every numeral, find the
+ repeated ones, read their occurrences side by side — and credits it with
+ the one rule-fires-in-the-wild event in the whole template. It is written
+ against `report/sections/*.tex` and `report/main.tex`. This repository's
+ prose is in `report/draft.md`, which will not be typeset until step 4, so
+ for the entire period the check is most needed IT POINTS AT FILES THAT ARE
+ EMPTY OR ABSENT. Run against draft.md as written it returns section numbers
+ as figures and its `uniq -c -f2` collapses the output to one line.
+ what_would_decide_it: >-
+ Rewrite the command against draft.md and verify it on a known answer:
+ it must surface "thirteen" and the mn-001/mn-002 pair, both of which are
+ known to repeat. A check that cannot find a defect already recorded in the
+ ledger is not yet a check. NOT AUTOMATED INTO CI — see fc-001's scope for
+ why a repetition count has no pass/fail threshold.
+ depends_on: []
+
+ - id: od-004
+ altitude: infra
+ status: open
+ title: Nothing verifies that the two ledgers are parseable YAML.
+ the_question: >-
+ docs/state.yaml and docs/inherited-measurements.yaml are the record, they
+ are hand-edited, and after fc-001 no automated check reads them at all.
+ A syntax error would be silent until someone tried to parse the file, and
+ this repository's whole argument is that a record which cannot be read
+ cannot be checked.
+ what_would_decide_it: >-
+ A ten-line workflow step running a YAML parser over both files. IT IS OPEN
+ RATHER THAN DONE FOR ONE REASON: it needs a YAML parser, PyYAML is not in
+ the standard library, and CLAUDE.md's working agreement says never add a
+ dependency without asking. The ask is this entry. Note that the risk is
+ already demonstrated locally — no YAML parser was available on this machine
+ without creating a throwaway environment.
+ depends_on: []
+
+ - id: od-005
+ altitude: impl
+ status: open
+ title: Section 8.6 of the draft is unwritten.
+ the_question: >-
+ "How did it feel" — recorded because the draft's status block assigns it to
+ the first author, and an unwritten section that is nobody's tracked
+ responsibility is indistinguishable from a section that was cut.
+ what_would_decide_it: The first author writing it, or deciding to cut it and saying so here.
+ depends_on: []
+
# - id: od-0NN
# altitude: fw | exp | impl | infra
# status: open
@@ -338,7 +540,206 @@ open_decisions: []
# information — the same argument that keeps a superseded measurement in
# docs/inherited-measurements.yaml instead of editing its value.
# ------------------------------------------------------------------------------
-foreclosures: []
+foreclosures:
+
+ # NOTE ON WHAT KIND OF EVIDENCE THESE REST ON. CLAUDE.md requires that a
+ # decision be recorded AFTER the thing it decides has been tried. These were:
+ # the deletions were made and the tree was read afterwards for what broke.
+ # That is a weaker trial than an experiment and it is named as such in each
+ # `rationale` — the evidence is "the reference dangles" or "the step cannot
+ # pass", both checkable from the tree, not "we measured it".
+
+ - id: fc-001
+ altitude: infra
+ closed: >-
+ The Python CI gate (.github/workflows/ci.yml): a two-interpreter matrix
+ running ruff, ruff format, mypy and pytest.
+ in_favour_of: >-
+ .github/workflows/report.yml alone. CI compiles the document and uploads
+ the PDF and the log; that is the entire automated surface.
+ rationale: >-
+ Every step in the workflow is inapplicable, and the first one FAILS rather
+ than passing vacuously: `pip install -e ".[dev]"` needs pyproject.toml,
+ which is gone (fc-002), so the job cannot start. Downstream, `ruff check
+ src tests` and `mypy` name directories that no longer exist and `pytest`
+ has no suite. The matrix's stated purpose — that 3.12 is the only
+ environment testing `requires-python = ">=3.12"` — is void once nothing
+ declares a Python requirement. A workflow retained here would be red on
+ every push for reasons unrelated to the report, and a check that fails for
+ the wrong reason is one people learn to override; the template argues
+ exactly this in report/figures/README.md against byte-diffing figures.
+ decided_by: The maintainer, on the instantiation specification; executed by Code.
+ scope: >-
+ THIS DOES NOT CLOSE AUTOMATED CHECKING GENERALLY, and two specific things
+ stay open. (1) od-004: a YAML-parse gate over the two ledgers is wanted and
+ is blocked only on the dependency question. (2) The repeated-claim check
+ (od-003) was considered for CI and DELIBERATELY LEFT MANUAL — repetition of
+ a figure is legal, so the check has no pass/fail threshold, only an
+ instruction to read occurrences side by side. Automating it would produce a
+ gate that fires on correct documents. It stops holding the moment any
+ executable code lands in this repository: a figure script would need the
+ lint and type gates back with it, and the .reference/ import guard too.
+ cost_to_reopen: >-
+ Low. Restore ci.yml and pyproject.toml together from `template` at 81a638e
+ and narrow the matrix to whatever the new code needs. They are one
+ decision, not two.
+ decided_in: The instantiation commit.
+
+ - id: fc-002
+ altitude: infra
+ closed: pyproject.toml.
+ in_favour_of: No Python packaging or tool configuration in the repository at all.
+ rationale: >-
+ The file's four jobs are packaging `src/research` (deleted), configuring
+ pytest (no suite), ruff (no code) and mypy (no code). The one condition
+ under which it would have earned its place — a figure generated from data —
+ does not hold and was checked rather than assumed: report/figures/ is
+ empty, no `make_*.py` exists, and every occurrence of the word "figure" in
+ report/draft.md means A NUMERAL, not a graphic. The document has no
+ plots. Keeping the file would have declared a Python project that does not
+ exist, which is the same defect as an untested claim carried forward.
+ decided_by: Code, from the tree; confirmed against the draft's own text.
+ scope: >-
+ Bounded by that check and by nothing else. IT STOPS HOLDING THE MOMENT ONE
+ GENERATED FIGURE IS WANTED — at which point pyproject.toml comes back with
+ the plotting dependency, and report/figures/README.md's rules (draw to
+ 6.5in, assert the drawn numbers against this ledger, do not byte-diff the
+ PDF) come back into force. That README is retained for exactly this
+ contingency; see fc-005.
+ cost_to_reopen: Low. Restore from `template` and add the plotting dependency, having asked.
+ decided_in: The instantiation commit.
+
+ - id: fc-003
+ altitude: fw
+ closed: >-
+ src/research/, experiments/exp000_example/, and tests/test_scaffold.py.
+ in_favour_of: A repository containing documents and a build, and nothing executable.
+ rationale: >-
+ No library, no experiment, no measurement apparatus. The example experiment
+ teaches a five-file order that nothing here will follow. THE ONE PART THAT
+ NEEDED A SEPARATE ARGUMENT is tests/test_scaffold.py, which was described in
+ the specification as having one job — the spikes import ban — and in fact
+ had four: package import, the spikes ban, the .reference/ ban, and
+ "every experiment has a preregistration". Three lose their subject with
+ fc-003 and fc-004. THE FOURTH DOES NOT: the .reference/ guard protects a
+ convention this repository actively depends on, since the pinned checkouts
+ are how every claim gets verified. It was deleted anyway, because its
+ precondition is that executable code exists to be scanned, and there is
+ none — the guard would have passed by scanning an empty set, which is the
+ shape of a check that cannot fail. The convention now rests on a comment in
+ .gitignore and on nothing else. That is a real loss and it is recorded
+ here rather than absorbed.
+ decided_by: The maintainer, on the instantiation specification; the fourth-guard analysis by Code.
+ scope: >-
+ Does not close the .reference/ CONVENTION, which is retained and is
+ load-bearing. Stops holding the moment any .py file lands: restore the
+ .reference/ guard first, before the others.
+ cost_to_reopen: >-
+ Low for the guards — restore from `template` at 81a638e and rename PACKAGE.
+ Moderate for src/ and experiments/, but reopening those means this is no
+ longer a document-only repository, which is a different decision.
+ decided_in: The instantiation commit.
+
+ - id: fc-004
+ altitude: fw
+ closed: spikes/, its README, and its enforcement test.
+ in_favour_of: >-
+ No low-ceremony lane. Exploratory prose is a discarded draft section and
+ lives in report/.
+ rationale: >-
+ Two reasons, and the second is the one that decided it. FIRST, the lane has
+ nothing to be low-ceremony about: a spike is where you TRY something, and
+ the trigger is "a question written about twice WITHOUT BEING TESTED". This
+ repository tests nothing. SECOND, and this is the finding: §6 of
+ report/draft.md argues that a project with strong ceremony structurally
+ will not reach for its informal lane, evidenced by the instance shipping
+ spikes/ with rules and enforcement and using it zero times in thirteen
+ experiments. Instantiating the directory here would have produced a second
+ unused spikes/ that looked like a second data point and was really the
+ first one copied — and its emptiness would then have been cited as evidence
+ for the argument it was copied from. Declining to instantiate keeps the
+ instance's n at one.
+ decided_by: Code, from §6 of the draft; ratified by the specification's "almost certainly delete".
+ scope: >-
+ DOES NOT CLOSE THE FINDING, which stands and is preserved in
+ docs/workflow.md. Does not claim spikes/ is a bad idea in general — it
+ claims this repository is not a place where the question can be asked. It
+ stops holding if this repository ever runs anything.
+ cost_to_reopen: Low. Restore the directory and its README from `template`.
+ decided_in: The instantiation commit.
+
+ - id: fc-005
+ altitude: infra
+ closed: >-
+ Deleting docs/state.yaml on the grounds that a document-only project has no
+ experiments to sequence.
+ in_favour_of: Keeping it, with the experiment-shaped machinery filled rather than removed.
+ rationale: >-
+ The ledger's argument never mentioned experiments: it exists so a later
+ session can distinguish "we decided against this" from "nobody thought of
+ it". Editorial decisions have that property. THE EVIDENCE IS THIS FILE'S
+ OWN CONTENT — the instantiation task generated six foreclosures, five open
+ decisions and a stopping rule before any report work began, and every one
+ of fc-001..fc-006 is a deletion that would otherwise be invisible in a
+ tree that simply appears never to have had tests. A ledger that fills
+ during its own instantiation has demonstrated its use rather than asserted
+ it. The stopping rule, which the template calls its hardest-won required
+ field, was also easy to answer here and is now answered.
+ decided_by: Code, from the outcome of attempting the adaptation without one.
+ scope: >-
+ NAMES WHAT DID NOT TRANSFER RATHER THAN CLAIMING A CLEAN FIT.
+ `falsifier_verdicts` and `constraints` are empty and may stay empty: a
+ falsifier is declared before a run, and this repository's one instrument is
+ a reading. The `position.checking` procedure survives but is thinner, since
+ "derive it from the ledger and merged history" has a much smaller ledger to
+ derive from. If those two blocks are still empty when the report ships,
+ that is a finding about the template's generality and belongs in the record.
+ cost_to_reopen: >-
+ Moderate and asymmetric. Deleting it later is cheap; deleting it now would
+ have cost the six entries above, which could not be reconstructed once the
+ reasoning that produced them was gone.
+ decided_in: The instantiation commit.
+
+ - id: fc-006
+ altitude: fw
+ closed: >-
+ Deleting docs/inherited-measurements.yaml on the grounds that a report
+ containing only inherited figures is a category error for a ledger of
+ inherited figures.
+ in_favour_of: >-
+ Keeping it as the SPINE of the repository — the file the verification pass
+ resolves claims into.
+ rationale: >-
+ The premise is right and the conclusion inverts. Every figure in
+ report/draft.md is `inherited` by the file's own definition, which makes
+ this the file's ideal case rather than its edge case: a count in a
+ repository at a pinned SHA is exactly recheckable in a way a physical
+ measurement is not. THREE PIECES OF EVIDENCE, all checkable. (1)
+ report/claims-verified.md is retained, and item 4 of its "what each entry
+ records" cites `docs/inherited-measurements.yaml ` as the Source field
+ — deleting the ledger breaks the instrument. (2) Appendix A's closing
+ sentence, "Every claim these notes make about what happened can be checked
+ in the third", is a promise whose mechanism IS the `sources` block with its
+ pinned SHA; without it, "verifiable at source" is an assurance and not a
+ pointer. (3) The decisive one: reading the draft against the template
+ surfaced mn-001 and mn-002, two statements of the same figure that
+ disagree on the denominator, sitting in two files that have coexisted
+ without contradiction because prose cannot contradict prose. That is the
+ file's founding argument, demonstrated on this repository's own documents.
+ decided_by: Code, on the mn-001/mn-002 finding.
+ scope: >-
+ DOES NOT CLAIM THE SCHEMA FITS CLEANLY. `units`, `n`, `conditions` and
+ `provenance.environment` are shaped for physical measurement and read
+ awkwardly for a count of experiments; they are filled honestly rather than
+ deleted, and the file's header says so. Does not claim the ledger is
+ complete: it is seeded with what this task handled, and gap-002 records
+ that no census exists. `status: measured` remains reachable by exactly one
+ future value, this report's own claims tally.
+ cost_to_reopen: >-
+ High, and rising. Every id allocated here is citable from the report, and
+ the ids are permanent by the rule at the top of this file.
+ decided_in: The instantiation commit.
+
# - id: fc-0NN
# altitude: fw | exp | impl | infra
# closed:
@@ -358,6 +759,14 @@ foreclosures: []
# ------------------------------------------------------------------------------
# FALSIFIER VERDICTS
#
+# EMPTY, AND THE EMPTINESS IS THE ENTRY. See fc-005's scope. A falsifier is
+# declared before a run, and this repository's only instrument is a person
+# reading a document against a checkout — od-001 carries a declared-in-advance
+# expectation, which is the nearest thing available and is deliberately NOT
+# called a falsifier here. If this block is still empty when the report ships,
+# that is a fact about how far the template's loop reaches into document work,
+# and it belongs in the record rather than being quietly tidied away.
+#
# Every specification carries a falsifier (docs/workflow.md). A verdict that
# lives only in a pull-request body is not in the tree and a cold session cannot
# read it — and by this repository's own argument, a verdict with no durable
@@ -382,6 +791,11 @@ falsifier_verdicts: []
#
# Measured costs and impossibilities that bind future work regardless of what is
# decided. Not decisions; consequences.
+#
+# EMPTY. Nothing here has measured a cost. The candidate — what the verification
+# pass costs to run — becomes a constraint the first time it is paid, and
+# recording it is worth doing, since the draft's §5 argues the whole method on a
+# cost/catch ratio and this repository will have a data point for it.
# ------------------------------------------------------------------------------
constraints: []
# - id: cn-0NN
diff --git a/docs/workflow.md b/docs/workflow.md
index 4c0db74..104c8f0 100644
--- a/docs/workflow.md
+++ b/docs/workflow.md
@@ -65,10 +65,19 @@ full of pre-registered experiments with falsifiers and provenance, a scratch
directory with no rules and a briefing with no verdict both start to feel like
things you would have to apologise for. The ceremony is worth keeping. The cost
of it is that the two cheapest instruments in the toolkit go untouched, so if
-you want them used, **trigger them** — see the spike trigger in
-`spikes/README.md`, and note that it exists precisely because "we added the
+you want them used, **trigger them** — the template's trigger was *a question
+written about twice without being tested goes to a spike*, which fires on an
+observable rather than on a mood, and it exists precisely because "we added the
directory" turned out not to be enough.
+> **This repository has no `spikes/`, and the paragraph above is why it does
+> not.** The finding stands; the directory was not instantiated. Reproducing a
+> lane that the argument above predicts will go unused — in a repository with
+> nothing to be informal about — would have manufactured a second data point
+> that was really the first one copied. Recorded as `fc-004` in
+> `docs/state.yaml`. The Cowork row keeps its obituary unchanged, because that
+> surface is still available and still unused.
+
## What a specification is
The loop's central artifact, and **the only one that leaves no trace in the
diff --git a/experiments/exp000_example/README.md b/experiments/exp000_example/README.md
deleted file mode 100644
index cca8c2e..0000000
--- a/experiments/exp000_example/README.md
+++ /dev/null
@@ -1,91 +0,0 @@
-# exp000_example — the shape of an experiment
-
-Five files, and **the order they are written in is the whole point**. They are
-shipped empty because the shape is what carries; the contents are yours.
-
-```
-preregistration.md written and committed ALONE, before the runner exists
-run.py the runner
-results.json what it produced, raw
-verdicts.json results scored against the pre-registered rule
-findings.md what it means, and which falsifiers fired
-```
-
-## Why the order is load-bearing
-
-**The order is the entire mechanism.** Every one of these files could be written
-in any sequence and the directory would look identical afterwards. What the order
-buys is that **the threshold is fixed before the numbers exist** — and the git
-history is the only thing that can prove it was.
-
-> *The failure it prevents:* not fraud. The ordinary case is that you run the
-> thing, look at the numbers, and *then* decide what counts as a difference —
-> and every choice you make at that point feels like judgement rather than
-> selection. A threshold chosen after seeing the data lets any outcome be the
-> desired one, and it does not feel like that from the inside.
-
-So:
-
-**1. `preregistration.md` is committed alone, in its own commit, before any
-runner exists.** Alone, so that `git log` shows a commit containing the
-threshold and nothing that could have produced a number. That commit is the
-evidence. It is cheap to make and impossible to reconstruct afterwards.
-
-**2. `run.py` produces `results.json` and does not score it.** The runner writes
-what it measured. Keeping scoring out of it means the scoring code can be read
-against the pre-registration by someone who has not looked at the results.
-
-**3. `verdicts.json` applies the pre-registered rule, mechanically.** It records,
-per comparison and per metric: the difference, the bar, **which clause of the bar
-was binding**, and the verdict. Recording which clause bound is what later makes
-it possible to ask what the criterion was actually testing — exactly the question
-that went unasked for ten experiments in the instance this template came from
-(`met-000-EXAMPLE` in `docs/inherited-measurements.yaml`).
-
-**4. `findings.md` is written last and states which falsifiers fired**, including
-the ones that did not. It reports the outcome the pre-registration did not
-predict as prominently as the one it did.
-
-**And when the protocol says to stop, stop.** A pre-registration that declares a
-check — a grid resolution, a sanity condition, a control that must hold — and
-then fails it has produced a result. In the instance, a declared stride check
-failed on the first run; the protocol said reduce the stride and discard the run,
-and following it cost one re-run and turned up a fact about the objective that
-anticipated the main finding. The alternative was to report the discarded run.
-
-## What goes in the preregistration
-
-Written before the fact, and none of it revisable afterwards without an
-amendment recorded in `findings.md`:
-
-- **The question**, and why it is worth an experiment rather than an argument.
-- **The arms**, and what differs between them. Name what differs *besides* the
- thing under test — the confound you already know about is the one you can
- declare, and declaring it lets you say in advance what you will do if it bites.
-- **The metrics**, split into primary (the verdict rests on these) and secondary
- (reported, decisive of nothing). Say which is which before you know which one
- moves.
-- **The threshold**, stated as a formula. If it is carried from somewhere else,
- it gets an entry in `docs/inherited-measurements.yaml` under `methods` **now**,
- not when someone doubts it.
-- **The falsifier for the specification itself** — what result would mean this
- experiment asked the wrong question. See `docs/workflow.md`.
-- **Declared limitations, before the fact.** What this experiment forecloses and
- what it cannot: the stimulus it does not have, the regime it does not cover.
- This is where a finding's scope is fixed, and scope is what gets dropped
- between here and the report.
-- **What each outcome means**, enumerated in advance. Every branch, including the
- surprising one. If you cannot say in advance what a result would mean, you will
- say afterwards that it means whatever it turned out to be.
-
-## Naming
-
-`experiments/expNNN_short_question/`. Numbered so ordering is unambiguous in the
-record, named after the question rather than the method.
-
-## On this directory
-
-`exp000_example` is not an experiment and produced nothing. Delete it once you
-have a real one, or keep it as a checklist — but do not let it accumulate
-contents, because an example experiment with plausible-looking numbers in it is a
-thing somebody will eventually cite.
diff --git a/experiments/exp000_example/findings.md b/experiments/exp000_example/findings.md
deleted file mode 100644
index e69de29..0000000
diff --git a/experiments/exp000_example/preregistration.md b/experiments/exp000_example/preregistration.md
deleted file mode 100644
index e69de29..0000000
diff --git a/experiments/exp000_example/results.json b/experiments/exp000_example/results.json
deleted file mode 100644
index e69de29..0000000
diff --git a/experiments/exp000_example/run.py b/experiments/exp000_example/run.py
deleted file mode 100644
index e69de29..0000000
diff --git a/experiments/exp000_example/verdicts.json b/experiments/exp000_example/verdicts.json
deleted file mode 100644
index e69de29..0000000
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index f37c65d..0000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,58 +0,0 @@
-[build-system]
-requires = ["hatchling"]
-build-backend = "hatchling.build"
-
-[project]
-# RENAME. This name, the directory src/research/, and PACKAGE in
-# tests/test_scaffold.py move together.
-name = "research"
-version = "0.0.0"
-description = "A research project."
-readme = "README.md"
-requires-python = ">=3.12"
-# No `license` field: this repository has no LICENSE file, and adding one is the
-# maintainer's call, not the scaffold's.
-#
-# NO DEPENDENCIES AS SHIPPED, deliberately. Add them here rather than in CI, so
-# that a local environment and the runner install the same set and neither can
-# drift ahead of the other. CLAUDE.md's working agreement says never add one
-# without asking; this is the file where the asking becomes visible in a diff.
-dependencies = []
-
-[project.optional-dependencies]
-dev = [
- "pytest>=8.0",
- "mypy>=1.11",
- "ruff>=0.6",
-]
-
-[tool.hatch.build.targets.wheel]
-packages = ["src/research"]
-
-[tool.pytest.ini_options]
-testpaths = ["tests"]
-addopts = "-q --strict-markers"
-markers = [
- "infrastructure: pins the scaffold, not any result about the subject matter",
-]
-
-[tool.ruff]
-line-length = 100
-src = ["src", "tests"]
-
-[tool.ruff.lint]
-select = ["E", "F", "I", "UP", "B", "SIM"]
-
-[tool.mypy]
-python_version = "3.12"
-files = ["src"]
-strict = true
-warn_unreachable = true
-
-# spikes/ is deliberately exempt from typing, linting and tests. It is where you
-# try things; its only permitted output is a decision or a deletion. The import
-# ban that keeps it out of the library is enforced in tests/test_scaffold.py,
-# which is what makes the exemption safe rather than a hole.
-[[tool.mypy.overrides]]
-module = "spikes.*"
-ignore_errors = true
diff --git a/report/Makefile b/report/Makefile
index 433d28e..cd999fa 100644
--- a/report/Makefile
+++ b/report/Makefile
@@ -1,11 +1,16 @@
# The one command: `make -C report` (or `make` from inside report/).
#
# The document builds with a TeX installation alone. No language runtime is
-# required: report/figures/*.pdf are generated artifacts and are COMMITTED, so a
-# clean checkout compiles without running anything. See figures/README.md for why
-# they are committed and why nothing diffs their bytes.
+# required -- and in this repository none is available: there is no Python
+# package and no pyproject.toml (fc-002). Generated figures would be COMMITTED
+# rather than built here, so that a clean checkout compiles without running
+# anything; there are none today. See figures/README.md.
#
-# `make figures` regenerates them and needs the project installed (pip install -e .).
+# `make figures` IS INERT TODAY: this document has no figures, and pyproject.toml
+# was deleted with the rest of the Python scaffolding (fc-002). The target is kept
+# because it degrades honestly -- it prints "nothing to regenerate" rather than
+# failing -- and because figures/README.md documents what has to come back before
+# it means anything again. If you restore it, restore pyproject.toml first.
LATEXMK := latexmk
@@ -13,6 +18,9 @@ LATEXMK := latexmk
all: main.pdf
+# NOTE: sections/ is empty and main.tex \inputs nothing from it yet -- the prose
+# lives in draft.md and is typeset at step 4 (docs/state.yaml). The wildcards
+# below match nothing today and are correct in advance rather than wrong later.
main.pdf: main.tex $(wildcard sections/*.tex) $(wildcard figures/*.pdf) refs.bib
$(LATEXMK) -pdf main.tex
diff --git a/report/claims-verified.md b/report/claims-verified.md
index 34e2eb7..ae7f0c4 100644
--- a/report/claims-verified.md
+++ b/report/claims-verified.md
@@ -1,9 +1,25 @@
-# Claim verification — TEMPLATE
+# Claim verification — NOT YET RUN
-**Verified at ``** against the experiments' `findings.md`,
-`preregistration.md` and `results.json`, `docs/state.yaml` and
+**This pass is open (`od-001`) and blocked on `gap-001`.** No claim in
+`report/draft.md` has been resolved to a source. The tables below are empty
+because nothing has been checked, not because everything passed.
+
+**Verified at ``** against **cloned checkouts in `.reference/`, at the
+SHAs pinned in `docs/inherited-measurements.yaml`** — principally
+`visgraf/bio-3d-vision` at `5f8e39f`, and `visgraf/math-ai-method` for claims
+about the template — plus `docs/state.yaml` and
`docs/inherited-measurements.yaml`.
+> **THE SOURCES ARE IN ANOTHER REPOSITORY, AND THAT CHANGES THIS PASS'S
+> CHARACTER.** The template assumed the record being checked against lived in the
+> same tree: `experiments/exp0NN/verdicts.json`, a path that cannot rot because
+> CI would notice. Here every source is a file in a repository this one does not
+> contain, at a SHA that must be pinned by hand and can only be wrong silently.
+> There is no equivalent of "the test suite would have caught it". **Cite by
+> source key, path and SHA, never by path alone** — `instance:
+> report/claims-verified.md @5f8e39f`, not `report/claims-verified.md`, which in
+> this repository names a different file: *this* one.
+
**No prose is written and no `.tex` file is touched during a verification pass.**
This document is a verification pass, not a draft. Keeping the two apart is what
stops a claim from being fixed by softening the sentence that carries it.
@@ -46,9 +62,13 @@ Per claim, in this order:
1. The sentence, quoted from the report.
2. The verdict, in bold, with the correct figure if it moved.
3. The comparison, the arms, the `n`, and the stimulus.
-4. **The source, by file and id.** `docs/inherited-measurements.yaml` entry id,
- the experiment's `verdicts.json`, the findings file — whichever actually
- holds it.
+4. **The source, by key, file and id.** The `docs/inherited-measurements.yaml`
+ entry id, and — inside the pinned checkout — the experiment's
+ `verdicts.json`, the findings file, the ledger entry, whichever actually
+ holds it. **Name the source key** (`instance`, `template`, `active-stereo`,
+ `bioeye`) so that a claim about the template is never filed as a claim about
+ the instance. That confusion is a domain invariant in `CLAUDE.md` because the
+ template's own front page has already made it.
5. Any caveat the source attaches to the value, quoted. *A caveat that lives only
in the ledger does not travel; this is where it gets carried into the prose or
consciously dropped.*
@@ -76,11 +96,29 @@ off:
1. Extract every numeral, ratio, percentage and multiplier that appears in the
typeset sources.
+ **Run it over `report/draft.md`, which is where this report's prose lives**,
+ and over the `.tex` sources once step 4 has typeset them. Both, once both
+ exist — a figure that is correct in the draft and mangled in the typeset copy
+ is the specific defect the draft/typeset split exists to expose.
+
```bash
- grep -onE '[0-9]+(\.[0-9]+)?(×|x|:1|%|-fold)?' report/sections/*.tex report/main.tex \
+ grep -onE '[0-9]+(\.[0-9]+)?(×|x|:1|%|-fold)?' report/draft.md \
+ report/sections/*.tex report/main.tex 2>/dev/null \
| sort -t: -k3 | uniq -c -f2 | sort -rn | head -40
```
+ > **THE COMMAND AS SHIPPED DOES NOT WORK ON THIS DOCUMENT, and that is
+ > recorded rather than patched over.** Run against `draft.md` it fails twice:
+ > `uniq -c -f2` counts whitespace-separated fields, not the `:`-separated ones
+ > `sort -t:` just ordered, so the whole output collapses to a single line; and
+ > markdown section numbering means `4.1` and `8.6` are returned as repeated
+ > figures while the prose's actual figures — *thirteen*, *eighteen*, *seven* —
+ > are words and are not matched at all. **A numeral-only check misses most of
+ > the claims in a document that spells its numbers out.** This is `od-003`;
+ > the fix is not yet written, and the check must be validated against a known
+ > answer (it has to surface the `mn-001`/`mn-002` pair) before it is trusted.
+ > Until then, do this step by reading.
+
2. **For every figure that appears more than once, open all of its occurrences
together.** Not in reading order — side by side. Ask of each: does this
sentence carry the same scope as the others?
@@ -95,9 +133,17 @@ off:
33×" against an exact 33.5× is true; "at least 34×" is not. Rounding *down* is
the safe direction for a lower bound, and *up* for an upper one.
-### This check has fired once, on this template's own prose
+### This check has fired once, on the template's own prose
+
+> **The `README.md` and `tests/test_scaffold.py` named below are the TEMPLATE's,
+> at `81a638e` — not this repository's.** This repository's README is a different
+> file and it has no test suite (`fc-001`, `fc-003`). The account is kept
+> verbatim as an inherited record of an event in `template`, because the whole
+> point of it is that it is a real event with commits attached; do not go looking
+> for these files here, and do not restate the event as something that happened
+> in this repository.
-Worth recording, because it is the only place in this repository where a rule is
+Worth recording, because it is the only place in the template where a rule is
shown catching the failure it was written for — and it caught it in the document
that ships the rule.
@@ -120,10 +166,32 @@ one of which was sitting inside the new entry about counts taken loosely.
The full defect and its check are `docs/spec-defects.md` §3.
+### A second candidate, found here, and NOT by this check
+
+Recorded so the two are not confused. Adapting the template surfaced `mn-001` /
+`mn-002`: the instance's verification tally stated as *22 claims / 10 confirmed*
+in this file and as *eighteen load-bearing / seven stood* in `report/draft.md`,
+agreeing exactly on 3 corrected and 8 under-qualified.
+
+**It was found by reading the two documents against each other, not by the grep**
+— and it could not have been found by the grep as written, since the figures are
+spelled out as words in the draft (`od-003`). It is not yet a verdict: both
+statements are recorded as inherited and unresolved, and reconciling them is
+`od-002`, which is blocked on `gap-001` like everything else here. The likely
+reconciliation is a subset selector — `met-002`, the undefined word
+"load-bearing" — which would make this an UNDER-QUALIFIED defect in the
+documents that define the category.
+
---
## The count
+**Empty because the pass has not run.** When it does, this table is the one
+number this repository will ever own — `status: measured` in
+`docs/inherited-measurements.yaml` is reachable by this value and by nothing
+else. Record it beside the previous count rather than over it if the pass is
+ever run twice.
+
Fill this in. It is a measurement about the drafting process and it is the reason
the pass is worth its cost — a tally that is 0 UNSUPPORTED and 8
UNDER-QUALIFIED says something specific and actionable about how the prose is
@@ -151,7 +219,7 @@ going wrong, which "we checked it" does not.
- Comparison:
- Arms / conditions: <...>
- `n`, budget, stimulus: <...>
-- Source: `docs/inherited-measurements.yaml` ; `experiments//verdicts.json`
+- Source: `docs/inherited-measurements.yaml` ; `: @`
diff --git a/report/draft.md b/report/draft.md
new file mode 100644
index 0000000..d693d25
--- /dev/null
+++ b/report/draft.md
@@ -0,0 +1,1315 @@
+# Personal Notes on a Methodology for Research and Development in Applied Computational Mathematics
+
+**Luiz Velho**, IMPA
+**Claude**, Anthropic (collaborator\*)
+
+> \* *These notes were produced in collaboration with Claude — model Opus, high
+> reasoning effort. See §8.4, which takes the recursion as a subject rather than
+> a disclaimer.*
+
+---
+
+> **Draft status.** This document was drafted on the conversational surface and
+> assembled by hand. Its factual claims about the source project have **not**
+> been through a claims-verification pass — which is the practice §4.1 describes
+> and §7 ships. Every figure in it is a claim to be checked at source before
+> typesetting, and the count of what survives that pass belongs in the record.
+>
+> Section 8.6 is to be written by the first author.
+
+---
+
+# 1. What this is, and what it is not
+
+## Not a tutorial
+
+The major labs have documented their platforms thoroughly, and the open-source
+community has covered the patterns for building software with AI assistance more
+thoroughly still. A fourth account of how to prompt, how to structure a session,
+how to wire a tool into an editor, would be noise. Nothing in these notes
+attempts it.
+
+The volume of that material is itself intimidating, particularly for researchers
+whose background is not computer engineering, in a field moving fast enough that
+specialists struggle to keep up. But the problem is not really volume, and a
+document whose contribution is *translation for scientists* would be solving the
+wrong thing.
+
+## What is actually missing
+
+Almost all of that material is about **building software**. Research is a
+different activity, with a different characteristic failure, and the practices
+that make AI collaboration safe for the first are insufficient for the second.
+
+In software, the failure is a **bug**. The artifact does the wrong thing. It
+crashes, or returns the wrong value, or corrupts a file — and an entire
+apparatus exists to catch exactly that: tests, types, continuous integration,
+code review, staged rollout. The failure announces itself, and the discipline is
+built around making it announce itself sooner.
+
+In research, the failure is a **wrong belief**. A claim that is well-formed,
+plausible, internally consistent, and supported by a number that was computed
+correctly and means something else. It does not crash. It passes every test,
+because it is not a defect in an artifact — it is a defect in what someone
+concluded from one. It propagates into the next experiment as a premise, into
+the next decision as evidence, and into the paper as a result.
+
+No test catches that. Type checking does not catch it, code review does not
+catch it, and a green CI badge is entirely compatible with it.
+
+This distinction is the whole subject of these notes. When a collaborator can
+produce fluent, confident, wrong claims faster than you can check them,
+**epistemic hygiene stops being a virtue and becomes the binding constraint on
+the practice.** Everything that follows — pre-registered falsifiers, refusing to
+ratify a number without reading it at source, marking every carried figure as a
+prior rather than a fact, a ledger of what was ruled out and why — exists to
+prevent a wrong belief. None of it appears in a guide about building software
+with AI, because none of those guides is trying to prevent one.
+
+## The register
+
+These are personal notes. One researcher, one field, one project, five days of
+intensive work, and a methodology that has been run exactly once. Everything
+here is a case study, and the sample size is one.
+
+That should be said before anything else, because the temptation in this genre
+is to write as though a practice that worked for you is a practice. What is
+offered is narrower and, I hope, more useful: an account specific enough to be
+checked, with the failures left in. The project it came from is public. Every
+claim in these notes about what happened there can be verified against a record
+that was kept for exactly that reason.
+
+Where the method failed, it says so — and it failed in ways that turned out to
+be more useful than its successes. That claim is the argument of these notes
+rather than an apology at the end of them.
+
+## A note on scope
+
+The title says *Applied Computational Mathematics* because that is where I work.
+Nothing in the method depends on it.
+
+What it requires is a repository, an experiment that can be pre-registered, and
+a claim that can be checked at its source. That is most of empirical
+computational science, and probably a good deal beyond it. The narrower title is
+honesty about the sample rather than a claim about the boundary.
+
+---
+
+# 2. The setting
+
+## Three surfaces
+
+The work is divided across three AI surfaces with different access and different
+jobs. The division is not about capability — the same model underlies them — but
+about **what each one can see and what it can change**, which turns out to
+determine what each is good for.
+
+**Chat** holds no repository. It derives, argues, designs experiments, reviews
+results, and writes the specifications the other surface executes. Its
+distinguishing property is that it can be wrong for free: a claim attacked here
+costs a paragraph, and the same claim attacked after implementation costs a
+refactor.
+
+**Code** lives inside the working tree. It reads and writes files, runs the
+suite, inspects diffs, drives git, opens pull requests. It builds what Chat
+specified and reports what it did, what it measured, and what it could not do.
+
+**Cowork** connects a result to everything else — cross-experiment synthesis,
+literature sweeps, briefings, decks. It is the surface for work that spans many
+files and many sessions without living in any one of them.
+
+One principle makes this a workflow rather than three parallel conversations:
+
+> **Every handoff between surfaces is a versioned artifact in the repository,
+> never chat context.**
+
+Context does not survive a session. A repository does. That single rule is what
+makes the arrangement reproducible, and it has a corollary that took a while to
+appreciate: **clearing a session is a test of the methodology.** If clearing
+feels risky — if you think *but it knows things that aren't written down* — that
+reluctance is a defect report on the artifacts, not a reason to keep the
+context.
+
+## The four-station loop
+
+One task travels a fixed circuit.
+
+**User** sets the goal and holds the authority. Adjudicates escalations, and is
+the only station that can change what the project is for.
+
+**Chat** writes a specification.
+
+**User relays it, verbatim.** This looks like ceremony and is not. It is the
+only review a specification gets before work is spent on it, and the cheapest
+point at which a specification asking for the wrong thing can be stopped. A
+specification that could go straight from Chat to Code has skipped its only
+gate.
+
+**Code** does the work and reports back.
+
+**Chat verifies in-repo** — clones the branch, runs what needs running, reads
+the claimed line — and returns one of three verdicts: *advance*, *retry*, or
+*escalate*.
+
+Two properties of that circuit matter more than its shape.
+
+**Chat never edits the repository, but it does hold the files.** The clone is a
+separate checkout, outside the working tree, which Chat reads and runs and never
+commits to. This is the reviewer's seat: it can check everything and change
+nothing. The predecessor project's constitution had explicitly ruled it out —
+*Chat holds excerpts by construction, so "Chat verifies Code's numbers" would
+rot into a formality* — and that premise was simply false. The seat existed and
+the workflow had talked the reviewer out of occupying it.
+
+**Escalation is expected, not exceptional.** It is how the loop discovers that a
+specification was mis-aimed, which happens often and should. A stretch of tasks
+with no escalations means either the specifications are unusually good or the
+reviewer is not looking hard enough, and the second is more likely.
+
+## The toolchain, and what each part is for
+
+Not a tutorial, but the choices carry weight and the reasons are worth stating.
+
+**GitHub is the autonomy mechanism, not storage.** Pull requests are a gate that
+cannot be bypassed; branch protection is what makes *never commit to main*
+structural rather than a request. Continuous integration means a rule about
+testing is enforced by a machine rather than by memory.
+
+**The repository is the memory.** Every durable thing — decisions, measurements,
+position, the plan and its amendments — lives in files that a cold session reads
+on arrival. Nothing load-bearing lives in a conversation.
+
+**Two ledgers carry what the code cannot.** A decision ledger, recording each
+foreclosed possibility with the evidence that closed it, its stated limits, and
+the cost of reopening it. A measurement ledger, recording each figure with the
+commit that produced it, the environment it was measured in, and its status —
+measured here, inherited from earlier work, or a recorded gap. Both are written
+by the surface that holds the files and read by the surface that does not.
+
+**Everything is forward-only.** Corrections appear as amendments, never as
+edits. Where a conclusion was drawn and later qualified, both appear, with the
+evidence that moved between them. This is not tidiness; §4.8 argues the pair is
+more informative than either half.
+
+## What was not used
+
+Two components were designed into the method and never exercised. Both need
+describing before their absence means anything.
+
+**Cowork** is the surface for work that spans many artifacts and no single
+session. The concrete jobs: sweeping the literature and reporting where a result
+sits relative to it; synthesising across experiments that were each designed to
+answer their own question, looking for the pattern none of them was testing;
+turning a directory of results into a briefing or a deck; reconciling what a
+roadmap intended against what actually merged. Where Chat holds an argument and
+Code holds a working tree, Cowork holds **the accumulated body of work** — and
+it is the only one of the three that can be handed a task and left to run.
+
+In this project every one of those jobs was done by Chat inside the
+conversation, or by hand, or not at all. The cross-experiment synthesis in
+particular — noticing that four separate experiments were saying the same thing
+at different levels — happened in a chat window and had to be written down
+afterwards from recollection, which is precisely the failure the method is built
+to prevent.
+
+**`spikes/`** is a lane inside the repository with different rules from
+everywhere else. Git-tracked, so a thing tried can be pointed at. No decision
+record, no findings entry, no definition of done, no tests required. One hard
+constraint — nothing in the library or the experiments may import from it,
+enforced by a test that runs in CI — and one permitted output: **a decision or a
+deletion.** Either it taught you something, which goes into the ledger and the
+spike is deleted, or it did not, and the spike is deleted. Directories that sit
+there deciding nothing are the failure mode the lane has.
+
+It exists because of a specific thing that went wrong in the predecessor
+project. A step was written up twice and never started, accumulating five open
+questions, because the constitution had exactly one standard — plan, decision
+record, findings, tests, definition of done — and that standard is right for
+library code and far too expensive for exploration. When a question is open and
+the only sanctioned move is to write about it, **uncertainty turns into
+documents.** The spike lane was invented so that *try it and look* would be a
+legal move.
+
+It shipped on day one, with its rules, its enforcement test and its README. It
+was used zero times.
+
+Both are the informal parts of a formal method, and that they are the two that
+went unused is the subject of §6. I do not think it is a coincidence.
+
+---
+
+# 3. The project
+
+Everything after this section draws its examples from one research project. This
+section supplies enough of it to follow them. It is deliberately brief and
+deliberately non-technical; the project has its own report, and readers who want
+the science should go there.
+
+## The question
+
+**Active vision** is the proposition that perception is better posed as a
+controlled sampling problem than as a reconstruction problem — that *choosing
+where to look* improves what is perceived. The project tested that claim for
+binocular depth estimation.
+
+The biological motivation is concrete. A camera samples every pixel at the same
+resolution; an eye does not. The human fovea covers one to two degrees at high
+acuity, resolution falls off sharply beyond it, and several times a second a
+saccade repositions that window. And the two eyes are not cameras with fixed
+geometry: they rotate about centres fixed in the head, converging on whatever is
+being looked at, so the stereo geometry itself changes every time gaze moves.
+
+## The machinery, in six sentences
+
+Two views of a scene, taken from slightly separated positions, disagree about
+where things are. That disagreement — **disparity** — is larger for near
+surfaces than far ones, so depth can be recovered from it. A **matcher** finds,
+for each point in one image, the corresponding point in the other; it returns a
+disparity, a confidence, and a **validity mask** marking points where it found
+nothing trustworthy. **Vergence** is how far away the two lines of sight cross —
+what the system is focused on — and converting disparity into metric depth
+requires knowing it. A **belief** accumulates the depth estimates across
+successive fixations, each new measurement combined with what is already
+believed, weighted by confidence. And a **gaze policy** chooses where to look
+next, typically by finding where the belief is least certain.
+
+That loop — fixate, measure, update the belief, choose the next fixation — is
+the object under study. Whether closing it improves anything is the question.
+
+## Three things that recur in the examples
+
+**Half-occlusion.** At a depth discontinuity — the edge of a nearer surface
+against a farther one — a strip of the background is visible to one eye and
+hidden from the other. Those points have no correspondent at all. The matcher
+does not report this; it finds the best available wrong match, and a sharp
+minimum at a wrong disparity is genuinely sharp. **The result is a confident,
+incorrect estimate at exactly the geometry an active system might hope to
+resolve by looking elsewhere.** This is the phenomenon that motivated the whole
+project, and several examples turn on it.
+
+**Foveal weighting.** Implementing the fovea by measuring everything at full
+resolution and then *discounting* what is far from gaze — as opposed to a real
+retina, which never samples the periphery finely in the first place. The
+difference between weighting and sampling turns out to matter a great deal.
+
+**The synthetic stimulus.** A random-dot stereogram: a field of noise, warped by
+a known depth field to produce the second view. Its virtue is that depth exists
+*only* in disparity — nothing can be recovered from shading or texture, so a
+system that appears to see depth is genuinely seeing stereo. §4.6 is about what
+was wrong with it.
+
+## The shape of the work
+
+Thirteen experiments over five days, each pre-registered before its runner
+existed. Two predecessor repositories, from which geometry, measurements and a
+set of methods were carried forward. Roughly four thousand lines of library code
+against a slightly larger volume of tests, thirteen foreclosed decisions,
+eighty-seven measurements taken in the project and forty-one inherited.
+
+Experiments are referred to below by their position in that sequence rather than
+by name. What matters for these notes is not which experiment found what, but
+that each was declared in advance, ran, and reported against its own falsifier.
+
+## What it found, in brief
+
+The negative results were consistent. The choice of gaze objective did not
+matter: two objectives that never once selected the same pixel produced
+indistinguishable results. Knowing *where* measurement was possible outweighed
+knowing *how uncertain* it was by a large factor. Foveal weighting on a
+uniformly sampled sensor was strictly lossy. Deliberately re-converging in order
+to bring new depths into range made estimation worse.
+
+One positive result survived. Closing the loop — actually re-imaging at each new
+fixation rather than re-weighting a single capture — helped, at a substantial
+cost, and the benefit decomposed almost entirely into **coverage** rather than
+accuracy: the moving system's advantage was that it *saw more of the scene*, not
+that it *estimated better*.
+
+And four of the thirteen experiments turned out to be about the instrument
+rather than the framework. They were the precondition for reading the other
+nine.
+
+None of that is the subject of these notes. It is the material the examples come
+from.
+
+---
+
+# 4. How it goes wrong, and how you know when it's going right
+
+Seven failure modes. Each is a way a research project acquires a wrong belief,
+each was observed in this project rather than anticipated, and each has a
+mechanism that either held or did not. The section ends with the harder
+question: what it looks like when the method is working.
+
+## 4.1 Fluent wrong claims
+
+The characteristic failure, and the one everything else is downstream of.
+
+A language model produces confident, well-formed, internally consistent
+statements. When it is right, that fluency is the point. When it is wrong, the
+fluency is the problem: nothing in the surface of the claim distinguishes the
+two, and the reader's only defence is to check.
+
+**What it looked like.** The conversational surface asserted false things at a
+rate of roughly one per day. Not hallucinated citations or invented functions —
+those are easy to catch. The failures were subtler and more dangerous:
+
+A background claim that a confidence failure had been *replicated* across two
+stimulus families, when the record showed the two families disagreeing in sign.
+The claim was plausible, it was about the project's own prior work, and it was
+the motivating premise of an entire section.
+
+A statistic described as *frozen* across a run when it drifted by a small amount
+that mattered.
+
+A foreclosure asserted to be settled by an experiment that had measured
+something else entirely — the identifier resolved, the entry existed, and it
+said a different thing.
+
+A count of experiments taken from the highest identifier rather than from the
+set, because one had been specified and deliberately not run.
+
+Every one of these is the same shape: **a claim that would survive any reading
+and fails only against its source.**
+
+**The mechanism.** Three parts, and they work together.
+
+*Non-ratification.* The surface that does not hold the files may not settle a
+numeric question. It states where to check, never what the number is. A
+specification says *verify before recording*, and a number arriving in a report
+is a hypothesis with a pointer until someone opens the file.
+
+*Verification at source.* The reviewing surface clones the branch and reads the
+line. Not the diff, not the excerpt, not the summary — the line. A reviewer
+working from excerpts can only check that a report is internally consistent,
+which is the one thing a wrong report is most likely to be.
+
+*A claims pass on any document making factual assertions.* Before the project's
+technical report was typeset, every load-bearing claim in it was checked at
+source and marked confirmed, corrected, under-qualified, or unsupported. Of
+eighteen claims in the first draft, seven stood as written.
+
+**Did it hold?** Yes, and the numbers are the argument. Three claims wrong on
+the figure, eight true only in the band or arm they came from, none unsupported
+— all caught before typesetting. The category that mattered most was not the
+outright errors but the **under-qualified** ones: claims right in the condition
+they were measured under and misleading without it. Those typeset cleanly and
+read as more general than they are, and there were nearly three times as many of
+them as of outright mistakes.
+
+**Where it did not hold.** Verification runs per passage; repetition crosses
+passages. The same ratio was verified correctly in one section and left without
+its qualifying condition in another, because a careful reading of each passage
+structurally cannot notice that a figure appears twice. The fix is mechanical
+rather than attentional — grep for repeated figures across the document — and it
+was added afterwards, which is to say the failure was found by having it happen.
+
+## 4.2 Stale state
+
+A specification is written from a snapshot and executed later, against a
+repository that other work has moved. The specification is not wrong when
+written and not right when run, and nothing in it marks the difference.
+
+**What it looked like.** Specifications drafted against a commit that had
+advanced by the time they were sent — twice, once trivially and once not. The
+serious instance had a premise about which arms of an experiment re-estimated a
+parameter, and it was false; the entire question the specification posed
+depended on it, and the work would have measured something other than what it
+claimed.
+
+The general form is worth stating because it is not obvious: **facts do not
+expire, they just get old.** A statement learned in a conversation stays
+available with undiminished confidence long after it stops being true, and a
+long-running thread accumulates true-once assertions at a steady rate while
+confidence in them stays flat.
+
+**The mechanism.** Every specification names, explicitly and at the top, the
+repository state it assumes — the commit, what is tracked, what does not yet
+exist, the SHAs of any reference checkout. **The executing surface's first
+action is to verify those assumptions and reject the task if any is false.**
+
+An assumption stated is an assumption that can fail loudly. An assumption
+unstated fails quietly and much later.
+
+**Did it hold?** Three specifications were rejected on false assumptions, each
+correctly, each before any work was spent. That is the mechanism working exactly
+as designed, and it is the clearest case in the project of a rule paying for
+itself: the rejections cost a round trip each, and the cheapest of them would
+otherwise have cost an experiment.
+
+It also generalises past its origin. The rule was written for repository state,
+and what it actually enforces is that **the surface with the files gets to
+overrule the surface with the memory** — which is the same asymmetry as §4.1's,
+arriving from a different direction.
+
+## 4.3 Work that cannot fail
+
+The deepest failure mode, and the hardest to see, because everything about it
+looks like success.
+
+A test whose assertion is satisfied by construction. A criterion no sample size
+can resolve. An acceptance check written after the code it accepts. In each case
+the apparatus runs, reports green, and establishes nothing — and because it
+reports green, nobody looks.
+
+**What it looked like.** The clearest instance was a statistical criterion
+inherited from earlier work: a difference counted as real if it exceeded the
+seed-to-seed spread. That is a defensible question — *is this effect larger than
+the variation between scenes* — but it is not a test of whether an effect
+exists, and it has a property nobody noticed for ten experiments: **the spread
+is a sample standard deviation, so it does not shrink with more samples.**
+Adding seeds refines the estimate of the spread and moves the threshold nowhere.
+No number of seeds could ever resolve anything.
+
+That was discovered only when a specification asked, for a different reason,
+*how many seeds would settle this?* — a question that cannot be answered without
+inspecting the criterion. Ten experiments had reported verdicts against it, and
+every one of their null results had been read as evidence of absence when the
+criterion could not have produced anything else.
+
+Smaller instances recur throughout. An acceptance test that would have passed on
+a scene where the geometry made the quantity identically zero. A calibration
+render whose three surfaces occluded each other, so a check written for three
+measured one — and the render succeeded silently. A falsifier that could only
+fire at a fixation where the effect it tested was structurally absent.
+
+**The mechanism.** Pre-registration with a falsifier, and a specific definition
+of one.
+
+Not a success criterion. A **falsifier for the specification itself**: a
+statement of what result would mean this specification asked the wrong question.
+It names a result, a threshold, and the conclusion that the task — not the
+worker — was mis-aimed. It is reported against whichever way it comes out,
+including when it does not fire.
+
+The rule that follows: **a specification with no falsifier cannot fail, and by
+the method's own standard it does not carry.**
+
+**Did it hold?** Partly, and the partial failure is instructive. Falsifiers
+caught a great deal — including several cases where the second clause fired and
+an experiment was found to have asked the wrong question. But three times the
+actual outcome fell outside *every direction the falsifier had enumerated*: an
+intervention that actively harmed rather than merely failing to help; an effect
+that shrank rather than staying flat or growing; a result that reframed the
+question rather than answering it.
+
+The pattern in those misses is consistent. **The falsifiers were written for the
+null and the confirmation, and the informative outcome was the reversal.** After
+the third occurrence the practice changed: enumerate all four directions —
+persists, shrinks, grows, reverses — and report which occurred. That is a small
+change and it is the single most useful thing the project learned about writing
+them.
+
+## 4.4 Inherited assumptions
+
+Numbers and methods carried in from earlier work acquire, silently, the
+authority of things decided here. Nobody decides to promote them. They simply
+arrive without a marker, get cited, and after a few citations there is no way to
+tell them apart from what was measured.
+
+**What it looked like.** This project inherited a great deal from two
+predecessors: forty-one measurements, a body of geometry, a matcher, a stimulus,
+and a set of parameter values. The measurements were handled from the second day
+— every one marked `inherited`, meaning *a prior awaiting re-test, not an
+established fact*, with the repository and commit it came from. That discipline
+worked. Inherited figures were cited as inherited, and when one was contradicted
+the contradiction was legible.
+
+The methods were not handled at all, and nothing in the apparatus noticed. A
+statistical criterion, a set of band thresholds, a search window, an inhibition
+radius, a foveal width, the parameters of a validity test — all carried across,
+all used, none marked. Thirteen carried methods, **eight of which had never been
+examined by anyone in this project**, and one of them was the criterion in §4.3
+that governed ten experiments before its properties were checked.
+
+The asymmetry is the finding. The same repository that would not let a *number*
+cross without provenance let a *criterion* cross unmarked — and the criterion
+determined how every number was read.
+
+**The mechanism.** Extend the inherited marker to methods, with the same three
+fields: what was carried, from where, and whether it has been re-derived here.
+Then one rule that the project learned the expensive way:
+
+> **A carried method gets its entry at first use, not at first suspicion.**
+
+**Did it hold?** It was not in place when it was needed. Applied to numbers on
+day two and to methods at experiment twelve, by which point the damage was done
+and required a re-analysis of every recorded verdict. That re-analysis is the
+reason the failure is well documented rather than well hidden.
+
+The template that came out of this project ships the methods section
+pre-populated with the rule and this failure as its worked example. That is the
+only honest form: a rule presented without the thing that motivated it gets
+simplified away by the next person to read it.
+
+## 4.5 Knowledge that lives only in a conversation
+
+The methodology's foundational rule is that every handoff is a versioned
+artifact and never chat context. The failure mode is what happens when something
+important slips past it — and the thing about this failure is that **you cannot
+detect it from inside the conversation**, because there the knowledge is present
+and load-bearing and feels like part of the project.
+
+**What it looked like.** An eighteen-step plan, with stages, altitudes, and two
+recorded amendments, existed in a chat window and nowhere else. It had shaped
+every specification for a week. Nobody noticed, because both parties to the
+conversation could see it.
+
+It was found the moment a session was cleared. The fresh session was asked to
+record the plan, searched the repository, the git history including commit
+bodies, and both reference checkouts, found six of eighteen steps inferable from
+side-references in old prompts and none of the stage groupings, and **refused to
+invent the rest.** Its stated reason was that the state file is the one document
+a cold session trusts without checking, and fabricated content carrying that
+authority would be the same defect in the place it does most damage.
+
+That refusal was the detector. Nothing else in the project would have caught it.
+
+A second instance in the same shape, smaller and sharper: a decision was taken
+in conversation and then recorded in the repository as *complete*, citing an
+experiment identifier that resolved to an entry about something else. The claim
+entered the durable record wearing a citation that did not support it, and it
+sat next to a verdict in the same file explicitly saying the opposite.
+
+**The mechanism.** The repository is the memory, and **clearing a session is the
+test of that claim.** If clearing feels risky, the reluctance is a defect report
+on the artifacts.
+
+There is a subtler corollary the second instance forced. A record that
+distinguishes *what is established* from *what is intended* has a boundary, and
+a claim can cross it. The project's state file was eventually split into a
+**position** half — derivable from the ledger, verifiable, cannot be wrong
+without the ledger being wrong — and a **plan** half, marked as intent supplied
+from outside the repository and not established by it. The failure was a claim
+that entered through the plan half and was recorded in the position half,
+**losing its warning without losing its origin.** Splitting the file created the
+boundary that made the error invisible.
+
+**Did it hold?** The rule held; the enforcement arrived late. Both instances
+were caught, one by a cold session and one by a reviewer reading the file, and
+both are in the record with their corrections. What the project did not have —
+and what the template now ships — is any check that runs *before* something
+load-bearing goes unwritten. Clearing the session is a good detector and an
+expensive one, because by then the work has already been done under the
+unwritten assumption.
+
+## 4.6 The instrument you authored
+
+This one is not about AI collaboration. It belongs here anyway, for a reason
+worth stating: **it was the failure that consumed the most effort, it is
+invisible to every mechanism described so far, and it is going to be common** —
+because AI collaboration makes synthetic experiments dramatically cheaper to
+build, and cheap instruments get less scrutiny than expensive ones.
+
+**What it looked like.** The project's synthetic stimulus followed a standard
+construction: generate a texture, warp it by the ground-truth field to produce
+the second view. The warp has a defect at depth discontinuities, where adjacent
+output samples draw from widely separated input regions, and the second image
+acquires a stretched patch of real texture with no consistent correspondence.
+
+The obvious criticism of such a stimulus is that it lacks the hard cases that
+real data has. The measured problem was worse and had the opposite sign. The
+artefact was **harder than the real thing it stood in for, and it passed the
+validity test built to catch exactly that class of failure** — self-consistent
+enough to be accepted, wrong enough to poison the estimate, and concentrated in
+precisely the region where the scientific question lived. Rendering the same
+geometry physically, the synthetic fixture was an order of magnitude worse in
+the tail. Two fifths of the measured pixels, four fifths of the total error.
+
+Four of thirteen experiments went to establishing this. Conclusions already
+drawn had to be re-read. And the obvious correction — restrict the analysis to
+the clean region — was itself wrong, because the contaminated band was also the
+only band where the question was live: excluding it did not isolate clean
+measurement, it isolated easy measurement.
+
+**The mechanism.** There isn't a clean one, which is why this subsection is
+shorter on remedy than the others. What the project arrived at:
+
+> **A stimulus is an instrument, and must be characterised before it is used to
+> measure.**
+
+Characterised in the specific sense of an unfamiliar sensor: what are its
+failure modes, where do they fall, and do they coincide with the regime you care
+about. The last clause is the load-bearing one, and the expectation should be
+that **they do** — because the regime you care about is usually the hardest
+thing to synthesise, and therefore where the synthesis cuts its corners.
+
+**Did it hold?** It was learned rather than applied, at a cost of roughly a
+third of the project's experiments. That said, the cost bought a durable result:
+the fixture's operating range is now measured, and every experiment afterwards
+was sized against it.
+
+The generalisation is uncomfortable and I think correct. **In simulation-based
+science the instrument is authored by the same hand as the hypothesis.** Real
+data can be difficult, biased, or insufficient; it is not written by someone who
+wants a particular answer. That is not an argument against synthetic stimuli,
+which offer dense ground truth no capture provides. It is an argument that they
+deserve the scrutiny given to an unfamiliar instrument, and that the scrutiny
+should be budgeted in advance rather than discovered.
+
+## 4.7 Momentum
+
+The last failure mode is the only one with no artifact to point at, because it
+consists of not looking.
+
+**What it looks like.** A method that is working stops being questioned, and it
+stops being questioned *precisely because it is working.* Each clean iteration
+lowers the felt need for the next one to be examined. Verdicts land, falsifiers
+report, the ledger grows, and somewhere in the middle of that the frame within
+which all of it is being measured stops being a question anybody asks.
+
+This project had two clear instances. The statistical criterion of §4.3 went ten
+experiments without examination during the stretch where results were arriving
+cleanly — the criterion was not questioned *because nothing was going wrong*.
+And a positive result, one experiment old and underpowered in its most important
+band, created immediate pressure to build on it rather than to consolidate; the
+consolidation, when it eventually happened, found the criterion defect that
+changed how every null in the project read.
+
+**The mechanism.** A devil's advocate pass: argue the case against the whole
+approach, not against its details. Not the specification's falsifier, which asks
+whether a task was well-posed, but one level up — *what result would mean this
+stage should not continue.*
+
+Two triggers, either party may call it. A stage boundary, where the next
+commitment is largest and least paid for. And **a run of clean iterations**,
+which is the counterintuitive one and the one that matters: a run of failures
+gets scrutiny for free.
+
+Two properties keep it from decaying. It must be **occasional** — a
+per-iteration requirement would generate a devil's advocate pass on a routine
+commit, the pass would be pro forma, and a check that always fires is a check
+nobody reads, which is §4.3's failure in a new costume. And it must **not be
+announced in advance**: a pass labelled as an exercise argues knowing it is an
+exercise, which is a weaker thing. The best instance in this project came from a
+provocation I could not distinguish from a position — and being unable to tell
+is what made it work.
+
+**Did it hold?** It was agreed in conversation and never written into the
+workflow, which by the method's own standard means it did not exist. It ran
+twice anyway, informally, and both times produced something. That is the least
+satisfying verdict in this section: a mechanism that worked when used and was
+never made structural, left depending on someone having a good instinct on a
+given afternoon.
+
+## 4.8 The signatures of good work
+
+Everything so far has been failure, and there is a reason for that: **failure is
+easy to write about because it hurts.** A wrong claim announces itself once you
+check it. A stale premise gets rejected. A criterion that cannot resolve
+anything can be shown, arithmetically, to be unable to.
+
+Recognising the method *working* is a genuinely harder problem, and it is the
+more important one. A good result and a lucky one look identical from outside. A
+project can produce clean verdicts for a month on a broken criterion — this one
+did — and every artifact along the way looks like evidence of health.
+
+So the two sides of the coin are not symmetric. **Failure is detected; success
+has to be recognised.** The first has mechanisms; the second has only signatures
+— and if you cannot read them, you cannot tell a working method from a
+comfortable one.
+
+Here is the transition, and it is the thing I would most want a reader to take
+from these notes.
+
+> **Every signature of the method working is an event that felt, at the time,
+> like something going wrong.**
+
+A result nobody wanted. A falsifier that fired in a direction nobody enumerated.
+A collaborator refusing to proceed. A correction to a claim already published
+internally. Each of those arrives as friction — as a delay, an inconvenience, a
+thing that has to be gone back over. And each of them is the system doing the
+only job it exists to do.
+
+That inversion is not a nice framing; it is the practical content of the whole
+method. If your instinct treats those moments as costs, you will optimise them
+away, and what remains will be a fast, fluent, unfalsifiable practice that feels
+excellent. **The discipline is learning to feel the friction as the product.**
+
+Five signatures, each observed here.
+
+**A result nobody wanted.** The strongest indicator and the least comfortable.
+Foveal weighting — the biologically motivated mechanism at the centre of the
+framework — lost on every seed. The one positive result decomposed to show the
+system's value was that it *saw more*, not that it *estimated better*. The
+statistical criterion turned out not to test what everyone had read it as
+testing. None of these was hoped for. **A method that only ever confirms is not
+a method**, and the surest sign of one is a project whose results all point the
+way its author was already facing.
+
+**An outcome outside every direction the falsifier enumerated.** Three times: an
+intervention that harmed rather than merely failing; an effect that shrank
+rather than growing; a result that reframed the question. Each omission recorded
+beside the result. A falsifier that always fires in one of its stated directions
+is probably not testing anything — it is checking that reality is one of the two
+shapes you imagined, and reality is under no obligation.
+
+**A control invented that nobody asked for.** A specification asked whether a
+geometric operation was accurate. The test that came back also asserted that
+*not* performing it must be ten times worse — because a number is only a result
+when the alternative is measured beside it. That pattern then recurred three more
+times, unprompted. **A collaborator adding the control you forgot is the
+clearest evidence that a standard has been internalised rather than complied
+with**, and it is the difference between a rule that is followed and a rule that
+is understood.
+
+**Someone stopping rather than inferring.** A cold session refused to write down
+a plan it could not find, and said why: fabricated content in the one file a
+cold session trusts would be the same defect in the place it does most damage.
+Another declined to commit because permission had not been given, at the cost of
+a round trip. Both were mildly inconvenient. Both were the constitution working
+on exactly the case where inferring would have been easier — which is the only
+case where a constitution is worth anything.
+
+**A correction that makes the claim narrower and more interesting.** A
+background section asserted that a confidence failure had been replicated across
+stimulus families; the record showed the families disagreeing in sign. The
+corrected claim was narrower, and it was a *better* motivation than replication
+would have been, because a failure that appears on real imagery and not on
+synthetic dots poses a sharper question than one that appears everywhere. **When
+a correction improves the argument, the record is doing what it exists for.**
+
+**How to cherish it: keep the wrong version beside the right one.** Not as
+ritual honesty — because the pair carries information neither half does. That a
+verification pass recommended a figure read off a rounded table, and a second
+pass corrected it from the underlying rows, is a finding *about verification*:
+the escalation from summary to source is what made the difference, and it is
+reusable. Erase the first version and you keep a correct number and lose the
+lesson.
+
+This is why the record is forward-only. Corrections are amendments, never edits.
+The history of a claim being wrong and then right is more valuable than the
+claim.
+
+---
+
+# 5. What it cost, and what it caught
+
+The previous section argued that the signatures of a working method arrive
+disguised as friction. This one puts numbers to the friction, and then makes a
+claim that follows from §4.8 and is easy to miss: **the costs and the catches
+are not two lists. They are the same list, read twice.**
+
+## What the friction cost
+
+**Verification costs an iteration.** Checking a document's claims at source
+before typesetting it took a full cycle and produced no prose. Re-scoring every
+recorded verdict under a corrected criterion took another. Two of thirteen
+iterations produced no new science.
+
+**Pre-registration costs a commit and an argument.** Declaring what would
+falsify a hypothesis, and separately what would mean the experiment asked the
+wrong question, before the runner exists — that is genuine work done at the
+moment when the answer is least visible and the temptation to defer is
+strongest.
+
+**Rejections cost round trips.** Three specifications were rejected on false
+premises. Each cost a cycle.
+
+**The record costs reading.** Nine thousand lines of ledger in five days, and
+one human reading every pull request.
+
+## What the friction caught
+
+The same events.
+
+The verification iteration found three wrong figures and eight claims true only
+in the condition they were measured under. The re-scoring iteration found that a
+criterion had governed ten experiments without testing what it was read as
+testing. The three rejections each stopped work that would have measured the
+wrong thing — one of them a premise so central that the experiment would have
+answered a different question entirely and reported the answer as if it were the
+one asked.
+
+**A wrong belief costs everything downstream of it.** That is not rhetoric; it
+is the arithmetic of this project. The criterion defect propagated through ten
+experiments and required re-reading every null result they had produced. Caught
+at experiment two, it would have cost an afternoon.
+
+So the honest accounting is not *the method cost two iterations and caught four
+errors.* It is: **the method spent two iterations to avoid re-running ten.**
+
+## The cost of success
+
+The costs above are the ones a reader expects. These are the ones that
+surprised, and every one of them arrives when things are going *well*.
+
+**A positive result creates pressure to build on it.** The project's one
+positive finding was a single experiment old, underpowered in its most important
+band, and untested against its own dose-response. The obvious next move was to
+build the next stage on it. The consolidation done instead found the criterion
+defect — which is to say, **the discipline that paid best was the one applied at
+the moment of greatest momentum.** Had the result been negative, nobody would
+have wanted to build on it and the consolidation would have happened for free.
+
+**A run of clean iterations is when framing stops being examined.** §4.7's
+trigger. Worth restating as a cost rather than a mechanism: the better a project
+is going, the more expensive its next unexamined assumption becomes, because
+more has been built on top before anyone checks.
+
+**Rigour is self-reinforcing, and that is not entirely good.** Every artifact
+that meets a high standard raises the felt cost of producing one that does not.
+The low-ceremony lane — where an idea can be tried and thrown away — shipped on
+day one and was never used once. §6 argues this is the method's deepest problem
+and not an oversight.
+
+**The record outgrows the reader.** A methodology that produces more record than
+anyone can read has moved the bottleneck rather than removed it. In the
+predecessor project, two failures got in exactly there, under merge pressure,
+with green tests. The apparatus catches wrong *numbers* well and wrong *merges*
+poorly, and the variable that moved was the rate.
+
+**The deliverable is unfashionable.** The better this works, the more of the
+output takes the form of *a well-ordered set of eliminated possibilities* —
+harder to publish than a benchmark number, and harder to describe at a
+conference. That is a fact about publishing rather than about the knowledge, but
+it is a real cost borne by whoever adopts the method.
+
+## The asymmetry that justifies the whole thing
+
+One number carries the argument. Of eighteen load-bearing claims in the first
+draft of the project's technical report, **seven stood as written.**
+
+The other eleven were not fabrications. They were claims made by someone who had
+read the record, believed them, and would have published them. Three were wrong
+on the figure; eight were true in the condition they came from and misleading
+without it.
+
+That is the ratio the method exists for. In software, shipping eleven of
+eighteen statements slightly wrong is a bad release. In research, it is a body
+of work that other people build on.
+
+The costs are all paid in time. The failure is paid in credibility, and it is
+paid by everyone who cited you.
+
+---
+
+# 6. Where the method failed
+
+Four of the seven mechanisms in §4 arrived late, after the failure they were
+built for had already happened. That is not damning — a method extracted from
+one project will be, by construction, a record of what that project learned the
+hard way. Those are covered where they occurred.
+
+This section is about the failures that are still live. The last one is the
+serious one, and I want to arrive at it properly.
+
+## Three that arrived late
+
+**The inherited marker was applied to numbers and not to methods.** Day two for
+figures, experiment twelve for criteria — by which point a carried statistical
+bar had governed ten experiments. The asymmetry is the finding: a repository
+that would not let a number cross without provenance let a criterion cross
+unmarked, and the criterion determined how every number was read.
+
+**Verification ran per passage while repetition crossed passages.** Found only
+because the same ratio appeared twice and someone happened to look at both.
+
+**Specification defects had no ledger.** Decisions got foreclosures,
+measurements got provenance, and badly-written specifications got nothing — so
+the same two errors recurred: bundling a bounded task with an unbounded one, and
+naming a branch without asking for a commit. Both were cheap and both were
+avoidable by a five-line list that did not exist.
+
+All three now ship in the template, each with the failure that motivated it.
+That is the ordinary way a method matures, and it is unremarkable.
+
+## The devil's advocate was agreed and never written down
+
+Less ordinary, and worth its own paragraph because of what it demonstrates.
+
+The pass was proposed, discussed, refined into two triggers, and endorsed. It
+ran twice, informally, and both times produced something the loop would not have
+produced. **It was never written into the workflow file.**
+
+By the method's own foundational rule — every handoff is a versioned artifact,
+never chat context — that means it did not exist. A mechanism designed to catch
+the failure of a method going unquestioned was itself never subjected to the
+method. If the sessions had been cleared, it would have vanished without trace,
+and nobody would have known to miss it.
+
+## And now the one that matters
+
+Two components of the method were never used. Not underused. **Zero times.**
+
+The low-ceremony lane — where an idea can be tried and thrown away, no decision
+record, no findings entry, no definition of done, one permitted output — shipped
+on the first day with its rules, its README, and a test that enforces its one
+constraint in continuous integration. It was used zero times in thirteen
+experiments.
+
+The third surface, for cross-experiment synthesis and work spanning many
+artifacts, was designed into the workflow, given a role in the table, and used
+zero times. The synthesis it existed for happened in a chat window and had to be
+reconstructed afterwards from recollection.
+
+The obvious reading is oversight. It is not, and the evidence against it is
+specific and damning.
+
+**The lane was built as a fix for exactly this failure, in the previous project,
+where it had already cost something.** In that project a step was written up
+twice and never started, accumulating five open questions, because the
+constitution had one standard — plan, decision record, findings, tests,
+definition of done — and that standard is right for library code and far too
+expensive for exploration. When a question is open and the only sanctioned move
+is to write about it, uncertainty turns into documents. The diagnosis was made
+explicitly. The remedy was designed explicitly. It was carried into the new
+repository on day one, complete with enforcement.
+
+And then the new project **did the same thing again.** Not by forgetting the
+lane, but by never reaching for it, while writing preambles and specifications
+and analyses about questions that could have been settled in twenty minutes by
+trying them.
+
+So the finding is not *the method lacked a lane for informal work.* It is:
+
+> **A project with strong ceremony does not reach for its low-ceremony lane,
+> even when the lane exists, is enforced, is documented, and was built
+> specifically because its absence had already caused harm.**
+
+The mechanism is not mysterious once stated. Every artifact that meets a high
+standard raises the felt cost of producing one that does not. After ten
+pre-registered experiments with falsifiers, verdicts and provenance, writing a
+directory containing a rough script and no record does not feel like the
+sanctioned move it formally is. **It feels like a lapse.** The rigour is
+self-reinforcing, and self-reinforcement does not distinguish between the parts
+of itself that are load-bearing and the parts that are ritual.
+
+This has a second face which is worse. The formal parts of the method are the
+ones that produce artifacts, and artifacts are the only thing the method can
+see. A spike that would have settled a question in an afternoon leaves nothing
+behind if it succeeds — the whole point is that it is deleted. **So the method is
+structurally blind to the value of the work it is discouraging**, and the record
+it produces will always show that the ceremony was worth it, because the record
+is made of ceremony.
+
+I felt this directly and did not name it at the time. There were several
+afternoons of writing about a question — carefully, with pointers and open
+questions and a plan for resolving it — where the honest move was to spend
+twenty minutes finding out. The writing was good. It met the standard. It also
+postponed the answer, and it was easier than admitting I did not know, because a
+document that says *here are the five open questions* looks like progress and a
+directory called `does-the-fixture-need-occlusion` looks like not knowing.
+
+That is the thing a methodology cannot fix by adding a rule, which is why it is
+the last section rather than a bullet in §4. **Adding the directory was not
+enough. It was already there.**
+
+## What might actually work
+
+Not confident about this, and stating it as a proposal rather than a finding.
+
+A **trigger** rather than a lane, in the same shape as the devil's advocate
+pass: *when a question has been written about twice without being tested, it
+goes to a spike.* Twice is countable. It fires on the observable — documents
+produced — rather than on a judgement about whether something needs trying,
+which is exactly the judgement the ceremony corrupts.
+
+And a **default toward the cheap move at the moment of maximum uncertainty**.
+The method's autonomy ladder governs how much the collaborator decides; nothing
+governs how much rigour the artifact must carry, and the answer should vary. At
+the point where a question is genuinely open, the standard should drop, not
+rise.
+
+Both are in the template. Neither has been tested, and the reader should treat
+them accordingly — as the most recent guesses of someone who has now made this
+mistake twice.
+
+---
+
+# 7. The template
+
+The method described here is extracted into a repository template, public and
+instantiable. This section says what is in it, what deliberately is not, and
+what it does not yet know.
+
+## What it contains
+
+**A constitution.** The rules the file-holding surface operates under: what it
+may do without asking, what requires permission, what it may never do. About a
+third of the original was domain-specific — coordinate frames, units,
+conventions particular to one problem — and that third is now an empty section
+marked *domain invariants*, with the filled original visible beside it. The
+blank is the template; the example is what makes the blank legible.
+
+**A workflow.** The four-station loop, the three verdicts, the rule that every
+specification carries a falsifier and names the repository state it assumes.
+Plus the two additions §4 argued for: the devil's advocate pass with its
+triggers, and the note that the reviewing surface's clone is a separate checkout
+it reads and runs and never commits to.
+
+**Two ledgers, as schema and prose headers with no entries.** Most of their
+value is in the headers — the rule that one is written by the surface holding
+the files and read by the surface that does not; the distinction between what is
+derivable and what is intent; the procedure for reopening a closed decision,
+which is to annotate and never reverse.
+
+**An experiment exemplar.** Five empty files in the order they are written,
+pre-registration first, with a short note on why the order is load-bearing.
+
+**The low-ceremony lane**, with its rules, its enforcement test, and — per §6 —
+its own record of non-use.
+
+**A report scaffold**, including the claims-verification practice: prose drafted
+in one format, typeset in another, both committed so the translation is
+diffable, and a pass that checks every load-bearing claim at source before
+anything is set.
+
+**A list of specification defects**, which is the artifact §6 says did not
+exist, seeded with the two that recurred.
+
+## What it does not contain
+
+No library code, no experiments, no ledger entries, no domain content. The rules
+travel; the content does not.
+
+Also absent, deliberately: several practices from the original project judged
+specific to it rather than general. A migration audit of two particular
+predecessors — the practice survives inside *carry only what can fail*, the
+artifact does not. A provenance file for fixture data, which without any data
+beside it is ceremony. A structural test resting on a decision the template's
+user has not taken.
+
+Each omission is recorded with its reason, which is the same discipline the
+template applies to everything else.
+
+## The rule the template is built on
+
+One rule governed the extraction and it is worth stating on its own:
+
+> **Every rule that ships carries the observed failure that motivated it, in one
+> sentence.**
+
+A rule without its reason gets simplified away by the next person to read it.
+Someone encountering *mark carried methods at first use* with no explanation
+will reasonably conclude it is bureaucratic and drop it. Someone encountering it
+beside *a carried criterion governed ten experiments before anyone asked what it
+tested* will not.
+
+This has a consequence that took a moment to accept. **The template is mostly a
+record of failures**, and it reads that way. That is correct. A methodology
+presented as a set of good practices is a set of assertions; presented as a set
+of failures with their remedies attached, it is evidence.
+
+## What it does not know
+
+The template was audited against one question: can a session starting cold, with
+no other context, read it and write a first specification without asking what
+any of it means? That audit found several gaps, most of which were closed. Four
+remain open and are recorded in the repository rather than fixed, because fixing
+them would mean inventing rules the original project never exercised.
+
+The largest: one of the method's own concepts — the distinction between
+decisions that change what the project *is* and decisions about how to build it
+— is defined in the template by an example from a layered software architecture.
+For a project without that structure, the distinction has no test. It is a real
+gap and the honest fix is for a second project to supply the general case by
+having its own.
+
+## And the thing to hold on to
+
+The template's value is **unproven**, and its front page says so. One
+researcher, one field, one project, five days. It does not claim to be a
+validated methodology. What it claims is narrower and checkable: these rules
+were exercised, several of them failed in specific recorded ways, and the fixes
+are marked as new and untested.
+
+There is one exception, and it is the only place in the template where a rule is
+caught working rather than argued for. The check for figures repeated across a
+document fired on the template's own front page, where a count had been taken
+from the highest identifier rather than from the set — and the figure had been
+repeated in a second file, so correcting the first would have left the second
+standing. The tree-wide search found it; a re-reading would not have.
+
+That is a small error about an unimportant number. It is also, exactly, the
+failure this whole method exists to prevent, occurring in the provenance
+paragraph of the document that ships the remedy. It is recorded there, beside
+the rule, for that reason.
+
+---
+
+# 8. Reflection
+
+## 8.1 The bottleneck moved
+
+Thirteen experiments in five days. At no point was implementation the limiting
+resource.
+
+What limited the work was **trust** — and specifically, one human reading. Every
+pull request, every ledger entry, every claimed number. The apparatus in §4
+exists because implementation stopped being scarce and verification did not, and
+a practice built for the old scarcity will optimise the wrong thing.
+
+That inversion has consequences beyond a single project. Research training is
+organised around production: how to design an experiment, implement it, analyse
+it, write it up. The implicit model is that thinking is fast and building is
+slow, so rigour can be afforded because you are waiting for the build anyway.
+Reverse it — building becomes fast, checking stays slow — and rigour stops being
+something you have time for and becomes the thing you spend your time on.
+
+This is not a lament. It is arguably a better allocation. But it is not the one
+anyone was trained for, and a researcher who keeps optimising production will
+produce more than they can vouch for, faster, without ever noticing the moment
+it happened.
+
+## 8.2 Confident and wrong has no analogue in the tools we inherited
+
+Version control, testing, code review, continuous integration: every one of them
+was built for artifacts that **break loudly**. A test fails. A type does not
+check. A merge conflicts. The entire apparatus of software engineering is a set
+of amplifiers for a signal that already exists.
+
+A wrong belief emits no signal. It is internally consistent by construction —
+that is what makes it believable — and it passes every check because it is not a
+defect in an artifact but a defect in what someone concluded from one. The green
+badge and the wrong claim are perfectly compatible, and in this project they
+coexisted for ten experiments.
+
+We have almost no tools for this. Pre-registration is one, borrowed from
+clinical trials and psychology, and it is telling that the borrowing was
+necessary — computational research had not needed it while the cost of running
+an experiment was itself a check on how many you could run and how loosely you
+could interpret them.
+
+The practices in §4 are, I think, the beginning of an answer rather than an
+answer. Non-ratification, provenance on every carried figure, a falsifier for
+the specification and not just for the hypothesis: these are attempts to make a
+wrong belief *break loudly*, by manufacturing the signal that does not naturally
+exist. Whether they are the right attempts is exactly what a second project
+would find out.
+
+## 8.3 The unfashionable deliverable
+
+The most reusable thing this project produced is not the code, and not its one
+positive result. It is a ledger of thirteen foreclosed possibilities, each with
+the evidence that closed it, the conditions under which it holds, and the cost
+of reopening it.
+
+Someone building a system of this kind now knows several things not to try, and
+why. That is a different sort of result from a benchmark number, and I would
+argue a more durable one: a benchmark is superseded by the next method, while a
+well-founded elimination stays eliminated until someone changes the assumptions
+it rested on — which the ledger states, so they can check.
+
+It is also almost unpublishable. There is no venue for *here are thirteen things
+that do not work and the reasons*, no citation economy that rewards it, and no
+obvious way to present it at a conference. That is a fact about publishing
+rather than about the knowledge, and I want to state it plainly rather than
+pretend the incentives are aligned: **the better this method works, the more of
+its output takes a form the field does not reward.**
+
+Which raises a question I cannot answer from one project. If AI collaboration
+makes elimination cheap — and it does; most of these thirteen cost an afternoon
+each — then a great deal of the total knowledge produced will be of a kind we
+currently have no way to share. Either the practice changes to accommodate it,
+or the knowledge stays in repositories that nobody reads.
+
+## 8.4 That this report exists at all
+
+These notes were produced under the method they describe, by the collaboration
+they analyse, including their own claims-verification pass. The recursion is not
+decoration. It is either the strongest available evidence for the method or a
+closed loop, and I owe the reader a position on which.
+
+The honest answer is: **both, in different parts, and the boundary is
+checkable.**
+
+The parts that are evidence are the ones where the method's mechanisms fired
+against this document. The claims pass ran on the technical report and corrected
+eleven of eighteen claims. The repeated-figure check fired on the template's own
+front page. A specification about this work was rejected on a false premise
+about which surface held which file. Those are not testimonials; they are
+recorded events with commits attached, and a reader can go and look.
+
+The part that is a closed loop is the argument. §6's finding — that a rigorous
+project will not use its own informal lane — is my reading of my own behaviour,
+produced in conversation with the collaborator whose behaviour is also under
+examination. No mechanism checked it. It survived because it explained something
+that had happened twice and because I recognised it, which is exactly the
+standard §4.1 says is insufficient.
+
+So: trust the events, weigh the arguments. That is the same instruction the
+method gives about any of its own claims, and applying it here is not modesty
+but consistency.
+
+## 8.5 What I would want checked
+
+Three things a second project would settle that this one could not.
+
+**Whether the failures are the method's or mine.** One researcher. Every failure
+in §6 is consistent with *this is what happens to a rigorous project* and
+equally consistent with *this is what happens when this particular person is
+given a rigorous framework*. I lean toward the first because the low-ceremony
+lane went unused in two consecutive projects under different conditions, but two
+is not many.
+
+**Whether the mechanisms transfer or were shaped by the domain.**
+Pre-registration with a falsifier works cleanly when an experiment produces a
+number that can be compared against a threshold. Whether it works for research
+whose outputs are qualitative, or exploratory, or where the interesting result
+is a construction rather than a measurement, I do not know. I would guess the
+falsifier survives and the threshold does not.
+
+**Whether the record is readable by anyone but its author.** Nine thousand lines
+of ledger, written by one collaborator, read by one human, in five days. A
+colleague opening it cold is the test that has not been run, and until it is,
+*the repository is the memory* is a claim about a memory of one.
+
+## 8.6 How did it feel
+
+As stated at the beginning, this is a document written in collaboration between a researcher and an AI entity. The curious reader may be challenged to imagine how this collaboration actually took place—and perhaps even to identify who wrote which parts.
+
+This subsection, starting with its very title, is the only one for which our readership can be certain that it was written by a human—because a machine does not have feelings, right?
+
+So, let us rephrase the question that cries out for an answer: **“How did it feel to partner with an AI?”** (Which, by the way, I prefer to call an *entity*, not an *agent*.)
+
+Unfortunately, it is indescribable. Like jazz, you have to experience it to know. It is certainly different, and it feels new. It is here to stay, but each one of us, as humans, must find the answer in our own inner way.
+
+On the other hand, for endeavors such as the one described in this document, we can say that the collaboration is a kind of *contract* whose terms the two parties must learn to negotiate in order for things to work out. It is also a moving target, because everything is changing at a rapid pace and requires us to learn as we go.
+
+But since, as a researcher, I am naturally drawn to discovery as well as to the unknown, I can assure you that **“It Feels Good!”**
+
+## 8.7 What I would tell a student starting now
+
+For a scientist who is also a mentor and educator, this is perhaps the question of a lifetime. It has two inseparable sides: one seen from the perspective of the teacher, and the other from that of the student. Together, they reveal an intrinsic interdependence of purpose, learning, and aspiration.
+
+The present moment is changing not only how we learn, but also what is worth learning. The emphasis is gradually moving away from mechanical procedures and the memorization of rules—the kinds of tasks that machines can now perform remarkably well and with increasing ease. What becomes more important, then, are basic principles, fundamentals, genuine understanding, intuition, judgment, and creativity. And, of course, talent helps.
+
+In that spirit, the advice I would give is simple: “Discover the things you love and the things you are good at. Find where they meet, and pursue that path.”
+
+Perhaps education, in this new context, is less about preparing someone to follow a predefined path and more about helping them acquire the confidence and understanding needed to discover one of their own.
+
+## 8.8 Is this a good way to spend a career?
+
+To answer this question, we may first need to reconsider what we mean by a *career*. In this new context, perhaps the traditional idea of a career—a more or less predefined trajectory through a profession—will no longer be the most useful one.
+
+A career may increasingly become something that is continuously invented: a sequence of questions, discoveries, collaborations, changes of direction, and new beginnings.
+
+So, to close briefly, I would return to the advice of the previous subsection: discover what you care about, discover what you can contribute, and invent your own career around the intersection of the two.
+
+If you can do that, perhaps the question of whether it is a good way to spend a career becomes less important.
+
+It may simply become a good way to spend a lifetime.
+
+---
+
+# Appendix A. Availability
+
+Four public repositories. The first two are the predecessors these notes refer
+to; the third is the project the examples come from; the fourth is the template
+§7 describes.
+
+- **`visgraf/bioeye`** — a thin vertical slice through the framework, written
+ first. It closed the accumulation loop and not the perception–action loop.
+- **`visgraf/active-stereo`** — a full implementation with verified geometry and
+ a decision record. The loop never ran. Both the failure that motivated the
+ low-ceremony lane and the constitution clause that talked the reviewer out of
+ verifying are in this repository's history.
+- **`visgraf/bio-3d-vision`** — the project. Thirteen experiments, each with its
+ pre-registration, runner, raw results, verdicts and findings; two ledgers; a
+ technical report with its claims-verification pass; and a forward-only history
+ in which every correction appears as an amendment.
+- **`visgraf/math-ai-method`** — the template, instantiable.
+
+Every claim these notes make about what happened can be checked in the third.
+That is the point of having kept it.
diff --git a/report/figures/README.md b/report/figures/README.md
index 50dedd4..5175234 100644
--- a/report/figures/README.md
+++ b/report/figures/README.md
@@ -1,50 +1,69 @@
# report/figures/
-Empty. Generated figures land here and are **committed**, so the document builds
-from a clean checkout with a TeX installation alone and no language runtime.
+**Empty, and expected to stay that way.** This document has no graphics: every
+occurrence of the word "figure" in `report/draft.md` means *a numeral*, not a
+plot. That was checked, not assumed, and it is what decided `fc-002` — deleting
+`pyproject.toml`, whose one remaining justification would have been a figure
+generated from data.
-## Determinism, and its limit
+The directory is kept because `report/main.tex` sets `\graphicspath{{figures/}}`,
+and this file is kept because the rules below are the ones that come back with
+the first figure. They are not in force today. **Do not read them as describing a
+practice this repository performs.**
-**Two runs on one machine should be byte-identical.** Suppress the PDF
-`CreationDate`, pin any hash salt your plotting library uses for element ids, and
-select a non-interactive backend. That much is worth doing: it makes "did this
-figure change?" a question `git status` can answer.
+## If a figure is ever added, these come back with it
-**It does not extend across machines, and you must not build a check that
-assumes it does.**
+**Restore `pyproject.toml` first** (from the template at `81a638e`), having asked
+before adding the plotting dependency — `CLAUDE.md`'s working agreement, and the
+scope clause of `fc-002`. Restore the `.reference/` import guard from
+`tests/test_scaffold.py` with it, per `fc-003`: a figure script is the first
+executable code here, and it is the first thing that could read a pinned checkout
+from code instead of by eye.
-> *Measured, in the instance this template came from:* regenerating one figure on
-> Linux produced 103,146 bytes against the 110,709 committed from macOS — same
-> trajectory, same producer string, a font-embedding difference. Neither file was
-> wrong.
+**Commit the generated PDF.** That is what lets `.github/workflows/report.yml`
+build with a TeX installation alone and no language runtime, which is the
+property the whole report build currently rests on.
-**So a committed PDF is not a checksum.** Do not add a CI gate that diffs a
-committed figure's bytes: it would fail on the runner for a reason that has
-nothing to do with the figure, and a check that fails for the wrong reason is one
-people learn to override.
+**Do not add a CI gate that diffs a committed figure's bytes.** Two runs on one
+machine can be made byte-identical — suppress the PDF `CreationDate`, pin the
+plotting library's hash salt, select a non-interactive backend — and that much is
+worth doing, because it makes "did this figure change?" a question `git status`
+can answer.
-## The check that travels
+> *Inherited, from the template (`sha` unexpanded, see the `template` entry in
+> `docs/inherited-measurements.yaml`):* regenerating one figure on Linux produced
+> 103,146 bytes against the 110,709 committed from macOS — same trajectory, same
+> producer string, a font-embedding difference. Neither file was wrong.
+>
+> **This figure has not been re-measured here and never will be, since there is
+> no figure to regenerate.** It is a prior. If you add a figure and want the
+> determinism claim, measure it yourself and give it an `mn-` id.
-**Regenerate and compare the DRAWN QUANTITIES.** Every `make_*.py` in this
-directory should:
+Determinism does not survive crossing machines, so a byte-diff gate would fail on
+the runner for a reason that has nothing to do with the figure — and a check that
+fails for the wrong reason is one people learn to override. The same argument
+kept the repeated-claim check manual; see `fc-001`'s scope.
-1. **print the numbers it drew**, so a reviewer can read them without opening the
- PDF; and
-2. **assert them against the record** — the measurement ids in
- `docs/inherited-measurements.yaml`, read as constants at the top of the script
- with the id in a comment beside each.
+## The check that travels
-The second is the one that matters. A figure is a claim, and a figure whose
-numbers are recomputed at draw time and never checked is a claim that can drift
-away from the ledger it illustrates without anything noticing. The assertions
-are what make a figure fail.
+**Regenerate and compare the drawn quantities.** Every `make_*.py` should print
+the numbers it drew, and **assert them against `docs/inherited-measurements.yaml`
+by id**, read as constants at the top of the script with the id in a comment
+beside each. The second is the one that matters: a figure is a claim, and a
+figure whose numbers are recomputed at draw time and never checked can drift away
+from the ledger it illustrates without anything noticing.
> *Why this is not paranoia:* a figure is regenerated far more often than it is
> re-read, and it is re-read by people who already believe what it shows.
+**Note that this rule is the reason `docs/inherited-measurements.yaml` and a
+figure script are a package.** A figure asserting against ids is the only
+mechanism in the whole template that makes a drawing fail, and it needs the
+ledger that `fc-006` kept.
+
## Sizing
-Draw to **6.5 in wide**. That is `\textwidth` for `report/main.tex` — `article`
-at letterpaper with 1 in margins — so a figure included at `width=\textwidth` is
+Draw to **6.5 in wide** — `\textwidth` for `report/main.tex` (`article`,
+letterpaper, 1 in margins) — so a figure included at `width=\textwidth` is
reproduced 1:1 and its type is never rescaled. Set the font size once, in a
shared style module, rather than per script.
diff --git a/report/main.tex b/report/main.tex
index 4e4c3ce..a582792 100644
--- a/report/main.tex
+++ b/report/main.tex
@@ -15,8 +15,11 @@
% in the history side by side, so that the sentence which changed on its way into
% LaTeX is visible as a change rather than as a fact.
%
-% None ships here, deliberately -- an empty draft.md would be a file teaching
-% nothing. Create it with the first paragraph of real prose.
+% draft.md IS PRESENT AND IS THE PROSE OF RECORD. This .tex file is still the
+% scaffold: typesetting is step 4 in docs/state.yaml and is GATED ON THE
+% CLAIMS-VERIFICATION PASS (od-001) by the stopping rule. Do not typeset ahead of
+% the pass -- the whole value of the split is that claims are corrected while the
+% prose is still cheap to move.
%
% This is what made claim verification possible in the instance this template
% came from. Two things follow from it, and they are the reason it is a practice
@@ -58,16 +61,24 @@
\graphicspath{{figures/}}
-\title{TITLE}
+\title{Personal Notes on a Methodology for Research and Development\\
+ in Applied Computational Mathematics}
% AUTHORSHIP IS A CLAIM LIKE ANY OTHER. If an AI collaborator did work that
% appears here, say what it did and where, rather than leaving the reader to
% infer it. The instance this template came from named its collaborator on the
% byline with a footnote pointing at an appendix that described what each surface
% actually did. Adopt that, or decide otherwise deliberately -- but decide.
+% Taken from report/draft.md, which names the collaborator on the byline with a
+% footnote, and devotes section 8.4 to the recursion rather than disclaiming it.
+% That is the deliberate decision this comment block asks for.
\author{
- AUTHOR \\
- \normalsize AFFILIATION
+ Luiz Velho \\
+ \normalsize IMPA
+ \and
+ Claude \\
+ \normalsize Anthropic \\
+ \normalsize \textit{collaborator; see \S8.4}
}
\date{\today}
diff --git a/spikes/README.md b/spikes/README.md
deleted file mode 100644
index cc8671e..0000000
--- a/spikes/README.md
+++ /dev/null
@@ -1,72 +0,0 @@
-# spikes/
-
-Where you try things. This is the low-ceremony part of the repository, and the
-low ceremony is the point: an idea should be cheap to test and cheap to throw
-away.
-
-## The trigger
-
-**A question written about twice without being tested goes to a spike.**
-
-That is the rule. It is not "use spikes when they seem useful", because that is
-what the instance this template came from had, and it produced a directory that
-shipped on day one — with its rules, its enforcement test and its README — and
-was **never used once in five days**.
-
-> *The failure it is aimed at:* in the predecessor repository, a step was written
-> up twice and never started, accumulating five open questions, because writing a
-> document was the only sanctioned response to not knowing. `spikes/` was
-> invented to fix that. Adding the directory did not fix it. A trigger is the
-> fix.
-
-The trigger is written to be countable rather than to be wise. If a question has
-appeared in two documents — two specifications, a specification and a findings
-file, an open decision that has been restated, a paragraph of a report and the
-state file — and no code has been run against it, the next thing that happens is
-a spike. Not a third document.
-
-Note what the trigger measures: **not how hard the question is, and not how
-important.** Twice-written is a proxy for "we keep reasoning about this instead
-of looking", which is the actual failure, and it has the advantage of being
-visible from outside the head of the person doing it.
-
-## The rules
-
-**Git-tracked.** Spikes are committed. A spike that lives only on your disk
-cannot be pointed at, and the whole reason this directory exists is so that "I
-tried that, here is what happened" is a thing you can show.
-
-**No decision record. No findings entry. No definition of done.** None of them
-apply here. Do not write a foreclosure *about* a spike — a decision is recorded
-after the thing it decides has been tried, so the spike is what you do *before*
-the record, and the record comes after if the answer was worth keeping.
-
-**No tests required.** Write them if they help you; nothing checks.
-
-**Not importable.** Nothing in `src/` or `experiments/` may import from here.
-Enforced by `tests/test_scaffold.py`, which CI runs. If the library needs
-something a spike has, that is the signal to write it properly in `src/` — with
-tests — not to import it from here.
-> *Why the ban is enforced rather than stated:* a lane whose whole value is that
-> it has no rules stops having that value the moment something with rules
-> depends on it. The first import is the one that turns a scratch directory into
-> untested production code, and it never announces itself as that.
-
-**The only permitted output is a decision or a deletion.**
-
-- *A decision:* write what you learned into `docs/state.yaml` — an open decision
- resolved, a new one opened, or a foreclosure with its reason — and then delete
- the spike.
-- *A deletion:* it did not work, or it stopped being interesting. Delete it. The
- git history keeps it if anyone ever wants it.
-
-A spike that is neither — still sitting there, not deciding anything, not
-deleted — is the failure mode this directory has. It should go. Ask, and then
-delete it.
-
-## Naming
-
-`spikes//`. Name it after the question you are answering, not
-the technique you are using: `does-the-fixture-need-occlusion/`, not
-`numpy-warp-test/`. When the question is answered the directory's name tells you
-the spike is finished.
diff --git a/src/research/__init__.py b/src/research/__init__.py
deleted file mode 100644
index 94a5b3c..0000000
--- a/src/research/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-"""The library. RENAME THIS PACKAGE -- see pyproject.toml.
-
-Flat as shipped, with no opinion about structure, because structure is a decision
-and this template has not taken it for you. If you add subpackages, take that
-decision explicitly and record it in ``docs/state.yaml`` as a foreclosure with
-its rationale.
-
- A measured argument for staying flat, from the instance this template came
- from: its predecessor grew a directory per architectural layer, and a
- directory per layer invites filling it. The result was a library 42.1% of
- which was stimulus infrastructure, against 9.1% for the three layers the
- framework was actually named for. Every individual step had been reasonable.
-
-That is an argument, not a rule, and it is offered rather than enforced. What is
-NOT optional is CLAUDE.md's rule about where authority lives: units, frames,
-conventions and the meaning of a returned value are declared in the docstring of
-the function that returns them, never in a governance file.
-"""
-
-#: The public surface. Stated here so that a change to it has to pass through a
-#: diff; pinned sorted by tests/test_scaffold.py.
-__all__: list[str] = []
diff --git a/src/research/py.typed b/src/research/py.typed
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/test_scaffold.py b/tests/test_scaffold.py
deleted file mode 100644
index b3f540a..0000000
--- a/tests/test_scaffold.py
+++ /dev/null
@@ -1,138 +0,0 @@
-"""Scaffold guards. Infrastructure -- these pin the repository's shape, not any
-result about the subject matter.
-
-Two of the rules in CLAUDE.md are stated as enforced. This is where they are
-enforced, so that "CI enforces that" is a true sentence rather than an intention:
-
-* nothing in the library or in experiments may import from ``spikes/``;
-* nothing in the repository may reach into the reference checkouts.
-
-WHY THESE ARE TESTS AND NOT PARAGRAPHS. Both rules are stated in prose elsewhere,
-and prose has nothing to fail against. The spikes rule in particular is the one
-that makes the low-ceremony lane safe: a directory with no tests and no review is
-harmless until something with tests and review imports from it, and the first
-such import never announces itself as the moment the rule stopped holding.
-
-The instance this template came from shipped a third guard here, pinning a
-project-specific layout decision (a flat package, no per-layer subpackages, taken
-on a measured argument about that project's predecessor). It is NOT carried: the
-template has no such decision, and shipping the guard without the decision would
-present a rule as tested practice when nothing here has exercised it. If you take
-a structural decision, add its guard beside these -- that is the pattern worth
-carrying, not the particular decision.
-"""
-
-import ast
-import pathlib
-
-import pytest
-
-pytestmark = pytest.mark.infrastructure
-
-REPO = pathlib.Path(__file__).resolve().parents[1]
-
-# RENAME THIS to your package, and rename src/research/ with it. It is a single
-# constant on purpose: the guards below are generic, and this is the only line in
-# the file that knows what the project is called.
-PACKAGE = "research"
-
-# Built by concatenation rather than written literally, so that the scan below
-# can cover *this* file too instead of having to exempt its own needle.
-REFERENCE_DIR = "." + "reference"
-
-# Directories whose code must stay free of both.
-GOVERNED = ("src", "tests", "experiments")
-
-
-def _python_files(*roots: str) -> list[pathlib.Path]:
- out: list[pathlib.Path] = []
- for root in roots:
- base = REPO / root
- if base.is_dir():
- out.extend(sorted(base.rglob("*.py")))
- return out
-
-
-def test_package_imports() -> None:
- """The library imports and states its public surface.
-
- Thin on purpose while the package is empty -- but not skipped, because a
- skipped test is a test that cannot fail. As the package grows, assert the
- names it exports here: the point is that the public surface is stated
- somewhere a change has to pass through.
- """
- package = __import__(PACKAGE)
-
- assert hasattr(package, "__all__"), f"{PACKAGE}.__all__ states the public surface"
- assert sorted(package.__all__) == package.__all__, "__all__ is kept sorted"
-
-
-def test_governed_trees_do_not_import_spikes() -> None:
- """A spike's only permitted output is a decision or a deletion.
-
- Neither of those is "the library grew a dependency on it". Parsed rather than
- grepped, so a mention in a docstring or a comment does not trip it and a real
- import cannot hide behind formatting.
- """
- offenders: list[str] = []
- for path in _python_files(*GOVERNED):
- tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
- for node in ast.walk(tree):
- if isinstance(node, ast.Import):
- names = [alias.name for alias in node.names]
- elif isinstance(node, ast.ImportFrom):
- names = [node.module or ""]
- else:
- continue
- for name in names:
- if name == "spikes" or name.startswith("spikes."):
- offenders.append(f"{path.relative_to(REPO)}:{node.lineno} imports {name}")
- assert not offenders, "spikes/ must not be importable by the library:\n" + "\n".join(offenders)
-
-
-def test_nothing_reaches_into_the_reference_checkouts() -> None:
- """The references are read by people, never by code.
-
- They are pinned clones of other repositories, they are git-ignored, and they
- are not present in CI. Any code path that reads one passes locally and fails
- everywhere else, which is the worst available failure mode.
- """
- offenders: list[str] = []
- for path in _python_files(*GOVERNED):
- for lineno, line in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1):
- if REFERENCE_DIR in line and "REFERENCE_DIR" not in line:
- offenders.append(f"{path.relative_to(REPO)}:{lineno}")
- assert not offenders, "code must not reference the pinned clones:\n" + "\n".join(offenders)
-
-
-def test_every_experiment_has_a_preregistration() -> None:
- """A preregistration is written and committed BEFORE the runner exists.
-
- NOT CARRIED FROM THE SOURCE INSTANCE. That project wrote a preregistration
- first for all thirteen of its experiments and never enforced it in CI, so
- this guard is an addition and has not been exercised by anything. It is here
- because the practice was exercised heavily and the enforcement is cheap;
- delete it if it gets in the way, and record why.
-
- This guard cannot check the ORDER -- that is what the commit history is for,
- and experiments/exp000_example/README.md explains why the order is the whole
- point. What it can check is the weaker, still useful thing: that no
- experiment directory acquires a runner or results without one.
-
- A directory with results and no preregistration is the shape of an experiment
- whose criterion was chosen after the numbers were seen, and that is not a
- thing anyone does deliberately -- it is a thing that happens when a question
- turns into a run without passing through a written threshold.
- """
- root = REPO / "experiments"
- if not root.is_dir():
- pytest.skip("no experiments/ directory yet")
-
- offenders: list[str] = []
- for directory in sorted(p for p in root.iterdir() if p.is_dir()):
- if directory.name.startswith((".", "__")):
- continue
- has_work = any((directory / name).exists() for name in ("run.py", "results.json"))
- if has_work and not (directory / "preregistration.md").exists():
- offenders.append(directory.name)
- assert not offenders, "experiments with work and no preregistration:\n" + "\n".join(offenders)