@hanzlaa/rcode 4.10.6 → 4.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +1 -1
  3. package/CONTRIBUTING.md +3 -0
  4. package/cli/install.js +160 -0
  5. package/dist/rcode.js +254 -234
  6. package/package.json +1 -1
  7. package/rcode/agents/rcode-project-researcher.md +1 -1
  8. package/rcode/agents/rcode-ux-designer.md +1 -0
  9. package/rcode/agents/rcode-verifier.md +2 -2
  10. package/rcode/agents/rules/planner/common-patterns.md +2 -1
  11. package/rcode/agents/rules/planner/goal-backward-thinking.md +33 -0
  12. package/rcode/agents/rules/planner/sprint-verification.md +1 -0
  13. package/rcode/agents/rules/planner/task-templates.md +20 -1
  14. package/rcode/agents/rules/project-researcher/detailed-guide.md +10 -0
  15. package/rcode/agents/rules/sprint-checker/dimensions.md +74 -0
  16. package/rcode/agents/rules/sprint-checker/process.md +12 -0
  17. package/rcode/agents/rules/verifier/behavioral-spot-checks.md +5 -1
  18. package/rcode/agents/rules/verifier/gap-output.md +5 -0
  19. package/rcode/agents/rules/verifier/reachability-check.md +69 -0
  20. package/rcode/bin/rcode-tools.cjs +39 -3
  21. package/rcode/commands/ui-phase.md +1 -1
  22. package/rcode/references/design-library/LICENSE +21 -0
  23. package/rcode/references/design-library/README.md +37 -0
  24. package/rcode/references/design-library/charts.csv +26 -0
  25. package/rcode/references/design-library/colors.csv +97 -0
  26. package/rcode/references/design-library/icons.csv +101 -0
  27. package/rcode/references/design-library/styles.csv +68 -0
  28. package/rcode/references/design-library/typography.csv +58 -0
  29. package/rcode/references/design-library/ui-reasoning.csv +101 -0
  30. package/rcode/references/design-library/ux-guidelines.csv +100 -0
  31. package/rcode/references/design-library/web-interface.csv +31 -0
  32. package/rcode/references/domain-probes.md +8 -0
  33. package/rcode/references/project-types.yaml +29 -0
  34. package/rcode/references/questioning.md +3 -0
  35. package/rcode/references/roadmapper-playbook.md +90 -0
  36. package/rcode/references/sprint-checker-playbook.md +14 -0
  37. package/rcode/references/verifier-playbook.md +20 -7
  38. package/rcode/workflows/autonomous.md +26 -3
  39. package/rcode/workflows/complete-milestone.md +1 -1
  40. package/rcode/workflows/council.md +51 -6
  41. package/rcode/workflows/discuss-phase.md +9 -2
  42. package/rcode/workflows/execute-sprint.md +35 -7
  43. package/rcode/workflows/execute-waves.md +2 -2
  44. package/rcode/workflows/execute.md +21 -6
  45. package/rcode/workflows/help.md +1 -1
  46. package/rcode/workflows/plan-research-validation.md +1 -1
  47. package/rcode/workflows/plan.md +31 -0
  48. package/rcode/workflows/ship.md +22 -0
  49. package/rcode/workflows/ui-phase.md +104 -22
  50. package/rcode/workflows/ui-review.md +9 -1
  51. package/rcode/workflows/verify-work.md +1 -1
  52. package/server/lib/scanner.js +25 -4
@@ -121,6 +121,17 @@ Verify the work is ready to ship:
121
121
  Check for `status: passed` or `status: human_needed` (with human approval).
122
122
  If no VERIFICATION.md or status is `gaps_found`: warn and ask user to confirm.
123
123
 
124
+ **If proceeding with anything other than a clean `status: passed`** (i.e.
125
+ `human_needed` or a user-confirmed `gaps_found`): the generated PR body
126
+ (step below) MUST include a `## Known Gaps` section listing every
127
+ unresolved human-verification item or gap from VERIFICATION.md — mirroring
128
+ `complete-milestone.md`'s `### Known Gaps` pattern. This is not optional
129
+ cosmetic detail: a PR shipped on `human_needed`/`gaps_found` without this
130
+ section reads to a reviewer as fully verified when it isn't. Do not rely on
131
+ the `## Verification` section's item list alone (below) to carry this —
132
+ that section is easy to skim past; `## Known Gaps` must be its own
133
+ clearly-labeled heading.
134
+
124
135
  2. **Clean working tree?**
