Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 41 additions & 19 deletions docs/vrs/01-ding/requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ is in [`spec.md`](./spec.md).

## Assumptions

- **DING-A01 Rendered screens only:** The only available evidence about a
composer's state is a rendered terminal screen. No maintained harness exposes
an evented idle signal, so every precondition below is a measured heuristic
over text. `DQ2` in [`../spec.md`](../spec.md) tracks closing that gap.
`DING-A01 Rendered screens only` is retired. Rendered-screen evidence is a
limit of the legacy `ding` transport. It is not an assumption for a maintained
harness that declares a native `deliver` transport.

- **DING-A02 Cooperative human:** The human sharing a pane is not adversarial.
A screen that deliberately imitates another harness's composer is a
correctness concern, not a security boundary, consistent with `A02`.
Expand All @@ -41,21 +41,43 @@ is in [`spec.md`](./spec.md).

## Requirements

### Must preserve initial transport and gate every retry

- **DING-R01 Combined initial transport:** A fresh notice uses one bounded PTY
transaction containing the bracketed paste, the accepted 0.5 second delay,
and Return. Ownership is recorded before that command starts. Composer
heuristics do not split or suppress this initial transport.
- **DING-R02 Two adjacent retained-safe retry observations:** A later bare
Return is permitted only for a transport-owned payload whose exact notice is
still the complete composer and is classified `RetainedSafe` in two
immediately adjacent inspections. The final observation is adjacent to the
Return itself. Any change, block, or uncertainty prevents retry submission.
- **DING-R03 Fail-closed receipt and retry:** After the initial transport, a
changed composer, a human draft, an active turn, a modal, an unreadable
screen, an unrecognized harness, and a bounded observation timeout never
become `Delivered` and receive no retry input. Anything not positively
### Must select one explicit transport

- **DING-R11 Declared transport:** An agent selects at most one delivery
transport. `ding` selects the legacy screen transport. `deliver "mcp"`
selects the Claude native transport. `deliver "app-server"` selects the
Codex native transport. A declaration with neither node has no DING delivery.
A declaration with both nodes, multiple `deliver` nodes, or an unsupported
`deliver` value is invalid.
- **DING-R12 No transport inference:** st2 does not infer a native transport
from an agent command or a screen. Native delivery uses the declared adapter.
A binary that supports `deliver` validates its value and its mutual exclusion
with `ding`.
- **DING-R13 Durable native delivery:** The inbox file remains the source of
truth for native delivery. Only the adapter's declared success condition can
complete a delivery attempt. A closed, unavailable, stale, or unknown native
transport leaves the message unread and retryable. Archive precedence and
restart recovery remain unchanged.
- **DING-R14 Missing transport report:** Doctor reports an active agent that
declares neither `ding` nor `deliver`. The omission remains a valid opt-out
and does not block the agent. The report makes a no-delivery state visible.

### Must preserve legacy transport and gate every legacy retry

- **DING-R01 Combined initial transport:** A fresh legacy notice uses one
bounded PTY transaction containing the bracketed paste, the accepted 0.5
second delay, and Return. Ownership is recorded before that command starts.
Composer heuristics do not split or suppress this initial transport.
- **DING-R02 Two adjacent retained-safe retry observations:** A later legacy
bare Return is permitted only for a transport-owned payload whose exact
notice is still the complete composer and is classified `RetainedSafe` in
two immediately adjacent inspections. The final observation is adjacent to
the Return itself. Any change, block, or uncertainty prevents retry
submission.
- **DING-R03 Fail-closed receipt and retry:** After the initial legacy
transport, a changed composer, a human draft, an active turn, a modal, an
unreadable screen, an unrecognized harness, and a bounded observation timeout
never become `Delivered` and receive no retry input. Anything not positively
understood retains staged ownership. On an inspect-only staged retry, a
maintained adapter may positively prove that the exact owned payload is no
longer retained; that proof relinquishes ownership only when an archive
Expand Down
61 changes: 49 additions & 12 deletions docs/vrs/01-ding/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,42 @@ specified in [`01-claude/spec.md`](./01-claude/spec.md) and
Active. A map to the implementation and its evidence, not a replacement for the
tests.

## Delivery selection

Delivery is opt-in. An agent declaration selects one transport:

| Declaration | Transport |
| --- | --- |
| `ding` | Legacy screen transport |
| `deliver "mcp"` | Claude native MCP transport |
| `deliver "app-server"` | Codex native app-server transport |
| Neither node | No delivery |

`ding` and `deliver` are mutually exclusive. More than one `deliver` node is
invalid. Any other `deliver` value is invalid. st2 does not infer a transport
from the agent command because command arguments are opaque.

