@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.
Files changed (146) hide show
  1. package/dist/activity-feed.d.ts +3 -5
  2. package/dist/activity-feed.js +16 -14
  3. package/dist/artifact.js +3 -3
  4. package/dist/attention.d.ts +19 -0
  5. package/dist/attention.js +30 -0
  6. package/dist/boundary-presentation.d.ts +2 -2
  7. package/dist/boundary-presentation.js +33 -40
  8. package/dist/catch-decisions.d.ts +5 -8
  9. package/dist/catch-decisions.js +45 -25
  10. package/dist/claude-delivery-status.js +10 -4
  11. package/dist/claude-delivery.js +19 -11
  12. package/dist/claude-mod.js +24 -53
  13. package/dist/cli/context.d.ts +9 -4
  14. package/dist/cli/context.js +30 -27
  15. package/dist/cli/express-presentation.d.ts +3 -1
  16. package/dist/cli/express-presentation.js +28 -16
  17. package/dist/cli/json.d.ts +3 -0
  18. package/dist/cli/json.js +14 -0
  19. package/dist/cli/mcp-server-command.js +2 -2
  20. package/dist/cli/observation-commands.d.ts +1 -3
  21. package/dist/cli/observation-commands.js +43 -56
  22. package/dist/cli/program.js +3 -3
  23. package/dist/cli/registry.js +7 -6
  24. package/dist/cli/square-commands.d.ts +0 -2
  25. package/dist/cli/square-commands.js +30 -44
  26. package/dist/codex-hook.d.ts +3 -2
  27. package/dist/codex-hook.js +13 -4
  28. package/dist/codex-queue.js +4 -4
  29. package/dist/decisions.d.ts +5 -16
  30. package/dist/decisions.js +9 -33
  31. package/dist/delivery-attempts.d.ts +52 -0
  32. package/dist/delivery-attempts.js +73 -0
  33. package/dist/delivery-deferral.js +2 -2
  34. package/dist/delivery-effects.d.ts +139 -0
  35. package/dist/delivery-effects.js +163 -0
  36. package/dist/delivery-health.d.ts +0 -1
  37. package/dist/delivery-health.js +1 -3
  38. package/dist/delivery-operations.d.ts +2 -2
  39. package/dist/delivery-operations.js +76 -101
  40. package/dist/delivery-primitives.d.ts +5 -0
  41. package/dist/delivery-primitives.js +3 -0
  42. package/dist/delivery.d.ts +1 -1
  43. package/dist/delivery.js +7 -6
  44. package/dist/help.js +14 -14
  45. package/dist/http-delivery.d.ts +16 -0
  46. package/dist/http-delivery.js +101 -0
  47. package/dist/inbox.d.ts +9 -22
  48. package/dist/inbox.js +23 -27
  49. package/dist/index.d.ts +1 -3
  50. package/dist/index.js +1 -1
  51. package/dist/mcp.d.ts +3 -0
  52. package/dist/mcp.js +23 -24
  53. package/dist/model.d.ts +12 -21
  54. package/dist/model.js +5 -8
  55. package/dist/notifications.d.ts +1 -1
  56. package/dist/notifications.js +8 -5
  57. package/dist/operation-presentation.d.ts +6 -6
  58. package/dist/operation-presentation.js +55 -58
  59. package/dist/owner.d.ts +11 -0
  60. package/dist/owner.js +32 -0
  61. package/dist/paseo-delivery.d.ts +4 -1
  62. package/dist/paseo-delivery.js +7 -12
  63. package/dist/paseo-state.d.ts +2 -2
  64. package/dist/paseo-state.js +6 -4
  65. package/dist/plugins/opencode/delivery.js +27 -15
  66. package/dist/ports.d.ts +18 -47
  67. package/dist/presentation-operations.d.ts +14 -19
  68. package/dist/presentation-operations.js +51 -61
  69. package/dist/presentation.d.ts +7 -17
  70. package/dist/presentation.js +46 -91
  71. package/dist/receive.d.ts +66 -0
  72. package/dist/receive.js +54 -0
  73. package/dist/runtime.d.ts +1 -8
  74. package/dist/runtime.js +2 -17
  75. package/dist/seats.d.ts +2 -3
  76. package/dist/seats.js +7 -15
  77. package/dist/session-lifecycle.d.ts +6 -2
  78. package/dist/session-lifecycle.js +39 -11
  79. package/dist/sqlite-square.d.ts +36 -23
  80. package/dist/sqlite-square.js +162 -211
  81. package/dist/square-api.d.ts +16 -11
  82. package/dist/square-api.js +27 -23
  83. package/dist/square-core.d.ts +0 -14
  84. package/dist/square-core.js +0 -9
  85. package/dist/square-domain.d.ts +4 -2
  86. package/dist/square-domain.js +65 -52
  87. package/dist/square-facade.d.ts +47 -105
  88. package/dist/square-index.d.ts +5 -13
  89. package/dist/square-index.js +13 -38
  90. package/dist/square-legacy-framing.d.ts +2 -0
  91. package/dist/square-legacy-framing.js +8 -0
  92. package/dist/square-location.d.ts +1 -14
  93. package/dist/square-location.js +4 -70
  94. package/dist/square-migration-host-ledger.d.ts +2 -2
  95. package/dist/square-migration-host-ledger.js +6 -5
  96. package/dist/square-migration.d.ts +4 -1
  97. package/dist/square-migration.js +93 -38
  98. package/dist/square-receive.d.ts +6 -0
  99. package/dist/square-receive.js +18 -0
  100. package/dist/square-sqlite.d.ts +9 -3
  101. package/dist/square-sqlite.js +139 -114
  102. package/dist/square-tools.d.ts +7 -10
  103. package/dist/square-tools.js +21 -61
  104. package/dist/stream.js +4 -5
  105. package/dist/views.d.ts +23 -13
  106. package/dist/views.js +47 -53
  107. package/dist/wakes.d.ts +1 -5
  108. package/dist/wakes.js +4 -6
  109. package/examples/symmetric-domains.mjs +250 -0
  110. package/guides/application-facade-delivery-evidence.md +6 -6
  111. package/guides/attention-primitive.md +32 -0
  112. package/guides/change-waits.md +2 -2
  113. package/guides/claude-native.md +18 -9
  114. package/guides/codex-delivery.md +17 -6
  115. package/guides/delivery-primitives.md +180 -0
  116. package/guides/mcp-connection-lifetime.md +1 -1
  117. package/guides/mcp.md +3 -3
  118. package/guides/migration.md +148 -151
  119. package/guides/operation-cancellation.md +1 -1
  120. package/guides/participant-ownership.md +78 -47
  121. package/guides/pi-tools.md +34 -5
  122. package/guides/primitive-catch-consolidation.md +5 -5
  123. package/guides/primitive-history-consolidation.md +2 -2
  124. package/guides/primitive-status-consolidation.md +3 -3
  125. package/guides/primitive-storage-consolidation.md +1 -1
  126. package/guides/receive-primitive.md +90 -0
  127. package/guides/row-runtime-delivery-evidence.md +1 -1
  128. package/guides/row-storage-design.md +22 -28
  129. package/guides/square-api.md +314 -91
  130. package/guides/symmetric-domains.md +92 -0
  131. package/package.json +14 -1
  132. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  133. package/plugins/claude/skills/square/SKILL.md +2 -6
  134. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  135. package/plugins/opencode/delivery.ts +27 -17
  136. package/plugins/paseo/client/palette.ts +18 -18
  137. package/plugins/paseo/client/square-message.tsx +17 -23
  138. package/plugins/pi/square-pi.js +17 -17
  139. package/skills/square/SKILL.md +2 -6
  140. package/dist/square-backend.d.ts +0 -13
  141. package/dist/square-backend.js +0 -1
  142. package/dist/square-stdio.d.ts +0 -22
  143. package/dist/square-stdio.js +0 -235
  144. package/dist/stdio-server.d.ts +0 -3
  145. package/dist/stdio-server.js +0 -212
  146. package/guides/backends.md +0 -193
