Skip to content

v1.17.0

Choose a tag to compare

@github-actions github-actions released this 02 Sep 05:39
· 232 commits to main since this release

Added

  • make ci runs the Go pull-request checks (make check then make test).
  • A missing or empty index.jsonl is rebuilt from the run journals, and a
    stale one has every newer unindexed journal appended, so gauntlet runs
    and the file-signal suggester still see a run whose process died after
    flushing the journal. gauntlet show already read those files.
    Reconstructed rows have no args or exit_code.
  • GH_TOKEN is read for release lookups, the same name GitHub CLI uses. It
    wins over GITHUB_TOKEN when both are set, and both now authenticate the
    checksum and asset downloads as well as the release listing, so a private
    --update-repo can actually install.
  • gauntlet pick can compose --stacked-prs from the run pane. Turning it
    on clears --commit, --push, and --merge-into and pins concurrency at
    1, so the launcher cannot emit a command the parser would refuse.
  • ? on the launcher opens a help overlay, the same key the dashboard uses.
    q / esc close it; they do not leave the picker.
  • A project prompt that contains the opening review marker can no longer
    close the fence: both BEGIN REVIEW and END REVIEW in the body are
    rewritten, matching what the end marker already did.
  • Conflicted paths named in the resolver prompt are fenced, dropped when they
    carry the resolver's output protocol or formatting characters, and capped;
    a conflict with more files than the prompt will name is left for a human
    instead of launching an agent that cannot finish.
  • Commit subjects taken from agent output drop bidi overrides and Unicode
    line separators, not only ASCII controls, so a model cannot spoof git log
    or forge a commit body.
  • Suggestion reasons from the triage agent are rune-capped like catalog
    descriptions, so one overlong line cannot flood the suggest listing.

Changed

  • --merge-into refuses to merge when git status cannot be read, the same
    way it already refuses a dirty tree, so a merge event cannot report work
    that never moved.
  • A missing or stale run index that cannot be reconstructed is an error
    from gauntlet runs, not a listing that silently omits the newest run.
  • A stacked-PR reload that cannot verify its pinned base commit fails
    rather than fetching a new tip and splitting the stack.
  • Dashboard and launcher wordmark is the path-arrow teal of the mark
    (#0e96a8 on dark terminals, a darker pull of that hue on light), one
    hue, not Catppuccin teal and not a per-letter gradient. Footer keys are
    body-colored chrome like the launcher's. The budget meter rides the heat
    ramp. Reload status uses the info hue. Panel names stay dim with the
    rest of the chrome.
  • Dashboard lanes and the feed drop the -review suffix the grid already
    omitted, so a name is spelled the same way on every instrument.
  • f in the dashboard footer says widen while the feed is narrowed, the
    same way space says resume while paused.
  • home / end jump the dashboard feed the way g / G do, and jump to
    the first or last row of the focused launcher pane.
  • The launcher shows the same "warming up" line as the dashboard until the
    terminal reports its size, instead of a blank screen.
  • --continue-sessions with --jobs above 1 or --stacked-prs is a usage
    error. Those modes give each review a fresh worktree, so there is no session
    to resume; the flag used to be accepted and silently ignored. Scripts that
    passed both will see exit 2.
  • q / esc on the live dashboard arms a hard stop instead of killing the
    run on the first press (1.15.0 still documented immediate quit). A second
    press, or q after the run has finished or is already draining, closes the
    dashboard and cancels the run. The header shows q TO STOP while armed;
    any other key disarms it. q on the help overlay still only closes help.
  • gauntlet runs prints STARTED as local YYYY-MM-DD HH:MM:SS. The old
    MM-DD HH:MM:SS column had no year and swapped day and month for readers
    used to ISO dates.
  • gauntlet runs DURATION uses the monotonic elapsed Close records, matching
    the run's Total time. An NTP step or a manual clock set between start and
    end can no longer stretch or shrink the listing. Old index rows without
    elapsed_s still use End−Start; a pair that moved backwards prints n/a
    instead of 0s.
  • Bundled review prompts: integer-width and abbreviation rules no longer
    rewrite language-idiomatic types and names; post-quantum crypto items are
    note-only; Kubernetes-native checks skip Dockerfile-only trees; docs-vs-code
    disagreements have a single owner.
  • Ruff on scripts/ selects the bugbear, pylint, pyupgrade, and bandit
    groups (and every other category those two files already pass), with a
    100-column cap, instead of the default four error codes. scripts/shots.sh
    is gated with shellcheck in the same CI job. Rule selection lives in
    pyproject.toml. make check-scripts runs the same pinned ruff, mypy, and
    shellcheck steps as that job, so a scripts/ change fails locally.
  • Docs for --jobs N describe the persistent lane worktrees the runner
    actually uses, not the per-review throwaway checkouts that 1.13.0 replaced.
  • Design docs name the git hardening the runner actually applies
    (core.pager=cat, attr.tree, local driver blanks) and the conflict-step
    cap that leaves an oversized conflict for a human. --stream is documented
    as on by default, matching the flag. The landing-page trust model names
    core.pager=cat rather than an empty pager.
  • Live usage ticks on the event bus are droppable, the same as agent output:
    a slow subscriber no longer stalls the scheduler on reconstructible
    telemetry. Final token counts still ride on review_end.
  • gauntlet show and suggest history look up a generated run id by the date
    it encodes, instead of probing every day directory under the journal.
  • gauntlet runs parses the index tail from the end, so a long index costs
    the rows shown rather than a split of the whole slice.
  • The file-signal suggester reuses one git handle for the tree listing and
    the churn window. Opening a repo no longer runs rev-parse HEAD until
    line stats need a baseline. A million-file tree is listed only up to the
    scan cap, so the unused tail does not stay in memory.
  • Project prompt discovery asks git for *-review.md by name instead of
    walking the tree. Generated and hidden directories are still skipped;
    a directory that is not a repository still walks.
  • Worktree line samples run git diff --shortstat and ls-files -o
    together instead of one after the other.

Fixed

  • A hot-reload handoff that cannot be read or parsed now aborts the successor
    instead of starting a fresh run. The unparseable case was silent, so the new
    process re-ran every finished review under a new run id.
  • Lane worktree removal failures and unreadable HEAD reads during --jobs
    scheduling are logged. Removal errors were discarded, and a failed HEAD
    read was indistinguishable from an unchanged tip, so a lane silently kept
    its stale base.
  • gauntlet update network and JSON decode errors name the URL that failed.
  • --merge-into no longer treats untracked files as uncommitted work. The
    merge is a scratch checkout of committed work, so a local notes.txt was
    never going to be in it; refusing the merge used to drop a loop's
    committed changes. Tracked dirty files still block, matching --jobs.
  • A pump that outlives an agent's process no longer publishes output or
    usage after that review has ended. Those events are keyed by agent, so
    they used to land on whatever the same agent started next.
  • A trailing } or ] after agents.json is refused, matching
    encoding/json. json.Decoder.More treats those closers as end-of-value,
    so {}} used to load as an empty definition set.
  • Listing runs recovers the whole tail of journals missing from
    index.jsonl, not only the newest, so two runs that died before Close
    both appear. A failed index write is retried on the next Close, and a
    rebuild cannot overwrite a Close that races it.
  • gauntlet runs and the file-signal suggester list a crashed run that
    sits behind a later Close, not only an unindexed suffix. The index is
    a cache of summaries; the n newest journals are the listing, so a hole
    in that window is filled from the event stream without rewriting Close
    rows.
  • Path flags (--dir, --dirs, --log, --prompt-dir, --bin) refuse an
    environment variable that is unset or empty instead of expanding it to
    nothing. $MISSING used to become the current directory (--dir), the
    bundled prompts (--prompt-dir), or a silently dropped log (--log). An
    explicit empty --prompt-dir or --log is a usage error too, matching
    --dir. A leading ~/ with no usable HOME is refused rather than taken
    relative to the working directory.
  • The launcher's a key and a set header's space bar act on the reviews the
    filter is showing, not the ones it hid. A fruitless filter no longer
    selects the whole catalog.
  • Typing a review filter on the launcher replaces the key legend with the
    keys that work there (enter keeps it, esc clears it). The legend used
    to keep advertising run and cancel, which those keys do not do until
    the filter is closed.
  • A --usage-limit probe that prints more than 4 KiB is ignored, the same
    as any other broken probe, instead of filling memory until the timeout.
  • One git or gh command's captured output is capped (32 MiB and 8 MiB)
    so a hostile tree or a runaway listing cannot grow without bound.
  • Closing --log reports a write error instead of dropping it.
  • The dashboard feed's scroll offset stays inside the retained ring, so a
    long pause cannot claim thousands of lines back after history is trimmed.
  • An in-place retry restores the working tree to the snapshot taken before
    the failed attempt, including the user's own uncommitted files, so the
    next try starts from the same files the first one saw. Isolated reviews
    already reset their worktrees. --continue-sessions no longer resumes a
    failed attempt's session.
  • The untracked-file line-count cache drops files that have vanished or
    left the untracked set, so a long loop that creates then commits files
    does not fill the table with dead keys and re-read every later file.
  • Git and GitHub errors that quote a remote URL drop URL userinfo. A remote
    stored as https://alice:token@host/repo.git is reported as
    https://host/repo.git, so the account name does not reach the terminal
    or the run journal.
  • dsh model overlays are keyed uniquely: foo/bar and foo_bar no longer
    share one --patch file, so a later pin cannot launch with an earlier
    pair's provider and model. A deleted overlay is rewritten instead of
    handed to dsh as a missing path.
  • The untracked-file line-count table stops admitting new keys at its cap
    instead of wiping the working set, so a tree with more than 4096 new
    files does not re-read every already-counted file on the next sample.
  • --usage-limit NaN (and nan) is a usage error. flag.Float64Var accepts
    it, the 0-100 range check cannot see it, and the runner's pct < limit
    comparison is then always false, so a run configured with a NaN limit
    stopped before its first review. Non-finite values are refused the same
    way a probe that prints them already is.
  • Stream-JSON token counters that are not whole numbers, or that claim more
    than a trillion tokens, are ignored rather than truncated or stored.
    {"output_tokens": 1.9} used to record 1, because JSON numbers decode as
    float64 and int(1.9) is 1. A 2^62 counter fitted in int and then
    overflowed the run total. Both match what json.Number parsing and the
    text-usage cap already required.
  • The dashboard reasoning glyph no longer panics when the clock is set
    before 1970. Go's remainder keeps the sign of a negative UnixNano, so
    the frame index was -1.
  • Unknown commands and flags include a "did you mean" hint, matching unknown
    review and agent names. --show-prompt does too.
  • gauntlet --list and --show-prompt no longer require an agent CLI in
    PATH. They only read prompts; gauntlet doctor reports which agents are
    installed.
  • Global flags (--help, --version, --log, --no-color) may precede the
    subcommand, so gauntlet --no-color doctor works. gauntlet show accepts
    flags before the run id, so gauntlet show --no-color RUN and
    gauntlet show --help after --no-color work.
  • gauntlet show on an unknown run id points at gauntlet runs.
  • Stacked PR creation treats head and base as an idempotency key: if
    gh pr create times out after GitHub accepted the pull request, or fails
    because it already exists, the existing URL is reused instead of stopping
    the stack or opening a second PR.
  • StartBranch on a lane or stack worktree converges when the branch already
    exists at its base, the same leftover-empty-branch rule AddWorktree
    already follows. A branch that carries commits is still refused.
  • A review that never launched (unknown name, unreadable prompt, or a command
    line that could not be built) now publishes review_end like every other
    outcome, so the journal, the dashboard, and gauntlet show record it. The
    file-signal suggester no longer treats those, or failed, timed-out, and
    interrupted launches, as finished runs that changed nothing.
  • gauntlet pick no longer treats untracked files as blocking --jobs.
    The runner has allowed them since 1.12; the launcher was still using a
    full dirty-tree check and refused a run that would have started.
  • Stacked PRs refuse a remote whose path is not OWNER/REPO on both sides.
    https://github.com/owner/.git and https://github.com//repo used to
    pass the one-slash check and be handed to gh as owner/ and /repo.
  • The README install snippet fetches the binary for the version it just
    resolved, rather than releases/latest/download, so a release published
    between the two curls cannot pair a new tag with the previous binary.
  • The README install snippet and make install say when ~/.local/bin is
    not on PATH. On macOS it is not there by default, so the binary was
    installed and then not found.
  • The directory lock is no longer inherited by agent and git children. The
    lock descriptor is opened close-on-exec, so a killed parent cannot leave
    the tree locked for as long as those children live.
  • Agent timeout and output-drain waits no longer leave a timer running after
    the process has already exited. Each wait is a timer that is stopped when
    the other path wins, matching the retry backoff.
  • --update-repo is checked as owner/repo at startup. A URL, a missing
    slash, or an extra path segment used to reach the GitHub API and fail there
    with a status line, or to hit a different endpoint entirely.
  • --dir, --dirs, and --push-remote refuse an empty value instead of
    treating it as the current directory or as origin.
  • A defined agent's usage with no roots is refused when the definition
    is loaded, rather than later when transcript registration fails without
    naming the file.
  • A hot-reload handoff records the predecessor's monotonic elapsed time, so
    --runtime and the dashboard clock do not jump when the wall clock steps
    during the exec. An older handoff without the field still uses the
    wall-clock span from when the run started.