Skip to content

Commit 7d43975

Browse files
authored
Fix dependabot auto-merge (#151)
# Fix Dependabot auto-merge ## Summary Fixes the Dependabot auto-merge workflow, which was previously configured to make a single timed-out attempt rather than waiting for required CI checks to pass. ## Changes ### Dependabot auto-merge overhaul (`scripts/ci/enable-dependabot-automerge.sh`, `.github/workflows/dependabot-auto-merge.yml`) - **Removed one-shot mode.** The old workflow ran the script with a 45-second hard timeout (`DEPENDABOT_AUTOMERGE_ONE_SHOT=true`), causing it to bail before CI checks could settle. The script now always polls until checks complete. - **Increased job timeout** from 1 minute to 40 minutes to give the polling loop enough time to observe all required checks. - **Added `SIGTERM` handler** so the job can emit a clear error message if the runner kills it at the job timeout boundary. - **Broadened failure detection** (allowlist instead of denylist): checks and statuses are now considered failed if their conclusion/state is anything other than `success`, `skipped`, `neutral`, or `pending` — catching conclusions like `startup_failure` and `stale` that the old explicit list missed. - **Added fallback when all required checks belong to the current job**: if the only required checks reported are the auto-merge job itself, the script now falls back to gating on all non-self check-runs/statuses instead of treating the gate as already satisfied. - **Removed `jq` conditional guard**: `jq` is now always required (it was already required in practice). ### New agent preflight script (`scripts/ci/agent-preflight.py`, `scripts/tests/test_agent_preflight.py`) Adds a changed-file-aware preflight script for agentic workflows. It runs a targeted set of validations (version-sync checks, LLM skill linting, etc.) based on which files are staged, so agents catch issues before hitting pre-commit hooks. Ships with a full test suite (`248` test cases). ### Dependency pin rollback (all CI workflows) Rolls back `mozilla-actions/sccache-action` from `v0.0.10` to `v0.0.9` across all workflows (`ci-rust.yml`, `ci-benchmarks.yml`, `ci-network.yml`, `ci-security.yml`, `ci-verification.yml`, `publish.yml`).
1 parent 81260ba commit 7d43975

13 files changed

Lines changed: 1051 additions & 115 deletions

File tree

.github/workflows/dependabot-auto-merge.yml

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@ permissions:
88
contents: read
99
pull-requests: read
1010

11+
# Dependabot auto-merge policy is defined in .llm/context.md and locked in by
12+
# scripts/tests/test_enable_dependabot_automerge.py. Do NOT add a one-shot
13+
# bypass, do NOT wrap the script in a sub-job-level `timeout`, and do NOT
14+
# lower `timeout-minutes` below 32 — the polling loop needs ~32 minutes
15+
# to settle plus runner overhead.
1116
jobs:
1217
dependabot:
1318
name: Enable auto-merge for Dependabot PRs
1419
if: github.event.pull_request.user.login == 'dependabot[bot]' && !github.event.pull_request.draft && github.event.pull_request.head.repo.full_name == github.repository
15-
timeout-minutes: 1
20+
timeout-minutes: 40
1621
concurrency:
1722
group: dependabot-automerge-${{ github.event.pull_request.number }}
1823
cancel-in-progress: true
@@ -36,22 +41,8 @@ jobs:
3641
with:
3742
ref: ${{ github.event.pull_request.base.sha }}
3843
- name: Enable auto-merge
39-
run: |
40-
# Keep a small buffer between command timeout and job timeout for error handling/cleanup.
41-
# Job runs on ubuntu-latest, where GNU timeout is available.
42-
timeout --signal=TERM --kill-after=5s 45s bash ./scripts/ci/enable-dependabot-automerge.sh
43-
rc=$?
44-
if [ "$rc" -ne 0 ]; then
45-
# GNU timeout exits with 124 when the command exceeded the limit.
46-
if [ "$rc" -eq 124 ]; then
47-
echo "::error::Automerge one-shot timed out after 45s; refusing long wait."
48-
else
49-
echo "::error::Automerge script failed with exit code ${rc}."
50-
fi
51-
exit "$rc"
52-
fi
44+
run: bash ./scripts/ci/enable-dependabot-automerge.sh
5345
env:
5446
PR_URL: ${{ github.event.pull_request.html_url }}
5547
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
56-
DEPENDABOT_AUTOMERGE_ONE_SHOT: "true"
5748
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.llm/context.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ Pre-commit validates registration only, NOT that proofs pass. Run affected proof
159159

160160
Also: `ci-rust.yml` (Miri), `ci-security.yml` (cargo-geiger, cargo-deny).
161161

162-
Dependabot auto-merge policy: this repository is squash-only. Use `scripts/ci/enable-dependabot-automerge.sh` (which enforces `--squash`, supports one-shot enable mode for fast auto-merge setup, defaults to waiting for checks with fallback when required-check metadata is unavailable, and checks policy drift) instead of inline merge commands in workflows.
162+
Dependabot auto-merge policy: this repository is squash-only and the auto-merge job MUST wait for every non-self CI gate on the PR head SHA to reach an explicitly accepted state (`success`, `skipped`, `neutral`) before enabling merge. Anything else -- including `failure`, `timed_out`, `cancelled`, `action_required`, `startup_failure`, `stale`, missing/`null` conclusions, or future GitHub-added states -- refuses the merge (allow-list semantics). Use `scripts/ci/enable-dependabot-automerge.sh` -- never inline `gh pr merge` in workflows, never re-introduce a "one-shot" / bypass path, never wrap the script in a sub-job-level `timeout`. Regression-tested in `scripts/tests/test_enable_dependabot_automerge.py`.
163163

164164
**CI fails on:** unformatted code, clippy warnings, broken doc links, markdown lint errors, workflow syntax errors, unregistered Kani proofs.
165165

@@ -220,12 +220,15 @@ For protocol tests that poll in loops (`poll_remote_clients()` / protocol `poll(
220220

221221
**Unreleased code rule:** Never add separate "Fixed" or "Changed" entries for code that has not yet been released. Fixes to unreleased features should be folded into the existing "Added" entry describing that feature. The changelog should describe the final shipped state, not intermediate development history.
222222

223+
**Version sync rule:** If `Cargo.toml` is `X.Y.Z`, the matching changelog header must be `## [X.Y.Z] - YYYY-MM-DD` (ISO date required). Keep `## [Unreleased]` undated. Validate with `bash scripts/sync-version.sh --check`; auto-fix with `bash scripts/sync-version.sh --changelog-only`.
224+
223225
## Mandatory Linting
224226

225227
- **After Rust changes:** `cargo fmt && cargo clippy --all-targets --features tokio,json` (or `cargo c`)
226228
- **After workflow changes:** `actionlint` (no exceptions)
227229
- **After doc changes:** `cargo doc --no-deps`
228230
- **After markdown changes:** `npx markdownlint 'file.md' --config .markdownlint.json --fix`
231+
- **Before finalizing agent work:** `python3 scripts/ci/agent-preflight.py --auto-fix` (changed-file-aware early checks; if output includes `Falling back to --all checks.`, resolve git-state issues and rerun)
229232
- **After shell-script changes:** `bash scripts/ci/check-shell-portability.sh`
230233
- **After `.llm/` changes:** All `.md` files under `.llm/` must be **300 lines or fewer** (enforced by pre-commit hook `llm-line-limit`)
231234
- **Link validation:** `./scripts/docs/check-links.sh`

.llm/skills/ci-cd-tooling/github-actions.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,3 +230,17 @@ env:
230230
- [ ] Verify `permissions:` block is minimal
231231
- [ ] Verify `runs-on` uses valid runner labels
232232
- [ ] Validate matrix combinations
233+
234+
## Dependabot auto-merge gating
235+
236+
Auto-merge polls `gh pr checks --required` for the PR head SHA (the primary path). When required-check metadata is unavailable -- no required checks are configured for the branch, or the only required check is the auto-merge job itself -- it falls back to `repos/{owner}/{repo}/commits/{sha}/check-runs` and `…/status`. Both paths exclude the auto-merge job's own entries by filtering `link`/`details_url`/`target_url` against `GITHUB_RUN_ID`.
237+
238+
Failure classification uses an **allow-list**: only `success`, `skipped`, and `neutral` proceed; everything else (including `failure`, `timed_out`, `cancelled`, `action_required`, `startup_failure`, `stale`, missing/`null`, or future GitHub-added states) blocks the merge. `skipped` is allowed because matrix builds and conditional jobs legitimately produce it; `neutral` is allowed because GitHub-native advisory checks (e.g. `dependency-review-action`) emit it for non-failure findings.
239+
240+
Three regression guardrails in `scripts/tests/test_enable_dependabot_automerge.py` lock in this policy:
241+
242+
1. `test_workflow_does_not_set_one_shot_env` -- fails CI if the bypass env var is reintroduced.
243+
2. `test_workflow_run_command_is_pure_script_invocation` -- fails CI if the script is wrapped in `timeout`, `xargs`, or any prefix command.
244+
3. `test_workflow_timeout_is_sufficient_for_polling` -- fails CI if the workflow `timeout-minutes` falls below 32 minutes (the polling settle ceiling + a 2-minute buffer).
245+
246+
Branch protection is a defense layer, not a substitute. Configure `main` to require the relevant CI checks so GitHub's native auto-merge respects them too -- but the script's own gating remains the source of truth.

.llm/skills/publishing-organization/changelog.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@
5050

5151
Sections: Added, Changed, Deprecated, Removed, Fixed, Security.
5252

53+
## Release Header and Version Sync
54+
55+
- Keep `## [Unreleased]` undated.
56+
- Use ISO dates on release headers: `## [X.Y.Z] - YYYY-MM-DD`.
57+
- If `Cargo.toml` is `X.Y.Z`, the matching changelog header must be dated.
58+
- Validate: `bash scripts/sync-version.sh --check`
59+
- Auto-fix link/date metadata: `bash scripts/sync-version.sh --changelog-only`
60+
5361
## Writing Guidelines
5462

5563
### Be User-Focused
@@ -122,6 +130,9 @@ Changing `Display`/`Debug` output is **Breaking** if users might depend on forma
122130
## Verification Before Committing
123131

124132
```bash
133+
# Validate changelog/version synchronization
134+
bash scripts/sync-version.sh --check
135+
125136
# Verify derives exist before claiming them
126137
rg '#\[derive.*Hash' src/lib.rs
127138

.llm/skills/workflows/dev-pipeline.md

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ Structured workflow from planning through shipping for fortress-rollback. Each p
4444

4545
### Scope Decision Rules
4646

47-
| Change Type | Needs Scope Doc? | Needs CHANGELOG? |
48-
|-------------|-----------------|-----------------|
49-
| Bug fix (pub-visible) | Yes | Yes |
50-
| Bug fix (internal) | Yes | No |
51-
| New public API | Yes | Yes |
52-
| Refactoring | Brief | No |
53-
| Dependency update | No | If user-visible |
54-
| CI/tooling | No | No |
55-
| Kani proof | Brief | No |
47+
| Change Type | Needs Scope Doc? | Needs CHANGELOG? |
48+
| --------------------- | ---------------- | ---------------- |
49+
| Bug fix (pub-visible) | Yes | Yes |
50+
| Bug fix (internal) | Yes | No |
51+
| New public API | Yes | Yes |
52+
| Refactoring | Brief | No |
53+
| Dependency update | No | If user-visible |
54+
| CI/tooling | No | No |
55+
| Kani proof | Brief | No |
5656

5757
---
5858

@@ -85,14 +85,14 @@ Before running verification/debugging commands in this workflow, confirm:
8585

8686
### Design Patterns to Follow
8787

88-
| When You Need | Use This Pattern | Example in Codebase |
89-
|---------------|-----------------|-------------------|
90-
| Configurable construction | Builder | `SessionBuilder` |
91-
| Protocol state machine | Type-state or enum | `SessionState`, protocol states |
92-
| Input prediction | Strategy | `PredictionStrategy` trait |
93-
| Bounded collections | Circular buffer | `SavedStates` |
94-
| Request/response | Request enum | `FortressRequest` |
95-
| Error context | Structured enum | `InternalErrorKind`, `InvalidRequestKind` |
88+
| When You Need | Use This Pattern | Example in Codebase |
89+
| ------------------------- | ------------------ | ----------------------------------------- |
90+
| Configurable construction | Builder | `SessionBuilder` |
91+
| Protocol state machine | Type-state or enum | `SessionState`, protocol states |
92+
| Input prediction | Strategy | `PredictionStrategy` trait |
93+
| Bounded collections | Circular buffer | `SavedStates` |
94+
| Request/response | Request enum | `FortressRequest` |
95+
| Error context | Structured enum | `InternalErrorKind`, `InvalidRequestKind` |
9696

9797
---
9898

@@ -143,11 +143,11 @@ cargo nextest run module_name --no-capture
143143

144144
### Commit Granularity
145145

146-
| Good Commit | Bad Commit |
147-
|-------------|-----------|
148-
| "Add `InputDelayTooLarge` variant to `InvalidRequestKind`" | "Various changes" |
149-
| "Validate input delay in `SessionBuilder::with_input_delay`" | "Fix stuff" |
150-
| "Add regression test for issue #42" | "WIP" |
146+
| Good Commit | Bad Commit |
147+
| ------------------------------------------------------------ | ----------------- |
148+
| "Add `InputDelayTooLarge` variant to `InvalidRequestKind`" | "Various changes" |
149+
| "Validate input delay in `SessionBuilder::with_input_delay`" | "Fix stuff" |
150+
| "Add regression test for issue #42" | "WIP" |
151151

152152
Each commit should pass `cargo c && cargo t` independently.
153153

@@ -168,6 +168,9 @@ rg '\.unwrap\(\)|\.expect\(|panic!\(|todo!\(' --type rust src/
168168
# Determinism scan
169169
rg 'HashMap|HashSet|Instant::now|thread_rng' --type rust src/
170170

171+
# Agent preflight (catches version sync/.llm/workflow issues early)
172+
python3 scripts/ci/agent-preflight.py --auto-fix
173+
171174
# Full quality gate (see context.md "Mandatory Linting" for details)
172175
cargo c && cargo t
173176
cargo doc --no-deps
@@ -260,10 +263,10 @@ For production-blocking bugs, compress the pipeline:
260263

261264
## Anti-Patterns
262265

263-
| Anti-Pattern | Why It Hurts | Do Instead |
264-
|-------------|-------------|------------|
265-
| Code first, think later | Rework, wrong abstraction | Scope and design first |
266-
| Skip tests | Bugs ship, regression debt | Write tests before code |
267-
| Mega-PR (>500 lines) | Hard to review, risky merge | Split into stacked PRs |
268-
| Fix + refactor in one PR | Hard to review, bisect-breaking | Separate commits/PRs |
269-
| Skip self-review | Obvious issues waste reviewer time | Review your own diff first |
266+
| Anti-Pattern | Why It Hurts | Do Instead |
267+
| ------------------------ | ---------------------------------- | -------------------------- |
268+
| Code first, think later | Rework, wrong abstraction | Scope and design first |
269+
| Skip tests | Bugs ship, regression debt | Write tests before code |
270+
| Mega-PR (>500 lines) | Hard to review, risky merge | Split into stacked PRs |
271+
| Fix + refactor in one PR | Hard to review, bisect-breaking | Separate commits/PRs |
272+
| Skip self-review | Obvious issues waste reviewer time | Review your own diff first |

.llm/skills/workflows/review-readiness.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Concrete gate between implementation and external review. Use this after `dev-pi
1616
- [ ] Tests cover happy + error paths for changed behavior
1717
- [ ] Design decision log reviewed for major architecture choices
1818
- [ ] CHANGELOG decision applied for user-observable/public changes
19+
- [ ] Agent preflight passes (`python3 scripts/ci/agent-preflight.py --auto-fix`)
1920

2021
If two or more checks fail, return to design and reduce scope before requesting review.
2122

@@ -32,6 +33,9 @@ rg '\.unwrap\(\)|\.expect\(|panic!\(|todo!\(|unimplemented!\(' --type rust src/
3233

3334
# Determinism scan
3435
rg 'HashMap|HashSet|Instant::now|SystemTime|thread_rng|random\(\)' --type rust src/
36+
37+
# Changed-file-aware preflight checks (version sync, .llm quality, workflows)
38+
python3 scripts/ci/agent-preflight.py --auto-fix
3539
```
3640

3741
---
@@ -45,6 +49,7 @@ Review Readiness
4549
- Build/tests: PASS|FAIL
4650
- Zero-panic: PASS|FAIL
4751
- Determinism: PASS|FAIL
52+
- Agent preflight: PASS|FAIL
4853
- Error handling: PASS|FAIL
4954
- Tests breadth: PASS|FAIL
5055
- Design log reviewed: YES|NO|N/A

AGENTS.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,3 @@
33
**Read and follow [`.llm/context.md`](.llm/context.md)** — the canonical source of truth for all project context, development policies, testing guidelines, and coding standards. You must read it before making any changes.
44

55
When clarifying questions are needed, follow [`.llm/templates/ask-user-question.md`](.llm/templates/ask-user-question.md) to keep questions concise and actionable.
6-
7-
## Critical Rules
8-
9-
- **Test output:** NEVER pipe test output through `tail`/`head` (e.g., `cargo nextest run 2>&1 | tail -40`). Instead, redirect to a temp file and read it: `cargo nextest run --no-capture > /tmp/test-results.txt 2>&1`. For repeated runs, use a for loop.

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
1515
## [Unreleased]
1616

17-
## [0.8.0]
17+
## [0.8.0] - 2026-04-25
1818

1919
### Changed
2020

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ When clarifying questions are needed, follow [`.llm/templates/ask-user-question.
88

99
- **Zero-panic:** No `unwrap()`, `expect()`, `panic!()`, `todo!()` in production code
1010
- **Pre-commit:** `cargo fmt && cargo clippy --all-targets --features tokio,json && cargo nextest run --no-capture` (or `cargo c && cargo t`)
11+
- **Agent preflight:** Before finalizing changes, run `python3 scripts/ci/agent-preflight.py --auto-fix`. If output includes `Falling back to --all checks.`, resolve the git-state issue and rerun preflight.
1112
- **Test output:** NEVER pipe test output through `tail`/`head` (e.g., `cargo nextest run 2>&1 | tail -40`). Instead, redirect to a temp file and read it: `cargo nextest run --no-capture > /tmp/test-results.txt 2>&1`. For repeated runs, use a for loop.
1213
- **Kani:** Always add `#[kani::unwind(N)]` to proofs; CI uses `--default-unwind 8` via `--quick` mode
1314
- **Changelog:** Ask "Does this affect `pub` items or user-observable behavior?" — if yes, update `CHANGELOG.md`

0 commit comments

Comments
 (0)