@@ -1,6 +1,6 @@
1
1
  # MCP connection lifetime
2
2
 
3
- An MCP server owns one artifact connection for its configured square. Its lifetime ends on close or disconnect; create a new server instance for a new connection. Replacing an artifact file under a running server remains unsupported. Every tool call opens an actual `Square` handle with that request's captured environment, default participant and harness session; `square.participant()` performs nonmutating name selection. Calls with an explicit `location` override instead open and close a per-call connection, leaving the configured-square owner untouched. Only the default connection is shared: authorization, presence, activity facts and cancellation remain operation-scoped. The artifact port already serializes transactions on its connection.
3
+ An MCP server owns one artifact connection for its configured square. Its lifetime ends on close or disconnect; create a new server instance for a new connection. Replacing an artifact file under a running server remains unsupported. Every tool call opens an identity-free `Square` handle with that request's resource environment; harness identity is passed to `join(name, {body, wakeAddress?})` or `selectOwnSeat(square, body, as?)`, never captured by the resource. Calls with an explicit `location` override instead open and close a per-call connection, leaving the configured-square owner untouched. Only the default connection is shared: authorization, presence, activity facts and cancellation remain operation-scoped. The artifact port already serializes transactions on its connection.
4
4
 
5
5
  `createSquareResourceOwner()` is the explicit lifetime boundary. `Square.at({resources, ...})` borrows its connection; closing a request handle leaves the owner open. Ordinary callers open and close their own Square. Cross-square `inbox()` reads remain independent. The connection captures no request abort signal. Explicit server close and transport disconnect share one shutdown path: the SDK cancels requests, handlers finish their cleanup, then the connection closes.
6
6
 
package/guides/mcp.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  Square provides a stdio MCP server through `square mcp-server`.
4
4
 
5
- Set `SQUARE_LOCATION` to the default `.square` artifact path; calls may override it with `location`. Relative paths use the server's startup working directory. Join with an explicit `as` name. Later calls may omit `as` only when the artifact recognizes exactly one current name held by the call's exact Body (`channel`, `session`, optional `agent`); zero or multiple matches refuse and require explicit selection. An explicit name selects a voice but never grants custody. Request metadata supplies the live Codex (`threadId`) or OpenCode (`ai.opencode/sessionID`) coordinate when present; otherwise a reliable startup coordinate is required for participant writes. Read-only calls do not need a Body.
5
+ Set `SQUARE_LOCATION` to the default `.square` artifact path; calls may override it with `location`. Relative paths use the server's startup working directory. Join with an explicit `as` name. Later calls may omit `as` only when the artifact recognizes exactly one current name held by the call's exact opaque owner; zero or multiple matches refuse and require explicit selection. An explicit name selects a voice but never grants custody. Request metadata supplies the live Codex (`threadId`) or OpenCode (`ai.opencode/sessionID`) coordinate when present, encoded through `harnessOwner`; otherwise the server-configured owner applies. CLI startup resolves `--owner`/`SQUARE_OWNER`, a complete harness coordinate, or the local user owner in that order, refusing incomplete harness identity. Owner is never a model tool argument. Read-only calls do not need an owner.
6
6
 
