oquay 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,350 @@
1
+ Metadata-Version: 2.5
2
+ Name: oquay
3
+ Version: 0.1.0
4
+ Summary: Local-first streaming event ledger and terminal cockpit for one workspace.
5
+ Project-URL: Homepage, https://github.com/onicarps/quay
6
+ Project-URL: Repository, https://github.com/onicarps/quay
7
+ Project-URL: Issues, https://github.com/onicarps/quay/issues
8
+ Author: quay contributors
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: agents,event-log,local-first,observability,terminal
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: System :: Logging
21
+ Classifier: Topic :: System :: Monitoring
22
+ Requires-Python: >=3.11
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8.0; extra == 'dev'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # Quay
28
+
29
+ A local-first event ledger for coding agents. Append-only SQLite, a typed
30
+ envelope with real state machines, a daemon that speaks a framed socket
31
+ protocol, a CLI, and a live terminal cockpit. Python standard library only.
32
+
33
+ Quay is a competitor to [Changi](../changi/): it keeps the "one small ledger in
34
+ your workspace" idea and fixes the parts that make Changi hard to run for long
35
+ periods — quadratic status reads, no crash recovery story, no backpressure, no
36
+ way to tell whether your history is intact, and no upgrade path.
37
+
38
+ - **Integrity you can check.** Every event is SHA-256 chained. `quay verify`
39
+ recomputes the whole chain and names the first divergence.
40
+ - **Status that does not get slower.** Plane counts are maintained
41
+ transactionally, so `quay status` is O(1) at 10 rows or 10 million.
42
+ - **A daemon that survives clients.** Per-connection reader/writer threads, a
43
+ bounded outbox, and a `lagged` frame that tells a slow subscriber exactly how
44
+ much it missed.
45
+ - **Real state machines.** Signals and work records follow explicit transitions,
46
+ enforced on write, so the ledger cannot fill with impossible history.
47
+ - **An upgrade path.** `quay import` reads an existing Changi ledger, maps it
48
+ onto Quay's envelope, and reports every adjustment it made.
49
+
50
+ Status: complete and tested. 381 tests, no skips, no mocks of storage or
51
+ transport. See `COMPETITIVE_ANALYSIS.md` for the requirements this implements
52
+ and `docs/ARCHITECTURE.md` for the design.
53
+
54
+ ## Install
55
+
56
+ Quay runs on Linux and macOS. Requires Python 3.11 or newer (standard library only; zero external Python dependencies).
57
+
58
+ ### Via NPM (Global Terminal Binary)
59
+
60
+ If you have Node.js 18+ and Python 3.11+:
61
+
62
+ ```bash
63
+ npm install --global @agentbus/quay
64
+ quay --version
65
+ quayd --version
66
+ ```
67
+
68
+ ### Via Pip (PyPI Package)
69
+
70
+ ```bash
71
+ pip install oquay
72
+ quay --version
73
+ quayd --version
74
+ ```
75
+
76
+ ### From Local Checkout
77
+
78
+ ```bash
79
+ cd projects/quay
80
+ python3 -m pip install . # or: pip install --user .
81
+ quay --version
82
+ quayd --version
83
+ ```
84
+
85
+ To run from a checkout without installing:
86
+
87
+ ```bash
88
+ export PYTHONPATH="$PWD/src"
89
+ python3 -m quay.cli --help
90
+ ```
91
+
92
+ ## Quickstart
93
+
94
+ ```bash
95
+ cd ~/my-project
96
+ quay init # create .quay/ and check the Git advisory
97
+
98
+ quay emit build/log '{"text": "compiled ok", "tags": ["build"]}' --kind note
99
+ quay emit build/alert '{"summary": "disk almost full"}' --kind signal
100
+ quay log --limit 5
101
+ quay status
102
+ ```
103
+
104
+ Payloads are validated per kind. A `note` carries `text` (plus optional
105
+ `tags`), a `signal` carries `summary` and a state, a `metric` carries `name` and
106
+ a numeric `value`, and `generic` accepts any JSON object. `quay kinds` prints
107
+ the exact contract for every kind.
108
+
109
+ Start work, keep it alive, finish it with evidence:
110
+
111
+ ```bash
112
+ quay work claim job-42 --executor builder --lease-seconds 120
113
+ quay work heartbeat job-42 --executor builder --lease-seconds 120
114
+ quay work complete job-42 --executor builder --evidence '{"artifact": "dist/app.tar.gz"}'
115
+ quay work list --state done
116
+ ```
117
+
118
+ Watch the ledger live in the cockpit, or stream JSON lines:
119
+
120
+ ```bash
121
+ quay watch
122
+ quay watch --plain --kind signal
123
+ ```
124
+
125
+ Verify and search:
126
+
127
+ ```bash
128
+ quay verify
129
+ quay query --topic 'build/*' --kind signal --limit 10
130
+ quay query --text "almost full" --count-only
131
+ quay query --kind work --state done --export csv
132
+ ```
133
+
134
+ Everything prints JSON automatically when piped, so no flag is needed in a
135
+ pipeline:
136
+
137
+ ```bash
138
+ quay status | jq '.planes'
139
+ ```
140
+
141
+ ## CLI reference
142
+
143
+ Common flags on every command: `--workspace PATH` (default: the current
144
+ directory), `--json`, `--no-json`, `--timeout SECONDS`. Output is JSON when
145
+ stdout is not a terminal; `--no-json` forces the human rendering.
146
+
147
+ | Command | Purpose |
148
+ | --- | --- |
149
+ | `quay init [--git-ignore --yes]` | Create `.quay/`, report a Git advisory, optionally add the ignore line |
150
+ | `quay start [--idle-seconds N]` | Start this workspace's daemon (`0` disables idle shutdown) |
151
+ | `quay stop [--all --yes]` | Stop this daemon, or every registered daemon |
152
+ | `quay restart` | Stop and start |
153
+ | `quay status` | Daemon health plus ledger and plane counts |
154
+ | `quay daemons` | Every registered workspace daemon on this machine |
155
+ | `quay doctor` | Install, socket, registry, and ledger integrity checks |
156
+ | `quay kinds` | The kind contracts: states and required fields |
157
+ | `quay verify [--no-record]` | Recompute the hash chain |
158
+ | `quay emit TOPIC [PAYLOAD] [--kind K] [--producer P] [--causation SEQ] [--correlation ID]` | Append one event |
159
+ | `quay work claim\|heartbeat\|complete\|abandon KEY --executor E […]` | Drive the work plane |
160
+ | `quay work list [--state S] [--executor E]` / `quay work reap` | Inspect work and expire lapsed leases |
161
+ | `quay query [filters]` | Search with kind, topic glob, producer, state, correlation, causation, seq range, time range, and text filters; `--count-only`, `--export jsonl\|csv`, `--limit`, `--offset`, `--order` |
162
+ | `quay log [--limit N] [--follow] [--for S] [--max-events N]` | Recent events, optionally streaming |
163
+ | `quay watch [--kind K] [--plain] [--history N] [--for S] [--max-events N]` | Live TUI cockpit, or JSON lines with `--plain` |
164
+ | `quay prune (--keep-last N \| --before TS) [--dry-run] [--yes]` | Delete an old prefix after a checkpoint |
165
+ | `quay bench [--scale quick\|full] [--out FILE]` | Run the benchmark suite |
166
+ | `quay import SOURCE [--dry-run] [--max-warnings N]` | Import a Changi ledger (see `docs/MIGRATION.md`) |
167
+
168
+ Exit codes: `0` success, `1` a check failed (for example `verify`), `2` usage or
169
+ validation error, `3` the daemon is unreachable.
170
+
171
+ `QUAY_PRODUCER` sets the default producer for `quay emit`.
172
+
173
+ ## Terminal cockpit
174
+
175
+ `quay watch` renders a full-screen dashboard: daemon health, ledger counters,
176
+ plane counts, a live event stream, and a filter bar. Keys:
177
+
178
+ | Key | Action |
179
+ | --- | --- |
180
+ | `q` | quit |
181
+ | `p` | pause or resume the stream |
182
+ | `v` | run verification and show the result |
183
+ | `r` | reap expired leases |
184
+ | `k` | cycle the kind filter |
185
+ | `/` | edit the topic filter |
186
+ | `?` | help overlay |
187
+
188
+ The dashboard redraws with minimal ANSI diffs rather than repainting the whole
189
+ screen, so it stays usable over a slow terminal. `quay watch --plain` prints
190
+ JSON lines instead, which is what you want in a pipe or a log.
191
+
192
+ ## Python API
193
+
194
+ ```python
195
+ from quay.client import QuayClient, SubscriptionStream
196
+ from quay.daemon import ensure_daemon
197
+
198
+ paths, _health = ensure_daemon(".") # start a daemon if needed
199
+
200
+ with QuayClient(paths.socket) as client:
201
+ event = client.request("emit", {
202
+ "topic": "agent/build",
203
+ "producer": "my-agent",
204
+ "payload": {"step": "compile"},
205
+ "kind": "note",
206
+ })["event"]
207
+ print(event["seq"], event["hash"][:12])
208
+
209
+ client.request("work_claim", {"key": "job-7", "executor": "my-agent", "lease_seconds": 60})
210
+ client.request("work_complete", {"key": "job-7", "executor": "my-agent",
211
+ "evidence": {"exit_status": 0}})
212
+ ```
213
+
214
+ Live subscription with backfill:
215
+
216
+ ```python
217
+ stream = SubscriptionStream(paths.socket, filters={"topic": "agent/*"}, replay=50)
218
+ stream.open()
219
+ for event in stream.events(timeout=5.0):
220
+ print(event["seq"], event["topic"])
221
+ ```
222
+
223
+ Using the store directly, with no daemon:
224
+
225
+ ```python
226
+ from quay.store import LedgerStore
227
+
228
+ with LedgerStore(".quay/ledger.db") as store:
229
+ store.append(topic="t", producer="p", payload={"hello": "world"})
230
+ print(store.status()["planes"])
231
+ print(store.verify()["ok"])
232
+ ```
233
+
234
+ ## Changi migration
235
+
236
+ ```bash
237
+ quay import /path/to/.changi --dry-run # report only
238
+ quay import /path/to/.changi # import
239
+ quay verify
240
+ ```
241
+
242
+ Changi's flat payload-with-extensions table is mapped onto Quay's typed
243
+ envelope: signals and work receipts become plane records, everything else stays
244
+ generic with its payload intact, and causal links are remapped to new sequence
245
+ numbers. Where Changi stored a terminal record with no history, Quay writes a
246
+ clearly marked reconstructed root rather than inventing a plausible past. Every
247
+ adjustment lands in the report. Full details: `docs/MIGRATION.md`.
248
+
249
+ ## Benchmarks
250
+
251
+ `quay bench --scale quick` prints the workload profile with the numbers, so
252
+ results are never quoted without their context. Two of the four benchmarks
253
+ compare against a reimplemented baseline: the one-transaction-per-event write
254
+ pattern Changi uses, and the full-table scan that a naive status implementation
255
+ performs.
256
+
257
+ Reference machine: Linux 6.6 (WSL2), Python 3.12.3. Quick profile: 2,000
258
+ appends, 10,000 rows, 300 events fanned out to 4 subscribers.
259
+
260
+ | Measurement | Quay | Baseline |
261
+ | --- | --- | --- |
262
+ | Append throughput (batched, 500/txn) | 3,008 events/s | 893 events/s single-append |
263
+ | `status()` at 10,000 rows | 0.24 ms | 49.5 ms full-table scan |
264
+ | Query, topic glob / producer filter | 2.7 ms / 2.7 ms | – |
265
+ | Query, payload text | 11.2 ms | – |
266
+ | Push fan-out p50 / p99 | 0.0 ms / 8.4 ms | – |
267
+ | Fan-out end-to-end p50 / p99 | 10.7 ms / 22.2 ms | – |
268
+ | Fan-out deliveries | 1,200 of 1,200, 0 dropped | – |
269
+
270
+ Reading these honestly:
271
+
272
+ - `status()` cost does not grow with ledger size; the scan baseline does. The
273
+ reported growth factor between the half-size and full-size runs is noise
274
+ (the smaller run measured slower), not a trend.
275
+ - The fan-out benchmark runs four concurrent collector threads, so its emit
276
+ round trip (10.5 ms p50) is higher than an idle daemon's (~2.9 ms p50). That
277
+ contention is part of what the number measures, and it is why the benchmark
278
+ reports push latency and end-to-end latency separately.
279
+ - The durable write dominates end-to-end latency. Batched writes amortize it.
280
+
281
+ Run the full profile with `quay bench --scale full --out bench.json`.
282
+
283
+ ## Demo
284
+
285
+ ```bash
286
+ ./run_demo.sh # unattended end-to-end walkthrough, exits 0
287
+ ```
288
+
289
+ The demo creates a scratch workspace in a temporary directory, starts a daemon,
290
+ writes notes, signals, and work records, exports JSON and CSV, streams live
291
+ events, runs verification, tampers with the ledger to prove verification fails,
292
+ imports a synthetic Changi ledger, runs the benchmark suite, and cleans up. It
293
+ prints each step and never touches your real workspace or registry.
294
+
295
+ ## Testing
296
+
297
+ ```bash
298
+ python3 -m pip install pytest
299
+ python3 -m pytest -q # 381 tests, ~100s
300
+ python3 -m pytest -q tests/test_store.py tests/test_server.py
301
+ ```
302
+
303
+ The suite uses real SQLite files, real AF_UNIX sockets, real daemon processes,
304
+ and real multi-process writes. Nothing mocks storage or transport. Coverage
305
+ includes: canonical encoding and hash chaining, schema validation and state
306
+ machines, tamper detection, crash and recovery, work leases and expiry,
307
+ backpressure and `lagged` accounting, daemon lifecycle and stale-state reclaim,
308
+ live TUI rendering and key handling, every CLI command, the benchmark suite, and
309
+ Changi import fidelity.
310
+
311
+ ## Documentation
312
+
313
+ | Document | Contents |
314
+ | --- | --- |
315
+ | `COMPETITIVE_ANALYSIS.md` | The target, the weaknesses addressed, requirements F1–F20 / N1–N7, and the milestone gates |
316
+ | `docs/ARCHITECTURE.md` | Modules, data model, concurrency, durability, teardown ordering, benchmark method |
317
+ | `docs/PROTOCOL.md` | Framing, frame shapes, every operation, filters, backpressure, error codes |
318
+ | `docs/MIGRATION.md` | Changi import mapping, reconstructed roots, report format, limitations |
319
+
320
+ ## Design limits
321
+
322
+ These are deliberate, and each one is documented rather than hidden:
323
+
324
+ - **One writer per workspace.** SQLite gives one write transaction at a time.
325
+ Batched writes make that a throughput question, not a correctness one.
326
+ - **No authentication.** The socket is `0600` and local-only. Multi-user
327
+ isolation is the filesystem's job, not Quay's.
328
+ - **No replication or federation.** This is a local ledger, not a cluster.
329
+ - **Payload text search scans.** Topic, producer, and sequence filters use
330
+ indexes; `--text` reads payloads by design.
331
+ - **Imports are not deduplicated.** Import into a fresh workspace, or expect
332
+ skip warnings for plane records that already reached a terminal state.
333
+
334
+ ## Layout
335
+
336
+ ```
337
+ projects/quay/
338
+ pyproject.toml packaging (hatchling, no runtime deps)
339
+ README.md
340
+ COMPETITIVE_ANALYSIS.md
341
+ docs/ architecture, protocol, migration
342
+ run_demo.sh unattended end-to-end demo
343
+ src/quay/ canonical, schemas, store, protocol, server,
344
+ client, daemon, workspace, cli, tui, bench, migrate
345
+ tests/ pytest suite (381 tests)
346
+ ```
347
+
348
+ ## License
349
+
350
+ MIT. See `LICENSE`.
@@ -0,0 +1,18 @@
1
+ quay/__init__.py,sha256=UasWilAukRx9Gti88HaHM0RIOPZs5YK9iF7XZfY3dtY,619
2
+ quay/bench.py,sha256=8lhn8LehpIYxpKxF31aJjTSUQXpm9eQuVKa0xoZpLmc,16031
3
+ quay/canonical.py,sha256=_S5izySAtohfVD_wVIO6We7Kx-zxagipjFfynJUOOHw,5457
4
+ quay/cli.py,sha256=baI4sEOjcLy9pRYNA957P9J284Rx11urO2nNSbnE9to,33639
5
+ quay/client.py,sha256=3EjyDcxYFeCyQyb-TeasbMJbTkM_ysyACg4fXFXzhaY,14318
6
+ quay/daemon.py,sha256=bKtUESC6uUGL3XCe9-CTxW3PuEC8pEJcqD_ktWQCR_M,9602
7
+ quay/migrate.py,sha256=sqFR9atQ_4Hnw1zCane34mBlXdYbVHkvAYUHAHFfW-o,17058
8
+ quay/protocol.py,sha256=Rp3Xvr8PjzfbkW0s_efvUXOKgAaeOZ32Fi_wgkBbFeE,8958
9
+ quay/schemas.py,sha256=30vXtRs21U5h8ukQ7fY25FpksDtSptHPk4gFAjXbyzs,14254
10
+ quay/server.py,sha256=wftEWpejd8iz6ymh6o7Yj0GPW6JkdLMmiE0Z9pbbJK0,35004
11
+ quay/store.py,sha256=n4fu8CKiGAQGQow4itfsuM9eFIStS--WiLOAbp5wsTo,49669
12
+ quay/tui.py,sha256=vuXKo-sGnlpFXwshQX8RgIbAsw8WOFPmXjNe-lyXOws,25172
13
+ quay/workspace.py,sha256=mcAKKk4qdH_wGsQUFn7ijN_HMFtEKV5kkF_bO_pnz9w,12881
14
+ oquay-0.1.0.dist-info/METADATA,sha256=Vfpab86h78K58iAW3w1oBqwFbaQWQMOg3ogSTTPF3AE,13505
15
+ oquay-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
16
+ oquay-0.1.0.dist-info/entry_points.txt,sha256=1-gz2lpvEukbDnwLx_ZadcA_bsStEBfRhUgeMj0oMOA,71
17
+ oquay-0.1.0.dist-info/licenses/LICENSE,sha256=kkMBX_wJRRciZcd1nyYEgQPFluqGTngMiIoYO6i2QHM,1074
18
+ oquay-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ quay = quay.cli:main
3
+ quayd = quay.daemon:daemon_main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 quay contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
quay/__init__.py ADDED
@@ -0,0 +1,20 @@
1
+ """Quay: a local-first streaming event ledger and terminal cockpit.
2
+
3
+ Quay records typed events durably in a single SQLite file, streams them to
4
+ live subscribers over a framed Unix-socket protocol, and renders them in an
5
+ incremental terminal UI.
6
+
7
+ The package has no runtime third-party dependencies.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ __all__ = ["__version__", "PROTOCOL_VERSION", "LEDGER_FORMAT_VERSION"]
13
+
14
+ __version__ = "0.1.0"
15
+
16
+ #: Wire protocol version. Bumped only on an incompatible frame change.
17
+ PROTOCOL_VERSION = "1"
18
+
19
+ #: On-disk ledger format version stored in the ``meta`` table.
20
+ LEDGER_FORMAT_VERSION = 1