The native selector is a new `deliver` node. A binary released before this
contract ignores that unknown agent child. It lowers a valid `deliver`-only
agent with no delivery sidecar. The agent receives no DING. It does not silently
use the legacy screen transport. This is a visible delivery outage.

A binary that supports `deliver` validates its value and its mutual exclusion
with `ding`. Native delivery must not be encoded as an argument to `ding`,
because a pre-change parser would accept that form as legacy `ding` and use the
wrong transport. Doctor reports an active agent that declares no transport. The
report does not make the valid no-delivery opt-out an error.

The durable inbox is the source of truth for every transport. An archive with
the same message name wins. A native adapter completes delivery only after its
declared provider-specific success condition. If the adapter is closed,
unavailable, stale, or in an unknown state, it sends no unsafe input and leaves
the inbox message unread for retry. Native adapters do not use the
rendered-screen classifier.

The rest of this document defines the unchanged legacy screen transport. The
native wire contracts are in each maintained harness specification.

## Composer states

One inspection of a rendered screen, evaluated against one exact expected
Expand Down Expand Up @@ -45,11 +81,11 @@ staged retry ─► receipt ─┬─ Accepted ───────────
└─ RetainedBlocked / Unproven ─────────────► Staged
```

Fresh delivery preserves the production transport: one bounded PTY transaction
contains a bracketed paste, a 0.5 second delay, and Return (`DING-R01`).
Ownership is recorded immediately before that transaction. The production path
does not inspect the composer first and does not use the separate staging
helper.
Fresh legacy delivery preserves the production transport: one bounded PTY
transaction contains a bracketed paste, a 0.5 second delay, and Return
(`DING-R01`). Ownership is recorded immediately before that transaction. The
production path does not inspect the composer first and does not use the
separate staging helper.

Every failure of that terminal command or of the following receipt observation
resolves to `Staged` (`DING-R07`): the paste and Return may already have reached
Expand Down Expand Up @@ -148,10 +184,11 @@ Declared `busy` never suppresses delivery; only fresh `dnd` defers it

## Known limits

- Idle proof depends on footer chrome that a harness may render differently
across permission or approval modes. A harness whose footer is not recognized
in a given mode defers indefinitely rather than delivering. This is an
explicit limit per `T01`, and each harness spec states which modes it proves.
- The classifier is a measured heuristic over rendered text, not an evented
signal, so a renderer change can defer delivery until the grammar is updated.
Tracked as `DQ2` in [`../spec.md`](../spec.md).
- Legacy idle proof depends on footer chrome that a harness may render
differently across permission or approval modes. A harness whose footer is
not recognized in a given mode defers indefinitely rather than delivering.
This is an explicit limit per `T01`, and each harness spec states which modes
it proves.
- The legacy classifier is a measured heuristic over rendered text, not an
evented signal, so a renderer change can defer legacy delivery until the
grammar is updated. Maintained native transports do not use this classifier.
18 changes: 11 additions & 7 deletions docs/vrs/02-agent-spec/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,10 +358,13 @@ Authoring: [pinned complete declaration][evals-fields]. st2 source:

<h3 id="f14">F14 Compact agent fields</h3>

Compact `command`, `argv`, `env`, `lifecycle`, and `ding` convert to the
generated agent PTY and derived sidecar. The tasks use F09, F11, and F12;
`ding` carries the dependency on the generated agent task described there.
Compact syntax adds no other behavior.
Compact `command`, `argv`, `env`, and `lifecycle` define the generated agent
PTY. Bare `ding` selects the derived legacy screen sidecar. `deliver "mcp"`
selects native Claude delivery. `deliver "app-server"` selects native Codex
delivery. The two selector nodes are mutually exclusive. More than one
`deliver` node and any other value are invalid. Neither node means no delivery.
Each generated transport depends on the generated agent task. The tasks use
F09, F11, and F12. Compact syntax adds no other behavior.

Authoring: [pinned compact tasks][evals-tasks]. That document and st2 `9887b28`
predate compact `argv` and `lifecycle`. Current st2 source:
Expand All @@ -370,9 +373,10 @@ predate compact `argv` and `lifecycle`. Current st2 source:

<h3 id="f15">F15 Provider and ignored fields</h3>

Core st2 ignores `harness`, `model`, `persona`, `permissions`, `transport`,
`strategy`, `meta`, and provider extensions. They do not change core equality,
wake behavior, or actions. Providers may convert them into F05 through F14;
Core st2 ignores `harness`, `model`, `persona`, `permissions`, the legacy
render-only `transport` field, `strategy`, `meta`, and provider extensions.
They do not change core equality, wake behavior, or actions. Providers may
convert them into F05 through F14;
core acts only on that concrete output.

Authoring: [pinned complete declaration][evals-fields]. st2 source:
Expand Down
9 changes: 6 additions & 3 deletions docs/vrs/ontology.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,12 @@ Authority: [`message::Message`](../../src/message.rs#L26-L46)

### DING

The terminal notification that makes an agent aware of unread messages.
The delivery signal that makes an agent aware of unread messages. A declaration
selects either the legacy terminal transport or one provider-native control
transport.

Authority: [DING module contract](../../src/ding/mod.rs#L1-L14)
Authority: [DING specification](./01-ding/spec.md) and
[DING module contract](../../src/ding/mod.rs#L1-L14)

## Collision rules

Expand All @@ -172,7 +175,7 @@ Authority: [DING module contract](../../src/ding/mod.rs#L1-L14)
value and [bus ID](../../crates/agent-spec/src/spec.rs#L203-L211) for the
host-qualified address.
- Use [message](../../src/message.rs#L26-L46) for the durable record and
[DING](../../src/ding/mod.rs#L1-L14) for its terminal notification.
[DING](./01-ding/spec.md) for its delivery signal.

## Avoid

Expand Down
5 changes: 4 additions & 1 deletion docs/vrs/requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ accepted.
- **R05 DING/archive semantics:** Inbox delivery, archive precedence, retries,
suppression, and restart recovery are deterministic and tested. DING may
interrupt agent work, but it must not alter or submit a human's active draft;
an unknown interaction state defers delivery.
an unknown interaction state defers delivery. A declaration selects either
the legacy screen transport or one supported native transport. st2 does not
infer a native transport from an opaque command. A failed or unavailable
transport leaves the inbox message unread and retryable.
- **R06 Restartable launch definitions:** A restarted PTY or exec receives the
complete effective launch definition, including environment and supported
launch fields.
Expand Down
37 changes: 28 additions & 9 deletions docs/vrs/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,34 @@ atomic inbox file → DING attempt → agent reads → archive receipt
composer cannot create a short-lived PTY probe on every inbox poll. Inbox
reads do not wake the sidecar; only mutations bypass its bounded poll cadence.

### Declared native DING delivery (DQ2 resolution)

An agent selects at most one delivery transport. `ding` keeps the legacy
screen transport unchanged. `deliver "mcp"` selects native Claude delivery.
`deliver "app-server"` selects native Codex delivery. An agent with neither
node has no delivery. The two node forms are mutually exclusive, multiple
`deliver` nodes are invalid, and an unknown `deliver` value is invalid.

The transport is explicit because an agent command is opaque. A binary released
before this contract ignores the unknown `deliver` child. It lowers a valid
`deliver`-only declaration with no delivery sidecar. The agent receives no DING
instead of receiving a DING through the legacy screen transport. This is a
visible delivery outage. A binary that supports `deliver` validates its value
and its mutual exclusion with `ding`.

Native delivery is not an argument to `ding`, which a pre-change parser would
accept as legacy delivery and route through the wrong transport. Doctor reports
an active agent that declares neither transport. The no-delivery form remains a
valid opt-out and does not block the agent.

Native delivery replaces rendered-screen inference for maintained Claude and
Codex agents that select it. Each native adapter uses its provider's evented
control channel and its provider-specific success condition. A closed,
unavailable, stale, or unknown channel sends no unsafe input. The durable inbox
message remains unread and retryable. Archive precedence, suppression, and
restart recovery remain the same for every transport. The legacy classifier
remains available only for agents that select `ding`.

## State and scope

- **R08:** Presence and activity status are separate signals. The catalog must
Expand Down Expand Up @@ -785,15 +813,6 @@ the resident supervisor continues to reconcile the complete local catalog.
boundary, and execution receipts are not yet specified. A successful
executable eval and Nathan's approval should resolve this before adding
scheduler requirements.
- **DQ2 Safe DING delivery:** Bounded observation now replaces the fixed
paste-to-Return delay: maintained Codex and Claude composers must be
positively empty before paste and show the exact staged notice twice before
a separate Return. Human, modal, active, changed, timed-out, and unknown
states fail closed, with staged-payload ownership preventing duplicate paste.
This measured screen heuristic is still not an evented proof and renderer
changes may defer delivery. Resolve the remaining gap with a stronger evented
signal or other measured classifier; a small on-device model is an optional
experiment, not a required architecture.
- **DQ3 Catalog agent state:** Define the catalog paths, schemas, freshness
rules, and atomic update semantics for presence, activity status, current
plan, and current plan step. Prove that stale state is distinguishable and
Expand Down
Loading