Affinage
Cheese your code π§ β high-quality results as easy as cheese. A portable, harness-agnostic Agent Skills toolkit.
npx -y skills add paulnsorensen/easy-cheese --skill affinageAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 12 stars12 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Triage a PR's review comments and failing CI (plus merge conflicts) through the /age lens, deciding which claims are worth acting on. Use when the user says "respond to PR comments", "handle review feedback", "affinage the PR", "/affinage <pr>", "fix the failing build", "resolve the conflicts and respond". Do NOT use for a bare diff with no PR (route to /age).
The file declares its own license as MIT. That is the authorβs claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
30.8 KB, as published. Nobody here has run it
/affinage
Use this skill when the user wants to act on external claims about a PR β review comments from humans or bots, plus failing CI checks and merge conflicts β and wants those claims graded through the same lens /age uses for fresh review, then handed to /cure for application.
/affinage always refines the claims that already exist on the PR (comments, CI failures, conflicts). Whether it also generates fresh /age findings depends on how it was reached:
- Standalone β the user typed
/affinage <pr>directly, with no upstreamhandoff_context. The PR diff has not been reviewed in this session, so/affinageruns/ageover it and folds the findings into the same report (unless--no-age). - Chained β reached from
/cookor/curewith ahandoff_context./agealready ran in that chain, so/affinageskips the fresh pass to avoid double-grading and only refines existing claims.
See ## Fresh-window review for the detection rule and ## Merge-conflict resolution for the conflict path.
Inputs
/affinage [<pr-ref>] [--auto --stake <floor>] [--plate] [--safe] [--open-pr] [--hard] [--full] [--include-outdated]
<pr-ref> accepts a PR number, a full GitHub PR URL, or nothing (auto-detect via gh pr view --json number on the current branch).
Flags:
--auto --stake <floor>β autonomous mode.<floor>isblocker,high,medium+, orall(same semantics as/cure). Skips the selection gate, dispatches/cure --auto --stake <floor>, posts all replies without prompting.--safeβ also gate the cure-selection and merge-conflict-resolution steps (which otherwise run autonomously by default). Reply posting is gated by default regardless of this flag β only--autoposts without prompting. Use--safewhen you also want to choose before anything is fixed or a conflict is resolved.--open-prβ allow affinage's terminal/plateto open a new PR when none exists (otherwise plate only updates the already-open one).--plateβ one-shot publish combo, equivalent to--auto --stake medium+ --open-pr: autonomously triage, cure the recommended floor (medium-and-above plus cheap contained-fix lows), post every reply, then plate. Because it carries--auto, replies post without prompting. An explicit--stake <floor>overrides themedium+default.--hardβ propagated metacognitive-gate flag./affinagedoes not fire the gate; passes--hardforward to its terminal/plateat publication.--fullβ un-collapses## Lowwhen β₯10 low-severity findings exist (mirrors/age --full).--include-outdatedβ include outdated review threads. Default skips them.--no-ageβ skip the standalone fresh/agepass. No effect when chained (the pass is already skipped). Use when you only want to triage existing comments, CI failures, and conflicts.
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 ${CLAUDE_SKILL_DIR} as optional host-provided fallback.
The handoff blocks below are the portable contract; slash commands are host renderings, not the control model.
Flow
-
Resolve PR. From
<pr-ref>orgh pr view --json numberon the current branch. Resolve<owner>/<repo>from the git remote. -
Fetch PR status. Call
python3 skills/affinage/scripts/affinage.pyz pr-status <pr>. The script returns JSON with build status, per-check failure summaries (last ~10 lines of failed logs + parsed failed-test names), and merge state. Map the exit code:- Exit 0 β proceed with grading.
- Exit 3 (
logs-expired) β the build is failing but every failing check's log was unfetchable (typically expired GitHub Actions logs past the retention window), so there is nothing to ground a CI finding on. Writestatus: halt: pr-status-logs-expiredand stop with the hint: "CI is failing but the logs have expired β rerun the failed jobs (gh run rerun <run-id> --failed, where<run-id>is the/actions/runs/<id>/segment of the failing check'surl, or read it fromgh pr checks) and re-invoke/affinage." - Any other non-zero (1 PR/gh API error, 2 missing gh binary) β write
status: halt: pr-status-unavailableand stop. - Merge conflicts. If
merge.mergeableisCONFLICTINGormerge.stateisDIRTY, the PR has unresolved conflicts. Resolve them before grading β see## Merge-conflict resolution.
-
Fresh-window review. If this is a standalone run and
--no-agewas not passed: computefiles_changed/insertions/deletionsfrom the PR diff stat, grep it forage_route.OVERRIDE_FLAGSrisk flags, and callage_route.route(files_changed=..., insertions=..., deletions=..., risk_flags=..., entry="affinage", comments=<unresolved-thread-count>, ci_class=<"failing"|"red"|"flaky"|None from pr-status>)β the same router/ageitself calls, but sized with affinage's comment count and CI failure class so a heavily-commented or red-CI PR gets the bigger fan-out even on a small diff. If the host only ships the bundle,echo '{"files_changed": <n>, "insertions": <n>, "deletions": <n>, "risk_flags": [...], "entry": "affinage", "comments": <n>, "ci_class": <"failing"|"red"|"flaky"|null>}' | python3 skills/affinage/scripts/affinage.pyz age-routeis the fallback (JSON on stdin, route JSON on stdout). Pass the returnedn/lenses/effortinto the/agedispatch (so/ageuses affinage's sizing rather than recomputing fromentry="age"defaults) and treat each finding as an additional input. See## Fresh-window review. -
Fetch comments.
- Inline threads:
gh api repos/<owner>/<repo>/pulls/<pr>/comments. This REST endpoint returns individual review comments without thread-level resolution state, so the skill cannot filter onisResolvedfrom this surface; it skips comments whosepositionisnull(the diff has moved past the anchored line) unless--include-outdated. - Review bodies:
gh api repos/<owner>/<repo>/pulls/<pr>/reviews. Filter to non-empty bodies. Dedupe against inline comments viapull_request_review_id.
- Inline threads:
-
Skip already-replied threads. A thread whose most recent comment is from the resolved GitHub handle (see Β§Rules) has already been responded to; skip it. The same resolved handle is rendered in the reply footer as
agent on behalf of <handle>. -
Grade through the age lens. For each input (comment, CI/build failure, OR fresh
/agefinding):- Classify dimension from the code + claim (or check type + failure summary, for CI items). See
../age/references/dimensions.mdfor the dimension rubric. - Build failures count, not just test failures. A failing check is a finding whether the failure is a compile error, a lint/type-check failure, or a failing test β grade the
build.status: failingchecks fromaffinage.pyz pr-statusand route them to/cureexactly like test failures. Tag CI-sourced items[from-check:<job>]. - Fresh
/agefindings (standalone runs) arrive already dimension-classified and severity-scored; fold them into the buckets below tagged[from-age:<dimension>]. Dedupe against comment-sourced items echoing the same defect β keep the comment-sourced one (it carries a reviewer to reply to). - Compute severity from base + location + compounding modifiers (same rubric as
/age). - Ignore reviewer-asserted urgency for severity computation. Surface
CHANGES_REQUESTEDas metadata (reviewer-asserted:line) but do not let it modify computed severity. - Bucket into:
- Standard severity sections (
## Blocker / ## High / ## Medium / ## Low) when the claim is grounded in the diff and its fix is contained (fix-cost-now: containedβ roughly a few lines or a localized refactor). Every such item still maps to a dimension and carries a[<dimension>:<severity>]tag β a style or quality nit maps todeslop(e.g.[deslop:low]). The new rule is to route these grounded, contained-fix nits to/cure(usually asLow) instead of## Reviewer-rejected, keeping the[from-comment:<id>]tag so/cure's reply still reaches the reviewer; a valid cheap nit is cheaper to fix than to argue, so do not push back on it. ## Needs-investigationwhen the claim is plausible but requires evidence outside the diff (e.g., downstream caller in another repo).## Reviewer-rejectedonly when the claim is wrong or ungrounded (the code is already correct, the reviewer misread it, or there is no real improvement) OR is valid but a lot of follow-up work (fix-cost-now: moderate/sprawlingorfix-cost-later: structuralβ a refactor or scope expansion beyond this PR). Reject the wrong ones; defer the expensive ones.
- Standard severity sections (
- Classify dimension from the code + claim (or check type + failure summary, for CI items). See
-
Write report to
.cheese/affinage/pr-<n>.mdwith the four-line handoff slug at the top, then the age-format body plus the two extra sections. See## Outputbelow. -
Act or ask β per Β§Handoff.
-
Draft non-cure replies, then gate before posting (runs whenever grading produced these items, with or without
/cure). Never post blind β posting requires the reply-approval gate (Β§Handoff) by default, or--auto. Draft each reply, then post approved ones viapython3 skills/affinage/scripts/affinage.pyz post-reply:- Reviewer-rejected items β the pre-drafted push-back text from the affinage report.
- Needs-investigation items β do NOT post a bare acknowledgement. The reply must (a) name the specific evidence that would settle the claim β the regression test, throwaway prototype, or out-of-diff file to read β and (b) state that a follow-up will report the result. Before posting, offer to run that investigation now: a regression test via
/pasteurize, or explore the out-of-diff evidence via/briesearch. If run, post a reply carrying the actual outcome; if the user declines, post the explicit"Needs <named test/exploration> to confirm β will follow up with the result."note β never a blind "investigating". - CI-sourced findings (
from-check:<job>tag) and fresh-review findings (from-age:<dimension>tag) β no reply (no reviewer to notify).
-
Post-cure reply posting (only when
/cureran). The chained/cureapplies its fixes and runs its/age --scopeloop but returns without plating (it owns no publication in the/affinagechain). When/curereturns, read.cheese/cure/pr-<n>.md's### Applied/### Deferredsections and post per-finding replies viapython3 skills/affinage/scripts/affinage.pyz post-reply:- Applied (with
from-comment:<id>tag) β"Fixed β <applied summary>." - Deferred (with
from-comment:<id>tag) β"Attempted fix reverted β <reason>."
- Applied (with
-
Plate β the final writes above (steps 9β10) must land before publication. Once every approved reply is posted, and only when the cure applied β₯1 fix (there is something to publish), dispatch terminal
/plate [--open-pr] [--hard] [--safe]to commit and publish cure's fixes to the PR./affinageowns this dispatch; publication lands after all replies. After publication lands, run the Β§ Post-PR learnings write-back (../cure/SKILL.mdΒ§ Handoff) β affinage owns terminal/plate, so it owns the write-back the chained/curesuppressed. Skip plate and write-back when no fix was applied β there is nothing to publish.
Fresh-window review
Detection: standalone = no upstream handoff_context; chained = a handoff_context with source_skill: /cook or /cure. Behaviour:
- Standalone (and
--no-agenot passed): compute theentry="affinage"router call (Flow step 3) and run/age <pr-ref>over the PR diff, passing the router'sn/lenses/effortso/agedoesn't recompute a smallerentry="age"sizing from the diff alone. Fold each returned finding into the affinage report's severity sections tagged[from-age:<dimension>]. They flow to/curewith every other selected finding and get no GitHub reply β there is no reviewer to notify, same as[from-check:β¦]items. - Chained, or
--no-age: skip the pass. Re-running/ageon an already-reviewed diff double-grades.
Run the fresh /age before grading external claims so a comment that merely echoes an /age finding can be deduped. To keep the parent context lean, run the pass under the same sub-agent gate as grading (## Sub-agent context gate).
Merge-conflict resolution
When affinage.pyz pr-status reports merge.mergeable: CONFLICTING or merge.state: DIRTY, the PR cannot merge until conflicts are resolved. /affinage does not resolve conflicts by hand β it routes to /melt, which runs the structural cascade (mergiraf β rerere β kdiff3).
- Materialise the conflicts locally:
gh pr checkout <pr>, thengit merge origin/<base>. (gh pr checkoutneither opens nor updates the PR, so it does not breach the no-/ghrule.) - Hand off to
/melt. It first checks for squash-merge residue and stops with remedies if found β surface those verbatim and do not auto-apply. - After
/meltresolves cleanly, the resolution commit is owned by/melt//cure./plateowns the verified commit and existing-PR update transaction.
- Default and
--automode: run the checkout +/meltautomatically before dispatching/cure, then re-runaffinage.pyz pr-statusto confirmmergeablecleared. If/meltcannot resolve (manual kdiff3 needed, or squash residue), writestatus: halt: merge-conflicts-need-humanand stop. --safemode: gate the checkout +/meltbehind the handoff prompt β offer "Resolve merge conflicts" alongside the cure-selection options.
Sub-agent context gate
/affinage keeps dialogue, selection, approval state, and reply posting in the parent context. When the parent context would balloon, resolve a fresh read-only reviewer through the shared agent resolver. A compatible reviewer is preferred; a general worker qualifies only with prompt-only no-write enforcement and degraded: true:
- Total input count (comments + CI failures) exceeds 10.
- Diff exceeds ~25 KB.
- Threads span more than 5 files.
The sub-agent returns a digest: graded findings table with dimension, severity, confidence, grounded-evidence cite, and pre-drafted push-back text for any Reviewer-rejected items. The parent owns the report write, selection gate, /cure dispatch, and reply posting.
Digest size, parent-vs-sub-agent split, and harness-agnostic sub-agent selection live in ../age/references/sub-agent-gate.md.
Preferred tools and fallbacks
Call source-code search and read backends directly according to the shared code-intelligence-routing.md contract. Beyond source-code routing there are affinage-specific tools:
| Need | Prefer | Fallback |
|---|---|---|
| PR status (build + merge) | skills/affinage/scripts/affinage.pyz pr-status | manual gh pr checks + gh pr view |
| GitHub fetch | gh api | none (skill halts) |
| Reply posting | skills/affinage/scripts/affinage.pyz post-reply | none β direct gh api calls bypass the agent on behalf of <handle> attribution |
| Diff inspection | delta | git diff --unified=3 |
Output
Write to .cheese/affinage/pr-<n>.md with the four-line handoff slug at the top, then the age-style body with two extra sections:
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>
# Affinage Report β PR #<n>
## Orientation
<one or two factual sentences about the PR and what was graded>
## PR status
- Build: passing | failing (N jobs)
- Merge: clean | conflicts (resolved via /melt | needs human)
- Comments: K unresolved (M skipped as outdated)
- Fresh review: ran /age (N findings) | skipped (chained) | skipped (--no-age)
## Blocker
- **[from-comment:<id>] [security:blocker]** alice on `src/auth.ts:42` β token parsed without validation.
- location: contract Β· fix-cost-now: contained Β· fix-cost-later: structural Β· confidence: certain
- reviewer-asserted: changes-requested
- recommendation: validate `authorization` header; reject with 401 on missing.
- **[from-check:test-suite] [correctness:blocker]** CI job `test-suite` β 3 tests failing in `tests/auth.test.ts`.
- location: contract Β· fix-cost-now: contained Β· fix-cost-later: structural Β· confidence: certain
- recommendation: re-run after fixing the missing null check.
- **[from-check:build] [correctness:blocker]** CI job `build` β `tsc` fails: `src/auth.ts:42: 'token' is possibly undefined`.
- location: contract Β· fix-cost-now: contained Β· fix-cost-later: structural Β· confidence: certain
- recommendation: narrow `token` before use; build is red until this compiles.
- **[from-age:efficiency] [efficiency:high]** fresh review β `src/api/users.ts:88` re-fetches the user inside the loop body.
- location: hot path Β· fix-cost-now: contained Β· fix-cost-later: contained Β· confidence: speculating
- recommendation: hoist the fetch above the loop.
## High
... (same shape)
## Medium
... (same shape)
## Low
- **[from-comment:<id>] [deslop:low]** copilot on `src/utils/format.ts:18` β rename `data` to `lineItems` for clarity.
- location: class Β· fix-cost-now: contained Β· fix-cost-later: contained Β· confidence: certain
- recommendation: rename `data` β `lineItems`. Valid cheap nit β fixed via `/cure`, not pushed back.
... (same shape; collapsible per --full rules)
## Needs-investigation
- **[from-comment:<id>]** bob on `src/api/users.ts:108` β "might break analytics pipeline."
- reason: claim plausible but pipeline lives in a different repo; diff cannot confirm.
- suggested action: human reads `analytics-svc/consumers/users.ts`.
## Reviewer-rejected
- **[from-comment:<id>]** copilot on `src/auth.ts:30` β "missing `await`; this promise is unhandled."
- reason: wrong β `parseToken` is synchronous (returns `string`, not a `Promise`, see `src/auth.ts:12`); there is nothing to await.
- draft reply: "`parseToken` is synchronous here (returns `string`, `src/auth.ts:12`), so there's no promise to await. Leaving as-is."
- **[from-comment:<id>]** dana on `src/api/users.ts:60` β "extract this into a generic repository layer."
- reason: valid but large β fix-cost-now: sprawling (6 files across 2 slices); scope expansion beyond this PR.
- draft reply: "Agreed this would be cleaner, but it's a cross-slice refactor beyond this PR's scope β filing a follow-up rather than growing this change."
## Confidence
<certain | speculating | don't know> β <one-line justification>
## Next step
Auto-fixing the recommended set via `/cure`; drafted replies are held for the reply-approval gate before posting (`--auto` posts directly). Replies post before terminal `/plate` publishes cure's fixes. On a reason to ask / `--safe`, the cure-selection prompt renders inline β pick findings to cure or `none` to stop.
Empty severity sections are omitted entirely. ## Needs-investigation and ## Reviewer-rejected are omitted when no items land there.
Per-finding confidence: uses the voice-kernel scale (../age/references/voice.md Β§ Reasoning posture): certain β the defect is verified by direct evidence (diff/code read, command output); speculating β inferred from indirect signal. A don't know grading never ships as a severity row β route it to ## Needs-investigation.
status: ok when grading completed; status: halt: <reason> when gh or pr-status.py failed in a way that blocks honest grading. next: is set per Β§Handoff β cure when β₯1 finding meets the medium+ floor; done otherwise.
Handoff
Pipeline: culture β mold β cook β press β age β cure β plate Β· /affinage is parallel to /age and feeds /cure.
After the report lands, affinage acts by default and asks only on a genuine reason (a sprawling/structural fix in the recommended set, conflicting findings) or under --safe (Flow step 8). What it acts on depends on whether any severity-section finding exists.
When at least one severity-section finding exists (any severity, including Low) β compute the recommended composite (all-medium, cheap). With no reason to ask and no --safe: announce the one-line selection, dispatch /cure (below), then render the reply-approval gate before posting the non-cure replies (Flow step 9) and the cure-dependent replies (step 10) β never post blind. --auto posts without the gate (Β§Auto mode). On a reason to ask or --safe: render the cure-selection table inline (per ../cure/references/selection.md) and ask via ../cheese/references/handoff-gate.md, pre-selecting the recommended composite and flagging heavy rows. Lead with the recommended composite, then present the four severity-floor options below it, in the same most-inclusive-to-least order, so the gate is predictable across every run:
- The five severity-floor options (recommended
all-medium, cheap, thenall,all-medium,all-high,all-blocker) are exactly age's β see../age/SKILL.mdΒ§ Selection gate for their labels and semantics.
Then offer the non-floor options last:
- Pick findings to fix β free-text reply using
/age//cureverbs (1,3,5,all-blocker,all-medium,all-high,cheap,all,none,skip N). - Resolve merge conflicts (offered only when the PR has conflicts) β checkout +
/meltper## Merge-conflict resolution, then re-render this gate. - Stop β leave the report for later β equivalent to
none.
The "present all four severity options on every run, empty-set-resolves-to-none" rule is age's β see ../age/SKILL.md Β§ Selection gate.
When no severity-section finding exists but Reviewer-rejected or Needs-investigation has items β /cure has nothing to act on, so skip it. Posting is gated by default; render the reply-approval gate (below) and post nothing until the user chooses. Only --auto posts without this gate.
Reply-approval gate β the single gate both Handoff branches use before any post-reply call:
- Post pushbacks only (recommended) β post
Reviewer-rejecteddrafts; holdNeeds-investigationitems for investigation. - Investigate now, then post β for each
Needs-investigationitem, run the follow-up investigation (/pasteurizefor a regression test,/briesearchto explore the out-of-diff evidence), then post a reply carrying the actual result. - Post all β post every drafted push-back and the explicit
Needs-investigationfollow-up notes (naming the needed evidence) without running the investigation first. - Skip posting β leave the report for later; post nothing.
- Per-finding β free-text pick of which drafts to post or investigate.
After the selection, post the approved replies via Flow step 9. Then β only when the cure applied β₯1 fix β dispatch terminal /plate (Flow step 11) so publication follows the replies; when no cure ran or no fix applied, there is nothing to plate. Exit with status: ok / next: done β see ## Auto mode Β§ "no findings meet the floor" for the auto path.
Slug next: values. Write next: cure when at least one finding meets the medium+ floor (medium-or-above, or a cheap contained-fix low). Write next: done when no severity-section finding exists or all meeting items are empty-selection after floor resolution.
On a non-empty cure selection (auto-selected by default or chosen at the gate), immediately dispatch /cure <slug> [--safe] [--open-pr] [--hard] with locked context:
handoff_context:
source_skill: /affinage
source_report: .cheese/affinage/pr-<n>.md
selection: "<verb or explicit ids>"
resolved_ids: [<expanded ids>]
/cure re-confirms cited ids and goes straight to apply. Because the handoff carries source_skill: /affinage, /cure applies its fixes and runs its /age --scope loop but suppresses its own terminal /plate and returns β affinage owns publication. Propagate --safe, --open-pr, and --hard to /cure when in scope. On return, /affinage posts every reply (Flow steps 9β10) β its terminal final writes β and only then dispatches terminal /plate [--open-pr] [--hard] [--safe] (Flow step 11), so publication lands after every reply. Skip the plate dispatch when the cure applied no fix.
Auto mode
When invoked with --auto --stake <floor> (or --plate, which enters this mode with --stake medium+ --open-pr):
- Skip the selection gate.
- If the PR has merge conflicts, resolve them via
/meltfirst (see## Merge-conflict resolution). If/meltcannot resolve, halt withstatus: halt: merge-conflicts-need-humanbefore any/curedispatch. - If standalone (and
--no-agenot passed), run the fresh/agepass so[from-age:β¦]findings join the floor-based auto-selection. - Auto-select every finding (comment-sourced, CI-sourced, OR fresh-
/age-sourced) that meets the floor β severity at or above the floor, plus cheap contained-fix lows when the floor ismedium+(same floor semantics as/cure). - Dispatch
/cure --auto --stake <floor>. - After
/cure --autoand its downstream/age --scope --autochain settle, post replies for the originally graded items only. Do NOT re-grade for findings discovered by/age --scope. - Reviewer-rejected items: post the pre-drafted push-back.
- Needs-investigation items: post the explicit follow-up note naming the evidence that would settle the claim (
"Needs <named test/prototype> to confirm β will follow up with the result."). Auto mode does not pause to run the spike; it posts the honest follow-up note, never a blind acknowledgement. - After the cure chain settles and all replies are posted (previous two bullets),
/affinagedispatches terminal/plate --open-pr [--hard]to publish cure's fixes β the final writes precede publication./curesuppresses its own terminal/platefor the/affinagechain (keyed onsource_skill: /affinage). Skip the dispatch when no fix was applied.
The whole cure chain (cure β /age --scope --auto β up to the two-cure-pass cap) must run in the parent affinage context so the post-cure reply step still has the original graded findings (slug, ids, from-comment:<id> tags, drafted push-back text) in memory. Spawning the cure chain in a sub-agent breaks reply posting β do not.
If no findings meet the floor, skip the /cure dispatch, post replies for Reviewer-rejected + Needs-investigation items only, and exit with status: ok / next: done.
--hard mode
/affinage passes --hard to its own terminal /plate (dispatched after replies), which fires /hard-cheese after verifying the final artifact state. /cure does not dispatch plate in the /affinage chain, so the gate fires once β at affinage's publication boundary.
Rules
- Grading is code-grounded, not reviewer-asserted.
CHANGES_REQUESTEDis metadata, not a severity bump. - Prefer fixing over pushing back. A valid, grounded nit whose fix is contained (
fix-cost-now: containedβ a few lines or a localized refactor) goes to/cureas aLowfinding tagged[from-comment:<id>]; do not draft a push-back for it. Reserve## Reviewer-rejectedfor claims that are wrong/ungrounded or whose fix is a lot of work (moderate/sprawling/structural). See../age/references/voice.md. - Never auto-apply fixes from
/affinageitself. Code fixes go through/cure; merge conflicts go through/melt. - Never post a GitHub reply without approval. Reply posting is gated by default (Β§Handoff reply-approval gate); only
--autoposts autonomously. ANeeds-investigationreply must name the follow-up test or exploration and offer to run it (/pasteurizeor/briesearch) before posting β never a blind acknowledgement. - Fresh
/ageruns only on standalone invocations (no upstreamhandoff_context) and only when--no-ageis absent. Chained runs never re-review the diff. - Merge conflicts are resolved through
/melt, not by hand.gh pr checkoutto materialise conflicts is allowed β it neither opens nor updates the PR. After resolution, affinage's terminal/plateowns the verified commit and push. /affinageowns terminal publication. The chained/curesuppresses its own/plate(keyed onsource_skill: /affinage); after a clean cure,/affinageposts every reply and then dispatches/plateto update the existing PR β or, with--open-pr, apply the explicit-choice and review-shape policy before publishing a new PR. Replies always precede plate: no final write lands after publication.- Every posted reply ends with the literal
agent on behalf of <handle>attribution viaskills/affinage/scripts/affinage.pyz post-reply, where<handle>is resolved fromRESPOND_GH_HANDLEβgh api user --jq .loginβgit config user.name. Never callgh apidirectly to post. - Idempotent re-runs: skip threads where the latest comment is from the resolved handle. The REST
/commentsendpoint does not expose thread resolution, so honest idempotency relies on the latest-comment-from-self heuristic; switch to GraphQLreviewThreadsif cross-session resolution-state visibility becomes required. - CI-sourced findings get no reply (no reviewer to notify).
- Apply the shared voice kernel (
../age/references/voice.md): name confidence ascertain | speculating | don't know; agree when no findings warrant grading.
References
skills/age/SKILL.mdβ review pipeline, dimensions, sub-agent gate, report shape.../age/references/dimensions.mdβ per-dimension rubrics and severity computation.skills/cure/SKILL.mdβ apply pipeline,--auto --stakefloors, handoff context shape.../cure/references/selection.mdβ selection verbs and composition.skills/melt/SKILL.mdβ merge-conflict resolution cascade (mergiraf β rerere β kdiff3).../cheese/references/handoff-gate.mdβ gate primitives.python3 skills/affinage/scripts/affinage.pyz post-replyβ reply posting withagent on behalf of <handle>attribution.python3 skills/affinage/scripts/affinage.pyz pr-statusβ 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 agent_resolution block.