Skip to content

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 own README.md, context.md, map/, tasks/ and log/, with the same node headers as the project, and shares the project's PROJECT.md, rules/ and citations/. Its nodes are part of the project graph: the project's map/graph.md draws them in a box of their own, edges may join them to project nodes, and brainstorm/map/ shows the brainstorm nodes alone. A brainstorm task is soft-private by default. No file in it is published unless the user adds brainstorm to include in publish/manifest.yaml; then the tasks whose header says privacy: public are exported and the others stay private. It is soft-private (see Privacy tiers): nothing outside brainstorm/ 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. The references check of opsci publish check refuses links to it; the exact rules are in Publishing and the filter. A hard-private brainstorm task is marked privacy: hard-private like any other.
  • docs/ holds the project's documentation. It is in the manifest's include list.
  • private-docs/ holds private notes. It is in the manifest's never list, so it is refused even if a broader include entry covers it. It is committed to the private repository and never exported. It is soft-private; a note that is hard-private is listed under hard_private: in the manifest. Side investigations, questions that need recorded work but do not drive the project, go in private-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's verifications/<vid>/ when it verifies the work of several tasks (in brainstorm/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 build warns about a verification task that is less private than what it verifies.
  • In the graphs. map/graph.md draws it as a card with a double border, with a dashed arrow labelled "verified by" from each node it checks; map/claims.md does the same for one that verifies a result. Node tables and the Notion Tasks database give its type as verification.
  • What it changes. Its findings are results in its own results/. A confirmed node may be set verification: verified with evidence: pointing there; a refuted node's status changes.

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 carry short_name; a plan header may also carry autonomy and hold_at; a result may carry kind and milestone; any node may carry artifacts, code, uses and tags. Any other key is an error, so a typo is caught.
  • verified means a stated check was run against a provenance record, and evidence points to both. Only the user sets human-verified. The open-science-project plugin refuses an agent's edit that sets it, and the publish check refuses a node whose verification: line was last changed in an agent's commit.
  • privacy decides whether the node may leave the private repository (see Privacy tiers). A task directory is exported only if its context.md says privacy: public. When privacy is absent, policy.default_privacy in the manifest applies; the template sets it to public. A header with the old field publish: is refused by opsci map build, with a message naming privacy.
  • After changing a header, run opsci map build. It writes map/graph.md (the project graph as an image, then a table of every node with what it depends on, each task linked to its map.md) and map/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 in depends_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 new creates it.
  • results/README.md: the milestone results of the project (milestone: true, and every result in results/).
  • 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) and private-docs/ are soft-private as directories. Hard-private material that is not inside a hard-private task is listed under hard_private: in publish/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:

  1. No secrets in the repository.
  2. Write for the public: everything may become public.
  3. Nothing reaches the public repository except through open-science-publish:publish, and only after the user approves the publish report.
  4. No site-specific details (absolute paths, user names, host names, accounts, partitions, emails) in tracked files.
  5. 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."