125
136
  ```bash
126
137
  git status --short
@@ -215,6 +226,17 @@ For each SUMMARY.md in the phase directory:
215
226
  - {human verification items from VERIFICATION.md, if any}
216
227
  ```
217
228
 
229
+ **5b. Known Gaps section (only when VERIFICATION.md status is not a clean `passed`):**
230
+ ```markdown
231
+ ## Known Gaps
232
+
233
+ This PR ships with `status: {human_needed|gaps_found}` per VERIFICATION.md, confirmed by the user in preflight.
234
+
235
+ - {gap/human-verification item 1 — file/truth + what's unconfirmed}
236
+ - {gap/human-verification item 2}
237
+ ```
238
+ Omit this section entirely when VERIFICATION.md status is a clean `passed` with zero open items.
239
+
218
240
  **6. Decisions section:**
219
241
  ```markdown
220
242
  ## Key Decisions
@@ -1,11 +1,11 @@
1
1
  # Workflow: rcode-ui-phase
2
2
 
3
3
  <purpose>
4
- Produce UI-SPEC.md with formalized design contract: color tokens, typography system, component inventory, interaction states, accessibility requirements. Detects frontend keywords (React, Next.js, Vue, Tailwind, CSS, UI) and suggests this workflow early if UI-SPEC.md is absent.
4
+ Produce two artifacts before any UI code gets written: UI-SPEC.md (formalized design contract color tokens, typography, component inventory, interaction states, accessibility requirements) and WIREFRAMES.md (per-role screen inventory — what exists, who sees it, loading/empty/error states for each). Grounds design choices in `rcode/references/design-library/` (vendored style/palette/typography/UX-rules data — see that directory's README) instead of an agent inventing tokens from nothing. Detects frontend keywords (React, Next.js, Vue, Tailwind, CSS, UI) and suggests this workflow early if UI-SPEC.md is absent.
5
5
  </purpose>
6
6
 
7
7
 
8
- ## Step 0 — Usage check
8
+ ## Step 0a — Usage check
9
9
 
10
10
  If `$ARGUMENTS` is empty or contains only `--help` or `-h`:
11
11
 
@@ -25,7 +25,7 @@ STOP — do not proceed.
25
25
  - `rcode-ux-designer` — UI specification generator
26
26
  </available_agent_types>
27
27
 
28
- ## Step 0 — Initialize
28
+ ## Step 0b — Initialize
29
29
 
30
30
  ```bash
31
31
  INIT=$(node .rcode/bin/rcode-tools.cjs init ui-phase "$ARGUMENTS" 2>/dev/null)
@@ -41,6 +41,7 @@ Parse:
41
41
  - `flags.existing_ui` — path to existing design system or Figma export
42
42
  - `flags.design_system` — path to design tokens file
43
43
  - `ui_spec_path` — `.rcode/UI-SPEC.md` (output location)
44
+ - `wireframes_path` — `.rcode/WIREFRAMES.md` (output location)
44
45
 
45
46
  ## Step 1 — Detect Existing UI Assets
46
47
 
@@ -49,12 +50,33 @@ If `flags.existing_ui` or `flags.design_system` provided:
49
50
  EXISTING=$(node .rcode/bin/rcode-tools.cjs find-files --type=design-tokens)
