@astrosheep/square 0.3.72 → 0.3.73
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.d.ts +0 -3
- package/dist/artifact.js +5 -59
- package/dist/catch-decisions.d.ts +7 -1
- package/dist/catch-decisions.js +21 -8
- package/dist/claude-delivery.d.ts +1 -1
- package/dist/claude-delivery.js +7 -3
- package/dist/cli/ledger-command.d.ts +12 -0
- package/dist/cli/ledger-command.js +68 -0
- package/dist/cli/registry.js +2 -0
- package/dist/help.js +7 -2
- package/dist/host-ledger-file-adapter.d.ts +4 -10
- package/dist/host-ledger-file-adapter.js +238 -198
- package/dist/host-ledger-migration.d.ts +26 -0
- package/dist/host-ledger-migration.js +227 -0
- package/dist/host-ledger-sqlite.d.ts +62 -0
- package/dist/host-ledger-sqlite.js +341 -0
- package/dist/host-ledger.d.ts +13 -4
- package/dist/ledger-maintenance.d.ts +30 -0
- package/dist/ledger-maintenance.js +110 -0
- package/dist/ports.d.ts +7 -1
- package/dist/runtime.d.ts +2 -1
- package/dist/runtime.js +12 -6
- package/dist/sqlite-transaction.d.ts +5 -0
- package/dist/sqlite-transaction.js +72 -0
- package/dist/square-actions.js +16 -8
- package/dist/square-migration.js +2 -1
- package/dist/square-sqlite.d.ts +2 -2
- package/dist/square-sqlite.js +15 -4
- package/guides/catch-contention-performance.md +157 -0
- package/guides/change-waits.md +1 -1
- package/guides/host-ledger.md +53 -6
- package/guides/indexed-host-ledger-performance.md +39 -0
- 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/client/square-message.tsx +35 -19
package/guides/host-ledger.md
CHANGED
|
@@ -1,11 +1,58 @@
|
|
|
1
|
-
# Host
|
|
1
|
+
# Host ledger
|
|
2
2
|
|
|
3
|
-
Square uses one user-level host
|
|
3
|
+
Square uses one user-level `host-ledger.sqlite` for participant bindings, delivery evidence, and wake attempts. It is separate from every `.square` artifact: the ledger owns host identity, ownership epochs, discovery and delivery evidence; the artifact owns square activity and participant lifecycle.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The default root is `~/.square/host-ledger`. Tests and isolated harnesses may set `SQUARE_HOST_LEDGER_ROOT`; otherwise `SQUARE_REGISTRY` selects its containing directory. An explicit adapter `rootPath` takes precedence. These settings select a single root, not additional ledgers.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Storage and delivery
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
SQLite uses DELETE journaling and FULL synchronous writes. Records retain original order and duplicates, with derived lookup indexes and separate presence/evidence revisions. Normal reads of an empty root create nothing. First mutation initializes storage only when neither legacy NDJSON file exists. Legacy-only storage refuses ordinary operation and requires explicit offline conversion; normal operation never dual-reads or automatically migrates.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
One wake attempt is one evidence record. Acquisition creates `dispatching` directly, before a send may happen. An interrupted dispatch becomes `unknown`, which forbids redispatch; current token-fenced recovery may still accept a late native admission. Accepted and possibly-sent wake records remain readable at any age. Prepared Claude correlations also bypass age visibility and housekeeping across every outcome, including failed and released, until explicitly retired. Ordinary records retain their existing seven-day visibility horizon, with expired ordinary rows reclaimed only during actual mutations. There is no automatic maintenance GC or activity-id-only sweep; durable delivery evidence is retired only by the explicit, artifact-proven `square ledger gc` below.
|
|
12
|
+
|
|
13
|
+
## Verify and convert offline
|
|
14
|
+
|
|
15
|
+
These commands need no participant or square location and use the resolved ledger root:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
square ledger verify
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Verification is read-only. It checks SQLite identity, version, schema, integrity, record codecs, derived indexes and revisions. With only legacy files, it scans both complete sources and reports accepted, ignored and rejected counts, readiness, and SHA-256 hashes of the original source bytes. Unsafe indexed records make readiness false without hiding the remaining counts or hashes; the CLI reports not ready and exits with status 2. With no files, it reports empty without creating a directory, database or lock. Output never includes record payloads, native credentials or endpoints.
|
|
22
|
+
|
|
23
|
+
To upgrade an NDJSON ledger:
|
|
24
|
+
|
|
25
|
+
1. Stop **every** Square host using that ledger, including interactive harnesses and background agents. Do not run old and new hosts together.
|
|
26
|
+
2. Keep a backup of the stopped legacy files.
|
|
27
|
+
3. Run:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
square ledger verify
|
|
31
|
+
square ledger migrate
|
|
32
|
+
square ledger verify
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
4. Start all hosts with the updated Square version together, using the same root.
|
|
36
|
+
|
|
37
|
+
Migration does not stop or restart hosts. Its locks fence compatible writers but do not prove hosts are stopped. It acquires the ownership claim fence, then `presence.ndjsonl.lock`, then `evidence.ndjsonl.lock`.
|
|
38
|
+
|
|
39
|
+
An older SQLite ledger format is upgraded in place by the same `square ledger migrate` under the same stopped-hosts prerequisite and the same lock order. The upgrade preserves every record, duplicate, original order and both observer revisions. `square ledger verify` stays read-only: it refuses an older or unsupported database with the migrate command and never upgrades silently.
|
|
40
|
+
|
|
41
|
+
The importer preserves all ages, future records, duplicates, original order, native correlation and residual fields. It ignores malformed JSON, wrong versions and records without a numeric timestamp, as the old reader did, and reports those counts. Any valid version-1 record whose indexed fields are unsafe refuses the entire conversion rather than losing evidence. Refusal identifies only source kind, rejected count and first rejected line; no accepted subset is published. Legacy unknown wake sources are normalized once during import.
|
|
42
|
+
|
|
43
|
+
A temporary SQLite database is built and verified under the same root, then published without overwriting an existing destination. Legacy sources remain byte-for-byte untouched. A valid existing SQLite ledger is an idempotent no-op; an older SQLite ledger format is upgraded in place under the fence; invalid or unsupported destinations are refused without repair. There is no force option. On a reported failure, keep hosts stopped, resolve the cause and rerun verification/conversion. An abrupt process termination can leave an unpublished `.host-ledger-migrate-*` directory; after confirming no converter is running, it can be removed without touching the sources or published database.
|
|
44
|
+
|
|
45
|
+
Once SQLite exists, legacy files are not consulted. Keeping them does not provide a rollback path after new SQLite writes: reverting to them would lose ownership and possibly-sent evidence. For a live SQLite ledger, use SQLite backup facilities rather than copying only its main file.
|
|
46
|
+
|
|
47
|
+
## Reclaim old evidence
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
square ledger gc --dry-run
|
|
51
|
+
square ledger gc
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`gc` considers wake and presentation evidence at least seven days old whose attention is permanently closed, proven per row against the square artifact named in the row: the activity historically reached that participant, and either catch recorded an exact receipt or the participant's later rejoin moved past it. Rows still `dispatching`, lifecycle rows, rows with unusable coordinates and rows whose artifact is missing, invalid or unreadable are retained. Eligibility is a proof, not a promise: `retired` counts rows actually removed, and a row whose record changed after the proof, or whose Claude correlation cannot be retired safely, is retained instead. `--dry-run` proves the same set and changes neither the ledger nor any artifact.
|
|
55
|
+
|
|
56
|
+
Reclamation is counted in rows, not bytes. Deleted rows free storage for reuse but do not necessarily shrink the SQLite file immediately; `gc` never runs `VACUUM`. Retiring a prepared Claude correlation payload leaves only a permanent `(harness, session, token)` recognition record, without payload, endpoint, location, participant, epoch or timestamp. These records are absent from ordinary evidence and attempts and confer no admission, presentation or receipt authority. Any retired token vetoes the entire native frame, including a mixed live/retired frame. Total token count remains unbounded. There are no hooks, timers or install steps: maintenance runs only when asked.
|
|
57
|
+
|
|
58
|
+
The obsolete `SQUARE_HOST_LEDGER_USER` and `SQUARE_HOST_LEDGER_LOCAL` settings, project-local fallback and scope-based copying are not used. Older local files and `wake-claims.ndjsonl` remain untouched and are not imported. Bindings that existed only in those abandoned stores require their host to rejoin; do not discard the supported ledger to reset bindings.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Indexed host-ledger measurement
|
|
2
|
+
|
|
3
|
+
The indexed host ledger eliminates whole-file NDJSON reads. This run does not show a consistent reduction in the 300-process catch-phase latency: the smaller-history case was slightly slower and the larger-history case was faster. Artifact contention remains substantial.
|
|
4
|
+
|
|
5
|
+
## Comparable workload
|
|
6
|
+
|
|
7
|
+
Baseline: `895ff50` (NDJSON with origin-separated instrumentation). SQLite plus safe retirement candidate: `60ea8b0`. Both used the same benchmark script, 300 processes, session discovery, active bell, disabled filesystem hints, and histories of 1,800 and 18,000 unrelated activities.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
node --import ./test/sandbox-env.js scripts/benchmark-idle-wait.mjs --workers 300 --mode session --active --no-hints
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The catch phase times opening the square, recognizing the participant, a first catch, a second catch checking duplicate consumption, and closing the square. These numbers are **not single-catch latency**, nor ordinary single-user latency. Discovery is measured separately. These are one completed run per implementation and history size, not repeated-run confidence intervals.
|
|
14
|
+
|
|
15
|
+
| Metric | NDJSON, 1,800 | SQLite, 1,800 | NDJSON, 18,000 | SQLite, 18,000 |
|
|
16
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
17
|
+
| Catch-phase p50 (ms) | 6,134.67 | 6,159.17 | 6,775.98 | 5,532.52 |
|
|
18
|
+
| Catch-phase p95 (ms) | 7,307.72 | 7,481.01 | 8,182.00 | 7,170.30 |
|
|
19
|
+
| Catch-phase aggregate CPU (ms) | 24,171.34 | 24,045.67 | 27,538.51 | 23,574.19 |
|
|
20
|
+
| Artifact busy observations | 46,710 | 52,686 | 56,401 | 46,069 |
|
|
21
|
+
| Catch-phase artifact body rows | 300 | 300 | 300 | 300 |
|
|
22
|
+
| Catch-phase artifact writes | 600 | 600 | 600 | 600 |
|
|
23
|
+
| Discovery p95 (ms) | 1,931 | 1,927 | 1,881 | 1,915 |
|
|
24
|
+
|
|
25
|
+
Every completed scenario discovered all 300 participants, consumed exactly one activity per participant, and consumed none again on the second catch.
|
|
26
|
+
|
|
27
|
+
## Ledger work
|
|
28
|
+
|
|
29
|
+
For either history size, the NDJSON catch phase performed 1,500 file reads totaling 95,520,000 bytes. The SQLite catch phase performed 1,500 data-read executions, returning 1,500 presence rows; all SQL result values together totaled 316,900 bytes (4,500 rows including header results). Host-ledger SQL busy observations were zero.
|
|
30
|
+
|
|
31
|
+
These are different transport measurements: file bytes versus SQL values returned to JavaScript. They demonstrate that whole-ledger materialization is gone; they do **not** measure equivalent physical disk I/O or prove a proportional speedup. SQLite bypasses the JavaScript filesystem hooks, so zero NDJSON operations/bytes is structural, not a standalone performance result.
|
|
32
|
+
|
|
33
|
+
The remaining catch-phase contention is attributed to the square artifact, separately from the host ledger. The result motivates investigating concurrent artifact transactions if reducing this latency is the next objective; it does not establish the benefit of any unimplemented change.
|
|
34
|
+
|
|
35
|
+
## Evidence and limits
|
|
36
|
+
|
|
37
|
+
Local raw reports: `/tmp/square-indexed-baseline-300.json` and `/tmp/square-indexed-final-300.json`. The completed SQLite run exited zero with empty stderr. An earlier invocation was terminated by the tool's 60-second execution limit before producing a report and was excluded; the completed run used the unchanged workload in a background process.
|
|
38
|
+
|
|
39
|
+
This benchmark uses an isolated ledger. It does not migrate the live host ledger, exercise real native transports, or measure GC reclamation. GC correctness is covered by the separate retirement, closure-proof, schema-upgrade, and CLI tests. Retirement leaves reusable SQLite pages and indefinitely retained Claude recognition tokens; neither immediate file shrinkage nor bounded total token growth is promised.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PluginTimelineItemProps } from "@getpaseo/plugin/client";
|
|
2
2
|
import { copyText, Icon, useToast } from "@getpaseo/plugin/client/react-native";
|
|
3
3
|
import { useMemo, useState } from "react";
|
|
4
|
-
import { Platform, Pressable, Text, View } from "react-native";
|
|
4
|
+
import { Platform, Pressable, ScrollView, Text, View } from "react-native";
|
|
5
5
|
import { squareHeadline, type SquareMessageData } from "../shared/message";
|
|
6
6
|
|
|
7
7
|
function withOpacity(color: string, opacity: number): string {
|
|
@@ -28,7 +28,7 @@ export function SquareMessage({ item, theme, layout }: PluginTimelineItemProps<S
|
|
|
28
28
|
bodyPanel: {
|
|
29
29
|
backgroundColor: theme.colors.surface1,
|
|
30
30
|
borderRadius: 12,
|
|
31
|
-
marginTop:
|
|
31
|
+
marginTop: 5,
|
|
32
32
|
marginLeft: 17,
|
|
33
33
|
overflow: "hidden" as const,
|
|
34
34
|
},
|
|
@@ -37,20 +37,14 @@ export function SquareMessage({ item, theme, layout }: PluginTimelineItemProps<S
|
|
|
37
37
|
fontSize: 12,
|
|
38
38
|
lineHeight: 21,
|
|
39
39
|
fontFamily: Platform.OS === "ios" ? "Menlo" : "monospace",
|
|
40
|
-
paddingHorizontal: layout.compact ?
|
|
41
|
-
paddingTop:
|
|
40
|
+
paddingHorizontal: layout.compact ? 10 : 12,
|
|
41
|
+
paddingTop: 8,
|
|
42
42
|
},
|
|
43
|
-
}), [
|
|
43
|
+
}), [foreground, layout.compact, muted, theme.colors.accent, theme.colors.surface1]);
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
46
|
<View>
|
|
47
|
-
<
|
|
48
|
-
accessibilityRole="button"
|
|
49
|
-
accessibilityLabel={latest !== undefined ? squareHeadline(latest) : "Square context"}
|
|
50
|
-
aria-expanded={expanded}
|
|
51
|
-
onPress={() => setExpanded(value => !value)}
|
|
52
|
-
style={({ pressed }) => ({ opacity: pressed ? 0.6 : 1 })}
|
|
53
|
-
>
|
|
47
|
+
<View>
|
|
54
48
|
<View style={{ flexDirection: "row", alignItems: "flex-start", gap: 7, paddingVertical: 2 }}>
|
|
55
49
|
<Text style={styles.marker}>■</Text>
|
|
56
50
|
<Text numberOfLines={1} style={styles.headline}>
|
|
@@ -58,7 +52,16 @@ export function SquareMessage({ item, theme, layout }: PluginTimelineItemProps<S
|
|
|
58
52
|
</Text>
|
|
59
53
|
{items && items.length > 1 && <Text style={styles.meta}>· {items.length} landed</Text>}
|
|
60
54
|
<View style={{ flex: 1 }} />
|
|
61
|
-
|
|
55
|
+
{!expanded && <Pressable
|
|
56
|
+
accessibilityRole="button"
|
|
57
|
+
accessibilityLabel={`展开 ${latest !== undefined ? squareHeadline(latest) : "Square context"}`}
|
|
58
|
+
aria-expanded={false}
|
|
59
|
+
onPress={() => setExpanded(true)}
|
|
60
|
+
hitSlop={6}
|
|
61
|
+
style={({ pressed }) => ({ padding: 5, opacity: pressed ? 0.6 : 1 })}
|
|
62
|
+
>
|
|
63
|
+
<Icon name="ChevronDown" size={18} color={muted} />
|
|
64
|
+
</Pressable>}
|
|
62
65
|
</View>
|
|
63
66
|
{(latest?.actor !== undefined || preview) && <View style={{ marginLeft: 17, marginTop: 2 }}>
|
|
64
67
|
<Text numberOfLines={1} style={styles.preview}>
|
|
@@ -67,17 +70,20 @@ export function SquareMessage({ item, theme, layout }: PluginTimelineItemProps<S
|
|
|
67
70
|
{preview}
|
|
68
71
|
</Text>
|
|
69
72
|
</View>}
|
|
70
|
-
</
|
|
73
|
+
</View>
|
|
71
74
|
{expanded && <View style={styles.bodyPanel}>
|
|
72
|
-
<
|
|
75
|
+
<ScrollView style={{ maxHeight: layout.compact ? 280 : 400 }} nestedScrollEnabled>
|
|
76
|
+
<Text selectable style={styles.bodyText}>{text}</Text>
|
|
77
|
+
</ScrollView>
|
|
73
78
|
<View style={{
|
|
74
|
-
marginHorizontal: layout.compact ?
|
|
75
|
-
marginTop:
|
|
79
|
+
marginHorizontal: layout.compact ? 10 : 12,
|
|
80
|
+
marginTop: 5,
|
|
76
81
|
borderTopWidth: 1,
|
|
77
82
|
borderTopColor: withOpacity(theme.colors.border, 0.55),
|
|
78
|
-
paddingVertical:
|
|
83
|
+
paddingVertical: 3,
|
|
79
84
|
flexDirection: "row",
|
|
80
|
-
|
|
85
|
+
alignItems: "center",
|
|
86
|
+
justifyContent: "space-between",
|
|
81
87
|
}}>
|
|
82
88
|
<Pressable
|
|
83
89
|
accessibilityRole="button"
|
|
@@ -94,6 +100,16 @@ export function SquareMessage({ item, theme, layout }: PluginTimelineItemProps<S
|
|
|
94
100
|
<Icon name="Copy" size={12} color={muted} />
|
|
95
101
|
<Text style={{ color: muted, fontSize: 11 }}>Copy content</Text>
|
|
96
102
|
</Pressable>
|
|
103
|
+
<Pressable
|
|
104
|
+
accessibilityRole="button"
|
|
105
|
+
accessibilityLabel="收起 Square context"
|
|
106
|
+
aria-expanded={true}
|
|
107
|
+
onPress={() => setExpanded(false)}
|
|
108
|
+
hitSlop={6}
|
|
109
|
+
style={({ pressed }) => ({ paddingHorizontal: 5, paddingVertical: 1, opacity: pressed ? 0.6 : 1 })}
|
|
110
|
+
>
|
|
111
|
+
<Icon name="ChevronUp" size={18} color={muted} />
|
|
112
|
+
</Pressable>
|
|
97
113
|
</View>
|
|
98
114
|
</View>}
|
|
99
115
|
</View>
|