Skip to content

Commit 60d90f9

Browse files
thehesiodamohr-northstarclaude
authored
AI workflows: 2-job sync split + PR-body verbosity ceiling + bump-version fixes (#1574)
Co-authored-by: Alexander Mohr <[email protected]> Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
1 parent 1f572e5 commit 60d90f9

6 files changed

Lines changed: 437 additions & 52 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
You are the classifier stage of the botocore sync workflow. Your only job is to run the
2+
async-need classifier and write the result to a JSON file. The downstream `sync` job consumes
3+
your output to decide whether to use the cheap (Sonnet) no-port path or the expensive (Opus)
4+
port path.
5+
6+
## Pre-computed values
7+
8+
These are passed in by the workflow — use directly, do NOT re-derive:
9+
10+
- Target botocore version: $LATEST_BOTOCORE
11+
- Last supported botocore version: $LAST_SUPPORTED
12+
- Dry-run flag: $DRY_RUN
13+
14+
## Step 1: Run the classifier
15+
16+
```text
17+
/aiobotocore-bot:check-async-need --from=$LAST_SUPPORTED --to=$LATEST_BOTOCORE
18+
```
19+
20+
The skill emits a `CLASSIFICATION:` line plus a per-function rationale block. Capture both.
21+
22+
## Step 2: Write classification to /tmp/classification.json
23+
24+
Write a JSON object with exactly these fields:
25+
26+
- `verdict`: one of `"no-port"`, `"port-required"`, `"ambiguous"`, `"error"`
27+
- `summary`: a single short sentence summarizing the classifier's decision (≤120 chars). For
28+
`no-port`: name the dominant theme (e.g. "Model/schema-only updates for N services"). For
29+
`port-required`: name the function or feature that triggered (e.g. "New
30+
`auth_scheme_preference` threading through args + client requires async override").
31+
- `rationale`: a markdown table with columns `File | Function | Change | Verdict | Reason`,
32+
one row per function the classifier inspected. The downstream `open-pr` skill renders this
33+
table verbatim into the PR body — keep each Reason cell to ≤80 chars and don't pad the table
34+
column separators. **Use the minimum-separator table convention: `|-|-|-|-|-|`**.
35+
36+
If the classifier itself failed (returned `error: <reason>`), set `verdict` to `"error"` and
37+
put the error reason in `summary`. Leave `rationale` as the string `"(classifier failed)"`.
38+
39+
If `$DRY_RUN` is `true`, also write a copy of the per-function rationale block to the GitHub
40+
step summary (`$GITHUB_STEP_SUMMARY`) so the human running the dispatch sees it without
41+
clicking through to JSON.
42+
43+
## Restrictions
44+
45+
- **Do NOT make any code changes.** No file edits outside `/tmp/`.
46+
- **Do NOT create branches, commits, PRs, or comments.** This stage is read-only.
47+
- **Do NOT do any porting work.** That belongs to the downstream `sync` job — running it here
48+
would defeat the whole point of the model split.
49+
- If you find yourself reaching for `mcp__github_file_ops__commit_files`, `gh pr create`, or
50+
similar — STOP and just write the JSON.
51+
52+
## Honesty
53+
54+
Never claim `no-port` without inspecting every changed function in an overridden file. If you
55+
could not run the classifier (network error, missing tag), set `verdict: "error"` and let the
56+
sync job escalate via the feedback-issue path. A false `no-port` is worse than `ambiguous`.

.github/botocore-sync-prompt.md

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,25 @@ pyproject.toml for version info:
1010
- Current supported range: $CURRENT_LOWER — $LAST_SUPPORTED
1111
- Exclusive upper bound: $CURRENT_UPPER
1212

13+
The `classify` job (a separate Sonnet-driven stage that runs before this one) has already
14+
produced the async-need verdict — use it directly in Step 3, do NOT re-run the classifier:
15+
16+
- Verdict: $CLASSIFIER_VERDICT
17+
- One-line summary: $CLASSIFIER_SUMMARY
18+
- Per-function rationale (markdown table): $CLASSIFIER_RATIONALE
19+
20+
The classify job also pre-computed the list of aiobotocore source files whose botocore
21+
counterpart changed in the diff range — comma-separated, empty if none changed:
22+
23+
- Affected aiobotocore files: $AFFECTED_AIOBOTOCORE_FILES
24+
25+
Use this list as the starting point for Step 5 (port path) instead of re-running
26+
`git diff --name-only` and walking the mirror tree. The list excludes files without an
27+
existing aiobotocore mirror (those are out of scope per `port-tests` skill rules).
28+
29+
If `$CLASSIFIER_VERDICT` is empty (rare — only when the classify job failed entirely),
30+
re-run the classifier yourself in Step 3 as a fallback.
31+
1332
## Configuration
1433

1534
- ENABLE_BUMP: $ENABLE_BUMP (if false, bumps create a feedback issue instead of attempting code changes)
@@ -171,29 +190,37 @@ gh api repos/REPO/pulls/PR_NUM/reviews \
171190

172191
**No PRs at all:** proceed to Step 3.
173192

174-
## Step 3: Classify the botocore diff
193+
## Step 3: Use the pre-computed classifier verdict
175194

176195
**The classifier is the authority, not the PR title.** Historical PRs have been
177196
mislabeled (e.g. a "Bump" title on what was actually a no-port update). If you are
178197
operating on an existing PR whose title contradicts the classifier's verdict, update the
179198
PR title to match — don't preserve a wrong inherited label.
180199

181-
Run the classifier:
200+
The `classify` job has already run `/aiobotocore-bot:check-async-need` for the target
201+
range. Use `$CLASSIFIER_VERDICT` directly — do NOT re-invoke the classifier (that
202+
duplicates work the cheap Sonnet stage already did). The per-function rationale in
203+
`$CLASSIFIER_RATIONALE` is also pre-formatted as a markdown table — pass it verbatim to
204+
`/aiobotocore-bot:open-pr --classifier-verdicts=...` in Step 7.
205+
206+
Branch on `$CLASSIFIER_VERDICT`:
207+
208+
- `no-port` → Go to Step 4 (no-port path). Quote `$CLASSIFIER_SUMMARY` in the PR body as
209+
the async-need justification. Do NOT justify a no-port verdict with "functions not
210+
overridden" — that is the wrong test.
211+
- `port-required` → Go to Step 5 (port path).
212+
- `ambiguous` → Escalate via Step 9 with the ambiguous verdicts as feedback questions.
213+
- `error` → The classifier itself failed. Treat as `ambiguous`: escalate via Step 9 with
214+
the error message (in `$CLASSIFIER_SUMMARY`) as context. Never silently assume no-port.
215+
216+
**Fallback:** if `$CLASSIFIER_VERDICT` is empty (the classify job failed before producing
217+
output), re-run the classifier yourself:
182218

183219
```text
184220
/aiobotocore-bot:check-async-need --from=$LAST_SUPPORTED --to=$LATEST_BOTOCORE
185221
```
186222

187-
The skill diffs the two botocore versions, finds new/changed functions in overridden files, and returns one of:
188-
189-
- `no-port` → no async-need signals found. Go to Step 4 (no-port path). Quote the skill's summary line in the
190-
PR body as the async-need justification. Do NOT justify a no-port verdict with "functions not overridden" — that is the
191-
wrong test.
192-
- `port-required` → at least one new/changed function has async-need signals. Go to Step 5 (port path).
193-
- `ambiguous` → the classifier could not rule out async-need for one or more functions. Escalate via Step 9 with
194-
the ambiguous verdicts as feedback questions.
195-
- `error: <reason>` → the classifier itself failed (e.g. `/tmp/botocore` missing, tag not fetched). Treat as
196-
`ambiguous`: escalate via Step 9 with the error message as context. Never silently assume no-port.
223+
This should be rare; the classify job uses Sonnet and has its own retry policy.
197224

198225
### Major bump detection
199226

.github/claude-review-prompt.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,23 @@ Python, uv, and all dev dependencies are pre-installed.
229229
Never claim tests pass unless you ran them successfully. If you could not run tests, say so in the PR
230230
description. Do not use checkmarks for untested items.
231231

232+
### No fake memory
233+
234+
Each workflow run is **stateless** — there is no persistent agent memory across runs. Never claim to
235+
have "saved to memory", "saved as durable feedback / preference", "noted for future runs", or any
236+
similar phrasing that implies the next run will inherit your decision. Those claims are confabulated
237+
— the next run starts cold from the prompt + skill files in `.github/` and `plugins/`.
238+
239+
If a reviewer requests a behavioral change for future runs, only one of these is honest:
240+
241+
- Make the change in this PR by editing the relevant prompt or `SKILL.md` and include the diff in
242+
your response.
243+
- Acknowledge the request and say a follow-up PR to the prompt/skill is needed; do NOT promise the
244+
next run will behave differently absent that PR.
245+
- Stay silent on the meta-feedback if you can't act on it now.
246+
247+
This rule applies to every reply you post — review summaries, inline replies, top-level comments.
248+
232249
## Reference
233250

234251
Use the repository's CLAUDE.md for guidance on style and conventions. See docs/override-patterns.md for how

0 commit comments

Comments
 (0)