Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/daemon.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# The daemon gate as its own CI job, per the pi/-and-hooks/ pattern. It runs on EVERY
# pull request and exits early when daemon/ is untouched — deliberately, because this
# check is a required status: a job that only triggers on daemon/** paths never reports
# on a Swift-only PR, and a required check that never reports blocks the merge forever.
# Always report, spend nothing when there is nothing to gate.
name: daemon

on:
pull_request:

jobs:
gate:
name: fmt · clippy · build · test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: did daemon/ change?
id: changed
run: |
if git diff --name-only "origin/${{ github.base_ref }}...HEAD" \
| grep -qE '^(daemon/|\.github/workflows/daemon\.yml)'; then
echo "run=true" >> "$GITHUB_OUTPUT"
else
echo "run=false" >> "$GITHUB_OUTPUT"
echo "daemon/ untouched — gate satisfied by construction"
fi
- if: steps.changed.outputs.run == 'true'
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- if: steps.changed.outputs.run == 'true'
run: bash daemon/test.sh
13 changes: 13 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,19 @@ the Swift gate must not.
bash .claude/skills/pi-extensions/scripts/test.sh
```

**If you touched `daemon/`, run its gate:**

```
bash daemon/test.sh
```

`daemon/` is the bench daemon (`benchd`) — a self-contained Rust cargo workspace, the
same carve-out as `pi/` and `hooks/`: its gate needs only the Rust toolchain, its CI job
triggers only on `daemon/**`, and the Swift gate never learns about it. Read
`daemon/direction.md` before working there; the milestone sequence is
`docs/future-planning/bench-roadmap.md`, and M0 (skeleton and suite isolation) is the
part that exists.

**If you touched `hooks/`, run its gate:**

```
Expand Down
1 change: 1 addition & 0 deletions daemon/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
42 changes: 42 additions & 0 deletions daemon/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# AGENTS.md — daemon/

The bench daemon: a self-contained cargo workspace, the `pi/`-style carve-out. Read
`direction.md` first; the milestone sequence and invariants are
`../docs/future-planning/bench-roadmap.md`. Vocabulary stays canonical in `../CONTEXT.md`.

## Gate

```
bash daemon/test.sh
```

fmt-check, clippy `-D warnings`, build, then tests — **build before test is load-bearing**:
the conformance suite runs the real `benchd` binary as a subprocess and locates it beside
its own. Run this gate when `daemon/` changed; the Swift gate at the repo root neither
knows nor needs the Rust toolchain, in either direction.

## Layout

- `crates/bench-wire` — every wire type and shared resolution rule, spelled once. If
`benchd` and `bench` could disagree about a value, its rule belongs here.
- `crates/benchd` — the daemon. Foreground, one unix socket, serial request handling.
- `crates/bench` — the CLI, the one agent-facing surface (and the future skill surface).
- There is deliberately **no root `Cargo.toml`** in the repo: `cargo` at the repo root
fails loudly instead of half-working.

## Rules

- **Bench-visible means logged.** A mutation appends its event before the response that
reports it. A new capability is new event kinds + new verbs over the same socket —
never a second channel (no files-as-IPC, no extra sockets, no notification side paths).
- **Tests never touch the operator's estate.** Claim a disposable `HOME` (or `BENCH_DIR`)
under the OS tempdir — the OS tempdir specifically: unix socket paths cap near 104
bytes and long scratch paths fail at bind. Include the negative control: assert the
shared root shape was never created (`hooks/test.sh`'s pattern).
- **Bounded children.** A test that spawns a daemon owns exactly that pid, kills it in a
Drop guard, and waits. Never kill by pattern (repo root AGENTS.md; #291 is why).
- **Exit codes are the contract**: 0 ok · 2 no daemon · 3 refused · 4 daemon failed.
A refusal names the rule it applied and the route to use instead.
- **Wire changes ride with their conformance test** in `crates/bench/tests/` — real
binaries, both directions, every status case, same as `SpoolWireConformanceTests`.
- Conventional commits, written as a human — no AI attribution (repo rule).
172 changes: 172 additions & 0 deletions daemon/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions daemon/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# The bench daemon workspace. Deliberately NOT reachable from a repo-root Cargo.toml —
# `cargo` at the repo root should fail loudly, not half-work (bench-roadmap.md, M0).
[workspace]
resolver = "2"
members = ["crates/bench-wire", "crates/benchd", "crates/bench"]

[workspace.package]
version = "0.0.1"
edition = "2024"

[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
time = { version = "0.3", features = ["formatting"] }
9 changes: 9 additions & 0 deletions daemon/crates/bench-wire/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "bench-wire"
version.workspace = true
edition.workspace = true
description = "Every bench wire type and shared resolution rule, spelled once."

[dependencies]
serde.workspace = true
serde_json.workspace = true
Loading
Loading