7
- The server exposes `join`, `express`, `catch`, `history`, `attention`, `hold`, `done`, and `status`. `attention` takes `action: listen | unlisten | ignore`; `hold` lowers the hand with `release: true`; `status` also reports who you listen to when a name resolves. `catch` consumes activities. `history` is read-only and accepts stable `act/<index>` cursors. Tool results include JSON `structuredContent` and a text representation of the same data. Square failures are returned as tool errors with a structured `{ error: { code, message, facts? } }` value.
7
+ The server exposes `join`, `express`, `catch`, `history`, `attention`, `done`, and `status`. `attention` takes `action: listen | unlisten | ignore`; `status` also reports who you listen to when a name resolves. `catch` consumes activities. `history` is read-only and accepts stable `act/<index>` cursors. All seven tools return the same human-readable text as the CLI in `content`, without `outputSchema` or `structuredContent`. Square failures set `isError: true` and return readable refusal and recovery text in `content`. Clients keep their native MCP result envelope; machine-readable domain results remain available through the SDK.
8
8
 
9
9
  Claude's bundled plugin configures this server automatically. Its mod supplies live session/cwd/subagent attribution through an out-of-band tool-use-ID association, not AI-visible arguments; see [Claude call identity](claude-native.md#mcp-tools-and-call-identity). The bundled endpoint requires that association rather than falling back to its startup environment.
10
10
 
11
- Tool definitions and argument-to-`Square`/`Participant` calls live in `square-tools.ts`, shared with the [Pi native tools](pi-tools.md). Join serializes the handle's name and receipt fields, never resource internals. All callers use the [same concrete resource API](square-api.md), with no alternate factory or generic outcome envelope. `express.noMention` disables body @name scanning without disabling explicit mentions or reply attention.
11
+ Tool definitions and argument-to-`Square`/`Participant` calls live in `square-tools.ts`, shared with the [Pi native tools](pi-tools.md). Domain receipts stay internal to these adapters; tools return only their rendered text. All callers use the [same concrete resource API](square-api.md), with no alternate factory or generic outcome envelope. `express.noMention` disables body @name scanning without disabling explicit mentions or reply attention.
12
12
 
13
13
  Configure a client with command `npx` and arguments `--yes --package @astrosheep/square square mcp-server`, or use the installed `square` executable with argument `mcp-server`. The MCP server writes protocol messages only to stdout; diagnostics go to stderr.
@@ -1,159 +1,156 @@
1
1
  # Migrating an existing square
2
2
 
3
- Square stores its authority in one versioned SQLite artifact. Format 1 kept the
4
- history as one JSON snapshot; format 2 introduced typed rows; format 3 added the
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.
3
+ Square stores its authority in one versioned SQLite artifact. Format 1 kept a snapshot;
4
+ format 2 introduced typed facts; format 3 added `unlisten`; format 4 moved participant custody
5
+ and delivery evidence into the artifact; format 5 added independent delivery deferrals.
6
+ **Format 6 separates participant membership from host attachment; format 7 replaces harness Body
7
+ coordinates with opaque owners; format 8 removes Square hold/resume activities and held state.**
8
+ Existing artifacts are never converted implicitly.
8
9
 
9
- Use the explicit command while all processes using the artifact are stopped:
10
+ Stop all processes using the artifact, make an offline backup, then run:
10
11
 
11
12
  ```sh
12
13
  square --location /path/to/SQUARE.square migrate
14
+ square --location /path/to/SQUARE.square doctor
13
15
  ```
14
16
 
