valent-pipeline 0.5.5 → 0.5.6

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valent-pipeline",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "description": "v3 multi-agent AI pipeline for software development lifecycle",
5
5
  "type": "module",
6
6
  "bin": {
@@ -90,9 +90,16 @@ function statusKind(status, column) {
90
90
  /* ---------- Chips ----------------------------------------------------------------------- */
91
91
  function statusChip(story) {
92
92
  const k = statusKind(story.status, story.column);
93
- return h('span', { class: `chip chip--status ${k.cls}`, title: `status: ${k.label}` },
93
+ // When the status was inferred from on-disk artifacts (backlog status lags an in-flight groom),
94
+ // mark it so the board reads honestly: a dotted chip + a title explaining the real backlog state.
95
+ const inferred = story.status_source === 'artifacts';
96
+ const title = inferred
97
+ ? `status: ${k.label} (inferred from artifacts — backlog still says ${story.backlog_status || 'pending'})`
98
+ : `status: ${k.label}`;
99
+ return h('span', { class: `chip chip--status ${k.cls}${inferred ? ' chip--inferred' : ''}`, title },
94
100
  h('span', { class: 'chip__icon', 'aria-hidden': 'true' }, k.icon),
95
101
  k.label,
102
+ inferred ? h('span', { class: 'chip__inferred-mark', 'aria-hidden': 'true', title: 'inferred' }, '~') : null,
96
103
  );
97
104
  }
98
105
 
@@ -457,6 +457,11 @@ body {
457
457
  .chip--status.is-idle { color: var(--ink-muted); background: var(--idle-soft); border-color: transparent; }
458
458
  .chip--status.is-warn { color: var(--warn); background: var(--warn-soft); border-color: transparent; }
459
459
 
460
+ /* Inferred status — derived from on-disk artifacts because the backlog status lags an active
461
+ groom. Dashed outline + a faint "~" mark signals "best-effort, not yet persisted". */
462
+ .chip--status.chip--inferred { border: 1px dashed currentColor; }
463
+ .chip__inferred-mark { opacity: 0.6; font-weight: 600; margin-left: 1px; }
464
+
460
465
  /* ---------- Backlog list ---------------------------------------------------------------- */
461
466
  .backlog {
462
467
  max-width: 1100px;
package/src/lib/board.js CHANGED
@@ -60,6 +60,30 @@ export function columnForStatus(status) {
60
60
  return STATUS_TO_COLUMN.get(status) || 'other'
61
61
  }
62
62
 
63
+ /**
64
+ * Grooming artifacts, most-advanced phase first. Their presence means a story is mid-groom even
65
+ * when the backlog `status` still says `pending` — the Workflow path (plan.workflow.js) grooms
66
+ * the batch in-memory and only persists `status: groomed` at the very end, so the backlog status
67
+ * lags reality during a live groom (and there is often no pipeline-state.json at all). The board
68
+ * is a projection, so it infers the grooming phase from the artifacts the groom agents have
69
+ * already written, exactly as it overlays the live current_story over a lagging backlog.
70
+ */
71
+ const GROOMING_ARTIFACT_STATUS = [
72
+ ['readiness-review.md', 'readiness-review'],
73
+ ['qa-test-spec.md', 'test-case-development'],
74
+ ['uxa-spec.md', 'ux-spec'],
75
+ ['reqs-brief.md', 'requirements-spec'],
76
+ ]
77
+
78
+ /** The most-advanced grooming status implied by present artifacts, or null if none. */
79
+ export function inferGroomingStatus(artifacts) {
80
+ if (!Array.isArray(artifacts)) return null
81
+ for (const [file, status] of GROOMING_ARTIFACT_STATUS) {
82
+ if (artifacts.includes(file)) return status
83
+ }
84
+ return null
85
+ }
86
+
63
87
  /** Story points, tolerating either field name (matches sprint.js `pointsOf`). */
64
88
  function pointsOf(item) {
65
89
  const p = item.points ?? item.story_points
@@ -74,10 +98,29 @@ function depsOf(item) {
74
98
  /** Normalize one backlog item into a board story (stable shape regardless of input quirks). */
75
99
  function normalizeStory(item, artifactsByStory) {
76
100
  const id = item.id
77
- const status = item.status || 'pending'
101
+ const backlogStatus = item.status || 'pending'
102
+ const artifacts = (artifactsByStory && artifactsByStory[id]) || []
103
+
104
+ // Reflect an in-flight groom the backlog status hasn't caught up to: if a story still reads
105
+ // `pending` (its column would be Backlog) but grooming artifacts already exist on disk, surface
106
+ // the inferred grooming phase so it shows in Grooming instead of sitting in Backlog. An explicit
107
+ // forward status (groomed/sprint-planned/development/…/shipped) always wins — only `pending` is
108
+ // upgraded, and `status_source` records that the status was derived, not read from the backlog.
109
+ let status = backlogStatus
110
+ let statusSource = 'backlog'
111
+ if (columnForStatus(backlogStatus) === 'backlog') {
112
+ const inferred = inferGroomingStatus(artifacts)
113
+ if (inferred) {
114
+ status = inferred
115
+ statusSource = 'artifacts'
116
+ }
117
+ }
118
+
78
119
  return {
79
120
  id,
80
121
  status,
122
+ backlog_status: backlogStatus,
123
+ status_source: statusSource,
81
124
  column: columnForStatus(status),
82
125
  depends_on: depsOf(item),
83
126
  points: pointsOf(item),
@@ -88,7 +131,7 @@ function normalizeStory(item, artifactsByStory) {
88
131
  active: false, // set true below if it matches current_story
89
132
  phase: null, // overlaid from current_story when active
90
133
  branch: null,
91
- artifacts: (artifactsByStory && artifactsByStory[id]) || [],
134
+ artifacts,
92
135
  cost: null, // { tokens, agent_ms, invocations, roles:[...] } when an audit report is supplied
93
136
  }
94
137
  }