refactor(model): name Feature status by domain, not RAG color
StatusLevel held green/orange/red — the RAG vocabulary CONTEXT.md explicitly lists under _Avoid_ for the Status term, leaking a presentation concern into the model and authored TOML. Replace with on-track/at-risk/off-track; color now lives only in the renderer's tone() mapping. Breaking change to authored .toml status values.
This commit is contained in:
@@ -58,20 +58,14 @@ const gridStyle = computed(() => ({
|
||||
|
||||
function tone(row: FeatureRow): Tone {
|
||||
if (row.delivered) return row.onTime ? 'success' : 'error'
|
||||
if (row.status === 'green') return 'success'
|
||||
if (row.status === 'orange') return 'warning'
|
||||
if (row.status === 'red') return 'error'
|
||||
if (row.status === 'on-track') return 'success'
|
||||
if (row.status === 'at-risk') return 'warning'
|
||||
if (row.status === 'off-track') return 'error'
|
||||
return 'neutral'
|
||||
}
|
||||
|
||||
function statusWord(row: FeatureRow): string {
|
||||
return row.status === 'green'
|
||||
? 'on track'
|
||||
: row.status === 'orange'
|
||||
? 'at risk'
|
||||
: row.status === 'red'
|
||||
? 'blocked'
|
||||
: 'in flight'
|
||||
return row.status ? row.status.replace('-', ' ') : 'in flight'
|
||||
}
|
||||
|
||||
function markerAt(row: FeatureRow, w: WeekId): MarkerKind | null {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Default Macroplan shown on first load — exercises every state:
|
||||
// on-time delivery, late delivery with slips, in-flight (green/orange/red),
|
||||
// on-time delivery, late delivery with slips, in-flight (on-track/at-risk/off-track),
|
||||
// an overdue Feature, and a Milestone with unmet required Features.
|
||||
export const SAMPLE_PLAN = `title = "Q3 — Checkout revamp"
|
||||
|
||||
@@ -32,7 +32,7 @@ learning = "Vendor lead time was the real constraint — derisk vendors up front
|
||||
name = "Dashboard"
|
||||
start = 2026-06-01
|
||||
original = 2026-06-08 # already past 'now' and undelivered → overdue
|
||||
status = "red"
|
||||
status = "off-track"
|
||||
note = "No recovery plan yet — needs an owner."
|
||||
|
||||
[[feature]]
|
||||
@@ -40,14 +40,14 @@ name = "Search"
|
||||
start = 2026-06-08
|
||||
original = 2026-06-22
|
||||
reestimates = [2026-07-06] # re-estimated once, still in flight → △
|
||||
status = "orange"
|
||||
status = "at-risk"
|
||||
note = "Third-party search API is flaky; spike a fallback."
|
||||
|
||||
[[feature]]
|
||||
name = "Notifications"
|
||||
start = 2026-06-22
|
||||
original = 2026-07-06
|
||||
status = "green"
|
||||
status = "on-track"
|
||||
|
||||
[[milestone]]
|
||||
name = "Code freeze"
|
||||
|
||||
@@ -11,7 +11,7 @@ export class PlanParseError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
const STATUSES = ['green', 'orange', 'red'] as const satisfies readonly StatusLevel[]
|
||||
const STATUSES = ['on-track', 'at-risk', 'off-track'] as const satisfies readonly StatusLevel[]
|
||||
|
||||
// ── Field schemas ──────────────────────────────────────────────────────────
|
||||
// A TOML date (smol-toml returns a Date subclass) or a yyyy-mm-dd string,
|
||||
|
||||
@@ -76,10 +76,10 @@ describe('F2 — on-time / late classification (ADR-0001)', () => {
|
||||
})
|
||||
|
||||
it('in-flight (undelivered) → ◯ only, onTime null, bar ends at the furthest estimate', () => {
|
||||
const r = rowOf(base('reestimates = [2026-06-29]\nstatus = "red"'), 'X')
|
||||
const r = rowOf(base('reestimates = [2026-06-29]\nstatus = "off-track"'), 'X')
|
||||
expect(r.onTime).toBeNull()
|
||||
expect(r.delivered).toBe(false)
|
||||
expect(r.status).toBe('red')
|
||||
expect(r.status).toBe('off-track')
|
||||
expect(r.barEndWeek).toBe('2026-06-29') // furthest open estimate
|
||||
expect(r.markers.some((m) => m.kind === 'original')).toBe(true)
|
||||
})
|
||||
@@ -91,7 +91,7 @@ describe('bar extent relative to now', () => {
|
||||
|
||||
it('extends an overdue undelivered bar to the now week, keeping ◯ at the estimate', () => {
|
||||
// original 2026-06-08 is before now (week of 2026-06-15) and undelivered → overdue
|
||||
const r = rowOf(feat('status = "red"'), 'X')
|
||||
const r = rowOf(feat('status = "off-track"'), 'X')
|
||||
expect(r.delivered).toBe(false)
|
||||
expect(r.barEndWeek).toBe('2026-06-15') // runs up to now, past the 06-08 estimate
|
||||
expect(r.markers.find((m) => m.kind === 'original')?.week).toBe('2026-06-08')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { WeekId } from './week'
|
||||
|
||||
export type StatusLevel = 'green' | 'orange' | 'red'
|
||||
export type StatusLevel = 'on-track' | 'at-risk' | 'off-track'
|
||||
|
||||
// ── Raw model: as authored, after TOML parse + validation, before derivation ──
|
||||
|
||||
|
||||
Reference in New Issue
Block a user