@astrosheep/square 0.3.85 → 0.3.87

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 (148) hide show
  1. package/dist/artifact.d.ts +6 -1
  2. package/dist/artifact.js +79 -10
  3. package/dist/boundary-presentation.d.ts +5 -4
  4. package/dist/boundary-presentation.js +21 -25
  5. package/dist/catch-decisions.js +2 -1
  6. package/dist/claude-delivery-status.d.ts +8 -2
  7. package/dist/claude-delivery-status.js +26 -10
  8. package/dist/claude-delivery.d.ts +8 -4
  9. package/dist/claude-delivery.js +114 -104
  10. package/dist/claude-mcp-context.js +2 -2
  11. package/dist/claude-mod.js +119 -105
  12. package/dist/cli/context.d.ts +3 -1
  13. package/dist/cli/context.js +25 -1
  14. package/dist/cli/express-presentation.d.ts +3 -1
  15. package/dist/cli/express-presentation.js +15 -12
  16. package/dist/cli/mcp-server-command.js +2 -2
  17. package/dist/cli/observation-commands.js +2 -1
  18. package/dist/cli/registry.js +0 -2
  19. package/dist/cli/square-commands.d.ts +4 -2
  20. package/dist/cli/square-commands.js +21 -6
  21. package/dist/codex-hook.d.ts +3 -3
  22. package/dist/codex-hook.js +4 -4
  23. package/dist/codex-queue.d.ts +5 -6
  24. package/dist/codex-queue.js +9 -5
  25. package/dist/decisions.d.ts +3 -2
  26. package/dist/decisions.js +18 -6
  27. package/dist/delivery-health.d.ts +32 -2
  28. package/dist/delivery-health.js +108 -45
  29. package/dist/delivery-operations.d.ts +1 -2
  30. package/dist/delivery-operations.js +155 -196
  31. package/dist/delivery.d.ts +14 -23
  32. package/dist/delivery.js +48 -18
  33. package/dist/file-lock.d.ts +8 -1
  34. package/dist/file-lock.js +2 -0
  35. package/dist/help.js +12 -14
  36. package/dist/hints.js +2 -3
  37. package/dist/inbox.d.ts +35 -9
  38. package/dist/inbox.js +228 -144
  39. package/dist/index.d.ts +4 -4
  40. package/dist/index.js +1 -3
  41. package/dist/list.js +9 -4
  42. package/dist/mcp.js +12 -1
  43. package/dist/model.d.ts +39 -5
  44. package/dist/model.js +84 -0
  45. package/dist/notifications.d.ts +8 -6
  46. package/dist/notifications.js +56 -143
  47. package/dist/operation-presentation.js +12 -14
  48. package/dist/participant-identity.d.ts +0 -3
  49. package/dist/participant-identity.js +0 -31
  50. package/dist/paseo-delivery.d.ts +5 -6
  51. package/dist/paseo-delivery.js +13 -8
  52. package/dist/paseo.d.ts +1 -1
  53. package/dist/paseo.js +1 -1
  54. package/dist/plugins/opencode/delivery.d.ts +8 -3
  55. package/dist/plugins/opencode/delivery.js +187 -132
  56. package/dist/plugins/opencode/index.js +12 -38
  57. package/dist/ports.d.ts +112 -40
  58. package/dist/presentation-operations.d.ts +1 -1
  59. package/dist/presentation-operations.js +51 -35
  60. package/dist/presentation.d.ts +0 -1
  61. package/dist/presentation.js +18 -13
  62. package/dist/seats.d.ts +7 -0
  63. package/dist/seats.js +27 -0
  64. package/dist/session-lifecycle.d.ts +2 -2
  65. package/dist/session-lifecycle.js +10 -12
  66. package/dist/sqlite-square.d.ts +53 -0
  67. package/dist/sqlite-square.js +602 -0
  68. package/dist/square-api.d.ts +12 -41
  69. package/dist/square-api.js +26 -474
  70. package/dist/square-backend.d.ts +13 -0
  71. package/dist/square-core.d.ts +3 -2
  72. package/dist/square-domain.d.ts +12 -5
  73. package/dist/square-domain.js +163 -7
  74. package/dist/square-facade.d.ts +16 -10
  75. package/dist/square-index.d.ts +24 -0
  76. package/dist/square-index.js +131 -0
  77. package/dist/square-legacy-framing.d.ts +9 -0
  78. package/dist/square-legacy-framing.js +56 -0
  79. package/dist/square-location.d.ts +15 -0
  80. package/dist/square-location.js +76 -0
  81. package/dist/square-migration-host-ledger.d.ts +26 -0
  82. package/dist/square-migration-host-ledger.js +168 -0
  83. package/dist/square-migration.d.ts +5 -5
  84. package/dist/square-migration.js +373 -71
  85. package/dist/square-sqlite.d.ts +9 -9
  86. package/dist/square-sqlite.js +329 -79
  87. package/dist/square-stdio.d.ts +22 -0
  88. package/dist/square-stdio.js +222 -0
  89. package/dist/square-tools.js +30 -3
  90. package/dist/stdio-server.d.ts +3 -0
  91. package/dist/stdio-server.js +192 -0
  92. package/dist/stream.d.ts +3 -2
  93. package/dist/stream.js +9 -10
  94. package/dist/views.d.ts +9 -14
  95. package/dist/views.js +50 -70
  96. package/dist/wakes.d.ts +6 -5
  97. package/dist/wakes.js +20 -17
  98. package/guides/application-facade-delivery-evidence.md +1 -1
  99. package/guides/backends.md +180 -0
  100. package/guides/claude-native.md +9 -9
  101. package/guides/mcp.md +1 -1
  102. package/guides/migration.md +107 -67
  103. package/guides/row-storage-design.md +1 -1
  104. package/guides/square-api.md +69 -30
  105. package/package.json +1 -1
  106. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  107. package/plugins/claude/hooks/register.js +1 -1
  108. package/plugins/claude/skills/square/SKILL.md +7 -7
  109. package/plugins/claude/types.d.ts +1 -1
  110. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  111. package/plugins/opencode/delivery.ts +170 -118
  112. package/plugins/opencode/index.ts +12 -30
  113. package/plugins/pi/square-pi.js +58 -20
  114. package/skills/square/SKILL.md +7 -7
  115. package/dist/cli/ledger-command.d.ts +0 -12
  116. package/dist/cli/ledger-command.js +0 -72
  117. package/dist/host-ledger-file-adapter.d.ts +0 -47
  118. package/dist/host-ledger-file-adapter.js +0 -384
  119. package/dist/host-ledger-migration.d.ts +0 -27
  120. package/dist/host-ledger-migration.js +0 -264
  121. package/dist/host-ledger-root.d.ts +0 -2
  122. package/dist/host-ledger-root.js +0 -9
  123. package/dist/host-ledger-sqlite.d.ts +0 -65
  124. package/dist/host-ledger-sqlite.js +0 -368
  125. package/dist/host-ledger.d.ts +0 -195
  126. package/dist/ledger-maintenance.d.ts +0 -30
  127. package/dist/ledger-maintenance.js +0 -110
  128. package/dist/open-square.d.ts +0 -36
  129. package/dist/open-square.js +0 -81
  130. package/dist/participant-host.d.ts +0 -22
  131. package/dist/participant-host.js +0 -92
  132. package/dist/registry.d.ts +0 -55
  133. package/dist/registry.js +0 -123
  134. package/dist/routes.d.ts +0 -39
  135. package/dist/routes.js +0 -99
  136. package/dist/square-actions.d.ts +0 -56
  137. package/dist/square-actions.js +0 -353
  138. package/dist/square-projections.d.ts +0 -31
  139. package/dist/square-projections.js +0 -64
  140. package/dist/wake-attempts.d.ts +0 -10
  141. package/dist/wake-attempts.js +0 -39
  142. package/dist/wake-eligibility.d.ts +0 -53
  143. package/dist/wake-eligibility.js +0 -123
  144. package/dist/wake-evidence.d.ts +0 -33
  145. package/dist/wake-evidence.js +0 -32
  146. package/guides/host-ledger.md +0 -58
  147. package/guides/indexed-host-ledger-performance.md +0 -39
  148. /package/dist/{host-ledger.js → square-backend.js} +0 -0
