---
name: push
description: The authoritative pre-push procedure for the cgg repo. Run before every `git push` (and before opening a release). It defines how to refresh every autogenerated README component (mermaid graphs, self-stats, benchmark table, language count/table), how to keep the Limitations and Potential-future-improvements sections honest against the code, the rule that EVERY factual claim in README.md must be neurosymbolically validated — proven by a deterministic symbolic check (cgg itself, rg/grep, cargo test, docs-check.py, `cgg --help`), not asserted from memory — that every CHANGELOG entry must carry a measured `### Performance` block (scripts/perf-compare.sh), that silent/transient functionality bugs get bold prominent disclosure, and how version numbers are incremented (Cargo.toml workspace + internal pins, Cargo.lock, CHANGELOG.md). Trigger when about to push, cut a release, or bump a version in the cgg repo.
---

# push — the cgg pre-push contract

A push is a publish. The README is cgg's public contract: agents read
it, users trust its numbers, and the autogenerated blocks are assumed
to match the binary that produced them. This skill is the gate that runs
**before `git push`** so nothing stale, unproven, or mis-versioned
leaves the repo.

It composes with two existing pieces and adds the parts they don't cover:

- `.githooks/pre-commit` regenerates the two embedded mermaid blocks and
  the self-stats line, runs the test suite, and runs `docs-check.py`.
- The `docs-sync` skill is the per-edit drift checklist for prose/tables.

This skill is the **whole-README, whole-repo final pass** plus the
**versioning procedure**. Do all of it before pushing.

## 0. Preconditions

```bash
cargo build --release -p cgg     # the binary every regen below uses
```

If the build fails, stop — every downstream number would be wrong.

## 1. Refresh every autogenerated README component

The README contains four machine-generated regions. Never hand-edit
them; regenerate from the freshly built binary so they describe the code
you are about to push.

| Region | Marker / location | Regenerate with |
| --- | --- | --- |
| cgg-walk graph | `<!-- cgg:begin:walk -->` … `:end:walk` | `cgg ./crates/cgg-walk -t mermaid -o walk.mmd` then `scripts/update-readme-graphs.py walk walk.mmd …` |
| cgg-lang graph | `<!-- cgg:begin:lang -->` … `:end:lang` | `cgg ./crates/cgg-lang/src/{detect,parser,lib}.rs -t mermaid -o lang.mmd` then `scripts/update-readme-graphs.py … lang lang.mmd` |
| self-analysis stats | `<!-- cgg:begin:self-stats -->` … `:end:self-stats` | `cgg ./crates --filter 'cgg::analyze_in_pool$' -n 1 …` piped to `scripts/update-readme-stats.py` |
| benchmark table | the per-language table under "## Self-analysis"/benchmark | `scripts/benchmark.sh` (clones repos; multi-minute) then `scripts/update-readme-stats.sh` / `scripts/patch-readme-stats.py` |

The first three are exactly what `.githooks/pre-commit` runs — so a
normal `git commit` already refreshes them. **Verify, don't assume:** the
graphs and self-stats can be stale on disk if a prior commit used
`--no-verify` or `CGG_SKIP_PRECOMMIT=1`. Run the regen and confirm
`git diff README.md` shows either nothing or only the expected
regenerated lines.

The **benchmark table is NOT touched by the hook.** If your change could
move per-language numbers (new language, resolver change, stdlib tuning,
perf work), re-run `scripts/benchmark.sh` and patch the table. If it
can't, say so explicitly in the push notes rather than shipping numbers
you didn't re-measure — silent staleness reads as "freshly measured."

## 2. Keep Limitations and Potential-future-improvements honest

These two prose sections rot silently because no generator owns them.
Before every push, re-derive each bullet from the code:

