Problema
Due difetti in .claude/workflows/implement-batch.js, entrambi osservati guidando il batch #277/#230/#281/#236/#234/#279 (PR #386–#391).
1. Un batch che non guida nulla riporta successo
args veniva normalizzato così: se stringa, JSON.parse in try/catch; al catch, _args = undefined; poi const STORIES = _args?.stories ?? [].
Chiamandolo con args: "#234 #236 #281 …" — la forma suggerita dalla riga di invocazione della skill, che interpola verbatim gli argomenti ricevuti — il parse falliva, STORIES diventava [], e il run usciva in ~30 ms con zero agent restituendo:
{ "contracts": [], "batch": [], "note": "PRs are ready-for-merge or escalated. Merge is the human gate…" }
Cioè la forma di un batch completato con successo, indistinguibile da un run reale in cui tutte le storie sono fallite. Il primo lancio del batch è andato a vuoto senza alcun segnale.
2. La guardia anti-duplicazione della first-review dipendeva dalla contabilità del chiamante
Il continuation probe (#373) era dentro if (resuming), dove resuming = Number.isInteger(story.prNumber) — quindi girava solo se il chiamante aveva passato prNumber nell'oggetto storia.
Un resume via Workflow({resumeFromRunId}) rieseguisce gli agent implement/PR dalla cache con gli stessi args: story.prNumber è assente, resuming è false, il probe non parte, firstReviewPosted resta false e il round-0 posta un'altra first-review su una PR che ne ha già una.
Osservato tre volte sulla stessa storia (#234, PR #390) attraverso tre cicli pausa/ripresa: quella storia è stata rivista da zero ogni volta invece di avanzare nei round di fix, ed è finita la meno progredita del batch — con 4 Major ancora aperti quando le altre erano a zero.
Impatto
- (1) fa perdere un lancio senza accorgersene; un orchestratore che riceve
batch: [] non ha modo di distinguere "input sbagliato" da "batch vuoto legittimo".
- (2) spreca un round di review
opus/xhigh per storia per ciclo di ripresa, e fa regredire il progresso della storia.
Fix atteso
- Validare l'input rumorosamente: lanciare su stringa non-JSON, args assente, oggetto senza
stories, o storia priva di id/title/branch — con un messaggio che dica la forma richiesta e perché title/branch non sono derivabili (il sandbox non ha accesso a gh/filesystem). Una lista esplicitamente vuota resta un no-op legittimo.
- Legare il probe all'esistenza della PR, non a
story.prNumber: un probe sonnet/low per storia per run costa molto meno di una review opus duplicata, e su una storia fresh entrambi i segnali tornano false lasciando il comportamento identico.
- Rendere il contratto degli argomenti visibile nel
meta del workflow, così il chiamante lo legge prima di invocare.
Note
Il difetto (1) non è "il chiamante ha sbagliato": la riga di invocazione della skill interpola gli argomenti ricevuti dentro Workflow({args: …}) senza convertirli, quindi la forma sbagliata è raggiungibile per costruzione.
Problema
Due difetti in
.claude/workflows/implement-batch.js, entrambi osservati guidando il batch #277/#230/#281/#236/#234/#279 (PR #386–#391).1. Un batch che non guida nulla riporta successo
argsveniva normalizzato così: se stringa,JSON.parseintry/catch; alcatch,_args = undefined; poiconst STORIES = _args?.stories ?? [].Chiamandolo con
args: "#234 #236 #281 …"— la forma suggerita dalla riga di invocazione della skill, che interpola verbatim gli argomenti ricevuti — il parse falliva,STORIESdiventava[], e il run usciva in ~30 ms con zero agent restituendo:{ "contracts": [], "batch": [], "note": "PRs are ready-for-merge or escalated. Merge is the human gate…" }Cioè la forma di un batch completato con successo, indistinguibile da un run reale in cui tutte le storie sono fallite. Il primo lancio del batch è andato a vuoto senza alcun segnale.
2. La guardia anti-duplicazione della first-review dipendeva dalla contabilità del chiamante
Il continuation probe (#373) era dentro
if (resuming), doveresuming = Number.isInteger(story.prNumber)— quindi girava solo se il chiamante aveva passatoprNumbernell'oggetto storia.Un resume via
Workflow({resumeFromRunId})rieseguisce gli agent implement/PR dalla cache con gli stessi args:story.prNumberè assente,resumingèfalse, il probe non parte,firstReviewPostedrestafalsee il round-0 posta un'altra first-review su una PR che ne ha già una.Osservato tre volte sulla stessa storia (#234, PR #390) attraverso tre cicli pausa/ripresa: quella storia è stata rivista da zero ogni volta invece di avanzare nei round di fix, ed è finita la meno progredita del batch — con 4 Major ancora aperti quando le altre erano a zero.
Impatto
batch: []non ha modo di distinguere "input sbagliato" da "batch vuoto legittimo".opus/xhighper storia per ciclo di ripresa, e fa regredire il progresso della storia.Fix atteso
stories, o storia priva diid/title/branch— con un messaggio che dica la forma richiesta e perchétitle/branchnon sono derivabili (il sandbox non ha accesso agh/filesystem). Una lista esplicitamente vuota resta un no-op legittimo.story.prNumber: un probesonnet/lowper storia per run costa molto meno di una reviewopusduplicata, e su una storia fresh entrambi i segnali tornanofalselasciando il comportamento identico.metadel workflow, così il chiamante lo legge prima di invocare.Note
Il difetto (1) non è "il chiamante ha sbagliato": la riga di invocazione della skill interpola gli argomenti ricevuti dentro
Workflow({args: …})senza convertirli, quindi la forma sbagliata è raggiungibile per costruzione.