/affinage
Act on external claims about a PR — review comments from humans or bots, plus failing CI checks and merge conflicts — grading them through the same lens
uses for fresh review, then handing them to
for application.
always refines the claims that already exist on the PR (comments, CI failures, conflicts). Whether it
also generates fresh
findings depends on how it was reached:
- Standalone — the user typed directly, with no upstream . The PR diff has not been reviewed in this session, so runs over it and folds the findings into the same report (unless ).
- Chained — reached from or with a . already ran in that chain, so skips the fresh pass to avoid double-grading and only refines existing claims.
See
for the detection rule and
## Merge-conflict resolution
for the conflict path.
Inputs
text
/affinage [<pr-ref>] [--auto --stake <floor>] [--plate] [--safe] [--open-pr] [--hard] [--full] [--include-outdated]
accepts a PR number, a full GitHub PR URL, or nothing (auto-detect via
on the current branch).
Flags:
- — autonomous mode; (, , , ) matches 's semantics. Skips selection, dispatches
/cure --auto --stake <floor>
, posts replies without prompting. Mechanics: .
- — also gates cure-selection and merge-conflict resolution (autonomous by default). Reply posting is gated by default regardless — only skips it.
- — let terminal open a new PR when none exists (else it only updates the open one).
- — one-shot publish combo =
--auto --stake medium+ --open-pr
: triage, cure the recommended floor, post every reply, then plate. An explicit overrides .
- — propagated metacognitive-gate flag; forwarded to terminal , not fired here.
- — un-collapses when ≥10 low-severity findings exist (mirrors ).
- — include outdated review threads (default: skip).
- — skip the standalone fresh pass; no effect when chained.
Portability reference:
../cheese/references/harness-portability.md
. It covers helper resolution, sub-agent dispatch, GitHub operations, and handoff transitions; prefer the bundled or repo-local helper first, and treat
as optional host-provided fallback.
The handoff blocks below are the portable contract; slash commands are host renderings, not the control model.
Flow
Exact CLI invocations, exit-code hints, and grading rationale for steps 2, 3, 6, and 9 below:
references/flow-details.md
.
- Resolve PR. From or ; resolve from the git remote.
- Fetch PR status.
affinage.pyz pr-status <pr>
. Exit 3 halts status: halt: pr-status-logs-expired
; any other non-zero halts status: halt: pr-status-unavailable
. Conflicting/dirty merge state routes to ## Merge-conflict resolution
before grading. Exit-code detail: references/flow-details.md
.
- Fresh-window review. Standalone and not passed: score the PR diff, route it through sized with affinage's comment count and CI failure class, run with the returned //, and fold each finding tagged . See .
- Fetch comments. Inline threads:
gh api repos/<owner>/<repo>/pulls/<pr>/comments
(REST; no thread-resolution state, so skip comments unless ). Review bodies: gh api repos/<owner>/<repo>/pulls/<pr>/reviews
, filtered to non-empty bodies, deduped against inline comments via .
- Skip already-replied threads. A thread last-commented by the resolved GitHub handle (§Rules) is already answered — skip it; the footer renders as
agent on behalf of <handle>
.
- Grade through the age lens. Classify each input (comment, CI failure, or fresh finding) by dimension — code/claim, or check type/failure for CI — per
../age/references/dimensions.md
, and by severity (base + location + compounding, same rubric as ); ignore reviewer-asserted urgency ( is metadata, never a severity bump). Bucket into severity sections (contained fixes), (needs out-of-diff evidence), or (wrong/ungrounded, or a lot of follow-up work). Full bucketing criteria: references/flow-details.md
.
- Write report to
.cheese/affinage/pr-<n>.md
: four-line handoff slug, then the age-format body plus two extra sections. See .
- Act or ask — per §Handoff.
- Draft non-cure replies, then gate before posting (whenever grading produced these items, with or without ). Never post blind — requires the reply-approval gate (§Handoff), or . Draft per
references/flow-details.md
; post approved ones via . CI-sourced () and fresh-review () findings get no reply.
- Post-cure reply posting (only when ran). Once returns, read 's / and post per-finding replies via : Applied () →
"Fixed — <applied summary>."
; Deferred () → "Attempted fix reverted — <reason>."
- Plate — once every approved reply is posted (steps 9–10) and the cure applied ≥1 fix, dispatch terminal
/plate [--open-pr] [--hard] [--safe]
; publication lands after every reply. After it lands, run the § Post-PR learnings write-back ( § Handoff) — affinage owns the write-back the chained suppressed. Skip plate and write-back when no fix was applied.
Fresh-window review
Standalone runs (see intro) compute the
router call (Flow step 3) and run
over the PR diff, passing the router's
/
/
so
doesn't recompute a smaller
sizing from the diff alone. Fold each returned finding into the report's severity sections tagged
— they flow to
like any other finding but get no GitHub reply (no reviewer to notify, same as
items).
Run the fresh pass before grading external claims so an echoing comment can be deduped, under the same sub-agent gate as grading (
## Sub-agent context gate
) to keep the parent context lean.
Merge-conflict resolution
When
reports unresolved conflicts,
routes to
(mergiraf → rerere → kdiff3) rather than resolving by hand. Default/
run checkout +
automatically before
;
gates it behind the handoff prompt. If
cannot resolve, write
status: halt: merge-conflicts-need-human
and stop. Full steps:
references/merge-conflict.md
.
Sub-agent context gate
keeps dialogue, selection, approval state, and reply posting in the parent context. When the parent context would balloon — inputs exceed 10, diff exceeds ~25 KB, or threads span more than 5 files — resolve a fresh read-only
through the shared agent resolver (a general worker qualifies only with
). The sub-agent returns a digest of graded findings (dimension, severity, confidence, evidence cite, pre-drafted push-back for
items); the parent owns the report write, selection gate,
dispatch, and reply posting. Digest size and selection detail:
../age/references/sub-agent-gate.md
.
Preferred tools and fallbacks
Call source-code search/read backends per
code-intelligence-routing.md
. Affinage-specific tools:
| Need | Prefer | Fallback |
|---|
| PR status (build + merge) | skills/affinage/scripts/affinage.pyz pr-status
| manual + |
| GitHub fetch | | none (skill halts) |
| Reply posting | skills/affinage/scripts/affinage.pyz post-reply
| none — direct calls bypass the agent on behalf of <handle>
attribution |
| Diff inspection | | |
Output
Write to
.cheese/affinage/pr-<n>.md
: the four-line handoff slug, then the age-style body plus two extra sections (
and the same severity /
/
shape
uses). Full annotated template:
references/report-template.md
.
markdown
status: ok | halt: <one-line reason>
next: cure | done
artifact: <path-to-prior-cure-or-press-report-if-any>
<one-line orientation: what the PR does and what was graded>
Empty severity sections are omitted; so are
/
when empty.
when grading completed;
when
/
failed.
per
§ Slug
values.
Handoff
Pipeline: culture → mold → cook → press → age → cure → plate ·
is parallel to
and feeds
.
Default: affinage acts without asking, and asks only for a genuine reason (a sprawling/structural fix in the recommended set, conflicting findings) or under
(Flow step 8).
- Severity-section findings exist (any severity, including ) — compute the recommended composite (). No reason to ask and no : announce the selection, dispatch with the locked (shape:
references/handoff-templates.md
§ Cure dispatch context), then render the reply-approval gate before posting (Flow steps 9–10) — never post blind. A reason to ask, or : render the cure-selection gate per ../cheese/references/handoff-gate.md
instead, pre-selecting the composite and flagging heavy rows. skips both gates ().
- No severity-section findings, but / items exist — nothing for to act on; render the reply-approval gate and post nothing until chosen. Only skips it.
After the selection, post approved replies (Flow step 9–10), then — only when the cure applied ≥1 fix — dispatch terminal
/plate [--open-pr] [--hard] [--safe]
(Flow step 11); publication lands after every reply. Exit
when there is nothing to act on.
Slug values. when ≥1 finding meets the
floor;
when no severity-section finding exists or all meeting items resolve to an empty selection.
Auto mode
Skips the selection gate. Resolves merge conflicts via
first (halt
status: halt: merge-conflicts-need-human
if unresolved). If standalone, runs the fresh
pass. Auto-selects every finding meeting
(
enters this mode at
--stake medium+ --open-pr
) and dispatches
/cure --auto --stake <floor>
; once its chain settles, posts replies for the originally graded items only, then dispatches terminal
/plate --open-pr [--hard]
once every reply posts (skipped if no fix applied). If no findings meet the floor: skip
, post rejection/investigation replies only, exit
. Full mechanics:
.
--hard mode
passes
to its terminal
, which fires
after verifying the final artifact state.
never dispatches plate in this chain, so the gate fires once — at affinage's publication boundary.
Rules
- Grading is code-grounded, not reviewer-asserted — see Flow step 6.
- Prefer fixing over pushing back. A grounded nit with a contained fix goes to as ; reserve for claims that are wrong, ungrounded, or a lot of work (Flow step 6,
../age/references/voice.md
).
- Never auto-apply fixes itself — code fixes go through , merge conflicts through (
## Merge-conflict resolution
).
- Never post a reply without approval — see the reply-approval gate (,
references/handoff-templates.md
).
- Every posted reply ends with the literal
agent on behalf of <handle>
attribution via skills/affinage/scripts/affinage.pyz post-reply
, where is resolved from → → . Never call directly to post.
- Idempotent re-runs rely on the latest-comment-from-self heuristic (Flow step 5) — the REST endpoint exposes no thread resolution state; use GraphQL if cross-session resolution state is ever needed.
- Apply the shared voice kernel (
../age/references/voice.md
): name confidence as certain | speculating | don't know
; agree when no findings warrant grading.
References
Affinage-local, each also routed inline above:
references/flow-details.md
,
references/merge-conflict.md
,
references/report-template.md
,
references/handoff-templates.md
,
.
is
../age/references/sub-agent-gate.md
(shared, not affinage-local).
Scripts:
skills/affinage/scripts/affinage.pyz post-reply
(reply posting),
(PR status fetcher).
Agent resolution
Resolve each dispatch through
../cheese/references/agent-resolution.md
.
| Work | Preferred types | Permissions/isolation | Minimum power | Effort | Fallback |
|---|
| Triage review claims and CI evidence | reviewer | read-only, fresh-context | powerful | high | compatible reviewer, then general |
The canonical affinage report carries the shared
block.