Skip to content

Plan lifecycle: edit owns no step status once work starts #42

Description

@DocksDocks

Goal

Make edit freeze the Steps table's execution state once the plan is past planned: no Status, Effect, display-number, or Depends change, no row removal, and new rows append-only born planned - so STEP_TRANSITIONS, dependency ordering, and the non-local ask gate cannot be bypassed by body edits; plan.mjs step becomes the only writer of step state after work starts.

Mode: plan-and-implement

Research

  • Advisory-directed follow-up to plan Plan lifecycle: closed-plan step repair and edit regression guard #40. The Plan lifecycle: closed-plan step repair and edit regression guard #40 guard refuses only terminal-to-non-terminal regressions; edit can still write planned -> done, in-flight -> planned, or any other move STEP_TRANSITIONS forbids, because checkPlan validates structure, never transitions.
  • Gating on the CURRENT status alone is insufficient twice over: ongoing -> blocked -> ongoing parks and resumes, and blocked -> planned and blocked -> drafting are legal transitions (STATUS_TRANSITIONS), so a started plan can legally RETURN to an editable label and reopen unrestricted editing. The freeze therefore uses an irreversible started predicate: the record status is not drafting or planned, OR the issue's label event history shows plan:ongoing was ever applied. Label events are GitHub-owned timeline facts no later transition or body write can erase; the history query runs only on the otherwise-editable path (drafting or planned), so settled-phase edits pay no extra call when already frozen.
  • A new Steps row born with a non-planned status on a started plan is the same bypass (a step can be conjured pre-done), so on a started OPEN plan a new row must carry planned and append after existing rows. On a CLOSED plan (finished and every other closed derivation) new rows are refused entirely: post-merge mutation is limited to terminal repair via plan.mjs step, and new work belongs to a follow-up plan - otherwise an appended planned row plus finished-plan step ... done smuggles unreviewed work into a merged record.
  • Freezing Status alone still lets edit bypass the Goal's guarantees: removing a non-terminal row fakes completion (archive requires every step terminal), rewriting a Depends cell reorders execution, renumbering display numbers silently retargets every dependency reference, and rewriting Effect (push -> local) suppresses the in-session ask the manager fires off that cell before running a non-local step. Past planned, an existing row's presence, display number, Depends, Effect, and Status are all frozen; Task, Files, and Done when stay editable as prose.
  • Correction routes replace deletion and rewriting on a live plan: an obsolete or mis-classified step is retired with plan.mjs step <n> <id> skipped, and a corrected replacement row is appended born planned. Appended rows are additive and auditable in the record and the candidate diff; nothing reviewed can be silently weakened. On a closed plan the only correction is terminal repair; new work takes a follow-up plan.
  • While drafting or planned, Steps tables are freely redrafted - statuses are all planned in practice and the state machine has not started; the guard must not constrain those phases.
  • The Plan lifecycle: closed-plan step repair and edit regression guard #40 terminal-repair path (step accepts terminal targets on a finished plan) stays the sanctioned repair route and is unaffected; this change makes it the ONLY route on such plans.
  • Verified consumers: the manager writes ## Review and ## Verification Results via edit while ongoing - those edits leave Status cells unchanged and must keep passing. scripts/tests/plan-cli.mjs has Plan lifecycle: closed-plan step repair and edit regression guard #40 assertions (regression refusal, terminal-preserving row addition) whose fixtures must be re-based on the stricter rule.

Steps

# Id Task Files Depends Effect Status Done when
1 guard Replace the terminal-only regression guard in editPlan with an execution-state freeze once work has started: Status, Effect, row presence, display numbers, and Depends frozen for existing rows; new rows append-only and born planned on open started plans, refused on closed plans; started = current status past planned OR plan:ongoing ever applied per label events plugins/plan-lifecycle/skills/productivity/plan-manager/scripts/plan.mjs - local done edit refuses any Status or Effect change, row removal, display or Depends rewrite, any non-planned or non-appended new row on a started open plan, and any new row on a closed plan - including after an ongoing-to-blocked-to-planned round-trip - with an error naming plan.mjs step
2 tests Rework the #40 edit-guard assertions and add bypass coverage scripts/tests/plan-cli.mjs, scripts/tests/fixtures/gh-stub/gh 1 local done plan-cli exit 0 with refusal coverage for planned-to-done, in-flight-to-planned, row removal, a Depends rewrite, an Effect downgrade, an edit after an ongoing-to-blocked-to-planned round-trip, and a row appended to a finished plan, drafting-phase edits still free, and status-preserving ongoing edits still passing
3 contract_docs Sync the contract, CLI table, and mirrored surfaces to the stricter rule, and pin the new ownership clause in the phase-sync test plugins/plan-lifecycle/skills/productivity/plan-manager/references/plan-contract.md, plugins/plan-lifecycle/skills/productivity/plan-manager/SKILL.md, docs/PLAN.md, plugins/plan-lifecycle/skills/productivity/plan-workspace/references/plan-md-template.md, scripts/tests/plan-skill-phases.mjs 1 local done plan-skill-phases exit 0 with a new pinned clause asserting the execution-state freeze wording across all four surfaces, and every surface states edit never changes step state once work starts
4 gate Re-sync content hashes and run the full repo gate plugins/plan-lifecycle/skills/, scripts/ 1, 2, 3 local done node scripts/ci.mjs exit 0 (diff touches repo-lane scripts/tests)

Acceptance

ID Command Expected
A1 node scripts/tests/plan-cli.mjs Exit 0 including refusals for planned-to-done, in-flight-to-planned, row removal, a Depends rewrite, an Effect downgrade, a post-round-trip edit on a started plan, and a row appended to a finished plan, plus a free Steps redraft while drafting and a status-preserving edit while ongoing
A2 node scripts/tests/plan-skill-phases.mjs Exit 0 with contract, SKILL table, docs/PLAN.md, and template synchronized, and the new execution-state freeze clause pinned across all four surfaces
A3 node scripts/ci.mjs Exit 0 (full gate; repo-lane files change)
A4 node plugins/plan-lifecycle/test/selftest.mjs Exit 0

Do not touch

  • setStepStatus semantics from plan Plan lifecycle: closed-plan step repair and edit regression guard #40 (terminal repair on finished plans) - already reviewed and landed.
  • archivePullRequestReferences latest-closure semantics - load-bearing, out of scope.
  • docs/plans/finished/ - frozen history.
  • Manifest versions and release tooling - release is a separate action.

Open questions

None

Review

Review records are stored in issue comments.

Verification Results

  • A1 node scripts/tests/plan-cli.mjs - exit 0, including refusals for planned-to-done via edit on an ongoing plan, row removal, a Depends rewrite, an Effect downgrade, a post-round-trip edit after plan:ongoing label history, and a row appended to a finished plan, plus a free Steps redraft while drafting (blocked-only history included) and a status-preserving edit while ongoing. Every refusal verified exit 1, unchanged body, no body-write call, and unchanged provenance.
  • A2 node scripts/tests/plan-skill-phases.mjs - exit 0 with contract, SKILL table, docs/PLAN.md, and template synchronized, and the new execution-state freeze clause pinned across all four surfaces.
  • A3 node scripts/ci.mjs - exit 0 (full gate; repo-lane files change): All ci.mjs checks passed - 2 plugin(s) + repo-wide.
  • A4 node plugins/plan-lifecycle/test/selftest.mjs - exit 0.
  • Session advisory disposition: widening "started" to any plan:blocked history was declined - pre-start parking is not started work and would freeze legitimate planned-phase repair redrafts; the boundary is pinned by a dedicated blocked-only-history acceptance test instead.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions