fix(branch): flush staged twig state transitions so workflow reads see fresh ADO (AB#3126) - #339
Merged
Merged
Conversation
…e fresh ADO (AB#3126) Root cause: `twig state` only stages locally; without a follow-up `twig sync` the change sits in twig's pending queue and never reaches ADO. `polyphony validate` then reads stale `Proposed` state via IWorkItemRepository (which doesn't sync) and refuses `implementation_complete` even though `next-impl` already issued the `begin_implementation` transition. Verb-level fixes: * `branch next-impl` -- SyncAsync after SetStateAsync so the staged transition is durable before return. * `branch close-scope` -- batched post-loop SyncAsync (guarded by closed.Count > 0) so multiple transitions amortize to a single push. Workflow YAML fixes (implement-merge-group.yaml): * `primary_completer` script: pre-validate `twig sync` (fresh read), post-state `twig sync` (durable write), plus `ErrorActionPreference = Stop` and `PSNativeCommandUseErrorActionPreference = true` so a failed sync halts rather than silently propagating stale state. * `primary_router`: explicit error route to a new primary_router_error_gate before the catch-all. Without this, a failed next-impl would route `error` through the catch-all to dependency_check, silently treating the failure as `all items done` and skipping every remaining task. * New `primary_router_error_gate` (human_gate, modeled on mg_pr_failed_gate_ado) surfaces the failure with retry/abort options. Convention documented in `.github/skills/polyphony-cli-developer/SKILL.md` -- new `State-mutation durability` section, sister-pattern to the existing `Post-condition verification` section. Tests: 3 new regression tests using runner.Invocations ordering (NextImpl HappyPath flush, close-scope batched flush, close-scope no-op skip). Full Polyphony.Tests suite still 3390/3394 pass (4 pre-existing skips); all lints green; conductor validate confirms the workflow. Follow-ups filed under AB#3104: AB#3127 (ScopeCommands), AB#3128 (SeedChildren parent tag), AB#3129 (close_mark_satisfied + terminal_satisfied workflow scripts). AB#3130 filed under AB#3085 for the Polyphony.csproj relative-ProjectReference quirk discovered while building inside the manual worktree. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PolyphonyRequiem
added a commit
that referenced
this pull request
May 12, 2026
…8) (#340) Sister-bug to AB#3126. `PlanCommands.SeedChildren` mutates the parent's tags via `ITwigClient.PatchFieldsAsync` (stamping `polyphony:planned` and any `polyphony:facets=...` override) but does NOT call `SyncAsync` afterward. The change is staged in twig's local cache + pending queue but never pushed to ADO. The next workflow step that reads the parent's tag (e.g. `state_detector` in `apex-driver.yaml` checking `polyphony:planned`) sees stale tag state. This interacts with the AB#3106 children-seeded heuristic -- the architect emits `polyphony:planned` but `state_detector` re-entry doesn't always see it without a sync. Verb-level fix: * `plan seed-children` -- `SyncAsync` after `PatchFieldsAsync` so the staged parent-tag mutation is durable before the verb returns. Guarded inside the existing `if (addPlanned || facetsTagSet)` block so the no-op idempotent path still skips the round-trip. Tests: 2 new regression tests using `runner.Invocations` ordering, mirroring the AB#3126 pattern (`BranchCommandsNextImplTests` / `BranchCommandsCloseScopeTests`): * `SeedChildren_AfterParentTagPatch_FlushesViaSync` -- asserts a `twig sync` invocation occurs after the `twig patch` for the parent tag. * `SeedChildren_NoParentTagPatch_DoesNotFlush` -- belt-and-suspenders; when no patch was needed (tag already present), no flush either. `StubPatchOk` extended to also stub the follow-up sync, and a new `StubSync` helper mirrors the BranchCommands pattern. Convention: follows the `State-mutation durability` section in `.github/skills/polyphony-cli-developer/SKILL.md` (added by PR #339). Tests: full Polyphony.Tests suite 3392/3396 pass (4 pre-existing skips); 2 flaky environmental tests (`ProcessRunnerTests.RunAsync_TimeoutAfter EmittedOutput_ThrowsProcessCanceledWithBufferedOutput` and `LifecycleRouterScriptTests.IsRoot_FalseWhenWorkItemDiffersFromApex`) intermittently fail under suite contention but pass in isolation. All 8 lint scripts green. Related: * AB#3126 -- original `BranchCommands.NextImpl` + `close-scope` fix (PR #339, commit f5f3aed). * AB#3127 -- `ScopeCommands` sibling site (separate PR). * AB#3129 -- `close_mark_satisfied` + `terminal_satisfied` workflow scripts (separate PR). * AB#3104 -- Bucket-C policy controllability umbrella. Co-authored-by: Daniel Green <dangreen@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PolyphonyRequiem
added a commit
that referenced
this pull request
May 12, 2026
…+ terminal_satisfied (AB#3129) (#341) Sister-fix to AB#3126. Two workflow scripts in `.conductor/registry/workflows/` invoked `twig state` to transition work items but did NOT follow up with `twig sync`, leaving the transition staged in twig's local pending queue rather than pushed to ADO. Subsequent processes reading ADO without first calling sync saw stale state. Fixed sites ----------- * `apex-driver.yaml` > `close_mark_satisfied` (apex-level close-out): added `twig sync` after the `twig state` invocation so the apex's terminal-state transition is durable in ADO before the workflow terminates. Also added the fail-fast prologue (`ErrorActionPreference` + `PSNativeCommandUseErrorActionPreference`) -- reinforces the AUDIT AB#3066 fail-loud design for this script. * `apex-item-dispatch.yaml` > `terminal_satisfied` (per-item wave corridor): added `twig sync` after the `twig state` invocation inside the existing try/catch, plus the fail-fast prologue. The AB#3066 observable-error envelope still applies -- a failed sync now surfaces via stderr + the `error` / `error_code` output fields, allowing the apex driver outer loop to re-evaluate next-ready on the following pass. No additional unflushed sites found in the other 11 workflow YAMLs; `implement-merge-group.yaml` > `primary_completer` was fixed by PR #339, the only other `twig state` call site in workflow scripts. New lint check -------------- Added Check 9 to `lint-apex-driver.ps1` to pin this convention going forward: every PowerShell `script` node in the apex-driver suite that calls `twig state` MUST declare the fail-fast prologue and follow the `twig state` with a `twig sync` later in the body. Five new Pester assertions cover the rule (two real-YAML pins on the two fixed sites, three synthetic mutation tests for missing-sync / missing-prologue / twig-note-only-no-trigger). Tests ----- * dotnet test Polyphony.slnx -c Release: 3392 pass / 4 pre-existing skip * Invoke-Pester .conductor/registry/tests: 361 pass / 2 fail (both pre-existing on main -- `lint-plan-level` open_questions policy check, unrelated to AB#3129) * Invoke-Pester tests/: 143 pass / 0 fail * conductor validate apex-driver.yaml: exit 0 * conductor validate apex-item-dispatch.yaml: exit 0 Cross-references: AB#3126 (parent class-of-bug, fixed by PR #339), AB#3127 + AB#3128 (sibling instances filed under AB#3104; AB#3128 landed in PR #340 just before this PR). Co-authored-by: Daniel Green <dangreen@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.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
Fixes AB#3126 --
primary_completerfailing with ""must be in InProgress state category, but is in Proposed"" in dogfood Run #7.Root cause (corrected)
The original AB#3126 issue body claimed ""no upstream step transitions the task from Proposed to InProgress"". That was wrong.
branch next-implALREADY does thebegin_implementationtransition viatwig.SetStateAsync-- the actual bug is that the staged change never reached ADO beforeprimary_completer'spolyphony validateran.twig stateonly stages locally; withouttwig syncthe change sits in twig's pending queue.ValidateCommandreads viaIWorkItemRepositorywithout syncing, so it saw staleProposedand refusedimplementation_complete. Empirically: failure timestamp 14:12:28; ADO state-change timestamp on the same item 14:17 (after my manual sync intervention while filing the issue).Changes
Verb-level (BranchCommands)
branch next-impl-- post-stateSyncAsyncso the staged transition is durable before return.branch close-scope-- batched post-loopSyncAsync(guarded byclosed.Count > 0so an empty loop doesn't push).Workflow YAML (implement-merge-group.yaml)
primary_completerscript:twig syncBEFOREpolyphony validate(fresh read),twig syncAFTERtwig state(durable write), plusErrorActionPreference = StopandPSNativeCommandUseErrorActionPreference = trueso a failed sync halts rather than silently propagating stale state.primary_router: explicit error route to a newprimary_router_error_gateBEFORE the catch-all. Without this, a failednext-implwould routeerrorthrough the catch-all todependency_check, silently treating the failure as ""all items done"" and skipping every remaining task -- a separate latent bug surfaced by rubber-duck critique.primary_router_error_gate(human_gate, modeled onmg_pr_failed_gate_ado) with retry / abort options.Convention
.github/skills/polyphony-cli-developer/SKILL.mdgains a ""State-mutation durability -- when ADO must hold transition X"" section, sister-pattern to the existing ""Post-condition verification"" section. Verbs that mutate state viaITwigClient.SetStateAsync/PatchFieldsAsyncand whose callers need that mutation to be visible to a different process MUST flush viaSyncAsyncbefore returning.Tests
Three new regression tests using
runner.Invocationsordering assertions:NextImpl_HappyPath_FlushesStagedTransitionAfterSetStateCloseScope_BatchedTransitions_FlushesOnceAfterLoopCloseScope_NoTransitions_DoesNotFlushFull Polyphony.Tests suite: 3390/3394 pass (4 pre-existing skips). All lints green (jinja-resolver, prose-children, lifecycle-router-coverage, type-agnostic, no-tracked-state).
conductor validateconfirms the workflow.Anti-fix
The original issue body proposed adding a
primary_starterworkflow node beforeimpl_branch_ensure. That would mask the underlying durability bug rather than fix it, and would leave every other staged-but-unflushed mutation site silently broken. The fix belongs at the C# verb layer, with the workflow YAML carrying complementarytwig synccalls in script nodes.Out of scope (filed as follow-ups)
Other unflushed mutation sites discovered during review -- same bug shape, deferred for surgical scope:
ScopeCommands.TagMutationAsyncPlanCommands.SeedChildrenparent tag/facetapex-driver.yamlclose_mark_satisfied+apex-item-dispatch.yamlterminal_satisfiedworkflow scriptsPolyphony.csprojrelativeProjectReferencetotwig2fails in per-run worktrees (filed under AB#3085)Sibling PR
Parent fork session is working on AB#3125 (scope-review deadlock) which also touches
implement-merge-group.yaml-- but in the scope-review section (~line 832), not near the changes here (lines 240, 591-619, and the new gate insertion). Should auto-merge.Verification
dotnet buildcleandotnet test-- 3390 passedconductor validate-- passesAB#3126