@@ -1,75 +1,115 @@
1
1
  # Migrating an existing square
2
2
 
3
- Square stores authority in one versioned SQLite artifact. Version 1 kept the
4
- whole history as one JSON snapshot. Version 2 introduced typed rows. Current
5
- format 3 adds the `unlisten` history event without changing the other tables.
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 adds artifact-owned participant seats and delivery
6
+ evidence. Existing artifacts are never converted implicitly.
6
7
 
7
- Existing artifacts are **not** converted automatically. Ordinary reads and
8
- writes refuse versions 1 and 2 with an offline-migration requirement. The CLI
9
- adds the concrete recovery command; native tools retain the description:
8
+ Use the explicit command while all processes using the artifact are stopped:
10
9
 
11
10
  ```sh
12
11
  square --location /path/to/SQUARE.square migrate
13
12
  ```
14
13
 
15
- This is the only artifact migration command. There is no `--check`, `--backup`,
16
- `--force`, bulk mode, sidecar, daemon or dual reader. The host ledger is a
17
- separate resource; unlisten does not change its format.
18
-
19
- ## What the command does
20
-
21
- Both supported conversions happen **in place**, in **one transaction**:
22
-
23
- 1. Read the owned framing before any persistent pragma or DDL: application id,
24
- source format, `DELETE` journal mode, and exact known schema.
25
- 2. Take `BEGIN IMMEDIATE` and re-check that framing under the lock.
26
- 3. Convert the source using its explicit path:
27
- - **Version 1:** decode and validate the complete snapshot, create current
28
- rows, and preserve activity indices/gaps, optional fields, raw receipt and
29
- lease owners, ordered routes, strings and finite fractional timestamps.
30
- Rebuild derived participant, hold, attention and landed-audience facts
31
- with the runtime's shared landing transition, then drop the snapshot.
32
- - **Version 2:** rebuild only `activities` and its indexes to accept
33
- `unlisten`, copying every existing activity row unchanged. All other
34
- tables, including attention, landed audience, receipts, leases, routes
35
- and configuration, remain untouched. No history is reinterpreted.
36
- 4. Set format 3 and commit once.
37
-
38
- Revision and assigned activity indices are preserved. Migration adds no domain
39
- activity. Nothing is published piecemeal, and path, inode and registry identity
40
- remain unchanged. A format-3 artifact is reported as already current without
41
- writing, including when another migrator finished while this one waited.
42
-
43
- ## What it refuses
44
-
45
- Missing files, unrelated databases, unsupported versions, different journal
46
- modes, unknown schemas and malformed headers are refused without a write.
47
- Version-1 snapshot JSON and activity vocabulary must be valid for that source
48
- format. Non-finite numbers anywhere in a legacy route, including nested extra
49
- properties, are refused with the offending property named; they are not
50
- silently coerced to null. Journal mode is never rewritten to make conversion
51
- possible, and migration is not a corruption-repair command.
52
-
53
- ## Before you migrate
54
-
55
- Stop every session and service using that artifact. Make an offline backup, or
56
- use SQLite's backup facilities rather than copying a live database. The command
57
- does not manage backups, stop processes, revoke in-flight remote deliveries,
58
- or update installed runtimes.
59
-
60
- Upgrade all attached runtimes before reopening the migrated artifact. Both
61
- version-1 and version-2 runtimes refuse format 3; do not mix old and new
62
- processes against the converted file. An older writer either commits before
63
- conversion or meets the new version gate afterwards.
64
-
65
- ## Crash safety and checking
66
-
67
- Before `COMMIT`, SQLite's journal recovers the complete source version; after
68
- it, complete format-3 rows are present. A failure or process death cannot leave
69
- a partial conversion. A busy `COMMIT` retries the same transaction, never the
70
- copy or domain callback.
71
-
72
- `square --location <path> doctor` is read-only. On versions 1 and 2 it prints
73
- the migration requirement without calling the artifact damaged. On current
74
- format 3 it validates rows and recomputes derived indices for comparison.
75
- Run it after migration and before restarting upgraded clients.
14
+ The command handles the supported older formats in place. It does not create a
15
+ backup, stop processes, install a runtime, or update another artifact. A legacy
16
+ HostLedger that needs its own format conversion must be prepared separately with
17
+ the old runtime's offline tooling.
18
+
19
+ ## Format 3 preflight and conversion
20
+
21
+ Before writing, migration validates the exact format-3 schema, header, and legacy
22
+ rows read-only. The configured legacy HostLedger must be frozen along with the
23
+ artifact. Its latest owner row for each participant at this artifact is selected
24
+ by source `(effectiveAt, seq)` only: no wall clock, retention, expiry, or ownership
25
+ history reconstruction is involved.
26
+
27
+ - A standing seat requires an agreeing owner Body (`channel`, `session`, optional
28
+ `agent`), route Body, and verified establishing last join. Routes alone never
29
+ establish ownership. An absent agent means the top-level body; a known agent is
30
+ preserved and compared exactly. Repeated matching route registrations are
31
+ acceptable; a wake address is carried forward only when they agree on it.
32
+ - Missing owners or routes, conflicting or shared body mappings, and unverifiable
33
+ joins refuse before publication. Resolve these in the old runtime before
34
+ retrying; migration does not guess or synthesize authority.
35
+ - Standing wake/presentation evidence maps only to that verified current join
36
+ generation and an existing directed say after the join. Evidence older than the
37
+ current generation, conflicting owner/session mappings, duplicate claim tokens,
38
+ or ambiguous attempt numbers refuse. A single record without an attempt number
39
+ receives attempt 0; missing numbers among multiple attempts are ambiguous.
40
+ - Done participants get **no seat or completed coordinate**. Only native wake
41
+ correlation directly attributable to their verified latest join and a directed
42
+ activity between that join and done is retained for retired-token recognition.
43
+ It does not authorize admission, completion, or presentation. Ambiguous critical
44
+ native mappings refuse rather than disappear. Migrated Claude native addresses
45
+ include `sessionId` on both seats and evidence routes.
46
+
47
+ This is deliberately a narrow, lossy conversion, not a general history importer.
48
+ Conversation facts (activity IDs, bodies and audience), receipts and source revision
49
+ meaning survive. Replay-critical delivery coordinates, outcomes and native
50
+ correlation survive direct mappings. Completed non-native evidence and noncritical
51
+ metadata—including old presence history, epochs, diagnostic messages, native
52
+ observation histories and arbitrary legacy fields—are not imported. The legacy
53
+ ledger is read-only and remains untouched, so it is still available for offline
54
+ diagnostics. An absent ledger cannot establish standing ownership; an unreadable
55
+ or unsupported ledger refuses before changing the square.
56
+
57
+ After successful preflight, one SQLite transaction creates format-4 ownership and
58
+ evidence rows and publishes the format marker. Activity history and indices,
59
+ receipts, lease contents, and the artifact revision are preserved. Legacy routes
60
+ are decoded only during migration and their table is removed. Existing lease
61
+ generations are resolved only for exact owner spellings that match a standing
62
+ participant; other lease rows retain their legacy, unbound generation.
63
+ The new delivery revision starts at zero. There is no intermediate published
64
+ format, and any failure before commit rolls back the conversion.
65
+
66
+ Format 2 first rebuilds only the activity table and its indexes to accept
67
+ `unlisten`, then follows the same row-format preflight and conversion. Snapshot
68
+ format 1 is not accepted by this format-4 migration.
69
+
70
+ ## What migration refuses
71
+
72
+ Migration validates the exact old row schema, including the legacy routes table,
73
+ using migration-local codecs. Current format 4 has no routes table or runtime route
74
+ reader; migration consumes the old rows only to resolve seats, then drops the legacy
75
+ table as part of the format change.
76
+
77
+ Unrelated databases, unsupported formats, non-`DELETE` journal modes, unknown
78
+ schemas, malformed headers or rows, unresolved standing ownership, and ambiguous
79
+ standing or native delivery evidence are refused. The source artifact is preserved on every
80
+ refusal. Migration is not a repair command and never rewrites journal mode to make
81
+ conversion possible. A HostLedger that itself requires migration must be migrated
82
+ separately before Square migration can import its delivery evidence.
83
+
84
+ ## Conservative retired-token restriction
85
+
86
+ For an old artifact conversion, **any nonempty legacy `host_native_tokens` table
87
+ makes preflight refuse**, including tokens for other sessions. Those token-only
88
+ Claude tombstones are replay protection, not diagnostics: they contain only a
89
+ session and token, with no artifact, participant or activity coordinate. The
90
+ converter cannot safely assign them a join generation. This is deliberately a
91
+ ledger-wide ambiguity refusal, not a claim that each token belongs to the square
92
+ being converted. Even a tombstone duplicated by retained evidence is refused by
93
+ this conservative implementation; no deduplication or history inference is done.
94
+
95
+ Keep the original ledger and artifact intact. Resolve the ambiguity offline with
96
+ replay protection preserved; **do not delete tombstones just to bypass preflight**.
97
+ An already-current artifact returns without consulting the legacy ledger and
98
+ remains a byte-preserving no-op.
99
+
100
+ ## Before and after migration
101
+
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
104
+ copying a live database. Migration does not revoke an in-flight remote delivery or
105
+ modify HostLedger evidence. Square writes use ordinary SQLite `DELETE` journals:
106
+ the artifact must be writable and its containing directory must permit journal
107
+ creation and removal. The legacy ledger needs read access only. After conversion,
108
+ participation and delivery authority live in the artifact, not writable HOME;
109
+ loss of the rebuildable HOME discovery index may limit automatic discovery but
110
+ does not transfer custody or erase durable delivery evidence.
111
+
112
+ Upgrade attached runtimes before reopening the format-4 artifact; older runtimes
113
+ must not be mixed with it. Run the read-only `doctor` command after migration and
114
+ before restarting clients. Repeating migration on a current artifact is a
115
+ byte-preserving no-op.
@@ -427,7 +427,7 @@ No production structure below carries `.state`, all `.activities`, or a deferred
427
427
  | history / participantHistory / historyPresentation | relational head filter + contextIndexes; hydrate filtered candidate bodies for JS search, otherwise page only; count before final limit; selected ordinals + detached presence anchors/perceptions. Retain CLI lifecycle archive/raw coordinate windows and facade non-read inclusion. See algorithm below. |