50
51
  ```
51
52
 
52
- Load existing design system, extract:
53
+ Load existing design system, extract into `EXISTING_DESIGN_SYSTEM_DATA` (a text block passed verbatim into Step 2's prompt):
53
54
  - Color palette (hex, variable names)
54
55
  - Typography scales (font family, sizes, weights, line heights)
55
56
  - Component list (buttons, forms, layouts, modals, etc.)
56
57
  - Interaction patterns (hover, focus, active, disabled states)
57
58
 
59
+ If `$EXISTING` is empty or extraction finds nothing usable, treat this the
60
+ same as "no existing system found" and fall through to Step 1b — don't leave
61
+ `EXISTING_DESIGN_SYSTEM_DATA` half-populated.
62
+
63
+ If an existing design system was found and `EXISTING_DESIGN_SYSTEM_DATA` is populated, skip Step 1b (don't override what's already decided) and go straight to Step 2.
64
+
65
+ ## Step 1b — Ground the design in the reference library (no existing system found)
66
+
67
+ Don't let the agent invent a palette/style from nothing. If PROJECT.md and
68
+ ROADMAP.md are both missing, stop here and say so: "No PROJECT.md/ROADMAP.md
69
+ found — run `/rcode-new-project-research` or `/rcode-new-project-roadmap`
70
+ first so there's a project category to ground the design in." Otherwise look up:
71
+
72
+ 1. **Category match** — grep the project's category (from PROJECT.md/ROADMAP.md — e.g. "B2B SaaS Enterprise", "Analytics Dashboard", "Fintech (Banking)") against `rcode/references/design-library/ui-reasoning.csv`'s `UI_Category` column. This returns a recommended style/color-mood/typography-mood and explicit anti-patterns to avoid — read the row, don't guess a category if none matches closely; fall back to the closest match and say so. Capture the result as `CATEGORY_MATCH`.
73
+ 2. **Style detail** — take the recommended style name from step 1 and look it up in `styles.csv` for concrete hex values, effects, accessibility rating, and an implementation checklist. Capture as `STYLE_DETAIL`.
74
+ 3. **UX rules** — grep `ux-guidelines.csv` for the categories relevant to this project's screens (Navigation, Forms, etc.) for concrete do/don't rules with code examples. Capture as `UX_RULES`.
75
+
76
+ `CATEGORY_MATCH` + `STYLE_DETAIL` + `UX_RULES` together are what Step 2's
77
+ prompt calls `DESIGN_LOOKUP_RESULT` below — assemble them into one text block
78
+ before spawning the agent.
79
+
58
80
  ## Step 2 — Spawn UI Designer
59
81
 
60
82
  Spawn `rcode-ux-designer` subagent:
@@ -62,34 +84,80 @@ Spawn `rcode-ux-designer` subagent:
62
84
  ```
63
85
  Task tool call:
64
86
  subagent_type: "rcode-ux-designer"
65
- description: "Generate UI-SPEC.md"
87
+ description: "Generate UI-SPEC.md and WIREFRAMES.md"
66
88
  prompt: |
67
- Generate a UI-SPEC.md file with the following structure:
68
-
69
- 1. **Color Tokens** — semantic variable names, hex values, accessibility contrast ratios
70
- 2. **Typography** — font family, scales (12px, 14px, 16px, 18px, 20px, 24px, etc.), weights, line heights
71
- 3. **Component Inventory** — list of UI components with property variants (size, color, state, disabled)
72
- 4. **Interaction States** — hover, focus, active, disabled, loading for interactive elements
73
- 5. **Accessibility** — WCAG 2.1 AA compliance checklist, color contrast requirements, keyboard navigation rules
74
- 6. **Responsive Breakpoints** — mobile, tablet, desktop breakpoints and stacking rules
75
-
76
- {existing_design_system_data_if_provided}
77
-
89
+ Ground every choice below in {EXISTING_DESIGN_SYSTEM_DATA if Step 1 found one, else DESIGN_LOOKUP_RESULT from Step 1b} —
90
+ do not invent a palette/style from nothing when reference data or an existing system exists.
91
+
92
+ Write UI-SPEC.md with:
93
+ 1. **Design Direction** — which style/category this follows (from the reference library lookup) and why, one paragraph
94
+ 2. **Color Tokens** — semantic variable names, hex values, accessibility contrast ratios
95
+ 3. **Typography** — font family, scales (12px, 14px, 16px, 18px, 20px, 24px, etc.), weights, line heights
96
+ 4. **Component Inventory** — list of UI components with property variants (size, color, state, disabled)
97
+ 5. **Interaction States** — hover, focus, active, disabled, loading for interactive elements
98
+ 6. **Accessibility** — WCAG 2.1 AA compliance checklist, color contrast requirements, keyboard navigation rules
99
+ 7. **Responsive Breakpoints** — mobile, tablet, desktop breakpoints and stacking rules
100
+
78
101
  Write to: {ui_spec_path}
79
102
  ```
80
103
 
104
+ ## Step 2b — Spawn Wireframes (per-role screen inventory)
105
+
106
+ Read REQUIREMENTS.md/PROJECT.md for the project's user roles (if any) and the
107
+ IA decision — `roadmapper-playbook.md`'s Information Architecture step (Workflow
108
+ step 3b) says this is persisted as either a standalone `.planning/IA.md` or a
109
+ `## Information Architecture` section in ROADMAP.md, so check both locations,
110
+ not just ROADMAP.md. If neither has an IA decision, this step cannot produce a
111
+ real result — stop and say so: "No Information Architecture decision found in
112
+ ROADMAP.md or IA.md — the roadmapper needs to produce one first
113
+ (`/rcode-new-project-roadmap` for a brand-new project, `/rcode-new-milestone`
114
+ to regenerate the roadmap for an existing one — both invoke rcode-roadmapper,
115
+ which owns the IA step)."
116
+
117
+ Spawn `rcode-ux-designer` subagent (same agent, second artifact):
118
+
119
+ ```
120
+ Task tool call:
121
+ subagent_type: "rcode-ux-designer"
122
+ description: "Generate WIREFRAMES.md"
123
+ prompt: |
124
+ For each top-level IA section and each screen it contains, write one entry:
125
+
126
+ ### {Screen name} — {route/path}
127
+ **Roles that see it:** {role list, or "all authenticated users"}
128
+ **Purpose:** {one line — what the user accomplishes here}
129
+ **Layout:** {ASCII or textual wireframe — header/nav placement, primary content
130
+ area, key actions, not a pixel-perfect mockup}
131
+ **Required states** (do not omit any that apply):
132
+ - Loading: {what renders while data is fetching}
133
+ - Empty: {what renders when there's no data yet — never a blank screen}
134
+ - Error: {what renders on fetch/action failure — never a silent failure}
135
+ - Success/populated: {the normal case}
136
+ **Primary actions:** {buttons/links a user takes from here, and where they go}
137
+
138
+ Cover EVERY screen implied by the roadmap's phases, not just Phase 1's.
139
+ A screen with no role that can see it, or with only a "populated" state and
140
+ no loading/empty/error state defined, is an incomplete entry — fix it before
141
+ writing, don't ship the gap.
142
+
143
+ Write to: {wireframes_path}
144
+ ```
145
+
81
146
  ## Step 3 — Store Reference in State
82
147
 
83
- Update state with UI-SPEC.md location:
148
+ Update state with both artifact locations:
84
149
  ```bash
85
150
  node .rcode/bin/rcode-tools.cjs state set --ui-spec-path ".rcode/UI-SPEC.md"
151
+ node .rcode/bin/rcode-tools.cjs state set --wireframes-path ".rcode/WIREFRAMES.md"
86
152
  ```
87
153
 
88
154
  Print:
89
155
  ```
90
156
  ✓ UI-SPEC.md generated: {ui_spec_path}
157
+ ✓ WIREFRAMES.md generated: {wireframes_path}
91
158
 
92
- Contains:
159
+ UI-SPEC.md contains:
160
+ • Design Direction (grounded in design-library lookup, not invented)
93
161
  • Color Tokens
94
162
  • Typography System
95
163
  • Component Inventory
@@ -97,10 +165,22 @@ Contains:
97
165
  • Accessibility Checklist
98
166
  • Responsive Breakpoints
99
167
 
100
- This spec will guide component development and design consistency.
168
+ WIREFRAMES.md contains:
169
+ • Every screen from the roadmap's IA, with role visibility
170
+ • Loading/empty/error/success state for each screen — no screen ships
171
+ without all four defined
172
+
173
+ These specs guide component development, design consistency, and give
174
+ sprint-checker something concrete to verify state-completeness against.
101
175
  ```
102
176
 
103
- ## Step 4 — Modify plan.md Detection (referenced in plan workflow)
177
+ ## Step 4 — Modify plan.md Detection (one-time setup, not per-invocation)
178
+
179
+ This step is setup documentation for `plan.md`, not part of `/rcode-ui-phase`'s
180
+ own run sequence — it does not re-run every time `/rcode-ui-phase` is
181
+ invoked. Before applying it, check whether `plan.md` already has this
182
+ detection step (grep for "ui_safety_gate" or "Detect frontend keywords");
183
+ skip if present.
104
184
 
105
185
  In plan.md workflow, add Step 0.6 — **Detect frontend keywords and suggest UI safety gate**
106
186
 
@@ -141,17 +221,19 @@ Run /rcode-ui-phase, then return to /rcode-plan
141
221
 
