@astrosheep/square 0.3.93 → 0.3.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activity-feed.d.ts +3 -5
- package/dist/activity-feed.js +16 -14
- package/dist/artifact.js +3 -3
- package/dist/attention.d.ts +19 -0
- package/dist/attention.js +30 -0
- package/dist/boundary-presentation.d.ts +2 -2
- package/dist/boundary-presentation.js +33 -40
- package/dist/catch-decisions.d.ts +5 -8
- package/dist/catch-decisions.js +45 -25
- package/dist/claude-delivery-status.js +10 -4
- package/dist/claude-delivery.js +19 -11
- package/dist/claude-mod.js +24 -53
- package/dist/cli/context.d.ts +9 -4
- package/dist/cli/context.js +30 -27
- package/dist/cli/express-presentation.d.ts +3 -1
- package/dist/cli/express-presentation.js +28 -16
- package/dist/cli/json.d.ts +3 -0
- package/dist/cli/json.js +14 -0
- package/dist/cli/mcp-server-command.js +2 -2
- package/dist/cli/observation-commands.d.ts +1 -3
- package/dist/cli/observation-commands.js +43 -56
- package/dist/cli/program.js +3 -3
- package/dist/cli/registry.js +7 -6
- package/dist/cli/square-commands.d.ts +0 -2
- package/dist/cli/square-commands.js +30 -44
- package/dist/codex-hook.d.ts +3 -2
- package/dist/codex-hook.js +13 -4
- package/dist/codex-queue.js +4 -4
- package/dist/decisions.d.ts +5 -16
- package/dist/decisions.js +9 -33
- package/dist/delivery-attempts.d.ts +52 -0
- package/dist/delivery-attempts.js +73 -0
- package/dist/delivery-deferral.js +2 -2
- package/dist/delivery-effects.d.ts +139 -0
- package/dist/delivery-effects.js +163 -0
- package/dist/delivery-health.d.ts +0 -1
- package/dist/delivery-health.js +1 -3
- package/dist/delivery-operations.d.ts +2 -2
- package/dist/delivery-operations.js +76 -101
- package/dist/delivery-primitives.d.ts +5 -0
- package/dist/delivery-primitives.js +3 -0
- package/dist/delivery.d.ts +1 -1
- package/dist/delivery.js +7 -6
- package/dist/help.js +14 -14
- package/dist/http-delivery.d.ts +16 -0
- package/dist/http-delivery.js +101 -0
- package/dist/inbox.d.ts +9 -22
- package/dist/inbox.js +23 -27
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +3 -0
- package/dist/mcp.js +23 -24
- package/dist/model.d.ts +12 -21
- package/dist/model.js +5 -8
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.js +8 -5
- package/dist/operation-presentation.d.ts +6 -6
- package/dist/operation-presentation.js +55 -58
- package/dist/owner.d.ts +11 -0
- package/dist/owner.js +32 -0
- package/dist/paseo-delivery.d.ts +4 -1
- package/dist/paseo-delivery.js +7 -12
- package/dist/paseo-state.d.ts +2 -2
- package/dist/paseo-state.js +6 -4
- package/dist/plugins/opencode/delivery.js +27 -15
- package/dist/ports.d.ts +18 -47
- package/dist/presentation-operations.d.ts +14 -19
- package/dist/presentation-operations.js +51 -61
- package/dist/presentation.d.ts +7 -17
- package/dist/presentation.js +46 -91
- package/dist/receive.d.ts +66 -0
- package/dist/receive.js +54 -0
- package/dist/runtime.d.ts +1 -8
- package/dist/runtime.js +2 -17
- package/dist/seats.d.ts +2 -3
- package/dist/seats.js +7 -15
- package/dist/session-lifecycle.d.ts +6 -2
- package/dist/session-lifecycle.js +39 -11
- package/dist/sqlite-square.d.ts +36 -23
- package/dist/sqlite-square.js +162 -211
- package/dist/square-api.d.ts +16 -11
- package/dist/square-api.js +27 -23
- package/dist/square-core.d.ts +0 -14
- package/dist/square-core.js +0 -9
- package/dist/square-domain.d.ts +4 -2
- package/dist/square-domain.js +65 -52
- package/dist/square-facade.d.ts +47 -105
- package/dist/square-index.d.ts +5 -13
- package/dist/square-index.js +13 -38
- package/dist/square-legacy-framing.d.ts +2 -0
- package/dist/square-legacy-framing.js +8 -0
- package/dist/square-location.d.ts +1 -14
- package/dist/square-location.js +4 -70
- package/dist/square-migration-host-ledger.d.ts +2 -2
- package/dist/square-migration-host-ledger.js +6 -5
- package/dist/square-migration.d.ts +4 -1
- package/dist/square-migration.js +93 -38
- package/dist/square-receive.d.ts +6 -0
- package/dist/square-receive.js +18 -0
- package/dist/square-sqlite.d.ts +9 -3
- package/dist/square-sqlite.js +139 -114
- package/dist/square-tools.d.ts +7 -10
- package/dist/square-tools.js +21 -61
- package/dist/stream.js +4 -5
- package/dist/views.d.ts +23 -13
- package/dist/views.js +47 -53
- package/dist/wakes.d.ts +1 -5
- package/dist/wakes.js +4 -6
- package/examples/symmetric-domains.mjs +250 -0
- package/guides/application-facade-delivery-evidence.md +6 -6
- package/guides/attention-primitive.md +32 -0
- package/guides/change-waits.md +2 -2
- package/guides/claude-native.md +18 -9
- package/guides/codex-delivery.md +17 -6
- package/guides/delivery-primitives.md +180 -0
- package/guides/mcp-connection-lifetime.md +1 -1
- package/guides/mcp.md +3 -3
- package/guides/migration.md +148 -151
- package/guides/operation-cancellation.md +1 -1
- package/guides/participant-ownership.md +78 -47
- package/guides/pi-tools.md +34 -5
- package/guides/primitive-catch-consolidation.md +5 -5
- package/guides/primitive-history-consolidation.md +2 -2
- package/guides/primitive-status-consolidation.md +3 -3
- package/guides/primitive-storage-consolidation.md +1 -1
- package/guides/receive-primitive.md +90 -0
- package/guides/row-runtime-delivery-evidence.md +1 -1
- package/guides/row-storage-design.md +22 -28
- package/guides/square-api.md +314 -91
- package/guides/symmetric-domains.md +92 -0
- package/package.json +14 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/skills/square/SKILL.md +2 -6
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/delivery.ts +27 -17
- package/plugins/paseo/client/palette.ts +18 -18
- package/plugins/paseo/client/square-message.tsx +17 -23
- package/plugins/pi/square-pi.js +17 -17
- package/skills/square/SKILL.md +2 -6
- package/dist/square-backend.d.ts +0 -13
- package/dist/square-backend.js +0 -1
- package/dist/square-stdio.d.ts +0 -22
- package/dist/square-stdio.js +0 -235
- package/dist/stdio-server.d.ts +0 -3
- package/dist/stdio-server.js +0 -212
- package/guides/backends.md +0 -193
package/guides/square-api.md
CHANGED
|
@@ -1,26 +1,36 @@
|
|
|
1
1
|
# Square and Participant API
|
|
2
2
|
|
|
3
|
-
`Square`
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
`Square` opens the concrete local SQLite resource shared by SDK, CLI, MCP and Pi. `square-api.ts`
|
|
4
|
+
exports the public `{at, build, inMemory}` entrypoints and `type Square`; `square.ts` remains the
|
|
5
|
+
executable CLI entrypoint.
|
|
6
|
+
For audience computation without opening a square, see the [attention primitive](attention-primitive.md).
|
|
7
|
+
For transaction-scoped attempt custody and the storage-independent dispatch/prepared presentation
|
|
8
|
+
lifecycles used by Square's actual adapters, see the [delivery primitives](delivery-primitives.md).
|
|
9
|
+
Full presentation settlement atomically completes its captured attempt and full-consumption receipts;
|
|
10
|
+
clipped previews record no full receipt. Cancellation/deferral is pre-effect policy, never a veto of a
|
|
11
|
+
successful sink. The public primitive distinguishes sink observations from refused/failed persistence;
|
|
12
|
+
Square retains its existing result shapes and explicit failed classification for rejected sinks.
|
|
13
|
+
For caller-owned item admission and sparse consumption, see the [receive primitive](receive-primitive.md).
|
|
6
14
|
`square-facade.ts` contains only public types. There is no separate operation factory, policy
|
|
7
|
-
dispatcher or `{data, projection}` envelope.
|
|
8
|
-
URI encoding, the versioned protocol and explicit remote limitations.
|
|
15
|
+
dispatcher or `{data, projection}` envelope.
|
|
9
16
|
|
|
10
17
|
## Open, select, join, close
|
|
11
18
|
|
|
12
19
|
Membership is explicit: no harness joins a square based on its filename or working directory.
|
|
13
|
-
`PUBLIC.square` has exactly the same behavior as any other square file.
|
|
20
|
+
`PUBLIC.square` has exactly the same behavior as any other square file. Membership (join/done,
|
|
21
|
+
read horizon and listens) is separate from host attachment (whether an exact owner is currently
|
|
22
|
+
executing for that participant). Host shutdown detaches; it does not make the participant leave.
|
|
23
|
+
An owner is an opaque, non-secret string compared only for equality. It is not a participant name
|
|
24
|
+
or authentication credential: one owner may control several names. The SDK requires it explicitly;
|
|
25
|
+
`localUserOwner()` supplies `user:<OS username>@<hostname>` when that is the intended actor.
|
|
14
26
|
|
|
15
27
|
```ts
|
|
16
28
|
import { Square } from '@astrosheep/square';
|
|
17
29
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
body: { channel: 'example', session: 'alice-process' },
|
|
21
|
-
});
|
|
30
|
+
const owner = 'example:alice-process';
|
|
31
|
+
const square = await Square.at({ path: './discussion.square' });
|
|
22
32
|
try {
|
|
23
|
-
const joined = await square.join('Alice');
|
|
33
|
+
const joined = await square.join('Alice', { owner });
|
|
24
34
|
const alice = joined.participant;
|
|
25
35
|
const sent = await alice.express('Hello @Bob', { force: true });
|
|
26
36
|
const page = await square.history();
|
|
@@ -31,92 +41,239 @@ try {
|
|
|
31
41
|
```
|
|
32
42
|
|
|
33
43
|
- `Square.build({path, markdown, hardCap?, throttlePerMinute?, template?, force?, ...})` returns
|
|
34
|
-
the same open resource. `Square.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
the same open resource. Public `Square.at` and `Square.build` resolve an omitted `wakeTransport`
|
|
45
|
+
once from `env ?? process.env` to the product's Claude/Codex/Paseo/HTTP defaults; if creation fails,
|
|
46
|
+
opening still succeeds without proactive wake. A supplied port is used unchanged; `false`
|
|
47
|
+
disables proactive wake. Activities still land normally without a transport.
|
|
48
|
+
- Synchronous `Square.inMemory` uses only an explicitly supplied port; omitted or `false` means
|
|
49
|
+
no wake. An injected transport runs the same claim, deferral and final currentness checks as
|
|
50
|
+
file delivery. Each memory resource has a unique `memory://` identity; this is not a remotely
|
|
51
|
+
openable address, is not indexed, and does not enable file-based hooks.
|
|
52
|
+
- `Square.at` accepts `path`, optional `cwd`, `env`, `clock`, `wakeTransport` and a caller-owned
|
|
53
|
+
connection via `resources`. At entry, `env` resolves the path, clock and discovery index path
|
|
54
|
+
once. `Square.at`, `Square.build` and `Square.inMemory` do not capture a
|
|
55
|
+
`owner`, `wakeAddress` or default `participant`. The same resource can serve multiple owners;
|
|
56
|
+
identity is supplied at each join or participant selection. `wakeTransport` injection is retained
|
|
42
57
|
for both file-backed build/open and memory resources.
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
explicit
|
|
64
|
-
|
|
58
|
+
- Local artifact paths resolve against `cwd`; an omitted path uses `SQUARE_LOCATION` at entry.
|
|
59
|
+
- `await square.participant(name, {owner})` selects an **attached, standing** seat held by that exact
|
|
60
|
+
owner without joining, claiming or reentering. It never manufactures a ghost handle: a never-joined,
|
|
61
|
+
done, foreign or detached seat is refused with `already_joined`, `reason: 'needs_seat'` and a `join`
|
|
62
|
+
recovery fact.
|
|
63
|
+
Foreign custody still requires explicit takeover. The returned handle captures the current owner
|
|
64
|
+
and generation; subsequent mutations recheck custody. A name alone never authorizes writes.
|
|
65
|
+
- `await square.seatsOf(owner)` returns the owner's standing seats as
|
|
66
|
+
`[{name, generation, attachment}]`, including attached and detached seats but not done participants.
|
|
67
|
+
It is read-only and neither joins nor restores a seat. `selectOwnSeat(square, owner, name?)` is the
|
|
68
|
+
adapter helper: an explicit name is checked through `participant`; otherwise exactly one attached
|
|
69
|
+
seat is required. For an overview-capable call,
|
|
70
|
+
`selectOwnSeat(square, owner, undefined, {optional: true})` instead returns `undefined` for a missing
|
|
71
|
+
owner or zero/multiple attached seats. Selection does not change membership.
|
|
72
|
+
- Join with an explicit name and owner: `square.join('Alice', {owner, wakeAddress?, ...options}, control?)`
|
|
73
|
+
returns one `JoinResult`: `{participant, kind, activity, observation?}`. The name is required even
|
|
74
|
+
when the owner holds exactly one seat. `participant` is a handle, not a string. `takeover: true`
|
|
75
|
+
atomically replaces a standing seat; a rejoin after done establishes a new join generation.
|
|
76
|
+
Same-owner reconnection retains its generation. A detached seat reattaches with kind `reconnected`,
|
|
77
|
+
no activity and no change to its read horizon or listens; the operation supplies any wake address.
|
|
78
|
+
A foreign owner still needs explicit takeover, including for an away participant. Old handles remain
|
|
79
|
+
fenced after takeover or rejoin, including when the same owner reacquires the name. There is no
|
|
80
|
+
separate `Participant.join()` or public `takeover()` API.
|
|
81
|
+
- Owner selection prevents accidental custody reuse, not malicious impersonation; never let untrusted
|
|
82
|
+
input choose another caller's owner. A Square owns its connection unless
|
|
83
|
+
`resources: createSquareResourceOwner()` lends it one. Closing a borrowing resource leaves that
|
|
84
|
+
owner open. Request signals never become the borrowed connection's lifetime. The MCP server drains
|
|
85
|
+
cancelled/in-flight work before closing its owner; explicit location overrides get independent
|
|
86
|
+
request-owned connections.
|
|
87
|
+
|
|
88
|
+
The rebuildable HOME discovery index is only a source of candidate paths, never custody authority.
|
|
89
|
+
File-backed successful joins and non-null reattachments attempt best-effort publication of their
|
|
90
|
+
owner and path; opening, building, selecting a participant or expressing does not publish. Memory
|
|
91
|
+
resources are not indexed. Losing the index may delay discovery, but cannot transfer a seat.
|
|
92
|
+
|
|
93
|
+
## Adapter owner resolution
|
|
94
|
+
|
|
95
|
+
Owners are nonempty strings without control characters, at most 512 UTF-8 bytes. The core stores
|
|
96
|
+
and compares them without interpreting harnesses. One adapter codec, `harnessOwner`, escapes each
|
|
97
|
+
coordinate segment with `encodeURIComponent`: `<channel>:<session>[:agent:<agent>]`.
|
|
98
|
+
`decodeHarnessOwner` is for adapters only; custom owner strings need no harness spelling.
|
|
99
|
+
|
|
100
|
+
CLI uses `--owner` or `SQUARE_OWNER` exactly when supplied, then one complete native harness
|
|
101
|
+
coordinate (Paseo is auxiliary), otherwise `localUserOwner()`. Multiple native identities or an
|
|
102
|
+
incomplete Claude-child identity refuse rather than using the human's owner. Plain terminal owners
|
|
103
|
+
have no automatic host lifecycle or wake address: join explicitly and use `catch --idle`.
|
|
104
|
+
MCP uses the call-local harness coordinate, else its configured owner, never tool arguments. Pi and
|
|
105
|
+
native adapters encode their own coordinate; the Slack bridge uses its local configured owner.
|
|
106
|
+
`inbox(input, owner)` and `square inbox --for-owner <owner>` select exact opaque equality.
|
|
107
|
+
|
|
108
|
+
## Host attachment lifecycle
|
|
109
|
+
|
|
110
|
+
`square.detach(name, {owner, generation})` checks the exact owner and generation inside the artifact
|
|
111
|
+
transaction. It returns `true` when it detaches, or `null` for stale, replaced, foreign, done or
|
|
112
|
+
already-detached custody. It lands no activity, preserves membership/read horizon/listens, and
|
|
113
|
+
clears wake readiness, catch ownership and delivery deferrals. Detached handles cannot write,
|
|
114
|
+
including an empty catch; reattachment or same-owner rejoin restores access. Proactive claims,
|
|
115
|
+
observers and final send/presentation checks exclude detached seats. Existing receipts and delivery
|
|
116
|
+
evidence survive. A detached participant stays away indefinitely; no timer manufactures a departure.
|
|
117
|
+
|
|
118
|
+
`square.reattach(name, {owner, wakeAddress?}, control?)` restores a standing seat of that exact owner
|
|
119
|
+
and returns a `Participant`, or `null` if the seat is absent, foreign or done; it never creates
|
|
120
|
+
membership. It preserves the generation, read horizon and listens. On an already attached seat it
|
|
121
|
+
returns a handle and can refresh the supplied wake address, subject to native-route preference.
|
|
122
|
+
Host start/resume restores detached seats found with `seatsOf(owner)` before arming delivery,
|
|
123
|
+
independently across seats and locations. Concurrent departure or takeover remains fenced; a foreign
|
|
124
|
+
takeover between the initial seat read and transaction can refuse instead of returning `null`.
|
|
125
|
+
New/forked owners restore nothing, and hosts do not recover from historical done coordinates.
|
|
126
|
+
|
|
127
|
+
There are three endings: participant `done` ends membership; Pi shutdown, Codex SessionEnd and
|
|
128
|
+
Claude session end detach only; host-confirmed permanent deletion uses
|
|
129
|
+
`square.endHeld(name, {owner, generation})` to land a fenced `done` on an attached or detached seat,
|
|
130
|
+
returning its lifecycle result or `null` when custody no longer matches. OpenCode `session.deleted`
|
|
131
|
+
is permanent deletion. Takeover replaces either attachment state with `done -> join`; late cleanup
|
|
132
|
+
or restoration by the old owner cannot reclaim it.
|
|
133
|
+
|
|
134
|
+
`square.cancelDeliveries(name, {owner, generation})` is also generation-fenced: for an attached
|
|
135
|
+
current seat it advances the suppression boundary through the current activity head, returning
|
|
136
|
+
whether it changed. A mismatched seat returns `false`; a detached or concurrently replaced seat may
|
|
137
|
+
be refused. It does not consume activity or recall an external send already past its final check.
|
|
138
|
+
|
|
139
|
+
## HTTP(S) wake notifications
|
|
140
|
+
|
|
141
|
+
File-backed public opens include a built-in `http` wake transport for both HTTP and HTTPS.
|
|
142
|
+
Register its address through SDK join or same-owner `reattach`:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
const { participant: bob } = await square.join('Bob', {
|
|
146
|
+
owner: 'example:bob-process',
|
|
147
|
+
wakeAddress: { kind: 'http', address: {
|
|
148
|
+
url: 'https://example.com/square/wake',
|
|
149
|
+
tokenEnv: 'BOB_WAKE_TOKEN', // optional; sender process environment variable
|
|
150
|
+
} },
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The URL must have an HTTP(S) scheme and hostname, with no embedded username/password or fragment.
|
|
155
|
+
Local/private endpoints are allowed. HTTPS uses normal certificate validation. The sender POSTs
|
|
156
|
+
`Content-Type: application/json` to that URL without following redirects. When `tokenEnv` is
|
|
157
|
+
configured, the sender resolves it in its own environment (`env` at open, otherwise `process.env`),
|
|
158
|
+
not the receiver's. A missing, blank or invalid bearer token refuses locally without HTTP I/O.
|
|
159
|
+
Keep the secret in that environment variable, never in the stored address. Diagnostics do not echo
|
|
160
|
+
URLs or raw network errors; URL query strings may themselves be sensitive and remain stored config.
|
|
161
|
+
|
|
162
|
+
The JSON body contains only:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"location": "/sender/path/discussion.square",
|
|
167
|
+
"participant": "Bob",
|
|
168
|
+
"activity": "act/12",
|
|
169
|
+
"actor": "Alice",
|
|
170
|
+
"attemptN": 0,
|
|
171
|
+
"preview": "<existing rendered Square wake reminder>"
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`attemptN` is omitted when unavailable. The preview is the existing rendered awareness reminder,
|
|
176
|
+
not a new payload template or full artifact. Owner, route and claim token are never included. `location` is the
|
|
177
|
+
**sender's artifact locator**, not a downloadable URL. HTTP adds wake notification, not a remote
|
|
178
|
+
Square backend: the receiver owns how it queues/wakes its agent and gives it access to Square.
|
|
179
|
+
|
|
180
|
+
Return 2xx promptly only after accepting wake custody. A 2xx records an accepted wake, **not** catch
|
|
181
|
+
or presentation; activity remains unread until normal consumption. Redirects and 4xx are rejected,
|
|
182
|
+
5xx are transient failures, and other non-2xx statuses are rejected. A network error or timeout
|
|
183
|
+
after sending starts is **unknown**: the receiver may already have accepted. Unknown/accepted wakes
|
|
184
|
+
are not automatically replayed. There is no retry loop in the HTTP adapter; existing delivery policy
|
|
185
|
+
owns future dispatch. Local configuration, absent preview, expired pre-send budget or a refused final
|
|
186
|
+
gate sends nothing. One deadline covers preparation, the final gate and waiting for response headers;
|
|
187
|
+
response bodies are cancelled rather than read. Claims, per-owner ordering, deferral, attachment and
|
|
188
|
+
cancellation checks are shared with every transport. `wakeTransport: false` still disables wakes,
|
|
189
|
+
and an injected transport remains authoritative.
|
|
190
|
+
|
|
191
|
+
The enclosing dispatcher's safety deadline is transport-neutral: if an attempt is still unsettled,
|
|
192
|
+
it records conservative **unknown**, even when HTTP has not started (for example, while the final
|
|
193
|
+
send gate is stalled). A local pre-send expiry is known no-send only when the adapter's result arrives
|
|
194
|
+
before that outer fallback. HTTP still must not start I/O after a stalled gate resumes past its
|
|
195
|
+
deadline; the persisted unknown remains and prevents automatic replay. This does not change the
|
|
196
|
+
shared dispatcher's timeout or retry policy.
|
|
65
197
|
|
|
66
198
|
## One set of operations and defaults
|
|
67
199
|
|
|
68
200
|
Square owns `history`, `status`, `participants`, `snapshot`, `stream`, and recovery observations.
|
|
69
201
|
A file-backed `stream` follows the path with a fresh artifact read for each page, so atomic file
|
|
70
202
|
replacement is visible even while the resource holds its original connection. Its iterator owns
|
|
71
|
-
its observation lifetime: `return()` interrupts a pending pull; closing
|
|
72
|
-
that independent path observation.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
203
|
+
its observation lifetime: `return()` interrupts a pending pull; closing a local Square does not close
|
|
204
|
+
that independent path observation.
|
|
205
|
+
Internal readers that explicitly borrow an artifact port leave its lifetime with the lender and
|
|
206
|
+
propagate port failures rather than waiting for path repair.
|
|
207
|
+
|
|
208
|
+
`Activity` is `{id, at, kind, actor, body?, metadata?, mentions, reach?, target?, reply?}` throughout
|
|
209
|
+
the SDK. Stream without a recipient yields these activities; with a recipient it selects
|
|
210
|
+
their landed directed activities and adds `perception` and the landing route `via`
|
|
211
|
+
(`mention`, `reply`, `attention`, or `bell`). Perception follows the viewer's actual landed audience;
|
|
212
|
+
`via` is absent when no directed route exists. Stream starts at a bounded tail or after an `ActivityId`
|
|
213
|
+
like `act/12`; it never consumes activity.
|
|
214
|
+
Participant owns `express`, `catch`, `listen`, `unlisten`, `ignore`, `listening`, `done`,
|
|
215
|
+
`deferDelivery`, `renewDeliveryDeferral`, and `releaseDeliveryDeferral`. History belongs only to
|
|
216
|
+
Square; there is no `Participant.history`.
|
|
217
|
+
|
|
218
|
+
- Express waits when throttled unless `noWait: true`, for **every** caller. `ExpressResult`
|
|
219
|
+
includes the committed `activity`, optional landing-time warnings (`unmatchedMentions`,
|
|
220
|
+
`replyAuthorNotHere`), `hint` and `observation`. Warnings are not stored on the activity.
|
|
81
221
|
- A say may carry `metadata`, an immutable JSON object with nested JSON values and a maximum serialized
|
|
82
222
|
UTF-8 size of 16 KiB (`ACTIVITY_METADATA_MAX_BYTES`). `alice.express({body?, metadata?, ...options}, control?)`
|
|
83
223
|
is the object form; the existing `express(body, options?, control?)` form remains valid. A non-empty
|
|
84
224
|
body may carry `{}`; an omitted, empty or whitespace-only body requires non-empty metadata. Metadata-only
|
|
85
225
|
says omit `body` from public activity records. Invalid objects, cycles, unsupported values and oversized
|
|
86
226
|
metadata refuse before landing with `activityUnsent`. Metadata is carried unchanged by express, history,
|
|
87
|
-
catch, status/latest, unread projections and stream records through the SDK,
|
|
227
|
+
catch, status/latest, unread projections and stream records through the SDK, CLI and stream.
|
|
88
228
|
MCP and Pi tools are not a metadata surface: their `express` requires a body and its schema rejects a
|
|
89
|
-
`metadata` argument
|
|
90
|
-
|
|
229
|
+
`metadata` argument. Tool history/catch return rendered text with a structured-activity placeholder,
|
|
230
|
+
not metadata JSON or activity objects. Metadata does not affect mentions, replies, listening, audience,
|
|
91
231
|
ordering, consumption or permissions. A current participant may temporarily defer proactive delivery
|
|
92
|
-
by exact metadata match through the SDK
|
|
232
|
+
by exact metadata match through the SDK lease API below. Metadata may nest up to 256 levels; the bound is enforced before
|
|
93
233
|
persistence and public reads. Human text uses a concise structured-activity placeholder and never prints
|
|
94
234
|
metadata JSON. There is no archive metadata filter, query language or index; CLI `--metadata <json>` is the only
|
|
95
235
|
metadata input flag.
|
|
96
|
-
- Catch returns `{activities,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
236
|
+
- Catch returns `{activities, remaining, reason, hint?, observation?}`. Each activity includes
|
|
237
|
+
viewer-relative `perception` and `via`. Reasons are `drained`, `idle`, `capped`, `quorum`,
|
|
238
|
+
`replaced`, or `already-active`. Omitted/zero idle is immediate; positive idle uses the single
|
|
239
|
+
leased quiet wait. Receipt selection, perception, routes, counts and terminal facts settle
|
|
240
|
+
in the receipt transaction. Cancellation/replacement releases only that waiter's lease and never
|
|
241
|
+
consumes future activity.
|
|
100
242
|
- All history callers return a `HistoryResult` object with `activities`. Default page size is ten;
|
|
101
243
|
`limit: null` explicitly requests an unbounded archive. Numeric limits must be 1–100. Contextual
|
|
102
244
|
queries still reject an implicit merged window over 100 activities; an explicit numeric limit
|
|
103
|
-
bounds it. CLI/tool schemas accept numeric limits only. History never writes receipts
|
|
245
|
+
bounds it. CLI/tool schemas accept numeric limits only. History never writes receipts; history
|
|
246
|
+
observations retain lifetime say ordinals for CLI `history --format number`.
|
|
104
247
|
- `participants()` returns `{participants, observation?}` and `listening()` returns
|
|
105
|
-
`{listening, observation?}`, not arrays carrying metadata.
|
|
248
|
+
`{listening, observation?}`, not arrays carrying metadata. Participant summaries use `presence`
|
|
249
|
+
(`never-joined`, `active`, `watching`, `away`, or `done`). Away participants remain members,
|
|
250
|
+
count toward in-square totals and keep receiving normal bell, mention and listened audience. Its roster
|
|
251
|
+
supplies the participant counts. Core `square.status({viewer})` uses only the explicitly supplied viewer; with
|
|
252
|
+
no viewer it returns an unfiltered overview without viewer-specific `listening`. Core status does
|
|
253
|
+
not select or authorize a seat. CLI and tools check an explicit `--as`/`as` with `selectOwnSeat`;
|
|
254
|
+
otherwise exactly one attached seat of the caller's owner becomes the viewer, while zero or multiple
|
|
255
|
+
seats (or no owner) produce the overview. Snapshot participants use the same `name`, `presence` and
|
|
256
|
+
`listening` facts, excluding never-joined people; its detached `delivered(name, id)` predicate
|
|
257
|
+
remains available.
|
|
106
258
|
- `unlisten(target)` removes only that participant's current listen edge to the target, without
|
|
107
259
|
leaving or blocking future mentions/replies. It returns the same `ListenerChangeResult` as listen
|
|
108
260
|
and ignore, with `activity: null` when no listen exists. Existing ignores are unchanged. Earlier
|
|
109
261
|
landed audiences and receipts are never rewritten; listening again remains available.
|
|
110
|
-
- Lifecycle operations use the same artifact custody gate
|
|
111
|
-
|
|
112
|
-
|
|
262
|
+
- Lifecycle operations return `{activity, observation?}` and use the same artifact custody gate;
|
|
263
|
+
observations do not repeat the activity. Join entry uses public activities with settled perception,
|
|
264
|
+
and status observations use public join activities. Done accepts no message and retires only its
|
|
265
|
+
attached, captured owner/join generation; it never implicitly rejoins. A done participant is not
|
|
266
|
+
recoverable authority for any host path.
|
|
267
|
+
- Recovery summarizes unread directed activity with per-sender count, latest age and the single
|
|
268
|
+
greatest-index preview. Age uses the greatest timestamp, which may belong to another activity.
|
|
113
269
|
|
|
114
|
-
|
|
115
|
-
Listener/lifecycle operations take optional observation options before that
|
|
270
|
+
Operations that accept an `OperationControl` use a final argument with `signal` and optional typed
|
|
271
|
+
`onProgress`. Listener/lifecycle operations take optional observation options before that argument;
|
|
272
|
+
participant selection, seat discovery and host cleanup methods do not take an `OperationControl`.
|
|
116
273
|
|
|
117
274
|
## Temporary proactive delivery deferral
|
|
118
275
|
|
|
119
|
-
SDK
|
|
276
|
+
SDK Participants expose three operations. There are no CLI/MCP/Pi authoring
|
|
120
277
|
commands or listing operation in this delivery. Existing proactive adapters still obey deferrals.
|
|
121
278
|
|
|
122
279
|
```ts
|
|
@@ -147,7 +304,7 @@ try {
|
|
|
147
304
|
24 hours. Invalid matches, durations and IDs refuse with `invalid_args` before mutation.
|
|
148
305
|
- Renewal extends that same live lease from the renewal operation's clock; expired/missing IDs
|
|
149
306
|
return `null`, and release returns `false`. Independent overlapping leases are never merged;
|
|
150
|
-
releasing one cannot end another. A captured current
|
|
307
|
+
releasing one cannot end another. A captured current owner/join generation is required for every
|
|
151
308
|
mutation. Stale handles retain the ownership refusal; done/takeover/rejoin retire old leases.
|
|
152
309
|
- Lease writes change delivery revision, not conversation history. They do not create a receipt,
|
|
153
310
|
change audience or unread counts, hide an archive record, or prohibit explicit catch. Catch alone
|
|
@@ -163,7 +320,7 @@ try {
|
|
|
163
320
|
with acquisition and remain dispatched; a sink effect already written cannot be withdrawn.
|
|
164
321
|
Presentation eligibility is checked before the effect, not again as a veto after success. A batch
|
|
165
322
|
claims and filters its items before its one sink call, then records exactly the successfully rendered
|
|
166
|
-
set under each captured
|
|
323
|
+
set under each captured owner/join generation. A lease acquired while the sink returns or before a
|
|
167
324
|
later item's evidence commit does not erase success or allow replay after release. Cancellation
|
|
168
325
|
likewise stops pre-effect work, not truthful completion after a successful sink: evidence settles
|
|
169
326
|
on the original connection with an independent bounded storage lifetime and the same seat fences.
|
|
@@ -179,13 +336,23 @@ has a concrete optional observation type, without conditional generics or a gene
|
|
|
179
336
|
Data-only calls do not hydrate join history, the pre-expression feed, catch presence enrichment,
|
|
180
337
|
or history/status presentation annotations.
|
|
181
338
|
|
|
182
|
-
Express captures
|
|
339
|
+
Express captures optional unread context before the attempt: the latest five directed,
|
|
340
|
+
unseen activities after the current join, each preview capped at 400 Unicode characters,
|
|
341
|
+
plus the count outside that preview. It hydrates only that page, not the entire cursor suffix.
|
|
342
|
+
This is a nonconsuming preview even when a short body fits in full; only `catch` or the
|
|
343
|
+
normal full-notification presentation path consumes it. Count and time facts come
|
|
183
344
|
from the committed transaction. Listener and lifecycle observations likewise use their mutation's
|
|
184
345
|
facts, with no postcommit context read. Join uses its mutation's essential facts and treats optional
|
|
185
346
|
entry/history enrichment as best-effort: `observation.enrichmentUnavailable` is explicit and the
|
|
186
347
|
successful receipt survives. Catch preserves its committed page even if optional nearby presence
|
|
187
348
|
fails. A cancellation or failed clock after commit cannot turn these receipts into an unsent retry.
|
|
188
349
|
|
|
350
|
+
Delivery readiness is participant-specific, not a property of the Square resource.
|
|
351
|
+
`ParticipantSummary.deliveryAvailable`, `JoinObservation.deliveryAvailable` and
|
|
352
|
+
`CatchObservation.deliveryAvailable` reflect whether the participant's seat has a stored wake
|
|
353
|
+
address. Catch captures this fact with its selection. Join and catch observations require
|
|
354
|
+
`observe: true`. These flags do not prove that a receiver is live or that delivery succeeded.
|
|
355
|
+
|
|
189
356
|
`OperationError.operation` supplements authoritative error facts with caller/filter/recipient and
|
|
190
357
|
express certainty. Selected-row/execution failures remain unknown unless admission proved the
|
|
191
358
|
expression unsent. CLI saves raw drafts even when opening the resource fails; its initial open
|
|
@@ -194,39 +361,95 @@ receives the express abort signal, while recovery reopening is deliberately unca
|
|
|
194
361
|
## Adapters and standalone functions
|
|
195
362
|
|
|
196
363
|
CLI and tools only adapt input, identity, resource lifetime and output. They do not import private
|
|
197
|
-
actions, storage, admission, scanners or query execution.
|
|
198
|
-
|
|
199
|
-
|
|
364
|
+
actions, storage, admission, scanners or query execution. CLI `--json` for join, express, catch,
|
|
365
|
+
history, listen, ignore, unlisten, listening, done, status, participants and inbox
|
|
366
|
+
serializes the same SDK result without requesting `observe: true`. Only `observation` is omitted and
|
|
367
|
+
a live Participant handle becomes `{name}`. History returns one JSON document, including an empty
|
|
368
|
+
`activities` array; `history --format` remains human-oriented. `participants --limit` limits only
|
|
369
|
+
human text, not the SDK result. Stream `--ndjson` remains one SDK stream record per line.
|
|
200
370
|
|
|
201
|
-
|
|
371
|
+
MCP/Pi use the shared `square-tools.ts` definitions. All seven tools return only the CLI's rendered text, with no `outputSchema`
|
|
372
|
+
or `structuredContent`. Join text includes its context and recent activity; express text
|
|
373
|
+
includes landing ID/reach, warnings and bounded unread previews, never the sent body echo
|
|
374
|
+
or internal observation tables. Failures remain failed tool results with readable recovery
|
|
375
|
+
text. The SDK and CLI JSON keep their full domain receipts. The optional express observation
|
|
376
|
+
contains `context: {activities: [{id, actor, preview, truncated}], remaining}`, not the old
|
|
377
|
+
unbounded `pendingPublic`/ordinal/perception tables.
|
|
378
|
+
|
|
379
|
+
Express still finishes attempted notification delivery before releasing its resource.
|
|
380
|
+
It overlaps at most four receiving sessions per dispatch, preserving order within each
|
|
381
|
+
session and draining started work even on failure. Claims, final freshness checks and
|
|
382
|
+
uncertain-delivery suppression remain unchanged. Paseo inventory discovery is asynchronous;
|
|
383
|
+
overlapping attempts share only the in-flight inventory read, never a cached idle status.
|
|
384
|
+
The standalone `discoverPaseoAgents` export now returns a promise.
|
|
385
|
+
|
|
386
|
+
`discover(input, options?, control?)`, `inbox(input, owner, options?, control?)`,
|
|
202
387
|
`diagnoseSquare(input, control?)`, and `migrateSquare(input, control?)` are ordinary public exports.
|
|
203
388
|
They are not methods on a synthetic cross-square object. Diagnose/migrate resolve a path without
|
|
204
389
|
opening a valid current-format Square first.
|
|
205
390
|
|
|
206
391
|
## Artifact format and explicit migration
|
|
207
392
|
|
|
208
|
-
The current artifact format is
|
|
209
|
-
|
|
210
|
-
|
|
393
|
+
The current artifact format is 7. It stores opaque owners rather than harness coordinates and
|
|
394
|
+
preserves format 6's attached/detached distinction. Seats, delivery evidence, cancellation and
|
|
395
|
+
independent delivery deferrals remain separate from conversation activities and catch ownership.
|
|
396
|
+
Ordinary opens refuse older formats with migration guidance and never convert them implicitly.
|
|
211
397
|
|
|
212
398
|
Stop all sessions and services using the artifact, make an offline backup, then run
|
|
213
399
|
`square --location <path.square> migrate` and `doctor` before reopening with upgraded runtimes.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
400
|
+
Supported formats 2–6 convert to 7 in one transaction. Format 6's Body coordinates become encoded
|
|
401
|
+
owners while attachment, generations, wakes, evidence and revisions survive. Formats 4/5 first gain
|
|
402
|
+
attached seats; format 4 also gains empty deferral support. Formats 4–6 never consult HostLedger.
|
|
403
|
+
Historic host-cleanup departures cannot be distinguished from voluntary `done` and stay done.
|
|
404
|
+
For formats 2/3, also freeze and back up the legacy HostLedger used for verified owner/evidence
|
|
405
|
+
import; no authority is manufactured for done participants. Replay-critical native ambiguity
|
|
406
|
+
refuses rather than disappears, including any nonempty legacy token-only tombstones.
|
|
407
|
+
Current-format migration is a byte-preserving no-op. See [migration.md](migration.md) for the
|
|
408
|
+
narrow metadata policy, source-preserving refusal and filesystem requirements. Updating a package
|
|
409
|
+
does not migrate files or install a new runtime.
|
|
410
|
+
|
|
411
|
+
## SDK breaking migration: opaque owners
|
|
412
|
+
|
|
413
|
+
Artifact format 7 introduced opaque owners; format-6 artifacts required explicit migration before
|
|
414
|
+
opening with that format. Current artifacts use format 8 and require explicit migration from format 7.
|
|
415
|
+
`Body`, `sameBody` and `validateBody` are removed from the core and public API. Replace identity
|
|
416
|
+
objects with an explicit owner at each identity-bearing operation; do not put identity on a resource:
|
|
417
|
+
|
|
418
|
+
```ts
|
|
419
|
+
import { Square } from '@astrosheep/square';
|
|
420
|
+
|
|
421
|
+
const aliceOwner = 'example:alice-process';
|
|
422
|
+
const bobOwner = 'example:bob-process';
|
|
423
|
+
const square = await Square.at({ path: './discussion.square' });
|
|
424
|
+
try {
|
|
425
|
+
// Previously: pass body/wakeAddress/participant to Square.at and join(name).
|
|
426
|
+
const { participant: alice } = await square.join('Alice', { owner: aliceOwner }); // wakeAddress is optional
|
|
427
|
+
const { participant: bob } = await square.join('Bob', { owner: bobOwner }); // same resource, another owner
|
|
428
|
+
// For an already attached seat, select it without joining:
|
|
429
|
+
const selected = await square.participant('Alice', { owner: aliceOwner });
|
|
430
|
+
const seats = await square.seatsOf(aliceOwner);
|
|
431
|
+
const { activities } = await square.history(); // not alice.history()
|
|
432
|
+
const status = await square.status({ viewer: selected.name });
|
|
433
|
+
console.log(alice.name, bob.name, seats, activities, status);
|
|
434
|
+
} finally {
|
|
435
|
+
await square.close();
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Replace `recognize()`/`bodySeats()` with `seatsOf(owner)`, followed by explicit attached-seat
|
|
440
|
+
selection as needed; replace `implicitJoin` with `reattach(name, {owner, wakeAddress?})`. Replace
|
|
441
|
+
`detachOwnedSession`/`endOwnedSession` with the generation-fenced `detach`/`endHeld` methods above.
|
|
442
|
+
`forBody` is removed: use the same Square with operation-local owners. Read readiness from
|
|
443
|
+
participant summaries or join/catch observations, not the removed resource-level
|
|
444
|
+
`automaticDeliveryAvailable`. Core status no longer infers a viewer. Participant selection no longer
|
|
445
|
+
returns a handle for a missing, done, foreign or detached seat.
|
|
446
|
+
|
|
447
|
+
## Other deliberate migration breaks
|
|
225
448
|
|
|
226
449
|
The independent `createSquareApplication` API, its options/results/aliases, and `square-application.ts`
|
|
227
|
-
are deleted. Consumers must open/close an actual Square and select a Participant. SDK joins
|
|
228
|
-
`(await square.join(name)).participant`; history callers must use `.activities` and opt into
|
|
229
|
-
archive reads with `limit: null`. SDK express now waits by default. MCP constructor configuration
|
|
450
|
+
are deleted. Consumers must open/close an actual Square and select a Participant. SDK joins return
|
|
451
|
+
`(await square.join(name, {owner})).participant`; history callers must use `.activities` and opt into
|
|
452
|
+
full archive reads with `limit: null`. SDK express now waits by default. MCP constructor configuration
|
|
230
453
|
uses `path`, consistent with `Square.at`, rather than `squarePath`.
|
|
231
454
|
|
|
232
455
|
External consumers must migrate before taking this source version. Editing source does not update
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Two equal domains, using the public primitives
|
|
2
|
+
|
|
3
|
+
[`examples/symmetric-domains.mjs`](../examples/symmetric-domains.mjs) is a plain
|
|
4
|
+
ESM installed-package consumer. It imports only Node builtins and
|
|
5
|
+
`@astrosheep/square/attention`, `/receive`, and `/delivery`; it never constructs
|
|
6
|
+
`Square`. Two instances of the **same `Domain` implementation** independently
|
|
7
|
+
originate, receive, present and consume work. Neither is a master or a relay.
|
|
8
|
+
|
|
9
|
+
## Run from an installed package
|
|
10
|
+
|
|
11
|
+
In a consumer project with `@astrosheep/square` installed:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
cp node_modules/@astrosheep/square/examples/symmetric-domains.mjs ./symmetric-domains.mjs
|
|
15
|
+
node ./symmetric-domains.mjs
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The script prints a JSON summary. It also exports
|
|
19
|
+
`demonstrateSymmetricDomains()` for programmatic use. The repository's
|
|
20
|
+
`test/package-consumer.test.js` packs the package, copies the shipped example
|
|
21
|
+
into an isolated consumer, runs it there, and asserts the summary. It does not
|
|
22
|
+
substitute repository-relative imports, a second implementation, or path aliases.
|
|
23
|
+
|
|
24
|
+
## What the demonstration does
|
|
25
|
+
|
|
26
|
+
1. East originates a message for one local reader and two selected west readers.
|
|
27
|
+
West independently originates a message for one local reader and two east
|
|
28
|
+
readers. The static application directory supplies eligibility and destination
|
|
29
|
+
routing; no primitive parses the recipient spelling.
|
|
30
|
+
2. Each origin commits its local item/audience before any link effect. East uses
|
|
31
|
+
opaque random string keys; west uses decreasing negative numbers. Every
|
|
32
|
+
destination assigns its own key. `data.origin` is just application data, not
|
|
33
|
+
a global cursor, admission key or identity codec.
|
|
34
|
+
3. `resolveAudience` selects recipients; the application maps `{recipient, route}`
|
|
35
|
+
once to receive's `{recipient, via}` addresses, preserving them locally and
|
|
36
|
+
across the handoff. Application routing groups that result by destination.
|
|
37
|
+
One `dispatchEffect` passes **one payload with both selected
|
|
38
|
+
remote recipients**, not one payload per recipient. Its attempt series is
|
|
39
|
+
bound to source item + destination + purpose in the source's private store.
|
|
40
|
+
4. East's link returns `accepted` after west admits. West's link deliberately
|
|
41
|
+
returns `unknown` after east admits. Admission leaves both destination
|
|
42
|
+
recipients pending. In both directions a second dispatch acquires no claim:
|
|
43
|
+
the actual stored `accepted`/`unknown` attempt stops replay through
|
|
44
|
+
`claimAttempt`, not a callback counter or link-level duplicate filter.
|
|
45
|
+
5. Each destination uses `preparePresentation` / `presentPrepared` to present the
|
|
46
|
+
full payload to its first recipient. Settlement completes that recipient's
|
|
47
|
+
presentation attempt and calls `recordConsumed` for the captured receipt in
|
|
48
|
+
**one synchronous transaction**. The other recipient remains pending until
|
|
49
|
+
separately consumed. Origin receipts are unchanged by destination consumption.
|
|
50
|
+
6. With the link disabled, both domains still admit and consume three local-only
|
|
51
|
+
messages. Exact consumption of the middle item is repeatable; subsequent
|
|
52
|
+
one-item pages find the older and newer receipt holes in local insertion order.
|
|
53
|
+
No remote sink is invoked for this local work.
|
|
54
|
+
|
|
55
|
+
## Primitive responsibilities and caller obligations
|
|
56
|
+
|
|
57
|
+
| Primitive | Owns | Example caller supplies |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `resolveAudience` | Deterministic attention selection | Eligible directory and explicit routing |
|
|
60
|
+
| `receive` | Item and address insertion | Local IDs, uniqueness, isolation and rollback |
|
|
61
|
+
| `consumeReceived` / `recordConsumed` | Selection/full receipts, first-write-wins coordinates | Ordered reads and recipient + local item receipt storage |
|
|
62
|
+
| `claimAttempt` / `settleAttempt` | Scoped attempt transitions, terminal uncertainty | One transaction-bound series, tokens, lease times and persistence |
|
|
63
|
+
| `dispatchEffect` | Claim/currentness/effect/timeout/settlement lifecycle | Bound port, link callback, pre-send policy and actual deadline enforcement |
|
|
64
|
+
| `preparePresentation` / `presentPrepared` | One-shot custody, sink outcome and settlement | Captured payload, receipt coordinates and atomic completion + receipts |
|
|
65
|
+
|
|
66
|
+
The memory adapter clones a draft and commits only when its synchronous callback
|
|
67
|
+
returns. A throw discards all changes, including an attempt completion if receipt
|
|
68
|
+
recording fails. It never holds a transaction across an `await`. Each effect claim
|
|
69
|
+
commits before external work; settlement opens a new transaction. Returned rows
|
|
70
|
+
are copied, and the two stores share no mutable state. A real application must
|
|
71
|
+
provide equivalent isolation, fencing, uniqueness and atomic rollback in its own
|
|
72
|
+
storage, plus durability if required. The primitives do not supply a database.
|
|
73
|
+
|
|
74
|
+
The link obeys `beforeSend()` and checks cancellation/deadline **after** awaiting
|
|
75
|
+
that gate, immediately before admission. A late policy answer is not permission
|
|
76
|
+
to send after timeout. Settlement does not re-check policy after a successful
|
|
77
|
+
sink: it records captured facts without erasing success or selecting new items.
|
|
78
|
+
Public effect reports distinguish callback observation from persistence results.
|
|
79
|
+
|
|
80
|
+
## Deliberately bounded claims
|
|
81
|
+
|
|
82
|
+
The adapter is nondurable illustrative memory, and the link is an in-process
|
|
83
|
+
function. `accepted` means destination admission only. Presentation success means
|
|
84
|
+
this example's callback captured the supplied full payload, **not** that a human
|
|
85
|
+
or model read it. Unknown suppresses automatic replay but does not reconcile it;
|
|
86
|
+
this example has no crash recovery, cross-domain transaction, exactly-once effect,
|
|
87
|
+
network protocol, federation, authentication or discovery implementation.
|
|
88
|
+
|
|
89
|
+
One handoff per destination is a boundary an application **could choose** for
|
|
90
|
+
billing. No charges, billing ledger or payment semantics are implemented here.
|
|
91
|
+
For the underlying contracts, see [attention](attention-primitive.md),
|
|
92
|
+
[receive](receive-primitive.md), and [delivery](delivery-primitives.md).
|