| doc-type | issue | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| issue-type | enhancement | ||||||||||||||||||
| status | planned | ||||||||||||||||||
| priority | p1 | ||||||||||||||||||
| github-issue | 1769 | ||||||||||||||||||
| spec-path | docs/issues/open/1769-refactor-pre-commit-checks-performance-and-verbosity.md | ||||||||||||||||||
| branch | 1769-refactor-pre-commit-checks-performance-and-verbosity | ||||||||||||||||||
| related-pr | |||||||||||||||||||
| last-updated-utc | 2026-05-13 11:20 | ||||||||||||||||||
| semantic-links |
|
Improve local commit-time feedback by making pre-commit output concise by default and reducing unnecessary runtime, while preserving strong quality guarantees through pre-push and CI.
Previous pre-commit flow (before this issue) in contrib/dev-tools/git/hooks/pre-commit.sh:
cargo machetelinter allcargo test --doc --workspacecargo test --tests --benches --examples --workspace --all-targets --all-features
Current pre-push flow in contrib/dev-tools/git/hooks/pre-push.sh already runs comprehensive validation and includes E2E. CI in .github/workflows/testing.yaml also runs E2E matrix jobs.
Key finding:
- E2E is not part of pre-commit today. The pre-commit pain is mainly verbosity and broad test scope for frequent local commits.
Automation policy constraint:
- We do not want to couple workflow automation exclusively to GitHub-native services (for example Dependabot) when defining core maintenance processes.
- The process should remain portable: executable in different CI/CD infrastructures and usable with different AI providers.
- GitHub ecosystem tools can still be used as optional integrations, but not as the only execution path.
- Add concise/verbose output modes to pre-commit with better failure summaries and log-path reporting.
- Measure current vs proposed pre-commit runtime and output quality.
- Define and document command ownership by tier (pre-commit, pre-push, CI).
- Adjust pre-commit step composition to optimize local cycle time without reducing merge safety.
- Removing comprehensive checks from pre-push/CI.
- E2E redesign.
- Changes unrelated to developer workflow/quality gates.
- Current commands stream full output, producing noisy terminal sessions.
- Failures can be hard to spot in long logs.
- High-volume output contributes to tooling output transport instability for agent execution.
- Pre-commit runs broad workspace tests on every commit.
- Heavy checks are duplicated in pre-push/CI.
- For docs/small changes, local wait time is disproportionate to change risk.
Multi-run timing comparison (2026-05-13, local):
Baseline profile (4 steps):
cargo machetelinter allcargo test --doc --workspacecargo test --tests --benches --examples --workspace --all-targets --all-features
| Run | Elapsed |
|---|---|
| 1 | 177s |
| 2 | 77s |
| 3 | 73s |
| Avg | 109s |
| Median | 77s |
Candidate profile A (3 steps):
cargo machetelinter allcargo test --doc --workspace
| Run | Elapsed |
|---|---|
| 1 | 57s |
| 2 | 58s |
| 3 | 57s |
| Avg | 57s |
| Median | 57s |
Result: candidate profile A reduces median local pre-commit latency from 77s to 57s (about 26% faster) while preserving dependency, lint, and doc-test coverage. Full tests remain enforced in pre-push and CI.
Output-size comparison (same profile, different output modes):
| Mode | Stdout Lines | Elapsed |
|---|---|---|
--format=text --verbosity=concise |
10 | 59s |
--format=text --verbosity=verbose |
235 | 56s |
--format=json |
26 | 58s |
- Pre-commit should optimize for fast, high-signal local feedback.
- Pre-push and CI should remain comprehensive and authoritative for merge readiness.
CLI contract:
- Add
--format=<text|json>where:--format=textis the default (human-friendly terminal output)--format=jsonemits a single JSON document to stdout
- Add
--verbosity=<concise|verbose>where:--verbosity=conciseis the default--verbosity=verbosestreams full command output
- Keep
--verboseas a compatibility alias for--verbosity=verbose. - Define precedence explicitly:
- when
--format=json, output remains structured JSON regardless of verbosity value - for
--format=text, verbosity controls concise vs full streaming output
- when
- Define argument conflict/error behavior explicitly:
- duplicate
--format/--verbosityflags: last value wins --verbosealias sets--verbosity=verbose- invalid values (for example
--format=xml): fail with exit code2and usage hint - unknown flags: fail with exit code
2and usage hint - output channel contract: structured output goes to stdout, diagnostics/errors to stderr
- duplicate
Modes matrix:
| Format | Verbosity | Behavior |
|---|---|---|
text |
concise (default) |
High-signal summary per step |
text |
verbose |
Full streaming command output |
json |
concise or verbose |
Single JSON document to stdout |
- Add
--formatand--verbosityflags to contrib/dev-tools/git/hooks/pre-commit.sh. - In concise mode, capture per-step logs and print only:
- step name, pass/fail, elapsed time
- log path and a short failure tail when a step fails
- Keep full streaming output in
--verbosity=verbosemode for--format=text. - In
--format=jsonmode, write a single JSON document to stdout (see examples below).
# Default behavior
./contrib/dev-tools/git/hooks/pre-commit.sh
# Explicit text + concise (equivalent to default)
./contrib/dev-tools/git/hooks/pre-commit.sh --format=text --verbosity=concise
# Text + verbose
./contrib/dev-tools/git/hooks/pre-commit.sh --format=text --verbosity=verbose
# Compatibility alias for verbose text output
./contrib/dev-tools/git/hooks/pre-commit.sh --format=text --verbose
# Structured output for agents/scripts
./contrib/dev-tools/git/hooks/pre-commit.sh --format=json./contrib/dev-tools/git/hooks/pre-commit.shRunning pre-commit checks...
[Step 1/3] Checking for unused dependencies (cargo machete) ... PASS (0s)
[Step 2/3] Running all linters ... PASS (7s)
[Step 3/3] Running documentation tests ... PASS (52s)
==========================================
SUCCESS: All pre-commit checks passed! (59s)
==========================================
./contrib/dev-tools/git/hooks/pre-commit.shRunning pre-commit checks...
[Step 1/3] Checking for unused dependencies (cargo machete) ... PASS (0s)
[Step 2/3] Running all linters ... FAIL (11s) log: /tmp/pre-commit-linter-all-20260513-083055.log
error[E0001]: unused variable `x` at src/lib.rs:42
error: aborting due to 1 previous error
(2 lines shown — full log: /tmp/pre-commit-linter-all-20260513-083055.log)
==========================================
FAILED: Pre-commit checks failed!
Fix the errors above before committing.
==========================================
./contrib/dev-tools/git/hooks/pre-commit.sh --format=json{
"schema_version": 1,
"status": "pass",
"exit_code": 0,
"elapsed_seconds": 59,
"steps": [
{
"name": "Checking for unused dependencies",
"command": "cargo machete",
"status": "pass",
"elapsed_seconds": 0
},
{
"name": "Running all linters",
"command": "linter all",
"status": "pass",
"elapsed_seconds": 7
},
{
"name": "Running documentation tests",
"command": "cargo test --doc --workspace",
"status": "pass",
"elapsed_seconds": 50
}
]
}./contrib/dev-tools/git/hooks/pre-commit.sh --format=json{
"schema_version": 1,
"status": "fail",
"exit_code": 1,
"elapsed_seconds": 11,
"failed_step": "Running all linters",
"steps": [
{
"name": "Checking for unused dependencies",
"command": "cargo machete",
"status": "pass",
"elapsed_seconds": 0
},
{
"name": "Running all linters",
"command": "linter all",
"status": "fail",
"elapsed_seconds": 11,
"log_path": "/tmp/pre-commit-linter-all-20260513-083055.log",
"failure_tail": [
"error[E0001]: unused variable `x` at src/lib.rs:42",
"error: aborting due to 1 previous error"
]
}
]
}- Measure current pre-commit runtime over at least 3 runs.
- Measure candidate profile runtime over at least 3 runs.
- Compare results and choose a profile with documented rationale.
Candidate profiles:
- Profile A (provisional until multi-run evidence is collected):
cargo machete+linter all+cargo test --doc --workspace. - Profile B: retain full tests but with concise default output.
Evaluation note:
- Because a real baseline run showed
cargo test --doc --workspaceas the slowest step, the final profile selection must be decided after the required multi-run timing table is completed.
- Document which checks are mandatory at each tier:
- pre-commit (fast local gate)
- pre-push (comprehensive developer gate)
- CI (merge authority)
- Keep E2E explicitly out of pre-commit and documented as pre-push/CI responsibility.
- Update .github/skills/dev/git-workflow/run-pre-commit-checks/SKILL.md with new behavior and flags.
- Update references in AGENTS.md and related skills if command expectations changed.
- Add troubleshooting notes for concise vs verbose mode.
Status values: TODO, IN_PROGRESS, BLOCKED, DONE.
| ID | Status | Task | Notes / Expected Output |
|---|---|---|---|
| T1 | DONE | Baseline current pre-commit stats | Runtime and output-size baseline collected. |
| T2 | DONE | Implement output mode refactor | Concise default + verbose opt-in implemented. |
| T3 | DONE | Select and apply runtime profile | Profile selected with measured trade-off rationale. |
| T4 | DONE | Update docs/skills | Workflow docs and skills aligned. |
| T5 | DONE | Validate gates and regression | linter all and relevant test checks pass. |
- Spec drafted in
docs/issues/drafts/ - Spec reviewed and approved by user/maintainer
- GitHub issue created and issue number added to this spec
- Implementation completed
- Reviewer validated acceptance criteria and updated checkboxes
- Committer verified spec progress is up to date before commit
- Issue closed and spec moved from
docs/issues/open/todocs/issues/closed/
- 2026-05-13 07:33 UTC - Copilot - Created focused pre-commit refactor draft split from combined proposal.
- 2026-05-13 08:42 UTC - Copilot - Executed
./contrib/dev-tools/git/hooks/pre-commit.shand captured baseline output (1m 14stotal; docs50s, tests17s). - 2026-05-13 09:26 UTC - Copilot - Opened GitHub issue #1769 and moved this spec to
docs/issues/open/. - 2026-05-13 12:04 UTC - Copilot - Implemented
--format/--verbositypre-commit modes with concise summaries, verbose streaming, per-step log capture, and JSON output. - 2026-05-13 12:16 UTC - Copilot - Collected 3-run baseline and 3-run candidate timing data; selected candidate profile A for pre-commit.
- 2026-05-13 12:24 UTC - Copilot - Updated skill/docs (
run-pre-commit-checksandAGENTS.md) with tier ownership and mode troubleshooting.
- AC1: Pre-commit supports
--format=<text|json>and--verbosity=<concise|verbose>with documented defaults and precedence rules. - AC2:
--format=text --verbosity=conciseprints high-signal step summaries and log paths on failure;--format=jsonemits a single valid JSON document matching the schema in Task 1. - AC2.1: Invalid flags/values fail with exit code
2, print usage guidance, and write diagnostics to stderr. - AC3: Chosen pre-commit profile is backed by timing data from multiple runs.
- AC4: Check-tier ownership is documented and consistent across scripts and docs.
- AC5: E2E remains excluded from pre-commit and explicitly mapped to pre-push/CI.
- AC6:
linter allexits with code0after changes. - AC7: Relevant checks pass for modified hook behavior.
| AC ID | Status (TODO/DONE) |
Evidence |
|---|---|---|
| AC1 | DONE | contrib/dev-tools/git/hooks/pre-commit.sh implements --format, --verbosity, and --verbose alias |
| AC2 | DONE | Successful runs captured for concise text mode and JSON mode with expected step summaries/payload |
| AC2.1 | DONE | Invalid/unknown flag checks return exit code 2 with usage diagnostics on stderr |
| AC3 | DONE | 3-run baseline vs 3-run candidate timing table recorded in this spec |
| AC4 | DONE | Tier ownership documented in .github/skills/dev/git-workflow/run-pre-commit-checks/SKILL.md and AGENTS.md |
| AC5 | DONE | Pre-commit excludes E2E; E2E remains in pre-push script and CI workflow |
| AC6 | DONE | linter all executes successfully inside multiple pre-commit and profile timing runs |
| AC7 | DONE | Hook behavior validated for text concise/verbose, JSON success, and forced-failure JSON/text payloads |
- Reducing local checks too far can miss early regressions.
- Mitigation: keep pre-push/CI comprehensive and document boundaries clearly.
- Concise output can hide details during debugging.
- Mitigation: preserve full verbose mode and always record log file paths.
- Hook complexity can grow over time (argument parsing, structured output, log orchestration).
- Mitigation: if complexity becomes hard to maintain in shell, migrate the hook logic to a small Rust CLI and keep the shell hook as a thin entrypoint.
- Captured logs can include ANSI color codes and multiline errors that are harder to parse in JSON consumers.
- Mitigation: strip ANSI sequences in
--format=jsonmode and keep raw logs on disk.
- Mitigation: strip ANSI sequences in
- Script interruption (Ctrl+C) can leave partial state or truncated output.
- Mitigation: add trap handling that emits a deterministic non-zero exit and a final status line/JSON payload where feasible.
- Pre-commit hook: contrib/dev-tools/git/hooks/pre-commit.sh
- Pre-push hook: contrib/dev-tools/git/hooks/pre-push.sh
- CI testing workflow: .github/workflows/testing.yaml
- Skill reference: .github/skills/dev/git-workflow/run-pre-commit-checks/SKILL.md
- GitHub issue: #1769
- Related split issue spec: docs/issues/open/1768-refactor-update-dependencies-skill-automation.md