describe-batch.mjs has no way to give up on a domain that is failing systematically. A bad describeCommand, a wrong idField, or an expired token makes every describe in the domain fail — and the loop works through the entire asset list anyway. On a large domain that is well over a thousand pointless CLI spawns before anything reports a problem.
There is already a stop rule, and it doesn't cover this
skills/setup/SKILL.md:529:
Stop rule for persistent failures: if a re-invocation documents 0 assets and its failures list names the same NON-EMPTY set of keys as the run before it, stop re-invoking — those failures are deterministic; report the failed keys with their recorded errors instead of looping. A run that documents 0 with an EMPTY failures list and budgetExhausted: true … is progress — so re-invoke.
That rule is correct, but it operates between invocations and it is prose the model executes — it requires holding two consecutive run summaries and diffing them. It protects against re-invoking a doomed domain forever. It cannot stop a single long run that is already doomed, because it can only fire once that run has ended.
The case it misses is the common one: a token that dies mid-run. The CLI's token stops working at roughly half its printed lifetime (skills/setup/SKILL.md Phase 1 token pre-flight), so on a long domain this is routine rather than exceptional. Every describe after that moment fails identically, and the run keeps spawning.
The change
Abort the current domain after 5 consecutive retryable failures, and say so in the summary.
The existing failure taxonomy already decides what counts:
- Retryable failures (timeout, transport, unexpected output — the classes
describe-batch.mjs already marks failed) count toward the limit. A success resets the counter to zero.
- PERMANENT gaps — a field the CLI refuses under every spelling — do not count. They are recorded on the item, never retried, and explicitly non-blocking; the entry is still marked documented.
- Budget exhaustion is not a failure. It is progress, and the existing rule already says so.
Five is chosen to be high enough that a handful of genuinely broken assets in an otherwise healthy domain doesn't abort the run, and low enough that a systemic failure costs seconds instead of tens of minutes.
On abort: stop the domain loop, leave everything already marked exactly as it is (the per-asset durability guarantee is unchanged), and report the abort distinctly in the run summary so the caller can tell it apart from normal completion. Nothing about resumption changes — the next invocation picks up where it stopped, as it does after budget exhaustion.
Where
plugins/gs-superadmin/scripts/describe-batch.mjs — the per-asset loop and the summary it emits.
plugins/gs-superadmin/skills/setup/SKILL.md — the stop-rule paragraph should note that the script now enforces a within-run limit, so the model-side rule is about re-invocation only.
Testing
A fixture where every describe fails should abort after exactly 5 attempts rather than walking the full asset list, and the summary should name the abort. A fixture with 4 failures followed by a success should run to completion — that's the boundary worth pinning, since an off-by-one here either aborts healthy runs or never fires.
Worth confirming that an aborted run and a budget-exhausted run remain distinguishable in the summary, since the skill's existing logic branches on exactly that difference.
describe-batch.mjshas no way to give up on a domain that is failing systematically. A baddescribeCommand, a wrongidField, or an expired token makes every describe in the domain fail — and the loop works through the entire asset list anyway. On a large domain that is well over a thousand pointless CLI spawns before anything reports a problem.There is already a stop rule, and it doesn't cover this
skills/setup/SKILL.md:529:That rule is correct, but it operates between invocations and it is prose the model executes — it requires holding two consecutive run summaries and diffing them. It protects against re-invoking a doomed domain forever. It cannot stop a single long run that is already doomed, because it can only fire once that run has ended.
The case it misses is the common one: a token that dies mid-run. The CLI's token stops working at roughly half its printed lifetime (
skills/setup/SKILL.mdPhase 1 token pre-flight), so on a long domain this is routine rather than exceptional. Every describe after that moment fails identically, and the run keeps spawning.The change
Abort the current domain after 5 consecutive retryable failures, and say so in the summary.
The existing failure taxonomy already decides what counts:
describe-batch.mjsalready marksfailed) count toward the limit. A success resets the counter to zero.Five is chosen to be high enough that a handful of genuinely broken assets in an otherwise healthy domain doesn't abort the run, and low enough that a systemic failure costs seconds instead of tens of minutes.
On abort: stop the domain loop, leave everything already marked exactly as it is (the per-asset durability guarantee is unchanged), and report the abort distinctly in the run summary so the caller can tell it apart from normal completion. Nothing about resumption changes — the next invocation picks up where it stopped, as it does after budget exhaustion.
Where
plugins/gs-superadmin/scripts/describe-batch.mjs— the per-asset loop and the summary it emits.plugins/gs-superadmin/skills/setup/SKILL.md— the stop-rule paragraph should note that the script now enforces a within-run limit, so the model-side rule is about re-invocation only.Testing
A fixture where every describe fails should abort after exactly 5 attempts rather than walking the full asset list, and the summary should name the abort. A fixture with 4 failures followed by a success should run to completion — that's the boundary worth pinning, since an off-by-one here either aborts healthy runs or never fires.
Worth confirming that an aborted run and a budget-exhausted run remain distinguishable in the summary, since the skill's existing logic branches on exactly that difference.