Project template and layout¶
Every project is copied from template/ in the framework repository. The layout is the same
in every project, so you, your collaborators and any agents you use find things in the same
place. This page lists what a new project contains and what each part is for.
Creating a project¶
Run opsci template instantiate yourself, then git init in the new directory:
opsci template instantiate <dir> --name <slug> --title "<title>" --author "<name>" --template <framework>/template
Or ask for open-science-project:new-project in Claude Code or Codex (see
Project skills). It asks for a directory, a short name, a title, your
name, and what the project is about, then runs the same command.
opsci template instantiate:
- refuses a destination that exists and is not empty, a name that is not lower case letters, digits and hyphens, and a title or author that is empty or holds a quote, backslash or newline;
- copies the template and fills its placeholders (
{{PROJECT_NAME}},{{PROJECT_TITLE}},{{AUTHOR}}, the dates, the framework repository and commit); - writes the first log entry, builds the map, and checks the result;
- leaves nothing behind if any step fails.
--no-context-management leaves out the lines about session jumps (for projects without the
open-science-context plugin) and records context_management: false. --date sets the
creation date and --framework-repo the repository URL to record. --framework-line adds
the README line "This project is run in the open with the open-science framework: ",
linking the framework's web page; the new-project skill passes it only when the user agrees.
opsci template check [ROOT] checks an existing project: every required file is present, no
placeholder or component marker is left, no text file holds an absolute path, and
.gitignore ignores data/, messages/, config/site.local.yaml and .env files while
keeping data/MANIFEST.yaml, messages/README.md and config/site.example.yaml.
The layout¶
| path | what | who edits |
|---|---|---|
README.md |
the public front page: one paragraph on the project and a table of where things are | user |
PROJECT.md |
what the project is about, in the user's words: the question, why it matters, approach, what counts as success, scope, key sources | user |
ABSTRACT.md |
the abstract of the project, in the user's words. TODO until the user writes it; once written, the project site's home page shows it with the write-up |
user; an agent only when asked |
WRITEUP.md |
a short page in the user's words: the main results and methods, or the current tasks and what the user is thinking about, with each result, method and task linked to its page. TODO until the user writes it; then it is the site's Write-up tab and is on the home page under the abstract |
user; an agent only when asked |
AGENTS.md |
instructions for every agent, loaded at the start of every session: the rules that are never broken, a summary of PROJECT.md, how to work, the layout |
user |
CLAUDE.md |
Claude Code's startup file: loads AGENTS.md, then lists the framework's skills and the dispatch tiers |
user |
context.md |
where the project stands now: goal, task table, in flight, next step, what waits on the user, open questions; at most 200 lines | main agent |
log/YYYY-MM.md |
the project log: one line per finished subtask, append only | anyone, append only |
map/README.md |
the logic of the project, hand-written; at most 150 lines | main agent, user |
map/graph.md, map/dead_ends.md, map/claims.md |
generated by opsci map build from the node headers; never edited by hand. claims.md is the claims graph (see Results and the claims graph) |
nobody |
results/ |
milestone results that combine several tasks, one <result-id>.md each; README.md is generated and lists every milestone result |
main agent |
tasks/<id>/ |
one directory per task: context.md (node header and state), plan.md, map.md (the task's graph of subtasks), results/ (the task's scientific results, one file each, and a generated README.md), log.md, subcontext/, working files; verifications/<vid>/ for verification tasks of its work |
that task's agents |
verifications/ |
verification tasks that check the work of several tasks, one <vid>/ each (see Verification tasks) |
that task's agents |
src/ |
shared code; never an output path | via worktree branches |
data/ |
git-ignored; data/<task-id>/ holds each task's large outputs; data/MANIFEST.yaml is tracked |
the producing task |
citations/ |
used.bib (works and software used, each with a usage field) and consulted.md (read but not used; soft-private, never exported) |
anyone |
rules/ |
the project's rules, one line each in README.md (R01, R02, ...), long ones in their own file |
user, agents |
contracts/ |
main.md (how the main agent works) and subagent.md (how dispatched agents work) |
user |
paper/ |
optional, laid out by the user | user |
lit_cache/ |
full texts of sources, for reading; never published | anyone |
archive/ |
retired material; nothing outside it may depend on it | main agent |
messages/ |
messages to the user from opsci notify; git-ignored except its README |
opsci notify |
config/ |
framework.yaml, codex.md (Codex's startup instructions, read after AGENTS.md), site.example.yaml (tracked) and site.local.yaml (git-ignored) |
user |
publish/ |
manifest.yaml (the allowlist), PRIVATE_POLICY.md, LAST_PUBLISHED, reports/ |
user, open-science-publish:publish |
.claude/ |
settings.json (permissions) and agents/ (the dispatch tiers) |
user |
.codex/ |
Codex dispatch tiers and, when Notion is enabled, its sync hook; startup guidance is in config/codex.md |
user |
brainstorm/ |
ideas before they become project work; a smaller copy of the layout; soft-private unless the user opts in | anyone |
docs/ |
project documentation; published | user, agents |
private-docs/ |
private notes and side investigations (investigations/); soft-private: committed to the private repository, never exported |
user, agents |
Small outputs go in tasks/<id>/; large data in data/<task-id>/. Names carry an ISO date
and the parameters that distinguish them. A new run gets a new name; nothing committed or
published is overwritten.
brainstorm/, docs/ and private-docs/¶
These three directories are part of layout version 2.
brainstorm/is for thinking before work starts. It has its ownREADME.md,context.md,map/,tasks/andlog/, with the same node headers as the project, and shares the project'sPROJECT.md,rules/andcitations/. Its nodes are part of the project graph: the project'smap/graph.mddraws them in a box of their own, edges may join them to project nodes, andbrainstorm/map/shows the brainstorm nodes alone. A brainstorm task issoft-privateby default. No file in it is published unless the user addsbrainstormtoincludeinpublish/manifest.yaml; then the tasks whose header saysprivacy: publicare exported and the others stay private. It is soft-private (see Privacy tiers): nothing outsidebrainstorm/may link to it (the generated map excepted), so the public project has no broken links, but a mention in passing is allowed. An idea becomes project work through a new project task that restates it, not a link to it. Thereferencescheck ofopsci publish checkrefuses links to it; the exact rules are in Publishing and the filter. A hard-private brainstorm task is markedprivacy: hard-privatelike any other.docs/holds the project's documentation. It is in the manifest'sincludelist.private-docs/holds private notes. It is in the manifest'sneverlist, so it is refused even if a broaderincludeentry covers it. It is committed to the private repository and never exported. It is soft-private; a note that is hard-private is listed underhard_private:in the manifest. Side investigations, questions that need recorded work but do not drive the project, go inprivate-docs/investigations/, one directory each (open-science-project:private-investigation).
config/framework.yaml records layout_version: 3. opsci warns when a project's layout
is older than the framework's; see Updating a project.
Verification tasks¶
A verification task audits, checks, reproduces or adversely reviews work that is already
done. It is an ordinary task, with the same files, whose header lists the nodes it checks in
verifies:. When the user asks for such a check, the agent makes a verification task
without asking; when it is unclear whether the request is a check or new work, it proposes
one and asks.
- Where it lives. In
tasks/<id>/verifications/<vid>/when everything it verifies belongs to task<id>; in the project'sverifications/<vid>/when it verifies the work of several tasks (inbrainstorm/verifications/for brainstorm work only).opsci task new <vid> --verifies <id>...chooses the place. - Privacy. By default the strictest privacy of the nodes it verifies. A verification task
inside a task is exported only if both are public.
opsci map buildwarns about a verification task that is less private than what it verifies. - In the graphs.
map/graph.mddraws it as a card with a double border, with a dashed arrow labelled "verified by" from each node it checks;map/claims.mddoes the same for one that verifies a result. Node tables and the Notion Tasks database give its type asverification. - What it changes. Its findings are results in its own
results/. A confirmed node may be setverification: verifiedwithevidence:pointing there; a refuted node'sstatuschanges.
Node headers¶
Every task, result, paper, site page and published dataset is a node in the project graph.
Its header is YAML front matter at the top of its main markdown file, or a node.yaml beside
a non-markdown artifact:
---
id: t07-mode-fit-v2 # unique; lower case, digits, hyphens; a task's id = its directory name
title: Mode fit with the corrected likelihood
short_name: mode-fit-v2 # optional; shown in session names as '<project> · <short_name>'
type: task # task | result | paper | page | dataset
status: active # active | done | failed | superseded | abandoned | paused
depends_on: [t03-noise-model]
supersedes: [t05-mode-fit-v1]
related: [t06-start-time-scan]
privacy: public # public | soft-private | hard-private (absent = policy.default_privacy)
summary: One sentence on what this node established, or why it failed.
verification: unverified # unverified | verified | human-verified
evidence: tasks/t07-mode-fit-v2/provenance.yaml # required unless unverified
---
A task's context.md and plan.md also show the header as a table under the title, between
opsci:node-table comment markers, so that it reads well in a Markdown viewer. opsci task new
writes it and opsci map build rewrites it from the front matter; never edit the table.
- Required:
id,title,type,status,summary. A task may carryshort_name; a plan header may also carryautonomyandhold_at; a result may carrykindandmilestone; any node may carryartifacts,code,usesandtags. Any other key is an error, so a typo is caught. verifiedmeans a stated check was run against a provenance record, andevidencepoints to both. Only the user setshuman-verified. Theopen-science-projectplugin refuses an agent's edit that sets it, and the publish check refuses a node whoseverification:line was last changed in an agent's commit.privacydecides whether the node may leave the private repository (see Privacy tiers). A task directory is exported only if itscontext.mdsaysprivacy: public. Whenprivacyis absent,policy.default_privacyin the manifest applies; the template sets it topublic. A header with the old fieldpublish:is refused byopsci map build, with a message namingprivacy.- After changing a header, run
opsci map build. It writesmap/graph.md(the project graph as an image, then a table of every node with what it depends on, each task linked to itsmap.md) andmap/dead_ends.md(every failed, superseded or abandoned node, with the reason), and the results pages and claims graph described next. It refuses bad headers, duplicate ids, edges to nodes that do not exist, and cycles independs_on; with an error it writes nothing.
Results and the claims graph¶
A result is a figure, a table, a value, a statement or a concept that the work established,
or an assumption it takes as given. An agent records something as a result when later work
will rely on it (another task, another result or the paper takes it as input or premise), or
when it answers part of the task's goal. Debugging findings (a bug fixed, a check passed, a
tuning run) go only in the task's map.md, unless they matter conceptually: they change what
the project believes or how its results must be read. The agent sets milestone: true when
a result obviously answers part of the project's question, and asks you when unsure. When a
result relies on outside work, the agent adds the work to citations/used.bib and its key to
the result's uses:; an assumption taken from outside is a result of its own, kind:
assumption, so the claims graph shows it as a premise.
Each result has its own file with a type: result header: tasks/<id>/results/<result-id>.md,
or results/<result-id>.md for a result that combines several tasks. The body shows and
describes the result; the header links it to everything else:
| field | what |
|---|---|
kind |
figure, table, value, statement, concept or assumption |
milestone |
true for a main result, one a paper would report |
artifacts |
where the result is stored: figures, tables, data files (paths from the project root) |
code |
the code that produced it |
depends_on |
the results, tasks and datasets it rests on |
uses |
keys in citations/used.bib: the papers, software and data from outside the project that it relies on |
supersedes |
the result it replaces |
opsci map build checks that every artifacts and code path exists (under data/, which
git ignores, a missing file is only a warning) and that every uses key is in
citations/used.bib, and writes:
tasks/<id>/results/README.md: the task's results, each with its figure, where it is stored, the code, what it rests on and what uses it; withdrawn results in a table below.opsci task newcreates it.results/README.md: the milestone results of the project (milestone: true, and every result inresults/).map/claims.md: the claims graph. An image of every result, in a box per task, with arrows from what it rests on (results, tasks, datasets) to what uses it (other results, papers), and the external works it uses in brackets on its card; then a table of every result with its statement, where it is stored, the code, its verification, and the weakest verification among the results it rests on (chain).
The claims graph follows changes at the next build. When a result, task or dataset fails or
is superseded, every live result that rests on it, directly or through other results, is
drawn with a red border, listed under "May no longer hold" in map/claims.md, flagged on
its results page, and reported as a warning by opsci map build. A verified result that
rests on unverified results is listed too. opsci map build also warns when a committed
artifact changed after its result file was last committed. Results in a results/
directory are drawn in the claims graph, not in map/graph.md.
Privacy tiers¶
Every node has a privacy tier, set by privacy: in its header
(opsci task new <id> --title "..." --privacy <tier>; the default is public):
| tier | meaning | examples |
|---|---|---|
public |
may be released; a public task's directory is exported | |
soft-private |
not released, but may be mentioned by name elsewhere; the public map names it without a link, grouped with others or reworded if its header gives too much away (Publishing) | private notes, brainstorm ideas, work too messy to release |
hard-private |
must not appear anywhere in the release, not even by name | proprietary data, unpublished ideas, collaborators' unpublished work, private information about people |
brainstorm/(unless the user publishes it) andprivate-docs/are soft-private as directories. Hard-private material that is not inside a hard-private task is listed underhard_private:inpublish/manifest.yaml.- A public task mentions soft-private material at most in passing, and never mentions hard-private material. A mention of soft-private material is plain text in backticks, not a link: a link to a file that is not exported is refused.
- A soft- or hard-private task keeps its work inside its task directory
tasks/<id>/, so it is easy to keep out. - Hard-private text that must stay in a published document, because removing it would break the text, can be redacted; the user decides each case (Publishing and the filter).
open-science-project:new-task gives a task public unless what the user described
obviously looks private; then it asks which tier, and the question gives the definitions of
both private tiers.
Configuration files¶
publish/manifest.yaml¶
The publish allowlist. Keys (any other key is refused):
| key | what |
|---|---|
include |
list of {path: ..., type: ...} entries. Only these paths can leave the repository. An entry with a type (result, paper, page, dataset) must be covered by a node of that type. Default: README.md, AGENTS.md, PROJECT.md, ABSTRACT.md, WRITEUP.md, CITATION.cff, LICENSE, LICENSE-docs, context.md, map, log, citations, rules, tasks, docs |
never |
paths or globs that are never exported, even if include covers them. Default: publish/PRIVATE_POLICY.md, lit_cache, data, config/site.local.yaml, private-docs |
policy.default_privacy |
the privacy tier of a node with none: public (template value), soft-private or hard-private |
policy.collaborators_agreed |
true once co-authors have agreed that shared work may be public; the publish check fails until it is set |
hard_private |
optional list of paths or globs of hard-private material that is not inside a hard-private task; never exported, and the export may not name it or copy its text |
public_repo |
URL or path of the public repository, used by opsci publish push |
site_url |
optional: the project site's URL when it is not the GitHub Pages URL of public_repo (a custom domain), or "" for no site. The public README.md links to it: the export adds The project site: <URL> under the title when the README lacks the link |
overrides |
optional list of finding kinds of the publish check that you accept instead of fixing: SLURM job numbers (leak kinds slurm-job-id, slurm-array-id, slurm-out-file) and quotations (copyright kinds long-quote, lit-cache-text). Each entry has check, kind, reason, date, and optional paths. See Overriding a finding |
status_exempt |
markdown files that need no status: header, added to the default list (READMEs, plot captions *.caption.md, AGENTS.md, CLAUDE.md, PROJECT.md, ABSTRACT.md, WRITEUP.md, context.md, log/, map/, citations/, rules/, task logs, task maps and subcontext/, docs/, and the same skeleton files under brainstorm/) |
Whatever the manifest says, publish/, lit_cache/, data/, messages/, .opsci/,
.env and config/site.local.yaml are never exported.
publish/PRIVATE_POLICY.md¶
Lists what must never become public: names of unpublished collaborations, data under agreements, internal project names. The regular expressions in its fenced block under "Patterns" are added to the leak scan, so the publish check refuses any exported file that matches one. The file itself is never exported.
config/framework.yaml¶
Where the project's scaffolding came from, read by open-science-project:update-from-template:
| key | what |
|---|---|
framework_repo |
the framework repository URL, or local copy |
copied_at_commit |
the framework commit the template was copied at |
copied_on |
the date |
context_management |
true if the project uses the open-science-context plugin |
layout_version |
the project layout version (2 added brainstorm/, docs/, private-docs/; 3 narrowed the git permissions and the literature tier's tools) |
local_divergence |
files the project changed on purpose; an update keeps these changes |
updates |
one entry per update: date, commit, what was taken, what was skipped |
config/site.local.yaml¶
Site settings, git-ignored: copy config/site.example.yaml and fill it in. Scripts read
these values, so nothing site-specific is written into tracked files.
scratch: <path to your scratch or bulk-storage directory>
batch:
scheduler: slurm # slurm | none
account: <allocation account>
partition: <partition>
modules: [] # module loads needed before a job runs
notify:
backend: file # file | slack
The values of scratch, batch.account, batch.partition and a list identifiers are also
added to the leak scan, so they cannot reach the public repository. A partition named by a
plain word (shared, gpu) is matched only where it names the partition
(--partition=shared, -p shared, partition: shared), since the bare word is ordinary
English. The notify: section is
described in Notifications.
.claude/¶
settings.json lets agents run, without a prompt, the git commands that change only the
local repository: add, commit, switch, checkout, branch, stash, mv, merge,
worktree add and worktree list. Claude Code runs read-only git commands (status,
diff, log, show) without a prompt anyway. It does not allow git as a whole: git -c,
git config and git fetch --upload-pack can run any program. Every other git command
asks first; git push asks even when a user setting allows it. Direct pushes to the public
repository (git push public, git push --mirror, any git -C .opsci/public command) are
denied; Publishing says what these rules do not stop.
agents/ defines the five dispatch tiers:
| tier | use for |
|---|---|
med-effort |
the default: implement a specified change, run a specified sweep, review a diff, analyse a result |
high-effort |
only after a med-effort attempt at the same task has provably failed, with the failure recorded |
low-effort |
fully specified mechanics with no decision left |
literature |
read a source and judge it; find which section supports a claim |
text |
mechanical work on text: find a string, extract a table, assemble a document |
Each tier file sets model: inherit; change it to the models you have. The literature
tier reads untrusted text (papers, web pages), so it has no shell: its tools are Read,
Grep, Glob, WebFetch, WebSearch and Write, and it writes only to lit_cache/. A
source it cannot fetch in full is reported, and the main agent downloads it.
.codex/¶
Codex has matching roles in .codex/agents/, expressed as TOML with the same contracts
and effort choices. The model is inherited by omitting an override. The literature role
runs with sandbox_mode = "workspace-write" and no network for its commands; Codex
reapplies a sandbox override given to the parent session, so that override wins. AGENTS.md directs
Codex to config/codex.md at startup. Plugin hooks adapt Codex patches to the project
checks, and the open-science-project plugin's hook guards direct pushes to the public
repository, as it does in Claude Code; Notion adds a project Stop hook. Review hooks with /hooks before relying on
them. Claude Code and Codex explains installation and permissions.
The rules every agent follows¶
AGENTS.md §0 lists the rules that are never broken:
- No secrets in the repository.
- Write for the public: everything may become public.
- Nothing reaches the public repository except through
open-science-publish:publish, and only after the user approves the publish report. - No site-specific details (absolute paths, user names, host names, accounts, partitions, emails) in tracked files.
- Only the user sets
verification: human-verified.
The project's own rules go in rules/README.md, one line each. The template starts with
seven (R01 to R07), for example "State the convention (units, sign, normalisation) before any
comparison."