feat(regenerate): batch path via Anthropic Message Batches API#21
Merged
Conversation
Adds opt-in `attune-author regenerate --batch` that submits all
polish requests for stale features as a single Anthropic batch
(~50% cost vs synchronous per-call pricing). Submit-and-detach
UX with a `--resume` partner that polls + splices results when
the user is ready.
CLI surface:
--batch submit; print batch ID + resume hint; exit
--resume poll + splice + write templates
--status one-shot status (supports --json)
--cancel cancel batch + remove state file
--force with --batch: overwrite an existing pending state
Argparse mutex enforces "exactly one batch-mode flag at a time".
Bare `regenerate` (no batch flags) prints a one-liner hint when
a state file exists but never auto-resumes.
State file lives at .help/.batch-state.json. Schema-versioned;
29-day retention check matches Anthropic's batch retention
window. On poll-loop timeout the state file is intentionally
KEPT so the next --resume picks up cleanly. SIGINT during
polling cancels the batch and re-raises.
Refactor: the synchronous path's render+write phases are
extracted to generator.prepare_polish_phase /
generator.apply_polish_results, and polish.build_polish_prompt
is shared between paths. Both paths produce byte-identical
on-disk templates for the same inputs (parity test in
tests/test_maintenance_batch.py guards the invariant).
Per-feature failure isolation: any errored depth fails the
whole feature (no templates written for it); other features in
the same batch succeed normally.
Tests: +57 new unit tests, +1 opt-in live integration test
(gated on ANTHROPIC_API_KEY + RUN_LIVE_BATCH=1, ~$0.02/run).
Full suite 670 pass / 32 skipped (live + skill-export
integration). Ruff clean on touched files.
Spec: specs/author-batch-maintain/{requirements,design,tasks}.md
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds opt-in
attune-author regenerate --batchfor cost-saving bulk help regen via Anthropic's Message Batches API (~50% cost vs synchronous per-call pricing). Submit-and-detach UX with a--resumepartner that polls + splices results when ready.Sequenced as item (3) from the 2026-05-08 enhancements briefing. Closes the briefing's three-item sequence (after attune-author#20 for skill-export and attune-rag#11 for native citations).
Spec:
specs/author-batch-maintain/{requirements,design,tasks}.md.CLI surface
Mutex-enforced flag set:
--batch | --resume | --status | --cancel. Modifiers:--force(with--batch: overwrite pending state file),--json(with--status: cron-friendly output).Resume ergonomics (the user's emphasis)
--resumereads.help/.batch-state.jsonautomatically.regenerateprints a hint when a state file exists; never auto-resumes silently.--resumepicks up cleanly.--statusis one-shot — query without committing to a wait.--jsonfor cron parsing.--cancelis one-shot — call SDK cancel + remove state file.--batchrefuses to overwrite an existing state file unless--forceis passed.What landed
attune_author.doc_gen._anthropic_batch— SDK wrapper withsubmit_batch/poll_batch, typedBatchPolishRequest/BatchPolishResult, adaptive timeout (min(30min, max(5min, 60·N/20))), SIGINT-cancels-and-reraises.attune_author.maintenance_batch—submit_maintenance_batch/resume_maintenance_batch/status_maintenance_batch/cancel_maintenance_batch, plusBatchStateschema and state-file lifecycle.attune_author.polish.build_polish_prompt— extracted prompt-building helper. Both paths build byte-identical prompts.attune_author.generator.prepare_polish_phase/apply_polish_results— extracted Phase 1 (render) and Phase 3 (write) helpers. Synchronous path now calls them too. Single source of truth for rendering.docs/regenerate-batch.md— when to use which path, fallback behavior, env-var overrides, cancel semantics, stale-state recovery.Tests
test_anthropic_batch.py) — golden-fixture parser, payload shape, full poll-loop state matrix, SIGINT cancel, adaptive timeouttest_maintenance_batch.py) — state file lifecycle, submit/resume/status/cancel, per-feature failure isolation, timeout-keeps-state, parity test (sync vs batch produce identical files)test_cli_batch.py) — argparse mutex, all four modes, --json shape, bare-command pending-batch hinttests/integration/test_batch_live.py, gated onANTHROPIC_API_KEY+RUN_LIVE_BATCH=1. Tagged@pytest.mark.live. Default-skipped; ~$0.02/run.Verification gate
V1 (Anthropic SDK class names) verified live against SDK 0.89.0:
client.messages.batchesMessageBatchexposesprocessing_status,request_counts,expires_at,cancel_initiated_at,results_urlMessageBatchIndividualResponsecarriescustom_id+result(discriminated union: Succeeded / Errored / Expired / Canceled)Pinned in code; recorded fixture at
tests/golden/batch_response.jsonregression-tests the parser.Test plan
RUN_LIVE_BATCH=1 pytest tests/integration -m liveto exercise end-to-end against the real Anthropic API🤖 Generated with Claude Code