Skip to content

feat(plan): add detect-state verb (P7b-i-c) - #88

Merged
PolyphonyRequiem merged 1 commit into
mainfrom
sdlc/plan-detect-state
May 6, 2026
Merged

feat(plan): add detect-state verb (P7b-i-c)#88
PolyphonyRequiem merged 1 commit into
mainfrom
sdlc/plan-detect-state

Conversation

@PolyphonyRequiem

Copy link
Copy Markdown
Owner

P7b-i-c — polyphony plan detect-state verb

Re-opened against main after #85/#86 merged. Original PR #87 was auto-closed by GitHub when its base branch was deleted.

What this adds

polyphony plan detect-state --root-id N --item-id N — the routing primitive plan-level.yaml calls at workflow entry to discover where the plan workflow left off.

State machine

State Workflow action
not_started run the architect
awaiting_review poll for review status
stale_generation re-run architect against current ancestor plans
closed_unmerged surface to human gate
merged_unseeded run plan seed-children
complete done
error surface to human gate

Composition

Pure composition — no new infrastructure: BranchNameBuilder, git ls-remote, gh pr list/view, RunManifestStore.Parse + PlanPrFrontMatter.Parse, twig show.

Tests

16 new tests cover full state machine, branch naming, stale-snapshot/current-snapshot/manifest-missing, custom planned-tag override.

Adds polyphony plan detect-state --root-id N --item-id N — the routing
primitive plan-level.yaml will call at workflow entry to discover where
the plan workflow left off.

State machine (precedence order):
  - error              — arg validation or git remote unavailable
  - not_started        — no plan PR exists for this branch
  - awaiting_review    — open plan PR with current ancestor snapshot
  - stale_generation   — open plan PR whose ancestor_plan_generations
                         lags the manifest's plan_generations ledger
  - closed_unmerged    — PR was closed without merging
  - merged_unseeded    — PR merged, but parent lacks polyphony:planned tag
  - complete           — PR merged AND parent carries the planned tag

Composes existing primitives: BranchNameBuilder for branch naming,
git ls-remote for branch existence, gh pr list (highest-numbered wins
for "latest"), gh pr view for full state, RunManifestStore.Parse +
PlanPrFrontMatter.Parse for stale-generation detection (manifest read
from origin/feature/{root} via git show — same idiom OpenPlanPr will
use after #85 merges), twig show for the planned-tag check.

Always exits 0 (routing-style verb); workflow branches on result.state.
plannedTag default is polyphony:planned; overridable for non-default
namespaces.

Adds IGhClient to PlanCommands primary constructor; updates the five
existing PlanCommands test sites accordingly. Stacks on top of the
sdlc/plan-commit-and-push branch (PR #86) which added IGitClient.

16 tests cover the full state machine, both branch naming variants,
the stale-snapshot/current-snapshot/manifest-missing trio, and the
custom planned-tag override. 1798 total tests in the suite (3 pre-
existing ProcessRunner environment flakes, unrelated).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@PolyphonyRequiem
PolyphonyRequiem merged commit 0e726bc into main May 6, 2026
1 check failed
@PolyphonyRequiem
PolyphonyRequiem deleted the sdlc/plan-detect-state branch May 6, 2026 18:11
PolyphonyRequiem added a commit that referenced this pull request May 6, 2026
…b-i-d) (#89)

Phase 3 P7b-i-d. Replaces v1.0.1's in-workflow LLM-verdict planning loop
(technical_reviewer / readability_reviewer / review_router / plan_approval)
with the platform-native PR-review flow built on the substrate verbs
shipped in P7b-i-a / P7b-i-b / P7b-i-c (and the four prior plan/branch/pr
verbs).

Lifecycle per invocation:
  1. depth_guard -> guidance_loader -> root_resolver -> type_loader
  2. ancestor_chain -> state_detector
  3. State-driven routes:
       not_started      -> architect -> write_plan -> ensure_plan_branch
                           -> commit_and_push -> open_plan_pr
                           -> plan_reviewer (advisory comment)
                           -> poll_status (single-shot)
       awaiting_review  -> poll_status
       stale_generation -> architect (re-author)
       merged_unseeded  -> seeder -> child_router (recurse)
       complete         -> child_router (recurse only)
       closed_unmerged  -> blocked exit
  4. poll_status routes by review state:
       approved (no parent change)  -> merge_plan_pr -> seeder
       approved + parent change     -> parent_change_blocked_exit (P8 stub)
       changes_requested            -> revise_counter -> architect (cap 5)
       pending                      -> pending_review_gate
       merged                       -> seeder
       closed                       -> closed_unmerged_gate

Key design decisions (per files/p7-design-doc.md):
- Q1 plan path:        plans/plan-{item_id}.md (handled by write-plan verb)
- Q2 reviewer:         single advisory plan_reviewer + human approval (option 2)
- Q3 pending review:   in-workflow human_gate (option a) -- DEVIATES from
                       design default (option b exit-with-pending) because
                       polyphony-full's state_detector still uses the OLD
                       single-string phase model and would infinite-loop
                       on awaiting_review. Switch to (b) lands with Phase 2c.
- Q4 root_id thread:   root_resolver derives once via 'root resolve'; all
                       downstream verbs read root_resolver.output.resolved_root_id
- Q5 review posting:   plan_reviewer is a script agent calling
                       'gh pr review --comment' once and returning
- Q6 revise loop:      capped at 5 iterations with revise_cap_gate
- Q7 output shape:     blocked exits use human_gate; reviewer-driven outputs
                       surface via gate Markdown
- Q8 parent change:    refused at this PR; honest 'P8 not implemented' gate

Architect output schema unchanged ({plan, children, open_questions, summary})
so the existing architect-plan-level.md prompt continues to work.

Open-questions policy (open_questions_policy / open_questions_counter /
open_questions_answer_counter / open_questions_gate) preserved verbatim
from v1.

Recursion preserved: plan_children_group for_each invokes ./plan-level.yaml
per plannable child with depth+1.

All error sites have explicit human_gate fallbacks (root_resolver_error_gate,
type_loader_error_gate, state_detector_error_gate, write_plan_error_gate,
ensure_branch_error_gate, commit_error_gate, open_pr_error_gate,
poll_error_gate, merge_error_gate, stale_generation_gate,
parent_change_blocked_exit, closed_unmerged_gate).

Drive-by: fix pre-existing build break in
PlanCommandsWritePlanTests.CreateCommand() -- it was missing IGitClient and
IGhClient args added to PlanCommands ctor in P7b-i-c (#88). Main has been
red since that merge; this PR brings the C# suite back to 1809/1809 green.

Validation:
- dotnet test: 1809/1809 pass (was: build break on main)
- lint-plan-level.ps1: 10/10 checks pass
- lint-plan-level.Tests.ps1: 5/5 Pester tests pass
- lint-strict-undefined.ps1: 10 workflows scanned clean
- lint-type-agnostic.ps1 -Surface all: 42 files scanned clean
- lint-root-routing.ps1: 7 phases / 3 sub-workflows validated

Pre-existing failure NOT addressed by this PR:
- lint-implement-pg.ps1 expects claude-opus-4.7-1m-internal but YAML has
  claude-opus-4.6 / claude-opus-4.7. Same failure on main. Tracked
  separately.

Co-authored-by: Daniel Green <dangreen@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant