diff --git a/.github/workflows/aatxe-council-selftest.yml b/.github/workflows/aatxe-council-selftest.yml index 24a1824..a83ef43 100644 --- a/.github/workflows/aatxe-council-selftest.yml +++ b/.github/workflows/aatxe-council-selftest.yml @@ -17,18 +17,18 @@ # so the council can be exercised without an open PR — useful for # smoke-testing the pipeline in isolation (or under `act`). # -# 3. Real LLM, real PR diff (workflow_dispatch with `mode=real` and -# the right secret). Requires either `KIMI_API_KEY` (pi-proxy -# backend) or Claude Code auth on the runner (claude-code backend). -# Posts the sticky `` comment when triggered -# on a real PR. +# 3. Real LLM, real PR diff (auto on `pull_request` when a backend +# secret is provisioned; also workflow_dispatch with `mode=real`). +# Requires either `ANTHROPIC_API_KEY` (claude-code backend, +# preferred — it produced the published 0.857 critical-F1 +# baseline) or `KIMI_API_KEY` (pi-proxy backend). Posts the sticky +# `` comment on the PR. # -# Default `pull_request` triggers mode 1 only — mode 3 is gated behind -# `workflow_dispatch` so the PR queue never blocks on real-LLM cost or -# token availability. Once `KIMI_API_KEY` is provisioned as a repo -# secret + a `pi` install step is wired up, this workflow can be -# extended to auto-promote `pull_request` runs to mode 3 when the -# secret is present. +# `pull_request` runs auto-promote to mode 3 when a backend secret is +# present on the repo (`ANTHROPIC_API_KEY` → claude-code, else +# `KIMI_API_KEY` → pi-proxy) — that's the real-PR dogfooding loop. With +# no secret provisioned (and always on fork PRs, which never see +# secrets) they fall back to mode 1, free and deterministic. name: aatxe-council-selftest @@ -66,7 +66,9 @@ permissions: jobs: selftest: runs-on: ubuntu-latest - timeout-minutes: 10 + # 20 (not 10): real-LLM runs add minutes of council latency on top + # of the cached cargo build. Stub runs still finish well under 10. + timeout-minutes: 20 steps: - name: Checkout uses: actions/checkout@v4 @@ -85,6 +87,7 @@ jobs: env: INPUT_MODE: ${{ github.event.inputs.mode }} INPUT_PR: ${{ github.event.inputs.pr-number }} + INPUT_BACKEND: ${{ github.event.inputs.backend }} PR_NUM: ${{ github.event.pull_request.number }} # Don't expand the secret into the shell — just check # presence by length-of-non-empty. GH masks the value in logs @@ -92,13 +95,18 @@ jobs: HAS_KIMI: ${{ secrets.KIMI_API_KEY != '' }} HAS_ANTHROPIC: ${{ secrets.ANTHROPIC_API_KEY != '' }} run: | - # Default to `stub` for pull_request events; honor the input - # for workflow_dispatch. `real` falls back to `stub` when the - # required backend secret isn't present so the job still runs - # green instead of erroring on a missing key. + # Honor the explicit input for workflow_dispatch. For + # pull_request events, auto-promote to `real` when a backend + # secret is present (the real-PR dogfooding loop); fall back + # to `stub` otherwise — fork PRs never see secrets, so they + # always stay on the free deterministic path. mode="${INPUT_MODE:-stub}" if [ "${{ github.event_name }}" = "pull_request" ]; then - mode="stub" + if [ "$HAS_ANTHROPIC" = "true" ] || [ "$HAS_KIMI" = "true" ]; then + mode="real" + else + mode="stub" + fi fi pr="${INPUT_PR:-$PR_NUM}" @@ -111,9 +119,22 @@ jobs: mode="fixture" fi - echo "mode=$mode" >> "$GITHUB_OUTPUT" - echo "pr=$pr" >> "$GITHUB_OUTPUT" - echo " → resolved: mode=$mode pr=$pr" + # Backend: an explicit dispatch input wins; otherwise prefer + # claude-code when ANTHROPIC_API_KEY is set (it produced the + # published 0.857 critical-F1 baseline), else pi-proxy. + backend="$INPUT_BACKEND" + if [ -z "$backend" ]; then + if [ "$HAS_ANTHROPIC" = "true" ]; then + backend="claude-code" + else + backend="pi-proxy" + fi + fi + + echo "mode=$mode" >> "$GITHUB_OUTPUT" + echo "pr=$pr" >> "$GITHUB_OUTPUT" + echo "backend=$backend" >> "$GITHUB_OUTPUT" + echo " → resolved: mode=$mode pr=$pr backend=$backend" - name: Run council (stub LLM, real PR diff) if: steps.cfg.outputs.mode == 'stub' && steps.cfg.outputs.pr != '' @@ -140,6 +161,14 @@ jobs: --markdown /tmp/council/selftest.md \ --out /tmp/council/selftest.json + # The claude-code backend shells out to the `claude` binary + # (`claude --print --output-format json`), authenticated headlessly + # via ANTHROPIC_API_KEY. Installed only when a real claude-code run + # is about to happen — stub/fixture paths stay free of it. + - name: Install claude CLI (claude-code backend) + if: steps.cfg.outputs.mode == 'real' && steps.cfg.outputs.backend == 'claude-code' + run: npm install -g @anthropic-ai/claude-code + - name: Run council (real LLM, real PR diff, post comment) if: steps.cfg.outputs.mode == 'real' && steps.cfg.outputs.pr != '' env: @@ -152,7 +181,7 @@ jobs: mkdir -p /tmp/council ./target/release/aatxe council \ --pr ${{ steps.cfg.outputs.pr }} \ - --backend ${{ github.event.inputs.backend || 'pi-proxy' }} \ + --backend ${{ steps.cfg.outputs.backend }} \ --confidence-floor 0.55 \ --markdown /tmp/council/selftest.md \ --out /tmp/council/selftest.json \ diff --git a/.gitignore b/.gitignore index a190dfd..2175de1 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,8 @@ examples/**/*.tsbuildinfo # Local dev artefacts (calibration sweeps, smoke runs, logs) tmp/calibrate/ tmp/smoke/ +tmp/m2/ +tmp/perf-vs/ tmp/*.log tmp/aatxe-go-runner diff --git a/Cargo.toml b/Cargo.toml index d51e00a..d509fa9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,12 @@ regex = "1.10" walkdir = "2.5" time = { version = "0.3", features = ["formatting", "parsing", "macros", "serde"] } +[workspace.lints.clippy] +await_holding_lock = "deny" +await_holding_refcell_ref = "deny" +undocumented_unsafe_blocks = "deny" +missing_safety_doc = "deny" + [profile.release] lto = "thin" codegen-units = 1 diff --git a/Makefile b/Makefile index 2e23a8f..f316aed 100644 --- a/Makefile +++ b/Makefile @@ -642,6 +642,11 @@ install: build-rust ## Install the aatxe binary to ~/.cargo/bin $(call say,install) $(CARGO) install --path crates/aatxe --locked +.PHONY: install-hooks +install-hooks: ## Install the Git pre-commit hook (cargo fmt/clippy + TS tsc) + $(call say,install-hooks) + install -m 755 scripts/pre-commit.sh .git/hooks/pre-commit + .PHONY: clean clean: ## Remove cargo target/, sdk/ts/dist, sdk/ts/node_modules, examples node_modules, tmp/ $(call say,clean) diff --git a/crates/aatxe-ast/Cargo.toml b/crates/aatxe-ast/Cargo.toml index 49b0766..ccc3bfd 100644 --- a/crates/aatxe-ast/Cargo.toml +++ b/crates/aatxe-ast/Cargo.toml @@ -7,6 +7,8 @@ license.workspace = true repository.workspace = true description = "AST-based scope inspection (tree-sitter for TS/Go/Rust) so the aatxe council reasons about symbol-level scope, not flat text. Pure: no IO." +[lints] +workspace = true [lib] path = "src/lib.rs" diff --git a/crates/aatxe-core/Cargo.toml b/crates/aatxe-core/Cargo.toml index 9d1f365..a2592ef 100644 --- a/crates/aatxe-core/Cargo.toml +++ b/crates/aatxe-core/Cargo.toml @@ -12,6 +12,8 @@ readme = "README.md" keywords = ["benchmark", "performance", "regression", "statistics", "ci"] categories = ["development-tools::profiling", "development-tools::testing"] +[lints] +workspace = true [lib] path = "src/lib.rs" diff --git a/crates/aatxe-council/Cargo.toml b/crates/aatxe-council/Cargo.toml index 1e15162..a8c8139 100644 --- a/crates/aatxe-council/Cargo.toml +++ b/crates/aatxe-council/Cargo.toml @@ -7,6 +7,8 @@ license.workspace = true repository.workspace = true description = "Pure orchestration for the aatxe agent council: a single-layer mixture-of-agents PR reviewer with a dedicated judge" +[lints] +workspace = true [dependencies] aatxe-core = { path = "../aatxe-core" } serde = { workspace = true } diff --git a/crates/aatxe-evals/Cargo.toml b/crates/aatxe-evals/Cargo.toml index 4eb2dae..620c87b 100644 --- a/crates/aatxe-evals/Cargo.toml +++ b/crates/aatxe-evals/Cargo.toml @@ -7,6 +7,8 @@ license.workspace = true repository.workspace = true description = "Eval harness for aatxe — scores the council pipeline against labeled PR diffs and the stats engine against synthetic ground-truth benchmark pairs." +[lints] +workspace = true [lib] path = "src/lib.rs" diff --git a/crates/aatxe-learn/Cargo.toml b/crates/aatxe-learn/Cargo.toml index 82a40f6..3715e71 100644 --- a/crates/aatxe-learn/Cargo.toml +++ b/crates/aatxe-learn/Cargo.toml @@ -7,6 +7,8 @@ license.workspace = true repository.workspace = true description = "Self-healing learning corpus for the aatxe council — distills the best human feedback on prior PRs into project-specific guidance the council reads from a GitHub Actions artifact" +[lints] +workspace = true [dependencies] aatxe-council = { path = "../aatxe-council" } serde = { workspace = true } diff --git a/crates/aatxe/Cargo.toml b/crates/aatxe/Cargo.toml index 7dcd021..e1c3926 100644 --- a/crates/aatxe/Cargo.toml +++ b/crates/aatxe/Cargo.toml @@ -7,6 +7,8 @@ license.workspace = true repository.workspace = true description = "Statistical microbenchmark + regression-gate CLI for TypeScript, Go, and Rust" +[lints] +workspace = true [[bin]] name = "aatxe" path = "src/main.rs" diff --git a/examples/big-diff-bench/Cargo.toml b/examples/big-diff-bench/Cargo.toml index 131b6d6..a643f87 100644 --- a/examples/big-diff-bench/Cargo.toml +++ b/examples/big-diff-bench/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" publish = false +[lints] +workspace = true [[bin]] name = "aatxe-big-diff-bench" path = "src/main.rs" diff --git a/examples/council-bench/Cargo.toml b/examples/council-bench/Cargo.toml index b159059..c2ca0a6 100644 --- a/examples/council-bench/Cargo.toml +++ b/examples/council-bench/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" publish = false +[lints] +workspace = true [[bin]] name = "aatxe-council-bench" path = "src/main.rs" diff --git a/examples/rust-example/Cargo.toml b/examples/rust-example/Cargo.toml index 3b4700e..fedfc83 100644 --- a/examples/rust-example/Cargo.toml +++ b/examples/rust-example/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" publish = false +[lints] +workspace = true [[bin]] name = "aatxe-rust-runner" path = "src/main.rs" diff --git a/scripts/pre-commit.sh b/scripts/pre-commit.sh new file mode 100755 index 0000000..b147788 --- /dev/null +++ b/scripts/pre-commit.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Aatxe pre-commit hook. +# +# Installed via `make install-hooks`. Runs only on files staged for commit. +# Fails fast on formatting, clippy, or TS type errors so `origin/master` stays +# green without a separate CI round-trip. + +set -euo pipefail + +cd "$(git rev-parse --show-toplevel)" + +staged=$(git diff --cached --name-only --diff-filter=ACM) +rust_staged=$(echo "$staged" | grep '\.rs$' || true) +ts_staged=$(echo "$staged" | grep '^sdk/ts/.*\.ts$' || true) + +if [ -n "$rust_staged" ]; then + printf '\033[1;34m▶ cargo fmt --check\033[0m\n' + cargo fmt --all -- --check + + printf '\033[1;34m▶ cargo clippy\033[0m\n' + cargo clippy --workspace --all-targets -- -D warnings +fi + +if [ -n "$ts_staged" ]; then + printf '\033[1;34m▶ tsc --noEmit\033[0m\n' + cd sdk/ts + if [ ! -d node_modules ]; then + npm install --silent + fi + npx tsc --noEmit -p tsconfig.json +fi + +printf '\033[1;32m✅ pre-commit checks passed\033[0m\n' diff --git a/sdk/rust/Cargo.toml b/sdk/rust/Cargo.toml index 9a2468f..0f1837f 100644 --- a/sdk/rust/Cargo.toml +++ b/sdk/rust/Cargo.toml @@ -12,6 +12,8 @@ readme = "README.md" keywords = ["benchmark", "microbenchmark", "performance", "regression", "ci"] categories = ["development-tools::profiling", "development-tools::testing"] +[lints] +workspace = true [lib] path = "src/lib.rs"