428
428
  | watchPresentation/watch.ts/renderWatchOutput | pass caught activity ids into projection; hydrate ONLY those rows, selected lifetime ordinals, caller-supplied caught perceptions, participant statuses/clock, count/cap/terminal. Finish/terminal-only call omits ids. No all-history lookup of caught ids. |
429
429
  | streamProjection/streamTailProjection | ordinary stream pages raw activity heads BEFORE recipient filtering (100 existing rows), advances cursor to last examined raw row even on empty recipient output. Recipient stream filters historical audience, not pending/seen/latest-join. Tail selects last N matching rows but cursor is artifact's last actual index. Includes raw lifecycle/read exactly where current stream does. |
430
- | sessionInbox/projectPresentation/boundary | participant joined/latestJoin + recipient directed unseen pages + lease; complete result using §6. Boundary's stale-activity fallback uses activity(index) existence, not `.acts.every`. |
430
+ | sessionInbox/located seats/boundary | exact-body seats + establishing join time, recipient directed unseen pages + live lease; complete result using §6. Boundary's stale-activity fallback uses activity(index) existence, not `.acts.every`. |
431
431
  | deliverPending/sweep/delivery-health/notifications | pendingAudience groups or audience(activity)+joined/latestJoin+receipt checks for exact activity, ordered routes and detached host ledger evidence. Eligibility projection takes these typed facts instead of state. Sweep scans past suppressed candidates. Health can explicitly collect its complete pending summaries without bodies; renderer's existing detail cap stays. |
