Overview
Tracks the work to fully align the Milestone, Stage, and Goal concepts into a
coherent, consistent family — grounded in CMMN 1.1, free of overlap, and
complete enough to support Stage-contained lifecycle tracking.
Not for the current port sprint. The `casehub-blackboard/` port (issue #76)
brings Stage and basic Milestone tracking. This epic captures what needs to be
tidied up and deepened once that foundation exists.
The Problem
Three gaps surfaced during the casehub-blackboard design session:
1. `Goal.terminal` creates overlap with `Milestone`
`Goal` has a `terminal` flag. A non-terminal Goal is "an observable checkpoint
with polarity." That is just a Milestone with a success/failure label — the
two concepts now overlap. If a Goal does not drive case completion, it is not
meaningfully different from a Milestone with a `GoalKind` annotation.
Options to resolve:
- Remove non-terminal Goals entirely — Goals are always terminal; use Milestone
for non-terminal checkpoints (possibly with an optional polarity annotation)
- Make `GoalKind` an optional field on `Milestone` (collapsing the two)
- Keep both, but define the boundary precisely and enforce it with a validator
2. `Milestone` has no stage containment
The CMMN 1.1 spec places Milestones inside Stages. casehub-engine's `Milestone`
class has no `parentStageId` — it is always top-level. This means:
- Stage exit criteria cannot reference "has milestone X been achieved within
this stage?"
- Stage completion tracking based on contained milestones is impossible
casehub-poc's `Milestone` had `parentStageId` and was a proper Stage member.
That capability was not carried forward into casehub-engine.
3. `Milestone` lifecycle state lives in the EventLog, not the object
`api/Milestone` has no PENDING/ACHIEVED state. The `CasePlanModel` Javadoc
explicitly defers this to Phase 2. The result: milestone lifecycle state is
scattered across EventLog queries and plan model maps, rather than being
readable directly from the object.
casehub-poc's `Milestone` had `MilestoneStatus { PENDING, ACHIEVED }` on the
object and an `achieve()` method. Simpler and more direct.
What Full Alignment Looks Like
A consistent family across all three CMMN-derived concepts:
| Concept |
Role |
Polarity |
Containable in Stage |
Lifecycle state |
| `Milestone` |
Neutral waypoint — marks where the case is |
None |
Yes (`parentStageId`) |
PENDING → ACHIEVED |
| `Goal` |
Outcome — what the case is trying to achieve |
SUCCESS / FAILURE |
No (always top-level) |
Drives COMPLETED / FAILED |
| `Stage` |
Container — groups PlanItems and Milestones |
N/A |
Yes (nested stages) |
PENDING → ACTIVE → COMPLETED / TERMINATED |
Under this model:
- You pass Milestones
- You achieve Goals
- You enter and exit Stages
There is no overlap. Non-terminal Goals are removed — they were Milestones.
Child Issues (to be created)
| Area |
Work |
| `Goal.terminal` cleanup |
Remove or restrict non-terminal Goals; define the concept boundary clearly |
| Milestone stage containment |
Add `parentStageId` to `api/Milestone`; Stage holds milestone IDs; stage exit criteria can reference contained milestones |
| Milestone lifecycle state |
Add `MilestoneStatus { PENDING, ACHIEVED }` to `api/Milestone`; `CasePlanModel` promotes on `MilestoneReachedEvent` |
| CMMN 1.1 audit |
Review all three concepts against CMMN 1.1 §5.4.4 (Stage), §5.4.7 (Milestone), and Goal semantics; document decisions |
Dependency
Depends on `casehub-blackboard/` (issue #76) being stable — Stage and
CasePlanModel must exist before Milestone containment and lifecycle promotion
can be wired in.
Overview
Tracks the work to fully align the Milestone, Stage, and Goal concepts into a
coherent, consistent family — grounded in CMMN 1.1, free of overlap, and
complete enough to support Stage-contained lifecycle tracking.
Not for the current port sprint. The `casehub-blackboard/` port (issue #76)
brings Stage and basic Milestone tracking. This epic captures what needs to be
tidied up and deepened once that foundation exists.
The Problem
Three gaps surfaced during the casehub-blackboard design session:
1. `Goal.terminal` creates overlap with `Milestone`
`Goal` has a `terminal` flag. A non-terminal Goal is "an observable checkpoint
with polarity." That is just a Milestone with a success/failure label — the
two concepts now overlap. If a Goal does not drive case completion, it is not
meaningfully different from a Milestone with a `GoalKind` annotation.
Options to resolve:
for non-terminal checkpoints (possibly with an optional polarity annotation)
2. `Milestone` has no stage containment
The CMMN 1.1 spec places Milestones inside Stages. casehub-engine's `Milestone`
class has no `parentStageId` — it is always top-level. This means:
this stage?"
casehub-poc's `Milestone` had `parentStageId` and was a proper Stage member.
That capability was not carried forward into casehub-engine.
3. `Milestone` lifecycle state lives in the EventLog, not the object
`api/Milestone` has no PENDING/ACHIEVED state. The `CasePlanModel` Javadoc
explicitly defers this to Phase 2. The result: milestone lifecycle state is
scattered across EventLog queries and plan model maps, rather than being
readable directly from the object.
casehub-poc's `Milestone` had `MilestoneStatus { PENDING, ACHIEVED }` on the
object and an `achieve()` method. Simpler and more direct.
What Full Alignment Looks Like
A consistent family across all three CMMN-derived concepts:
Under this model:
There is no overlap. Non-terminal Goals are removed — they were Milestones.
Child Issues (to be created)
Dependency
Depends on `casehub-blackboard/` (issue #76) being stable — Stage and
CasePlanModel must exist before Milestone containment and lifecycle promotion
can be wired in.