Ambient system companions over one privacy-preserving signal daemon (aggregate-only, no keystroke content): a git-driven terminal garden and IOKit hardware collectors. ambient daemon macos privacy terminal

Commit 7d186eb8e3

7d186eb8e302a3f3f543e1bd54660cc95f8d82bc

parent: 8f684ea77b

Verified · cmc ci/test: error

cmc <hello@cleberg.net> · 2026-09-04 04:02 UTC

Add gitbay CI and a pre-push hook for the Swift tests

.gitbay/ci.yml runs cargo test --locked and clippy with -D warnings on every
push. It checks for zsh with zsh/zpty first so the differential secret-typing
tests fail loudly on a runner without it instead of being skipped.

The runner is Linux, so .githooks/pre-push runs the macos-collector Swift
tests on the Mac doing the push.

Fix the two clippy findings that -D warnings would reject: approx_constant in
privacy_invariant.rs and doc_lazy_continuation in terminal-garden.

Ref #4

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
.gitbay/ci.yml added +16
@@ -0,0 +1,16 @@
1# gitbay CI. One build per push; every step runs with `sh -c` and the first
2# failure stops the job.
3#
4# The runner is Linux and unprivileged, so the macos-collector Swift tests
5# cannot run here. They run from .githooks/pre-push on the Mac doing the push
6# (git config core.hooksPath .githooks); see README "Build & test".
7jobs:
8 test:
9 steps:
10 # The differential secret-typing tests (the privacy ship-gate) drive a
11 # real interactive zsh under zsh/zpty. Check for it up front so a missing
12 # zsh fails with a clear message instead of deep inside cargo test, and
13 # is never silently skipped.
14 - zsh -c 'zmodload zsh/zpty' || { echo "runner needs zsh with the zsh/zpty module (apt-get install zsh)" >&2; exit 1; }
15 - cargo test --locked
16 - cargo clippy --all-targets --locked -- -D warnings
.githooks/pre-push added +21
@@ -0,0 +1,21 @@
1#!/bin/sh
2# Run the macos-collector Swift tests before every push.
3#
4# gitbay's runner is Linux, so the Swift side of the Swift/Rust wire-contract
5# test cannot run in CI. This hook is where it gates: a failing swift test
6# aborts the push. Wired up once per clone with:
7#
8# git config core.hooksPath .githooks
9set -eu
10
11case "$(uname -s)" in
12Darwin) ;;
13*)
14 echo "pre-push: not macOS, skipping swift test" >&2
15 exit 0
16 ;;
17esac
18
19root=$(git rev-parse --show-toplevel)
20echo "pre-push: swift test (macos-collector)" >&2
21swift test --package-path "$root/macos-collector"
README.md +14
@@ -211,3 +211,17 @@ swift build # builds the collector
211211swift test # the Swift↔Rust wire-contract test
212212swift run macos-collector --once # one real IOKit read (no root)
213213```
214
215### CI
216
217`.gitbay/ci.yml` runs `cargo test --locked` and
218`cargo clippy --all-targets --locked -- -D warnings` on every push. The
219differential secret-typing tests need `zsh` with the `zsh/zpty` module on the
220runner; the job checks for it first and fails rather than skipping the gate.
221
222The runner is Linux, so the Swift tests run from `.githooks/pre-push` on the
223Mac doing the push. Enable it once per clone:
224
225```sh
226git config core.hooksPath .githooks
227```
crates/signal-schema/tests/privacy_invariant.rs +1 −1
@@ -40,7 +40,7 @@ fn value_channel_is_exactly_f64() {
4040 );
4141 // Construction only accepts an f64. If someone adds a String-accepting
4242 // constructor, this file is where the review happens.
43 let _v = Value(3.14_f64);
43 let _v = Value(2.5_f64);
4444}
4545
4646/// The `Signal` type must declare no content-carrying field. We assert this
crates/terminal-garden/src/main.rs +1 −1
@@ -44,7 +44,7 @@ fn main() {
4444
4545/// Connect and render live: the daemon replays the last-value cache on connect,
4646/// then streams updates. We keep the latest value of each metric (keyed by name
47/// + repo tag) and re-render the garden on every frame. `Ok(())` is a clean EOF
47/// and repo tag) and re-render the garden on every frame. `Ok(())` is a clean EOF
4848/// (the daemon closed the stream).
4949fn run(socket: &PathBuf) -> std::io::Result<()> {
5050 let stream = UnixStream::connect(socket)?;