Skip to content

chore: add a development harness for agents working on Tsim - #281

Merged
rafaelha merged 12 commits into
mainfrom
chore/dev-harness
Nov 10, 2026
Merged

rafaelha merged 12 commits into
mainfrom
chore/dev-harness

Conversation

@rafaelha

@rafaelha rafaelha commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

A harness for agents that work on Tsim's code, built from what slowed down the last 87 agent sessions on this repository.

What past sessions showed

  • At least 6 out-of-memory incidents, each of which killed other sessions; limits stated in prompts did not hold.
  • About 21 h of tool time in sleep and polling loops waiting on jobs.
  • About 540 one-off timing and comparison scripts, with repeated corrections to how they measured (threads, postselection, baselines, cold numba cache).
  • 40 commands killed their own shell with pkill -f.
  • test_the_calling_thread_keeps_parallel_kernels_after_run_tasks failed in 49 runs and was deselected by hand 36 times.

What this adds

tsim-dev CLI (dev/, installed by uv sync as an editable path dependency of the dev group; not part of the wheel). Output is short, details go to .tsim-dev/, and exit codes mean 0 pass, 1 the code failed or got slower, 2 the harness could not run, 3 still running.

Command What it does
check pre-commit on the changed files, then the full suite
test --changed tests of the changed files
bench --ref origin/main interleaved A/B in fresh processes, one pinned thread, warm numba cache; "slower" only when every round agrees; circuits from tsim-bench, postselected as their paper does (--no-postselect to turn off), --suite quick/full/hard/corpus, --max-width K --width-of tsim/clifft
bench --side .:threads=10 --side .:threads=1 the same A/B between sides with their own threads or strategy; a side is ., a revision, a checkout or a Python executable
bench --width --ref origin/main active widths only, compiled and not sampled; rows print as circuits finish on both sides (100 of 190 in 9 s), --timeout is the deadline for the whole run; any wider width exits 1. Shot benchmarks also compare the two sides' widths and check that a side's width does not change between runs
corpus fetch/list fetch the pinned tsim-bench, list circuits with arXiv id, width and mask size
inspect qubits, T count, active width and stats, without sampling
profile py-spy, split into compile and sampling, with kernel time charged to the calling function
run, job start/wait/stop any command under systemd memory, CPU and time caps

Benchmark circuits come from tsim-bench at a pinned commit (dev/tsim_dev/corpus.py), fetched into ~/.cache/tsim-dev on first use; --corpus-dir or TSIM_BENCH_DIR point at a local checkout. tsim-bench holds 190 circuits from 28 papers; for each one a circuits.toml records the arXiv paper (the original one for circuits that benchmark papers collected), the sampler, and the postselection the paper's protocol uses, with the reason (see its BENCHMARKS.md). Its python -m tsim_bench check verifies that no masked detector fires without noise (183 circuits of width ≤ 20 pass; 7 are too wide to sample). Suites: quick (8 circuits), full (32, widths up to 16, including tricycle CCZ factories up to width 16) and hard (7 tricycle/BLP factories, widths 16–26). The timing worker lives there too, so other backends can reuse it.

Developer skills in .claude/skills/ (.agents/skills links to it): shipping-tsim-changes, benchmarking-tsim, profiling-tsim, testing-tsim-correctness. They are not under skills/, so they are not published to users.

.claude/settings.json: a PreToolUse hook that blocks pkill -f and background Python without a memory cap; read-only git/gh and tsim-dev are allowed; push, PR creation and merge ask.

AGENTS.md: points to the CLI and the skills; rules for heavy jobs, performance and pull requests updated.

Test fix: the kernels test used a 4,096-element prange, which numba's TBB layer runs on one thread. It now uses 2^22 elements.

Verification

  • uv run tsim-dev check: lint passes, 2606 tests pass in 82 s (on a TBB host, where the kernels test used to fail).
  • Null A/B, this branch against origin/main (identical src/), quick suite with postselection, one thread, 5 rounds, AMD Ryzen 9 9900X 12-Core Processor. kept is the accepted fraction:
