mirror of
https://github.com/openprose/prose.git
synced 2026-09-19 05:55:05 +08:00
6cb6e45aa8
Repairs surfaced by an independent code-grounded verification pass. 00: cost is observed-not-budgeted (not 'resource budget the gates are constrained by'); Tenet 2's intelligence layer lists all three model-authored compile artifacts (Forme topology + canonicalizer + postcondition validators). 01: state the `### Invariants` actuation-boundary ideal in Part I (with a bridge note it is authored-only today); gateway compiles to a trigger the reactor serves (not `prose serve`); migration line corrected (`co` has no `kind: responsibility`; std has 3); note the language-root `receipts.jsonl` vs the harness `receipts.json`. 02: launch DoD no longer implies version-equality; new Part III §10 (declared-capability resolution + serve auth + async storage seam) so every Honest-Limit has a forward item. 03: signer rendered as shipped (`scheme: "none"`, no `kind:"null"|"signed"`); Part II honest that `### Tools`/`### Environment`/`### Skills` are name-only and not harness-consumed; `as_of` added to the gap-cluster-2 receipt set; gateway `### Schedule` forward item; no `reactor test` subcommand. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
730 lines
42 KiB
Markdown
730 lines
42 KiB
Markdown
# OpenProse Reactor Pattern
|
|
|
|
###### How to write OpenProse for a Reactor-class harness — the language layer beneath evented reconciliation.
|
|
|
|
The OpenProse corpus divides labor exactly, and each document maps to what
|
|
ships:
|
|
|
|
- [01-Language.md](./01-Language.md) — **the Language & Framework**, bundled as
|
|
the **SKILL**: syntax, kinds, sections, compile model, std/co.
|
|
- [02-ReactorHarness.md](./02-ReactorHarness.md) — **the Reactor
|
|
Harness**, bundled as the **CLI/Server**: the runtime control architecture
|
|
(loop, invariants, the reconciler, memoization, forecast, receipts, composition). It
|
|
answers _what the runtime must do_.
|
|
- [03-ReactorPattern.md](./03-ReactorPattern.md) — **this
|
|
document, the Reactor-Native Authoring Pattern**: **SKILL-bundled but
|
|
harness-governed**. Where the Harness doc says what the runtime does, this
|
|
says **what the author writes** so the runtime can do it. It bridges the
|
|
Language doc and the Harness doc and is the definitive guide to writing
|
|
`*.prose.md` for a Reactor-class harness.
|
|
- [ReactorFeedback.md](../history/ReactorFeedback.md) — **the
|
|
decision log**, not shipped: the dialectic that produced the Harness doc;
|
|
the clean statements live in the docs above and it does not repeat them.
|
|
- [00-Tenets.md](./00-Tenets.md) — **the constitution**. When any
|
|
document tensions with a tenet, the tenet wins.
|
|
|
|
The relationship is the same as the one between a language reference and an
|
|
effective-style guide. The harness doc tells you the machine has memoization,
|
|
forecast-gated quiescence, deterministic commit-gating, and receipt composition.
|
|
This doc tells you how to write contracts so those mechanisms actually engage
|
|
instead of degrading to "cron plus a prompt."
|
|
|
|
This file has three parts, mirroring the harness doc:
|
|
|
|
1. The ideal Reactor-native authoring pattern.
|
|
2. What the OpenProse skill implements today.
|
|
3. What must change in the skill before the pattern is fully authorable.
|
|
|
|
The single most important principle, stated once up front so the rest is read
|
|
in its light:
|
|
|
|
> **The Reactor paradigm does not change OpenProse syntax. It inverts which
|
|
> kind is the program.** Every section, kind, and keyword the pattern needs
|
|
> already exists. What changes is doctrine: the responsibility is the
|
|
> top-level authored object, the render is where the work happens (there is no
|
|
> separate fulfillment system), and two contract sections (`### Maintains` and
|
|
> `### Continuity`) carry a cost-and-reconciliation obligation they did not
|
|
> visibly carry before.
|
|
|
|
---
|
|
|
|
## I. The Ideal Reactor-Native Authoring Pattern
|
|
|
|
### The inversion
|
|
|
|
`01-Language.md` settles the mental model: the **responsibility is the program**.
|
|
A standing goal, written as durable intent, is the top-level authored object;
|
|
everything else is derived from it.
|
|
|
|
```text
|
|
The responsibility is the program.
|
|
A gateway is how the world reaches it.
|
|
A function is a helper one render calls; the DAG is how responsibilities compose.
|
|
Forme, the canonicalizer, and the VM are the substrate, never the unit of authoring.
|
|
```
|
|
|
|
The authoring consequence: **you do not start by writing a system. You start by
|
|
writing one sentence of durable intent and what makes it true** — a `### Goal`
|
|
and a `### Maintains`. A responsibility is _served_, not _run_, but that is not a
|
|
limitation: "not directly runnable" means "continuously reconciled," which is the
|
|
entire point. The render of a single responsibility still runs standalone
|
|
(language sovereignty), so there is no lesser, non-executable artifact here.
|
|
|
|
### The canonical contract
|
|
|
|
A Reactor-native unit of work is, at its smallest, **one file**: a
|
|
`kind: responsibility` whose `### Maintains` declares the truth to keep current.
|
|
It grows only as the work demands:
|
|
|
|
| File | `kind:` | What the author is really writing |
|
|
| --- | --- | --- |
|
|
| The standing goal | `responsibility` | One `### Goal` sentence + the `### Maintains` world-model that makes it true (type, facets, canonicalization, postconditions) |
|
|
| Event ingress | `gateway` | How and when the world is allowed to wake the loop |
|
|
| A helper | `function` | A called, ephemeral computation a render invokes — `### Parameters` → `### Returns` |
|
|
|
|
There is no separate "fulfillment system," no "sense service," no "verdict
|
|
service," and no "ledger service": the render *is* the fulfillment, the
|
|
canonicalizer *is* how change is sensed, `gateCommit` *is* the admissibility check, and
|
|
the signed receipt ledger *is* the storage — all owned by the harness, not
|
|
authored. A reader who understands the responsibility understands the program;
|
|
the rest is _how_, and may change without the intent changing (invariant 1, at
|
|
authoring time).
|
|
|
|
### Authoring derived from the invariants
|
|
|
|
Each harness invariant produces a concrete, checkable authoring rule. This
|
|
table is the spine of the pattern; the prose after it only elaborates.
|
|
|
|
| Harness invariant | Authoring rule it imposes |
|
|
| --- | --- |
|
|
| 1. Markdown is intent | The responsibility carries all semantic weight. Never push intent into a prompt or a tool config. If it matters, it is in `### Goal` / `### Requires` / `### Maintains`. |
|
|
| 2. Materiality is compiled and shared | Declare *what counts as a material change* as natural language inside `### Maintains` — which fields matter, how they normalize, where the facets fall. The compile phase lowers that into a deterministic canonicalizer; you state the materiality, not the hash. |
|
|
| 3. Adapters are the only reason homes differ | Author no host-specific logic in contracts. Declare needs in `### Tools` / `### Environment` by name only. The same contract must run local and cloud. |
|
|
| 4. Activations are bounded | Never write a render that assumes it keeps running. No "while true," no in-session waiting for the next event. Continuity lives in the receipt ledger, not a session. |
|
|
| 5. Cost scales with surprise | Write `### Maintains` so "did anything material change?" is **cheaply decidable** — give the truth a stable content identity and facet it so an unrelated change wakes nobody. Declare freshness (`valid_until`) in `### Continuity` so silent staleness still wakes the node. This is a rule authors often violate. |
|
|
| 6. The commit gate is deterministic | State satisfaction as **postconditions inside `### Maintains`** — deterministic where you can express a validator, self-attested by the render where it is semantic. There is no separate judge and no `### Criteria`. |
|
|
| 7. Receipts are content-addressed | Trust the harness's signed receipt ledger as the audit / composition / exit unit. Do not hand-roll a scratch log. |
|
|
| 8. State is replayable and exitable | Keep all durable truth in the responsibility's `### Maintains` world-model, in plain structured records. A reader must be able to take the contract and its trail to another harness. |
|
|
|
|
### Rule 1 — `### Goal` is one standing-intent sentence
|
|
|
|
A goal is a state that should remain true, not a task or a deliverable. Not
|
|
"triage the inbox," not "produce a report," but an invariant: "the research inbox
|
|
is deduplicated, prioritized, and converted into action."
|
|
|
|
The test is no longer "could a judge score it" — there is no judge. The test is:
|
|
**can you name the maintainable truth and declare its shape in `### Maintains`?**
|
|
If the sentence describes an _activity_ rather than a _state_, rewrite it until
|
|
it describes a state. A sentence whose truth has nothing observable to maintain
|
|
against is the highest-value early signal: the first render fails, its receipt
|
|
names the gap, and the contract author hears about it — front-loaded, then
|
|
silent.
|
|
|
|
### Rule 2 — `### Continuity` declares the wake source; `### Maintains` carries the materiality
|
|
|
|
These were once one rule; they are two jobs, and conflating them is a common
|
|
authoring mistake.
|
|
|
|
`### Continuity` answers **when, beyond an input change, this node should
|
|
re-render**:
|
|
|
|
- **Input-driven (default).** The node wakes when a subscribed `### Requires`
|
|
facet moves. You write nothing extra.
|
|
- **Self-driven.** The truth goes stale on its own. Declare a `valid_until`
|
|
freshness window — "a stargazer count older than one business day is stale."
|
|
When it lapses, the continuity clock mechanically moves the facet's fingerprint
|
|
and wakes the node; you do not hand-write a cron.
|
|
- **External-driven.** A `kind: gateway` turns an ingress event (webhook, queue,
|
|
schedule) into a wake.
|
|
|
|
The *memoization* half — what counts as a material change, and whether "nothing
|
|
changed" is cheaply decidable — belongs in **`### Maintains`**, not here. Write
|
|
`### Maintains` so an unchanged world produces an unchanged fingerprint: give the
|
|
truth a stable content identity (a content hash, a max-timestamp, a revision) and
|
|
facet it so an unrelated change wakes nobody. If the only way to know whether
|
|
anything changed is to redo the full expensive render, you have written a
|
|
contract whose cost scales with the clock, not surprise — say so deliberately
|
|
(the node will run at freshness cadence), exactly as projection-only is a
|
|
deliberate choice rather than a degenerate Reactor.
|
|
|
|
### Rule 3 — satisfaction is a postcondition in `### Maintains`, not a `### Criteria` judge
|
|
|
|
There is no `### Criteria` and no judge. State what "satisfied" means as
|
|
**postconditions inside `### Maintains`**:
|
|
|
|
- **Deterministic where you can express it.** "The release-notes file's last
|
|
commit is newer than the latest merged PR touching `src/`" compiles into a
|
|
validator the harness runs at commit. If it fails, the render commits nothing.
|
|
- **Self-attested where it is semantic.** Where the obligation cannot be reduced
|
|
to a validator, the render must attest it satisfied its own `### Maintains`
|
|
before it signs. `gateCommit` fails closed: no attestation, no commit. (Part II:
|
|
the deterministic gate is built but currently unwired, so the live commit rides
|
|
the render's self-attestation until the compiled validators are threaded on.)
|
|
|
|
When the truth genuinely has *nothing observable to maintain against*, the render
|
|
cannot satisfy its postcondition and writes a `failed` receipt naming the gap —
|
|
routed to the contract author. Author postconditions *expecting* this on the
|
|
first few activations; that front-loading is the promise, not a defect. There is
|
|
no `up`/`drifting`/`down`/`blocked` status to encode and no "undecidable" enum —
|
|
the `failed` receipt and its reason carry it.
|
|
|
|
### Rule 4 — `### Invariants` draws the actuation boundary
|
|
|
|
`### Invariants` (the section that absorbs the old `### Constraints`) is where the
|
|
author quarantines world-mutation — the authoring-time expression of the
|
|
render/commit split: a render may act, but only the canonicalized published truth
|
|
re-enters the memo, and `### Invariants` is where the author bounds what the
|
|
render may touch — the actuation boundary the harness is to enforce at the
|
|
render/commit split. (Part II: authored but not yet lowered into the render.)
|
|
|
|
Two boundary shapes recur:
|
|
|
|
- **Full Reactor.** The render may mutate the world (send the email, update the
|
|
briefing, write the register). `### Invariants` bounds _how_ (rate, scope,
|
|
prohibited actions, "leave the final send to a human").
|
|
- **Projection-only Reactor.** The author forbids all world-mutation except
|
|
writing the published truth itself: an observe-only overseer, a dashboard that
|
|
must stay true, an audit that watches but never touches. Say so explicitly:
|
|
"the only writable surface is the published truth; never modify, signal, or
|
|
write into the observed system."
|
|
|
|
A projection-only contract is not a degenerate Reactor. It keeps every cost and
|
|
audit property; it simply declines the reconcile-the-world payoff. Choose it
|
|
deliberately, not by backing into it.
|
|
|
|
### Rule 5 — write `### Maintains` so the harness can memoize
|
|
|
|
Authors most often write the anti-pattern: a render that re-derives everything
|
|
every time. The fix is not a service decomposition — it is shaping `### Maintains`
|
|
so the dumb reconciler can skip. The reconciler keys each render on the 3-tuple
|
|
`(contract_fingerprint, input_fingerprints, freshness_epoch)`; your leverage is
|
|
to make the input fingerprints move only on real change. (Shipped v1 realizes the
|
|
freshness term as a forecast self-receipt over a 2-tuple key — identical decision
|
|
semantics; see [02-ReactorHarness.md](./02-ReactorHarness.md) Part II.)
|
|
|
|
1. **Give the truth a stable content identity.** The canonicalizer fingerprints
|
|
the published truth; make sure an unchanged world yields an unchanged
|
|
fingerprint (drop volatile fields — a re-poll timestamp, a request id — from
|
|
materiality). This is what lets the reconciler skip *before* any render runs
|
|
(quiescence behavior 1).
|
|
2. **Facet the truth.** Split `### Maintains` into `####` parts so a consumer
|
|
subscribing to one facet is not woken by a change in another (quiescence
|
|
behavior 3). A diamond reconverges to a single wake.
|
|
3. **Declare freshness.** Put `valid_until` windows in `### Continuity` so silent
|
|
staleness still wakes the node (quiescence behavior 2).
|
|
|
|
Variable-depth work, where you genuinely need it, is ordinary control flow
|
|
*inside the one render* — a `call` to a `function` for an expensive sub-step,
|
|
gated by an `if` in `### Execution`. It is not a "judge tier" and not an
|
|
autowired service graph.
|
|
|
|
### Rule 6 — composition is subscribing to an upstream facet, not a new primitive
|
|
|
|
"Responsibility B depends on responsibility A" needs no new syntax. B names the
|
|
upstream facet in `### Requires`; Forme matches it to A's `### Maintains` facet
|
|
and draws the subscription edge. B's render wakes on A's receipt when that
|
|
facet's fingerprint moves — identical to consuming a webhook. Two authoring
|
|
obligations make it safe:
|
|
|
|
- **Reference, don't embed.** B's `### Requires` names A's responsibility id /
|
|
facet as a declared subscription, not a copied value.
|
|
- **Pin revision and trust.** For cross-trust-domain composition, pin which
|
|
revision of A you accept and an acceptable signer set; unpinned composition is
|
|
a supply-chain attack the author closes, not the runtime. (Part II: v1 "signed"
|
|
is meaning-layer chain-consistency over a null signer; the cryptographic
|
|
byte-hash signer is deferred.)
|
|
|
|
Freshness needs no special authoring: each facet carries its own `valid_until`,
|
|
so a stale upstream facet's fingerprint lapses and wakes B through the ordinary
|
|
path. There is no transitive-freshness function to shape and no per-cycle
|
|
staleness comparison to hand-write.
|
|
|
|
This is how a downstream responsibility consumes an upstream one — a subscription
|
|
edge, not a special case.
|
|
|
|
### Patterns that are Reactor-native
|
|
|
|
The std pattern library is use-case agnostic, but a subset recurs in
|
|
Reactor-native contracts and should be the author's default vocabulary:
|
|
|
|
| Pattern | Reactor-native role |
|
|
| --- | --- |
|
|
| `fan-out`, `map-reduce` | Sensing a wide world cheaply, in parallel, inside one render |
|
|
| `guard` | A precondition gate before an expensive `call` or a world-mutating step |
|
|
| `worker-critic`, `proposer-adversary` | A built-in check a render runs before it attests and commits |
|
|
| `oversight` | The actor / observer / arbiter split — the canonical projection-only shape |
|
|
|
|
These coordinate `call`s to `function`s *inside a render*; none is a separate
|
|
node or an autowired graph. Depth — running a critic only on the uncertain
|
|
branch — is ordinary `### Execution` control flow.
|
|
|
|
### A worked example: the competitor-activity monitor
|
|
|
|
The running example across the harness and world-model specs: keep a current
|
|
picture of a set of competitors, where downstream consumers care about
|
|
*different parts* of that picture and should wake only when their part moves.
|
|
|
|
**The responsibility** (the whole program):
|
|
|
|
```markdown
|
|
---
|
|
name: competitor-activity-monitor
|
|
kind: responsibility
|
|
id: 067NC4KG01RG50R40M30E20918
|
|
---
|
|
|
|
### Goal
|
|
|
|
The activity of the tracked competitors — their funding, hiring, and product
|
|
launches — is current.
|
|
|
|
### Requires
|
|
|
|
- `competitors`: the watchlist (names + domains) this monitor tracks.
|
|
- `pages` from `competitor-sources-gateway`: the latest fetched source content,
|
|
staged with a cheap content identity so unchanged sources don't wake the
|
|
extraction.
|
|
|
|
### Maintains
|
|
|
|
A `competitor-activity` world-model with three independently-subscribable parts:
|
|
|
|
#### funding
|
|
Rounds, amounts, investors, and dates. Material: a new or changed round.
|
|
Immaterial: re-ordering, prose phrasing, the timestamp of the last poll.
|
|
|
|
#### hiring
|
|
Open roles and headcount signals by function. Material: a role appears or
|
|
closes, or headcount crosses a band. Immaterial: list order, exact view counts.
|
|
|
|
#### product-launches
|
|
Announced launches and ship dates. Material: a new launch or a moved date.
|
|
|
|
Postcondition: every entry cites the source it was derived from; an entry with
|
|
no observable source is not committed (the render attests this).
|
|
|
|
### Continuity
|
|
|
|
- Input-driven: wake when `pages` (new staged source content) or the
|
|
`competitors` watchlist changes. Unchanged sources leave the `pages`
|
|
fingerprint still, so the expensive extraction memo-skips at zero render tokens.
|
|
- Self-driven: each part carries a `valid_until` of +1 business day as a safety
|
|
net, so a silently stale part is re-checked even if the gateway missed a change.
|
|
|
|
### Invariants
|
|
|
|
- Read-only on the outside world: never contact a competitor or alter a source.
|
|
- The only writable surface is the `competitor-activity` world-model.
|
|
|
|
### Tools
|
|
|
|
- cli:fs-read # reads staged source content; the gateway owns web-fetch
|
|
```
|
|
|
|
**The gateway** that feeds it — cheap, deterministic ingress with a stable
|
|
content identity, so the monitor's render only fires on real change:
|
|
|
|
```markdown
|
|
---
|
|
name: competitor-sources-gateway
|
|
kind: gateway
|
|
id: 067NC4KG01RG50R40M30E2091A
|
|
---
|
|
|
|
### Goal
|
|
|
|
The latest fetched source content for each tracked competitor is staged for
|
|
extraction — fetched and staged only, never interpreted here (extraction is the
|
|
monitor's render).
|
|
|
|
### Continuity
|
|
|
|
external-driven: the competitor sources cannot push, so the gateway re-checks
|
|
them on a *freshness-paced* cadence — a `valid_until` of +6h on the staged
|
|
`pages`, not a blind heartbeat (invariant 5).
|
|
|
|
### Maintains
|
|
|
|
#### pages
|
|
The fetched source content per competitor, normalized to its stable main text.
|
|
Material: the content hash of a competitor's normalized text changed. Immaterial:
|
|
fetch timestamp, request ids, response headers, ads, and boilerplate.
|
|
|
|
### Schedule
|
|
|
|
- Re-fetch each competitor's funding, careers, and product pages when the staged
|
|
`pages` freshness window (+6h) lapses — the freshness-paced recheck for sources
|
|
that cannot push, not a fixed-interval poll.
|
|
```
|
|
|
|
The gateway does the cheap, deterministic half (fetch + normalize + hash); the
|
|
monitor does the expensive, semantic half (extract funding / hiring / launches),
|
|
and only when the gateway's `pages` fingerprint actually moves.
|
|
|
|
**A downstream consumer** subscribes to one facet, and wakes only on that facet:
|
|
|
|
```markdown
|
|
---
|
|
name: weekly-competitor-brief
|
|
kind: responsibility
|
|
id: 067NC4KG01RG50R40M30E20919
|
|
---
|
|
|
|
### Requires
|
|
|
|
- `funding` from `competitor-activity-monitor` # subscribes to ONE facet
|
|
|
|
### Maintains
|
|
|
|
A short brief summarizing the latest funding activity for the watchlist.
|
|
|
|
### Continuity
|
|
|
|
- Self-driven: a `valid_until` of +7 days, so the brief refreshes weekly even
|
|
if funding stays quiet.
|
|
```
|
|
|
|
What an ideal Reactor harness does with this:
|
|
|
|
- A gateway poll that finds **no changed source content** leaves the `pages`
|
|
fingerprint unmoved, so the monitor's extraction **memo-skips at zero render
|
|
tokens** (the gateway pays only a cheap, non-LLM fetch + hash) and the brief
|
|
never wakes — *cost scales with surprise.* When sources change but the funding
|
|
*facet* does not, the monitor renders, re-derives an unchanged `funding`
|
|
fingerprint, and still leaves the brief asleep.
|
|
- A new **hiring** signal moves only the `hiring` fingerprint; the brief
|
|
subscribes to `funding`, so it stays asleep — *facets make subscription
|
|
selective.*
|
|
- When `funding`'s `valid_until` safety net lapses, the continuity clock advances
|
|
the monitor's freshness epoch and wakes it with a zero-token self-receipt; the
|
|
monitor re-extracts from the latest staged `pages`, and only real news
|
|
propagates to the brief.
|
|
- A render that cannot cite a source for an entry fails its postcondition,
|
|
commits nothing, and leaves a `failed` receipt — the prior truth stands.
|
|
|
|
Two of these are ideal harness behavior the contract is authored to, not yet
|
|
delivered: forecast-paced freshness arming (today `serve` polls a flat interval)
|
|
and the gateway's `### Schedule` cadence (today dropped by the compiler) are Part
|
|
II deferrals — the contract above is correct; the harness climbs to it.
|
|
|
|
No `kind: system`, no `### Services`, no judge, no ledger service: the render
|
|
maintains the world-model, the canonicalizer senses change, `gateCommit` gates
|
|
the commit, and the signed receipt ledger is the trail.
|
|
|
|
### Anti-patterns
|
|
|
|
Each is the natural non-Reactor instinct and why it breaks an invariant:
|
|
|
|
- **Modeling the work as a graph of services instead of one responsibility.**
|
|
Inverts the model; the wiring becomes the source of intent (breaks
|
|
invariant 1). Start from the `### Maintains` truth, not a system.
|
|
- **A render with a `loop until done` that waits for events in-session.** That
|
|
is a long-running agent loop, not bounded activations (breaks invariant 4).
|
|
- **A `### Maintains` with no material/immaterial split, so every re-poll looks
|
|
changed.** The canonicalizer's fingerprint moves every cycle; the reconciler
|
|
can never skip and cost scales with the clock (breaks invariant 5). This is the
|
|
most common and most expensive mistake.
|
|
- **A contract whose only "did anything change" test is the full render, written
|
|
as if it memoizes.** Not an invariant-5 _correctness_ break (the continuity
|
|
clock still makes it safe) but a false cost claim. The Reactor-native form names
|
|
the absence of a cheap identity and accepts freshness-cadence cost
|
|
deliberately — exactly as projection-only is a deliberate choice, not a
|
|
degenerate Reactor.
|
|
- **Hand-coding a cadence in `### Execution`** instead of declaring the wake in
|
|
`### Continuity` (an input subscription or a `valid_until`). It hides the
|
|
schedule from the harness and defeats forecast-paced quiescence (breaks
|
|
invariant 5).
|
|
- **Putting volatile fields in `### Maintains`** — a poll timestamp, a request
|
|
id, a re-ordered list — so the fingerprint moves on noise (breaks invariant 5).
|
|
- **Consuming another responsibility's value by copying it into the contract.**
|
|
Not a verifiable, revision-pinned subscription; a supply-chain hole (breaks
|
|
Rule 6 composition and invariant 7's content-addressed receipts). Name the
|
|
facet in `### Requires` instead.
|
|
- **"Swarm of subagents that continuously watches."** The cron-plus-prompt shape
|
|
the Reactor replaces. The Reactor-native form is: a subscribed input or a
|
|
lapsed `valid_until` wakes one bounded render; nothing watches in between.
|
|
|
|
### Precedence for authors
|
|
|
|
When authoring rules tension, follow the harness precedence stack:
|
|
|
|
```text
|
|
correctness > safety > cost > interrupt-minimization
|
|
```
|
|
|
|
If a correct, safe contract surfaces more `failed` receipts to the author early,
|
|
accept them. If making a contract cheaper would make it unsafe (dropping a
|
|
postcondition on a high-stakes goal so it commits without checking), pay the
|
|
cost. Silence is a target, never a constraint the other rules bend around.
|
|
|
|
---
|
|
|
|
## II. What The OpenProse Skill Implements Today
|
|
|
|
The current model in Part I — intelligent compile (per-node canonicalizer +
|
|
Forme topology + postcondition validators, fired only on a contract-set change)
|
|
over a dumb deterministic run (compare `(contract_fingerprint, input_fingerprints)`
|
|
→ skip / render / `gateCommit` / propagate) — is the model the shipped skill
|
|
authors against. This section is the conformance ledger: what the skill routes
|
|
today, measured honestly against that pattern.
|
|
|
|
The reference harness backing the skill is the three packages that ship
|
|
together — `@openprose/reactor` (the engine SDK, `0.3.1`), `@openprose/reactor-cli`
|
|
(command `reactor`, `0.2.2`, thirteen commands), and `@openprose/reactor-devtools`
|
|
(the replay viewer, `0.2.0`). The skill's `responsibility-runtime.md` and
|
|
`concepts/{responsibility,reactor}.md` are already written to this model; the
|
|
retired judge/verdict/status/pressure/fulfillment vocabulary is gone from the
|
|
authoring surface, not merely deprecated, and the legacy `prose` CLI binary has
|
|
been removed (the language is embodied by the SKILL in-session).
|
|
|
|
### The authoring surface that already exists
|
|
|
|
The headline finding still holds: **the Reactor-native pattern requires no new
|
|
syntax** — but the syntax it requires is the *current* one. The skill routes the
|
|
kinds and sections Part I names, and only those.
|
|
|
|
| Pattern element | Skill support today |
|
|
| --- | --- |
|
|
| `kind: responsibility` with `### Goal` / `### Requires` / `### Maintains` / `### Continuity` / `### Invariants` (+ `### Tools` / `### Shape` / `### Runtime`) | Present and canonical (`concepts/responsibility.md`, `contract-markdown.md`); `id:` is tooling-minted and stable across `name:`/filename renames |
|
|
| `kind: function` with `### Parameters` / `### Returns` — a stateless called helper (the former `service`) | Present; run as a lone render with no Forme phase |
|
|
| `kind: gateway` — sugar for an external-driven responsibility; Forme's entry-point set | Present; `reactor serve` registers it and stages ingress (fetch → extract → stage + a durable idempotency cursor) |
|
|
| `kind: pattern` / `kind: test` | Present; patterns expand at compile time, tests route to the in-session `prose test` semantic (there is no `reactor test` subcommand) |
|
|
| Facets: `####` parts under `### Maintains` — name = fingerprint unit + subscription symbol; atomic default | Present and **facet-granular propagation is live in production** (the v2 named-parts model); a downstream subscribed to one facet does not wake when another moves |
|
|
| `### Maintains` as the world-model schema doing four jobs (type, canonicalization spec, facets, postconditions) | Present; the postconditions live **inside `### Maintains`**, compiled to validators (there is no separate `### Criteria` section — it was removed) |
|
|
| `### Continuity` as the structural wake-source declaration (input / self / external) | Present; self-driven recheck and the gateway entry point are both wired (Phase 4) |
|
|
| `### Execution` ProseScript for variable-depth work inside one render | Present — an `if`-gated `call` to a `function` is the depth mechanism, not a judge tier |
|
|
| Compile as SKILL-loaded sessions (Forme / canonicalizer / postcondition) → deterministic lowering → content-addressed IR cache | Present; a `.prose` set mounts without hand-authoring via a true semantic `Requires ↔ Maintains` match |
|
|
| Run: dumb reconciler — memo-skip on unmoved `(contract_fp, input_fp)`, single-flight + coalescing, failure = no-commit, propagate only on a `rendered` moved fingerprint | Present (`@openprose/reactor`); restart-survival proven (truth + ledger survive a fresh process) |
|
|
| `gateCommit`: deterministic postcondition validators + render self-attestation of `### Maintains`; receipt status in `{rendered, skipped, failed}` | Partial; no judge, no verdict, no status enum — but the commit rides the render's **self-attestation** today: the deterministic `gateCommit(...)` validators are built and tested yet have **zero live callers** (02-ReactorHarness gap cluster 1) |
|
|
| Content-addressed, chain-verifiable receipt ledger; cost = `tokens.fresh` vs `tokens.reused` + `surprise_cause` | Present (`reactor receipts` chain-verifies and tamper-detects; the devtools meter renders "cost scales with surprise") |
|
|
| Composition: a downstream responsibility names an upstream facet in `### Requires`; Forme draws the subscription edge | Present; the reconciler reads the topology `edges` to resolve propagation |
|
|
|
|
The skill can already express a Reactor-native program end to end *in the current
|
|
model*: one responsibility with a faceted `### Maintains`, a gateway for ingress,
|
|
a `function` helper for an expensive sub-step, an actuation boundary in
|
|
`### Invariants`, postcondition-gated commit, and a composition edge that is a
|
|
real subscription rather than a copied value. Two honest caveats the author must
|
|
hold (see limits): the `### Invariants` actuation boundary is **authored but not
|
|
yet harness-enforced** — it is never lowered into the render, so it constrains the
|
|
model only as prose; and the postcondition gate currently rides the render's
|
|
self-attestation, not the deterministic `gateCommit(...)` validators.
|
|
|
|
### Current authoring limits
|
|
|
|
Part I states the pattern as an unqualified north star. This is where authoring
|
|
reality is honest, rule by rule.
|
|
|
|
| Authoring rule (Part I) | State | What the author must currently know |
|
|
| --- | --- | --- |
|
|
| 1. Intent lives in the responsibility | Conformant | Fully authorable today |
|
|
| 2. Materiality stated semantically in `### Maintains` | Conformant (authoring) | The author states materiality in prose; it is lowered to a deterministic canonicalizer. The lowering runs **only spec→code** — a compile session reads the `### Maintains` prose and emits the canonicalization spec, which the deterministic producer compiles. There is no `.prose` *parser*; compile is sessions, not a grammar |
|
|
| 3. No host-specific contract logic | Conformant (authoring); harness resolution deferred | `### Tools` (`cli:` / `mcp:`), `### Environment`, and `### Skills` are authored name-only and never install or contact at compile — but the Reactor harness does not yet **consume** them: compile reads only `### Requires`/`### Maintains`/`### Continuity`/`### Execution`, and the render runs a fixed built-in toolset, so declared capability names are inert today. Fail-closed resolution/enforcement of the declared surface is a forward item (02 Part III §10) |
|
|
| 4. Bounded activations | Conformant | Idiomatic; the "loop until done" anti-pattern is author error, not a skill gap |
|
|
| 5. Written for memoization / variable depth | Conformant | The reconciler's skip on `(contract_fp, input_fp)` is live (cost scales with surprise, including an immaterial-churn re-poll that still skips); facet selectors are live; depth is an `if`-gated `call` in `### Execution`. The author's leverage is real today |
|
|
| 6. Composition via a subscribed upstream facet | Conformant (meaning-layer) | A downstream names the upstream facet in `### Requires` and Forme wires the edge; receipts are content-addressed and the chain is verifiable. The **cryptographic** signer is a null-state — v1 "signed" is meaning-layer chain-consistency, not a byte-hash — so cross-trust-domain *pinning to a signer set* is not yet enforceable |
|
|
| 7. Receipts as the audit / composition / exit unit | Conformant | The ledger is flat `<state-dir>/receipts.json`, content-addressed, chain-verifiable; `cost` (fresh/reused/surprise_cause) and `status` are first-class. No hand-rolled scratch log is needed |
|
|
| 8. Replayable and exitable | Conformant | Contract + world-model + ledger are plain and portable; `reactor-devtools <state-dir>` replays a saved run with zero running reactor and zero key |
|
|
|
|
### Honest current limits for authors
|
|
|
|
- **`### Continuity` self-driven recheck exists, but `serve`'s freshness clock
|
|
arms nothing by default.** The bridge is real (a lapsed `valid_until` flips a
|
|
fact's status, moves the facet fingerprint, and wakes the node — a **zero-token**
|
|
fingerprint move, not a model re-render), and the SDK computes each node's
|
|
soonest `next_self_recheck`. But the shipped `serve` daemon's freshness reader
|
|
defaults to none and no node emits `valid_until` by default, so it sleeps a flat
|
|
`--poll-interval` (default 60s) and does the fixed-interval work the ideal
|
|
forbids. Forecast-paced / adaptive idle is the deferred next step. Declare
|
|
`valid_until` now; the cadence tightens later without a source change.
|
|
- **The `### Invariants` actuation boundary is authored, not enforced.** The
|
|
authored rate/scope/prohibited-action quarantine is never lowered into the
|
|
render — neither compiled, attested, nor harness-checked — so it constrains the
|
|
model only as prose, bounded in practice by the cwd-rooted workspace sandbox and
|
|
the turn cap. There is also no world-mutation actuation sink yet (render tools
|
|
are fs/shell over a private workspace; connectors are read-only ingress).
|
|
- **A gateway's `### Schedule` cadence is not yet honored.** Gateways poll
|
|
external sources, but a per-gateway `### Schedule` (e.g. "every 6h") is dropped
|
|
by the compiler today; cadence is the serve loop's flat poll interval. Declare
|
|
the intended schedule now; it binds once the compiler carries it.
|
|
- **The deterministic commit gate is unwired.** `compilePostconditions(...)`
|
|
runs on the compile path, but the gate that evaluates it, `gateCommit(...)`, has
|
|
zero live callers; the commit rides the render's `### Maintains` self-attestation
|
|
(02-ReactorHarness gap cluster 1).
|
|
- **Serve ingress is local cron-poll + HTTP only.** Gateway poll connectors and
|
|
an HTTP trigger surface ship; **queues, file watches, and provider
|
|
subscriptions** do not. A worktree/planning-dir watcher must use a poll
|
|
`### Continuity` today and note the intended file-watch form.
|
|
- **The cryptographic signer is a null-state, and the dependency edge pins no
|
|
signer set.** Composition pins a content-addressed *revision* and is
|
|
chain-verifiable at the meaning layer, but the topology edge carries no
|
|
*acceptable-signer-set* pin, and the byte-hash signer that would enforce one is
|
|
deferred. Functionally adequate within one trust domain; cryptographically
|
|
weaker than the ideal across domains.
|
|
- **Materiality is authored, not yet inferred.** The author writes the
|
|
material/immaterial split in `### Maintains` prose and the compile session
|
|
lowers it. The skill does **not** infer facets or materiality from the truth's
|
|
shape; that inference loop is deferred. The store also does not materialize a
|
|
per-facet `published/<facet>/…` subtree on disk (it persists `published.json`
|
|
+ content-addressed blobs) — facet-granular propagation holds regardless; only
|
|
the on-disk subtree mirror is absent.
|
|
|
|
> The pattern is fully writable today, in the current model. Its remaining
|
|
> climb is harness cadence and cryptographic hardening (forecast-paced rechecks,
|
|
> richer ingress adapters, the byte-hash signer), not retired vision. Author to
|
|
> the ideal; do not claim the runtime delivers the deferred items it does not.
|
|
|
|
---
|
|
|
|
## III. What Must Change In The Skill
|
|
|
|
The retired-model framings are gone: the skill already routes the current kinds
|
|
and sections, the examples are migrated, and `concepts/{responsibility,reactor}.md`
|
|
teach the no-judge model. What remains is the climb from "fully authorable in the
|
|
current model" to "every Part I payoff is also *delivered*" — a mix of finishing
|
|
the authoring story for the newest sections and the genuine harness deferrals.
|
|
|
|
### 1. Finish the facet authoring story
|
|
|
|
Facet propagation is live and the named-parts model is canonical, but two seams
|
|
are open:
|
|
|
|
- **Facet inference is deferred.** Today the author writes the `####` parts and
|
|
the material/immaterial split by hand. The skill should eventually offer a
|
|
lint that flags an under-faceted `### Maintains` (one giant atomic truth whose
|
|
consumers clearly want selectors) — and, post-v1, an inference pass that
|
|
proposes facets from the truth's shape.
|
|
- **The on-disk subtree mirror is absent.** The store fingerprints each facet's
|
|
material field-paths and persists `published.json` + content-addressed blobs;
|
|
it does not materialize a `published/<facet>/…` subtree. Author doctrine
|
|
should not promise a subtree on disk — describe facets as the *subscription
|
|
and fingerprint* unit, which is what actually ships.
|
|
|
|
### 2. Complete the no-judge postcondition doctrine in `contract-markdown.md`
|
|
|
|
`### Maintains` now carries four jobs (type, canonicalization, facets,
|
|
postconditions) and `### Continuity` is a structural wake-source declaration.
|
|
These are documented in `concepts/responsibility.md`; `contract-markdown.md`
|
|
should mirror the load they bear:
|
|
|
|
- `### Continuity` is the author's input to memoization and cadence — name the
|
|
freshness referents (`valid_until`) and the wake sources (input / self /
|
|
external), and make "nothing changed" cheaply observable.
|
|
- Postconditions are the author's commit gate. State them as observable
|
|
referents; when the truth has *nothing observable to maintain against*, that
|
|
is an expected, high-value `failed` receipt routed to the author — **never** a
|
|
`blocked`/`drifting` status enum (those are retired).
|
|
- The failed receipt that *names the gap and routes it to the author* depends on
|
|
the harness widening the durable receipt with `as_of`, a `reason`, and an
|
|
author-addressing field (02 gap cluster 2); until that lands, the doctrine is
|
|
authorable but the routed reason is dropped at commit. Author to it now.
|
|
|
|
### 3. Land the materiality lowering's prose→spec half
|
|
|
|
Compile runs as SKILL-loaded sessions and the **spec→code** canonicalizer
|
|
lowering is real (a compile session emits the canonicalization spec; the
|
|
deterministic producer compiles it). The remaining work is hardening the
|
|
**prose→spec** step — reading the author's `### Maintains` materiality prose into
|
|
the spec reliably — so that "state the materiality, not the hash" is trustworthy
|
|
across more contract shapes. This is a compile-session quality effort, not a new
|
|
grammar; there is no `.prose` parser to build.
|
|
|
|
### 4. Wire the deterministic commit gate
|
|
|
|
The author states satisfaction as postconditions inside `### Maintains`, and the
|
|
compile phase lowers them to deterministic validators (`compilePostconditions`
|
|
runs on the compile path). But the gate that would evaluate them at commit,
|
|
`gateCommit(...)`, has zero live callers — the commit rides the render's own
|
|
`### Maintains` self-attestation. So the author's postconditions are *compiled*
|
|
but not yet the *enforced* commit gate. Threading the compiled validators onto
|
|
the live commit step (02 gap cluster 1) makes Rule 3's "no attestation, no
|
|
commit" a deterministic guarantee rather than a render self-report. The author
|
|
writes postconditions to it now; the enforcement tightens harness-side.
|
|
|
|
### 5. Lower and enforce the `### Invariants` actuation boundary
|
|
|
|
Rule 4's actuation quarantine is authored prose today: `### Invariants` declares
|
|
the rate/scope/prohibited-action bounds, but the section is never lowered into
|
|
the render — it constrains the model only as prose, bounded in practice by the
|
|
cwd-rooted workspace sandbox and the turn cap, with no world-mutation actuation
|
|
sink. Lowering it into the render (02 gap cluster 3) makes a projection-only
|
|
contract's "the only writable surface is the published truth" a harness-enforced
|
|
guarantee rather than an author's request. Author the boundary now; it binds once
|
|
the harness lowers it.
|
|
|
|
### 6. Forecast-paced continuity cadence
|
|
|
|
The self-driven recheck path is wired (freshness-lapse → synthetic self-receipt,
|
|
a zero-token fingerprint move), but `reactor serve` polls it on a flat
|
|
`--poll-interval`. The deferred work is arming each node's soonest
|
|
`next_self_recheck` off its freshness so an idle reactor sleeps to the next real
|
|
expiry instead of waking every interval — the *forecast-paced quiescence* Part I
|
|
implies. The same step is owed for a gateway's declared `### Schedule`, which the
|
|
compiler drops today (so the gateway runs on the flat serve poll, not its
|
|
authored cadence); honoring it as a freshness-paced cadence rides with this work.
|
|
The author already writes the `valid_until` (and the `### Schedule`) that feeds
|
|
it; the cadence tightens harness-side, without a source change.
|
|
|
|
### 7. Richer serve ingress adapters
|
|
|
|
`reactor serve` supports local cron-poll and HTTP, plus gateway poll connectors
|
|
with a durable idempotency cursor. Queues, file watches, provider subscriptions,
|
|
and webhook authentication remain later runtime phases. Until they land, a
|
|
file-watch-shaped responsibility is authored as a poll `### Continuity`; the
|
|
intended ingress form should be noted in the contract so it migrates cleanly.
|
|
|
|
### 8. The cryptographic signer (cross-trust-domain composition)
|
|
|
|
The receipt ledger is content-addressed and chain-verifiable, and the `signer`
|
|
is an explicit null-state (`{ scheme: "none", null_reason: ... }`; the signature
|
|
union ships only the null branch). v1 "signed" means meaning-layer
|
|
chain-consistency. The deferred milestone is the
|
|
cryptographic byte-hash signer, after which a downstream can pin an *acceptable
|
|
signer set* in `### Requires` and have it enforced — closing the cross-trust-domain
|
|
supply-chain edge that is, today, the author's discipline rather than the
|
|
runtime's guarantee.
|
|
|
|
### Definition of done for the authoring layer
|
|
|
|
- `contract-markdown.md` documents `### Continuity` as the cadence/memo input
|
|
and `### Maintains` postconditions as the commit gate, with the
|
|
retired-status-enum doctrine explicit.
|
|
- A facet under-faceting lint exists; doctrine never promises an on-disk
|
|
`published/<facet>/…` subtree.
|
|
- The prose→spec materiality lowering is hardened across the example corpus.
|
|
- The compiled postconditions are wired onto the live commit (`gateCommit`), so
|
|
the author's `### Maintains` postconditions are the enforced commit gate, not
|
|
only the render's self-attestation.
|
|
- The `### Invariants` actuation boundary is lowered and harness-enforced, so a
|
|
projection-only contract's "only the published truth is writable" is a
|
|
guarantee, and the failed receipt carries the routed `reason`.
|
|
- `reactor serve` arms `next_self_recheck` (forecast-paced cadence) so an idle
|
|
reactor sleeps to the next real expiry.
|
|
- The cryptographic signer lands and a pinned signer set is enforceable for
|
|
cross-trust-domain composition.
|
|
|
|
### Open authoring items
|
|
|
|
Deferred by design, tracked so they are neither invented nor dropped:
|
|
|
|
1. **The fixpoint (topology-as-responsibility).** Mounting the compile-phase
|
|
renders — Forme above all — as ordinary nodes so the reconciler wakes them on
|
|
a contract-set-change receipt, and the system maintains itself, is the
|
|
closing recursion. It is **post-v1** by design; v1 runs compile as a batch
|
|
step on contract change with the topology a fixed input per scheduling epoch.
|
|
This is the deliberate deferral, not an oversight.
|
|
2. **Facet / materiality inference.** The author states facets and materiality
|
|
today; an inference loop that proposes them from the truth's shape is
|
|
v-next.
|
|
3. **Ledger compaction.** A signed-snapshot-plus-truncate path for high-churn
|
|
nodes is deferred; today the ledger is the full append-only trail.
|
|
4. **The default `valid_until` freshness projector for serve.** A first-class
|
|
serve-time projector that surfaces soonest-expiry across nodes (the input to
|
|
adaptive idle) rides with the forecast-paced cadence work above.
|
|
|
|
> `02-ReactorHarness.md` says what the machine must do.
|
|
> `03-ReactorPattern.md` says what to write so it does it. The pattern is
|
|
> fully writable now in the current model; its full power lands as the harness
|
|
> climbs from flat-poll continuity and a null-state signer to forecast-paced
|
|
> rechecks and a cryptographic chain — and, last, to the fixpoint. Author to the
|
|
> ideal, document the climb honestly.
|