Squash the batch-implement blockers: nine fixes for the phase gate, the CI gate and the review-state card #501
No reviewers
Labels
No labels
agent-ready
bug
contract
documentation
duplicate
enhancement
good first issue
help wanted
invalid
needs-human
needs-input
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
iterative-works/iw-cli!501
Loading…
Reference in a new issue
No description provided.
Delete branch "batch-implement-blockers"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Bug-squashing batch over the
batch-implementblockers found while auditing the last three weeks of issues. Nine issues, one commit each, TDD throughout, landed sequentially so every change was made against a final tree rather than in parallel.Closes #467, #473, #472, #487, #474, #400, #465, #466, #337.
What changed, worst first
0441415phase-prasks the forge for the recorded pull request's head commit and refuses a url that is not on this branch. Previously aDuplicatecreate returned whatever url the issue-keyed card held — including an earlier, already-merged phase's — which the gate then waited on and--batchmerged.e0c2d42(workflowLabel, event)and keeps the newest, after collapsing attempts per run id. Runs tied at the newest instant are all kept.ci_gated_shais now cleared on every non-green exit and stamped with the commit actually gated.bd391d7ci_pending/ci_fixingmap to a newPhaseOutcome.AwaitCIinstead ofRecover, so waiting for CI no longer spends the--max-retriesbudget. Breaking — see below.a22104eci_wait_startedthat is unreadable or ahead of the clock no longer disables the total timeout silently. It is clamped, the wait stays bounded, and the gate says so on stderr.dc707d3format: date-timeis parsed, not merely type-checked. This is what stops a bad value reachingstartOfWaitthrough an ordinary write.9c158d0git pushputs only "failed to push some refs" on stderr; the hook writes its reasons to stdout, and both are now forwarded.138b099./iw ./test lintruns the pre-push hook's exact scalafix command, and is part of the default run. Green now means pushable.f3cf1a4review-state updatecan recordphase,stepandbranch, with matching--clear-*flags.004ecd2--itemsis repeatable and a comma is just a comma. A bullet containing a comma is no longer split into fragments.Verification
./mill core.test— 186/186./mill dashboard.test— 241/241scalafix --check --scala-version 3.3.7 --files core/ --files commands/— clean./iw ./test lint— passes, and its failure branch was proven by planting avarand observing the blockEvery fix took a genuine red step first. The two places a red could not be taken cleanly are stated in the commit trail: #473's "unreadable value must warn" assertion could not exist until the return type carried a warning, and #337 needed
PhaseArgs.repeatedArgto exist before the test file would compile (the production path was left on the old split for the red run, so the behaviour red is real).Three things that must happen outside this branch
iw-plugin/commands/batch-implement.scala:348-366matches all fourPhaseOutcomecases with no wildcard. Nothing there sets-Xfatal-warnings, so it still compiles and then throwsscala.MatchError: AwaitCIthe first time a batch observesci_pending— strictly worse than the "Exhausted N recovery attempts" message it replaces. It needscase PhaseOutcome.AwaitCI => <poll>plus aREQUIRES: iw-cli >=bump. This must not cross a release boundary unpatched. No shim was added, by decision.iw-cli-opsskill comma-join--itemstoday. Until they are rebuilt fromcommands/phase-commit.scala(whose USAGE line this PR updates), agents keep emitting one long bullet.phase/step/branch; nothing here makeswf-implementpass or read them.Review
/iterative-works:review-codeacross 8 skills, every finding then adversarially verified: 0 critical, 4 warnings, 18 suggestions. Nothing blocks merge. The four warnings:PhasePr.scala:177(flagged independently by architecture, api, error-handling and testing) — the guard answers "is this PR on my branch?" by comparing head commits, because the port carries no head branch. Two consequences: on GitLab the MRshais recomputed asynchronously after a push, so a legitimately-matching MR can be refused within that window; and the refusal message says "clear pr_url from the card", which deletes correct state if the cause was a transport failure. The refuse-don't-trust posture was a deliberate call; the destructive advice and the GitLab race were not part of it.CiGateCommand.scala:188— clearing a staleci_gated_shaon the hand-over path is untested. Proven by mutation: setting the flag tofalseon both sites leaves all 186 tests green.ScalafixGateTest.scala:31— the default-run assertion can silently stop testing the default run.schemas/review-state.schema.json:24— thephasedescription contradicts what the CLI writes (verbatim string, never a number).Convergent across three skills and confirmed independently:
PhaseArgs.repeatedArginherits!value.startsWith("--")from its sibling inReviewStateCliParser, so--items "--dev flag now required"is silently dropped — a narrower instance of the same silent-loss class #337 is about. Left as-is for consistency with the sibling parser; it wants its own issue.One verifier died on an API error, so a single
PhaseArgs.scala:16finding went unverified and is not counted above. Three other findings on the same function survived and say the same thing.Not in this batch
runStreamingreturns a killed child's signal code as if it were the child's own answer, so acore.testthat exceeds the 5-minute default is SIGTERMed by our own harness and reported as a test failure. That is the "PUSH BLOCKED over passing output" case, and it is not flakiness.🤖 Generated with Claude Code
https://claude.ai/code/session_013UpRzj7oiV1UDArn4hfu7U