Skip to content

pr-triage as one Go binary, plus a vendable skill - #5

Merged
aarora79 merged 2 commits into
mainfrom
sample/pr-triage-go
Sep 25, 2026
Merged

aarora79 merged 2 commits into
mainfrom
sample/pr-triage-go

Conversation

@aarora79

Copy link
Copy Markdown
Owner

One static executable does both halves of pr-triage: it fetches pull requests from the GitHub API and asks Jev eleven questions about each one. A machine with no Python can sort a review queue from a single file.

GitHub Enterprise Server

The API base is a variable rather than a glued-together path, so GHES needs no patch. Three ways to set it, highest precedence first:

pr-triage owner/repo -api-base https://ghe.example.com/api/v3
pr-triage https://ghe.example.com/owner/repo          # from the URL host
export GITHUB_API_URL=https://ghe.example.com/api/v3  # what Actions already sets

The progress line names the API it chose, so a wrong guess surfaces before any call.

Credentials

Each reads the flag first and the environment second.

What Flag Environment
GitHub -token GITHUB_TOKEN, GH_TOKEN, GH_ENTERPRISE_TOKEN, then gh auth token
TypeSafe -jev-key TYPESAFE_API_KEY

-fetch-only wants the GitHub token and no TypeSafe key; -dataset wants the reverse. The half that reads your source and the half that calls an external API can hold different credentials and run in different places.

vend/

A SKILL.md telling an agent when to reach for a triage and how to read one, plus an installer that fetches the binary and drops the skill beside it. It checks for both credentials and names the missing one rather than failing halfway through a run.

curl -fsSL https://raw.githubusercontent.com/aarora79/jev-samples/main/samples/pr-triage/vend/install.sh | sh

Faithful to the Python

Same eleven questions, weights, tier cuts, 0.02 deadband and size floor. Datasets and reports match field for field, so a file written by either tool reads in the other. build.sh refreshes the embedded questions.yml from the canonical copy and payload_test.go fails when they drift.

Verified live

  • A fetch of apache/airflow by owner/repo, token from the gh CLI, dataset keys matching the Python's field for field
  • An 18 pull request replay returning the same tier counts as the Python: 3 high, 5 medium, 8 low, 2 trivial
  • -explain printing all eleven questions, its contribution column summing to exactly the 0.3847 load it reported
  • GHES resolution all three ways, and -api-base winning over a URL host
  • -fail-on-tier low exiting 2 and naming the pull request; -fail-on-tier high exiting 0
  • gofmt, go vet, go test clean; ./build.sh 0.1.0 cross-compiles linux, darwin and windows on amd64 and arm64 with SHA256SUMS

Stripped and static, each binary is about 7 MB against 10.4 MB for a plain build. The five platform builds together come to 35 MB, and a user downloads one.

Bugs

Two fixed in this port: Go's flag package stops parsing at the first non-flag argument, so pr-triage owner/repo -all silently ignored -all, and main was overriding a gate's exit code with the generic 1.

One left alone, in the Python payload. design_decisions level 0 reads - None: the same edit repeated, or a value changed, which is YAML for a mapping rather than a string, so Jev receives an object as that rubric level and echoes it back in the legend. It is what made the first replay fail. The Go side now reads a legend value as any type, so a released binary keeps working against an old payload. Quoting the string would fix it properly and would move every recorded number, so it wants its own change.

After merging

Cut pr-triage/0.1.0 and attach go/dist/*. The version line is per tool, since tags are namespaced (agents-md-readiness/0.2.0), and install.sh here resolves the newest pr-triage/ tag.

One static executable does both halves: it fetches pull requests from the
GitHub API and asks Jev eleven questions about each one, so nothing has to
run before it. A machine with no Python can sort a review queue from one
file.

GitHub Enterprise Server works without a patch, because the API base is a
variable rather than a glued-together path. Three ways to set it, highest
precedence first: -api-base, the host of a URL passed as the repository, or
GITHUB_API_URL, which is what GitHub Actions already exports on both
github.com and GHES. The progress line names the API it chose, so a wrong
guess surfaces before any call.

Credentials each read the flag first and the environment second. GitHub
takes -token, GITHUB_TOKEN, GH_TOKEN, GH_ENTERPRISE_TOKEN or the gh CLI.
TypeSafe takes -jev-key or TYPESAFE_API_KEY. -fetch-only wants the GitHub
token and no TypeSafe key, and -dataset wants the reverse, so the half that
reads your source and the half that calls an external API can hold
different credentials and run in different places.

vend/ makes it adoptable: a SKILL.md telling an agent when to reach for a
triage and how to read one, and an installer that fetches the binary and
drops the skill beside it. It checks for a GitHub and a TypeSafe credential
and names the missing one rather than failing halfway through a run.

The judgment is unchanged from the Python: the same eleven questions, the
same weights, the same tier cuts, the same 0.02 deadband, the same size
floor. Datasets and reports match field for field, so a file written by
either tool reads in the other.

Verified live:
- A fetch of apache/airflow by owner/repo, token from the gh CLI, and a
  dataset whose keys match the Python's field for field.
- An 18 pull request replay returning the same tier counts as the Python,
  3 high, 5 medium, 8 low, 2 trivial.
- -explain printing all eleven questions, its contribution column summing
  to exactly the 0.3847 load it reported.
- GHES resolution all three ways, and -api-base winning over a URL host.
- -fail-on-tier low exiting 2 and naming the pull request, -fail-on-tier
  high exiting 0.
- gofmt, go vet, go test clean. ./build.sh 0.1.0 cross-compiles linux,
  darwin and windows on amd64 and arm64 with SHA256SUMS. Stripped and
  static, each binary is about 7 MB against 10.4 MB for a plain build.

Two bugs fixed in this port. Go's flag package stops parsing at the first
non-flag argument, so `pr-triage owner/repo -all` silently ignored -all;
the binary now takes the repository and parses what follows it. And main
was overriding a gate's exit code with the generic 1.

One bug left alone, in the Python payload. design_decisions level 0 reads
`- None: the same edit repeated, or a value changed`, which is YAML for a
mapping rather than a string, so Jev receives an object as that rubric
level and echoes it back in the legend. The Go side reads a legend value as
any type, so a released binary keeps working against an old payload.
Quoting the string would fix it and would move every recorded number, so
it wants its own change.

Also adds go/README.md, three .gitignore lines for a plain go build in the
new folder, the layout and testing rules in AGENTS.md, and a row in the
root README.
@aarora79 aarora79 added the enhancement New feature or request label Sep 25, 2026
design_decisions level 0 read:

    - None: the same edit repeated, or a value changed

which is YAML for a mapping, not a string, so Jev received
{"None": "the same edit repeated, or a value changed"} as that rubric level
and echoed the object back in the legend. Every run recorded until now
described level 0 to the model as a JSON object. Quoting the string fixes
it and keeps the wording the rubric always intended.

Fixed in the Python payload and in the Go copy, which build.sh syncs and
payload_test.go guards. A scan of every other criteria entry, list and map
alike, found no second instance.

The fix changes what the model is asked, so the numbers moved.
design_decisions shifted on 17 of the 18 airflow pull requests, mostly
downward in score and more sharply in confidence: #73703 from 0.28 to 0.08,
#73719 from 0.55 to 0.40, #73722 from 0.41 to 0.26. Loads moved by up to
0.016, and the tier counts held at 3 high, 5 medium, 8 low and 2 trivial.
Both committed reports were regenerated and every figure the README pins
was re-synced to that run and checked against it: #73704 to 0.50, #73713 to
0.37, #73722 to 0.19.

Running the Go binary over the same dataset agrees with the Python inside
the documented drift, a mean gap of 0.0093 and a worst of 0.0234, and both
now show the quoted text in the legend.

Also promotes the skill install out of a subsection into its own
"Install it as a Claude Code skill" section, so it can be linked on its
own. It answers the whole question in one place: the one curl command, what
lands where, the two credentials, and what to say to the agent afterward.
The root README row links straight to it.
@aarora79
aarora79 merged commit bb99a3e into main Sep 25, 2026
1 check passed
@aarora79
aarora79 deleted the sample/pr-triage-go branch September 25, 2026 22:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant