@astrosheep/square 0.3.88 → 0.3.90
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/artifact.js +11 -9
- package/dist/boundary-presentation.d.ts +3 -2
- package/dist/boundary-presentation.js +50 -77
- package/dist/delivery-deferral.d.ts +28 -0
- package/dist/delivery-deferral.js +83 -0
- package/dist/delivery-operations.js +8 -6
- package/dist/delivery.js +4 -1
- package/dist/inbox.js +21 -10
- package/dist/model.d.ts +1 -0
- package/dist/ports.d.ts +11 -3
- package/dist/presentation-operations.d.ts +20 -1
- package/dist/presentation-operations.js +68 -44
- package/dist/sqlite-square.js +29 -0
- package/dist/square-domain.d.ts +1 -1
- package/dist/square-domain.js +12 -0
- package/dist/square-facade.d.ts +8 -0
- package/dist/square-migration.d.ts +2 -1
- package/dist/square-migration.js +25 -26
- package/dist/square-sqlite.d.ts +5 -2
- package/dist/square-sqlite.js +79 -6
- package/dist/square-stdio.d.ts +1 -1
- package/dist/square-stdio.js +30 -17
- package/dist/stdio-server.js +23 -3
- package/guides/backends.md +26 -13
- package/guides/change-waits.md +40 -11
- package/guides/migration.md +30 -12
- package/guides/operation-cancellation.md +16 -1
- package/guides/participant-ownership.md +60 -186
- package/guides/square-api.md +72 -9
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/paseo/README.md +7 -6
- package/plugins/paseo/client/markdown.tsx +125 -185
- package/plugins/paseo/client/square-message.tsx +9 -25
- package/plugins/paseo/client/transform.ts +5 -4
- package/plugins/paseo/index.client.tsx +14 -12
- package/plugins/paseo/package.json +3 -0
- package/plugins/paseo/shared/message.ts +50 -1
package/dist/square-stdio.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { validateMetadataMatch, validateDeferralDuration, validateDeferralId } from './delivery-deferral.js';
|
|
1
3
|
import { spawn } from 'node:child_process';
|
|
2
4
|
import path from 'node:path';
|
|
3
5
|
import { createInterface } from 'node:readline';
|
|
4
6
|
import { SquareError } from './model.js';
|
|
5
7
|
import { harnessSessionSources } from './participant-identity.js';
|
|
6
8
|
import { requireJoinName } from './seats.js';
|
|
7
|
-
export const STDIO_PROTOCOL_VERSION =
|
|
9
|
+
export const STDIO_PROTOCOL_VERSION = 2;
|
|
8
10
|
export const STDIO_MAX_FRAME_BYTES = 8 * 1024 * 1024;
|
|
9
11
|
export const identityVariables = [...harnessSessionSources.flatMap(source => [source.variable, ...(source.childVariable ? [source.childVariable] : [])])];
|
|
10
12
|
export function wireIdentity(env) {
|
|
@@ -157,33 +159,44 @@ class StdioSquare {
|
|
|
157
159
|
this.connection = connection;
|
|
158
160
|
this.handle = handle;
|
|
159
161
|
}
|
|
160
|
-
call(method, args, control, participant) {
|
|
161
|
-
return this.connection.request(method, { handle: this.handle, args, ...(participant === undefined ? {} : { participant }) }, control);
|
|
162
|
+
call(method, args, control, participant, participantHandle) {
|
|
163
|
+
return this.connection.request(method, { handle: this.handle, args, ...(participant === undefined ? {} : { participant, participantHandle }) }, control);
|
|
162
164
|
}
|
|
163
|
-
participantHandle(name) {
|
|
165
|
+
participantHandle(name, reference) {
|
|
166
|
+
const call = (method, args, control) => this.call(method, args, control, name, reference);
|
|
164
167
|
return {
|
|
165
168
|
name,
|
|
166
169
|
express: ((bodyOrInput, optionsOrControl, control) => {
|
|
167
170
|
if (typeof bodyOrInput === 'object' && bodyOrInput !== null)
|
|
168
|
-
return
|
|
169
|
-
return
|
|
171
|
+
return call('express', [bodyOrInput, undefined], optionsOrControl);
|
|
172
|
+
return call('express', [bodyOrInput, optionsOrControl], control);
|
|
170
173
|
}),
|
|
171
|
-
catch: (options, control) =>
|
|
172
|
-
listen: (target, options, control) =>
|
|
173
|
-
ignore: (target, options, control) =>
|
|
174
|
-
unlisten: (target, options, control) =>
|
|
175
|
-
listening: (options, control) =>
|
|
176
|
-
hold: (reason, options, control) =>
|
|
177
|
-
resume: (options, control) =>
|
|
178
|
-
done: (body, options, control) =>
|
|
174
|
+
catch: (options, control) => call('catch', [options], control),
|
|
175
|
+
listen: (target, options, control) => call('listen', [target, options], control),
|
|
176
|
+
ignore: (target, options, control) => call('ignore', [target, options], control),
|
|
177
|
+
unlisten: (target, options, control) => call('unlisten', [target, options], control),
|
|
178
|
+
listening: (options, control) => call('listening', [options], control),
|
|
179
|
+
hold: (reason, options, control) => call('hold', [reason, options], control),
|
|
180
|
+
resume: (options, control) => call('resume', [options], control),
|
|
181
|
+
done: (body, options, control) => call('done', [body, options], control),
|
|
182
|
+
deferDelivery: async (input, control) => {
|
|
183
|
+
const match = validateMetadataMatch(input?.match), durationMs = validateDeferralDuration(input?.durationMs);
|
|
184
|
+
return call('deferDelivery', [{ match, durationMs }], control);
|
|
185
|
+
},
|
|
186
|
+
renewDeliveryDeferral: async (id, durationMs, control) => call('renewDeliveryDeferral', [validateDeferralId(id), validateDeferralDuration(durationMs)], control),
|
|
187
|
+
releaseDeliveryDeferral: async (id, control) => call('releaseDeliveryDeferral', [validateDeferralId(id)], control),
|
|
179
188
|
history: (query, control) => this.history(query, control),
|
|
180
189
|
};
|
|
181
190
|
}
|
|
182
|
-
async participant(name) {
|
|
191
|
+
async participant(name) {
|
|
192
|
+
const reference = randomUUID();
|
|
193
|
+
return this.participantHandle(await this.call('participant', [name, reference]), reference);
|
|
194
|
+
}
|
|
183
195
|
async join(...[name, options, control]) {
|
|
184
196
|
requireJoinName(name);
|
|
185
|
-
const
|
|
186
|
-
|
|
197
|
+
const reference = randomUUID();
|
|
198
|
+
const result = await this.call('join', [name, options, reference], control);
|
|
199
|
+
return { ...result, participant: this.participantHandle(result.participant, reference) };
|
|
187
200
|
}
|
|
188
201
|
history(...[query, control]) { return this.call('history', [query], control); }
|
|
189
202
|
status(...[options, control]) { return this.call('status', [options], control); }
|
package/dist/stdio-server.js
CHANGED
|
@@ -16,6 +16,13 @@ export async function runSquareStdioServer(root) {
|
|
|
16
16
|
let square;
|
|
17
17
|
let location;
|
|
18
18
|
let opening = false, closing = false;
|
|
19
|
+
const participants = new Map();
|
|
20
|
+
const captureParticipant = (reference, participant) => {
|
|
21
|
+
if (typeof reference !== 'string' || reference.length === 0 || participants.has(reference))
|
|
22
|
+
throw new SquareError('invalid_args', 'Participant handle must be a fresh connection-local reference');
|
|
23
|
+
participants.set(reference, participant);
|
|
24
|
+
return participant.name;
|
|
25
|
+
};
|
|
19
26
|
const running = new Map();
|
|
20
27
|
const write = (message) => { if (!process.stdout.destroyed)
|
|
21
28
|
process.stdout.write(JSON.stringify(message) + '\n'); };
|
|
@@ -91,10 +98,12 @@ export async function runSquareStdioServer(root) {
|
|
|
91
98
|
const args = params.args.map((arg) => arg === null ? undefined : arg);
|
|
92
99
|
let result;
|
|
93
100
|
switch (method) {
|
|
94
|
-
case 'participant': return (await square.participant(args[0]))
|
|
101
|
+
case 'participant': return captureParticipant(args[1], await square.participant(args[0]));
|
|
95
102
|
case 'join': {
|
|
103
|
+
if (typeof args[2] !== 'string' || args[2].length === 0 || participants.has(args[2]))
|
|
104
|
+
throw new SquareError('invalid_args', 'Participant handle must be a fresh connection-local reference');
|
|
96
105
|
const joined = await square.join(args[0], args[1], control);
|
|
97
|
-
result = { ...joined, participant: joined.participant
|
|
106
|
+
result = { ...joined, participant: captureParticipant(args[2], joined.participant) };
|
|
98
107
|
break;
|
|
99
108
|
}
|
|
100
109
|
case 'history':
|
|
@@ -112,8 +121,19 @@ export async function runSquareStdioServer(root) {
|
|
|
112
121
|
default: {
|
|
113
122
|
if (typeof params.participant !== 'string')
|
|
114
123
|
throw new SquareError('invalid_args', 'Participant operation requires a name');
|
|
115
|
-
const participant =
|
|
124
|
+
const participant = participants.get(params.participantHandle);
|
|
125
|
+
if (participant === undefined || participant.name !== params.participant)
|
|
126
|
+
throw new SquareError('invalid_args', 'Select a captured participant handle on this connection');
|
|
116
127
|
switch (method) {
|
|
128
|
+
case 'deferDelivery':
|
|
129
|
+
result = await participant.deferDelivery(args[0], control);
|
|
130
|
+
break;
|
|
131
|
+
case 'renewDeliveryDeferral':
|
|
132
|
+
result = await participant.renewDeliveryDeferral(args[0], args[1], control);
|
|
133
|
+
break;
|
|
134
|
+
case 'releaseDeliveryDeferral':
|
|
135
|
+
result = await participant.releaseDeliveryDeferral(args[0], control);
|
|
136
|
+
break;
|
|
117
137
|
case 'express':
|
|
118
138
|
result = await participant.express(args[0], args[1], control);
|
|
119
139
|
break;
|
package/guides/backends.md
CHANGED
|
@@ -55,9 +55,10 @@ import { Square, type SquareBackends } from '@astrosheep/square';
|
|
|
55
55
|
const backends: SquareBackends = {
|
|
56
56
|
local: { command: process.execPath, args: ['/absolute/path/to/square/dist/stdio-server.js', '--root', '/tmp/squares'] },
|
|
57
57
|
};
|
|
58
|
-
const square = await Square.at({ path: 'stdio://local/design', backends, participant: 'Bob'
|
|
58
|
+
const square = await Square.at({ path: 'stdio://local/design', backends, participant: 'Bob',
|
|
59
|
+
body: { channel: 'example', session: 'bob-process' } });
|
|
59
60
|
try {
|
|
60
|
-
const bob = (await square.join()).participant;
|
|
61
|
+
const bob = (await square.join('Bob')).participant;
|
|
61
62
|
const caught = await bob.catch();
|
|
62
63
|
console.log(caught.activities);
|
|
63
64
|
} finally {
|
|
@@ -99,7 +100,7 @@ configured local SQLite connection. Pi tools accept the identical location and r
|
|
|
99
100
|
- CLI failed-expression drafts stay in local `./drafts` for backend locations; recovery commands and
|
|
100
101
|
display labels retain the URI. Local-artifact draft placement is unchanged.
|
|
101
102
|
|
|
102
|
-
## Protocol version
|
|
103
|
+
## Protocol version 2
|
|
103
104
|
|
|
104
105
|
Newline-delimited JSON-RPC 2.0 over UTF-8 stdin/stdout, one object per line, at most 8 MiB per frame.
|
|
105
106
|
Stdout is protocol only. No batch requests. The initial versioned handshake opens one logical square
|
|
@@ -107,9 +108,9 @@ per connection and returns an opaque handle. IDs are unique positive integers on
|
|
|
107
108
|
requests may overlap and responses may arrive out of order.
|
|
108
109
|
|
|
109
110
|
```json
|
|
110
|
-
{"jsonrpc":"2.0","id":1,"method":"square/open","params":{"version":
|
|
111
|
-
{"jsonrpc":"2.0","id":1,"result":{"version":
|
|
112
|
-
{"jsonrpc":"2.0","id":2,"method":"join","params":{"handle":"square","args":["Bob",{"observe":true}]}}
|
|
111
|
+
{"jsonrpc":"2.0","id":1,"method":"square/open","params":{"version":2,"key":"design","location":"stdio://local/design","identity":{"PI_SESSION_ID":"session-id"},"body":{"channel":"pi","session":"session-id"},"participant":"Bob"}}
|
|
112
|
+
{"jsonrpc":"2.0","id":1,"result":{"version":2,"handle":"square"}}
|
|
113
|
+
{"jsonrpc":"2.0","id":2,"method":"join","params":{"handle":"square","args":["Bob",{"observe":true},"fresh-participant-reference"]}}
|
|
113
114
|
```
|
|
114
115
|
|
|
115
116
|
Open may include `build: {markdown, hardCap?, throttlePerMinute?, template?, force?}` instead of
|
|
@@ -120,25 +121,36 @@ coordinates (including PASEO_AGENT_ID), then overlays explicit call-local facts
|
|
|
120
121
|
`process.env`. Participant defaults remain separate from square keys. Each new tool open therefore
|
|
121
122
|
uses that call's identity, not MCP/Pi/backend startup identity.
|
|
122
123
|
|
|
123
|
-
After open, methods take `{handle, args
|
|
124
|
-
|
|
124
|
+
After open, square methods take `{handle, args}` and Participant methods additionally require
|
|
125
|
+
`{participant, participantHandle}`. Selection/join supply a fresh opaque connection-local reference;
|
|
126
|
+
the server retains the actual captured Participant under it instead of reselecting by name per
|
|
127
|
+
request. Version 2 deliberately refuses older peers rather than accepting unfenced handles.
|
|
128
|
+
`args` is the public operation's positional arguments excluding `OperationControl`, plus the
|
|
129
|
+
selection reference noted below; trailing JSON nulls represent omitted optional arguments.
|
|
125
130
|
Nested nulls such as `history({limit:null})` retain their public meaning.
|
|
126
131
|
|
|
127
132
|
| Methods | args | participant field |
|
|
128
133
|
| --- | --- | --- |
|
|
129
|
-
| `participant` | `[name
|
|
130
|
-
| `join` | `[name?,
|
|
134
|
+
| `participant` | `[name?, freshReference]` | no; selection only, not joining |
|
|
135
|
+
| `join` | `[name, options?, freshReference]` | no |
|
|
131
136
|
| `history`, `status`, `participants` | `[options?]` | no |
|
|
132
137
|
| `recovery` | `[name?]` | no |
|
|
133
138
|
| `express` | `[body, options?]` | required |
|
|
134
139
|
| `catch`, `listening`, `resume` | `[options?]` | required |
|
|
135
140
|
| `listen`, `ignore`, `unlisten` | `[target, options?]` | required |
|
|
136
141
|
| `hold`, `done` | `[body?, options?]` | required |
|
|
142
|
+
| `deferDelivery` | `[{match, durationMs}]` | required |
|
|
143
|
+
| `renewDeliveryDeferral` | `[id, durationMs]` | required |
|
|
144
|
+
| `releaseDeliveryDeferral` | `[id]` | required |
|
|
137
145
|
|
|
138
146
|
Results are the concrete public operation results, including requested observations. Participant
|
|
139
147
|
handles serialize as names for `participant` and `join.participant` and become real local handles
|
|
140
|
-
on the client
|
|
141
|
-
|
|
148
|
+
on the client, using their retained connection-local references. Done/takeover/rejoin fences old
|
|
149
|
+
references even when the same Body later reacquires the name. Observation locations are canonical
|
|
150
|
+
remote URIs and automatic delivery is false. SDK/stdio deferrals have identical validation, independent
|
|
151
|
+
lease and owner-generation semantics; create/renew return only `{id, expiresAt}`, renew returns null
|
|
152
|
+
and release false for an inactive ID, and stale handles receive the normal ownership refusal.
|
|
153
|
+
No CLI/MCP/Pi deferral-authoring command is included. Errors are JSON-RPC `{code:-32000,message,data:{code?,facts?,operation?}}`; Square codes, structured
|
|
142
154
|
facts, history bounds and express certainty survive the boundary. The client does not infer
|
|
143
155
|
`unsent` from a connection failure. There is no mutation retry or replay.
|
|
144
156
|
|
|
@@ -147,7 +159,8 @@ Progress is a notification
|
|
|
147
159
|
`OperationProgress`. Request abort sends `$/cancelRequest` with `{id}`; the server aborts only that
|
|
148
160
|
request and returns its response **after** cleanup, including releasing a catch lease. Another
|
|
149
161
|
request on the same handle continues normally. Cancellation never undoes an explicitly committed
|
|
150
|
-
receipt.
|
|
162
|
+
receipt or lease mutation. A committed deferral remains until release, expiry or seat retirement;
|
|
163
|
+
connection close does not cancel it. `square/close` with `{}` aborts/drains outstanding requests, closes SQLite and acknowledges
|
|
151
164
|
with a null result. Client close is idempotent, waits briefly for graceful cleanup, then terminates
|
|
152
165
|
an unresponsive process. EOF, malformed framing, version mismatch and process exit reject pending
|
|
153
166
|
requests and release the connection. A lost express response after dispatch has unknown effect;
|
package/guides/change-waits.md
CHANGED
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
# Waiting for changes
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Artifact revisions and the rebuildable discovery index drive change detection. Filesystem
|
|
4
|
+
notifications are hints, not activity delivery or consumption receipts.
|
|
4
5
|
|
|
5
|
-
- A process shares a revision reader per canonical artifact and a filesystem watcher per containing
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
- A process shares a revision reader per canonical artifact and a filesystem watcher per containing
|
|
7
|
+
directory. Subscribers have independent baselines, deadlines and cancellation. The final release
|
|
8
|
+
closes the watcher, reconciliation timer and outstanding read.
|
|
9
|
+
- Directory notifications invalidate lightweight reads. A two-second reconciliation checks revisions
|
|
10
|
+
even when notifications are missing. Missing directories, unsupported watches, watcher failures
|
|
11
|
+
and directory replacement retain that fallback. Latency is not a hard guarantee: a missed hint
|
|
12
|
+
may wait for reconciliation, scheduling and lock contention.
|
|
13
|
+
- Conversation revision controls the pending-content cache. Delivery revision separately invalidates
|
|
14
|
+
eligibility, including delivery evidence, cancellation and deferral writes. Projections resolve
|
|
15
|
+
current typed seats and selected bodies in one coherent artifact read; they never grant authority
|
|
16
|
+
from an index entry or a cached eligibility decision. No sidecar, broker or second delivery store
|
|
17
|
+
is introduced. An unreadable candidate cannot authorize cached content.
|
|
18
|
+
- Each active session pending observer subscribes before selecting current candidates and facts,
|
|
19
|
+
including when no seat is yet discoverable. Index publication can wake discovery without an
|
|
20
|
+
artifact write. A failed injection waits for substantive change; its own failed evidence write
|
|
21
|
+
does not cause a retry loop.
|
|
22
|
+
- Deferral create/renew/release and opportunistic pruning change delivery revision, not conversation
|
|
23
|
+
activity. Release rechecks eligibility without a new message. Expiry needs no synthetic write:
|
|
24
|
+
the observer includes the earliest relevant matching live deferral expiry in its existing wait
|
|
25
|
+
deadline, alongside catch-lease expiry/staleness. Overlapping leases remain OR; an early expiry
|
|
26
|
+
rechecks the remaining live leases. Renewal changes that deadline without self-triggered delivery.
|
|
27
|
+
- There is no new daemon or detached per-message timer. A one-shot producer skips deferred work and
|
|
28
|
+
returns promptly. An existing active delivery observer will reconsider release/expiry; absent an
|
|
29
|
+
observer, pending work waits for the next normal delivery opportunity. A lease does not create a
|
|
30
|
+
process that was not already running.
|
|
31
|
+
- Blocking catch waits for conversation revision or its heartbeat/quiet deadline. A held catch keeps
|
|
32
|
+
its single lease alive and pauses quiet expiry. Core catch and streams use the same storage change
|
|
33
|
+
detector. Held express also waits for changes; throttling waits for its eligibility time.
|
|
34
|
+
- Deferrals are independent of catch filters, presentation claims, full-delivery seen receipts,
|
|
35
|
+
clipped previews and session-local cancellation. Sharing storage detection does not share session
|
|
36
|
+
delivery state. Cancellation never creates a receipt or removes archive history. A deferral cannot
|
|
37
|
+
recall a send already past its final freshness check.
|
|
12
38
|
|
|
13
39
|
## Verification
|
|
14
40
|
|
|
@@ -23,6 +49,9 @@ node --import ./test/sandbox-env.js scripts/benchmark-idle-wait.mjs --workers 30
|
|
|
23
49
|
node --import ./test/sandbox-env.js scripts/benchmark-idle-wait.mjs --workers 300 --mode session --active --no-hints
|
|
24
50
|
```
|
|
25
51
|
|
|
26
|
-
The benchmark creates disposable synthetic history.
|
|
27
|
-
|
|
28
|
-
|
|
52
|
+
The benchmark creates disposable synthetic history. Process-local sharing does not deduplicate
|
|
53
|
+
monitors across processes. Timings measure committed-write-to-projection latency, not native
|
|
54
|
+
message landing, and discovery deadlines are probe bounds rather than an SLA. Historical benchmark
|
|
55
|
+
evidence may describe older HostLedger implementations; it is not current custody authority.
|
|
56
|
+
A long-running extension must reload upgraded code before using the new artifact format; this
|
|
57
|
+
delivery does not install or restart one.
|
package/guides/migration.md
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Square stores its authority in one versioned SQLite artifact. Format 1 kept the
|
|
4
4
|
history as one JSON snapshot; format 2 introduced typed rows; format 3 added the
|
|
5
|
-
`unlisten` activity; format 4
|
|
6
|
-
evidence. Existing artifacts are never
|
|
5
|
+
`unlisten` activity; format 4 added artifact-owned participant seats and delivery
|
|
6
|
+
evidence; format 5 adds independent expiring delivery deferrals. Existing artifacts are never
|
|
7
|
+
converted implicitly.
|
|
7
8
|
|
|
8
9
|
Use the explicit command while all processes using the artifact are stopped:
|
|
9
10
|
|
|
@@ -11,11 +12,24 @@ Use the explicit command while all processes using the artifact are stopped:
|
|
|
11
12
|
square --location /path/to/SQUARE.square migrate
|
|
12
13
|
```
|
|
13
14
|
|
|
14
|
-
The command handles
|
|
15
|
+
The command handles supported formats 2, 3 and 4 in place and publishes only final format 5.
|
|
16
|
+
It does not create a
|
|
15
17
|
backup, stop processes, install a runtime, or update another artifact. A legacy
|
|
16
18
|
HostLedger that needs its own format conversion must be prepared separately with
|
|
17
19
|
the old runtime's offline tooling.
|
|
18
20
|
|
|
21
|
+
## Format 4 additive conversion
|
|
22
|
+
|
|
23
|
+
An exact format-4 artifact needs no legacy owner or route import: that authority already lives
|
|
24
|
+
inside the artifact. Read-only preflight checks framing and stored facts. One SQLite transaction
|
|
25
|
+
adds the empty `delivery_deferrals` table and its owner/generation/expiry lookup index, then
|
|
26
|
+
publishes format 5. The dedicated table stores participant key, captured generation, opaque ID,
|
|
27
|
+
validated match and expiry, separately from catch leases and delivery evidence. All existing
|
|
28
|
+
activities (including metadata), audience, receipts, seats, catch leases, delivery evidence,
|
|
29
|
+
cancellation boundaries and both revision values remain unchanged. No lease is manufactured.
|
|
30
|
+
The conversion does not consult or mutate HostLedger. Failure rolls back the table, index and
|
|
31
|
+
format marker together.
|
|
32
|
+
|
|
19
33
|
## Format 3 preflight and conversion
|
|
20
34
|
|
|
21
35
|
Before writing, migration validates the exact format-3 schema, header, and legacy
|
|
@@ -54,8 +68,8 @@ ledger is read-only and remains untouched, so it is still available for offline
|
|
|
54
68
|
diagnostics. An absent ledger cannot establish standing ownership; an unreadable
|
|
55
69
|
or unsupported ledger refuses before changing the square.
|
|
56
70
|
|
|
57
|
-
After successful preflight, one SQLite transaction creates
|
|
58
|
-
|
|
71
|
+
After successful preflight, one SQLite transaction creates ownership/evidence rows and the
|
|
72
|
+
empty deferral table/index, then publishes format 5. Activity history and indices,
|
|
59
73
|
receipts, lease contents, and the artifact revision are preserved. Legacy routes
|
|
60
74
|
are decoded only during migration and their table is removed. Existing lease
|
|
61
75
|
generations are resolved only for exact owner spellings that match a standing
|
|
@@ -65,12 +79,13 @@ format, and any failure before commit rolls back the conversion.
|
|
|
65
79
|
|
|
66
80
|
Format 2 first rebuilds only the activity table and its indexes to accept
|
|
67
81
|
`unlisten`, then follows the same row-format preflight and conversion. Snapshot
|
|
68
|
-
format 1 is not accepted by this format
|
|
82
|
+
format 1 is not accepted by this migration. Formats 2/3 never publish an intermediate format 4.
|
|
69
83
|
|
|
70
84
|
## What migration refuses
|
|
71
85
|
|
|
72
|
-
Migration validates
|
|
73
|
-
using migration-local codecs
|
|
86
|
+
Migration validates each source's exact row schema. Formats 2/3 include the legacy routes table,
|
|
87
|
+
read using migration-local codecs; format 4 already has seats and delivery evidence. Current
|
|
88
|
+
format 5 has no routes table or runtime route
|
|
74
89
|
reader; migration consumes the old rows only to resolve seats, then drops the legacy
|
|
75
90
|
table as part of the format change.
|
|
76
91
|
|
|
@@ -99,8 +114,8 @@ remains a byte-preserving no-op.
|
|
|
99
114
|
|
|
100
115
|
## Before and after migration
|
|
101
116
|
|
|
102
|
-
Stop every session or service using the square and any process writing its
|
|
103
|
-
HostLedger. Make an offline backup, or use SQLite's backup facilities rather than
|
|
117
|
+
Stop every session or service using the square, and for formats 2/3 freeze any process writing its
|
|
118
|
+
legacy HostLedger. Make an offline backup, or use SQLite's backup facilities rather than
|
|
104
119
|
copying a live database. Migration does not revoke an in-flight remote delivery or
|
|
105
120
|
modify HostLedger evidence. Square writes use ordinary SQLite `DELETE` journals:
|
|
106
121
|
the artifact must be writable and its containing directory must permit journal
|
|
@@ -109,7 +124,10 @@ participation and delivery authority live in the artifact, not writable HOME;
|
|
|
109
124
|
loss of the rebuildable HOME discovery index may limit automatic discovery but
|
|
110
125
|
does not transfer custody or erase durable delivery evidence.
|
|
111
126
|
|
|
112
|
-
Upgrade attached runtimes before reopening the format-
|
|
113
|
-
must not be mixed with it.
|
|
127
|
+
Upgrade all attached runtimes before reopening the format-5 artifact; older runtimes
|
|
128
|
+
must not be mixed with it. Updating a package does not migrate an artifact, and migrating an
|
|
129
|
+
artifact does not install upgraded runtimes. For rollback after a committed conversion, stop all
|
|
130
|
+
clients and restore the offline backup with its compatible older runtime; do not downgrade the
|
|
131
|
+
format marker or drop tables in place. Run the read-only `doctor` command after migration and
|
|
114
132
|
before restarting clients. Repeating migration on a current artifact is a
|
|
115
133
|
byte-preserving no-op.
|
|
@@ -10,10 +10,25 @@ The signal is checked before lifecycle work and before artifact mutation, and it
|
|
|
10
10
|
|
|
11
11
|
Commit boundaries are one-way. Once an activity or observation commits, later cancellation cannot roll it back or turn the result into a retry. A caller may discard the response after the commit, but the committed activity remains recoverable through `history` or a later `catch`; Square does not add a second consumption acknowledgement phase.
|
|
12
12
|
|
|
13
|
-
The single
|
|
13
|
+
The single catch lease and independent delivery-deferral leases have distinct lifetimes. Cancelling
|
|
14
|
+
an active catch releases only that catch's lease. Cancelling a deferral operation before commit
|
|
15
|
+
prevents its mutation; once creation or renewal commits, the lease survives caller cancellation
|
|
16
|
+
until explicit release, expiry or retirement of its captured seat. A committed release is likewise
|
|
17
|
+
not undone. There is no automatic rollback lease or per-message cancellation timer.
|
|
14
18
|
|
|
15
19
|
Ownership and delivery have the same limitation: cancellation can stop work that has not entered the boundary, but it cannot withdraw an ownership effect or a transport send that already happened. The returned committed result and existing degradation classification remain authoritative.
|
|
16
20
|
|
|
21
|
+
Presentation checks cancellation and eligibility before invoking its sink. A rejected sink is not
|
|
22
|
+
success, including a native notification whose landing acknowledgement was cancelled. Once the sink
|
|
23
|
+
succeeds, recording its evidence and seen receipts is mandatory settlement, not a new cancellable
|
|
24
|
+
presentation attempt. It uses the same artifact connection and captured Body/join generation, without
|
|
25
|
+
inheriting the pre-effect operation signal or a signal captured when opening that artifact. Claim
|
|
26
|
+
release after a cancelled/failed attempt uses the same independent cleanup lifetime and writes no
|
|
27
|
+
success receipt. Both settle through the normal authority gate, with a five-second storage budget;
|
|
28
|
+
explicit connection close still cancels them. Storage failure is reported, not converted into success.
|
|
29
|
+
A batch settles all successfully rendered items before closing its connections, even if one settlement
|
|
30
|
+
fails. No settlement reopens a path or grants a replacement seat authority.
|
|
31
|
+
|
|
17
32
|
The same `Square`/`Participant` methods serve SDK and adapters; see [resource API](square-api.md). Join, listener and lifecycle essential observations use commit-time facts. Join's optional entry enrichment may be unavailable without rejecting its successful receipt. CLI express passes its abort signal through the initial `Square.at`; raw draft saving precedes a separate uncancelled recovery reopen.
|
|
18
33
|
|
|
19
34
|
Pi notification cancellation is separate and unchanged.
|