feat(verbs): IPostconditionVerifier shared abstraction (Move #3, absorbs #193) - #198
Merged
Merged
Conversation
…rbs #193) Introduces IPostconditionVerifier — a shared, injectable check for `after this verb returns, origin must hold blob X at path Y`. This is the Class B bug pattern that produced #192 (manifest commit-and-push silently no-oping when local HEAD already had the blob but origin didn't). The verifier returns a discriminated union with three cases: - Satisfied: origin holds the expected blob at every path - NeedsPush: origin is missing one or more paths - Conflict: origin has different content for some paths Treats `git fetch` failure (most common: ref doesn't exist yet) as NeedsPush so first-push-of-a-fresh-branch goes through the recovery path. Applied to two consumers, killing the bug class everywhere it currently lives: - ManifestCommands.CommitAndPush — absorbs #193's fix verbatim, plus the Conflict path - PlanCommands.CommitAndPush — same shape, same Class B bug shape Both verbs now follow the Move #2 routing-style envelope (sentinel int/ string defaults, in-body validation, exit 0 with error_code on every failure path — never ConfigError or RoutingFailure). Adds: - PostconditionExpectation, PostconditionOutcome, IPostconditionVerifier, PostconditionVerifier (Polyphony.Postconditions namespace) - DI registration as singleton (uses IGitClient, no transport state) - PlanCommitAndPushResult.ErrorCode field for routing - FakePostconditionVerifier test fixture - CwdSerialCollection — xUnit collection that serializes test classes that mutate Environment.CurrentDirectory (avoids races between Manifest and Plan commit-and-push test classes) - Postconditions/PostconditionVerifierTests — full coverage of all three outcome cases plus fetch-failure and show-failure recovery - Skill doc: new `Post-condition verification` section in polyphony-cli-developer/SKILL.md with worked example Closes #192. Supersedes and absorbs #193 (#193 will be closed). 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
Introduces
IPostconditionVerifier— a shared, injectable check for "after this verb returns, origin must hold blob X at path Y". This is the Class B bug pattern that produced #192 (manifest commit-and-push silently no-oping when local HEAD already had the blob but origin didn't).This PR absorbs and supersedes #193 — #193's fix to
ManifestCommands.CommitAndPushbecomes a one-linepostconditions.VerifyAsync(...)call once the abstraction is in place, and the same fix is applied toPlanCommands.CommitAndPush(which had the identical Class B bug shape).Closes #192.
What
IPostconditionVerifierdoesReturns a discriminated union with three cases:
SatisfiedNeedsPushConflictTreats
git fetchfailure (most common: ref doesn't exist yet — first push of a fresh branch) asNeedsPushso the recovery path runs. Per-path read failures are treated the same way — push and let the real failure surface there if anything is actually broken.Consumers refactored
ManifestCommands.CommitAndPushgit fetch+git showguard added in #193postconditions.VerifyAsync(...)switchPlanCommands.CommitAndPushBoth verbs also now follow the Move #2 routing-style envelope: sentinel int/string defaults, in-body validation, exit
0witherror_codeon every failure path — neverConfigErrororRoutingFailure.Discriminated union shape
PostconditionOutcomemirrorsRebaseOutcome(inInfrastructure/Processes/):abstract record+sealed nested recordcases. Consumersswitchwith adefaultarm that throws — the compiler can't prove exhaustiveness across the assembly boundary, and a future case must fail loudly.Tests
PostconditionVerifierTests— full unit coverage of all three outcome cases, fetch-failure recovery, show-failure recovery, vacuous case (empty expectations → Satisfied without fetch), byte-exact comparison.ManifestCommandsCommitAndPushTests— fully rewritten. Uses the realPostconditionVerifier(GitClient(runner))to exercise the full fetch+show plumbing. Absorbs all 5 of fix(manifest): guard commit-and-push no-op against stale origin (#192) #193's test scenarios plus a Conflict-path test plus a sentinel-default test.PlanCommandsCommitAndPushTests— fully rewritten. UsesFakePostconditionVerifierto drive switch arms without re-stubbing fetch+show (that's covered by the verifier's own tests). Adds Class-B-parity tests for theLocalCleanButOriginMissingandLocalCleanButOriginConflictcases.CwdSerialCollection— new xUnit collection that serializes test classes that mutateEnvironment.CurrentDirectory(avoids races between Manifest and Plan commit-and-push test classes both wanting to chdir into a temp dir).FakePostconditionVerifier— test fixture for verbs that don't need the real verifier wiring.Total: 2880 unit tests pass, 101 Pester tests pass, jinja-resolver lint clean.
Skill doc
Adds a Post-condition verification — when origin must hold blob X section to
.github/skills/polyphony-cli-developer/SKILL.mdwith a worked example showing the verifier-switch pattern.After merge
sdlc/fix-manifest-commit-push-remote-guard.