Skip to content

performance: a 2x workspace-status budget for a single-resource trace, met and checked in CI (#4032) - #4065

Merged
lex00 merged 1 commit into
mainfrom
feat/4032-trace-budget
Oct 17, 2026
Merged

lex00 merged 1 commit into
mainfrom
feat/4032-trace-budget

Conversation

@lex00

@lex00 lex00 commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Closes #4032

A single-resource trace now meets a budget of 2x workspace status, and CI enforces it. A trace of the most edited file went from about 19 s to about 2.8 s, with byte-identical output.

Baseline (before)

chant 0.124.27 at dd79deae731a5b8bd4e738ca06f83efdd4ad1b22, 2525 commits, full clone; Apple M5 Pro (18 cores), macOS, node v24.13.1; CLI from source (node --import tsx packages/core/src/cli/main.ts); median of 5 interleaved runs after one warm-up.

Command Median Ratio to status
workspace status prod --json 2.85 s 1
graph --intent packages/core/src/cli/main.ts --json (233 commits) 18.38 s 6.44x
graph --intent LICENSE --json (1 commit) 5.19 s 1.82x
graph --intent packages/core/src/workspace/intent.ts#intentGraph --json 5.85 s 2.05x

The dominant cost

The cli/main.ts trace started 3760 git processes, and 3474 were git merge-base --is-ancestor from SignerPositions.lookup in trust/rotation.ts: a binary search of about 12 ancestry checks per commit to find where it entered the base's first-parent line. recordProvenance made the same calls for every decision record. After the fix the trace starts 134 processes.

  • trust/rotation.ts: SignerPositions reads git rev-list --parents <base> once and answers each full commit id from an in-memory map; refs and short ids still take the old path.
  • intent.ts: descendants() reads a revision's children once instead of one rev-list --ancestry-path per decision or work window, in an in-memory entry keyed by repository and full commit id.
  • trust/ssh-commit.ts: the object format comes from the commit's own tree line instead of a git rev-parse per commit.
  • No cache on disk: nothing goes into the working tree, the git common dir or chant/lifecycle.

After

Target Before After
status 2.88 s 2.81 s / 3.01 s
cli/main.ts 19.88 s (6.91x) 2.74 s (0.98x) / 2.88 s (0.96x)
LICENSE 5.97 s (2.07x) 2.41 s (0.86x) / 2.54 s (0.85x)
intent.ts#intentGraph 5.95 s (2.07x) 2.49 s (0.88x) / 2.58 s (0.86x)

Under heavy machine load (load average about 30) the ratio stayed at 0.6x to 0.77x.

Budget and CI

The budget: a single-resource trace takes at most 2x workspace status <env> --json on the same machine, median of 5 against median of 5. It is documented in a new "How long a trace takes" section of cli/workspace-graph.mdx. scripts/bench-trace.ts times the three targets and fails over 2x; it refuses a shallow clone and a missing base revision, since the trace would skip signature checks. It runs at the end of the check job in .github/workflows/chant.yml on every pull request and push to main, with fetch-depth: 0, git remote set-head origin main, pipefail, and its result in the step summary. It should add about 1 to 1.5 minutes. At --budget 0.5 it printed OVER BUDGET and exited 1.

Output equality

Old and new code at --at dd79deae7, JSON and text: byte-identical for the three targets, trust/rotation.ts#SignerPositions, --intent --record ws-001, ws-078, ws-092, and workspace records --at … --json (745 KB). New ancestry-index.test.ts checks every commit's placement against the old git path, and every pair against git rev-list --ancestry-path, in a temp repo with merges.

Acceptance

  • Baseline numbers recorded, with the chant version, the repo revision and the runner.
  • The budget stated as a ratio (2x workspace status), and met (0.85x to 0.98x).
  • The dominant cost named, with the change that fixed it and the numbers before and after.
  • The benchmark runs in CI and fails over budget.

Checks

  • npx tsx scripts/typecheck.ts: no regressions (26 baselined)
  • npx tsx scripts/check-lexicons.ts: no untracked tier-1 failures
  • intent, trust, ancestry index, bench script, level-0 goldens: 250 passed with GITHUB_SHA, GITHUB_RUN_ID and CI set
  • Full vitest: only per-test budget timeouts under heavy machine load, all passing on rerun, plus the 3 known local op/factory.e2e.test.ts failures
  • lint-docs-sentences (no regressions), lint-prompts, check-docs-diataxis, ./scripts/build-docs.sh: pass
  • No output change; nothing written to the real repository's chant/lifecycle.

For #4023 and #4025: the largest remaining cost is one git cat-file commit per commit in the ssh attestor (86 for cli/main.ts, about 0.9 s); a git cat-file --batch read is the next step. policyAtBase builds SignerPositions twice per trace. The bench targets intent.ts#intentGraph by name.

…from one walk each, with a time budget checked in CI (#4032)

A trace of packages/core/src/cli/main.ts on chant's repo took 6.4 to 6.9
times as long as workspace status, about 19 s. Most of it was 3474
git merge-base --is-ancestor processes: SignerPositions placed each
commit on the base's first-parent line by a binary search of ancestry
checks. It now reads the parents of everything the base reaches once and
answers each full commit id from a map. descendants() reads the children
of the revision once instead of one rev-list --ancestry-path per decision,
and the ssh attestor takes the object format from the commit's tree line
instead of a rev-parse per commit. Output is unchanged.

scripts/bench-trace.ts times three traces against workspace status, median
of 5 interleaved runs, and fails over 2x. It runs in the check job on a
full clone.
@lex00
lex00 merged commit 509acfa into main Oct 17, 2026
11 checks passed
@lex00
lex00 deleted the feat/4032-trace-budget branch October 17, 2026 05:07
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.

performance: a time budget for the trace and overlap reads, measured on chant's own repo

1 participant