Releases: caty-ai/sitter
Release list
v0.5.5: dedicated ask ledger + run-ledger rotation contract (docs); --production-shape bench (#74)
v0.5.5 — dedicated ask ledger + run-ledger rotation contract (docs); --production-shape bench
No behaviour change in sitter. This release ships a norm for operators and the evidence behind it.
Docs (the contract)
- Run the expect family on its own ledger.
expect/ack/ask/watch/sweepshould use a ledger that nositter runand no foreign writer appends to. Sharing stays supported, but every sweep or watch pass stages and replays the whole file, so its cost grows with the run supervisor's history rather than with live asks. (docs/reference.md→ Ledger placement and rotation;docs/reference.ja.md→ 台帳の配置とローテーション.) - Rotate run ledgers by rename, under the ledger lock, only when nothing in the expect family reads them. Two pre-rotation checks (no reference to the path from any expect-family caller; the
"expect_id"row count equals the record kept in<ledger>.expect-count, never re-derived from the file). A missing record on a file that carries expect rows is an unknown state and blocks both rotation and rescue. Never rotate a ledger the family reads — the next pass stages a fresh file and every active ask disappears silently. docs/specs/ledger-separation.md(new): the measured production shape (14,170 rows, 6 of them expect-family), why compaction was rejected at design review, contracts A1–A5 / B1–B5, and three runbooks exercised on macOS during review — one-time placement of live asks (drain every caller, copy and prove the copy byte-for-byte under the lock, repoint, re-prove), rescue of stranded asks (snapshot count,event_iddedupe, id-collision check and append in one lock hold, fail closed), and rotation with the count check and the rename in one critical section.
Bench
tests/fixtures/gen-ledger.sh --production-shape <lines> <out>: production-shaped ledger (run-family and foreign rows ≈ 5:3, the same six expect rows last). The two-argument mode is byte-identical to v0.5.4.tests/bench-ledger.sh --production-shape [--repeat N] [--lines N]: shared vs dedicated, median of N.
| ledger | rows | sweep --once |
watch --once |
|---|---|---|---|
| shared, production shape | 13,884 | 2.627 s | 1.412 s |
| dedicated ask ledger | 6 | 1.212 s | 0.076 s |
(macOS arm64, median of 3; the discarded rows cost ≈ 1.4 s per pass for either verb.)
Review
PR #83 — 12 rounds with Opus 5 / Qwen 3.8 Max / Gemini 3.8 Flash; every blocking finding was reproduced by the seat and adopted the same round; 3/3 GO at 6bd477a. Non-blocking leftovers are tracked in #84. Operator wiring (the maintainer's sitter-ask wrappers, launchd jobs and dashboard) is tracked outside this repository via #74.
v0.5.4
v0.5.4 — sweep verifies the staged ledger prefix (#76)
sweep --once stages a private copy of the ledger and re-checks only the bytes appended after it. Since v0.5.0 an in-place replacement of the ledger that was equal-or-longer than the stage but no longer contained the staged history (hand compaction, restoring a backup over it, shrink-then-grow) could produce a spurious nudge and --on-fail hook call.
sweepnow records the SHA-256 of its private stage and, for each due candidate, trusts the staged state only while the live ledger is at leaststage_byteslong and its firststage_bytesbytes still hash to the same value; otherwise it falls back to a full replay for that candidate. Hash or I/O failures end the pass with exit 1 instead of trusting the offset.- Regression test
ledger_sweep_staged_prefix_replacement(equal-length and longer replacements). - Docs: the live ledger is append-only. Supported writers only append; replacing a live ledger in place is out of contract.
Cost: one hash of the stage per pass plus one prefix hash per due candidate (8 MB ≈ 0.03 s).
v0.5.3: LC_ALL=C scoped to sitter's string handling; hooks and the wrapped command inherit the operator locale (#75)
v0.5.3 — LC_ALL=C no longer leaks into hooks and the wrapped command (#75)
Seven functions in sitter assigned LC_ALL=C as a bare global. When the operator had LC_ALL exported (containers, CI), those assignments overwrote the exported value mid-run, so --on-fail hooks observed LC_ALL=C instead of the operator's locale.
Fixed
json_quote,sanitize_expect_value,truncate_utf8,emit_expect_event,build_ask_event_record:local LC_ALL=C.invoke_hook,emit_event: assignment removed; the 512-bytedetail_truncatedcount moves to abyte_lengthhelper so noLC_ALL=Cframe is alive when the hook is spawned (bash locals inherit the export attribute of an exported global).--on-failhooks and the wrapped command now inherit the operator'sLC_ALL/LANGunchanged. Sitter's internal string-handling helpers and the explicitly scoped replay pipelines still run underLC_ALL=C.
Tests
hook_sees_operator_locale(run + expect/sweep hook paths, unset control, 513-byte multibytedetail_truncated).ledger_sweep_exported_locale_equivalence(sweep rows bytewise identical under LANG-only /LC_ALL=C.UTF-8/en_US.UTF-8/C).
Docs: docs/reference.md, docs/reference.ja.md.
v0.5.2: truncate_utf8 non-iconv fallback now drops invalid UTF-8 anywhere in the text (#77)
v0.5.2: truncate_utf8 non-iconv fallback now drops invalid UTF-8 anywhere in the text (#77)
Bug fix. When iconv is unavailable, truncate_utf8 previously trimmed only an
incomplete trailing sequence, so a stray invalid byte inside --text survived
into an emitted ledger row. The fallback is now a forward byte walk that keeps
the longest RFC 3629 well-formed prefix (the same result the iconv branch
computes). Test-only seam SITTER_TEST_NO_ICONV=1; two new tests; docs state
the guarantee per validator (macOS libiconv is lenient on forms above
U+10FFFF and obsolete 5/6-byte sequences).
Evidence: PR #79 (L1-7 record; CI on
8e8e66b green on ubuntu and macOS; seats Opus 5 / Kimi K3 / Qwen 3.8 Max r3 GO x3).
Follow-up: #80
v0.5.1
v0.5.1 — string fast paths (json_quote / sanitize_expect_value) and operator notes on sweep hook serialisation and child-log rotation
Perf batch #72 (S). Clean values (no '"', '', 0x01-0x1f) skip the per-byte
loop in json_quote and sanitize_expect_value; output is byte-identical to
v0.5.0 (differential test against the frozen tests/fixtures/sitter.baseline).
64 KiB --text: 4.69 s -> 0.075 s. Four remaining perf findings are closed as
measured won't-fix on #72; two of them add operator notes to docs/reference*.md.
Evidence: PR #78 completion record (L1-7), CI on bbb0766 green on ubuntu and
macOS — #78
Issue: #72
v0.5.0 — single-pass sweep, fork-free replay
Performance release for the expect/sweep/watch subsystem (#71, PR #73).
What changed
expect_replay_lineparses ledger lines with bash regex instead of 8–11printf | sedpipelines per line (zero subprocesses per line).sweep --oncestages the ledger once, records the staged byte offset under the ledger lock, and per SLA-due candidate scans only the live tail appended since staging. A possible mention of the key, a shorter live file, an unterminated staged row, or any read error falls back to the original full replay.- Replay is now bytewise and identical on every platform; previously a ledger line containing invalid UTF-8 was poisoned on macOS (BSD sed) and passed or skipped on Linux depending on locale state. Emitted text is still truncated to valid UTF-8 (when iconv is available — see follow-up).
sweep --onceis fail-closed: a snapshot copy or live-tail read failure exits nonzero instead of reporting success; temp files are cleaned on every exit path.- Ledger format, emitted events, hook and ack rules: unchanged.
Measured (macOS arm64, tests/bench-ledger.sh, single observations, before = v0.4.0)
| lines | sweep before → after | watch before → after |
|---|---|---|
| 1,000 | 32.8 s → 3.4 s | 14.2 s → 0.3 s |
| 10,000 | 285.7 s → 21.7 s | 134.2 s → 2.5 s |
| 50,000 | 1215 s → 96.5 s | 551 s → 12.3 s |
Verification: frozen copy of the v0.4.0 script (tests/fixtures/sitter.baseline) as an oracle; seven new equivalence/race/cleanup cases; 181/181 on macOS (bash 5 and 3.2) and ubuntu. Windows/MSYS unverified on this lane.
Review: writer Codex (GPT-6 Astra); three independent read-only seats (Opus 5, Kimi K3, Gemini 3.8 Flash) over three rounds; blocking findings 0 at merge. Full record in PR #73.
Follow-ups: ledger compaction design; localise the remaining LC_ALL=C assignments + exported-locale test; equal-or-longer external ledger replacement; truncate_utf8 non-iconv fallback.
v0.4.0
sitter v0.4.0 — --heartbeat-file: liveness that does not depend on stdout
Workers that are silent by design (a remote agent turn over ssh, a CLI that prints only at the end) used to be killed by the log-mtime stall detector while healthy (#10, two incidents). This release adds an opt-in, worker-agnostic liveness input.
New
sitter run --heartbeat-file <path>— the stall clock now uses the freshest of the log mtime and the heartbeat file mtime. sitter never executes anything to produce the heartbeat; the wrapper that owns worker knowledgetouches the file while its own cheap check says the worker is alive. ADR-0001 (no--probe) stays in force. Design record:docs/adr/0003-heartbeat-file.md.examples/heartbeat-wrapper.sh— template: exec's the worker, touches only when the worker is alive and a bounded vouch passes, exits with the worker.- README (en/ja/zh/th) "Know what counts as frozen" now has a three-way guide: raise
--stall-after/--stall-after 0 --timeout/--heartbeat-file.
Guarantees
- Flag unset: behavior byte-identical to v0.3.x (ledger schema and
reasonvocabulary unchanged). - Fail-closed: a missing, non-regular, symlinked or unstattable heartbeat contributes nothing (the log stays the stall clock); creation or per-attempt touch failure aborts the attempt.
- Refused at startup (exit 2): empty path, symlink/non-regular path, collision with
--ledger/ its lock /--kill-file/--log,--stall-after 0;ask/watchreject the flag. SITTER_HEARTBEAT_FILEis exported to the wrapped child only — never read as configuration, never passed to--on-fail.
Review
Design: 3 blind seats (GLM 5.3 / Kimi K3 / Grok 4.6), amendments A1–A10. Implementation: 3 seats r1 + delta D1–D9, cumulative GO. Suite 174 PASS / 0 FAIL on Linux and macOS. PR #59, Issue #46.
repo-state caller v0.12.1: app-authenticated stamp pushes (family-os#140)
repo-state caller v0.12.1: app-authenticated stamp pushes (family-os#140)
v0.3.3 — WSL2: install, scheduling, and notification docs stop assuming macOS
Docs-only release (Issue #52, PR #53; parent: family-os#139).
- Install block (README ×4 languages): the PATH-persist step now ships both rc-file variants inline —
~/.zshrc(macOS default) and~/.bashrc(Linux/WSL2 default) — so a WSL2 copy-paste reader no longer silently ends up withoutsitteron PATH. - Scheduling: new systemd user units
examples/sitter-sweep.service+examples/sitter-sweep.timernext to the launchd plist, with install steps and precise WSL2 caveats (systemd default on current Ubuntu, sudo + PowerShell enablement for other distros, cron autostart rule,loginctl enable-lingerfor unattended sweeps). - Notifications: README example now offers
notify-send(Linux/WSL2) and a Windows-toast pointer alongside macOS notification centre.
No behavior change: make test 150 PASS / 0 FAIL. Review: 3-seat heterogeneous panel (GLM 5.3 / Kimi K3 / Grok 4.6) 3/3 GO + delta confirmation GO. Previous release: v0.3.2.
repo-state caller + generation stamp (family-os#127 rollout; closes #50)
repo-state caller + generation stamp (family-os#127 rollout; closes #50)