circuit                         k  shots/s   kept  compile   shots/s vs ref (min, max)      compile vs ref
inject_cultivate_d3_p0.001      4    29.5M  0.687     0.02   1.00x [1.00, 1.01] same    0.99x same
inject_cultivate_d5_p0.001      6    14.3M  0.144     0.13   1.00x [1.00, 1.00] same    1.00x same
end2end_d3_dsurface15_p0.001    4     925k  0.538     1.82   1.00x [0.99, 1.01] same    1.00x same
hook_inject_T_d15_pd5_p0.001    1     823k  0.667     2.06   1.00x [1.00, 1.00] same    1.01x same
distillation_color_d5_Z         3    12.8M 0.0797     0.02   1.00x [1.00, 1.00] same    0.98x same
coherent_d3_r3                  4    18.9M      -     0.01   1.00x [0.99, 1.00] same    0.98x same
surface_d7_r7                   0    11.3M      -     0.11   1.00x [1.00, 1.00] same    1.00x same
qv_q10                         10    10.6M      -     0.14   1.01x [1.00, 1.02] same    1.00x same
  • --suite full: all 32 circuits run without errors (one round, no reference).
  • --side .:threads=10 --side .:threads=1, 2 rounds: 7.8x on inject_cultivate_d5_p0.001, 3.3x on coherent_d5_r5, 2.1x on qv_q10.
  • bench --width --ref origin/main over the whole corpus: 190 circuits, 0 errors, all the same width as main; 180 rows within 64 s, all in 512 s (the 15-to-1 d25 circuit compiles for 240 s).
  • Jobs: a 200 MB cap, a 3 s time limit and job stop were each reported correctly.
  • The hook is covered by test/unit/test_dev_cli.py, with the verdict logic, the test mapping and the skill frontmatter.

Not included

  • GPU runs and comparisons with Clifft, SymFT and Magna stay in tsim-bench.
  • The fuzz harnesses and the exact oracle are still on local branches.
  • The hook and permission rules were tested as scripts, not in a live Claude Code session.
  • A review workflow (risk tiers, bot-comment gate) follows in a stacked pull request.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

☂️ Code Coverage

current status: ✅

Overall Coverage

Statements Covered Coverage Threshold Status
4897 4266 87% 0% 🟢

New Files

No new covered files...

Modified Files

No covered modified files...

updated for commit: 44541ca by action🐍

@rafaelha
rafaelha marked this pull request as ready for review November 10, 2026 15:02
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-10T15:05:44.194204Z e34f504 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Base automatically changed from docs/user-skills to main November 10, 2026 15:03

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e34f504e2b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/hooks/guard_bash.py Resolved
Comment thread dev/tsim_dev/bench.py Resolved
Comment thread dev/tsim_dev/bench.py Resolved
Comment thread dev/tsim_dev/checks.py Resolved
Comment thread dev/tsim_dev/checks.py Resolved
Comment thread dev/tsim_dev/jobs.py Resolved
rafaelha and others added 3 commits November 10, 2026 08:10
- `tsim-dev` CLI (dev/, installed by `uv sync`): `check`, `test --changed`,
  `bench --ref` (interleaved fresh-process A/B, pinned single thread, warm numba
  cache, round-by-round verdicts, tsim-bench corpus), `inspect`, `profile`
  (py-spy, split by phase, time charged to kernel callers), and `run`/`job`
  under systemd memory, CPU and time caps. Short output, logs in .tsim-dev/,
  exit codes 0 pass, 1 failed or slower, 2 harness error, 3 running.
- Benchmark registry of eight circuits in dev/circuits/.
- Developer skills in .claude/skills (also .agents/skills): shipping changes,
  benchmarking, profiling, correctness testing. Not published to users.
- .claude/settings.json: a Bash hook that blocks `pkill -f` and uncapped
  background Python; read-only git/gh allowed, push/PR/merge ask.
- AGENTS.md points to the CLI and skills; heavy-job and PR rules updated.
- Fix test_the_calling_thread_keeps_parallel_kernels_after_run_tasks on TBB
  hosts: TBB runs a 4096-element prange on one thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `tsim-dev bench` reads circuits from tsim-bench at a pinned commit
  (dev/tsim_dev/corpus.py), fetched into the shared cache on first use, or
  from a local checkout with `--corpus-dir` / TSIM_BENCH_DIR. Each circuit
  there records its arXiv paper and how that paper postselects.
- Postselection follows the recorded protocol by default; `--no-postselect`
  turns it off. Rows show the kept fraction.
- `--suite quick|full|hard|corpus` (suites defined in tsim-bench),
  `--max-width K --width-of tsim|clifft` to keep only circuits up to a
  recorded active width, and circuits named by tsim-bench id or file stem.
- `tsim-dev corpus fetch|list`. The timing worker is tsim-bench's.
- Drop dev/circuits/ and dev/tsim_dev/_worker.py. The .stim files there were
  never committed (`*.stim` is ignored), so a fresh clone could not run them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…im-dev bench