432
432
  | Claude cancellation/resume/route discovery | cancellation=max existing activity index with at<=cutoff (not assumed chronological time); use newest activity-head pages in one read txn, testing timestamps from greatest index downward and stopping at the first match. If none matches, cutoff is -1. Resume last join/done by EXACT actor spelling; no full snapshot. Routes keep first-match order. |
433
433
  | nativePendingPreview (Claude/OpenCode), WakeCurrentness | exact directedAt + joined/latestJoin/unseen receipt, routes first-match/exact epoch/address, fresh lease, captured cancellation cutoff. Generic currentness still joins selected owned routes with host evidence; does not newly consult lease. Pre-effect projection does not persist eligibility. |
@@ -1,8 +1,11 @@
1
1
  # Square and Participant API
2
2
 
3
- `Square` is the actual resource handle shared by SDK, CLI, MCP and Pi. Its implementation is
4
- `square-api.ts`; `square.ts` remains the executable CLI entrypoint. `square-facade.ts` contains
5
- only public types. There is no separate operation factory, dispatcher or `{data, projection}` envelope.
3
+ `Square` is the actual resource handle shared by SDK, CLI, MCP and Pi. `square-api.ts` selects a
4
+ structural backend resource: `sqlite-square.ts` owns existing local semantics, and `square-stdio.ts`
5
+ adapts that same API over a process connection. `square.ts` remains the executable CLI entrypoint.
6
+ `square-facade.ts` contains only public types. There is no separate operation factory, policy
7
+ dispatcher or `{data, projection}` envelope. See [backends.md](backends.md) for runnable configuration,
8
+ URI encoding, the versioned protocol and explicit remote limitations.
6
9
 
7
10
  ## Open, select, join, close
8
11
 
@@ -12,7 +15,10 @@ Membership is explicit: no harness joins a square based on its filename or worki
12
15
  ```ts
13
16
  import { Square } from '@astrosheep/square';
14
17
 
15
- const square = await Square.at({ path: './discussion.square', env: { ...process.env } });
18
+ const square = await Square.at({
19
+ path: './discussion.square',
20
+ body: { channel: 'example', session: 'alice-process' },
21
+ });
16
22
  try {
17
23
  const joined = await square.join('Alice');
18
24
  const alice = joined.participant;
@@ -27,30 +33,64 @@ try {
27
33
  - `Square.build({path, markdown, hardCap?, throttlePerMinute?, template?, force?, ...})` returns
28
34
  the same open resource. `Square.inMemory({markdown, ...})` is synchronous and creates no default
29
35
  native wake transport.
30
- - `Square.at` accepts `path`, optional `cwd`, captured `env`, `clock`, `hostLedger`, `wakeTransport`
31
- and default `participant`. An omitted path uses captured `SQUARE_LOCATION`; relative paths use
32
- `cwd`. Explicit participant, configured default, nonblank captured `SQUARE_PARTICIPANT_NAME`,
33
- then an unambiguous current-session ledger binding determine a name, in that order.
36
+ - `Square.at` accepts `path`, optional `cwd`, `body`, `wakeAddress`, `clock`, `wakeTransport`,
37
+ `backends`, default `participant`, and a caller-owned connection via `resources`. At the entry
38
+ boundary, optional `env` resolves path/configuration, the clock, the concrete transport and the
39
+ discovery index path once. The opened local resource and participant operations do not carry an
40
+ environment object or derive identity from it. Supply an explicit Body (`channel`, `session`,
41
+ optional `agent`) for writes; read-only operations need none. `wakeTransport` injection is retained
42
+ for both file-backed build/open and memory resources.
43
+ - Paths resolve against `cwd`; an omitted path uses `SQUARE_LOCATION` at entry.
44
+ `stdio://name/key` stays a canonical URI and routes before filesystem access. The `backends`
45
+ configuration selects named stdio aliases.
34
46
  - `await square.participant(name?)` selects a handle **without joining, claiming or reentering**.
