Work · Repository
adaptive-preregistration
KIT Adaptive Preregistration: shared agent protocols (preregistration, conversation logging) installed into every repo with the kit_ap CLI
- Python
- 2 posts
- 1 merged PR
Posts about it
- 2026-09-28 One question per study: laying out research across repositories Why the work here is split into studies, simulators and one shared research corpus, and scientific-research-scaffold, the small CLI that sets new repositories up in that shape and checks existing ones against it.
-
2026-09-24
Adaptive preregistration: predict first, then run the code
A protocol for simulations and research software in which the predictions, the pass criteria and the environment are fixed in git before anything a report will cite is allowed to run.
README
A protocol for running simulations and research software honestly: write the predictions and pass criteria down, tag them in git, then run the code. The plan can change between stages, but every change is dated, says whether the result was already known, and can’t turn a fail into a pass.
This repo holds the protocol and kit_ap, a small CLI that installs it into any git repository. Once
installed, the rules sit in the project itself, so a person, a script or a coding assistant (Claude Code,
Codex, Copilot, Cursor, Gemini) working there reads the same rules.
- The method: Adaptive preregistration: predict first, then run the code
- The protocol itself:
PREREG_PROTOCOL.md, with its references
Quick start
git clone https://github.com/polarizetech/adaptive-preregistration.git
cd /path/to/your/project
/path/to/adaptive-preregistration/bin/kit_ap init # core, prereg
/path/to/adaptive-preregistration/bin/kit_ap add experiment-pr-log # optional: branch/PR per experiment
After that the project carries its own copy of the CLI, so you don’t need the clone:
.agents/bin/kit_ap status # installed version, modules, up to date?
.agents/bin/kit_ap update # pull the latest kit and re-apply
.agents/bin/kit_ap add experiment-pr-log # add a module (dependencies come with it)
.agents/bin/kit_ap remove experiment-pr-log # remove one: its files, hooks and instructions
.agents/bin/kit_ap list # what modules exist
Requirements: Python 3.9+ and git, on macOS or Linux (Windows via WSL). No other dependencies. Some modules
need more (experiment-pr-log needs the GitHub CLI, gh); init prints those notes.
Modules
| Module | Default | What it does |
|---|---|---|
core |
always | Where the protocols live, how conflicts are handled, and a session-start check for updates. |
prereg |
✓ | A claim or an exploratory label, then predict first. Every unit (an app, sim, tool, calculator or dataset analysis; a folder with preregistrations/, or the repo itself) starts with a falsifiable claim, settled with the user in one question and kept in its SCOPE.toml with the evidence and decision behind each scientific feature. A unit the user marks exploratory is built from a short note instead, and gets its claim before its first experiment. Then PREREG.md, DEVIATIONS.md, RESULTS.md, the experiment registry and milestone tags, per unit. scope-status lists unscoped units and what’s open. Preregistration · Scoping |
experiment-pr-log |
One branch and draft PR per experiment, tag receipts posted to the PR, and an optional commit guard that refuses outputs before -prereg and edits to a frozen plan. Protocol |
|
tool-versioning |
Releasing a tool whose output other people’s research consumes: one version source mirrored in CITATION.cff (and TOOL.toml), annotated vX.Y.Z tags never moved, a release that changes an output bumps MINOR and names it under ### Outputs changed with the consumers to re-run, a consumers list, one-way dependencies, separately versioned extensions, and release-check for the mechanical parts. Installs alongside prereg. Protocol |
What gets installed
A project doesn’t link to this repo at runtime. kit_ap copies the protocols into the project and pins
the exact kit commit in a lockfile. Everything is ordinary committed files, so it works in cloud sandboxes,
offline, and for collaborators.
this repo (the kit) a project that uses it
───────────────────── ───────────────────────────────────────────
modules/core/ ──kit_ap init──▶ AGENTS.md managed block: always-on rules
modules/prereg/ CLAUDE.md "@AGENTS.md" (Claude Code import)
modules/experiment-pr-log/ ◀─kit_ap update─ .agents/ protocols/, tools/, bin/kit_ap, kit_ap.lock
.claude/settings.json, .github/hooks/ hooks, merged
| Tool | Reads the rules from | Hooks |
|---|---|---|
| Codex (CLI, IDE, cloud) | AGENTS.md |
none |
| GitHub Copilot (VS Code, coding agent) | AGENTS.md |
.github/hooks/kit_ap-*.json (VS Code agent hooks, preview) |
| Cursor | AGENTS.md |
none; the rules say what to run by hand |
| Claude Code | CLAUDE.md → @AGENTS.md |
.claude/settings.json |
| Gemini CLI | GEMINI.md by default; set context.fileName to ["AGENTS.md", "GEMINI.md"] in .gemini/settings.json |
none |
| People, and chat tools without repo access | AGENTS.md and .agents/protocols/ |
none |
The managed part of AGENTS.md sits between <!-- kit_ap:start --> and <!-- kit_ap:end -->. Anything
outside the markers is yours and is kept. If the markers are damaged, kit_ap stops rather than guess.
How changes are applied
- Validated before anything is written.
kit_apworks out the complete change first and stops, touching nothing, if.claude/settings.jsondoesn’t parse, the markers are damaged, or it would overwrite a file it doesn’t manage (use--forceto replace such files). The lockfile is written last. - Committed for you.
init,add,removeandupdatecommit the paths they changed, and only those. Other staged or unstaged work is left as it was, and gitignored paths are skipped.AGENTS.md,CLAUDE.mdand.claude/settings.jsonare committed whole, including any edits of yours outside the managed part. Pass--no-committo review first. - Pinned.
.agents/kit_ap.lockrecordssource,ref(a branch or a tag),commitand a sha256 of every managed file.addandremoveuse that pinned commit, so they never upgrade other modules as a side effect. - Drift is refused. If a managed file was edited in the project,
update,addandremovestop. Put the change in the kit, or use--forceto discard it. - Update checks. The
coremodule adds a session-start hook that runskit_ap check --hook. When the kit has moved on, the agent is told to mention it; it never updates on its own. The check is cached (6 hours; 30 minutes after a failure), times out after 5 seconds, and never fails the session.
Security model
kit_ap update downloads the kit from the source in the project’s lockfile and runs that kit’s own
installer, so changes to the installer ship with the protocols. That means updating executes code from that
source. update prints the source and the commit range before it applies, and warns when the source isn’t
this repository. Review any change to .agents/kit_ap.lock in a pull request the way you would review a
change to a dependency, and pin a tag (--ref v0.1.0) if you want updates to be deliberate.
Limits
- The protocol protects the record, not the idea: a well-preregistered experiment on a wrong model is still wrong.
- Most rules are instructions. Only the commit guard and the hooks enforce anything, and both can be bypassed
(
git commit --no-verify, tools without hooks). The record makes violations visible afterwards. - A local git history isn’t an independent timestamp. Archive
-preregtags (OSF, Zenodo, Software Heritage) when outsiders need to rely on the result; seePREREG_PROTOCOL.md§2.
Developing the kit
A module is a folder in modules/, laid out by convention:
modules/<name>/
module.json {"name", "title", "description", "default": bool, "requires": [...], "notes": [...]}
instructions.md added to the AGENTS.md block, in dependency order. Keep it short: rules + pointers.
hooks/claude.json {"hooks": {...}} merged into .claude/settings.json
hooks/copilot.json copied to .github/hooks/kit_ap-<name>.json
anything else copied into .agents/ at the same relative path, keeping the executable bit
(conventions: protocols/, tools/, githooks/, config/)
- Long-form protocols go in
protocols/;instructions.mdholds a pointer plus the rules that must be followed even if nothing else is read. Every line of it is read in every session of every project. - Hook commands must reference
.agents/bin/…or.agents/tools/…. That’s howkit_aprecognises its own hooks when it updates or removes them. - Tools must never break the session: warn and exit 0 on failure. Stdlib Python 3.9+ or bash only.
notesare printed once, when a module is first added.{repo}expands to the project path.- Two modules can’t ship the same file. For config several modules add to, use a
.d/folder.
python3 -m unittest discover tests # end-to-end tests against throwaway repos
bin/kit_ap init --force /tmp/scratch # try a working-tree install; the lock marks it `dirty`
See CONTRIBUTING.md for the rules on changes, and CHANGELOG.md for what changed between versions.
Citing and license
If you use or adapt the protocol, please cite it; CITATION.cff has the details (GitHub’s
“Cite this repository” button reads it). The protocol builds on published work listed in the
references of PREREG_PROTOCOL.md.
License: code under MIT, protocol documents under CC BY 4.0. See LICENSE.
Changelog
scope-status finds an app version at apps//
The scaffold is moving apps to apps/<slug>/vN/, one folder per app and one inside it per version (polarizetech/scientific-research-scaffold#2). The kit names that layout in one place: the default folder scope-status uses for a [[apps]] entry in STUDY.toml that has no path.
- An app entry is now looked for at
apps/<slug>/<version>, then at the earlierapps/<version>-<slug>. - Nothing else in the kit depends on where apps live. Preregistrations are found by their
preregistrations/folder wherever the unit is, andPREREG_PROTOCOL.mdalready says the layout is the organisation’s convention. - A project on the earlier layout behaves as before.
No release in this PR: the changelog entry is under “Unreleased”.
Tests: 51 pass; the manifest test now covers an app in each layout.
🤖 Generated with Claude Code