142
222
  ## Success Criteria
143
223
 
144
- - UI-SPEC.md created with all 6 sections
224
+ - UI-SPEC.md created with all 7 sections, design direction grounded in `design-library/` lookup (or an existing design system), not invented
145
225
  - Color tokens documented with contrast ratios
146
226
  - Component inventory complete with variants
147
227
  - Accessibility checklist included
148
- - State updated with UI-SPEC.md path
228
+ - WIREFRAMES.md created covering every screen implied by the roadmap's IA, each with role visibility and all four states (loading/empty/error/success) defined
229
+ - State updated with both UI-SPEC.md and WIREFRAMES.md paths
149
230
 
150
231
  ## On Error
151
232
 
152
233
  - If subagent fails: provide template UI-SPEC.md
153
234
  - If frontend detection fails: skip suggestion
154
235
  - If config.yaml missing ui_safety_gate: default to true (suggest)
236
+ - If no IA decision exists in ROADMAP.md or IA.md yet: WIREFRAMES.md cannot be produced meaningfully — stop Step 2b and say so rather than writing a screen list with no basis
155
237
 
156
238
  ## Next Up
157
239
 
@@ -1,7 +1,7 @@
1
1
  # Workflow: rcode-ui-review
2
2
 
3
3
  <purpose>
4
- Retroactively audit completed UI work against the UI-SPEC.md contract. Validates across 6 pillars: color consistency, typography compliance, component inventory coverage, accessibility requirements, responsive behavior, and design coherence. Produces audit report with pass/fail per pillar.
4
+ Retroactively audit completed UI work against the UI-SPEC.md contract (and WIREFRAMES.md, when present, for per-screen state coverage). Validates across 6 pillars: color consistency, typography compliance, component inventory coverage, accessibility requirements, responsive behavior, and design coherence. Produces audit report with pass/fail per pillar.
5
5
  </purpose>
6
6
 
7
7
 
@@ -41,6 +41,7 @@ Parse:
41
41
  - `flags.phase` — audit specific phase (optional, default: all completed phases)
42
42
  - `flags.detailed` — include detailed findings per component
43
43
  - `spec_path` — load UI-SPEC.md if exists
44
+ - `wireframes_path` — load WIREFRAMES.md if exists (optional; used for per-screen state coverage)
44
45
  - `phase_name` — phase to audit
45
46
 
46
47
  ## Step 1 — Validate UI-SPEC.md Exists
@@ -65,6 +66,10 @@ Read UI-SPEC.md for:
65
66
  - Accessibility requirements
66
67
  - Responsive breakpoints
67
68
 
69
+ If `.rcode/WIREFRAMES.md` exists, also read it for the per-role screen inventory and
70
+ required states (loading/empty/error/success) — missing states become findings under
71
+ Component Inventory or Design Coherence.
72
+
68
73
  Scan phase/codebase for:
69
74
  - Component files (*.tsx, *.jsx, *.vue)
70
75
  - CSS/styling files (*.css, *.module.css, tailwind.config.js, etc.)
@@ -85,6 +90,9 @@ Task tool call:
85
90
  **UI-SPEC.md Reference:**
86
91
  {contents_of_ui_spec_md}
87
92
 
93
+ **WIREFRAMES.md Reference (if present):**
94
+ {contents_of_wireframes_md}
95
+
88
96
  **Codebase to Audit:**
89
97
  {component_files_list}
90
98
  {styling_rules}
@@ -117,7 +117,7 @@ UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
117
117
  **If Playwright-MCP tools are available in this session (`mcp__playwright__*` tools
118
118
  respond to tool calls) AND (`UI_PHASE_FLAG` is `true` OR `UI_SPEC_FILE` is non-empty):**
119
119
 
120
- For each UI checkpoint listed in the phase's UI-SPEC.md (or inferred from SUMMARY.md):
120
+ For each UI checkpoint listed in the phase's UI-SPEC.md and WIREFRAMES.md (screen states — loading/empty/error/success), or inferred from SUMMARY.md if neither exists:
121
121
 
122
122
  1. Use `mcp__playwright__navigate` (or equivalent) to open the component's URL.
123
123
  2. Use `mcp__playwright__screenshot` to capture a screenshot.