`--side REF[:threads=N,device=cpu|gpu,strategy=NAME]`, given twice, replaces
`--ref` when the sides differ in more than the checkout: 10 cores against 1,
the GPU executor against the CPU, or two strategies. REF is `.`, a git
revision, a checkout or a Python executable (an environment built elsewhere).
`device=cpu` hides CUDA devices; `device=gpu` fails unless the worker reports
Tsim's CUDA executor usable (tsim-bench 4cb8ca2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rafaelha and others added 9 commits November 10, 2026 08:22
- Fetching tsim-bench takes a lock and renames a complete export into place,
  so overlapping runs share one export and an interrupted one is redone; a
  pinned commit that is not pushed gives an error, not a tar traceback.
- `tsim-dev profile` accepts `--corpus-dir` (it crashed without it).
- A corpus circuit given by its path keeps its recorded postselection, and
  `--no-postselect` also applies to `.stim` paths outside the corpus.
- A/B runs compare the two sides' kept fractions: if they differ beyond
  binomial noise the rate is marked `kept-differs` and the run fails.
- `k` falls back to the recorded Tsim width (not the `--width-of` backend's);
  the JSON records each side's thread count; malformed manifests are harness
  errors (exit 2).
- Pin tsim-bench 88f3b72 (consistent end-to-end cultivation masks).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`npx skills add QuEraComputing/tsim` also searches `.claude/skills` and
`.agents/skills`, so it offered the four development skills next to the user
skill. Mark them `metadata.internal: true` (shown only with
INSTALL_INTERNAL_SKILLS=1), name the user skill in the documented install
command, and test the flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mpling

Each side compiles every selected circuit once (tsim-bench's worker in width
mode, 4 jobs in parallel by default) and prints its width next to the one
recorded in tsim-bench; the verdict per circuit is wider, narrower, same or
unknown, and any wider width exits 1. A circuit over Tsim's limit shows
`>=N`, the width from Tsim's refusal. A compile longer than --case-timeout
(300 s) is stopped and recorded as an error; the remaining circuits continue
in a new worker. The default suite is the whole corpus: 190 circuits, both
sides, in 519 s on a Ryzen 9 9900X, all the same as main.

Pin tsim-bench cd0acb6 (width mode; Tsim 1.1 worker).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop `--side ...:device=cpu|gpu`, the CUDA check and the GPU notes in the
benchmarking and profiling skills until Tsim has a GPU backend on main.
Sides take `threads` and `strategy`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With a reference, each row compares the two sides' active widths and ends in
`WIDER: k 6 -> 7` or `NARROWER: ...` when they differ; a wider width fails
the run. A width that changes between runs of one side is an error, since
it means compilation is not deterministic. `k` is the sampler's own
`active_width` (Tsim 1.1), without a fallback to the recorded width.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Benchmark only changes that could affect compile or sampling cost (the
  compile and sampling paths, dependencies, numba settings), and run
  `bench --width` for changes that could change the active width.
- A `slower` verdict is a finding, not a veto: confirm it, profile it, and
  remove it or explain it; a correctness fix may cost performance if stated.
- Distinguish new work (a new branch off main) from updating an existing
  pull request (keep its branch and commit on top).
- Infer acceptance criteria and ask only when they are unclear.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-commit

CI runs pyright over the whole repository, the pre-commit hook only over src
and test, so these were missed locally. The width worker stream now yields
lines and records how the worker ended in an `Exit` dataclass, and the
width-change message no longer sorts a set that may hold None. The hook now
also checks dev/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both sides now compile at once: the circuits are cut into batches of 8, small
files first, and `--jobs` workers take the next batch when free. A row prints
as soon as a circuit is done on both sides, with an [n/N] count, so the first
rows appear within a second and one slow compile delays only its batch.
`--timeout` is the deadline for the whole width run (default 1800 s; 600 s
per measurement for shot benchmarks as before): no batch starts after it,
and the circuits not compiled are reported as errors.

Whole corpus against main: 100 of 190 rows in 9 s, 180 in 64 s, all in 512 s
(the 15-to-1 d25 circuit alone compiles for 240 s); all widths the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The Bash hook also blocks `pkill --full`, the long form of `pkill -f`.
- Two sides that describe themselves the same way (two dirty checkouts on
  the same commit) get their checkout path in the label instead of being
  rejected or mixing their rounds; only truly identical sides are refused.
- `tsim-dev check` runs pre-commit under the memory and time caps too.
- `tsim-dev test --changed` keeps deleted files: a deleted source file runs
  the full suite, a deleted test file runs nothing.
- `tsim-dev run` exits 1 when the command fails, as documented, and prints
  the command's own exit code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rafaelha
rafaelha merged commit fdeba61 into main Nov 10, 2026
10 checks passed
@rafaelha
rafaelha deleted the chore/dev-harness branch November 10, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant