A GitHub CLI extension that supercharges gh pr list with a richer,
color-coded table view — approvals, AI reviewer status, check details,
comment resolution, and clickable PR links.
Also includes gh x pr atm for org-wide PR visibility.
# Title Author State Rev AI Appv Checks Cmts Branch Upd
#12 PLAT-18678: Migrate user-groups to .NET 10 jdoe open ✓ pass 2 pending 19/19 feature/PLAT-18678 23h
#10 .net 10 upgradation asmith open • - 0 fail - feature/PLAT-8516 17d
#5 feat(user-groups): Add golden-path IaC structure bclark open • fail 0 pass 2/4 golden-path-alignment 4mo
Requires GitHub CLI (gh) authenticated with your account.
gh extension install hemsoft-dev/gh-xThat's it. Prebuilt binaries are available for all platforms — no Go toolchain needed.
gh x pr list [flags] # enriched PR list for current repo
gh x pr me [flags] # your open PRs plus Dependabot PRs across an owner
gh x pr atm [flags] # org-wide PRs needing your attention
gh x pr review [number] # read-only agentic PR review
gh x pr changelog # show release notes for recent versions
gh x monitor # full-terminal multi-repository PR and issue dashboard
gh x status # repository health with open and recently merged PRs
gh x project # issues grouped by project Status (alias: p)
gh x run list [flags] # workflow runs with clickable IDs
gh x version # show version and check for updates (also: --version, -v)Use the global --clear flag to start with a clean terminal display:
gh x --clear status
gh x s --clear
gh x pr me --clearThe flag may appear before the command or among its flags. It clears the visible
display and moves the cursor to the top once, before the banner and command
output. It defaults to false; --clear=false also leaves the display unchanged.
JSON output and non-terminal stdout ignore clearing, so pipes and redirected
files do not receive terminal-clearing sequences. Existing watch and monitor
refresh and terminal restoration behavior stays the same.
If you switch between personal and work accounts, gh x adapts automatically.
When the active account cannot access the target repository, commands retry
with each other logged-in account until one succeeds (gh auth status --json hosts supplies candidates) and print a one-line notice on stderr, for
example:
[gh-x] note: retried as fhemmerrelias after an access failure
The target host is resolved following gh's own precedence: an explicit
--hostname, an explicit HOST/OWNER/REPO value on --repo/-R, then the
GH_REPO environment variable, then the current repository's git remote, then
GH_HOST, then github.com. SSH and SCP-style remote aliases are expanded
through ssh -G; the alias retains its original case for OpenSSH matching, and
a configured HostName becomes the API host only when it is github.com,
matches GH_HOST, or is listed in gh's local host configuration. This lets a
dotted alias such as hub.lumenfield.work-hemsoft route to github.com. Canonical
github.com and configured Enterprise API hosts are preserved before SSH
resolution; when a public alias resolves to the ssh.github.com transport
endpoint, it maps back to the github.com API host. The SSH configuration
lookup has a two-second deadline and bounded pipe drain. If it fails or the SSH destination is not a known API host, a plausible
dotted remote host remains the fallback API host.
Fallback candidates come from that same host in gh auth status --json hosts,
and Enterprise Server
retries inject GH_ENTERPRISE_TOKEN/GITHUB_ENTERPRISE_TOKEN instead of
GH_TOKEN, so personal and corporate hosts never exchange credentials.
The global active account is never modified — the retry token lives only
inside the retried subprocess. Explicit GH_TOKEN or GITHUB_TOKEN overrides
are respected for github.com targets, GH_ENTERPRISE_TOKEN or
GITHUB_ENTERPRISE_TOKEN for Enterprise Server targets (no fallback), auth
commands never fall back, and public repositories never trigger a retry
because either token can read them. Tokens resolve lazily and are cached per
account and host for the current process; they are not stored or shared
between runs. Search-backed pull request and issue lists verify repository
access before accepting an empty result because GitHub can return an empty
successful response when the active account cannot see a private repository.
Two flows are pinned to the active account and never fall back on any host:
identity-scoped
queries (gh x pr me, gh x pr atm, gh x codespace list — they resolve
"me" or list the authenticated user's resources, so retrying under a different
identity could answer with someone else's data), and the entire
gh x pr review operation (fetch, agent, and submission are identity-bound,
and posting a review is non-idempotent). @me in author and assignee filters resolves to the active account's login up
front for the same reason; in --search, only identity qualifiers (author:,
assignee:, mentions:, commenter:, involves:, review-requested:,
reviewed-by:) are expanded, leaving other query text untouched.
The issue table keeps the standard filters and adds compact repository context:
| Column | Description |
|---|---|
| # | Issue number, linked to the issue on GitHub in terminals with OSC 8 support |
| PRs | Pull requests linked through GitHub's issue-closing relationship. - means none and ? means relationship data was unavailable |
| Parent | Immediate parent issue. Same-repository parents use #NUMBER; cross-repository parents use OWNER/REPO#NUMBER |
| Sub | Completed direct sub-issues over total direct sub-issues, such as 2/5 |
| Title | Truncated to 51 characters |
| Author | Issue author login |
| State | open or closed |
| Labels | Comma-separated issue labels |
| Assignees | Comma-separated assignee logins |
| Updated | Relative update time |
An empty, unfiltered open issue backlog prints one randomly selected
celebration from the shared pool. State, search, author, assignee, milestone,
and label filters keep the factual No issues found. message.
gh x project (alias p) groups open issues by a GitHub Project v2's Status
field, in its option order. Archived project items do not count as active
membership. Issues outside the selected project or without a
Status appear under No status. Empty Status groups remain visible in the
count header. The default repository resolves from the checkout or GH_REPO,
as with status; -R overrides it.
gh x project
gh x p --project my-org/1
gh x p --closed-days 14
gh x p --closed-days 0 # hide recently closed
gh x p --group-by label # force label grouping
gh x p --group-label watching --group-label chief-todo
gh x p --json | jq .schemaVersion
# Optional shell alias: alias gp='gh x p'When several projects contain open issues, the default is the project with the
most of those issues (ties use owner, then project number). --project OWNER/N
selects one explicitly, including a project with no open repository issues.
Project access requires read:project; the broader project scope includes it.
Without that permission the command prints one scope hint and groups by label.
With no project it also uses labels. GitHub can hide inaccessible projects as
not found; a missing or hidden selected project uses label fallback with one
hint to check the selector or project access. Malformed selectors and network
failures report errors.
Label mode uses each issue's first label, or No label. Repeat --group-label
to prefer named labels in that order, falling back to the first label when none
matches. The grouping label is omitted from the row's other labels. Label groups
sort by name; issues within each group sort oldest first. Age means time since
creation, and Updated means time since the last update. The closed section sorts
by closure time, newest first, and defaults to the last seven days. Memberships
are queried in batches of 30 issues, with additional pages fetched
for each issue as needed. The operation uses the standard 30-second GitHub timeout
(GH_X_GITHUB_TIMEOUT overrides it). All issue pages are fetched; pull requests returned by the
REST issues endpoint are excluded.
Linked PRs include closed and merged references. Unknown linked PRs or sub-issue
counts display ? with an explanatory hint.
Example (synthetic issues):
Repository example/loops
Project Open loops · https://github.com/orgs/example/projects/1
Waiting on Franz 1 · Chief todo 1 · Watching 0 · Done 0
Waiting on Franz (1)
Issue Title Age Updated Labels PRs Sub
#2 Approve release plan 2d 1h area:dev #8 2/5
Chief todo (1)
Issue Title Age Updated Labels PRs Sub
#1 Prepare weekly brief 3d 2h area:dev 0/0
Recently closed (last 7 days) (1)
Issue Title Age Updated Labels PRs Sub
#3 Review old loops 4d 3h 0/0
--json writes one document, without the banner, update notice, colour or
terminal clearing. Non-TTY text also has no colour and never clears. Argument
and acquisition failures write {"schemaVersion":1,"error":"message"} and exit
nonzero. Output-write failures also exit nonzero. JSON timestamps use UTC.
This is a separate schema from status JSON.
gh x status and its schema version 1 are unchanged.
{
"schemaVersion": 1,
"repository": "example/loops",
"project": null,
"grouping": "label",
"groups": [{"id": "label:watching", "name": "watching", "count": 1, "issues": [{
"number": 2, "title": "Approve release plan",
"url": "https://github.com/example/loops/issues/2",
"createdAt": "2026-10-19T12:00:00Z",
"updatedAt": "2026-10-21T11:00:00Z", "closedAt": null,
"labels": ["area:dev"],
"pullRequests": [{"number": 8, "url": "https://github.com/example/loops/pull/8"}],
"subIssues": {"completed": 2, "total": 5}
}]}],
"recentlyClosed": [], "closedDays": 7, "hints": []
}project is null in label mode, otherwise an object with id, owner,
number, title, url, and ordered statusOptions (id, name). groups
contains id, name, count, and issues. Group IDs distinguish Status
options and label names from unassigned groups, even when their displayed names
match (No label or No status). Conflicting fallback names gain an explanatory
suffix. Each issue has the fields shown above;
labels excludes the grouping label in label mode. recentlyClosed uses the
same issue shape and preserves all labels. closedAt is null for open issues;
subIssues is null when unavailable. pullRequests is an empty array when
there are none and null when unavailable. Other collection fields are arrays,
including when empty. hints contains any scope or enrichment notices that
would appear in text output. closedDays records the requested window.
In an interactive terminal, status shows local Git results while GitHub data is
loading, then adds completed sections. A rotating cursor names the pending work.
Worktree cleanup stays pending until its evidence is available. Long previews
keep the header and newest results visible; the full compact output prints when
the command finishes. Ctrl-C removes the indicator and restores the cursor.
NO_COLOR removes color while keeping loading feedback. Pipes, redirected
output, and TERM=dumb receive a single snapshot without animation.
The preview adapts to terminal resizing and keeps account fallback notices
above its results. Windows consoles enable virtual-terminal processing for the
animation and restore their previous mode afterward. Consoles without that
support receive a single snapshot.
gh x status starts with a repository health header for the resolved default
branch and current worktree. Each invocation shows the current local date and
time as YYYY-MM-DD hh:mm AM/PM ZONE, for example 2026-09-05 12:32 PM EDT.
The zero-padded 12-hour clock and zone abbreviation follow the machine's local
time zone, including daylight saving time. The timestamp also appears in plain
redirected output and when GitHub sections are unavailable.
The compact header aligns branch, worktree, and stash inventories beneath the
default branch. It always reports stashes, including 0 stashes, and marks each
health row with a green ✓ only when its clean-state rule passes:
- Default branch: checked out, synced with an existing upstream, and clean.
- Branches: 1 local branch, 0 dangling branches, and 1 remote-tracking branch.
- Worktrees: 1 total worktree and 0 cleanup candidates.
- Stashes: 0 stashes.
The default-branch check uses that branch's own worktree, even when the current feature worktree has changes. Missing or unavailable default-branch status and a default branch that is not checked out receive no check.
Other inventory values appear in yellow without a check. No-color output keeps the checks and alignment. Stash counts come from the shared repository inventory on every invocation, including from linked worktrees; an inspection failure shows an unavailable message without a check.
The header counts local, remote, and dangling branches, then reports linked worktrees and conservative cleanup candidates. A linked worktree is suggested only when it is unlocked, clean, merged into the default branch, and has no open pull request. Git-prunable records must also have a branch or detached commit known to be merged. Current, primary, locked, and default-branch worktrees are never suggested. Suggestions are informational; the command never deletes or prunes a worktree.
Open issues, enriched open pull requests, the five most recently merged pull
requests, and the five most recent workflow runs appear in separate tables
below the header. Use --merged=N to change the merged pull request count.
--merged=0 hides that section and skips its fetch. Merged pull requests are
ordered by merge time, newest first, and use the same enriched table as open
pull requests. The repository API returns existing merges even when the remote
retains an old repository name after a move. Workflow runs keep the same
status, title, workflow, branch, event, linked ID, elapsed-time, and age columns
as gh x run list. Local Git status still renders
when GitHub data is unavailable. When supplemental pull request data or issue
relationships are unavailable, the affected section prints the same
unavailable diagnostic line like the ones gh x pr list prints while
healthy rows keep their data.
When either unfiltered open-work section is empty, it prints one randomly
selected celebration from the same pool used by the list commands.
When all five recent workflow runs completed successfully, status prints one
randomly selected perfection message from a workflow-specific pool.
gh x status # show the five most recently merged pull requests
gh x status --merged=10 # show up to ten
gh x status --merged=0 # hide recently merged pull requests
gh x status --refresh # bypass cached GitHub dataSuccessful GitHub-backed status sections are cached for 60 seconds under
$GIT_COMMON_DIR/gh-x/status-cache-v5. Linked worktrees share that directory.
The cache is isolated by effective remote configuration (including
gh repo set-default and URL rewrites), the active branch's tracking remote,
GitHub authentication context, GH_REPO, GH_HOST, --merged, and color mode.
Environment and credential-store tokens affect the fingerprint;
no tokens or authentication files are stored in snapshots. Local branches,
worktrees, stashes, cleanup candidates, working-tree changes, and relative time labels
are updated on every run.
The hosted default branch is cached when no local remote HEAD is available.
--refresh bypasses cached sections and replaces the snapshot, including any
failures. Failed sections retain their error/unknown display and retry after
five seconds while healthy sections keep their original freshness deadlines.
Rate-limit failures wait at least 60 seconds. Expired sections refresh separately;
corrupt and incompatible snapshots are ignored.
Automatic release checks cache successful results for one hour; gh x version
forces a new check. See the status freshness guide
for authentication isolation, cooldowns and safe cache diagnostics.
gh x status --json writes exactly one JSON object to stdout. It uses the same
collection, cache, enrichment and health rules as the terminal view. It skips
animation, screen clearing, the banner and update checks, including with
--clear. gh x s --json is equivalent. --merged and --refresh still apply.
gh x status --json | jq .schemaVersion
gh x status --json --refresh --merged=0Example with empty GitHub lists and the merged section disabled. This example omits worktree items for space; real output includes every worktree:
{
"schemaVersion": 1,
"generatedAt": "2026-10-21T02:30:00-04:00",
"repository": {
"nameWithOwner": "hemsoft-dev/gh-x",
"rootPath": "/home/franz/github/hemsoft/gh-x",
"defaultBranch": "main"
},
"health": {
"defaultBranch": {
"status": "available", "reason": "", "clean": true,
"name": "main", "checkedOut": true, "upstream": "origin/main",
"synced": true, "ahead": 0, "behind": 0,
"changes": {
"clean": true, "staged": 0, "modified": 0, "deleted": 0,
"renamed": 0, "untracked": 0, "conflicted": 0
}
},
"currentBranch": {
"status": "available", "reason": "", "clean": true,
"name": "main", "checkedOut": true, "upstream": "origin/main",
"synced": true, "ahead": 0, "behind": 0,
"changes": {
"clean": true, "staged": 0, "modified": 0, "deleted": 0,
"renamed": 0, "untracked": 0, "conflicted": 0
}
},
"branches": {
"status": "available", "reason": "", "clean": true,
"local": 1, "remote": 1, "dangling": 0
},
"worktrees": {
"status": "available", "reason": "", "clean": true,
"total": 1, "cleanupCandidates": 0, "items": []
},
"stashes": {"status": "available", "reason": "", "clean": true, "count": 0}
},
"issues": [],
"pullRequests": {"open": [], "merged": [], "mergedLimit": 0},
"workflowRuns": [],
"workflowRunsPerfect": false,
"sections": [
{"name": "issues", "status": "available", "reason": "", "cached": false,
"fetchedAt": "2026-10-21T02:30:00-04:00", "cacheAgeSeconds": 0},
{"name": "pullRequests.open", "status": "available", "reason": "", "cached": false,
"fetchedAt": "2026-10-21T02:30:00-04:00", "cacheAgeSeconds": 0},
{"name": "pullRequests.merged", "status": "disabled", "reason": "", "cached": false,
"fetchedAt": null, "cacheAgeSeconds": 0},
{"name": "workflowRuns", "status": "available", "reason": "", "cached": false,
"fetchedAt": "2026-10-21T02:30:00-04:00", "cacheAgeSeconds": 0}
]
}schemaVersion: 1 is the public contract version, independent of the internal
cache version. Consumers should ignore unknown fields. Adding fields is
compatible; removing fields or changing their types or meanings requires a new
schema version. Arrays are always arrays, including when empty. Unknown optional
timestamps and aiClean are null. Timestamps use RFC 3339; producers emit UTC.
The example uses an Eastern offset for readability.
GitHub failures do not fail the command. The matching sections entry becomes
unavailable with a reason; local health and other lists remain available.
Enrichment failures also make that section unavailable while retaining its rows.
Read the section status before treating an empty list as an empty backlog.
Fatal local collection errors, including a non-Git directory, return a nonzero
exit code with {"schemaVersion":1,"generatedAt":"...","error":{"code":"status_failed","message":"..."}}.
Diagnostics may appear on stderr; stdout contains only the document.
See the version 1 field reference for every field, cache provenance, display values and failure semantics.
Compared to gh pr list, this command keeps all existing filters but renders a denser, color-coded table:
| Column | Description |
|---|---|
| # | PR number — clickable link to the PR on GitHub (terminals with OSC 8 support) |
| Issues | Issues linked through GitHub's closing relationship. - means none and ? means relationship data was unavailable |
| Title | Truncated to 51 chars |
| Author | PR author login |
| State | open, draft, closed, or merged |
| Rev | Overall review decision: ✓ approved, ✗ changes requested, or • review required |
| AI | AI reviewer status: pass (approved/no issues), fail (issues found), ? (review data unavailable), or - (no AI review). Detects CodeRabbit, Copilot PR reviewer, connected Codex, other [bot] reviewers, and Cubic's AI reviewer check. Copilot follows the severities its current-head summary lists, whatever its heading says: any open or previously missed Medium or High finding fails, even when the finding's thread is resolved or another bot reviews later, while Low findings or none pass. An unresolved Copilot thread also fails unless Copilot listed it as Low. When Copilot reviewed last, a summary gh-x can't read fails. A truncated review history stays ? unless the newest window establishes Copilot's latest current-head verdict; another bot's approval or a newer Codex receipt alone cannot establish it |
| Appv | Count of unique formal approvals, including bot reviewers |
| Checks | CI status: pass, review, fail, pending, merge, or -. review (green) means every non-review check passed and only a recognized AI reviewer check remains. merge (red) indicates merge conflicts with the base branch. Includes required checks from repo rulesets that haven't reported yet |
| Cmts | Review thread resolution: resolved/total (e.g., 3/5). A trailing ! marks a clean AI verdict with no unresolved AI threads; for a Copilot verdict that needs Approval recommended or a formal approval, and no open or previously missed findings, so a pass can appear without it. - means no threads, and ? means thread data was unavailable |
| Branch | Head branch name |
| Upd | Relative time: 12m, 3h, 2d, 4mo |
An open or draft pull request with formal approvals adds one green detail line
per unique approver beneath its table row. Each line shows the approver login,
the latest active approval in local time with its time-zone label, and the
relative age, for example @octocat approved 2026-09-22 09:15 AM EDT (2h ago).
The same details appear in the open pull request section of gh x status.
--json returns these records in the approvers array with login,
approvedAt, and age fields instead of adding formatted text to JSON output.
| Flag | Description |
|---|---|
-R, --repo OWNER/REPO |
Target a specific repository |
-L, --limit N |
Maximum PRs to show (default: 30) |
-s, --state STATE |
Filter: open, closed, merged, all |
-A, --author USER |
Filter by PR author |
-a, --assignee USER |
Filter by assignee |
--app APP |
Filter by GitHub App |
-B, --base BRANCH |
Filter by base branch |
-H, --head BRANCH |
Filter by head branch |
-l, --label LABEL |
Filter by label (repeatable) |
-S, --search QUERY |
GitHub search syntax |
-d, --draft |
Show only draft PRs |
-w, --web |
Open in browser |
--json |
Output as JSON |
gh x pr list
gh x pr list --author "@me" --state all
gh x pr list --repo owner/repo --limit 10
gh x pr list --label bug --label urgent
gh x pr list --search "review:required status:success"
gh x pr list --jsonAn empty, unfiltered open backlog prints one randomly selected celebration from
the shared pool. Narrowed or historical queries keep the factual
No pull requests found. message, and JSON output remains unchanged.
The relationship, AI, and comment columns depend on a supplemental GraphQL
query that runs after gh pr list. When GitHub cannot serve that query, the
affected rows show ? instead of guessing: a failed fetch never renders as an
empty relationship or a clean review. One pull request with a failed alias no
longer hides the data GitHub did supply for the rest of the batch; only the
failed aliases stay unknown.
Every affected table prints one diagnostic line explaining the reason, for
example gh api graphql: You have exceeded a secondary rate limit.... In
--json mode the stdout array stays machine-readable and the diagnostic goes
to stderr. The message carries gh's own error text, which contains request
results and standard CLI messages, not credentials.
A completed current-head AI review still decides the AI column on its own:
a review with an unresolved actionable finding reports fail even when other
enrichment is unknown, and pending CI keeps reporting pending independently
of supplemental enrichment. Connected Codex counts as clean only when its
Completed activity row names the current head and the connector adds a later
thumbs-up reaction to the pull request. That reaction does not count as a formal
approval.
gh x monitor is an interactive, read-only dashboard for pull requests and
issues across all repositories owned by the active account and its organizations.
“All repos” discovers those owners on every refresh, including repositories
absent from the configuration. Configured repositories are optional sidebar
shortcuts, each with its own query, counts and row limit. A busy repository
cannot crowd another shortcut out. The sidebar selects a repository in
terminals at least 100 columns wide; compact terminals show the repository in
the header instead. The top tabs switch between pull requests and issues,
configurable sections apply GitHub search filters, and the detail pane shows
the selected item's metadata and scrollable body.
Issue rows include the immediate parent and direct sub-issue progress. The
issue detail pane repeats both values.
gh x monitor # launch the dashboard
gh x m # short alias
gh x monitor --print-query # discover owners and print account-wide queriesThe first launch creates config.yml in the operating system's user config
directory. When possible, it seeds repos with the repository in the current
working directory.
| Platform | Configuration file |
|---|---|
| Windows | %AppData%\gh-x\config.yml |
| macOS | ~/Library/Application Support/gh-x/config.yml |
| Linux and other Unix systems | ${XDG_CONFIG_HOME:-$HOME/.config}/gh-x/config.yml |
The repository's src/cfg.yml is development data and is not read by
gh x monitor in production.
The dashboard supports terminals from 60×16 cells and adapts its columns to
80×24, 120×40, and larger windows. Compact tables keep titles and decision
fields readable; details retain the remaining fields. The monitor asks the
terminal for its background color and selects a dark or light palette. It
starts with the dark palette if the terminal does not answer. Set NO_COLOR=1
for text-only status cues, bold focus, and reverse selection. This preserves
alternate-screen, keyboard, and mouse behavior.
| Keys | Action |
|---|---|
tab / shift+tab |
Focus the list, details, or repository navigation. |
j / k, arrows, g / G |
Move within the focused pane or jump to its beginning/end. |
| Left / Right | Switch PR and issue tabs; select All open when configured. |
1–9 |
Select a configured section. Sections also support mouse clicks. |
/, Enter, Escape |
Type a local filter, apply it, then clear it. |
| Page Up / Page Down | Scroll the selected item's details. |
s, e, ?, r |
Open settings, edit YAML, show help, or refresh. |
o, y, Y |
Open the selected URL, copy it, or copy its checkout/development command. |
q, Ctrl+C |
Save selections, cancel any refresh, and restore the terminal. |
Mouse clicks select tabs, sections, rows, and panes; the wheel moves within the
focused pane. In compact layouts, focus repositories with tab and use j/k,
or click the header to cycle repositories. Settings mark their active field
with > and validate before saving. Selection remains visible when another
pane has focus. Long details scroll with a pinned title and position indicator.
The monitor design record explains the stack and layout.
version: 1
repos:
- hemsoft-dev/gh-x
- ghe.example.com/acme/platform
defaults:
limit: 30
interval: 10m
prSections:
- title: "Mine"
filters: "is:open author:@me"
- title: "Review requests"
filters: "is:open review-requested:@me"
limit: 20
- title: "All open"
filters: "is:open"
issueSections:
- title: "Assigned to me"
filters: "is:open assignee:@me"
- title: "All open"
filters: "is:open"| Field | Meaning |
|---|---|
version |
Configuration schema version. The current value is 1. |
repos |
Optional sidebar shortcuts as OWNER/REPO entries. Use HOST/OWNER/REPO for GitHub Enterprise Server. An empty list still supports All repos. |
defaults.limit |
Default rows fetched per section, from 1 to 100. The generated default is 30. |
defaults.interval |
Auto-refresh cadence as a Go duration, from 10s to 6h. The generated default is 10m. |
prSections |
Pull request sub-tabs. Each entry needs title and filters; optional limit from 1 to 100 overrides the default. |
issueSections |
Issue sub-tabs with the same title, filters, and optional limit fields. |
Section filters use GitHub search syntax and apply to both the account-wide
view and sidebar shortcuts. All repos adds the active account and every
organization returned by that host’s authenticated GitHub account, with
pagination. Shortcut queries add their configured repository scope. Section
filters cannot contain repo:, user:, or org: qualifiers: GitHub's classic
search unions those qualifiers rather than narrowing the monitor's scope. GitHub.com
repositories use the compact OWNER/REPO form.
Enterprise repositories keep their host prefix and are queried through that
host's authenticated gh account. If no shortcuts are configured, the monitor
uses GH_HOST or github.com. Owner discovery failures report an error and
retain the last account-wide snapshot while successful shortcuts remain
available. Failed shortcut queries also retain their own last snapshot and
show an unavailable state. All repos never falls back to a configured selection.
Each section still obeys its row limit; the footer shows loaded and matching
counts. If either section list is empty or omitted,
the monitor restores its generated default sections.
Press s to edit repositories, the default row limit, and the refresh interval
inside the dashboard. Press e to edit the complete YAML file with $VISUAL,
then $EDITOR, or the platform default editor. The monitor reloads the file
when the editor exits. A separate state.json in the same directory remembers
the selected tab, section, and repository; it is not a configuration source.
--print-query performs authenticated owner discovery before printing the
account-wide search documents. It does not fetch PRs or issues. Owner batches
contain at most 16 user and organization qualifiers, and multi-document output
labels each host and operation.
Every one-shot command bounds its complete GitHub operation, including account
discovery and fallback attempts, to 30 seconds. Set GH_X_GITHUB_TIMEOUT to a
positive Go duration to override it:
$env:GH_X_GITHUB_TIMEOUT = "45s"
gh x issue list --repo hemsoft-dev/gh-xEach monitor refresh has a separate 45-second total deadline controlled by
GH_X_MONITOR_REFRESH_TIMEOUT. Host queries run concurrently within that
single budget. If one host times out, data from successful hosts remains visible
with a short host-qualified warning. If the whole refresh times out, the monitor
clears its refreshing state, retains the last successful snapshot, and reports
the timeout. Quitting the monitor cancels and waits for the in-flight gh child
instead of leaving it running. Invalid, zero, or negative duration settings fail
with a configuration error; they never disable the deadline.
Your open PRs, authored or assigned, plus all open Dependabot PRs across every
repo in the selected organization or personal account. Dependabot PRs appear
even when they are unassigned. The combined results show each PR once, ordered
by most recent update, with --limit applied to the whole list.
# Title Repo Author State Rev AI Appv Checks Cmts Upd
#42 feat: add repo governance (CI lint, Cop... my-app jdoe open • fail 0 fail 0/1 2d
#15 fix: update auth token refresh logic api bsmith open • - 0 pass 3/3 5d
Works with both organizations and personal accounts.
| Flag | Description |
|---|---|
-o, --org ORG |
Organization or user to search (default: inferred from current repo) |
-L, --limit N |
Maximum PRs to show (default: 30) |
--json |
Output as JSON |
gh x pr me # my PRs and Dependabot PRs across current owner
gh x pr me --org AcmeCorp # the same selection in a specific org
gh x pr me --org octocat # the same selection in a personal account
gh x pr me --limit 10 # combined list capped at 10
gh x pr me --json # the same enriched results as JSONAn org-wide view of PRs that need your attention — no more checking each repo individually.
# Title Repo Author State Rev AI Appv Checks Cmts Upd
#42 feat: add repo governance (CI lint, Cop... my-app jdoe open • fail 0 fail 0/1 2d
#41 feat: add contract-testing for PactNet... my-app jdoe open • pass 0 pass 12/12 2d
By default, shows open PRs across the org that need your review: PRs requesting
your review, plus PRs by others that are assigned to you or that you have
reviewed, leaving out any you have already approved.
Use --authored to see open PRs you created instead, or --review-required to
see only PRs where your review is directly requested.
| Flag | Description |
|---|---|
-o, --org ORG |
Organization to search (default: inferred from current repo) |
-L, --limit N |
Maximum PRs to fetch per search (default: 30); the default view runs three searches |
-a, --authored |
Show open PRs you authored instead of PRs needing your review |
-r, --review-required |
Show only PRs where your review is directly requested |
--ready |
Show only PRs ready to merge (open, AI pass, checks pass, all comments resolved) |
--json |
Output as JSON |
gh x pr atm # PRs needing my review across current org
gh x pr atm --org AcmeCorp # PRs needing my review in a specific org
gh x pr atm --authored # my PRs across current org
gh x pr atm --review-required # only PRs where my review is directly requested
gh x pr atm --org AcmeCorp -r --limit 10 # review requests, capped
gh x pr atm --json # machine-readable outputRuns a PR review through an agentic CLI. The command resolves PR metadata with
gh pr view, builds a review prompt, and delegates analysis to a provider
preset. By default it prints the agent's read-only review locally. With
--post, it captures structured findings and posts one GitHub PR review with a
formal Markdown body and validated inline review comments.
Default provider is codex with model gpt-5.5, high reasoning effort, and
strict review mode. These are configurable with flags or environment
variables.
Approvals are opt-in. --allow-approve implies posting a review, and approval
is only selected in strict mode when the structured result is explicitly
approval-eligible and has no critical, medium, or nitpick findings. Critical
findings post as REQUEST_CHANGES; other findings post as COMMENT.
| Flag | Description |
|---|---|
-R, --repo OWNER/REPO |
Target a specific repository |
-a, --agent AGENT |
Provider preset: codex, claude, copilot, gemini, opencode, or custom |
--command COMMAND |
Custom command template for --agent custom |
-m, --model MODEL |
Model passed through to supported providers |
--effort EFFORT |
Reasoning effort for supported providers: low, medium, or high |
--mode MODE |
Review mode: strict, medium, or fast-lane |
--preset MODE |
Alias for --mode |
-B, --base BRANCH |
Override the base branch in the review prompt |
-i, --instructions TEXT |
Additional review instructions |
--instructions-file FILE |
Read additional instructions from a file |
--reviewer NAME |
Reviewer identity used in posted review reports |
--dry-run |
Print the resolved command and prompt without running the agent |
--post |
Post a GitHub PR review with inline comments |
--allow-approve |
Allow strict-mode approval when the review has no findings |
gh x pr review
gh x pr review 42 --mode strict
gh x pr review 42 --mode medium
gh x pr review 42 --mode fast-lane
gh x pr review 42 --post
gh x pr review 42 --post --allow-approve
gh x pr review 42 --agent claude --model sonnet
gh x pr review 42 --agent copilot
gh x pr review 42 --dry-run
GH_X_PR_REVIEW_AGENT=claude GH_X_PR_REVIEW_MODE=medium gh x pr review 42
GH_X_PR_REVIEW_POST=true GH_X_PR_REVIEW_ALLOW_APPROVE=true gh x pr review 42
gh x pr review 42 --agent custom --command 'my-reviewer --prompt "{prompt}"'Workflow run listing with clickable run IDs — ctrl-click any ID to open the run in your browser.
Title Workflow Branch Event ID Elapsed Age
✓ feat: add pr subcommand group for mul... Copilot Setup Steps v0.4.0 push 25714589506 10s 19m
✓ feat: add pr subcommand group for mul... Auto Release main push 25714553634 45s 20m
X feat: comprehensive quality improvem... CI Quality Gates feature/quality-imp... pull_request 25708704123 1m43s 3h
| Column | Description |
|---|---|
| Status | ✓ success, X failure, * in progress, ○ queued, ! cancelled |
| Title | Commit or PR title (truncated) |
| Workflow | Workflow name |
| Branch | Head branch |
| Event | Trigger: push, pull_request, dynamic, etc. |
| ID | Run ID — clickable link to the run on GitHub (OSC 8) |
| Elapsed | Run duration: 10s, 1m56s, 2h15m |
| Age | Time since created: 5m, 2h, 3d |
| Flag | Description |
|---|---|
-R, --repo OWNER/REPO |
Target a specific repository |
-L, --limit N |
Maximum runs to show (default: 20) |
-s, --status STATUS |
Filter: queued, in_progress, completed, success, failure, etc. |
-w, --workflow NAME |
Filter by workflow name |
-b, --branch BRANCH |
Filter by branch |
-e, --event EVENT |
Filter by event type |
-u, --user USER |
Filter by user who triggered the run |
gh x run list # recent runs
gh x run list --status failure # failed runs only
gh x run list --workflow "CI Quality Gates" # specific workflow
gh x run list --branch main --limit 10 # main branch, last 10
gh x run list --event pull_request # PR-triggered runsView release notes directly from the CLI:
gh x pr changelog # latest release
gh x pr changelog --limit 10 # last 10 releases
gh x pr changelog --version 0.3.0 # specific versionThe currently installed version is marked with ← installed.
gh x versiongh-x v0.1.2 © 2026 HemSoft Developments · gh extension install hemsoft-dev/gh-x
✓ Up to date
If a newer release exists:
gh-x v0.1.0 © 2026 HemSoft Developments · gh extension install hemsoft-dev/gh-x
↑ v0.1.2 available · gh extension upgrade gh-x
t3.json selects the gh-x SVG as this repository's project icon in T3 Code. The image is self-contained and uses a path relative to the checkout, so the configuration also works in worktrees. Leave the project's icon selection on Automatic to use it.
Requires Go 1.26.10 or newer. CI reads the exact patched version from
go.mod; local builds with the default GOTOOLCHAIN=auto setting download it
when the installed Go toolchain is older.
# Build and install locally (one-time symlink setup)
go build -o gh-x.exe ./src # Windows
go build -o gh-x ./src # macOS/Linux
gh extension install .
# After code changes, just rebuild — no reinstall needed
go build -o gh-x.exe ./src
gh x pr listA convenience script is provided for Windows:
.\build.ps1 # runs vet → test → buildRepository maintainers check the official Go release history
each Monday and before a release for patches to the supported Go line. Keep
go.mod as the single version pin used by CI and release builds. Update the pin
and the local development requirement together, noting the security fixes and
any compatibility decision in the pull request.
Qualify each patch with the local audit from a disposable checkout. Review the race tests, vulnerability report and all canonical release-target builds, then require green CI and a clean current-head review. Check the CI and release setup logs for the selected compiler version. Historical benchmark records keep the compiler version used for that measurement.
The credential scanning guide documents the pinned source/history checks, hosted push protection, safe reports and maintainer triage.
Run node benchmarks/run.mjs for offline synthetic rendering, monitor and Codex
snapshot benchmarks. The performance guide documents
the fixtures, versioned budgets, sampling policy and retained CI artifacts.
Dependabot checks Go modules and GitHub Actions every Monday at 09:00 Eastern.
Minor and patch updates are grouped by ecosystem, with at most two open version
update pull requests per ecosystem. Major-version updates are intentionally
deferred to dedicated migration pull requests. Compatibility-sensitive v0
minor updates receive the same manual review as a major update; dependency pull
requests are never auto-merged.
The repository includes launchers for the local Copilot CLI and Codex CLI usage dashboards:
.\run.ps1 # open the Copilot dashboard in the active Copilot Canvas
.\run-codex.ps1 # open the Codex dashboard in a standalone window
.\run-dashboard-hub.ps1 # open the combined local dashboard hubThe Codex dashboard reads ~\.codex\state_5.sqlite and local rollout JSONL
files in read-only mode. It shows recent sessions, token and cache totals,
requests, tool calls, context usage, runtimes, the latest weekly rate-limit
snapshot, and projected usage at reset based on the current pace. Changed
session rows glow after each refresh. The server listens only on 127.0.0.1
behind a random URL token. The Codex launcher starts that server in the
background and opens it in a dedicated Microsoft Edge or Google Chrome app
window. Later launches reuse the running local server. Both dashboard servers
reject malformed HTTP request targets with a 400 response and keep serving
healthy requests.
The Codex Canvas, standalone server and dashboard hub run snapshot readers in
child processes, with at most two readers per provider or Canvas instance.
Healthy readers stay warm to reuse their caches. A disconnected request or 30-second read deadline stops its
reader process; capacity is released only after that process exits. New
refreshes can then start even if an older reader ignored cancellation or
blocked its event loop. Server shutdown also stops active and idle readers. Each process keeps its IPC
control loop separate from the computation thread. If its parent dies, that
control loop terminates the whole reader process even when computation blocks.
Injected readers used by tests must honor the supplied AbortSignal; merely
abandoning their promise does not free capacity.
For phone access over Tailscale, install the persistent loopback-only hub once:
.\install-dashboard-hub.ps1The installer checks the current device's DNS name, IPv4 address and Serve
configuration before starting the hub or changing its logon task. It registers
HemSoft CLI Dashboard Hub, mounts /dashboards/ over HTTPS and adds a raw
TCP forward on tailnet port 80. It prints each URL only after that operation
succeeds. Use the printed HTTPS URL from another tailnet device, or the printed
HTTP fallback when that client cannot resolve MagicDNS. Neither route is
exposed to the LAN or public internet.
The task uses the same user's limited, noninteractive S4U logon. The installer starts the hub through that task, and the native launcher suppresses console creation for Node. Disabled tasks are preserved: installation refuses to update them. See the background launch contract for preparation, descendant handling, S4U access limits and deployment proof.
Each Tailscale operation must succeed within 30 seconds. A failed command names
the operation and exit code. A timeout reports unverified completion and keeps
captured stderr for diagnosis. If HTTPS succeeds but TCP fails, installation
returns a nonzero result and reports that HTTPS and the logon task remain.
Correct the named failure and rerun the installer. It never resets Serve or
deletes scheduled tasks during recovery. Unrelated mounts remain; conflicting
/dashboards, port 80 or non-HTTPS port 443 targets cause preflight to fail.
Active foreground sessions on those ports or this HTTPS host also fail
preflight. A matching TCP destination is accepted only as a plain forward;
TLS termination, PROXY protocol and unknown handler settings are not overwritten.
Extra settings on the requested HTTPS mount, including application-capability
forwarding, fail preflight rather than being lost. Unrelated mounts are untouched.
Port 443 with public Funnel access also fails preflight; the installer neither
publishes private usage through it nor disables unrelated public routes.
A same-named task is updated only when its sole action runs this exact launcher
under the current user. Use -TaskName to avoid an unrelated task's name.
Do not change Serve configuration concurrently with installation; preflight and
route writes are separate native operations, not an atomic transaction.
The hub reads Codex state directly and uses the installed copilot-spend
extension's read-only session-store adapter and current UI. Installer regressions
run offline in CI and in the local audit:
pwsh -NoProfile -File tests/installer/test-dashboard-hub.ps1These tests use a native command double and scheduler mocks. They never contact Tailscale services or register real tasks. Required CI checks run PowerShell 7 on Ubuntu and Windows PowerShell 5.1 on Windows. On Windows, the same fixtures can also exercise the Windows PowerShell 5.1 installer launcher:
pwsh -NoProfile -File tests/installer/test-dashboard-hub.ps1 `
-InstallerPowerShell "$env:WINDIR/System32/WindowsPowerShell/v1.0/powershell.exe"The same required Windows job installs Node from .node-version and runs the
standalone Codex dashboard launcher test under PowerShell 7:
node --test src/codex-dashboard/launcher.test.mjsBoth launcher tests must pass with zero skips on Windows. The integration test uses a temporary Codex home and a synthetic browser command, starts and reuses its test-owned server, then stops it and removes the temporary files. It never opens a real browser or reads personal session data. Linux retains the Windows lifecycle skip; local audit success on Linux cannot qualify this hosted check. The existing Windows PowerShell 5.1 installer checks remain required.
- Wraps
gh pr list --jsonfor core PR data and authentication - Makes a single GraphQL call for supplemental data (review threads, AI reviewer detection, comment counts)
- Fetches required status check contexts from repo rulesets to detect pending-but-unreported CI checks
- Uses termenv for color output, respecting
NO_COLORandCLICOLORconventions - SSH host aliases (e.g.,
github-work:org/repo) are handled gracefully viagh repo viewfallback
CI and the local audit enforce the same checked-in measurement limits. Run the complete local check set from a disposable checkout with PowerShell 7:
pwsh -NoProfile -File .agents/skills/perfection/scripts/perfection-audit.ps1The runner reports each gate as pass, fail or blocked and continues
independent checks after failures. A missing tool or unavailable GitHub access
blocks the affected gate, not unrelated work. Failed coverage collection blocks
coverage and CRAP instead of reusing an old profile. Any failed or blocked local
gate makes the audit exit nonzero.
All maintained production Go packages, including the hidden .github/scripts/... release
and review helpers, participate in build, vet, static analysis, complexity and
fresh coverage gates. The ./tests/behavior package runs separately without a
coverage profile. CRAP joins use full relative
file paths and function declaration lines, so helper files and methods with the
same names cannot borrow coverage from one another. No generated Go source is
tracked. *_test.go files are excluded from production complexity; no maintained
helper is excluded. Mutation testing retains its separately declared ./src scope.
The local set includes release-helper tests, all 12 release-target builds,
CLI behavior, dashboard tests, ruleset and changelog validation, mutation
fixtures, and performance tests and budgets. Local performance qualification
always measures the full workload rather than applying CI's changed-path filter.
The runner tests and .github/local-quality-gates.json inventory are checked in
CI; an unclassified new or removed CI step fails the inventory test.
Install Go from go.mod, Node from .node-version, PowerShell 7, Git, GitHub CLI, Bash with its
standard Unix utilities, and the pinned analyzers from .github/quality-tools.env.
Race tests require Go's supported C compiler setup. Git Bash supplies the shell
utilities on Windows; CI runs these checks on Ubuntu. Changelog validation needs
read access to this repository's releases through gh. The runner does not
install Go analyzers or modify GitHub. Markdown lint uses the pinned npx command.
Install the locked JavaScript development tools with node .github/scripts/dashboard-quality/install.mjs
before running the audit.
A JSON report and performance evidence are retained in a unique system temporary
directory, printed at completion. Use -ReportPath <existing-directory>/audit.json
to save the report elsewhere. Build outputs and the coverage profile are removed
from that temporary directory; no generated output is placed in the checkout.
Local success does not replace GitHub qualification. The report separately lists
CodeQL setup/analysis, PR dependency review, current-head Codex review and the
required aggregate Quality Gate as hosted checks not run locally. The audit
never uploads source, requests reviews, posts comments or dispatches workflows.
Dashboard JavaScript also has an all-source coverage and per-function complexity gate, with reviewed aggregate/per-module coverage floors and worst-function CRAP ceilings. CRAP above 30 fails; scores from 15 through 30 form the review queue. The dashboard quality policy documents exact tools, scope, source-range joins, zero-coverage entry modules, baselines and retained failure reports. These JavaScript limits are separate from the Go limits below.
| Gate | Limit |
|---|---|
| Statement coverage | at least 70% |
| Cyclomatic complexity | at most 10 per function |
| Cognitive complexity | at most 15 per function |
| CRAP score | below 30 per function |
| Mutation efficacy | at least 90% |
| Mutator coverage | at least 90% |
The mutation gate runs Gremlins v0.6.0 against ./src:
bash .github/scripts/run-mutation-gate.shGremlins v0.6.0 derives a mutant's test package from the Go package name.
Every file in src declares package main, so Gremlins tests the module root
instead. That go test fails during setup, and Gremlins counts its exit code 1
as a kill. Results before #264 (100% efficacy in about 10 seconds) came from
that failure and tested no mutant. The gate now puts a small go stand-in,
.github/scripts/mutation-go-shim, first on PATH. For each mutant it runs
go test -json -vet=off on the scope's own packages and records the run:
- a named failing test or an evidenced runtime panic kills the mutant; passing tests leave it alive;
- a mutant that fails with a compiler error is not viable and scores neither way;
- a setup or resolution failure, any other build failure, or a run where no test ran is unproven. A resource kill, test-process timeout, or unexplained package failure is also unproven and cannot inflate efficacy.
The gate fails on any unproven run, on a mutation Gremlins could not apply or restore, or when Gremlins' killed, lived or not-viable count differs from the recorded runs. It then applies the floors. Its log shows those counts, the timed-out count and per-mutant test time.
Each mutant recompiles package main. The gate keeps a private build cache,
warmed before the run and pruned after each mutant, so a run stays near
150 MB instead of adding about 19 MB per mutant to the shared Go cache. It
starts one Gremlins worker per two CPUs (MUTATION_WORKERS overrides this),
because one per CPU pushed real runs past Gremlins' timeout and reported
mutants that would have lived as timed out. TMPDIR needs room for one module
copy per worker.
The measured full-scope candidate (2026-11-05, Linux, 12 CPUs, 3 workers) took 74 minutes: 2,456 killed, 114 lived, 241 not viable, 11 timed out and 152 not covered. Efficacy was 95.56% and mutator coverage was 94.42%, with zero unproven runs or mutation apply/restore errors. Of the kills, 2,447 named a failing test and 9 retained a runtime panic in the record. Mean per-mutant test time was 4.6 seconds. Both 90% floors remain unchanged.
The new boundary and state tests include three bounded flag-loop controls. Each calls the real parser in a child with a 20-second deadline and, on Linux, an address-space limit of its current size plus 512 MB. A reversed loop index therefore fails a named assertion before exhausting the runner. These address-space controls skip off Linux. The measured mutation run used a private 6 GB memory scope; it does not establish a hosted-runner memory limit. Hosted runtime and memory behavior remain to be measured in CI.
The tool version, thresholds, and package scope live in
.github/quality-tools.env. Raise a mutation floor only after the complete
Perfection audit establishes a stable higher baseline. Update that file, the
mutation fixture expectations, this table, and the Perfection examples in the
same change. Never lower a floor to make a failing change pass.
Every qualifying push to main runs the CI quality workflow. After all gates
pass, the release workflow examines every commit since the latest reachable
semantic-version tag and applies the highest required bump. The next version
starts from the highest semantic-version tag anywhere in the repository, so a
tag on another history cannot be reused:
- Subjects using
type!:ortype(scope)!:, or commit bodies with aBREAKING CHANGE:orBREAKING-CHANGE:footer, produce a major release. feat:and scopedfeat(scope):subjects produce a minor release.- Every other subject produces a patch release.
The workflow skips a commit that already has a semantic-version tag. Changes
limited to Markdown, .agents/**, or LICENSE do not start a release. GitHub
Releases is the authoritative version history. After publishing, the workflow
opens a changelog update and enables squash auto-merge for its exact head.
Only same-repository github-actions[bot] PRs targeting main, using a
chore/changelog-X.Y.Z branch, and modifying only CHANGELOG.md qualify.
Every pull request to main, including Dependabot updates and generated
changelogs, must pass the read-only Current-head Codex Review job before the
required Quality Gate succeeds. Ordinary pull requests are bound by event
number, branch, full head SHA, and ordered pull-request history. The job runs
the trusted default-branch helper and accepts only clean evidence attributable
to that exact head. Missing, stale, truncated, ambiguous, refused, superseded,
or unresolved evidence cannot pass.
.github/release-targets.json is the canonical release matrix. Pull-request CI
compiles every entry into a disposable directory before Build & Test can pass;
it does not upload those files. Auto Release uses the same manifest and builder
to create the published assets.
| Operating system | Architectures | Assets |
|---|---|---|
| Darwin | amd64, arm64 | darwin-amd64, darwin-arm64 |
| FreeBSD | 386, amd64, arm64 | freebsd-386, freebsd-amd64, freebsd-arm64 |
| Linux | 386, amd64, arm, arm64 | linux-386, linux-amd64, linux-arm, linux-arm64 |
| Windows | 386, amd64, arm64 | windows-386.exe, windows-amd64.exe, windows-arm64.exe |
Reproduce the cross-build locally from the repository root:
$env:RELEASE_VERSION = "dev"
$env:RELEASE_BUILD_DATE = git show -s --format=%cs HEAD
$env:RELEASE_OUTPUT_DIR = Join-Path $env:TEMP "gh-x-release-targets"
go run ./.github/scripts/release-targets buildEach log line identifies the exact GOOS/GOARCH target and output asset. A
failed compilation names that target and stops the quality gate. To change the
matrix, edit only .github/release-targets.json, update the expected list in
.github/scripts/release-targets/main_test.go, and run that package's tests.
Validation rejects empty, duplicate, malformed, unknown-field, and
Go-toolchain-unsupported entries. A tagged retry for a commit that predates the
canonical manifest uses the frozen .github/release-targets-legacy.json
snapshot; current CI and releases never use that compatibility file.
Every published binary carries GitHub build provenance created from the same trusted release job before asset publication. The attestation binds its SHA-256 digest to this repository, the release workflow, and the validated source commit. The initial attestation covers the complete build before any upload, so an interrupted publication can upload a missing, already-attested asset without signing it under a later workflow context. A retry verifies every rebuilt asset against the repository, trusted workflow, original source commit, and SLSA predicate before continuing. It runs that reconciliation with the helper from the immutable trusted workflow revision, never the older released checkout. Existing release assets are never replaced: a retry reuses bytes with a matching digest, uploads only missing attested assets, and fails closed on a digest mismatch or unexpected asset.
After downloading an asset produced after the organization transfer, verify it with this command:
$releaseTag = '<post-transfer-release-tag>'
$sourceCommit = gh release view $releaseTag --repo hemsoft-dev/gh-x `
--json targetCommitish --jq .targetCommitish
gh attestation verify .\windows-amd64.exe `
--repo hemsoft-dev/gh-x `
--signer-workflow hemsoft-dev/gh-x/.github/workflows/auto-release.yml `
--source-digest $sourceCommit `
--predicate-type https://slsa.dev/provenance/v1Replace the release tag and asset path with the downloaded release. The command
must exit successfully and name hemsoft-dev/gh-x, the trusted release workflow,
and the expected source commit. To verify all downloaded assets, run
the same command once per file. Pre-transfer assets keep their original signing
identity: use HemSoft/gh-x for --repo and
HemSoft/gh-x/.github/workflows/auto-release.yml for --signer-workflow
when verifying those historical assets. Keep release download commands
canonical. See the exact options and output fields on the
gh attestation verify manual page.
Release retries verify the canonical identity first. A pre-transfer certificate
may use the original HemSoft/gh-x signer, but is accepted only when its verified
certificate identifies the same immutable repository ID, 1262580000. An
unrelated repository that later occupies the old name cannot satisfy that check.
Generated changelog reviews retain stricter bot-author, same-repository, branch-name, and CHANGELOG-only checks on both pull-request and manually dispatched CI runs. The trusted release workflow requests connected Codex only. Cubic and other optional products are not requested or awaited; their actionable findings and unresolved conversations still require assessment.
Connect the HemSoft GitHub account to Codex and enable repository code review,
as described in OpenAI's GitHub guide.
Configure the repository Actions secret CODEX_REVIEW_TOKEN with that user's
GitHub token, granting pull-request write and contents read/write access. A
fine-grained token also needs repository metadata read access. The helper
requires the repository push permission before allowing a new request.
The helper checks GET /user and repository access before posting. This proves
GitHub identity and access; Codex's response proves the account connection.
The default GITHUB_TOKEN and installation tokens do not inherit that connection.
Missing credentials or an unsupported identity produce an actionable error.
The user token is supplied only to the review-request command. Release creation
and guarded merge retain the workflow's bot token.
Auto Release is the sole automated request owner for generated changelogs,
serialized by its existing non-canceling concurrency group. Request comments
persist the head, author, URL, and GitHub creation time. Verification jobs never
post requests. Ordinary pull requests reuse automatic or manual Codex activity;
a later HemSoft @codex review comment is bound to the full head through the
ordered pull-request timeline. Duplicate pull-request events replace an older
verifier for the same pull request/head and reuse the original 30-minute
review deadline. Auto Release's manually dispatched verifier uses a run-specific
concurrency lane, so pull-request, edited, and replacement-head events cannot
cancel the exact run that the release job awaits. Before any
current-head activity exists, the ten-minute setup window starts from the later of
the head commit and the triggering pull-request update, so old branches and
newly ready drafts receive one fresh window without a check rerun resetting it.
Refusal comments persist terminal access, quota, plan, or configuration failures;
reruns report the same failure immediately with the response URL and correction.
A timeout derives from the original request time, so reruns cannot reset it.
Auto Release also preserves the remote changelog head when its content is
unchanged, including after main advances. Only changed notes create a new head.
After correcting a refused bot request, a connected HemSoft session can run the
helper from a reviewed checkout. Do not run a manual request concurrently with
Auto Release; it owns automated requests. In PowerShell:
$env:GITHUB_REPOSITORY = 'hemsoft-dev/gh-x'
$env:CHANGELOG_BRANCH = 'chore/changelog-0.11.8'
$env:EXPECTED_HEAD = '<exact 40-character PR head>'
gh api user --jq .login
go run ./.github/scripts/changelog-merge request
go run ./.github/scripts/changelog-merge reviewThe request command reuses existing current-head evidence. It permits one human
replacement of an explicitly refused bot request. Re-requesting after a human
request's refusal or timeout requires an explicit access repair and manual review
decision; automatic reruns never retrigger it. A timeout ends waiting, not the
validity of subsequently available review evidence. A genuine later clean receipt
for the same head can pass verification without another trigger or wait, including
after an old bot refusal. Silence, timeout, or refusal alone can never pass.
Never remove comments or create empty commits to reset a wait.
For an ordinary pull request, rerun the failed checks from the pull request after
clean evidence appears; this reuses the unchanged head without another request.
For a generated changelog, rerun CI with
gh workflow run ci.yml --ref <branch> -f changelog_branch=<branch> -f changelog_head=<exact-40-character-head>
after clean evidence appears.
The enable command waits for that CI gate and revalidates evidence before it can
queue a merge; request and review never enable auto-merge.
Auto Release captures the dispatched CI run ID using the versioned GitHub API,
waits for that exact run to succeed, verifies its head, and requires exactly one
successful Quality Gate job in that run before calling enable. Failure output
identifies the pull request, expected head, authoritative run ID, and a clickable
URL for that exact Actions run. An older failed check or competing pull-request
event cannot cancel or satisfy that run.
To verify a candidate helper revision against an existing bot PR before deployment,
dispatch CI from the reviewed candidate branch with changelog_branch and
changelog_head inputs. It checks that live PR using a read-only token and the
explicit workflow revision. Normal PR and changelog-branch CI use the trusted
default-branch helper. A changelog-branch run cannot override its own target.
The candidate run's check belongs to the candidate SHA, so it does not replace
the changelog PR's required check. Its final Quality Gate deliberately fails
when the candidate ref is neither main nor a generated changelog branch;
inspect Current-head Codex Review for candidate-helper evidence. Repeat the
dispatch to verify evidence reuse without another comment or review charge.
Repository auto-merge must be enabled. The release workflow rechecks reviews
immediately before queueing and every 30 seconds while queued. Changed or
unavailable evidence, a changed head, or an expired monitoring deadline causes
it to withdraw auto-merge. A withdrawal failure is reported explicitly.
The release workflow waits for the PR
to actually merge before deleting its exact head branch and dispatching CI
on main. It does not bypass branch protection. A failed release run resumes that update
from the existing GitHub Release and current main, even when main has
advanced; a tag without a release remains a skip. CI also rejects stale release
links in CHANGELOG.md. After the bot merge, the workflow dispatches CI on
main so intervening product changes remain eligible for release. Each
created release includes prebuilt binaries for the supported operating systems
and architectures.