Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .agents/skills/tix-ci-sweep/SKILL.md
Original file line number Diff line number Diff line change
@@ -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 `<temp-dir>/show.txt` and copy `etc/scripts/ci-check-local.sh` to `<temp-dir>/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 <change-id>` directly and verify that `HEAD` is the intended change.
2. From the repository root, run `<temp-dir>/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 <change-id>`.
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 <change-id>`.

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 <starting-change-id>`. 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.
4 changes: 4 additions & 0 deletions .agents/skills/tix-ci-sweep/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -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."
11 changes: 8 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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 `<crate name>`".
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

Expand Down
42 changes: 42 additions & 0 deletions etc/scripts/ci-check-local.sh
Original file line number Diff line number Diff line change
@@ -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
Loading