fix(#3311): milestone lock makes parallel-phase state conflicts visible (#3455)

* fix(#3311): milestone lock makes parallel-phase state conflicts visible

Two sessions running different phases in one working tree silently
clobbered STATE.md's single un-scoped ## Current Position slot: byte-level
serialization already existed (STATE.md.lock since #464), but nothing ever
surfaced that two sessions claimed two different phases, and
state.advance-plan — which takes no phase argument — kept advancing whatever
plan the just-clobbered position named.

Adds the maintainer-chosen milestone lock (issue #3311 comment): an advisory
.planning/milestone.lock claim keyed by phase + session id (session identity
via getWorkstreamSessionKey: env-first, then controlling TTY). begin-phase
claims (inside the STATE.md lock), advance-plan detects a claim/position
mismatch and heartbeats a matching claim, phase.complete warns via warnings[]
and releases the claim when the claimed phase completes. Conflicts warn
(stderr + typed milestone_conflict JSON field) instead of blocking, per the
decision's blocking/warning latitude; TTL 4h with heartbeat liveness expires
abandoned claims. milestone.lock is registered in the canonical artifact
registry so validate.health W019 recognizes it.

* chore(#3311): point changeset fragment at pr 3455

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-08-14 03:04:09 -04:00
committed by GitHub
parent 011153c076
commit 6b34557ba3
10 changed files with 1000 additions and 3 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 3455
---
Parallel phases running in the same working tree no longer corrupt STATE.md silently: state.begin-phase, state.advance-plan and phase.complete now consult a milestone claim (.planning/milestone.lock) keyed by phase + session id, and surface a visible milestone_conflict warning (stderr plus a typed JSON field, and phase.complete's warnings[]) when another live session holds a different phase — instead of silently overwriting the single Current Position slot.