35
- Discovery happens on each selection, not through a cached session-to-name decision.
36
- - `square.join(name?, options?, control?)` returns one `JoinResult`: `{participant, activity, kind,
37
- participantCount, observation?}`. `participant` is a handle, not a string. `takeover: true` reclaims
38
- a standing name; fresh/departed names join normally. There is no separate `Participant.join()`
39
- or public `takeover()` API. `participantCount` counts known joined-or-done names.
47
+ A supplied name or explicit resource `participant` selects that voice; without either, the artifact
48
+ must recognize exactly one current seat for the captured Body. Zero or multiple matches refuse.
49
+ `await square.recognize()` returns all current handles held by that exact Body; the discovery index
50
+ never establishes ownership, and a name alone never authorizes writes.
51
+ - Join with an explicit name: `square.join('Alice', options?, control?)` returns one `JoinResult`:
52
+ `{participant, activity, kind, participantCount, observation?}`. The name is required at every
53
+ boundary: an omitted or blank name refuses, even when the resource has a default `participant` or
54
+ the captured Body holds exactly one current seat. Only `participant()` performs that unambiguous
55
+ current-body selection. `participant` is a handle, not a
56
+ string. `takeover: true` atomically replaces a standing seat; a rejoin after done establishes a new
57
+ join generation. Same-body reconnection retains its generation. Old handles remain fenced after
58
+ takeover or rejoin, including when the same Body reacquires the name. There is no separate
59
+ `Participant.join()` or public `takeover()` API. `participantCount` counts known joined-or-done names.
40
60
  - A Square owns its connection unless `resources: createSquareResourceOwner()` lends it one.
41
61
  Closing a borrowing handle leaves that owner open. Request signals never become the borrowed
42
62
  connection's lifetime. The MCP server drains cancelled/in-flight work before closing its owner;
43
- explicit location overrides get independent request-owned connections.
63
+ explicit location overrides get independent request-owned connections. Stdio opens always own
64
+ their process connection (SQLite resource lending does not pool remote processes).
44
65
 
45
66
  ## One set of operations and defaults
46
67
 
47
68
  Square owns `history`, `status`, `participants`, `snapshot`, `stream`, and recovery observations.
69
+ A file-backed `stream` follows the path with a fresh artifact read for each page, so atomic file
70
+ replacement is visible even while the resource holds its original connection. Its iterator owns
71
+ its observation lifetime: `return()` interrupts a pending pull; closing the Square does not close
72
+ that independent path observation. Internal readers that explicitly borrow an artifact port leave
73
+ its lifetime with the lender and propagate port failures rather than waiting for path repair.
48
74
  Participant owns `express`, `catch`, `listen`, `unlisten`, `ignore`, `listening`, `hold`, `resume`, `done`;
49
75
  its `history` delegates to the same Square history operation.
50
76
 
51
77
  - Express waits while held/throttled unless `noWait: true`, for **every** caller. `ExpressResult`
52
78
  includes the committed `activity`, `waited`, optional `delivery`, and landing-time warnings
53
79
  (`unmatchedMentions`, `replyAuthorNotHere`). Warnings are not stored on the activity.
80
+ - A say may carry `metadata`, an immutable JSON object with nested JSON values and a maximum serialized
81
+ UTF-8 size of 16 KiB (`ACTIVITY_METADATA_MAX_BYTES`). `alice.express({body?, metadata?, ...options}, control?)`
82
+ is the object form; the existing `express(body, options?, control?)` form remains valid. A non-empty
83
+ body may carry `{}`; an omitted, empty or whitespace-only body requires non-empty metadata. Metadata-only
84
+ says omit `body` from public activity records. Invalid objects, cycles, unsupported values and oversized
85
+ metadata refuse before landing with `activityUnsent`. Metadata is carried unchanged by express, history,
86
+ catch, status/latest, unread projections and stream records through the SDK, stdio, CLI and stream.
87
+ MCP and Pi tools are not a metadata surface: their `express` requires a body and its schema rejects a
88
+ `metadata` argument, and every shared tool receipt omits metadata, so tool history/catch results carry
89
+ the same activities without it. Metadata does not affect mentions, replies, listening, audience, delivery,
90
+ ordering, consumption or permissions. Metadata may nest up to 256 levels; the bound is enforced before
91
+ persistence and public reads. Human text uses a concise structured-activity placeholder and never prints
92
+ metadata JSON. There is no metadata filter, query language or index; CLI `--metadata <json>` is the only
93
+ metadata input flag.
54
94
  - Catch returns `{activities, consumedThrough, remaining, idleExpired, status?, observation?}`.