- **Every "future improvement" must still be unimplemented.** For each
  bullet, find the symbolic proof that it has NOT happened (or has only
  partially happened). If the code now does it, move it out of the
  list — to the feature table, the CHANGELOG, or delete it. Partial
  progress gets rewritten to name exactly what remains (e.g. "8 stdlib
  lists remain: csharp, fortran, java, …"), never left as a blanket
  claim.
- **Every "limitation" must still be true.** A limitation that the code
  has since fixed is a lie in the contract. Prove each one still holds
  (no watch mode → no `watch`/`notify` in `cli.rs`; Tcl/Hack absent →
  not in `plugins.rs`; macros not expanded → no preprocessor sim).

Validation patterns (these are the symbolic half of §3, applied to prose):

```bash
rg -n 'trait_impl_target' crates/cgg/src/main.rs   # is dispatch still Rust-only?
git log --oneline -- crates/cgg-core/src/stdlib/   # which stdlib lists got cleaned?
rg -niE 'watch|daemon|notify' crates/cgg/src/cli.rs # watch mode landed yet?
rg -n 'register\(' crates/cgg-lang/src/plugins.rs   # which languages exist?
```

## 3. Neurosymbolic validation of ALL README claims (mandatory gate)

**Rule: no claim ships unproven.** Before push, every factual assertion
in `README.md` must be validated *neurosymbolically* — the neural half
(reading the prose, deciding what it asserts) is paired with a
**symbolic** half (a deterministic check that proves it against the
actual code or tooling). An assertion you can only support "from
memory" or "it was true last release" is not validated; soften it,
remove it, or go prove it.

Symbolic oracles available in this repo — prefer them in this order:

1. **cgg itself** — the strongest oracle. Counts, edges, cross-file
   numbers, "resolves method dispatch", "finds cross-file edges":
   run cgg on a fixture/the repo and read the real numbers.
2. **`cgg --help`** — the source of truth for the CLI flag table. Every
   row in "## CLI" must exist in `--help`; this is enforced by
   `docs-check.py` and must pass.
3. **`cargo test --workspace`** — behavioral claims backed by tests
   (detection rules, resolver behavior, format output).
4. **`rg`/`grep` over `crates/`** — existence claims: a language is
   supported, a flag exists, an FFI form is detected, a resolver phase
   runs in a given order.
5. **`scripts/docs-check.py`** — the consistency invariants (plugin
   count == README "(N)" heading == language-table rows == benchmark
   `REPOS` count; every README flag exists in `--help`). **Must exit 0.**
6. **`scripts/benchmark.sh`** — the only oracle for the benchmark
   numbers. If you didn't run it, you didn't validate them.

Claim classes and their required proof:

| Claim in README | Symbolic proof required |
| --- | --- |
| "Supports N languages" / language table | `rg -c 'register\(' plugins.rs`, registry test, `docs-check.py` |
| Any CLI flag / default | appears in `cgg --help` |
| Counts in self-stats / benchmark | regenerated by the binary / `benchmark.sh` |
| "resolves X" / "links cross-file" / FFI | a cgg run that produces the edge, or a passing test |
| Pipeline phase order | matches `cgg::analyze_in_pool` in `crates/cgg/src/lib.rs` (NOT `main.rs` — that is a 98-line shim since the library split) |
| A limitation / future gap | the absence proof from §2 |

If a claim has no available oracle, it does not belong in the README as
a fact — rephrase it as clearly-marked intent or cut it.

## 4. Version increment policy

cgg is pre-1.0 and follows a loose SemVer (see `CHANGELOG.md` preamble:
the default graph only ever *grows*). Increment by the largest category
the change touches:

- **PATCH** (`0.2.0 → 0.2.1`) — bug fixes, doc-only changes, stdlib list
  tuning, internal refactors. No new user-visible surface.
- **MINOR** (`0.2.0 → 0.3.0`) — additive contract surface: a new
  language, a new flag, a new output format, a new resolver phase, new
  audit fields. This is the common case for feature work.
- **MAJOR** (`→ 1.0.0`) — reserved for the 1.0 stability commitment.
  Pre-1.0, behavior changes that would otherwise be "breaking" ship as
  MINOR **with a `### Compatibility / migration` note in the CHANGELOG**;
  the default graph must remain a superset of the prior release.

When you bump, change **all** of these in one commit, then validate:

1. `Cargo.toml` → `[workspace.package] version` (one line).
2. `Cargo.toml` → the five internal pins under `[workspace.dependencies]`
   (`cgg-core`/`cgg-lang`/`cgg-resolve`/`cgg-format`/`cgg-walk`). They
   must match the workspace version.
3. `Cargo.lock` → refresh by running `cargo build` (rewrites the six
   `cgg*` entries). Stage the result; the pre-commit hook does not.
4. `CHANGELOG.md` → add `## [x.y.z] - YYYY-MM-DD` (today's date) at the
   top, with `### Added` / `### Changed` / `### Fixed` / `### Compatibility`
   subsections as applicable. Match the existing entry's voice.
5. `CHANGELOG.md` → **a `### Performance` block is mandatory in every
   entry.** See §4a.

Symbolic check after bumping:

```bash
rg -n '0\.2\.0' Cargo.toml            # must return nothing for the old version
cargo build --release -p cgg          # Cargo.lock now consistent
cgg --version                         # prints the new version
```

## 4a. Performance is a release-note requirement

**Every CHANGELOG entry carries a `### Performance` block. No
exceptions, including for releases that "shouldn't" affect latency —
that claim is exactly the one worth checking.**

cgg's pitch is that it is fast enough to run before every edit. A
release that quietly regresses latency breaks that pitch silently, and
four releases shipped before this rule with no numbers at all.

Generate the numbers, never estimate them:

```bash
scripts/perf-compare.sh                 # vs the last `chore: release` commit
scripts/perf-compare.sh <ref> <runs>    # explicit baseline / sample count
```

It builds the baseline in a **separate git worktree**, so it works with
a dirty tree and never touches your working copy. Output is a markdown
table ready to paste.

The block must state, alongside the table:

- **Machine load at measurement time.** The script prints it. A loaded
  box changes the answer materially — the same two commits measured
  −4.1% at load 3.3 and −1.9% at load 36.
- **Whether the comparison is like-for-like.** If the new version
  enables work by default that the baseline lacked, the delta is the
  cost of new default behaviour, not overhead. Say which.
- **An explanation for every regression over ~5%** on a repo whose
  baseline exceeds 150 ms. Below that, jitter reads as several percent
  and the script flags it `⚠noise`.
- **Test-suite and pre-commit-hook timings**, since those are the
  latency contributors developers feel most often.

A regression you can explain is fine to ship. A regression you did not
measure is not.

## 4b. Functionality bugs get bold disclosure

**A bug that changed results silently is disclosed prominently, in full,
and near the top of its CHANGELOG entry — not buried in a list.**

The class that matters most is a bug producing a *plausible wrong
answer*. A crash announces itself. A graph that is quietly missing edges,
or that differs run to run, does not: every number published while it was
live was wrong without saying so, and nobody had cause to re-check.

Rules:

1. **Transient / nondeterministic bugs lead the `### Fixed` section**,
   with a blockquote callout above it so a scanner cannot miss it. Same
   input, different answer is the most serious class cgg has.
2. **Quantify the blast radius.** Not "fixed a resolver bug" — state
   what it did and how much: *"1,404 of 2,858 edges on phoenix"*,
   *"1460/1463/1466/1469 across ten runs"*. A reader needs to know
   whether their own results were affected.
3. **Say which releases were affected**, and say so explicitly if
   previously published numbers (README tables, benchmark rows) were
   wrong as a result.
4. **Never report a delta against a nondeterministic baseline as a
   finding.** Check the baseline is stable first; if it is not, report
   the range, not the difference. A single draw compared against a fixed
   value is not a measurement — it produced a spurious "−5 edges" in
   this project's own notes before being caught.
5. **State the mechanism**, so a reader can judge whether their codebase
   hits it — e.g. *"routine in C/C++, where many directories carry their
   own `common.h`"*.
6. **Add the regression test in the same commit**, and name it in the
   entry. A silent bug that had no test is two defects.

Being unflattering in a release note is cheap. A user discovering the
bug themselves, after trusting a number cgg gave them, is not.

## 5. Final push sequence

```bash
# 1. build + regen autogen (§1) + validate claims (§2,§3)
# 2. bump version if warranted (§4)
scripts/perf-compare.sh                # §4a — paste into the CHANGELOG
cargo test --workspace                 # green (also gated by the hook)
scripts/docs-check.py                  # exit 0 (also gated by the hook)
git add -A
git commit -m "…"                      # pre-commit hook re-regens graphs/stats, re-runs tests + docs-check
git push origin main
```

Let the pre-commit hook run — do not `--no-verify` a push. If the hook
restages README.md, that is the autogen blocks correcting themselves;
inspect the diff, it should be only generated regions.

## What this skill does NOT do

- It does not replace `docs-sync` (the per-edit drift checklist) — it
  assumes that ran and does the final whole-repo sweep.
- It does not auto-bump the version — versioning is a judgment call
  (§4); decide the category, then apply it everywhere at once.
- It does not invent benchmark numbers — unmeasured numbers are not
  shipped; either re-run `benchmark.sh` or state that they were not
  re-measured.