@@ -133,6 +133,16 @@ function buildPhaseTree(projectDir, rawPhases, listCached, overrides) {
133
133
  if (!sprintFiles.length) return p;
134
134
 
135
135
  const phaseComplete = /complete|done/i.test(p.status || '');
136
+ // Story status must not cascade blindly from the phase's self-reported
137
+ // status (#gap: dashboard-truthfulness). A phase marked 'complete' in
138
+ // state.json is unverified until its own VERIFICATION.md says so — only
139
+ // then do we let stories inherit 'done'. Otherwise stories stay 'todo'
140
+ // even if the phase claims completion, so the board never shows a green
141
+ // checkmark with zero independent evidence behind it.
142
+ const verificationFile = files.find(f => /-VERIFICATION\.md$/i.test(f));
143
+ const verificationText = verificationFile ? (safeReadText(path.join(phasesDir, dir.name, verificationFile)) || '') : '';
144
+ const phaseVerified = phaseComplete && /status:\s*passed/i.test(verificationText);
145
+ const storyStatus = phaseVerified ? 'done' : 'todo';
136
146
  const sprints = sprintFiles.map(f => {
137
147
  const m = f.match(/^(\d+)-(\d+)-SPRINT\.md$/i);
138
148
  const num = m ? parseInt(m[2], 10) : 0;
@@ -163,7 +173,7 @@ function buildPhaseTree(projectDir, rawPhases, listCached, overrides) {
163
173
  const story = {
164
174
  id: idM ? idM[1] : `${sid}-task-${stories.length + 1}`,
165
175
  title: (titleAttrM && titleAttrM[1].trim()) || (titleTagM && titleTagM[1].trim()) || `Task ${stories.length + 1}`,
166
- status: phaseComplete ? 'done' : 'todo',
176
+ status: storyStatus,
167
177
  };
168
178
  if (ov[story.id]) story.status = ov[story.id].status;
169
179
  if (acM && acM[1].trim()) story.acceptance = acM[1].trim();
@@ -187,7 +197,7 @@ function buildPhaseTree(projectDir, rawPhases, listCached, overrides) {
187
197
  stories.push({
188
198
  id: hId,
189
199
  title: hm[2].trim(),
190
- status: ov[hId] ? ov[hId].status : (phaseComplete ? 'done' : 'todo'),
200
+ status: ov[hId] ? ov[hId].status : storyStatus,
191
201
  });
192
202
  }
193
203
  }
@@ -217,7 +227,11 @@ function buildPhaseTree(projectDir, rawPhases, listCached, overrides) {
217
227
  }).filter(depId => depId !== null && depId !== intId)
218
228
  )];
219
229
 
220
- return { ...p, sprints, dependsOn: phaseDependsOn };
230
+ // Surface the same verification check at the phase level so consumers of
231
+ // the tree (phases list, progress %, currentPhase, milestones) can also
232
+ // avoid trusting a self-reported 'complete' status blindly — not just the
233
+ // per-story cascade above. null when the phase doesn't claim completion.
234
+ return { ...p, sprints, dependsOn: phaseDependsOn, verified: phaseComplete ? phaseVerified : null };
221
235
  });
222
236
  }
223
237
 
@@ -278,7 +292,14 @@ function buildDashboard(state) {
278
292
  const range = started || completed
279
293
  ? [fmtShort(started), fmtShort(completed)].filter(Boolean).join(' – ')
280
294
  : '';
281
- return { ...p, name: p.name || p.slug || String(p.id || ''), range, state: toState(p.status) };
295
+ // A phase self-reporting 'complete' without a passing *-VERIFICATION.md
296
+ // (p.verified === false, set by buildPhaseTree) is downgraded to 'active'
297
+ // rather than shown as 'done' — the dashboard must not render a green
298
+ // checkmark, fill the progress bar, or skip a phase as currentPhase on
299
+ // an unconfirmed status alone (same drift class as the sprint-level check).
300
+ let phaseState = toState(p.status);
301
+ if (phaseState === 'done' && p.verified === false) phaseState = 'active';
302
+ return { ...p, name: p.name || p.slug || String(p.id || ''), range, state: phaseState };
282
303
  }).sort((a, b) => (parseFloat(a.id ?? a.number) || 0) - (parseFloat(b.id ?? b.number) || 0));
283
304
 
284
305
  // ---- progress (prefer story counts; fall back to phase-level counts) ----