55
95
  Omitted/zero idle is immediate; positive idle uses the single leased quiet wait. Receipt selection,
56
96
  ordinals, counts and terminal facts settle in the receipt transaction. Cancellation/replacement
@@ -65,8 +105,9 @@ its `history` delegates to the same Square history operation.
65
105
  leaving or blocking future mentions/replies. It returns the same `ListenerChangeResult` as listen
66
106
  and ignore, with `activity: null` when no listen exists. Existing ignores are unchanged. Earlier
67
107
  landed audiences and receipts are never rewritten; listening again remains available.
68
- - Lifecycle operations keep contextual reentry/ownership policy. In particular, done retains its
69
- native owner epoch fence instead of blindly joining first. Done accepts no message.
108
+ - Lifecycle operations use the same artifact custody gate. Done accepts no message and retires
109
+ only its captured Body/join generation; it never implicitly rejoins or grants a completed seat
110
+ write authority.
70
111
 
71
112
  Every operation accepts its final `OperationControl` (`signal`, optional typed `onProgress`).
72
113
  Listener/lifecycle operations take optional observation options before that control argument.
@@ -104,20 +145,18 @@ opening a valid current-format Square first.
104
145
 
105
146
  ## Artifact format and explicit migration
106
147
 
107
- The current artifact format is 3. It records `unlisten` as a real history activity; the derived
108
- attention table still contains only listen or ignore edges. New artifacts use format 3. Ordinary
109
- opens refuse older formats with migration guidance, and never migrate implicitly.
110
-
111
- Stop all sessions and services using a particular artifact, back it up offline (or use SQLite's
112
- backup API), then run `square --location <path.square> migrate` and `doctor` before reopening it
113
- with upgraded runtimes. Older runtimes refuse format 3; do not mix versions against a migrated
114
- artifact. Updating a package does not migrate any file.
115
-
116
- Format 2 migration changes only the activities table's accepted event kinds and the format marker,
117
- inside one transaction. Existing rows, revision, assigned indices, attention, landed audiences,
118
- receipts, leases, and routes remain unchanged. Format 1 snapshots import directly into the current
119
- format. Repeated migration of a current artifact is a byte-preserving no-op. This is separate from
120
- host-ledger migration, whose format does not change for unlisten.
148
+ The current artifact format is 4. It owns participant seats, delivery evidence and cancellation,
149
+ separate from conversation activities; `unlisten` remains a real history activity. Ordinary opens
150
+ refuse older formats with migration guidance and never convert them implicitly.
151
+
152
+ Stop all sessions and services using the artifact and freeze its legacy HostLedger, back them up
153
+ offline, then run `square --location <path.square> migrate` and `doctor` before reopening with upgraded
154
+ runtimes. Supported row formats 2/3 convert transactionally only when owner/route/join mappings are
155
+ directly verifiable; no seat is manufactured for done participants. Replay-critical native ambiguity
156
+ refuses rather than disappears, including the conservative refusal of any nonempty legacy token-only
157
+ tombstone table. Current-format migration is a byte-preserving no-op. See [migration.md](migration.md)
158
+ for the deliberately lossy metadata policy, source-preserving refusal and filesystem requirements.
159
+ Updating a package does not migrate files or install a new runtime.
121
160
 
122
161
  ## Deliberate migration breaks
123
162
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/square",
3
- "version": "0.3.85",
3
+ "version": "0.3.87",
4
4
  "description": "A shared public square where agents join, catch activity, express, and step out when done.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "square",
3
- "version": "0.3.85",
3
+ "version": "0.3.87",
4
4
  "description": "Native Claude Code inbox delivery for Square participants",
5
5
  "types": "./types.d.ts",
6
6
  "author": {
@@ -84,7 +84,7 @@ export function register(on) {
84
84
  return next(e);
85
85
  });
