From c10701d72fe8e1903f31cc390a2bd84e5fa352d1 Mon Sep 17 00:00:00 2001 From: Byron Date: Tue, 18 Aug 2026 11:24:49 +0200 Subject: [PATCH 1/2] Add a Tix CI sweep skill, for consistently working commits A green stack tip can hide broken intermediate commits, making review, rebasing, and bisecting unreliable. Document that every commit must include its required adaptations, route multi-crate changelog entries through crate scopes, and add a Tix sweep with a local CI runner to enforce the rule. Assisted-by: GPT 5.6 Co-authored-by: GPT 5.6 --- .agents/skills/tix-ci-sweep/SKILL.md | 51 +++++++++++++++++++ .../skills/tix-ci-sweep/agents/openai.yaml | 4 ++ AGENTS.md | 11 ++-- DEVELOPMENT.md | 14 ++--- etc/scripts/ci-check-local.sh | 42 +++++++++++++++ 5 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 .agents/skills/tix-ci-sweep/SKILL.md create mode 100644 .agents/skills/tix-ci-sweep/agents/openai.yaml create mode 100755 etc/scripts/ci-check-local.sh diff --git a/.agents/skills/tix-ci-sweep/SKILL.md b/.agents/skills/tix-ci-sweep/SKILL.md new file mode 100644 index 00000000000..abbe470a5b7 --- /dev/null +++ b/.agents/skills/tix-ci-sweep/SKILL.md @@ -0,0 +1,51 @@ +--- +name: tix-ci-sweep +description: "Visit every visible commit in a clean Tix-managed stack or tree, oldest first, and repair each commit until the repository's local CI check passes. Use when asked to validate, sweep, or fix every Tix commit so each one is independently CI-clean." +--- + +# Tix CI Sweep + +Repair every visible non-base commit without squashing, reordering, or skipping commits. Use stable Tix change IDs because amendments rewrite commit hashes. + +## Prepare + +1. Run `git status --porcelain=v1 --branch`. Require a clean index and worktree, including no untracked files. Do not stash, discard, or absorb pre-existing work. +2. Require `tix`, `cargo`, `just`, `cargo-nextest`, `cargo-machete`, and `cargo-deny` to be available. +3. Create a private directory with `mktemp -d "${TMPDIR:-/tmp}/tix-ci-sweep.XXXXXX"`. +4. Write `tix show` unchanged to `/show.txt` and copy `etc/scripts/ci-check-local.sh` to `/ci-check-local.sh`. Run the copy from the repository root throughout the sweep because both repository files disappear when visiting commits older than their introduction. +5. Record the starting checkout and its stable change ID. From `show.txt`, collect every visible commit row except base separators, ordered oldest first. Preserve topological order; for independent commits at the same depth, use their bottom-to-top display order. Stop if a displayed change-ID prefix is ambiguous or duplicated. + +## Sweep + +For each recorded change ID: + +1. Run `tix travel ` directly and verify that `HEAD` is the intended change. +2. From the repository root, run `/ci-check-local.sh`. +3. When it passes, continue to the next change ID without amending. +4. When it fails, reproduce the printed command without output suppression. Inspect the failure, relevant callers, tests, and nearby history. Distinguish a repository defect from a missing tool, unsupported host behavior, network failure, or flake. +5. Fix the repository defect with the smallest change that makes the current commit self-contained. Preserve the commit's intent and do not pull unrelated later changes backward. +6. Run the focused failing check while the worktree is dirty. Inspect the complete diff and stage only intended paths; do not absorb generated residue blindly. +7. Amend the staged fix with signing disabled: + + ```bash + GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=commit.gpgSign GIT_CONFIG_VALUE_0=false tix amend --index + ``` + +8. Require a clean worktree, then rerun the complete local CI script. Repeat the diagnose, fix, focused-check, amend, and full-check cycle until it passes. + +## Handle Travel Conflicts + +Treat a failed `tix travel` as an expected replay conflict only when it says time travel would conflict and the worktree remains unchanged. + +1. If the semantic resolution is clear, rerun `tix travel --materialize-conflicts `. +2. Require an unmerged index, then inspect `git diff --cc`, index stages `:1:`, `:2:`, and `:3:`, nearby code, tests, and relevant history. +3. Resolve while preserving both the amended ancestor and replayed commit intents. Stage only the resolution and run the signing-disabled `tix amend --index` command above. +4. Require a clean worktree and retry `tix travel `. + +Stop instead of guessing when resolution requires API, compatibility, or product judgment. + +## Stop and Complete + +- Stop on an unexpected Tix failure, unresolved environmental failure, repeatable flake, ambiguous change ID, or failure whose correct fix is unclear. Report the current change ID, command, output, and worktree state. Do not reset, switch with Git, create substitute commits, or push. +- After all commits pass, return with `tix travel `. Verify the original checkout is restored and `git status --porcelain=v1` is empty. +- Report the tested change IDs, amended changes, resolved travel conflicts, and any CI jobs outside the local script's scope. Remove the temporary directory only after successful completion. diff --git a/.agents/skills/tix-ci-sweep/agents/openai.yaml b/.agents/skills/tix-ci-sweep/agents/openai.yaml new file mode 100644 index 00000000000..e13e14bd8b6 --- /dev/null +++ b/.agents/skills/tix-ci-sweep/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Tix CI Sweep" + short_description: "Repair every Tix commit against local CI." + default_prompt: "Use $tix-ci-sweep to visit and repair every commit in the current Tix stack until the local CI gate passes." diff --git a/AGENTS.md b/AGENTS.md index f62e24b6b75..d23cdc0fda9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -59,13 +59,16 @@ uses `gix-error` (look at its `Cargo.toml`); if it does, follow the patterns bel Follow "purposeful conventional commits" style: - Use conventional commit prefixes ONLY if message should appear in changelog -- Breaking changes MUST use suffix `!`: `change!:`, `remove!:`, `rename!:` +- Breaking changes MUST use `!` before the colon: `change!:`, `remove!:`, `rename!:`, or _scoped_ forms like `feat(gix-odb)!:` - Features/fixes visible to users: `feat:`, `fix:` +- For a changelog-worthy commit touching multiple crates, _scope_ it to the crate that should receive the changelog entry, like `feat!(gix-ref)`. - Refactors/chores: no prefix (don't affect users) - Examples: - `feat: add Repository::foo() to do great things. (#234)` - `fix: don't panic when calling foo() in a bare repository. (#456)` - `change!: rename Foo to Bar. (#123)` + - `feat(gix-odb)!: add a new object lookup API` + - `fix(gix-ref)!: reject invalid reference names` ### Code Style @@ -146,8 +149,10 @@ Follow "purposeful conventional commits" style: - Ubuntu-latest git version is the compatibility target - `cargo smart-release` for releases (driven by commit messages) -- Split breaking changes into separate commits per affected crate if one commit-message wouldn't be suitable for all changed crates. -- First commit: breaking change only; second commit: adaptations +- Every commit must be self-contained and pass CI independently + - Feel free to run `etc/scripts/ci-check-local.sh` until it passes as proxy, as running every commit against CI isn't feasible. +- Keep breaking changes and all adaptations required to build and test the workspace in the same commit +- When such a commit touches multiple crates, _scope_ its conventional commit message to the crate whose changelog should receive the entry ## When Suggesting Changes diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index c56501ebc01..96ce6df2fef 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -41,6 +41,9 @@ Features or other changes that are visible and people should know about look lik And here is how it's used and some more details. - fix: don't panic when calling `foo()` in a bare repository. (#456) +When a changelog-worthy commit touches multiple crates, use the crate that should receive the changelog entry as the conventional +commit _scope_, for example `feat(gix-odb)!: add a new object lookup API` or `fix(gix-ref)!: reject invalid reference names`. + Everything else, particularly refactors or chores, don't use _conventional commits_ as these don't affect users of the API. Examples could be: @@ -57,14 +60,11 @@ are breaking so would be seen with their _exclamation mark_ suffix, like `change Commit messages are used for guiding `cargo smart-release` to do most of the release work for us. This includes changelog generation as well as picking the right version bump for each crate. -## Commit splitting on breaking changes. - -Knowing that `cargo smart-release` is driven by commit messages and affects their versions with per-crate granularity, it becomes important -to split edits into multiple commits to clearly indicate which crate is actually broken. +## Commit self-containment -Typical patterns include making a breaking change in one crate and then fix all others to work with it. For changelogs to look proper -and version bumps to be correct, the first commit would contain only the breaking changes themselves, -like "rename: `foo()` to `bar()`", and the second commit would contain all changes to adapt to that and look like "adapt to changes in ``". +Every commit must be self-contained and pass CI independently. Keep a breaking change and all adaptations required to build and test the +workspace in the same commit. Do not split them merely to route changelog entries. If that commit touches multiple crates, use the +conventional commit _scope_ to name the crate whose changelog should receive the entry, as described above. ## Commit History diff --git a/etc/scripts/ci-check-local.sh b/etc/scripts/ci-check-local.sh new file mode 100755 index 00000000000..c5f21550edc --- /dev/null +++ b/etc/scripts/ci-check-local.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repo_root="$(git rev-parse --show-toplevel)" +cd -- "$repo_root" + +print_command() { + printf '%q ' "$@" +} + +run() { + printf 'check: ' + print_command "$@" + printf '\n' + "$@" >/dev/null || { + status=$? + printf 'FAILED (%d): ' "$status" >&2 + print_command "$@" >&2 + printf '\n' >&2 + return "$status" + } +} + +require_clean() { + changes="$(git status --porcelain=v1)" + if [[ -n "$changes" ]]; then + printf '%s\n%s\n' "FAILED: test -z \"\$(git status --porcelain=v1)\"" "$changes" >&2 + return 1 + fi +} + +require_clean +run cargo fmt --all -- --check +run cargo machete +run just clippy -D warnings -A unknown-lints --no-deps +run cargo deny check bans licenses sources --workspace --all-features +run env GIX_TEST_IGNORE_ARCHIVES=1 just ci-test +run just doc-tests +run env GIX_TEST_CREATE_ARCHIVES_EVEN_ON_CI=1 cargo nextest run --workspace --no-fail-fast --exclude gix-error +run just ci-journey-tests +require_clean From 3fbf322bec9d88425654def09bbd8fd30b45e965 Mon Sep 17 00:00:00 2001 From: Codex GPT-5 Date: Tue, 18 Aug 2026 12:33:21 +0200 Subject: [PATCH 2/2] fix: update h2 past RUSTSEC-2026-0258 Update the locked h2 release from 0.4.15 to 0.4.16 so the cargo-deny advisories check no longer reports the unbounded empty DATA frame vulnerability. Validation: cargo deny check advisories --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9575ad62b9b..5c7dcc54d2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2738,9 +2738,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +checksum = "a9f37a958b41b3b19ee2707c06439c0e9e547e847223eb791ecb0cb821c65e27" dependencies = [ "atomic-waker", "bytes",