Benchmark fixtures had accumulated around individual call sites, leaving
the same repository shapes and command modes expressed several ways.
This change makes repository state the organizing concept: benchmark
groups select semantic `FixtureRecipe`s, share table-driven cases, and
retain separate fixtures only when a controlled contrast, destructive
precondition, or disproportionate setup cost requires one.
The real-repository list benchmarks now share one pinned
`rust-lang/rust` fixture with eight worktrees and fifty branches spread
across history. The list matrix keeps default, branch, warm, and cold
coverage without maintaining several “real” repository handles. Remove
and prune cases share the same case machinery, while the destructive
large-repository prune state remains separate.
The scheduled workflow now converts Criterion estimates directly with
`jq`, removing the one-off Python converter and its tests. The benchmark
guide records the canonical-fixture principle and the remaining
recipe-to-group mapping.
Tests: `cargo run -- hook pre-merge --yes` (4,551 tests); `cargo bench
--bench list large_repository -- --test`; `cargo test -p wt-perf`;
benchmark check, clippy, formatting, and diff checks.
> _This was written by Codex on behalf of max-sixty_.
## Summary
Today the nightly benchmark job uploads `target/criterion` as a 90-day
artifact and nothing else; results aren't queryable across runs. This
adds a JSONL append to a public gist so we can track regressions and
trends over time.
## Changes
`.github/scripts/criterion-to-jsonl.py` — walks
`target/criterion/**/new/estimates.json` and emits one JSON line per
benchmark group (timestamp, commit SHA, mean_ns, stddev_ns). Errors out
if no estimates are found, so a broken bench step fails the workflow
instead of silently skipping.
`.github/workflows/nightly-benchmark.yaml` — new step clones the gist
using the existing `WORKTRUNK_BOT_TOKEN` (already has `gist` scope),
appends new rows to `results.jsonl`, commits, and pushes. The 90-day
artifact upload is unchanged — it remains the per-run raw data; the gist
becomes the long-term time series.
## Gist
https://gist.github.com/worktrunk-bot/19bb23cb9658722abfe69479d0a4f9bf
Created locally as `worktrunk-bot`, contains a placeholder line that
will sit alongside real rows once the next nightly fires (or
`workflow_dispatch` triggers it manually).
## Test plan
- [ ] `actionlint` and `pre-commit` clean (verified locally)
- [ ] Smoke-tested the Python script against synthetic Criterion data —
produces correct JSONL, errors on empty tree
- [ ] Trigger `workflow_dispatch` post-merge to verify the first real
append lands in the gist
> _This was written by Claude Code on behalf of @max-sixty_
Co-authored-by: Claude <noreply@anthropic.com>
The nightly survey script is now shipped upstream in tend's plugin. The
local copy at `.github/scripts/todays-survey-files.sh` was nearly
identical — only the default file extensions differed. With the upstream
script now surveying all tracked files (no extension filter), there's no
need for a local override.
Depends on max-sixty/tend#200
> _This was written by Claude Code on behalf of @max-sixty_
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Replaces 7 hand-written Claude CI workflow files with 6 generated by
`uvx tend init` from `.config/tend.toml`
- Consolidates 5 project-specific skills (`review-pr`, `triage-issue`,
`fix-ci`, `nightly-cleaner`, `running-in-ci`) into a single
`running-tend` overlay skill that extends the generic skills now in the
`tend` repo
- Moves `review-reviewers` skill to the `tend` repo (it's not
project-specific)
- Adds `.github/actions/claude-setup` composite action for project setup
steps
- Adds `.github/tend-security-model.md` documenting worktrunk-specific
security configuration
- Deletes `claude-hourly-review-reviewers.yaml` (moved to tend repo)
The generated workflows are produced by the
[tend](https://github.com/max-sixty/tend) generator, which handles
triggers, conditions, engagement verification, checkout, and the
composite action invocation. Project-specific config (bot name, secrets,
setup steps, watched workflows, cron schedules) lives in
`.config/tend.toml`.
Net: -1585 lines. The deleted code is now in the `tend` repo as reusable
infrastructure.
> _This was written by Claude Code on behalf of @max-sixty_
---------
Co-authored-by: Claude <noreply@anthropic.com>
The nightly cleaner only reviews commits from the past 24 hours — code
that hasn't changed recently never gets a fresh look. This adds a
rolling survey phase that reviews ~10 existing files per night, covering
the full repo every 28 days.
Each file is deterministically assigned to a day via `cksum(path) mod
28`. A new script (`.github/scripts/todays-survey-files.sh`) outputs
today's slice. The cleaner reads each file and looks for bugs, stale
docs, dead code, simplification opportunities, missing tests, and
guideline drift.
Also fixes the early-exit behavior: quiet days (no commits in 24h) now
skip only the diff review, and still run issue management and the
survey.
> _This was written by Claude Code on behalf of @max-sixty_
---------
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Remove `CLICOLOR_FORCE: 1` from all 7 Claude workflow env blocks
- Simplify `list-recent-runs.sh` (no longer needs to unset
`CLICOLOR_FORCE`)
`CLICOLOR_FORCE=1` overrides `NO_COLOR` per the clicolors spec, causing
`gh` to emit ANSI color codes in JSON output even in non-TTY contexts.
This breaks `jq` parsing in 5 of 9 review sessions (2-5 wasted tool
calls each). `CARGO_TERM_COLOR: always` already handles cargo output
coloring.
Fixes#1346
## Test plan
- [ ] Verify `gh api ... | jq` works without ANSI codes in next review
session
- [ ] Verify `cargo test` still shows colored output (controlled by
`CARGO_TERM_COLOR`)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Unsets `CLICOLOR_FORCE` in `list-recent-runs.sh` before setting
`NO_COLOR=1`
- `CLICOLOR_FORCE=1` (set by the Claude Code environment) overrides
`NO_COLOR` per the [clicolors spec](https://bixense.com/clicolors/),
causing `gh` to emit ANSI codes in JSON output that break `jq` parsing
Fixes#1324
## Context
PR #1317 added `NO_COLOR=1` to fix ANSI codes in `gh` output, but the
Claude Code environment also sets `CLICOLOR_FORCE=1` which takes
precedence. Every hourly review run fails at startup because
`list-recent-runs.sh` can't parse the colorized JSON.
## Test plan
- [ ] Hourly review runs should no longer fail with `jq: parse error:
Invalid numeric literal`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Sets `NO_COLOR=1` in `list-recent-runs.sh` to prevent `gh` from
emitting ANSI escape sequences
- Fixes `jq: parse error: Invalid numeric literal` failures in the
hourly review workflow
Fixes#1315.
## Evidence
Run
[22777859584](https://github.com/max-sixty/worktrunk/actions/runs/22777859584)
hit this bug — `gh run list --json` output contained ANSI color codes
(`[1;38m[`, `[32m"skipped"`) that `jq` cannot parse. `gh` respects the
[`NO_COLOR`](https://no-color.org/) environment variable, so setting it
is cleaner than stripping codes after the fact.
## Test plan
- [ ] Script produces valid JSON output in CI environment
---------
Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Extracts run-fetching logic into a reusable
`.github/scripts/list-recent-runs.sh` script
- Dynamically discovers `claude-*` workflows instead of maintaining a
hardcoded list
- Uses a two-step time filter: fetches runs started in the past 3 hours,
then keeps only completed runs whose `updatedAt` is within the past hour
(see #1301 for why)
- Simplifies the hourly review workflow prompt to just reference the
script
Fixes#1301
## Test plan
- [ ] Next hourly review run should skip any in-progress jobs without
polling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>