15
- The command handles supported formats 2, 3 and 4 in place and publishes only final format 5.
16
- It does not create a
17
- backup, stop processes, install a runtime, or update another artifact. A legacy
18
- HostLedger that needs its own format conversion must be prepared separately with
19
- the old runtime's offline tooling.
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
-
33
- ## Format 3 preflight and conversion
34
-
35
- Before writing, migration validates the exact format-3 schema, header, and legacy
36
- rows read-only. The configured legacy HostLedger must be frozen along with the
37
- artifact. Its latest owner row for each participant at this artifact is selected
38
- by source `(effectiveAt, seq)` only: no wall clock, retention, expiry, or ownership
39
- history reconstruction is involved.
40
-
41
- - A legacy owner Body (`channel`, `session`, optional `agent`) establishes a
42
- standing seat only when it agrees with all of that name's local route Bodies
43
- and the establishing last join is verified. An owner without routes needs no
44
- wake address. Without a legacy owner, one unambiguous Body recorded by that
45
- participant's own routes establishes its seat instead. An absent agent means
46
- the top-level body; a known agent is preserved and compared exactly. Repeated
47
- matching registrations are acceptable; a wake address is carried forward only
48
- when the routes agree on it. Routes are location-bound: moving an artifact copy
49
- to another path does not establish that its original routes apply there. An
50
- off-location copy is not ownership-recovery evidence for the original location.
51
- - Without an owner or any route Body, the participant stays standing without a
52
- seat. Its source participant state, activities, attention, receipts, landed
53
- audience and catch lease are preserved. No Body, route or departure is invented;
54
- the name has no write authority or delivery eligibility. Doctor retains its
55
- strict history-derived consistency checks.
56
- - A Body may own several participant seats. Each name retains its own seat,
57
- establishing join generation and wake address; sharing does not merge names or
58
- choose one name over another. All evidence for each name must agree: if its
59
- routes disagree with each other or with its legacy owner Body, that name stays
60
- standing without a seat. Disagreement does not abort migration and neither side
61
- is chosen as authoritative. Unverifiable joins still refuse before publication.
62
- Migration does not guess or synthesize authority.
63
- - Evidence structure is validated before placement, including rows that cannot be
64
- imported. Malformed fields, invalid activity IDs or malformed native correlation
65
- still refuse without changing either source.
66
- - Wake/presentation evidence for a recovered seat is imported only when its Body
67
- agrees and it names an existing directed say after the verified current join.
68
- A missing seat, conflicting session/agent/channel or native harness, unknown
69
- participant, or activity outside the generation causes the row to be skipped,
70
- not a failed conversion. Nothing is reassigned to another owner.
71
- - Done participants get **no seat or completed coordinate**. Only native wake
72
- correlation directly attributable to their verified latest join and a directed
73
- activity between that join and done is retained for retired-token recognition.
74
- It does not authorize admission, completion, or presentation. Conflicting
75
- completed Body mappings, duplicate claim tokens and ambiguous attempt numbering
76
- cause all affected rows to be skipped, rather than selecting a winner. A single
77
- record without an attempt number receives attempt 0. Migrated Claude native
78
- addresses include `sessionId` on both seats and evidence routes.
79
-
80
- This is deliberately a narrow, lossy conversion, not a general history importer.
81
- Conversation facts (activity IDs, bodies and audience), receipts and source revision
82
- meaning survive. Replay-critical delivery coordinates, outcomes and native
83
- correlation survive direct mappings. Completed non-native evidence and noncritical
84
- metadata—including old presence history, epochs, diagnostic messages, native
85
- observation histories and arbitrary legacy fields—are not imported. The legacy
86
- ledger is read-only and remains untouched, so it is still available for offline
87
- diagnostics. An absent ledger still permits route-derived seats; an unreadable
88
- or unsupported ledger refuses before changing the square. Evidence without a
89
- current or verified completed owner is skipped and counted. The source ledger is
90
- retained for offline diagnosis; skipped evidence never grants delivery authority.
91
-
92
- After successful preflight, one SQLite transaction imports seats and evidence
93
- and adds delivery deferral support, then publishes the current format. All
94
- existing activities, their indices and audience, participant state, attention,
95
- receipts, lease contents, and the source revision are preserved. Legacy routes
96
- are consumed only during migration. Lease generations are resolved only for exact
97
- owner spellings that match a standing participant; other lease rows retain their
98
- legacy, unbound generation. The command reports recovered seats and unowned
99
- standing names, and recovered seats whose Body is shared with another recovered
100
- seat, and skipped evidence rows, alongside the activity, receipt, lease and source
101
- route counts. The shared count measures seats, not distinct Bodies or shared groups.
102
- The skipped count includes every wake/presentation row read from the selected
103
- legacy ledger but not imported, including completed non-native diagnostics.
104
- The new delivery revision starts at zero. There is no intermediate published
105
- format, and any failure before commit rolls back the conversion.
106
-
107
- Format 2 first rebuilds only the activity table and its indexes to accept
108
- `unlisten`, then follows the same row-format preflight and conversion. Snapshot
109
- format 1 is not accepted by this migration. Formats 2/3 never publish an intermediate format 4.
110
-
111
- ## What migration refuses
112
-
113
- Migration validates each source's exact row schema. Formats 2/3 include the legacy routes table,
114
- read using migration-local codecs; format 4 already has seats and delivery evidence. Current
115
- format 5 has no routes table or runtime route
116
- reader; migration consumes the old rows only to resolve seats, then drops the legacy
117
- table as part of the format change.
118
-
119
- Unrelated databases, unsupported formats, non-`DELETE` journal modes, unknown
120
- schemas, malformed headers, rows or native correlation are refused. The source artifact is preserved on every
121
- refusal. Migration is not a repair command and never rewrites journal mode to make
122
- conversion possible. A HostLedger that itself requires migration must be migrated
123
- separately before Square migration can import its delivery evidence.
124
-
125
- ## Conservative retired-token restriction
126
-
127
- For an old artifact conversion, **any nonempty legacy `host_native_tokens` table
128
- makes preflight refuse**, including tokens for other sessions. Those token-only
129
- Claude tombstones are replay protection, not diagnostics: they contain only a
130
- session and token, with no artifact, participant or activity coordinate. The
131
- converter cannot safely assign them a join generation. This is deliberately a
132
- ledger-wide ambiguity refusal, not a claim that each token belongs to the square
133
- being converted. Even a tombstone duplicated by retained evidence is refused by
134
- this conservative implementation; no deduplication or history inference is done.
135
-
136
- Keep the original ledger and artifact intact. Resolve the ambiguity offline with
137
- replay protection preserved; **do not delete tombstones just to bypass preflight**.
138
- An already-current artifact returns without consulting the legacy ledger and
139
- remains a byte-preserving no-op.
140
-
141
- ## Before and after migration
142
-
143
- Stop every session or service using the square, and for formats 2/3 freeze any process writing its
144
- legacy HostLedger. Make an offline backup, or use SQLite's backup facilities rather than
145
- copying a live database. Migration does not revoke an in-flight remote delivery or
146
- modify HostLedger evidence. Square writes use ordinary SQLite `DELETE` journals:
147
- the artifact must be writable and its containing directory must permit journal
148
- creation and removal. The legacy ledger needs read access only. After conversion,
149
- participation and delivery authority live in the artifact, not writable HOME;
150
- loss of the rebuildable HOME discovery index may limit automatic discovery but
151
- does not transfer custody or erase durable delivery evidence.
152
-
153
- Upgrade all attached runtimes before reopening the format-5 artifact; older runtimes
154
- must not be mixed with it. Updating a package does not migrate an artifact, and migrating an
155
- artifact does not install upgraded runtimes. For rollback after a committed conversion, stop all
156
- clients and restore the offline backup with its compatible older runtime; do not downgrade the
157
- format marker or drop tables in place. Run the read-only `doctor` command after migration and
158
- before restarting clients. Repeating migration on a current artifact is a
159
- byte-preserving no-op.
17
+ The command handles supported formats 2–7 in place and publishes only final format 8, in one
18
+ transaction. It does not make a backup, stop processes, install a runtime or update another
19
+ artifact. For formats 2/3, also freeze and back up the legacy HostLedger used for owner/evidence
20
+ import. A legacy ledger needing its own conversion must be prepared separately with its old
21
+ runtime's offline tooling. Snapshot format 1 is not accepted by this conversion.
22
+
23
+ Format 7 to 8 deletes hold/resume activities and rows keyed to them, clears replies that point to
24
+ deleted activities, and preserves every surviving activity index. Gaps remain legal; `lastActIndex`
25
+ is the greatest surviving index and `nextActIndex` never reuses a deleted index.
26
+
27
+ ## Membership and attachment after conversion
28
+
29
+ Membership owns a participant's join/done history, read horizon and outgoing listens. Attachment
30
+ says whether its exact owner is currently executing for it. There are three distinct endings:
31
+
32
+ - A participant's voluntary `done` ends membership. A later explicit participant join is required
33
+ to return; no host start/resume revives it.
34
+ - Pi shutdown, Codex SessionEnd and Claude session end detach only. No activity lands; membership,
35
+ listens, read horizon, receipts and delivery evidence stay intact. Wake readiness, catch ownership
36
+ and delivery deferrals are cleared. Away participants still receive landed audience and accumulate
37
+ unread activity, but cannot write or receive proactive delivery. Away has no timeout.
38
+ - Host-confirmed permanent deletion ends membership through fenced
39
+ `endHeld(name, {owner, generation})`, including for a detached seat. OpenCode `session.deleted`
40
+ uses this path.
41
+
42
+ On start/resume, hosts discover only their exact owner's standing seats with `seatsOf(owner)` and
43
+ restore the detached ones with `reattach(name, {owner, wakeAddress?})` before arming delivery.
44
+ Same-owner `join` or `reattach` preserves the generation, read horizon and listens, so mentions
45
+ received while away remain unread. Reattachment returns `null` for absent, foreign or done seats;
46
+ it never creates membership. Explicit takeover still performs `done -> join` with a fresh
47
+ generation, even when taking over an away participant; old cleanup/recovery cannot reclaim it.
48
+ The owner API requires this explicit format migration; see the
49
+ [SDK breaking migration](square-api.md#sdk-breaking-migration-opaque-owners).
50
+
51
+ ## Conversion from formats 6, 5 and 4
52
+
53
+ Format 6 -> 7 maps each seat's `channel`, `session` and optional `agent` through the adapter codec
54
+ into one owner string, preserving its generation, attachment and wake. Evidence, deferrals, catch
55
+ leases, cancellations, conversation facts and both revisions stay unchanged. Unmappable legacy
56
+ coordinates (for example an encoded owner exceeding 512 UTF-8 bytes) refuse without changing the
57
+ source; they are never truncated or reassigned. Keep the source backup for offline resolution.
58
+
59
+ Formats 4/5 first acquire format 6 semantics in that same transaction: every existing seat becomes
60
+ attached. Format 4 also gains empty deferral support. No detached seat is invented. Formats 4–6
61
+ never consult HostLedger; any failure rolls back schema changes and final publication together.
62
+
63
+ Existing done participants stay done. Before format 6, a host-cleanup `done` was indistinguishable
64
+ from a voluntary leave; migration conservatively treats both as voluntary. It does not infer
65
+ attachment history, reconstruct departures or provide a host recovery path for completed seats.
66
+
67
+ ## Verified import from formats 3 and 2
68
+
69
+ Formats 2/3 need owner and delivery evidence import. Before writing, migration validates exact
70
+ source framing, header and legacy facts read-only. The frozen legacy HostLedger's latest owner
71
+ for each participant at this artifact is selected by source `(effectiveAt, seq)` only: no wall
72
+ clock, retention, expiry or ownership-history reconstruction is involved.
73
+
74
+ - A legacy owner Body (`channel`, `session`, optional `agent`) establishes a standing, attached
75
+ seat only when it agrees with all of that name's local route Bodies and the establishing join
76
+ is verified. An owner without routes needs no wake address. Without an owner, one unambiguous
77
+ route Body can establish the seat. An absent agent means the top-level Body; a known agent is
78
+ preserved and compared exactly. Matching repeated registrations are acceptable; wake readiness
79
+ carries forward only when the routes agree. Routes are location-bound: moving an artifact copy
80
+ does not establish that its original routes apply there.
81
+ - Without a verifiable owner or route Body, the participant stays standing without custody.
82
+ Membership, history, attention, receipts, landed audience and catch ownership are preserved.
83
+ No Body, route, attachment or departure is guessed. Its name grants no write/delivery authority.
84
+ - A Body may own several names; each retains its own generation and wake readiness. Sharing
85
+ does not merge names or choose a winner. Conflicting owner/route Bodies leave that name
86
+ standing without custody; disagreement does not abort conversion. An unverifiable establishing
87
+ join still refuses before publication.
88
+ - Evidence structure is validated before placement, including evidence that cannot be imported.
89
+ Malformed facts, activity IDs or native correlation refuse without changing either source.
90
+ - Wake/presentation evidence imports only when its Body agrees and it names an existing directed
91
+ say after the verified current join. Missing custody, conflicting session/agent/channel or native
92
+ harness, unknown participants and activity outside the generation cause evidence to be skipped,
93
+ never reassigned to another owner.
94
+ - Done participants gain no seat or recovery authority. Only native correlation directly
95
+ attributable to their verified latest join and directed activity before departure is retained
96
+ for retired-token recognition. It authorizes neither admission nor presentation. Ambiguous
97
+ completed-Body mappings, duplicate claim tokens and ambiguous attempt numbering cause all
98
+ affected evidence to be skipped, rather than selecting a winner. A record without an attempt
99
+ number receives attempt 0. Migrated Claude wake addresses retain their native session identity.
100
+
101
+ This is a narrow, deliberately lossy conversion, not a general history importer. Conversation,
102
+ activity IDs, bodies, audience, receipts, source revision meaning and directly verified replay
103
+ protection survive. Completed non-native evidence and noncritical metadata—including old presence
104
+ history, epochs, diagnostic messages, native observation histories and arbitrary legacy extras—do
105
+ not import. Evidence without current or verified completed ownership is skipped and counted. The
106
+ read-only legacy ledger remains available for offline diagnosis; an absent ledger permits route-
107
+ derived custody, but an unreadable or unsupported ledger refuses before changing the square.
108
+
109
+ One transaction imports verified authority/evidence, adds deferral and attachment support, applies
110
+ the format-7-to-8 deletion, and publishes format 8. Existing conversation and membership facts are
111
+ preserved apart from the deleted hold/resume rows and cleared replies; no intermediate format is
112
+ published. Legacy catch ownership is generation-bound only for exact standing owner
113
+ spellings; otherwise it stays unbound. Legacy routes are consumed only during migration and are
114
+ not a live authority source. The new delivery revision starts at zero. Format 2 first adds support
115
+ for `unlisten`, then follows the same conversion.
116
+
117
+ The receipt reports recovered seats, unowned standing names, seats sharing a recovered Body,
118
+ skipped evidence, and source activity/receipt/catch/route counts. The shared count measures seats,
119
+ not distinct Bodies or groups. Skipped includes every legacy wake/presentation record read but
120
+ not imported, including completed non-native diagnostics.
121
+
122
+ ## Conservative refusal and retired-token protection
123
+
124
+ Unrelated databases, unsupported formats, non-`DELETE` journal mode, unknown framing, malformed
125
+ headers/facts or native correlation refuse without source changes. Migration is not repair and
126
+ never rewrites journal mode to make conversion possible.
127
+
128
+ For formats 2/3, **any nonempty legacy token-only tombstone collection makes preflight refuse**,
129
+ including tokens for other sessions. Those Claude tombstones are replay protection, not diagnostics:
130
+ they identify only a session and token, without artifact, participant, activity or join generation.
131
+ The converter cannot safely assign them. This is a ledger-wide conservative ambiguity refusal,
132
+ not a claim that each token belongs to the artifact being converted. Even a token duplicated by
133
+ retained evidence refuses; conversion does not deduplicate or infer history to bypass protection.
134
+
135
+ Keep the original ledger and artifact intact. Resolve ambiguity offline with replay protection
136
+ preserved; **do not delete tombstones just to bypass preflight**. An already-current artifact
137
+ returns without consulting the legacy ledger and remains a byte-preserving no-op.
138
+
139
+ ## Before reopening and rollback
140
+
141
+ Freeze every artifact user and, for formats 2/3, every legacy-ledger writer. Make an offline backup
142
+ or use SQLite backup facilities rather than copying a live database. Migration cannot revoke an
143
+ in-flight remote delivery. Writes use ordinary SQLite `DELETE` journals: the artifact and its
144
+ containing directory must permit writing and journal creation/removal. A legacy ledger needs read
145
+ access only. Losing the rebuildable HOME discovery index may delay restoration/delivery but cannot
146
+ transfer custody or erase evidence. The disposable index changes to `{location, owner}`; old index
147
+ rows are ignored. Rejoin with the exact migrated owner to republish discovery (or let that same
148
+ harness reconnect). This preserves generation, listens and unread activity; do not use takeover
149
+ merely to rebuild the index.
150
+
151
+ Upgrade all attached runtimes before reopening format 8; older runtimes must not be mixed with it.
152
+ Updating a package does not migrate an artifact, and migration does not install upgraded runtimes.
153
+ After conversion, run read-only `doctor` before restarting clients. To roll back a committed
154
+ conversion, stop all clients and restore the offline backup with its compatible older runtime;
155
+ do not downgrade the format marker or remove facts in place. Repeating current-format migration
156
+ is a byte-preserving no-op.
@@ -21,7 +21,7 @@ Ownership and delivery have the same limitation: cancellation can stop work that
21
21
  Presentation checks cancellation and eligibility before invoking its sink. A rejected sink is not
22
22
  success, including a native notification whose landing acknowledgement was cancelled. Once the sink
23
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
24
+ presentation attempt. It uses the same artifact connection and captured owner/join generation, without
25
25
  inheriting the pre-effect operation signal or a signal captured when opening that artifact. Claim
26
26
  release after a cancelled/failed attempt uses the same independent cleanup lifetime and writes no
27
27
  success receipt. Both settle through the normal authority gate, with a five-second storage budget;
@@ -1,60 +1,91 @@
1
1
  # Participant ownership
2
2
 
3
- A participant has one current artifact-owned seat: its normalized participant name, exact Body
4
- (`channel`, `session`, optional `agent`), establishing join generation, and optional wake address.
5
- Display spelling and discovery-index entries never grant write authority. The artifact transaction
6
- is the sole custody authority; there is no writable HostLedger ownership layer.
7
-
8
- ## Captured handles and lifecycle
9
-
10
- `Square.at` captures an explicit Body for writes. `participant(name?)` selects a handle without
11
- joining; `recognize()` returns current handles held by that exact Body. A name alone is not
12
- permission to mutate. Read-only archive operations need no Body. See [square-api.md](square-api.md)
13
- for the public resource lifetime and selection contract.
14
-
15
- - `join(name)` atomically lands the join and stores its seat. Same-body reconnect preserves its
16
- generation and may refresh the wake address. A foreign Body receives `already_joined` rather
17
- than taking ownership implicitly.
18
- - `join(name, {takeover:true})` explicitly completes the captured old seat and establishes a new
19
- one with `done -> join` in one transaction. Its compare-and-set generation prevents a concurrent
20
- takeover from being silently replaced.
21
- - Participant mutations carry the captured seat into the transaction. The gate checks both Body
22
- and generation, including when the same Body later reacquires the name. An old handle never
23
- becomes fresh because a name matches again.
24
- - `done` retires only the current captured seat, catch lease and delivery deferrals. A later join
25
- establishes a fresh generation. Completed coordinates retained for exact-body diagnostics are
26
- not current write authority. Owner-session cleanup uses the same fence and cannot complete a
27
- replacement seat.
28
- - Stdio retains the selected Participant in a connection-local reference. Each operation uses that
29
- captured handle instead of reselecting authority by name. Rejoin/takeover therefore fences old
30
- remote handles just as it does local SDK handles.
31
-
32
- The HOME discovery index is rebuildable candidate-path data. Losing it can delay discovery but
33
- cannot transfer a seat or erase delivery evidence. A wake address is only transport readiness for
34
- an existing seat, not an alternative ownership fact.
3
+ Membership and host attachment are different facts. Membership says a participant is in the
4
+ square; it owns their join history, read horizon and outgoing listens. Attachment says whether
5
+ an exact opaque owner is executing for that participant now.
6
+ A standing seat may be attached or detached. The artifact transaction is the sole custody
7
+ authority; display spelling, wake addresses and the rebuildable HOME discovery index never
8
+ grant ownership. There is no writable HostLedger ownership layer.
9
+
10
+ Owners are opaque non-secret strings, not harness coordinates or authentication. Harness adapters
11
+ encode their own identity; terminal owners use `user:<OS user>@<host>` and are never automatically
12
+ detached or ended by a harness. A seat's optional wake route is self-contained transport readiness,
13
+ not an owner or an alternative custody fact.
14
+
15
+ ## Three endings
16
+
17
+ - **The participant leaves:** `Participant.done()` ends membership, removes its seat, catch
18
+ lease and delivery deferrals, and removes outgoing listens. A later explicit `join` establishes
19
+ a fresh generation and read horizon. No host start or resume brings a voluntary leave back.
20
+ - **The host session ends:** `detach(name, {owner, generation})` detaches only that exact owner and
21
+ join generation. It clears wake readiness, catch ownership and delivery deferrals, without landing
22
+ an activity or changing membership, listens or the read horizon. A detached participant is
23
+ **away**, still receives normal landed audience and accumulates unread activity, but cannot write
24
+ or receive proactive delivery. Away has no timeout; a session that never returns simply stays away.
25
+ - **The host confirms permanent deletion:** `endHeld(name, {owner, generation})` ends membership
26
+ with a fenced `done`, including for a detached seat. OpenCode's `session.deleted` uses this path.
27
+ Pi shutdown, Codex SessionEnd and Claude session end do not prove permanent deletion and therefore
28
+ detach.
29
+
30
+ An explicit takeover remains `done -> join` in one transaction, whether the old seat is attached
31
+ or detached. It transfers the name to the new owner with a fresh generation; old cleanup and
32
+ recovery cannot reclaim it.
33
+
34
+ ## Captured handles and restoration
35
+
36
+ A `Square` captures no participant identity. `participant(name, {owner})` selects an attached,
37
+ standing seat held by that exact owner without joining; never-joined, done, foreign and detached
38
+ seats refuse with join recovery facts, not ghost handles. `seatsOf(owner)` is a read-only discovery
39
+ surface for the owner's standing seats as `{name, generation, attachment}`. It includes attached and
40
+ detached seats, but does not join or return done participants. See [square-api.md](square-api.md)
41
+ for resource lifetimes and adapter selection.
42
+
43
+ - `join(name, {owner, wakeAddress?})` atomically establishes membership and its seat when absent.
44
+ A same-owner reconnect preserves the establishing generation. On a detached seat it also
45
+ reattaches, using the operation's supplied wake address, and returns `reconnected`, with no
46
+ activity, new read horizon or listening changes. A foreign owner gets `already_joined`; takeover
47
+ must be explicit.
48
+ - `reattach(name, {owner, wakeAddress?})` returns a handle for an exact-owner standing seat, or
49
+ `null` for absent, foreign or done custody; it never creates membership. A foreign takeover racing
50
+ the transaction can instead refuse. It can also refresh an attached seat's wake address subject to
51
+ native-route preference. Host start/resume restores only detached seats found through
52
+ `seatsOf(owner)` before arming delivery. One unavailable location or
53
+ failed reattachment does not block the others. A new or forked session has a different owner and
54
+ restores nothing. Hosts never infer restoration from a historical departure.
55
+ - Participant mutations require both the captured generation and an attached current seat.
56
+ Detach suspends existing handles; they cannot express, catch, listen, leave or change
57
+ deferrals until reattachment. Rejoin/takeover fences old handles permanently, even if the same
58
+ owner later reacquires the name. Host cleanup checks owner and generation inside its transaction;
59
+ stale, foreign, done or already-detached detach changes nothing. Host `cancelDeliveries` also
60
+ requires the captured owner/generation and an attached current seat; it does not consume activity.
61
+
62
+ The HOME index supplies candidate paths only. Successful file-backed joins and non-null
63
+ reattachments attempt best-effort publication; opening or expressing does not publish. Losing the
64
+ index may delay restoration or delivery but cannot transfer a seat, erase delivery evidence or
65
+ consume unread activity. A wake address is transport readiness for an attached seat, not an
66
+ alternative ownership fact.
35
67
 
36
68
  ## Independent delivery deferrals
37
69
 
38
- `deferDelivery`, `renewDeliveryDeferral`, and `releaseDeliveryDeferral` require that same captured
39
- current seat. Deferrals live in the artifact, keyed by normalized participant, generation and fresh
40
- UUID, independently of the single catch lease and of delivery claims/receipts. A lease contains
41
- only its validated metadata match and expiry; it is not an acknowledgement intent or an activity.
70
+ `deferDelivery`, `renewDeliveryDeferral`, and `releaseDeliveryDeferral` require the same captured,
71
+ attached current seat. Independent artifact-owned leases belong to one participant and generation,
72
+ separately from catch ownership, delivery claims and receipts. Each has a fresh opaque ID, validated
73
+ metadata match and expiry. It is not an acknowledgement intent or an activity.
42
74
 
43
75
  Each lease's nested own-property scalar equalities combine with AND; matching active leases combine
44
- with OR. Renewal cannot revive an expired lease. Release touches only its named live lease, and
76
+ with OR. Renewal cannot revive an expired lease. Release touches only its named live lease and
45
77
  returns false for an absent/expired ID. A current owner cannot use another participant's ID.
46
- Expired rows may be pruned during owner writes; read/claim eligibility always checks expiry and
47
- the current generation. Done/takeover/rejoin remove deferrals in the same transaction, and stale
48
- rows can never become active for a new generation.
78
+ Expiry is checked live. Detach, done, takeover and rejoin clear deferrals in the same transaction;
79
+ reattachment never revives an old lease.
49
80
 
50
81
  Deferrals change only proactive eligibility. They preserve audience, history, unread counts and
51
- explicit catch. An explicit catch consumes normally and prevents later replay after release.
52
- All proactive paths recheck the same typed policy at claim and final send/presentation boundaries.
53
- An already-dispatched external message cannot be recalled by acquiring a lease.
82
+ explicit catch. A caught record is not replayed after release. Producers, observers, claims and
83
+ final send/presentation gates all require attached current custody and apply the same deferral
84
+ policy. Neither detach nor a deferral can recall an already-dispatched external message.
54
85
 
55
86
  ## Acceptance boundaries
56
87
 
57
- Tests exercise foreign and stale handles, same-body reconnection/rejoin, explicit takeover,
58
- transaction-scoped generation fences, independent leases, precise expiry, explicit catch and
59
- stdio parity. Assertions belong to the authoritative facts, not helper call ordering or display
60
- copy. Migration of old owner/route facts is offline and explicit; see [migration.md](migration.md).
88
+ Tests exercise foreign/stale/detached handles, same-owner restoration with unread activity intact,
89
+ voluntary leave, explicit takeover, permanent deletion, exact-owner adapter lifecycle, generation
90
+ fences and delivery eligibility. Assertions belong to authoritative facts, not helper ordering or
91
+ display copy. Migration is offline and explicit; see [migration.md](migration.md).