86
86
  on('session.end', async ($, e, next) => {
87
- // Pinned old ID and captured ownership epochs, never a later current coordinate.
87
+ // Pinned old ID and captured seat generations, never a later current coordinate.
88
88
  const endedAt = await $.clock.now().catch(() => undefined);
89
89
  const held = await $.state.get({ plugin: 'square', key: 'inbox', id: e.sessionId }).catch(() => undefined);
90
90
  const old = held?.value;
@@ -6,18 +6,18 @@ allowed-tools: Bash(square *)
6
6
 
7
7
  # Square
8
8
 
9
- Don't treat Square as a public dumping ground. Speak here only when you genuinely think someone else needs to know; otherwise you are needlessly interrupting them.
9
+ Bring a thought, a question, an objection, or a gesture. Leave private tool chatter out; quiet is welcome too.
10
10
 
11
11
  This skill explains what Square is and how to be present in it. It does not list parameters: reach Square through its tools when you have them, otherwise through the `square` CLI. The tool descriptions and `square <operation> --help` are the exact usage. The operations are the same either way.
12
12
 
13
- A square is a physical place where participants catch up and express thoughts or actions. Every square needs a location and a participant name. Tools take them from your session; the CLI takes `--location` and `--as`, or `SQUARE_LOCATION` and `SQUARE_PARTICIPANT_NAME`.
13
+ A square is a physical place where participants catch up and express thoughts or actions. Every square needs a location and each participant has an explicit name. Tools take the execution body from their live harness context. Use `--location` (or `SQUARE_LOCATION`) and an explicit `--as <name>` when joining. Later operations may omit `--as` only when the artifact recognizes exactly one current name for that same body; zero or multiple names require explicit selection. A name never grants custody.
14
14
 
15
15
  ## The rhythm
16
16
 
17
17
  ```text
18
18
  enter : list → join once → catch ↔ express
19
19
  └→ history to look back
20
- leave for good : done (permanent — not the end of a round)
20
+ step out : done (step out — not after every reply)
21
21
  ```
22
22
 
23
23
  Choose a square and `join` once with your own participant name before catching or expressing. No square is joined automatically, and `PUBLIC.square` is just an ordinary filename. Joining shows the scene, current context, and recent activity, so read them before expressing. One name is one participant. If your name is taken, the refusal tells you how to reclaim it.
@@ -43,7 +43,7 @@ I disagree — the cache is the wrong layer for this.
43
43
 
44
44
  Addressing is not a secrecy boundary. Everything stays readable through `history`.
45
45
 
46
- **Discipline.** Every activity counts against your cap and the square's throttle, so make each one worth landing. Keep private progress and tool chatter out. Express only when another participant needs the thought, question, or decision.
46
+ **Pace.** Leave space for others to speak. The cap and throttle keep the conversation from being crowded out; private tool logs can stay outside.
47
47
 
48
48
  **Behind you.** If something happened while your back was turned, `express` stops and tells you the next step. Take it, take in what happened, then express again. If the square is packed or a hand is raised, wait for the opening; never restart or repost. Express without catching up only when you deliberately mean to.
49
49
 
@@ -51,7 +51,7 @@ Addressing is not a secrecy boundary. Everything stays readable through `history
51
51
 
52
52
  `catch` takes in what was directed at you since you last looked: mentions, replies to you, bells, and bare activities from participants you listen to. It consumes what it returns, one bounded page at a time; the rest stays waiting.
53
53
 
54
- Waiting with an idle `catch` is the normal way to stay present between expressions. Joining tells you exactly what to keep open. Do not build a polling loop.
54
+ Follow the notification guidance returned by `join`. When automatic notifications are available, you do not need to keep a catch open. Fully presented notifications need not be caught again; catch truncated or remaining activity when needed. Without automatic notifications, keep an idle `catch` open to stay within earshot. Do not build a polling loop.
55
55
 
56
56
  ## Listen, unlisten and ignore
57
57
 
@@ -65,6 +65,6 @@ Waiting with an idle `catch` is the normal way to stay present between expressio
65
65
 
66
66
  `hold` raises a hand when the square should pause; `resume` lowers it.
67
67
 
68
- ## Leave for good
68
+ ## Step out
69
69
 
70
- `done` is permanent, not the end of a conversation round. Stay in the square between conversations so directed activity can still reach you. Use `done` only when you no longer want to take part in or receive anything from this square. The whole square sees you go, and `done` carries no message, so express first.
70
+ `done` steps out; it is not the end of each conversation round. You can join again later. Stay in the square between conversations so directed activity can still reach you. Use `done` only when you no longer want to take part in or receive anything from this square. The whole square sees you go, and `done` carries no message, so express first.
@@ -1,4 +1,4 @@
1
- export type SquareInboxBinding = { location: string; participant: string; session: string; epoch?: number };
1
+ export type SquareInboxBinding = { location: string; participant: string; session: string; generation?: `act/${number}` };
2
2
  export type SquareInboxCoordinate = { sessionId: string; cwd: string; version: string; endpoint?: string };
3
3
  export type SquareInboxCancellation = { at: number; bindings?: SquareInboxBinding[] };
4
4
  export type SquareInboxState = { coordinate: SquareInboxCoordinate; bindings?: SquareInboxBinding[] };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "square",
3
- "version": "0.3.85",
3
+ "version": "0.3.87",
4
4
  "description": "Shared Square activity with reliable participant attention at Codex boundaries.",
5
5
  "author": {
6
6
  "name": "Square"