github-rollup
Capability: contract:tracker
Kind: implementation
Vendor: GitHub
Append to (or create) the status-rollup comment on a GitHub issue without bringing the rollup body into agent context.
Prerequisites
- Runtime: Python 3.11+ run via
uv(uv run --directory tools/github-rollup github-rollup …); stdlib-only, no third-party dependencies. - CLIs:
uv;gh— the script shells out to it for all GitHub access (the default@usercomes fromgh api user). - Credentials / auth: an authenticated
ghsession (gh auth statusmust pass) — the comment read / append / PATCH all go throughgh. - Network:
api.github.comviagh.
Configuration
This helper has no persistent config file of its own. Callers pass the
target issue from <project-config>/project.md → tracker_repo; the
status-rollup comment format is the GitHub tracker convention documented
in tools/github/status-rollup.md and reused by security lifecycle
skills.
Why
Every skill that updates a <tracker> issue (import receipt,
sync passes, CVE allocation, dedupe, fix-PR announcements) folds
its status update into one rollup comment per tracker. The
existing recipe in
tools/github/status-rollup.md
walks the agent through: fetch the comment, concatenate
<old body> + ruler + new entry, PATCH the comment. That loops
the full rollup body — which grows monotonically and is the
single largest comment on a long-running tracker — through agent
context every sync pass.
This tool does the read / append / PATCH in a subprocess. Only a
one-line confirmation lands on the agent’s stderr. The body never
crosses the boundary.
append and amend-latest print the rollup comment’s URL on stdout, so a
caller can link to it without reading the rollup.
Invocation
uv run --directory tools/github-rollup github-rollup <subcommand> ...
Under the secure agent setup this CLI’s gh runs sandboxed and fails,
so skills call the same procedures through vetted-ops instead:
vetted-op-tracker --caller <skill> rollup-append <N> "<action>" <scratch>/entry.md,
rollup-amend-latest <N> "<action>" <scratch>/entry.md, and rollup-fold <N> <comment-id> "<action>".
The uv CLI remains for use outside the sandbox.
append <issue> --action "<label>" ...
Append a new entry to the rollup comment on <issue>. Creates
the rollup if none exists. Required: --action <label> (the
right-hand field of the entry’s summary line). Body comes from
either --entry-body "<text>" or --entry-body-file <path>
(- reads stdin).
Optional flags:
--user @handle— override the summary’s@userfield (default: the authenticatedghuser fromgh api user).--now <ISO8601>— override the date used in the summary (default: real now). Useful for deterministic replay tests.--dry-run— print the decision (create vs append) without writing.
A new rollup’s marker line names the tracker repository
(<!-- <repo-name> status rollup v1 — … -->); an existing rollup is
found by any <!-- <name> status rollup v<N> marker.
amend-latest <issue> --action "<label>" ...
Replace the body of the most recent entry, keeping its date and user.
Use it when a later step of the same pass has to fill in a value the
entry already mentions (for example a draft id). Refuses with exit 4
when the latest entry’s action is not <label>, so an entry someone
else appended in the meantime is never overwritten. Body from
--entry-body or --entry-body-file; --dry-run supported.
fold <issue> --comment-id <id> --action "<label>"
Fold one legacy bot comment into the rollup: the entry takes the
legacy comment’s own date and author, every line is left-trimmed, and
the legacy comment is deleted only after the append succeeded.
Refuses (exit 4) when the comment is the rollup itself. Which comments
are foldable, and the action label to use, is the caller’s decision —
see status-rollup.md.
--dry-run supported.
list <issue>
Print every entry’s summary line in order (or --json for a
machine-readable array of {date, user, action}). Exit 3 if the
issue has no rollup yet.
latest <issue>
Print just the body of the most recent entry. Useful for “what did the last sync do?” in a follow-up script. Exit 3 if the rollup or entries are missing.
Failure modes
| Exit | Meaning |
|---|---|
| 0 | Success (or --dry-run planned). |
| 2 | CLI argument error (mutually-exclusive flags, missing required). |
| 3 | Issue has no rollup yet, or rollup has no entries (for latest / amend-latest). |
| 4 | Refused: amend-latest action mismatch, or fold pointed at the rollup itself. |
| other | gh returned non-zero; the underlying stderr is forwarded. |