Skip to content

feat: add AI-powered fixing, MCP server, and fixture testing enhancements - #52

Merged
moshloop merged 16 commits into
mainfrom
feat/gavel-runner-skill
Jun 1, 2026
Merged

feat: add AI-powered fixing, MCP server, and fixture testing enhancements#52
moshloop merged 16 commits into
mainfrom
feat/gavel-runner-skill

Conversation

@moshloop

Copy link
Copy Markdown
Member

What

  • AI-powered linter violation fixing with Claude/multi-backend support and iterative fix-and-re-lint cycles
  • MCP (Model Context Protocol) server integration for safe AI assistant access to gavel commands
  • One-time bypass options for lint and file-size violations without persisting rules
  • Interactive file filtering with live search and working-tree age display
  • Fixture testing framework with Go testing.T integration, golden file updates, and rich detail documents
  • Go mod tidy automation and linked-deps upgrade path for dependency management

Why

  • Enable AI assistants to automatically repair violations and improve developer workflow
  • Provide controlled MCP interface for external tools while maintaining safety
  • Improve user experience with better filtering, visibility, and one-time overrides
  • Enhance test infrastructure with native Go testing support and golden file workflows
  • Automate dependency management tasks in commit workflows

Notes

  • Breaking changes: handleCommitLintFindings now returns lintFindingsOutcome; aifix.Request.Model, MaxCostUSD, and Files fields removed
  • AI config now uses captain's centralized configuration system
  • Fixture framework adds FixtureOrigin tracking and table node structure
  • Status rendering refactored to use new ttyrender package

moshloop added 8 commits May 26, 2026 14:06
…d of hardcoded model

Replace aifix's hardcoded Claude model selection and cost tracking with captain's centralized AI configuration system. This allows gavel lint --ai-fix to honor the same defaults as captain ai prompt, including model, backend, budget, and request options from ~/.captain.yaml.

Key changes:
- Remove Model and MaxCostUSD fields from aifix.Request
- Add AIConfig (captainai.Config) and AIRequestProto (captainai.Request) fields to allow callers to pass pre-built configuration
- Replace direct claude-cli provider instantiation with generic NewProvider that validates streaming capability
- Update lint and commit commands to build AIConfig and AIRequestProto from captain's AIRuntimeOptions
- Add comprehensive tests verifying config propagation and error handling for non-streaming backends

BREAKING CHANGE: aifix.Request.Model and aifix.Request.MaxCostUSD removed; callers must now provide AIConfig and AIRequestProto instead.
Extend aifix to work with any streaming AI provider (claude_cli, codex_cli, gemini_cli) instead of being Claude-specific. Provider selection is now determined by AIConfig.Backend.

Key changes:
- Remove hardcoded Claude references from package documentation
- Remove unused Files field from aifix.Request
- Improve ReLint error handling by capturing and surfacing errors instead of silently swallowing them, ensuring the loop stops fast
- Extract common AI configuration logic into buildAIFixRequest helper for reuse across commit and lint flows
- Add pr status --ai-fix command to feed PR status snapshots into the AI for fixing failures
- Refactor pr status command to use clicky's structured option binding
- Add comprehensive tests for ReLint error surfacing and PR status prompt generation

BREAKING CHANGE: aifix.Request.Files field removed; callers should remove this field from their Request construction.
…cking

Add RunTesting, RunGomega, RunGinkgo, and RegisterGinkgoSpecs methods to Runner for native Go testing integration. Implement FixtureOrigin struct to track source location (file, line, section path) for each fixture node. Convert table parsing to return a TableNode containing row nodes instead of flat fixture list. Add origin metadata to all fixture nodes during parsing.

This enables:
- Mirroring markdown structure as nested t.Run nodes
- Better error reporting with source locations
- Ginkgo and Gomega integration for BDD-style testing
- Improved test organization and debugging

Refs #fixture-testing
…CI migrator skill

Add three major features:

1. Golden file updates: introduce --update-golden flag for fixtures to rewrite @file expectations with actual output instead of failing on mismatch. Supports per-row glob expansion in stdout/stderr cells.

2. Go mod tidy automation: add --tidy flag to `gavel commit` (enabled by default) to run `go mod tidy` in every Go module and stage updates. Includes linked-deps upgrade path to drop local replaces and pin requires to latest tagged versions, then restore replaces post-commit as unstaged edits.

3. Agent Skills: document gavel-ci-migrator skill for migrating GitHub Actions workflows to flanksource/gavel composite action.

Also refactor status rendering to use new ttyrender package for in-place terminal updates, and improve logging verbosity control in betterleaks linter.

Refs: gavel-ci-migrator skill documentation, golden file testing, go.mod management
…ibility and rich detail documents

Introduce DisplayOptions to control which fixture results and output fields are shown based on verbosity level. Add OutputMode enum (Never, OnFailure, Always) for granular stdout/stderr visibility control. Implement DisplayOptionsForVerbosity to layer fixture-specific affordances on global logging verbosity. Add Detail type for rich clicky documents in test results, enabling syntax-highlighted source code and structured traces in the UI. Update fixtures command with --show-passed, --show-stdout, and --show-stderr flags. Modify FixtureResult.Pretty() to respect display options when rendering output. Add comprehensive tests for display option behavior and detail document serialization.
Replace inline go build / npm UI steps in release.yml and lint.yml with
calls to Taskfile targets so build logic (LDFLAGS, version stamping, UI
bundles) lives in one place. Generalize build:linux-amd64 into a
parameterized build:cross task driven by GOOS/GOARCH and repoint deploy
to it.
…enrichment

Introduce RunLogOptions struct to FetchRunJobs to enable atomic log attachment before caching. This fixes a cache-poisoning issue where a run cached without logs would silently suppress --logs requests.

Key changes:
- Add RunLogOptions with FetchLogs and TailLines fields
- Implement runHasFailureLogs to detect cached runs needing enrichment
- Add persistCompletedRun helper to consolidate cache persistence logic
- Extract IsFailureConclusion and RunHasFailedJob helpers for reusability
- Update all FetchRunJobs callers to pass RunLogOptions
- Add comprehensive tests for cache enrichment scenarios

This ensures that --logs requests are never silently ignored due to stale cache entries, while maintaining the performance benefit of the immutable cache for completed runs.

Refs: cache-poisoning fix for log fetching
Update multiple Go module dependencies including:
- flanksource/captain to v0.0.7
- flanksource/clicky to v1.21.12
- flanksource/gomplate/v3 to v3.24.82
- flanksource/is-healthy to v1.0.88
- grpc-ecosystem/grpc-gateway/v2 to v2.27.7
- opentelemetry modules to latest versions
- kubernetes modules from v0.35.4 to v0.36.1
- google.golang.org/protobuf to v1.36.12-0.20260120151049-f2248ac996af

Remove local development replace directives for flanksource/clicky and flanksource/captain.
Remove unused dependencies: gregjones/httpcache and mxk/go-flowrate.
@github-actions

github-actions Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Gavel summary

Source Pass Fail Skip Duration
lint: golangci-lint 0 1 0 743.173µs
lint: tsc 2 1 0 7.1s
./testrunner/ui 45 0 0 158ms
baseline 18 0 0 590.967µs
changegraph 14 0 0 475ms
commit 115 0 0 1.5s
fixtures 22 0 0 2ms
git 155 0 2 7.3s
github.com/flanksource/gavel/ai/aifix 18 0 0 -
github.com/flanksource/gavel/claudehistory 16 0 0 -
github.com/flanksource/gavel/cmd/gavel/choose 18 0 0 -
github.com/flanksource/gavel/commit 188 0 0 7.2s
github.com/flanksource/gavel/fixtures 143 0 0 90ms
github.com/flanksource/gavel/fixtures/types 10 0 0 -
github.com/flanksource/gavel/git 38 0 0 20ms
github.com/flanksource/gavel/github 108 0 2 -
github.com/flanksource/gavel/github/activity 8 0 0 10ms
github.com/flanksource/gavel/github/cache 40 0 5 -
github.com/flanksource/gavel/internal/ttyrender 3 0 0 -
github.com/flanksource/gavel/linters 49 0 0 10ms
github.com/flanksource/gavel/linters/betterleaks 14 0 0 10ms
github.com/flanksource/gavel/linters/golangci 2 0 0 -
github.com/flanksource/gavel/linters/jscpd/testdata/sample 0 0 1 -
github.com/flanksource/gavel/service 46 0 0 4.5s
github.com/flanksource/gavel/testrunner/bench 12 0 0 -
github.com/flanksource/gavel/testrunner/parsers 88 0 0 210ms
github.com/flanksource/gavel/testrunner/runners 72 0 0 300ms
github.com/flanksource/gavel/todos 36 0 0 -
github.com/flanksource/gavel/todos/claude 55 0 0 470ms
github.com/flanksource/gavel/todos/types 30 0 0 -
github.com/flanksource/gavel/utils 51 0 0 -
github.com/flanksource/gavel/verify 196 0 0 20ms
kubernetes 43 0 0 4ms
lint: betterleaks 0 0 1 -
linters 8 0 0 21ms
parsers 14 0 0 688.139µs
runcache 11 0 0 224ms
serve 28 0 0 738ms
utils 75 0 0 30ms

Totals: 1791 passed · 2 failed · 11 skipped · 30.4s

Failing linters

golangci-lint — error

golangci-lint execution failed: fork/exec /home/runner/work/gavel/gavel/.gavel/golangci-lint: exec format error
Output:

tsc — error

tsc wrapper failed: exit status 1
Stderr:
gavel-tsc: cannot load 'typescript' from /home/runner/work/gavel/gavel/site/node_modules. Install it with your package manager (e.g. `npm i -D typescript`).

View full results

moshloop added 8 commits May 31, 2026 18:50
The UI now defaults to a failures-only status filter (!passed, !skipped),
so passed/skipped tests are absent from the DOM on load. Specs that clicked
or asserted on those nodes hung indefinitely because chromedp clicks had no
deadline, wedging the whole suite until the go test panic timeout.

- Navigate with ?status=all in specs that interact with passed/skipped tests
- Add clickWithTimeout helper bounding each click so a missing selector fails
  the spec in seconds with the selector text, instead of hanging the suite
- Bound each spec's chromedp context as an overall backstop
Commit the gitignored testrunner/ui/dist/testui.js and pr/ui/dist/prui.js
so downstream modules that fetch gavel by tag/SHA can satisfy the
//go:embed dist/*.js directives. Also build+commit them in release.yml
before tagging so future tags stay self-contained.
…ix race conditions

Isolate test environments from developer machine state and process-global registries to prevent flaky CI failures:

- Configure git identity in test repos so git operations work on CI runners without global config
- Isolate HOME and git excludesFile in tests to prevent developer's global settings from interfering
- Fix race condition in supervisePackage by using atomic.Bool instead of pointer to bool for concurrent access
- Prevent snapshot replay from consulting process-global clicky task registry, which leaks phantom tasks from concurrent runs
- Add baseURL parameter to buildSearchQuery to enable deterministic testing of org resolution
- Update clicky dependency to v1.21.13
- Fix TestRunnerNoTests to expect success (empty test tree) instead of error for directories with no tests

These changes eliminate sources of test flakiness where CI failures occurred due to leaked state from previous runs or developer machine configuration.
Remove redundant Go dependency verification steps and test UI build from the workflow. Instead, build gavel once and use the resulting binary to run tests. This streamlines the CI pipeline and reduces build time by eliminating unnecessary steps.
The Test workflow now runs ./gavel test, which discovers the
testrunner/ui and site vitest suites in addition to the Go tests.

- testrunner/ui ships a pnpm-lock.yaml, so gavel invokes pnpm there;
  enable corepack and pnpm install it. Drop the stray npm
  package-lock.json so gavel resolves a single manager, and switch the
  Taskfile test-ui build to pnpm to match.
- site has only a package-lock.json; npm ci it so its vitest suite runs.
- Fix testrunner/ui config.ts reading window.__gavelBasePath at module
  load, which threw 'window is not defined' under vitest's node env.
  Guard with typeof window and rebuild the embedded bundle.

Both vitest suites now pass (testrunner/ui 45, site 14).
The lint, go-mod-tidy, and gavel-action jobs run `task test-ui pr-ui`
but only the Test job enabled pnpm via corepack, so test-ui's
`pnpm install` failed with exit 127 everywhere else. Switch test-ui to
npm to match pr-ui: replace pnpm-lock.yaml with package-lock.json, build
via `npm install && npm run build`, and drop the corepack step. gavel's
own runner auto-detects npm from the lockfile, so `./gavel test` keeps
running the vitest suite.

With the UIs building, golangci-lint runs for the first time on this
branch and surfaced 22 findings, all fixed at the source:

- errcheck/ineffassign: parse the vitest line via strconv.Atoi; drop the
  dead `err = nil`.
- SA1012: pass the real parent context (cmd.Context()/opts.Context) to
  newStopContext/resolveLinterExecutable instead of nil.
- SA4006/QF1001/QF1008/S1017: remove dead omitempty handling, apply
  De Morgan, drop redundant embedded selectors and HasPrefix guard.
- unused: delete superseded helpers (applyGroupIgnores, printFailureDetails,
  collectFailed, prettyYAML/mustMarshalYAML/configOriginLabel, mergeBase,
  hasDuplicateLines, containsScope) and retarget their tests at the live
  code paths (applyPostLintFilters, collectLeaves, OutputMode.ShouldShow).
Resolve the remaining red CI jobs surfaced once the UIs build:

- site: add the missing remark-frontmatter and remark-mdx-frontmatter
  deps that site/vite.config.ts imports, so `./gavel test`'s site vitest
  suite can load its config (was ERR_MODULE_NOT_FOUND). Refresh the
  npm lockfile.
- testrunner/ui e2e: the BeforeSuite DeferCleanup removed Chrome's
  user-data dir while the browser still held its SingletonLock, failing
  the suite with "unlink ... DeferCleanup". Register teardown via ordered
  DeferCleanup (browser stop runs before dir removal, LIFO) and retry the
  RemoveAll briefly to absorb the lock-release window.
- go-mod-tidy check: scope the post-tidy git diff to go.mod/go.sum. It
  previously built the UIs and diffed the whole tree, so the regenerated
  minified testui.js bundle (toolchain-dependent bytes) tripped the check.
  Tidy only needs go.mod/go.sum, not the embedded JS.
- Commit the npm-built testui.js so the embedded bundle matches what
  `npm run build` produces.
The composite action runs `gavel test --lint --format json=gavel-results.json`
and then `gavel summary --input` on that file. clicky serializes the command
handler's return value, but runTests' clean non-UI completion returned
(nil, nil) — to avoid clicky rendering a duplicate one-line Snapshot.Pretty()
on the terminal. That nil was marshalled to the file as a bare `null`, so the
uploaded artifact and the sticky PR comment summary were empty (the action's
placeholder stub only triggers when the file is *missing*, not null).

Return the snapshot — which already serializes to the {"tests":..,"lint":..}
shape gavel summary expects — whenever a serialized format is requested, and
keep returning nil for the default pretty/terminal path so the summary isn't
printed twice. Gated on clicky.Flags.ResolveFormat() != "pretty".
@moshloop
moshloop merged commit b490ffd into main Jun 1, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant