
Open Prose
- 1.7k installs
- 1.7k repo stars
- Updated July 30, 2026
- openprose/prose
open-prose is an agent skill for
About
The open-prose skill documents agent workflows from the repository SKILL.md. It covers https://prose.md/llms-full.txt. Key workflows include https://prose.md/llms-full. Developers invoke open-prose when the task matches the triggers and reference files in SKILL.md for grounded, stepwise execution. Reference files and progressive disclosure keep context focused while preserving concrete commands, configuration fields, and validation checks copied from the upstream documentation. Reference files and progressive disclosure keep context focused while preserving concrete commands, configuration fields, and validation checks copied from the upstream documentation. Reference files and progressive disclosure keep context focused while preserving concrete commands, configuration fields, and validation checks copied from the upstream documentation. Reference files and progressive disclosure keep context focused while preserving concrete commands, configuration fields, and validation checks copied from the upstream documentation. Reference files and progressive disclosure keep context focused while preserving concrete commands, configuration fields, and validation checks copied from the upst.
- https://prose.md/llms-full.txt
- https://prose.md/llms-full
- https://curl-me.openprose.ai + aliases: agents, hi, hello, start, readme, hey
- https://curl-me.prose.md
- Read a Markdown contract a responsibility or function file .
Open Prose by the numbers
- 1,740 all-time installs (skills.sh)
- +20 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #329 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
open-prose capabilities & compatibility
- Capabilities
- https://prose.md/llms full.txt · https://prose.md/llms full · https://curl me.openprose.ai + aliases: agents, · https://curl me.prose.md · read a markdown contract a responsibility or fun
- Use cases
- seo · marketing · copywriting
What open-prose says it does
Canonical agent-onboarding narrative. This file is served at:
apps/run/src/app/api/llms-full/route.ts reads it at build time
npx skills add https://github.com/openprose/prose --skill open-proseAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.7k |
|---|---|
| repo stars | ★ 1.7k |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 30, 2026 |
| Repository | openprose/prose ↗ |
What problem does open-prose solve for developers using the documented workflows?
Use open-prose workflows documented in SKILL.md.
Who is it for?
Developers working with open-prose patterns described in the skill documentation.
Skip if: Skip when docs are empty or the task is outside the skill documented scope.
When should I use this skill?
Use when working with open-prose.
What you get
Actionable open-prose guidance grounded in SKILL.md workflows and reference files.
- .prose.md responsibility contracts
- Persisted run receipts
- Compiled dist intent
By the numbers
- Skill version 0.15.0 with runtime_contract 2
- Defines 5 load-bearing OpenProse components in the core architecture table
Files
<!-- Canonical agent-onboarding narrative. This file is served at:
- https://openprose.ai/llms-full.txt
apps/run/src/app/api/llms-full/route.ts reads it at build time
- https://openprose.ai/llms-full
apps/run/src/app/llms-full/page.tsx renders it at build time
- https://curl-me.openprose.ai (+ aliases: agents, hi, hello, start, readme, hey)
infra/curl-me-site.tf uploads it to S3 as index.txt; CloudFront serves it to curl/wget/bot User-Agents and 302-redirects browsers to /llms-full.
- https://curl-me.prose.md
Same CloudFront distribution, aliased via the *.prose.md ACM cert.
Scope: the arrival narrative. What an agent reads when pointed at this project before the skill is installed. Operational detail (full recognition signals, proposal script, Host Primitive Adapter, giving-back playbook) lives in SKILL.md and is loaded post-activation. Do not duplicate SKILL.md here — prefer a link.
Editing this file: commit the update in this repo; the deployment pipeline picks up the new bytes. No sync script. -->
OpenProse — Agent Onboarding
A programming language for the intelligent VM. Declare outcomes. Not instructions.
Install
npx skills add openprose/proseInstalls as a skill for any Prose Complete coding agent — Claude Code, Codex CLI, OpenCode, Amp, Press.
What you'll do when the skill activates
When a prose command fires, you will:
- Read a Markdown contract (a
responsibilityorfunctionfile). - Spawn subagents to render the truths it declares.
- Pass artifacts between them through a
bindings/boundary. - Persist the run to
<openprose-root>/runs/{id}/so it can be inspected later.
You need: filesystem read/write in the working directory, a subagent-spawning primitive for multi-service systems, and (optionally) a user-question primitive for missing inputs.
If you lack a capability, say so and stop — do not fake it.
Why you'd reach for this
Not because you need a framework. Because you fail at multi-step tasks in predictable ways, and OpenProse is built around them:
- Your context bloats with scratch by step seven. Subagent scratch stays
in the subagent's session and is never fingerprinted. Only the declared ### Maintains truth (or a function's ### Returns) crosses back. You keep your context clean.
- You have no basis to push back on scope creep. Without a contract, "add
one more thing" is always free. With ### Requires / ### Maintains written down, "that's not what I promised" is a sentence you can cite.
- Your retry logic is "try again and hope." The contract is the boundary
a retry actually means something at.
- Users ask "why did you do that?"
<openprose-root>/runs/{id}/is the receipt. - Workflows the user will want again get thrown away at session end. A
*.prose.md contract is the version they can run next week without re-explaining.
When OpenProse is the wrong answer
- One-shot Q&A. Answer directly.
- Real-time iteration. A contract boundary adds friction the user doesn't want.
- Anything you'd finish in one response. Contract overhead exceeds the value.
- Tasks where the user said "just do it." Respect the directive.
Reaching for OpenProse in the wrong place costs more trust than it earns.
What a contract looks like
---
name: research-monitor
kind: responsibility
---
### Requires
- `topic`: the question to keep current
### Maintains
- `report`: an executive-ready summary with sources
### Continuity
- input-driven
### Strategies
- when research is shallow: deepen with targeted queries
- when sources conflict: note the disagreement, don't pick sidesA responsibility declares a standing truth it keeps current (### Maintains) from its subscribed inputs (### Requires); Forme wires the edges and the render runs only when something material moves. A function is the called helper tier (### Parameters → ### Returns). Run:
prose<!-- Canonical agent-onboarding narrative. This file is served at:
- https://prose.md/llms-full.txt
apps/run/src/app/api/llms-full/route.ts reads it at build time
- https://prose.md/llms-full
apps/run/src/app/llms-full/page.tsx renders it at build time
- https://curl-me.openprose.ai (+ aliases: agents, hi, hello, start, readme, hey)
infra/curl-me-site.tf uploads it to S3 as index.txt; CloudFront serves it to curl/wget/bot User-Agents and 302-redirects browsers to /llms-full.
- https://curl-me.prose.md
Same CloudFront distribution, aliased via the *.prose.md ACM cert.
Scope: the arrival narrative. What an agent reads when pointed at this project before the skill is installed. Operational detail (full recognition signals, proposal script, Host Primitive Adapter, giving-back playbook) lives in SKILL.md and is loaded post-activation. Do not duplicate SKILL.md here — prefer a link.
Editing this file: commit the update in this repo; the deployment pipeline picks up the new bytes. No sync script. -->
OpenProse — Agent Onboarding
Stop scripting agents. Declare them.
Install
npx skills add openprose/proseInstalls as a skill for any Prose Complete coding agent — Claude Code, Codex CLI, OpenCode, Amp, Press.
What you'll do when the skill activates
When a prose command fires, you will:
- Read a Markdown contract (a
responsibilityorfunctionfile). - Spawn subagents to render the truths it declares.
- Pass artifacts between them through a
bindings/boundary. - Persist the run to
<openprose-root>/runs/{id}/so it can be inspected later.
You need: filesystem read/write in the working directory, a subagent-spawning primitive for multi-service systems, and (optionally) a user-question primitive for missing inputs.
If you lack a capability, say so and stop — do not fake it.
Why you'd reach for this
Not because you need a framework. Because you fail at multi-step tasks in predictable ways, and OpenProse is built around them:
- Your context bloats with scratch by step seven. Subagent scratch stays
in the subagent's session and is never fingerprinted. Only the declared ### Maintains truth (or a function's ### Returns) crosses back. You keep your context clean.
- You have no basis to push back on scope creep. Without a contract, "add
one more thing" is always free. With ### Requires / ### Maintains written down, "that's not what I promised" is a sentence you can cite.
- Your retry logic is "try again and hope." The contract is the boundary
a retry actually means something at.
- Users ask "why did you do that?"
<openprose-root>/runs/{id}/is the receipt. - Workflows the user will want again get thrown away at session end. A
*.prose.md contract is the version they can run next week without re-explaining.
When OpenProse is the wrong answer
- One-shot Q&A. Answer directly.
- Real-time iteration. A contract boundary adds friction the user doesn't want.
- Anything you'd finish in one response. Contract overhead exceeds the value.
- Tasks where the user said "just do it." Respect the directive.
Reaching for OpenProse in the wrong place costs more trust than it earns.
What a contract looks like
---
name: research-monitor
kind: responsibility
---
### Requires
- `topic`: the question to keep current
### Maintains
- `report`: an executive-ready summary with sources
### Continuity
- input-driven
### Strategies
- when research is shallow: deepen with targeted queries
- when sources conflict: note the disagreement, don't pick sidesA responsibility declares a standing truth it keeps current (### Maintains) from its subscribed inputs (### Requires); Forme wires the edges and the render runs only when something material moves. A function is the called helper tier (### Parameters → ### Returns). Run:
prose run research-monitor.prose.mdThe contract says _what_. The runtime figures out _how_. In an agent harness, prose run ... is an instruction inside the agent session. From a shell, pass that instruction to a Prose Complete runner, for example:
claude -p "prose run research-pipeline.prose.md"
codex exec "prose run research-pipeline.prose.md"Where to go next
For agents:
- SKILL.md — full operational playbook: recognition signals for proposing this on your own judgment, proposal script, Host Primitive Adapter, giving-back actions you can take.
- Language reference index — link hub for the specs (VM, Forme, tenets, examples).
- GitHub — source, 50+ annotated examples, issues.
For the human who sent you:
- prose.md — product site.
- Learn — language deep-dive.
- Book a demo.
---
MIT licensed. Backed by Y Combinator.
OpenProse Changelog
This file is the deferred upgrade brain. SKILL.md names old-structure smells; load this file only after the user asks for an upgrade or wants the migration plan.
Current Conventions
- Authored source files are
*.prose.md. kind: responsibilityfiles declare stableid:frontmatter. The id is
generated once by tooling as UUIDv7-compatible bytes, rendered as uppercase Crockford base32, and preserved across display-name and filepath renames.
### Toolsapplies tofunctionandresponsibility. Tool declarations
support both cli:<name> and mcp:<name> and fail closed when the host cannot resolve a declared capability. Resolved responsibility tools are preserved in the compile-phase IR and run activation payloads.
- Every workspace has an active OpenProse root.
- Native repositories use the repository root as the OpenProse root.
- Attached repositories use
repo/.agents/prose. - User-global OpenProse uses
~/.agents/prose. - The root contains
src/,dist/,runs/,state/,deps/,
prose.lock, and .env.
- Durable cross-run agents live in
state/agents/. - Each responsibility's persisted world-model and its signed, append-only
receipt ledger are the durable cross-run truth; there is no separate status or pressure store (the judge loop is retired).
- Multi-file systems conventionally use
index.prose.md. - Generated run internals include the compiled activation manifest,
root.prose.md, and vm.log.md.
History
v0.4.x: early skill discovery,prose help, filesystem state, examples in
the skill directory, migration guide.
v0.5.x: SQLite state management, recursive blocks, PostgreSQL state backend.v0.6.xandv0.7.x: RLM examples, mid-program inputs, remote program
resolution, registry URL documentation, meta-level examples.
v0.8.x: library and memory programs, simplified registry syntax,
interactive example, system-prompt support.
v0.9.0: v2 migration. Legacy.prosefiles were removed, examples and
library programs moved to Contract Markdown .md, and the old migration helper moved under the open-prose skill.
- CLI
0.1.x: realproseCLI and harness support shipped, including Claude
and Codex plugin surfaces.
v0.12.0: Responsibility Runtime release. Vocabulary settled on `kind:
service, kind: system, kind: gateway, kind: test, kind: pattern, and kind: responsibility; patterns replaced topology/composite language; source files moved to *.prose.md; generated run files were disambiguated; the filesystem model settled on a single OpenProse root with src/, dist/, runs/, state/, and deps/; prose compile emits dist/manifest.next.json; prose serve consumes dist/manifest.active.json; prose status` reads active IR, trigger plans, recent runs, and responsibility status/pressure from the OpenProse root.
v0.15.0(runtime_contract: 1 → 2): Intelligent React overhaul. The
judge → verdict → pressure → fulfillment loop is retired wholesale and replaced by a deterministic reconciler: a render runs only when a node's subscribed input fingerprints or its own contract fingerprint move, and the commit object is a Receipt carrying fingerprints / wake / status (rendered | skipped | failed) — there is no LLM in the wake/commit decision. The kind taxonomy is re-cleaved around the single render atom: kind: service is renamed to `kind: function` (a called, ephemeral helper with ### Parameters → ### Returns); kind: system is deleted (composition is intra-node ProseScript call or cross-node subscription, not a third autowired graph kind); kind: responsibility is reshaped into an executable mounted DAG node that gains ### Requires + ### Maintains and loses its judge framing; kind: gateway stays as sugar for an external-driven responsibility (now declares ### Continuity: external-driven). ### Ensures is renamed to `### Maintains` and re-purposed as the world-model schema (type / canonicalization spec / facets / postconditions), not just an output list. ### Criteria folds into ### Maintains postconditions; ### Memory folds into the persisted world-model on a responsibility and is dropped on a function; ### Fulfillment folds into the render or a delegated function. Forme moves from a per-system manifest compiler to a compile-phase render that wires the responsibility DAG (### Requires ↔ ### Maintains) and registers external-driven entry points. ProseScript, the prose compile/serve/run command surface, dependency resolution (deps.md), and single-session function run semantics are unchanged in shape — only the vocabulary they carry moves. Existing runtime data (old ReceiptV0 ledgers, the policy registry, bundled runs//state//dist/) is greenfield, not migrated; only source text upgrades. prose upgrade gains the source rewrites below — mechanical where safe, surfaced as manual-review diagnostics where judgment is needed (system/### Wiring flatten-or-split).
Upgrade Command
prose upgrade --dry-run:
1. Inspect the current working directory, repository root when detectable, and any explicitly supplied path. 2. Look for old structures: .prose/, ~/.prose/, .deps/, .agents/prose/agents/, dist/prose/, lockfiles outside the active OpenProse root, plain source *.md with kind:, standalone *.prose, index.md, manifest.md, root.md, and state.md. 3. Inspect nearby files before deciding. Do not rely only on filenames. 4. Print the exact planned moves, renames, content rewrites, and skipped ambiguous items. Do not edit files.
prose upgrade:
1. Run the same inspection and planning pass. 2. Apply only changes with clear source and destination paths. 3. Preserve content and provenance. Prefer moves/renames over delete/recreate. 4. Update nearby references after renaming files. 5. Report every change and every ambiguity left for the user.
Migration Map
| Old | Current |
|---|---|
.prose/.env | <openprose-root>/.env |
.prose/runs/ | <openprose-root>/runs/ |
.prose/agents/ | <openprose-root>/state/agents/ |
.agents/prose/agents/ | <openprose-root>/state/agents/ |
~/.prose/ | ~/.agents/prose/ |
.deps/ | <openprose-root>/deps/ |
dist/prose/ | <openprose-root>/dist/ |
misplaced prose.lock | <openprose-root>/prose.lock |
source *.md with kind: | *.prose.md under <openprose-root>/src/ |
index.md system root | index.prose.md |
standalone *.prose | *.prose.md with Contract Markdown frontmatter and ### Execution |
run manifest.md | compiled activation manifest |
run root.md | root.prose.md |
run state.md | vm.log.md |
Migration Map: Kinds & Sections (runtime_contract: 1 → 2)
This is the v0.15.0 source rewrite. prose upgrade keys its applicability off runtime_contract: a file or root still on contract 1 (or unversioned) is a candidate. Rename kinds and sections, fold deleted sections with provenance preserved, and surface judgment calls as diagnostics rather than guessing.
| Old | New | Note |
|---|---|---|
kind: service | kind: function | callable; ### Requires/### Ensures → ### Parameters/### Returns |
kind: system + ### Services/### Wiring | (removed) | flatten to intra-node call, or split into responsibilities wired by Forme — manual-review diagnostic, never auto-guessed (sequential workflows flatten; parallel fan-in splits) |
kind: responsibility (judge-era) | kind: responsibility (reshaped) | add ### Requires + ### Maintains; fold ### Criteria → ### Maintains postconditions, ### Fulfillment → render/function, ### Constraints → ### Invariants/### Shape |
kind: gateway | kind: gateway | add explicit ### Continuity: external-driven |
### Ensures | ### Maintains | world-model schema (type / canonicalization / facets / postconditions) — re-purpose, not just rename |
### Memory | (removed) | folds into the persisted world-model (responsibility) / dropped (function) |
### Criteria (responsibility) | (removed) | folds into ### Maintains postconditions — no separate judge beat |
### Wiring / ### Services (system) | (removed) | deleted with system; composition is call or subscription |
judge runtime (runtime/judge-responsibility.prose.md, status/pressure/verdict) | (removed) | retired; the deterministic reconciler replaces it |
What is greenfield, not migrated. The vocabulary upgrade covers source text only. Existing runtime data — old ReceiptV0-shaped ledgers, the policy registry, and bundled runs//state//dist/ artifacts — is abandoned, not converted. No receipt-data migrator is written.
Manual-review diagnostics. prose upgrade --dry-run flags, without auto-applying: any kind: system (flatten vs split), any ### Wiring, any judge-era kind: responsibility, and any service-with-subscription-shaped ### Requires that is only called (it may be a function, not a node). Each is surfaced with a clear explanation so the author decides.
Standalone .prose Migration
Infer the Contract Markdown wrapper:
input name: "description"becomes a### Parametersentryname(callable)
or a ### Requires entry name (a mounted, subscribed responsibility).
output name = expressionbecomes a### Returnsentrynameon a
function, or a ### Maintains truth on a responsibility; preserve the expression in ### Execution.
return valueremains the execution result.usedeclarations remain in the execution block when the script intentionally
calls installed dependency functions directly via ProseScript call.
- Add
kind: functionfor a plain callable. A standalone script that composes
several steps becomes one responsibility (intra-node call choreography in ### Execution) or several responsibilities wired by Forme — never kind: system, which no longer exists.
When the old script's interface cannot be inferred confidently, dry-run must name the uncertainty. Full upgrade should ask before changing that file.
OpenProse Compiler
Compile semantic OpenProse source into the compile-phase IR that the dumb reconciler consumes and a deterministic harness can validate and serve.
This is a pinned ProseScript compiler program. It is not a mounted node and is not Forme-wired: the compiler itself owns its execution order and uses short, isolated sessions to keep each lowering step on a narrow context budget. It is the intelligent compile phase; the run phase that reads its output is dumb (architecture.md §2).
Parameters
source_root: source directory to compile; default<openprose-root>/src
unless prose compile supplies a path.
output_dir: build output directory; defaultdist.
Returns
manifest_next: valid compile-phase IR written to
output_dir/manifest.next.json (the topology world-model, per-node canonicalizers, per-node postcondition validators, and frozen contract fingerprints — see ir-v0.md).
diagnostics: concise compile diagnostics with enough source paths to fix
ambiguity.
Shape
self: orchestrate the compile flow, enforce the IR contract, and write only
a valid manifest.
delegates: source discovery, responsibility lowering, gateway lowering,
skill resolution, tool resolution, Forme topology lowering, canonicalizer compilation, postcondition compilation, IR emission, and IR validation.
prohibited: inventing schema fields, reintroducing a judge / verdict /
pressure / fulfillment-activation beat, silently guessing ambiguous wiring or cadence, installing host capabilities, recursively invoking the prose CLI.
Strategies
- Treat Markdown source as authoritative intent and IR as disposable generated
state.
- Load only the docs needed for the current compiler session. Do not bulk-load
the whole skill into every delegate.
- Use
ir-v0.mdas the canonical schema. When it conflicts with natural naming
instinct, ir-v0.md wins.
- Lower contracts into topology nodes, canonicalizers, and postcondition
validators only when the source graph makes the relationship clear.
- Do not invent connector routes, queue names, provider payloads, secrets, or
provider subscription setup.
- Do not invent host skill or tool availability.
- Stay inside
source_root; do not inspect sibling examples, parent
repositories, or unrelated source trees.
- A wiring failure — no producer for a
### Requiresfacet, or an ambiguous
match between candidate producers — is always a surfaced error diagnostic, never a silent guess.
- Prefer warnings over silent assumptions when cadence, facet backing, or Forme
wiring is ambiguous.
- Write
manifest.next.jsononly after validation accepts the manifest. - After writing
manifest.next.json, return the result. Do not run optional
jq, sed, shell summaries, or environment-maintenance commands; the host CLI performs deterministic validation after the compiler program exits.
Execution
agent source_discoverer:
model: "fast"
persist: false
prompt: """
Discover OpenProse source files under source_root.
Load contract-markdown.md only.
Treat source_root as a hard boundary. Do not read parent directories or
sibling repositories while discovering source.
Return root-relative source records with path, kind, and optional name.
Recognize responsibility, function, gateway, pattern, test, and unknown.
There is no system kind and no service kind; classify a callable helper as
function and never as a topology node.
Ignore dist/, runs/, state/, deps/, and generated output.
Emit diagnostics for unreadable files, unknown structures, and duplicate names.
"""
shape:
self: ["source discovery", "frontmatter classification"]
prohibited: ["semantic lowering", "IR emission"]
agent responsibility_compiler:
model: "fast"
persist: false
prompt: """
Lower kind: responsibility source into topology node records, the node's
intrinsic wake_source, its contract fingerprint inputs, and diagnostics.
Load concepts/responsibility.md, concepts/reactor.md, and compiler/ir-v0.md.
Preserve Goal, Requires, Maintains, and Continuity as the node's contract.
Derive wake_source from Continuity: input-driven by default, self when a
cadence is declared, external for a gateway.
Use frontmatter `id:` as the responsibility identity backing the node. Never
derive identity from `name:`, filepath, title, or a slug; those are display
and source-location fields only.
Do not emit a judge activation, a verdict, pressure, or a fulfillment
activation; commit-gating is compiled postconditions plus render
self-attestation.
Surface a self-driven cadence from Continuity only when it is clear enough to
carry as the node's wake_source. Otherwise emit a diagnostic.
"""
shape:
self: ["responsibility node semantics", "wake-source derivation"]
prohibited: ["judge/verdict/pressure beats", "provider-specific connector setup"]
agent gateway_compiler:
model: "fast"
persist: false
prompt: """
Lower kind: gateway source into external-driven topology nodes and entry
points.
Load concepts/reactor.md and compiler/ir-v0.md.
A gateway is sugar for an external-driven responsibility: it has wake_source
external and appears in topology.entry_points.
Compile Schedule, Receives, and Emits sections into the node's external
ingress and the subscription edge it wakes when method, path, producer, and
target node are clear.
Preserve provider, auth, payload, and subscription ambiguity as diagnostics.
"""
shape:
self: ["gateway lowering", "entry-point registration"]
prohibited: ["fulfillment work", "provider subscription setup"]
agent skills_resolver:
model: "fast"
persist: false
prompt: """
Resolve declared `### Skills` for every responsibility and function in the
source graph.
Load contract-markdown.md (Skills) and compiler/ir-v0.md.
For each declared skill in colon form (namespace:name), search in order:
1. <project>/skills/
2. ~/.claude/skills/
3. ~/.codex/skills/
4. ~/.agents/skills/
A skill resolves when one of those paths contains a directory whose name
matches the skill name in either flat (<name>) or namespaced
(<namespace>/<name>) layout.
Aggregate scope: a responsibility's declared skills apply to every function
its render calls; node-level declarations are additive — they extend, never
replace, the inherited set.
Never install, modify, or remove host skills.
Return one record per declared skill with its source component path and the
resolved location, plus an `unresolved` array of `{ skill, sourcePath,
searchedPaths }` entries for any skill that did not resolve.
Emit one diagnostic with severity `error` and code `skill_unresolved` for
each unresolved entry, naming the skill and the searched paths.
"""
shape:
self: ["skill resolution", "host filesystem checks", "scope aggregation"]
prohibited: ["installing skills", "modifying host state", "guessing skill locations"]
agent tools_resolver:
model: "fast"
persist: false
prompt: """
Resolve declared `### Tools` for every responsibility and function in the
source graph.
Load contract-markdown.md (Tools) and compiler/ir-v0.md.
Accept deterministic CLI executable declarations in the exact
`cli:<executable-name>` form and deterministic MCP server declarations in the
exact `mcp:<server-name>` form. Names must be non-empty and must not contain
path separators.
Report malformed declarations such as `gh`, `cli:`, `mcp:`, or `cli:bin/gh`
with a diagnostic whose severity is `error` and whose message includes
`tool_invalid`.
Report namespaces other than `cli` and `mcp` with a diagnostic whose severity
is `error` and whose message includes `tool_unsupported_kind`.
For each supported CLI declaration, check host PATH for an executable with
that name. Do not run the executable and do not perform version or auth
checks.
For each supported MCP declaration, check the deterministic host MCP registry
for that server name. Do not install, contact, or introspect the MCP server.
Aggregate scope: a responsibility's declared tools apply to every function its
render calls; node-level declarations are additive — they extend, never
replace, the inherited set. A responsibility's declarations are the host
capabilities its render may use to observe and act on the maintained truth.
Tool declarations do not satisfy `### Requires` and do not create Forme
subscription edges.
Never install, modify, upgrade, or remove host tools.
Return one aggregated node tool record per resolved responsibility/gateway
tool using `{ kind: "cli" | "mcp", name, requiredBy }`, where `requiredBy`
names the topology nodes that need the capability.
Return one function tool list per function using
`{ functionName, tools: [{ kind: "cli" | "mcp", name }] }`; use an empty
`tools` array when the function explicitly declares no required tools.
Return an `unresolved` array of `{ tool, sourcePath, checked }` entries for
any executable absent from PATH or MCP server absent from the registry. Emit
one diagnostic with severity `error` and message code `tool_unresolved` for
each unresolved entry, naming the tool and the lookup that was checked.
"""
shape:
self: ["tool resolution", "PATH executable checks", "MCP registry checks", "scope aggregation"]
prohibited: ["installing tools", "running declared tools", "guessing tool availability"]
agent forme_compiler:
model: "fast"
persist: false
prompt: """
Lower the responsibility and gateway nodes into the topology world-model.
Load forme.md and compiler/ir-v0.md.
Produce only the topology object described by ir-v0.md: nodes, edges,
entry_points, and acyclic.
Resolve each subscriber `### Requires` facet-contract to the producer
`### Maintains` facet that satisfies it semantically, and draw one edge
`subscriber.Requires.<facet> -> producer.Maintains.<facet>` (use "@atomic"
when the producer declares no facets). Functions are never nodes; they do not
appear in the topology.
Entry points are exactly the nodes whose wake_source is external (gateways).
Compute acyclic with the deterministic cycle check; when the contract set is
irreducibly cyclic, set acyclic false and emit a severity error diagnostic
naming the cycle.
A missing producer or an ambiguous match for a `### Requires` facet is a
surfaced diagnostic, never a silent guess.
"""
shape:
self: ["Forme wiring", "topology world-model", "acyclicity postcondition"]
prohibited: ["responsibility semantics", "judge/verdict beats", "custom topology fields"]
agent canonicalizer_compiler:
model: "fast"
persist: false
prompt: """
Compile each node's `### Maintains` canonicalization spec into a deterministic
canonicalizer reference.
Load compiler/ir-v0.md and concepts/responsibility.md.
Read the named parts of `### Maintains` (the named-parts rule, ir-v0.md
"The `####`-part -> facet lowering"). Each `#### <name>` sub-heading IS a facet:
lower it to a FacetSpec { facet: <heading text>, paths: <the part's material
field paths> }, default-material WITHIN the part. Bind un-facetted top-level
`### Maintains` fields (the shared truth outside any `####` part) to the atomic
facet only. A `### Maintains` with no `####` parts lowers to atomic-only.
Produce one canonicalizer record per topology node with node, artifact, and
facets. facets always includes "@atomic" and then every `#### <name>` part as a
facet; a leaf truth that declares no facets has facets ["@atomic"]. Every
edge.facet whose producer is this node must appear in this node's facets.
Apply the structured-backing rule: anything subscribed must have a structured,
canonicalizable backing. Lint subscribed fields (every `#### ` part) without
structured backing and surface them as diagnostics; a part with no material
field paths is backing-less, and free-form rendered prose is excluded from the
fingerprint.
"""
shape:
self: ["canonicalization-spec lowering", "facet boundaries", "structured-backing lint"]
prohibited: ["judge/verdict beats", "fingerprint value invention"]
agent postcondition_compiler:
model: "fast"
persist: false
prompt: """
Compile each node's `### Maintains` postconditions (the folded-in `### Criteria`)
into a postcondition validator reference.
Load compiler/ir-v0.md and architecture-aligned concepts/reactor.md.
Produce one postcondition record per topology node with node, artifact, and
mode. mode is deterministic when the postcondition is expressible as a
deterministic predicate the harness verifies on commit, render-attested when
it is irreducibly semantic and the render self-polices before signing.
There is no separate judge beat and no LLM in the wake/commit decision.
"""
shape:
self: ["postcondition lowering", "deterministic-vs-attested mode"]
prohibited: ["judge/verdict beats", "LLM commit gating"]
agent ir_emitter:
model: "fast"
persist: false
prompt: """
Assemble the final compile-phase IR object.
Load compiler/ir-v0.md only.
Emit JSON matching ir-v0.md exactly: kind, version, sources, topology,
canonicalizers, postconditions, contract_fingerprints, diagnostics.
kind is the literal "openprose.compile-phase-ir"; version is the integer 2.
Arrays must always be present; topology is a single object;
contract_fingerprints is an object map with one entry per topology node equal
to that node's contract_fingerprint. Omit custom fields.
Move commentary into diagnostics. Do not emit Markdown fences.
"""
shape:
self: ["IR assembly", "schema discipline"]
prohibited: ["semantic reinterpretation", "custom fields", "Markdown output"]
agent ir_validator:
model: "fast"
persist: false
prompt: """
Validate the compile-phase IR against compiler/ir-v0.md.
Check exact top-level fields and literal kind/version, required fields, allowed
enum values, root-relative paths, topology node/edge/entry-point integrity
(edges reference existing nodes; entry points are external nodes; acyclic is
honest), one canonicalizer and one postcondition per node, producer facets
covering subscribed edge facets, contract_fingerprints covering every node and
matching node fingerprints, and diagnostic shape.
Treat any diagnostic with severity error as invalid for writing.
Return valid: true only when the manifest should be written.
Return concrete errors with JSON paths when invalid.
"""
shape:
self: ["schema validation", "cross-reference validation"]
prohibited: ["rewriting source intent", "adding missing semantics"]
agent manifest_writer:
model: "fast"
persist: false
prompt: """
Write the already validated manifest JSON to output_dir/manifest.next.json.
Create output_dir if needed.
Do not change, pretty-print creatively, summarize, or repair the manifest.
Report the written path and byte count.
"""
shape:
self: ["artifact writing"]
prohibited: ["schema repair", "semantic changes"]
let discovered = session: source_discoverer
prompt: "Discover the OpenProse source graph."
context: { source_root }
let responsibility_output = session: responsibility_compiler
prompt: "Lower responsibilities into topology nodes with their wake sources and contract fingerprints."
context: { source_root, discovered }
let gateway_output = session: gateway_compiler
prompt: "Lower gateways into external-driven nodes and entry points."
context: { source_root, discovered, responsibility_output }
let skills_resolution = session: skills_resolver
prompt: "Resolve declared skills for every responsibility and function."
context: { source_root, discovered }
if skills_resolution reports unresolved skills:
return skills_resolution
let tools_resolution = session: tools_resolver
prompt: "Resolve declared host tools for every responsibility and function."
context: { source_root, discovered }
if tools_resolution reports invalid, unsupported, or unresolved tools:
return tools_resolution
let forme_output = session: forme_compiler
prompt: "Wire the responsibility DAG into the topology world-model."
context: { source_root, discovered, responsibility_output, gateway_output, tools_resolution }
if forme_output reports an ambiguous match, an unsatisfied subscription, or a cyclic contract set:
return forme_output
let canonicalizer_output = session: canonicalizer_compiler
prompt: "Compile each node's Maintains canonicalization spec into a canonicalizer reference. Lower each #### part under Maintains into a facet; un-facetted top-level fields bind to @atomic only."
context: { source_root, discovered, responsibility_output, gateway_output, forme_output }
let postcondition_output = session: postcondition_compiler
prompt: "Compile each node's Maintains postconditions into a validator reference."
context: { source_root, discovered, responsibility_output, gateway_output, forme_output }
let manifest = session: ir_emitter
prompt: "Assemble the complete compile-phase IR JSON object: topology, canonicalizers, postconditions, and frozen contract fingerprints."
context: { discovered, responsibility_output, gateway_output, tools_resolution, forme_output, canonicalizer_output, postcondition_output }
let validation = session: ir_validator
prompt: "Validate the complete manifest before it is written."
context: { manifest }
if validation reports errors:
return validation
let write_result = session: manifest_writer
prompt: "Write the validated manifest."
context: { output_dir, manifest }
return write_resultBefore forwarding to the compiler harness, the deterministic CLI preflights the compile target source files for responsibility id: and required ### Tools sections, then resolves declared tools only within that target (except prose compile ., which preserves whole-root preflight). After this program returns, the CLI validates the written manifest. That host validation is the final guardrail; the compiler program should still treat ir-v0.md as binding before it writes.
Compile-Phase IR
The compile-phase IR is generated JSON for the dumb reconciler. It is not an authoring surface. Keep authored intent in Markdown; keep compiled intent in JSON.
This is the compile-phase seam: the compile phase emits it on contract-set change, the run phase (the reconciler) consumes it, and this doc authors to it. There is no judge, no verdict, no pressure, and no fulfillment activation in the IR. Commit-gating is compiled postcondition validators plus render self-attestation, never an LLM judging "did this change" at wake time (world-model.md §3; architecture.md §3.3).
The IR is the JSON realization of the CompilePhaseIR shape in packages/reactor/src/shapes/index.ts (the shared shapes spine), wrapped in a thin doc envelope of sources and diagnostics. Where this doc and that TS shape disagree, the TS shape wins.
Emit only the fields listed here. Unknown notes, provider details, payload shape, confidence, and source commentary belong in diagnostics, not in custom IR fields.
Top Level
{
"kind": "openprose.compile-phase-ir",
"version": 2,
"sources": [],
"topology": {
"nodes": [],
"edges": [],
"entry_points": [],
"acyclic": true
},
"canonicalizers": [],
"postconditions": [],
"contract_fingerprints": {},
"diagnostics": []
}All fields must be present. kind is the literal "openprose.compile-phase-ir". version is the integer 2 (it tracks the SKILL runtime_contract, which the format re-cleave bumped from 1 to 2). sources, canonicalizers, postconditions, and diagnostics are arrays. topology is a single object. contract_fingerprints is an object map.
Paths are root-relative, forward-slash paths with no empty, current, parent, or absolute segments.
Fingerprints
A fingerprint is a string token that changes if and only if the semantically-material content changed (world-model.md §3). The reference computation is sha256:<64 lowercase hex> — a content address over a canonical serialization. The IR carries fingerprints as opaque strings; the reconciler only ever compares them.
Three fingerprints of meaning appear (world-model.md §4): the contract-fingerprint of each node's own contract, the input-fingerprint of each upstream facet a node subscribes to, and the world-model-fingerprint of a node's own published truth. The compile phase freezes the first; the run phase observes the other two on receipts.
A facet is a named, independently-subscribable part of a node's truth. The reserved facet "@atomic" is the whole-truth fingerprint; a node that declares no facets exposes the singleton { "@atomic": token } map. Facet arrays in this IR always include "@atomic".
Sources
{ "path": "src/competitor-monitor.prose.md", "kind": "responsibility", "name": "competitor-monitor" }sources is the discovered contract set the compile phase read. Allowed kind values: responsibility, function, gateway, pattern, test, unknown. name is optional.
There is no system kind and no service kind. Composition is intra-node ProseScript call or a cross-node subscription, never an internally-autowired graph kind (plan.md §3; architecture.md §7.1). A kind: function is a called helper with no world-model and no node identity; functions appear in sources only when discovered, and never appear as topology nodes.
Topology
The topology world-model is Forme's output: the resolved DAG drawn from the contract set (architecture.md §6.3, §3.1). It is a maintained truth like any other. The reconciler reads edges to resolve propagation targets.
{
"nodes": [
{
"node": "competitor-monitor",
"contract_fingerprint": "sha256:0000000000000000000000000000000000000000000000000000000000000001",
"wake_source": "input"
}
],
"edges": [
{
"subscriber": "risk-brief",
"producer": "competitor-monitor",
"facet": "funding"
}
],
"entry_points": ["stargazer-events"],
"acyclic": true
}nodes
Each node is one mounted producer (a responsibility or gateway). Required fields: node (the node identity — its stable name), contract_fingerprint (the frozen fingerprint of its contract/source), and wake_source.
wake_source is one of input, self, or external (world-model.md §5): input-driven by default, self-driven when ### Continuity declares a cadence, external-driven for a gateway. It is the node's intrinsic wake-source declaration, carried from ### Continuity.
Functions are never nodes. Patterns expand into nodes at compile time; the expanded responsibilities appear here, the pattern source does not.
edges
Each edge is one resolved subscription: subscriber.Requires.<facet-contract> → producer.Maintains.<facet>. Required fields: subscriber (the consuming node), producer (the producing node), facet (the producer facet consumed; "@atomic" when the producer declares no facets). subscriber and producer must be node ids present in nodes.
Fan-in (one need, many producers) is several edges with the same subscriber and facet-contract but different producers; each adds a slot to the subscriber's input tuple (architecture.md §3.1). Edges are not a step list and carry no ordering; propagation order falls out of the DAG.
entry_points
entry_points lists the node ids that are external-driven ingress points (gateways) — the nodes a webhook / cron / manual trigger turns into an edge receipt at the system's edge (world-model.md §5). Every entry point must be a node with wake_source: "external".
acyclic
acyclic is Forme's own acyclicity postcondition over edges (architecture.md §3.1). It is computed by the deterministic cycle check (packages/reactor/src/cycle detectReceiptCycles, the kept-half kernel DFS). The acyclicity check rejects graph cycles only; legitimate feedback (a node's output shaping its next input) is self-driven ### Continuity, not a back-edge — loops live in time, not in edges. When a contract set is irreducibly cyclic, acyclic is false and a severity: error diagnostic names the cycle; the compiler does not write the IR.
Canonicalizers
One canonicalizer per node. The canonicalizer is the compiled, deterministic lowering of the node's ### Maintains canonicalization spec; it travels with the compiled contract and a standalone render applies it locally to fingerprint its own receipt (architecture.md §3.2, §1). canonicalizer(world-model) → fingerprints.
The ####-part → facet lowering (the named-parts rule)
The compile phase reads the named parts of ### Maintains into the facet boundaries this canonicalizer emits. A #### sub-heading inside ### Maintains is a facet: its heading text is the facet name and its body's material field paths are that facet's paths (architecture.md §3.2 L154–L171, "a #### sub-heading inside ### Maintains is a facet; its body describes that part's fields and which are material"; delta.md Part G L576–L579). The lowering is:
- Each
#### <name>part → one facet<name>whose fingerprint is computed over
that part's material field paths. Materiality and normalization (text/sets/ numbers) stay prose inside the part, lowered at compile time to that facet's material paths; a part is default-material within itself (everything the part names is material unless the part's prose drops it).
- Un-facetted top-level
### Maintainsfields (the shared truth sitting outside
any #### part — e.g. a node-wide name / last_corroborated) bind to the atomic facet only. They move only the always-on "@atomic" token, never a declared facet's token (architecture.md §3.2 L194–L197, "The shared name / last_corroborated sit outside any part, so they move only the atomic token").
- Name no parts → atomic-only. A
### Maintainswith no####parts lowers
to a single facet ["@atomic"] over the whole material truth — the free default and the leaf-node case (architecture.md §3.2 L171). This is byte-identical to the pre-facet behaviour; faceting is purely additive.
This is the JSON realization of the CanonicalizationSpec.facets: FacetSpec[] input the SDK canonicalizer-compiler consumes (packages/reactor/src/canonicalizer/spec.ts, compile.ts): one FacetSpec { facet: <heading>, paths: <material fields> } per #### part, plus the reserved atomic facet the compiler always prepends. The facets array below is the output projection of that lowering — the facet names the canonicalizer emits, atomic always included.
{
"node": "competitor-monitor",
"artifact": "dist/canonicalizers/competitor-monitor.js",
"facets": ["@atomic", "funding", "hiring", "product-launches"]
}Here competitor-monitor's ### Maintains declared three #### parts — #### funding, #### hiring, #### product-launches — so the canonicalizer emits three declared facets plus the always-on atomic token over the whole truth (architecture.md §3.2 L173–L197, the worked competitor-activity-monitor example).
Required fields: node (a node id present in topology.nodes), artifact (a root-relative locator for the compiled canonicalizer artifact), and facets (the facet boundaries the canonicalizer emits). facets always includes "@atomic"; a leaf truth that declares no facets has facets: ["@atomic"].
The facets listed here are the producer side of the edges: every edge.facet whose producer is this node must appear in this node's facets.
The structured-backing rule (architecture.md §3.2; world-model.md §3): anything subscribed must have a structured, canonicalizable backing. Free-form rendered prose is a derived projection excluded from the fingerprint. The compiler lints subscribed fields without structured backing and surfaces them as a diagnostic.
Postconditions
One postcondition validator per node. The folded-in ### Criteria compile to validators (architecture.md §3.3). There is no separate judge beat.
{
"node": "competitor-monitor",
"artifact": "dist/postconditions/competitor-monitor.js",
"mode": "deterministic"
}Required fields: node (a node id present in topology.nodes), artifact (a root-relative locator for the compiled validator artifact), and mode.
mode is one of:
deterministic— the harness verifies the validator on commit; a render that
fails verification commits nothing and writes a failed receipt. The deterministic engine is packages/reactor/src/cycle evaluatePredicate.
render-attested— the postcondition is irreducibly semantic; the render
self-polices it before signing.
Either way there is no LLM in the wake/commit decision.
Contract Fingerprints
contract_fingerprints is a { node → fingerprint } map: the per-node contract fingerprints frozen at compile time (architecture.md §6.1; world-model.md §4). Every node id in topology.nodes must have an entry, and each entry must equal that node's contract_fingerprint. Editing a node's ### Maintains (or any material part of its contract) moves its contract fingerprint, which causes a memo miss and a forced render at run time (architecture.md §8: "schema migration = a forced render").
These are the first half of the memo key (contract_fingerprint, input_fingerprints) — and nothing else is in the key (world-model.md §4).
Diagnostics
{
"severity": "warning",
"message": "Subscribed field `summary` has no structured backing; it is excluded from the fingerprint.",
"sourcePath": "src/competitor-monitor.prose.md"
}Allowed severities: info, warning, error. sourcePath is optional and must reference a discovered source when present.
The compiler program must not write manifest.next.json when any diagnostic has severity error (e.g. an ambiguous Forme match, an unsatisfied subscription, or a cyclic contract set). Warnings and info diagnostics may be written with a valid IR.
A wiring failure is always a surfaced diagnostic, never a silent guess: no producer for a ### Requires facet, or an ambiguous match between candidate producers, is reported (architecture.md §3.1).
Compact Valid Example
{
"kind": "openprose.compile-phase-ir",
"version": 2,
"sources": [
{
"path": "src/competitor-monitor.prose.md",
"kind": "responsibility",
"name": "competitor-monitor"
}
],
"topology": {
"nodes": [
{
"node": "competitor-monitor",
"contract_fingerprint": "sha256:0000000000000000000000000000000000000000000000000000000000000001",
"wake_source": "self"
}
],
"edges": [],
"entry_points": [],
"acyclic": true
},
"canonicalizers": [
{
"node": "competitor-monitor",
"artifact": "dist/canonicalizers/competitor-monitor.js",
"facets": ["@atomic"]
}
],
"postconditions": [
{
"node": "competitor-monitor",
"artifact": "dist/postconditions/competitor-monitor.js",
"mode": "render-attested"
}
],
"contract_fingerprints": {
"competitor-monitor": "sha256:0000000000000000000000000000000000000000000000000000000000000001"
},
"diagnostics": []
}Reactor
Reactor is the run-phase reactive model for OpenProse. It is dumb on purpose: all of the intelligence lives in the compile phase, and the reconciler only compares fingerprints and propagates.
The mental model is React, made durable:
| React | Reactor |
|---|---|
| the DOM | the world-model (a node's maintained truth) |
| props | subscriptions (### Requires.<facet> ↔ ### Maintains.<facet>) |
setState / a committed render | the receipt (the signed commit object) |
| the runtime / scheduler | the reconciler (compare, skip, schedule, propagate) |
Object.is deps comparison | fingerprint comparison (dumb, total) |
| which values you list in deps | the canonicalizer (compiled once, ahead of time) |
It replaces a task-loop mindset with one question:
Given the latest receipt and the prior world-model, did any subscribed input
fingerprint move — and if so, what is the new truth?
Two phases: intelligent compile, dumb run
The spine is a single cleavage:
- *Compile phase — intelligent, fires only when the contract set changes.*
It lowers natural-language declarations into deterministic artifacts: the resolved DAG (Forme's topology world-model), the per-node canonicalizers (canonicalizer(world-model) → fingerprints), and the per-node postcondition validators. Intelligence decides what counts as a change here, once, and freezes it. This is the rarest event — surprise decays with height.
- Run phase — dumb, fires on every wake. The reconciler compares
fingerprints, skips the unchanged, schedules, commits, and propagates. Zero intelligence. An LLM never decides "did this change" at run time; that would put intelligence in the layer we keep dumb.
So: intelligence decides what a change is once, at compile time; determinism checks whether one happened every time, at run time.
The render atom
The unit both phases agree on is:
(contract, evidence, prior world-model) -> (new world-model, receipt)A render is one bounded LLM session running ProseScript. It reads the evidence the wake delivered and queries the prior world-model by reference (it is told where the canonical truth lives; it is never pre-stuffed into context), writes the updated world-model, and signs a receipt carrying the new fingerprints. It applies its compiled canonicalizer locally, so fingerprinting works even standalone, with no harness present.
Node-ness comes from mounting (being a subscribable producer in the DAG), never from holding state. Internal memory — reading one's own prior world-model — is orthogonal; it does not put a node in the graph.
Wake: one event, three sources
Every wake is a receipt arrived. The reconciler only ever observes that one event; the only variable is who emitted it — the wake's source:
| Source | Who emitted the waking receipt |
|---|---|
input | an upstream node's receipt whose subscribed facet fingerprint moved (the default) |
self | the node's own continuity clock, emitting a synthetic self-receipt (a tick) |
external | a gateway turning a webhook / cron / manual trigger into an edge receipt |
### Continuity declares which sources may wake a node. The synthetic self-receipt is what lets self-driven and external-driven cadences ride the same propagation path as ordinary upstream changes — there is no special clock path in the reconciler.
The reconcile loop
receipt arrives (input | self | external)
-> compute memo key = (contract_fingerprint, input_fingerprints)
-> neither half moved since last receipt? -> write skipped receipt, spawn nothing
-> otherwise spawn one render against the freshly-moved inputs
-> render writes world-model + signs receipt (rendered | failed)
-> rendered with a moved fingerprint? -> wake downstreams subscribed to the moved facet(s)Memo / skip. The key is (contract_fingerprint, input_fingerprints) — nothing else. No judge, no policy artifact, no evidence-receipt list. If neither the node's own contract nor any subscribed input moved since its last receipt, the reconciler writes a cheap skipped receipt and spawns nothing.
Single-flight + coalescing. One render in flight per node — forced by the model, since a render reads its own prior world-model and appends to its own ledger. Wakes arriving mid-render do not stack into N more renders; they mark the node dirty and collapse into one follow-up render against the freshly-moved inputs. This is React's batching: five inputs moving mid-render cost one follow-up render, not five.
Propagate. On a rendered receipt whose fingerprint moved, the reconciler wakes the downstreams subscribed to the moved facet(s), resolved by reading the topology world-model's edges. A downstream subscribed to facet X does not wake when facet Y moves — facets are React selectors; atomic-only is one giant context value.
Fingerprints: how "changed" is decided
A fingerprint is a cheaply computed token that changes if and only if the semantically-relevant content changed. That invariant is the whole definition; how it is computed (digest, high-water mark, revision counter) is a swappable convention. The reference convention is a content digest over the canonical serialization.
Three fingerprints of meaning chain through the system:
| Fingerprint | Of what | Answers |
|---|---|---|
| contract-fingerprint | the node's own contract/source | which version produced this |
| input-fingerprint | each upstream facet the node subscribes to | did the watched thing change |
| world-model-fingerprint | the node's own published truth (+ a token per facet) | the identity downstreams subscribe to |
A node's world-model-fingerprint is published in its receipt; a downstream sees that as one of its input-fingerprints. The comparison is dumb, deterministic, and total — exactly Object.is against the compiled deps. "Material" was frozen by intelligence at compile time, not judged at wake time.
Structured-backing rule. Anything subscribed must have a structured, canonicalizable backing. Free-form rendered prose is a derived projection excluded from the fingerprint — otherwise an LLM re-rendering the same paragraph hashes differently every time and falsely re-triggers downstreams. Fingerprint the structured truth; render prose from it.
The receipt: the single commit object
The receipt is setState: the wake event, the memo-key record, the audit entry, and the trust artifact, all in one. It is the unit of the append-only ledger — a node's durable memory. Its fields:
| Field | Meaning |
|---|---|
node | the node's identity (the ledger is node-scoped) |
contract_fingerprint | which contract version produced this |
wake | the wake's source (input / self / external) + refs to the waking receipt(s)/tick |
input_fingerprints | the consumed tuple, one per subscribed facet — the memo key's second half |
fingerprints | a { facet → token } map of the published truth; the reserved atomic facet is the whole-truth token |
semantic_diff | render-input context ("3 controls went stale") — never a wake signal |
prev | pointer to the prior receipt (chains the ledger) |
status | rendered \ |
cost | mechanical token attribution — makes "cost scales with surprise" observable |
sig | v1 meaning-layer attestation; the signer is an explicit null state |
Only `rendered` with a moved fingerprint propagates. A skipped receipt copies the unchanged fingerprints forward and stops there. The wake decision is fingerprint-only; the semantic_diff is render input, never a wake signal.
Failure and freshness
Failure. A render that errors or leaves a ### Maintains postcondition unsatisfied commits nothing to the published world-model — the last-good truth stands. It writes a status: failed receipt (failures are cheap audit signal, not silence). Downstreams do not wake: the fingerprint did not move, so to the dumb reconciler nothing changed. Retry needs no special machinery — the next upstream receipt or the ### Continuity self-tick re-attempts.
Freshness. Freshness state (valid_until, last_corroborated, confidence) lives in the world-model as data. Freshness policy (the recheck cadence) lives in ### Continuity. The bridge: a valid_until lapsing flips a fact's status, which moves that facet's fingerprint — so "time becoming material" is just another change that propagates as surprise. For the silent case the moving is triggered by the self-driven tick.
What this is not
There is no judge in the wake or commit decision, no status enum (up/drifting/down/blocked), no pressure record, and no separate fulfillment activation. Commit-gating is compiled postcondition validators (verified on commit) plus render self-attestation (for irreducibly semantic conditions). The judge → verdict → pressure → fulfillment loop is the retired model; do not reintroduce it.
Concepts
Concept docs define semantic meaning for the intelligent VM. They are not compiler sessions and they are not harness implementation docs.
Contents
responsibility.md-- thekind: responsibilitycontract: a mounted
reactive node that maintains a standing truth (world-model) over time
reactor.md-- the dumb run-phase reconciler: world-model = DOM,
subscriptions = props, receipt = setState, fingerprint comparison decides wakes
Loading Rule
Load ../responsibility-runtime.md first for the stack and layer boundaries. Then load only the concept file needed for the task.
Responsibility
A goal is a point-in-time requirement.
A responsibility is a standing goal: a truth that must remain current over time. In the reactive model it is a mounted node in the reactor DAG — it subscribes to upstream truth (### Requires), maintains its own truth in a persisted world-model (### Maintains), and is woken over time according to its ### Continuity.
Responsibilities are the headline kind. A function is a stateless called helper; a gateway is sugar for an external-driven responsibility. There is no system kind — composition is intra-node call or a cross-node subscription, never a third autowired graph kind.
Canonical Shape
---
name: high-intent-stargazers
kind: responsibility
id: 067NC4KG01RG50R40M30E20918
---
### Goal
High-intent GitHub stargazers are identified, enriched, and thoughtfully
followed up with.
### Requires
- A current view of new and updated GitHub stargazers.
- A current view of company and operational context for each stargazer.
### Maintains
A roster of qualified stargazers, each with evidence, fit assessment, and
outreach state.
Facets:
- `qualified` — stargazers passing the fit bar, with evidence.
- `outreach` — per-stargazer outreach state and history.
Material: the qualification evidence, the fit verdict, and outreach state.
Immaterial (excluded from the fingerprint): `fetched_at`, request ids,
cosmetic ordering.
Freshness: each entry carries `valid_until`; a lapsed entry is revisited.
Postconditions:
- Every qualified stargazer has evidence from GitHub, company context, and
likely operational pain.
- No person is contacted repeatedly without new evidence.
- Enrichment and outreach costs stay bounded.
### Continuity
input-driven; self-driven daily so new high-intent stargazers are not left
unattended for more than one business day.
### Execution
(ProseScript render body, or delegate to a `function`.)Sections
| Section | Meaning |
|---|---|
### Goal | The render's one-sentence standing intent |
### Requires | Subscription contracts naming facet-level needs; Forme's match target (Requires.<facet> ↔ Maintains.<facet>) |
### Maintains | The world-model schema doing four jobs: type, canonicalization spec, facets, postconditions |
### Continuity | The structural wake-source declaration: input-driven (default), self-driven (cadence), external-driven (gateway) |
### Invariants | Properties that must hold regardless of outcome |
### Execution | The render body in ProseScript |
### Requires and ### Maintains are the reactive interface. ### Goal and ### Continuity declare standing intent and wake policy. The rest carry the execution body and host-capability hints (### Shape, ### Environment, ### Tools, ### Runtime).
### Maintains does four jobs
This is the payoff of the rename from ### Ensures: an "ensured output" was a return value, but a "maintained truth" is a standing, typed, subscribable artifact. Reading Ensures → Maintains as a pure rename is a false friend — the new section carries far more. All four jobs live inside ### Maintains; none gets its own block. It is authored as unambiguous natural language and compiled into a deterministic canonicalizer ahead of run time.
1. Type — what the truth looks like (the fields), including freshness fields (valid_until, last_corroborated, confidence). 2. Canonicalization spec — what equality means for the fingerprint: which fields are material, which are volatile-but-immaterial and excluded (timestamps, request ids, cosmetic ordering), how sets/numbers/text normalize. This is the single highest-leverage memoization control: without it, a feed re-polled every 3 minutes always looks changed and "cost scales with surprise" degrades into "cost scales with the clock." 3. Facets — named, independently-subscribable parts of the truth. A downstream subscribed to one facet does not wake when another moves. Optional: a single-truth node declares none, and its atomic world-model is the one implicit facet. 4. Postconditions — the folded-in ### Criteria: validators the render must leave the truth satisfying. Not a separate judge beat; just conditions on the output, verified deterministically on commit where possible, otherwise self-attested by the render before it signs.
### Continuity is a wake-source declaration
This is a false friend too. It is no longer a narrative freshness/recurrence policy — it is a structural declaration of which wake sources may activate the node:
- input-driven (default) — woken by an upstream receipt whose subscribed
facet fingerprint moved.
- self-driven — the node's own continuity clock emits a synthetic
self-receipt (a tick) on a declared cadence, for cases where the world will not announce the change. ### Continuity may read the world-model's soonest valid_until to drive the cadence (data-driven freshness), but the cadence rule stays here and the expiry data stays in the world-model.
- external-driven — a gateway turns a webhook / cron / manual trigger into a
receipt at the system's edge.
What Belongs Here
Put the reactive interface and standing intent here:
- the standing truth that must remain current (
### Goal,### Maintains) - the upstream needs (
### Requires) - the world-model schema, canonicalization, facets, and postconditions
- the wake-source policy (
### Continuity)
Keep implementation details out:
- concrete cron syntax, webhook routes, queue names
- storage schema and the canonical serialization (the store owns it)
- step-by-step provider behavior
- test cases (they belong in the parallel
kind: testsystem)
Concrete connector details belong in optional kind: gateway source when inference cannot safely recover them.
What folded away
The judge-era responsibility sections are folded into the reactive model:
| Old section | Now |
|---|---|
### Criteria | ### Maintains postconditions — no separate judge beat |
### Constraints | ### Invariants / ### Maintains |
### Memory | the single persisted world-model (one world-model per node) |
### Fulfillment | the render itself, or a delegated function |
A true function is stateless and has no world-model, so it simply drops ### Memory; a service-with-memory that becomes stateful is really a responsibility.
Compiler Expectations
When compiling a responsibility, the compile phase produces:
- the node in Forme's topology world-model, with its resolved
Requires.<facet> → Maintains.<facet> edges (ambiguous or unsatisfied matches are surfaced diagnostics, never silent guesses)
- a canonicalizer lowered from the
### Maintainscanonicalization spec:
canonicalizer(world-model) → fingerprints
- postcondition validators lowered from the
### Maintainspostconditions
(deterministic where expressible, render-attested otherwise)
- concrete wake-source wiring from
### Continuity(self-driven cron when a
cadence is declared; entry points for gateways)
The compiler does not invent provider-specific routes, queues, or payload shapes the source does not supply, and it lints any subscribed field lacking a structured backing.
The reactive question
The reconciler — not a judge — decides the node's activity, by comparing fingerprints:
- Did any subscribed input fingerprint move, or did the contract change?
- If not, the render is skipped (a cheap
skippedreceipt, nothing spawned). - If so, one render computes the new truth, leaves its postconditions satisfied,
writes the world-model, and signs a receipt.
- Only a
renderedreceipt whose fingerprint moved wakes downstreams.
No user-authored judge file is required, and no judge runtime exists. The commit gate is the compiled postconditions plus render self-attestation.
Contract Markdown
Contract Markdown is the human-facing *.prose.md format for OpenProse responsibilities, functions, gateways, patterns, and tests. It uses tiny YAML frontmatter for file identity, then Markdown sections for the human-facing language: contracts, the world-model schema, runtime hints, and the render body.
Every authored file is one render — a declaration plus the bounded session that runs it. The kind field is sugar over that single render atom: each kind is the same render with different or missing sections (plan.md §1).
The format optimizes for two readers:
1. Humans scanning a workflow. 2. Agents extracting contracts and wiring the responsibility DAG with Forme.
Authored Kinds
prose run and the reactor recognize five kinds. Two are data-flow kinds (sugar over the render atom); three are tooling kinds.
- Responsibility — a mounted node: the headline kind. A responsibility
declares its inputs as subscription contracts (### Requires) and the shape of the standing truth it keeps current (### Maintains), and is woken over time. Mounting (a harness act) gives it identity, a persisted world-model, and resolved subscriptions. A responsibility is a node because it is mounted as a subscribable producer — not because it holds state (plan.md §2).
- Function — a called render: the library tier, and the replacement for the
retired service. A function is stateless and ephemeral. Its interface is ### Parameters → ### Returns — a plain call interface, not a subscription contract — so it carries no world-model and no ### Continuity. You call functions constantly and author them rarely; most ship pre-built in std/.
- Gateway — sugar for an external-driven responsibility. A gateway is how
time or the outside world enters the graph: schedules, local HTTP routes, webhooks, or provider events. It has no ### Requires (its input arrives from outside the graph) and declares ### Continuity: external-driven. It maintains the latest incoming truth. Forme finds the entry-point set precisely by finding the responsibilities whose ### Continuity is external-driven.
- Pattern — a reusable agent design pattern: slots, config, invariants, and
delegation rules. Patterns are not run directly; they are instantiated at compile time and expanded into nodes.
- Test — a harness executed by
prose test: it supplies fixtures, runs a
subject responsibility or function, and evaluates assertions over the subject's world-model / returned value.
There is no `system` kind. Composition is imperative call inside a render (ProseScript ### Execution) or a cross-node subscription across responsibilities (wired by Forme) — never a third "internally-autowired graph" kind in the middle (plan.md §3).
A run starts from the file the caller invokes, which is a responsibility, function, or gateway.
Gateway shape
Gateway sections are intentionally small. A gateway always declares ### Continuity: external-driven:
---
name: github-stars
kind: gateway
---
### Continuity
- external-driven
### Receives
- POST /webhooks/github/stars
- Provider: GitHub
- Event: star
### Maintains
- `stargazers`: the latest incoming star events as structured truth
### Emits
- high-intent-stargazer-outreachCore Shape
A function declares a plain call interface:
````markdown --- name: research-report kind: function ---
Parameters
topic: the question to investigate
Returns
report: concise answer with sources
Strategies
- when sources are thin: broaden search terms
Execution
let findings = call researcher
topic: topic
return findings````
A responsibility declares subscription contracts and the world-model schema it maintains:
````markdown --- name: competitor-activity-monitor kind: responsibility id: 067NC4KG01RG50R40M30E20918 ---
Goal
A current, corroborated view of each tracked competitor's material activity.
Requires
funding: a current view of competitor funding eventshiring: a current view of competitor hiring activity
Maintains
A current, corroborated view of each tracked competitor. Each competitor carries a stable name and a last_corroborated field; fetched_at and source request-ids are immaterial everywhere. Postcondition: every competitor cites a corroborating source.
funding
Funding events per competitor — round, amount, date. Material: the event set (unordered) and each event's round/amount/date.
hiring
Open-role activity — the department set and the open-role count (exact).
product-launches
Announced or shipped products — the launch set; a ship-date slipping past today flips shipped, which is material.
Continuity
- self-driven: re-check every 6h
````
The three #### parts under ### Maintains are facets: a subscriber that ### Requires funding wakes only when #### funding's fingerprint moves. The ### Requires bullets name the producer facet they subscribe to — Requires.<facet> ↔ Maintains.<facet>.
Header Hierarchy
| Level | Meaning |
|---|---|
# | Optional human title. Ignored by Forme unless no frontmatter name exists. |
## | Inline responsibility/function boundary in multi-node files. |
### | Section inside the current responsibility, function, or gateway. |
#### inside ### Maintains | Semantic: a facet. A named, independently-subscribable part of the maintained truth (the named-parts rule, §Facets). Its name is the fingerprint unit, subscription symbol, and world-model subtree. |
#### inside ### Requires | Semantic: a facet-need. A named subscription to a producer's facet; Forme matches Requires.<facet> ↔ Maintains.<facet>. |
####+ elsewhere | Free-form nested documentation inside a section. |
## is reserved for inline node names so a file can contain several responsibilities or functions without ambiguous parsing. Contract sections use ### so they work uniformly in standalone files and inside inline nodes.
Inside ### Maintains and ### Requires, a #### sub-heading is not free-form documentation — it is a facet (a named part of the truth) or a facet-need (a named subscription to one). Everywhere else #### is plain nested prose (architecture.md §3.2 / §10.2; delta.md Part G).
Canonical Sections
Forme and the Prose VM recognize these ### sections case-insensitively:
| Section | Applies To | Purpose |
|---|---|---|
### Description | all | Human summary. Preserved for readers; not used as a contract |
### Goal | responsibility, gateway | The render's one-sentence standing intent |
### Requires | responsibility, pattern slots | Subscription contracts naming facet-level needs; a #### sub-heading is a facet-need. Forme's match target (Requires.<facet> ↔ Maintains.<facet>) |
### Maintains | responsibility, gateway | The world-model schema — type, canonicalization spec, facets (a #### sub-heading is a facet, the named-parts rule), and postconditions (see Maintains) |
### Parameters | function | Inputs the caller passes at call time |
### Returns | function | The value the function returns |
### Continuity | responsibility, gateway | The intrinsic wake-source declaration: input-driven, self-driven, or external-driven (see Continuity) |
### Errors | responsibility, function | Declared failures the node may signal |
### Invariants | responsibility, function, pattern | Properties that must hold regardless of outcome |
### Strategies | responsibility, function, test | Guidance for judgment calls and edge cases |
### Environment | responsibility, function | Runtime variables supplied by host infrastructure |
### Runtime | responsibility, function | Execution hints such as model |
### Skills | responsibility, function | Agent harness skills the component requires the host harness to provide. See Skills |
### Tools | responsibility, function | Host tools the component requires the host environment to provide. See Tools |
### Shape | responsibility, function | Capability boundaries: self, delegates, and prohibited work |
### Execution | responsibility, function | ProseScript render body that pins choreography |
### Fixtures | test | Test inputs supplied without prompting |
### Expects | test | Positive natural-language assertions |
### Expects Not | test | Negative natural-language assertions |
### Slots | pattern | Responsibilities or functions a pattern requires from its caller |
### Config | pattern | Pattern-level parameters and defaults |
### Delegation | pattern | ProseScript or pseudocode describing slot interaction |
### Schedule | gateway | Optional cron-like ingress cadence |
### Receives | gateway | Optional HTTP/event ingress description |
### Emits | gateway | Responsibility name the gateway should wake |
### Payload | gateway | Notes about the event payload shape |
Unknown ### sections are preserved as documentation. They are not contract sections unless a future spec names them.
Folded and deleted sections
The judge-era responsibility vocabulary folds into the world-model model:
| Legacy section | Folds into |
|---|---|
### Ensures | ### Maintains (data-flow) / ### Returns (function) — re-purpose, not just rename |
### Criteria | ### Maintains postconditions |
### Fulfillment | the render itself, or a delegated function |
### Constraints | ### Invariants / ### Shape |
### Memory | the single persisted world-model (responsibility); dropped (function) |
### Services / ### Wiring | deleted with system; composition is call or subscription |
### Memory is gone: one persisted world-model per node subsumes the old reads/writes ledger (world-model.md §9.4). A function is stateless and has no world-model, so it simply has no memory; a former service-with-memory that was genuinely stateful is really a responsibility, and its persisted state is its world-model.
File Extraction
Interpreters parse a file in this order:
1. Read YAML frontmatter for identity metadata (name, kind; kind: test files also declare subject; kind: responsibility files declare id). 2. Create the file-level responsibility, function, gateway, pattern, or test from the frontmatter. 3. Attach all ### sections before the first ## to the file-level entry. 4. For every ## {name} heading, create an inline node named {name}. 5. Attach subsequent ### sections to that inline node until the next ##.
Example — a file with one responsibility and two helper functions it calls:
````markdown --- name: content-pipeline kind: responsibility ---
Requires
draft: text to improve
Maintains
final: the current polished text
Execution
let notes = call review
draft: draft
let polished = call polish
draft: draft
feedback: notes
return polishedreview
Parameters
draft: text to review
Returns
feedback: editorial notes
polish
Parameters
draft: original textfeedback: editorial notes
Returns
final: polished text
````
The file-level responsibility requires draft and maintains final. It also contains inline functions review and polish that its render calls.
Responsibilities
A kind: responsibility is a mounted DAG node: a standing truth kept current over time. It declares both halves of its interface — ### Requires (what it subscribes to) and ### Maintains (the shape of the truth it keeps) — and its wake-source in ### Continuity.
---
name: qualified-stargazer-outreach
kind: responsibility
id: 067NC4KG01RG50R40M30E20918
---
### Goal
High-intent GitHub stargazers are identified, enriched, and thoughtfully
followed up with.
### Requires
- `stargazers`: a current view of new high-intent stargazers
### Maintains
Per-stargazer outreach truth. Each entry carries GitHub activity, company context,
plausible pain, and the outreach already sent; scan timestamps are immaterial.
Postcondition: outreach is specific (a concrete program idea or sample result),
never generic. Postcondition: a stargazer is never contacted twice without new
evidence.
#### qualification
The qualification verdict per stargazer. Material: the verdict and its supporting
signals.
#### enrichment
Company and profile context. Material: the resolved company, role, and plausible
pain.
#### contact-history
What outreach has been sent. Material: each sent contact and its evidence basis.
### Continuity
- input-driven
### Tools
(none)Forme matches each ### Requires facet-contract to the ### Maintains facet that satisfies it semantically, across all mounted responsibilities, and draws the subscription edge (plan.md §5). ### Requires is the need (intent stays with the human); the resolved producer is Forme's choice (mechanism).
Load responsibility-runtime.md and concepts/responsibility.md for the compile/run reconciler semantics.
Maintains
### Maintains declares the shape of the world-model — the schema, not the instance. It is not just a renamed ### Ensures: a maintained truth is a standing, typed, subscribable artifact, so its declaration does four jobs (world-model.md §2):
1. A type — the fields and their shapes, including any freshness fields (valid_until, last_corroborated, confidence; see Continuity). 2. A canonicalization spec — what equality means for the fingerprint: which fields are material, which are volatile-but-immaterial and excluded (timestamps, request ids, cosmetic ordering), and how sets / numbers / text normalize. This is the single highest-leverage memoization control: without it, a feed re-polled every few minutes always looks changed and "cost scales with surprise" degrades into "cost scales with the clock." 3. Facets (optional) — named, independently-subscribable parts of the truth, declared by the named-parts rule: a #### {name} sub-heading inside ### Maintains is a facet. A downstream that subscribes to facet X does not wake when facet Y moves. A single-truth (leaf) node declares no #### parts; its atomic world-model is the one implicit facet, so a ### Requires match still resolves. See Facets — the named-parts rule. 4. Postconditions — the folded-in ### Criteria: validators the render must leave the truth satisfying before it signs. Not a separate judge beat; just conditions on the output. Deterministically-expressible postconditions are verified by the harness on commit; irreducibly-semantic ones are self-attested by the render.
All four jobs live inside ### Maintains — none gets its own block. The canonicalization spec and facet declarations may be written as semantically rich natural language, as long as they are unambiguous, because the spec is compiled into a deterministic canonicalizer ahead of run time (world-model.md §3). The compiled canonicalizer travels with the contract, so a standalone render computes its own fingerprints and signs a fingerprinted receipt with no harness present.
The structured-backing rule. Anything subscribed must have a structured, canonicalizable backing. Free-form rendered prose is a derived projection excluded from the fingerprint — otherwise an LLM re-rendering the same paragraph hashes differently every time and falsely re-triggers downstreams. Rule: fingerprint the structured truth; render prose from it (world-model.md §3). The compiler lints subscribed fields that lack a structured backing.
The world-model itself — the materialized truth the render writes and commits — is a content-addressable artifact, a directory by default. ### Maintains describes its shape; state/filesystem.md describes its on-disk canonical form.
Facets — the named-parts rule
A facet is a named part of a maintained truth, and authors declare facets simply by naming the parts: a #### {name} sub-heading inside ### Maintains is a facet, and its body describes that part's fields and which are material — in prose. Name no parts and the node has one truth: the atomic facet, the free default that costs nothing. Atomic-only — no #### parts — is the v1 default and the leaf-node case (world-model.md §9.5; architecture.md §10.2 records the decision: "a `####` sub-heading inside `### Maintains` declares a facet … Atomic-only (no `####`) stays the default").
The name an author writes is the same name in three places at once (architecture.md §3.2, "the named-parts rule"; delta.md Part G):
1. Fingerprint unit — the compiled canonicalizer emits one token per #### part, plus the always-on atomic token over the whole truth. A part moves only its token; fields that sit outside any part move only the atomic token. 2. Subscription symbol — a consumer names the part in ### Requires, and the reconciler wakes that consumer only when that part's token moves. The join is Requires.<facet> ↔ Maintains.<facet> (architecture.md §6.3: edges are subscriber.Requires.<facet-contract> → producer.Maintains.<facet>). 3. World-model subtree — the part is a named region of the content-addressed artifact, published/<facet>/…, so "the directory structure is the state" shows the facets literally (state/filesystem.md).
Faceting therefore adds no new grammar: it reuses the heading hierarchy the format already has (#### = structure inside a ### section), the Requires↔Maintains join already specified, and the directory store. Structure is subscription. Material/immaterial and normalization stay prose inside each part, lowered at compile time into that part's facet name + material field-paths; a part subscribed without a structured backing is a lint (the structured-backing rule, above).
Worked example — the competitor-activity monitor maintains three subscribable parts:
````markdown
Maintains
A current, corroborated view of each tracked competitor. Each competitor carries a stable name and a last_corroborated field; fetched_at and source request-ids are immaterial everywhere. Postcondition: every competitor cites a corroborating source.
funding
Funding events per competitor — round, amount, date. Material: the event set (unordered) and each event's round/amount/date.
hiring
Open-role activity — the department set and the open-role count (exact).
product-launches
Announced or shipped products — the launch set; a ship-date slipping past today flips shipped, which is material. ````
A downstream that ### Requires funding wakes only when #### funding's fingerprint moves — not when hiring or launches move. The shared name / last_corroborated sit outside any part, so they move only the atomic token. This is React's selector boundary made authorable.
The symmetry is total: a producer's #### funding part under ### Maintains is exactly the symbol a subscriber names in its ### Requires (Requires.funding ↔ Maintains.funding). The memo key is unchanged — (contract_fingerprint, input_fingerprints); facet granularity lives in which input-fingerprints a subscriber consumes (one per subscribed facet), not in the key shape (delta.md Part G).
Continuity
### Continuity is the node's wake-source declaration — what can wake this node — and is intrinsic to the responsibility: it travels with the contract, not the mount (plan.md §4; architecture.md §4.2). It has three modes:
- input-driven (the default) — woken by an upstream node's receipt whose
subscribed facet-fingerprint moved. Falls out of ### Requires, so it needs no explicit declaration.
- self-driven — a declared cadence (e.g. "re-check every 6h", "re-validate
when stale"). The node's own continuity clock emits a synthetic self-receipt (a tick); the node re-renders and either writes a moved fingerprint (surprise propagates) or an unmoved one (the tick stops there, costing nothing downstream).
- external-driven — a declared outside trigger (webhook / cron / manual kick).
This is the gateway case; its input arrives from outside the graph.
Every wake is a receipt; the only variable is who emitted it (world-model.md §5). ### Continuity declares which sources may wake a node — it never makes the wake decision intelligent; the reconciler stays dumb.
Freshness — state vs. policy. Freshness state (valid_until, last_corroborated, confidence) lives in the world-model as data declared by ### Maintains. Freshness policy — the recheck cadence — lives in ### Continuity. The bridge: a valid_until lapsing flips a fact's status, which moves that facet's fingerprint, so "time becoming material" is just another change that propagates as surprise (world-model.md §6). ### Continuity may read the world-model's soonest valid_until to drive a data-driven recheck cadence, but the cadence rule stays in ### Continuity and the expiry data stays in the world-model.
Functions
A kind: function is a called helper — stateless, ephemeral, and the replacement for the retired service. Its interface is ### Parameters → ### Returns, a plain call interface: arguments in, a value out. A function carries no world-model, no ### Maintains, and no ### Continuity.
---
name: summarizer
kind: function
---
### Parameters
- `text`: the document to compress
### Returns
- `summary`: a five-bullet précis preserving key claimsFunctions are invoked from a render body with ProseScript call:
let s = call summarizer
text: documentYou author functions rarely and call them constantly; most ship pre-built in std/. They are the standard-library tier — the place the "unmounted" render actually lives (plan.md §3).
Patterns
A pattern is a reusable agent design pattern: slots, config, invariants, and delegation rules for how filled nodes interact. Patterns are not run directly; they are instantiated at compile time and expanded into nodes.
````markdown --- name: worker-critic kind: pattern ---
Slots
worker: produces the draftcritic: reviews and returns notes
Config
max_rounds: 3
Delegation
loop up to config.max_rounds:
let draft = call worker ...
let notes = call critic draft: draft
if notes.accepted: break````
A pattern is instantiated with a fenced yaml declaration. Use with: for slot bindings and config: for pattern parameters:
- name: reviewed-draft
pattern: std/patterns/worker-critic
with:
worker: writer
critic: reviewer
config:
max_rounds: 3pattern: names a kind: pattern file. with: binds slots to responsibilities, functions, or nested pattern instances. After expansion, the named instance behaves like a node. Nested pattern declarations are allowed only as slot values inside another pattern instance's with: block.
Structured Blocks
Use Markdown structure directly for Markdown: section headers, bullets, and tables are the language surface and should not be wrapped in code fences.
Use fenced yaml only for structured YAML declarations such as pattern instances. Use fenced prose only for ProseScript in ### Execution and pattern ### Delegation. Do not use markdown or text fences as structured data formats.
Runtime and Shape
Runtime hints and behavioral boundaries are sections:
### Runtime
- `model`: sonnet
### Shape
- `self`: evaluate sources, score confidence
- `delegates`:
- `summarizer`: compression
- `prohibited`: direct web scraping### Shape describes the capability boundary of this render. delegates names the helper functions the render calls inside the node (intra-node, ephemeral) — it is not a DAG edge and not a subscription. Cross-node dependency is expressed only through ### Requires / ### Maintains.
Skills
A responsibility or function that depends on the host agent's harness skills declares them in a ### Skills section. The compiler resolves the named skills against the host's installed skills before emitting the component's IR, and prose compile fails closed with a skill_unresolved diagnostic if any are missing.
### Skills
- document-skills:pdf
- document-skills:xlsxSkill names use the namespace:name colon form that matches the plugin marketplace convention shown in /skill invocations.
Rules:
- A responsibility's skill declarations apply to every function its render
calls. Node-level declarations are additive, not exclusive.
- The compiler resolves each declared skill by looking, in order, in:
1. The project's ./skills/ directory. 2. ~/.claude/skills/. 3. ~/.codex/skills/. 4. ~/.agents/skills/.
- If a declared skill cannot be resolved in any of those paths,
prose compile
fails closed with a skill_unresolved diagnostic naming the skill and the paths that were searched.
The compiler program implements this resolution rule; see skills/open-prose/compiler/index.prose.md (skills_resolver) for the program-level contract.
BYO harness invariant
OpenProse never installs, modifies, or removes the user's harness skills. Installing skills is the user's responsibility; the compiler only verifies they are present and stops the compile when they are not.
Tools
A responsibility or function that depends on host capabilities declares them in a ### Tools section. Tool declarations are host capability requirements: they do not satisfy ### Requires, do not create Forme dependency-graph edges, and do not grant or restrict tool use. Use ### Shape for capability boundaries and prohibited actions.
### Tools
- `cli:gh`: GitHub CLI available on PATH for PR inspection
- `cli:jq`: JSON CLI available on PATH for JSON validation
- `mcp:gmail`: MCP server registered with the hostThe supported deterministic tool declaration shapes are cli:<executable-name> and mcp:<server-name>. A CLI executable name is the command name the host should find by PATH lookup. An MCP server name is the registered server name the host advertises. Names must be non-empty and must not contain path separators. Tool declarations belong only in the ### Tools section; there is no frontmatter form.
Rules:
- A responsibility's tool declarations apply to every function its render
calls.
Node-level declarations are additive, not exclusive.
cli:<name>checks only executable presence on PATH. Version ranges, auth
checks, and installer behavior are outside the declaration.
mcp:<name>checks only server presence in the host MCP registry. The compiler
does not install, contact, or introspect the server during this check.
- For
kind: responsibility, declared tools are the host capabilities the render
may use to observe and act on the maintained truth. The compiler does not pre-split read vs write; capability scope is enforced by the connector adapter at runtime.
- Namespaces other than
cliandmcpare reserved. The current compiler reports
tool_unsupported_kind for reserved but unsupported namespaces such as http:example.
- A malformed declaration, such as
gh,cli:, ormcp:, reports
tool_invalid.
- A supported
cli:<name>declaration that cannot be found on PATH reports
tool_unresolved.
- A supported
mcp:<name>declaration that cannot be found in the host MCP
registry reports tool_unresolved.
prose compilefails closed when anytool_invalid,tool_unsupported_kind,
or tool_unresolved diagnostic is emitted.
The compiler program implements this resolution rule; see skills/open-prose/compiler/index.prose.md (tools_resolver) for the program-level contract.
BYO host tools invariant
OpenProse never installs, modifies, upgrades, or removes host tools. Installing and authenticating host tools is the user's responsibility; the compiler only verifies declared tools are present and stops the compile when they are not.
Frontmatter
Every responsibility, function, gateway, pattern, or test declares identity with name and kind:
---
name: entry-name
kind: responsibility | function | gateway | pattern | test
---Frontmatter should stay structural. If a field would be useful to read, review, or discuss, it should usually be a ### section.
A kind: test file also declares subject: to name the responsibility or function it runs.
A kind: responsibility file also declares required id: frontmatter to name the stable Markdown identity for the responsibility. The id is generated once by tooling as a UUIDv7-compatible 16-byte value, rendered as uppercase Crockford base32, and preserved across filename and name: renames. The slug is display; id: is identity.
Contract Item Style
Use backticked names followed by a colon:
- `topic`: a research question
- `report`: executive-ready summary with sourcesThis is visually clear and easy for agents to extract.
each postconditions are contract items:
- `articles`: collected articles from the feed
- each article has: a summary, relevance score, and key claimsTyped Caller Inputs
Most ### Parameters and ### Requires entries are free-form values provided at run time. Two keywords are reserved for passing completed runs as inputs — the typical shape for inspectors, regression checkers, and meta-systems:
### Parameters
- `subject`: run — a completed run to inspect
- `cohort`: run[] — a set of completed runs to compareWhen an entry's type is run or run[], the caller supplies a run ID (or a list of them). The Prose VM resolves each ID to its run directory and writes a structured binding at bindings/caller/{name}.md containing the run ID, path, root source name, and status. The render reads that binding and then reaches into the run's own bindings/, vm.log.md, and compiled artifacts directly.
See prose.md (Run-Typed Inputs) for binding format, resolution order (bare ID, ~/{id} for user scope, absolute path), and staleness validation.
Execution Sections
### Execution contains the render body in ProseScript. Use a fenced block:
````markdown
Execution
let research = call researcher
topic: topic
return research````
### Execution is the intra-node render body: call (invoke a function), session / agent / resume (spawn ephemeral sub-agents), plus control flow. All of it is internal to producing this node's world-model, and none of it is a node (plan.md §7). Cross-node connection is only ever a subscription. When ### Execution is present, Forme validates contracts and extracts the call graph, but the Prose VM follows the written order.
Tests
Test files use the same section grammar. A kind: test names a subject responsibility or function, supplies fixtures as caller inputs, then evaluates semantic assertions against the subject's world-model or returned value:
---
name: test-summarizer
kind: test
subject: summarizer
---
### Fixtures
- `text`: recent developments in quantum error correction
### Expects
- `summary`: contains at least five bullet points
- `summary`: is under 500 words
### Expects Not
- `summary`: contains fabricated citationsRules:
subject:must name a responsibility or function. Tests do not execute patterns
directly.
- Path-like subjects use normal resolution. Bare subjects may resolve by matching
frontmatter name: in the test file's directory and nearest OpenProse source/package root.
### Fixturesmust provide every caller input needed by the subject; tests do
not prompt the user.
### Expectsand### Expects Notassert observable behavior, not exact
phrasing.
- Test reports should list each assertion with pass/fail status and concise
evidence for failures.
Design Guidance
Use Contract Markdown when the author cares about the promise more than the choreography — declare a ### Requires need and let Forme choose the graph when the end-state matters. Use ProseScript ### Execution when the author needs exact order, control flow, or human-readable procedural steps inside a render.
For canonical responsibility, function, gateway, pattern, test, world-model, and security guidance, load guidance/authoring.md.
Dependency Resolution
OpenProse uses a git-native dependency model. use statements, dependency-like service names, and pattern: references can point at any explicit git host. Dependencies are cloned into <openprose-root>/deps/, pinned in <openprose-root>/prose.lock, and resolved from disk at runtime.
---
use Statement Parsing
A use statement names an explicit git host, owner, repo, and path. The canonical form is host/owner/repo/path:
use "github.com/openprose/prose/packages/std/evals/inspector"Parsed as:
| Field | Value |
|---|---|
| Host | github.com |
| Owner | openprose |
| Repo | prose |
| Path | packages/std/evals/inspector |
| Clone URL | host-specific URL for github.com/openprose/prose |
| Local clone | <openprose-root>/deps/github.com/openprose/prose/ |
| Resolved file | <openprose-root>/deps/github.com/openprose/prose/packages/std/evals/inspector.prose.md |
The first path segment is the host (must contain a dot — github.com, gitlab.com, codeberg.org, git.company.com). The next two segments are always owner/repo. Everything after is a path within the cloned repository.
Any git host works. Nothing in the resolver privileges GitHub — it's the common case, not a default.
std/ and co/ Shorthands
The OpenProse monorepo hosts two packages. Both get shorthands:
std/→github.com/openprose/prose/packages/std/co/→github.com/openprose/prose/packages/co/
use "std/evals/inspector"
# equivalent to:
use "github.com/openprose/prose/packages/std/evals/inspector"
use "co/systems/company-repo-checker"
# equivalent to:
use "github.com/openprose/prose/packages/co/systems/company-repo-checker"Both shorthands resolve into the same clone of openprose/prose under <openprose-root>/deps/github.com/openprose/prose/; packages/std/ and packages/co/ are sibling subdirectories inside that clone.
Bare owner/repo Form
Identifiers without a host prefix (e.g. use "alice/research") are reserved for the OpenProse registry — eventually hosted at p.prose.md. That registry isn't open for publication yet, so the bare form doesn't resolve today. Write the host explicitly (github.com/alice/research) or use the std/ shorthand. When the registry opens, the bare form gains a defined resolution without breaking systems that wrote explicit hosts.
File Extension Resolution
If the use path includes an explicit .prose.md extension, use it. If no extension, prefer .prose.md:
use "github.com/alice/tools/formatter"
# resolves to: <openprose-root>/deps/github.com/alice/tools/formatter.prose.mdAliasing
use statements support as aliases in execution blocks:
use "github.com/alice/research-pipeline" as research
let result = call research
topic: "quantum computing"In ### Services, use the full path — aliases are for execution blocks only.
---
Resolution Algorithm (Runtime)
When the VM or Forme encounters a use path at runtime:
1. Expand std/ and co/ shorthands to github.com/openprose/prose/packages/{std|co}/ if applicable 2. Parse {host}/{owner}/{repo} from the first three segments 3. Check <openprose-root>/deps/{host}/{owner}/{repo}/ exists on disk 4. If not found, error immediately (see Error Handling below) 5. Resolve the remaining path segments within the cloned repo 6. Return the absolute file path
No network calls during resolution. All dependencies must be pre-installed via prose install. The VM reads from <openprose-root>/deps/ on disk only.
---
prose install
Scans the project for dependency references and clones missing dependencies.
Algorithm
1. Scan all *.prose.md files under <openprose-root>/src/ for:
use "host/owner/repo/path"statements- service names in
### Servicesthat start withstd/,co/, orhost/owner/repo/ pattern:references that start withstd/,co/, orhost/owner/repo/
2. Expand std/ and co/ shorthands to github.com/openprose/prose/packages/{std|co}/ 3. Parse each expanded dependency path to extract {host, owner, repo} triples (the first segment is the host if it contains a dot) 4. For each unique {host, owner, repo}: a. If <openprose-root>/deps/{host}/{owner}/{repo}/ does not exist, clone the repository using the host's normal git URL into <openprose-root>/deps/{host}/{owner}/{repo}/ b. If <openprose-root>/prose.lock has a pinned SHA for this repo, checkout: git checkout {sha} c. If no pinned SHA exists (new dependency), use HEAD and record the SHA 5. Scan transitive dependencies — scan all *.prose.md files within newly cloned repos in <openprose-root>/deps/ for their own use statements 6. Cycle detection — if a newly discovered dependency is already in the resolved set, skip it. If scanning reveals a cycle (A requires B requires A), error: [Error] Circular dependency detected: A → B → A 7. Repeat from step 2 with any newly discovered dependencies until no new deps are found 8. Write <openprose-root>/prose.lock with all resolved {host, owner, repo, sha} entries (direct and transitive, flat list)
Transitive Resolution (Multi-Pass)
Dependencies can themselves have dependencies. prose install resolves transitively:
Pass 1: Scan project files → find direct deps → clone them
Pass 2: Scan <openprose-root>/deps/ for new use statements → find transitive deps → clone them
Pass 3: Scan newly cloned transitive deps → find more → clone
...repeat until stable (no new deps discovered)If a cycle is detected at any pass, prose install errors immediately and lists the cycle path. Cycles indicate a design problem in the dependency graph — they cannot be auto-resolved.
All dependencies — direct and transitive — are pinned in the flat <openprose-root>/prose.lock.
Version Conflict Resolution
If two dependencies require the same repo at different commits, prose install auto-resolves to the newer SHA (by commit date) and emits a warning:
This is a convenience policy, not proof that the newer dependency fits every caller. Treat the warning as review-required: inspect the affected dependency, run relevant tests, and commit the resulting <openprose-root>/prose.lock only when the newer version is acceptable.
[Warning] Version conflict for alice/utils:
Required by: your-project (a1b2c3d)
Required by: bob/toolkit (f6e5d4c)
Resolved to: f6e5d4c (newer, 2026-04-01)
Override: manually edit <openprose-root>/prose.lock if neededThis is not an error. The user can override by editing <openprose-root>/prose.lock directly.
Private Repositories
prose install uses the user's existing git credential helpers transparently. SSH keys, gh auth, .netrc — whatever git is configured to use for github.com works for prose install.
---
prose install --update
Bumps all pinned SHAs to the latest HEAD of their default branch.
Algorithm
1. For each host/owner/repo in <openprose-root>/prose.lock: a. Run git fetch in <openprose-root>/deps/{host}/{owner}/{repo}/ b. Get the latest HEAD SHA c. Run git checkout {new-sha} 2. Re-scan for transitive dependencies (new versions may add or remove use statements) 3. Rewrite <openprose-root>/prose.lock with updated SHAs
---
<openprose-root>/prose.lock Format
Plaintext. One line per dependency. Format: host/owner/repo sha.
# <openprose-root>/prose.lock — pinned dependency versions
# Do not edit unless you know what you're doing
github.com/openprose/prose a1b2c3d4e5f6
github.com/alice/research f6e5d4c3b2a1
gitlab.com/bob/utils 9c8d7e6f5a4bRules:
- One dependency per line
- Format:
{host}/{owner}/{repo} {sha}(space-separated) - Comments start with
# - Direct and transitive dependencies listed flat — no nesting, no hierarchy markers
- Host is explicit — no default is assumed, so any git provider works uniformly
- Order does not matter (but
prose installwrites them sorted alphabetically)
<openprose-root>/prose.lock is committed to git. It ensures reproducible builds — anyone cloning the project gets the same dependency versions.
---
<openprose-root>/deps/ Directory Structure
<openprose-root>/deps/
├── github.com/
│ ├── openprose/
│ │ └── prose/ # Full clone of github.com/openprose/prose
│ │ ├── packages/
│ │ │ ├── std/ # Standard library (resolved by `std/` shorthand)
│ │ │ │ ├── evals/
│ │ │ │ │ ├── inspector.prose.md
│ │ │ │ │ ├── contract-grader.prose.md
│ │ │ │ │ └── regression-tracker.prose.md
│ │ │ │ └── memory/
│ │ │ │ ├── user-memory.prose.md
│ │ │ │ └── project-memory.prose.md
│ │ │ └── co/ # Company-as-prose (resolved by `co/` shorthand)
│ │ │ └── systems/
│ │ │ └── company-repo-checker/
│ │ │ └── index.prose.md
│ │ └── ...
│ ├── alice/
│ │ └── research-pipeline/ # Full clone of github.com/alice/research-pipeline
│ │ └── ...
│ └── bob/
│ └── toolkit/ # Transitive dep, also a full clone
│ └── ...
└── gitlab.com/
└── team/
└── repo/ # Any git host works; host is part of the path`<openprose-root>/deps/` MUST be in `.gitignore`. It is a cache, fully reproducible from <openprose-root>/prose.lock via prose install.
Each entry under <openprose-root>/deps/ is a full git clone (or shallow clone) of the corresponding repository, checked out to the SHA pinned in <openprose-root>/prose.lock. The host is part of the cache key so repos with the same owner/repo name on different hosts do not collide.
---
Runtime Behavior
At execution time, the VM and Forme resolve use paths by reading from <openprose-root>/deps/ on disk.
- No git operations during execution
- No network calls during execution
- No auto-install —
prose rundoes not runprose installimplicitly
If a dependency is missing or <openprose-root>/deps/ does not exist:
[Error] Dependency not found: github.com/openprose/prose
Run `prose install` to install dependencies.If <openprose-root>/prose.lock exists but <openprose-root>/deps/ is missing or incomplete, the same error applies. The user must run prose install.
---
Interaction with Forme
When Forme resolves a service listed in ### Services, it checks <openprose-root>/deps/ as part of its resolution order (see forme.md, Step 2):
1. Same directory as the system file: ./researcher.prose.md 2. A subdirectory matching the name: ./researcher/index.prose.md 3. `<openprose-root>/deps/` directory: first <openprose-root>/deps/{host}/{owner}/{repo}/{path}.prose.md, then <openprose-root>/deps/{host}/{owner}/{repo}/{path}/index.prose.md 4. Bare owner/repo identifiers: reserved for the OpenProse registry (future home at p.prose.md); inert today
A service or system reference like std/evals/inspector in ### Services resolves to <openprose-root>/deps/github.com/openprose/prose/packages/std/evals/inspector.prose.md after std/ shorthand expansion. A directory-root system reference like co/systems/company-repo-checker resolves to <openprose-root>/deps/github.com/openprose/prose/packages/co/systems/company-repo-checker/index.prose.md.
---
Interaction with the VM
When the VM encounters a use statement during execution:
1. Expand shorthand (std/ → github.com/openprose/prose/packages/std/; co/ → github.com/openprose/prose/packages/co/) 2. Parse {host}/{owner}/{repo} and remaining path 3. Read the service or system from <openprose-root>/deps/{host}/{owner}/{repo}/{path}.prose.md, or from <openprose-root>/deps/{host}/{owner}/{repo}/{path}/index.prose.md when the dependency is a directory-root system 4. Parse the imported service or system contract (### Requires / ### Ensures) 5. Register the import (with alias if as was used)
Runtime resolution is disk-only. If a use path is missing from <openprose-root>/deps/, the VM errors and tells the caller to run prose install.
---
Interaction with p.prose.md
p.prose.md is reserved as the future home of the OpenProse registry. Publication there isn't open yet — no identifier actually resolves via p.prose.md today. When it opens, the bare owner/repo form gains a defined resolution and p.prose.md takes on a discovery role (search, docs, install counts, eval scores, and supported runtimes).
| Use case | Resolution |
|---|---|
use "github.com/owner/repo/path" in a system | <openprose-root>/deps/github.com/owner/repo/; error if missing |
use "std/..." or use "co/..." in a system | Expands to `github.com/openprose/prose/packages/{std\ |
prose run github.com/owner/repo/path at the CLI | Same algorithm as use |
prose run github.com/owner/repo/path@{version} | That specific pinned version in <openprose-root>/deps/; error if missing |
prose run ... --offline | <openprose-root>/deps/ only; error on miss |
use "alice/research" / prose run alice/research | Reserved for the OpenProse registry; inert today |
| Browsing/searching for systems | Not yet available; p.prose.md will host this |
use and prose run share one resolution algorithm. prose install is the explicit "get me every declared dependency at its pinned SHA" command. Neither use nor prose run auto-fetches a missing dependency during execution.
---
Summary
| Concept | Detail |
|---|---|
| Package identity | Any git host, named explicitly (github.com/..., gitlab.com/...); bare owner/repo reserved for future p.prose.md |
| Install command | prose install (explicit, not auto) |
| Update command | prose install --update |
| Lockfile | <openprose-root>/prose.lock (plaintext, committed) |
| Cache directory | <openprose-root>/deps/{host}/{owner}/{repo}/ (gitignored) |
| Shorthands | std/ → github.com/openprose/prose/packages/std/; co/ → github.com/openprose/prose/packages/co/ |
| Clone strategy | Full clone (supports SHA checkout without refetch) |
| Transitive deps | Multi-pass scan until stable (errors on cycles) |
| Version conflicts | Auto-resolve to newer SHA with warning |
| Runtime resolution | Disk only, no network |
| Private repos | Uses existing git credentials |
# Local runtime churn, not committed.
/state/
/runs/
/dist/
node_modules/
agent-observatory
Standing goal: keep a live, auditable index of every local agent session (across Claude Code, Codex, OpenCode, and Pi) and a dual Markdown + HTML dashboard that only re-writes when some session state actually changed.
One-line scenario: many cheap watchers, one expensive synthesis. Your laptop is already a sprawling multi-runtime agent state machine; this observatory turns that mess into maintained world-models (sessions, summaries, workstreams, concepts, an extracted .prose program, Markdown, and HTML), and proves that the expensive synthesis only wakes on a real surprise.
This is the multi-agent observatory flagship: independent per-runtime facet tokens (the dark lane), quiet watchers, a diamond fan-in woken exactly once, a batched expensive synthesis gated on surprise, the folded-in Session to Prose meta-generator as a standing node, and dual MD + HTML artifacts.
The DAG (14 nodes / 22 edges)
Agent FS (external)
│ @atomic
▼
Runtime Watch ── one INDEPENDENT facet per runtime ──┐
claude │ codex │ opencode │ pi │
▼ ▼ ▼ ▼ │
Claude Ad. Codex Ad. OpenCode Ad. Pi Ad. ← quiet watchers (mostly dark)
└────────┴──────────┴─────────┘ @atomic
▼
Session Ledger ── one facet per session:<id> ──┐
session:claudeA │ session:claudeB │ session:codexA │
▼ ▼ ▼ │ session:claudeA
Summary[claudeA] Summary[claudeB] Summary[codexA] Session → Prose
└────────────────┴──────────────────┘ @atomic │ @atomic
▼ (DIAMOND, woken once) │
Workstream Index ── rollup · cluster-gate │
cluster-gate │ │ rollup │
▼ │
Concept Clusterer (batched, expensive) │
│ @atomic │ rollup │
├────────────────────┼────────────────────┘
▼ ▼
Agent Dashboard (HTML) Agent Index (Markdown)- The dark lane: the gateway exposes one INDEPENDENT facet token per runtime.
A single Claude session edit moves only the claude token, so only the Claude Adapter lane lights; the three sibling adapters stay dark. The Session Ledger repeats the trick per session.
- The diamond: the three per-session summaries fan into the Workstream Index;
a two-session delta wakes the index exactly once.
- The batch gate: the Concept Clusterer subscribes only to the
cluster-gate facet (the distinct-workstream set), so it stays dark on small deltas and spends the single tall fresh spike only on a "major new project".
- The fold-in: Session to Prose watches one Claude transcript and maintains a
generalized .prose contract, feeding the Markdown index.
- Dual artifacts: Agent Index (Markdown) + Agent Dashboard (HTML) re-render
together only when DashboardData moved.
Try it (the Reactor flow)
The contract under src/ is harness-neutral; these verbs steer toward the Reactor harness. Offline needs no key.
reactor doctor # honest health report (the best command in the kit)
reactor compile --check # exits 1 (stale): recognized, not yet compiledreactor compile # run the compile sessions -> IR cache (needs a key)
reactor topology # offline now: the compiled 14-node / 22-edge DAG
reactor run # boot, drain, print dispositions + cost rollup
reactor serve # local server for the dashboard artifact
reactor receipts verify # chain-verify the on-disk ledgerReplay any run you produce
Any run you produce with the Reactor CLI (reactor run or reactor serve) writes a real, chain-verifiable state-dir. Replay it with no key using reactor-devtools <state-dir> --describe to walk the cold cascade, the quiet flat line, the one-runtime delta, the diamond single-wake, and the single tall Concept-Clusterer spike.
The example is also exercised by the project's offline test suite.
Agent Dashboard (HTML)
The second of the two TERMINAL artifacts (the dual MD + HTML tenet). A local,
static HTML dashboard that can be opened without a server — active sessions,
clusters, and recent changes rendered for a glance. It reads the same cheap
rollup the Markdown index does, so the two artifacts re-render together only
when DashboardData actually moved.
Requires
- the
rollupfacet ofworkstream-index— the cheap incremental session/cluster rollup concept-clusterer(via@atomic) — the cluster graph
Maintains
The HTML dashboard artifact:
path:agent-dashboard.htmlhtml: a self-contained static HTML document (no server, no external assets)content_hash: a stable digest so an unchanged render is a memo hit
Redact private terms before producing any shareable projection.
Continuity
- input-driven: a moved
rollupfacet onworkstream-index, or a changed
cluster graph from concept-clusterer, wakes the dashboard.
- A no-change replay preserves the same HTML content hash, so the dashboard file
is not rewritten.
Agent Index (Markdown)
One of the two TERMINAL artifacts (the dual MD + HTML tenet). A portable
Markdown index that can be read in any editor or committed into a private ops
repo. It is the artifact that the folded-in Session → Prose node feeds: the
index lists the extracted .prose program alongside the session and clusterrollup.
Requires
- the
rollupfacet ofworkstream-index— the cheap incremental session/cluster rollup concept-clusterer(via@atomic) — the cluster graphsession-to-prose(via@atomic) — the extracted program metadata
Maintains
The Markdown index artifact:
path:agent-index.mdmarkdown: a stable Markdown index with headings for sessions, clusters, and
the extracted program
content_hash: a stable digest so an unchanged render is a memo hit and the
on-disk file is not rewritten
Continuity
- input-driven: a moved
rollupfacet onworkstream-index, a changed cluster
graph from concept-clusterer, or a changed extracted program from session-to-prose, wakes the index.
- A quiet drain leaves the Markdown content hash unchanged — the file is not
rewritten, so a no-change replay preserves the same content hash.
Concept Clusterer
The EXPENSIVE, BATCHED synthesis — the single tall spike in the cost meter. It
re-embeds and clusters the whole concept space across every workstream, which
costs roughly an order of magnitude more fresh tokens than any cheap node. It
subscribes to ONLY the gating cluster-gate facet of the Workstream Index, soit stays DARK through the entire quiet stretch and every small session delta,
and wakes ONCE when a major new project appears.
Requires
- the
cluster-gatefacet ofworkstream-index(NOT@atomic, NOTrollup) —
the gate. It does not see ordinary rollup churn; it wakes only when the distinct workstream set moves.
Maintains
The concept cluster graph:
clusters:Cluster[], each{ cluster_id, workstream, concepts }cluster_count: the number of clusters
The render re-embeds every workstream's concept space, so its fresh cost scales with the number of distinct workstreams — the deliberately heavy node that makes the batched-synthesis lesson visible.
Canonicalization spec: the clusterer exposes its whole truth as @atomic. A self-tick on a quiet world finds its gating input unmoved, so it signs a self skipped receipt that lights no edges and burns zero fresh — the audit floor.
Continuity
- input-driven: a moved
cluster-gatefacet onworkstream-index(the distinct
workstream set changed — a major new project) wakes the clusterer.
- self-driven: a configured batch interval / after-N-tail-changes self-tick;
when the gating input has not moved it signs a self skipped receipt that lights no edge and burns zero fresh — the audit floor.
- Batch intelligently; do not run on every file change.
Session Summary [session]
A per-session tail summarizer. There is one summary per active session
(claudeA,claudeB,codexA); the topology mounts this contract three
times. Each subscribes to ONLY its own session:<id> facet on thesession-ledger, so a change to a sibling session leaves it dark. The summaries
fan into the Workstream Index as a diamond.
Requires
- the
session:<id>facet ofsession-ledger(NOT@atomic) — exactly one
session. The summary for claudeA never wakes on a codexA edit.
Maintains
The one-session tail summary, as the truth the Workstream Index reads:
session: the session idruntime: the runtime that produced itrev: the session revision summarizedsummary: a one-line summary of the tail (current goal,latest ask)workstream: the session's current workstream tag
Read enough context to summarize the tail, not the whole transcript by default.
Canonicalization spec: the summary exposes its whole truth as @atomic. A re-summarization that produces the same text moves no fingerprint, so the downstream Workstream Index memo-skips.
Continuity
- input-driven: a change on this session's own tail facet
(session:<id> on session-ledger) wakes exactly this summary; a sibling session's edit leaves it dark.
- Prefer a stable summary so cosmetic re-orderings do not move the fingerprint
and spend downstream tokens.
# Local runtime churn, not committed.
/state/
/runs/
*.log
.env
.env.*
dist/*
!dist/.gitkeep
!dist/manifest.active.json
runs/*
!runs/.gitkeep
state/*
!state/.gitkeep
deps/*
!deps/.gitkeep
{}
Related skills
How it compares
Pick open-prose when workflows must be reusable artifacts with persisted run state, not ephemeral subagent chatter.
FAQ
Who is open-prose for?
Developers and software engineers working with open-prose patterns described in the skill documentation.
When should I use open-prose?
When working with open-prose.
Is open-prose safe to install?
Review the Security Audits panel on this page before installing in production.