@pellux/goodvibes-tui 0.19.22 → 0.19.23

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/CHANGELOG.md CHANGED
@@ -4,6 +4,50 @@ All notable changes to GoodVibes TUI.
4
4
 
5
5
  ---
6
6
 
7
+ ## [0.19.23] — 2026-04-22
8
+
9
+ SDK 0.23.x constraint-propagation reconciliation. Surfaces the new per-chain constraint data across the WRFC panel, process modal, agent inspector, and agent detail modal; adds system-message notifications for constraint enumeration and violations; exposes the WRFC-injected `systemPromptAddendum` so operators can see the engineer addendum was applied.
10
+
11
+ ### Changed
12
+
13
+ - **SDK dep bumped to `@pellux/goodvibes-sdk@0.23.2`.** Picks up constraint-propagation additions from 0.23.0 (feature), the 0.23.1 cleanup (removed opt-in golden-prompt suite that was skipped by default), and the 0.23.2 docs bundle (the complete `docs/wrfc-constraint-propagation.md` + reference-runtime-events + observability + error-kinds + migration docs shipped with the feature). New types consumed by this TUI release: `Constraint` and `ConstraintFinding` from `platform/agents/completion-report`; three new fields on `WrfcChain` (`constraints`, `constraintsEnumerated`, `syntheticIssues`); `WORKFLOW_CONSTRAINTS_ENUMERATED` runtime event; optional constraint summary fields on `WORKFLOW_REVIEW_COMPLETED` (`constraintsSatisfied`, `constraintsTotal`, `unsatisfiedConstraintIds`) and `WORKFLOW_FIX_ATTEMPTED` (`targetConstraintIds`); optional `constraints?` on `EngineerReport`; optional `constraintFindings?` on `ReviewerReport`; `AgentRecord.systemPromptAddendum?`.
14
+
15
+ ### Added
16
+
17
+ - **`WORKFLOW_CONSTRAINTS_ENUMERATED` system message** (`src/runtime/bootstrap-core.ts`). When an engineer agent enumerates one or more constraints, a low-priority `[WRFC] Engineer enumerated N constraint(s) for chain <chainId>` message is routed to the SystemMessagesPanel. Zero-constraint chains produce no message (backward-compatible).
18
+ - **Constraint-violation system message on review failure** (`src/runtime/bootstrap-core.ts`). When `WORKFLOW_REVIEW_COMPLETED` fires with `passed: false` and `unsatisfiedConstraintIds` is non-empty, a high-priority `[WRFC] ✗ Chain <chainId>: N constraint violation(s) forced failure` message is emitted to both the main conversation and the SystemMessagesPanel. Complements the existing SDK-level score/threshold message.
19
+ - **`WORKFLOW_FIX_ATTEMPTED` constraint-targeting message** (`src/runtime/bootstrap-core.ts`). When a fix agent is spawned to address specific constraints (`targetConstraintIds` present), a low-priority `[WRFC] Fix #N targeting N constraint(s) on chain <chainId>` message is routed to the SystemMessagesPanel. Only fires when `targetConstraintIds` is non-empty.
20
+ - **Constraint count in Fix agent process-modal label** (`src/renderer/process-modal.ts`). When a running fix agent's WRFC chain has constraints, the Background Processes modal label gains a compact `[Nc]` suffix (e.g. `[Fix #2] my task (7.8 → 9.9/10) [3c]`). Chains with zero constraints are unchanged.
21
+ - **`systemPromptAddendum` indicator in Agent Inspector** (`src/panels/agent-inspector-panel.ts`). The per-agent summary line in the Inspector panel now includes an `Addendum yes` field when the selected agent's `AgentRecord.systemPromptAddendum` is set. This lets operators confirm that the WRFC constraint addendum was injected into the engineer's system prompt. Agents without an addendum are unchanged.
22
+ - **Constraint data in Agent Detail modal** (`src/renderer/agent-detail-modal.ts`). The agent detail modal (opened from the Background Processes modal with Enter) now surfaces three additional SDK 0.23.x fields when available: (1) an `Addendum: yes` line when `AgentRecord.systemPromptAddendum` is set; (2) a `Constraints (N):` block listing each constraint id/text/source from the agent's WRFC chain; (3) a `Findings: N checked, N unsatisfied` summary from the reviewer's `constraintFindings`. An optional `wrfcController` dep was added to `AgentDetailModalDeps` — backward-compatible, existing callers without it continue working unchanged. Zero-constraint chains and agents not in a WRFC chain render byte-identically to pre-0.23.
23
+ - **WRFC panel constraint badge** (`src/panels/wrfc-panel.ts`). Each chain row renders a compact `c:N/M` badge showing satisfied/total constraints when present, colored by aggregate state: green when all satisfied, red on any unsatisfied finding, grey when no findings yet, yellow for mixed verified/unverified. Omitted entirely for zero-constraint chains.
24
+ - **WRFC panel expanded constraint detail** (`src/panels/wrfc-panel.ts`). Expanding a chain now shows per-constraint lines with a severity-tagged status marker: `[SAT]` (satisfied), `[UNS CRIT]` / `[UNS MAJOR]` / `[UNS MINOR]` (unsatisfied with severity), or `[UNV]` (unverified). Inherited constraints — those carried from a parent chain after gate-failure retry — are suffixed with ` *`. List caps at 10 with a `(+N more)` tail and respects the panel's `maxLines` budget.
25
+ - **WRFC panel selected-chain summary** (`src/panels/wrfc-panel.ts`). The selected-chain summary row now shows `N sat / M total (K inherited)` when the focused chain has constraints, giving the operator at-a-glance status without expanding the row.
26
+ - **WRFC panel controller-flags block** (`src/panels/wrfc-panel.ts`). When the controller injects synthetic issues on the chain (e.g. fixer constraint-continuity violations), a `Controller flags` section renders above the reviewer Issues block with a `[CRITICAL]` prefix, so operators see why a chain went back to fixing even when the reviewer didn't flag anything.
27
+
28
+ ### Surface Audit — Per-Surface Verdict
29
+
30
+ | Surface | File | Verdict |
31
+ |---------|------|---------|
32
+ | WRFC chain panel | `src/panels/wrfc-panel.ts` | Reconciled — constraint badge `c:N/M`, expanded-detail severity-tagged markers (`[SAT]` / `[UNS CRIT|MAJOR|MINOR]` / `[UNV]`) with inherited ` *` suffix, selected-chain summary, controller-flags block, `WORKFLOW_CONSTRAINTS_ENUMERATED` subscription |
33
+ | System message router | `src/core/system-message-router.ts` | Not applicable — routing infrastructure, no direct event handling |
34
+ | Bootstrap runtime events | `src/runtime/bootstrap-core.ts` | Reconciled — 3 new subscriptions added |
35
+ | Process modal | `src/renderer/process-modal.ts` | Reconciled — Fix label shows constraint count |
36
+ | Agent Inspector panel | `src/panels/agent-inspector-panel.ts` | Reconciled — systemPromptAddendum indicator |
37
+ | Agent Logs panel | `src/panels/agent-logs-panel.ts` | Not applicable — log tail only, no report/constraint fields |
38
+ | Agent Detail modal | `src/renderer/agent-detail-modal.ts` | Reconciled — systemPromptAddendum indicator, constraint list, reviewer findings |
39
+ | Orchestration panel | `src/panels/orchestration-panel.ts` | Not applicable — reads `OrchestrationGraphRecord` from store domain, no constraint fields |
40
+ | Agent builtin panel registry | `src/panels/builtin/agent.ts` | Not applicable — registration only, no rendering |
41
+ | Tasks panel | `src/panels/builtin/operations.ts` | Not applicable — no WRFC chain rendering |
42
+ | Eval panel | `src/panels/eval-panel.ts` | Not applicable — eval suite scores, not WRFC constraint data |
43
+ | UI events plumbing | `src/runtime/ui-events.ts` | Not applicable — re-exports SDK; `WorkflowEvent` union already includes new event |
44
+ | Config command | `src/input/commands/config.ts` | Not applicable — no new SDK 0.23.0 config knobs |
45
+ | Runtime services | `src/runtime/services.ts` | Not applicable — no new service instantiation required |
46
+ | Bootstrap core | `src/runtime/bootstrap-core.ts` | Reconciled (see above) |
47
+ | Docs | `docs/` | Not applicable — TUI docs have minimal WRFC prose; no legacy content to update |
48
+
49
+ ---
50
+
7
51
  ## [0.19.22] — 2026-04-21
8
52
 
9
53
  Hotfix release: regenerates foundation artifacts against SDK 0.22.0 after 0.19.21's release pipeline failed the `foundation artifacts gate` test. No consumer-facing feature changes beyond what 0.19.21 carried.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml/badge.svg)](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Version](https://img.shields.io/badge/version-0.19.22-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
5
+ [![Version](https://img.shields.io/badge/version-0.19.23-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
6
6
 
7
7
  A terminal-native AI coding, operations, automation, knowledge, and integration console with a typed runtime, omnichannel surfaces, structured memory/knowledge, and a raw ANSI renderer.
8
8
 
@@ -159,6 +159,12 @@ The TUI now consumes the extracted `@pellux/goodvibes-sdk` platform layer for sh
159
159
  - Archetype registry that supports built-ins and user-defined markdown archetypes
160
160
  - Task lifecycle tracking across exec, agent, MCP, plugin, integration, daemon, scheduler, and ACP work
161
161
  - Automated WRFC loops with review/fix/check chains, configurable gates, and explicit evidence in completion reports
162
+ - WRFC panel renders a constraint badge (`c:N/M`) per chain, colored by aggregate satisfaction status
163
+ - Expanded chain detail shows each constraint with status marker `[SAT]`, `[UNS CRIT|MAJOR|MINOR]` (unsatisfied, severity-tagged), or `[UNV]` (unverified), with ` *` suffix for inherited constraints
164
+ - Selected-chain summary shows satisfied/total/inherited counts at a glance
165
+ - Controller-flagged synthetic issues render above reviewer issues as `[CRITICAL]` "Controller flags"
166
+ - Agent-detail modal surfaces `systemPromptAddendum` (WRFC engineer addendum) when present on the agent record
167
+ - System-message router surfaces `WORKFLOW_CONSTRAINTS_ENUMERATED` as an operator-visible message when constraints are loaded
162
168
  - Built-in planning/strategy layer with execution plans, adaptive plan modes, and status/explain/override controls
163
169
 
164
170
  ### Tools And Intelligence
@@ -1235,7 +1241,7 @@ Those pieces cover conversation-noise routing, panel-health/performance budgets,
1235
1241
  | `/fork [name]` | `/branch-save` | Save a named snapshot of the current conversation |
1236
1242
  | `/merge <name>` | — | Append messages from a branch after the fork point |
1237
1243
  | `/agents` | — | List active and completed agents |
1238
- | `/wrfc` | — | Show WRFC chain status |
1244
+ | `/wrfc` | — | Show WRFC chain status, constraint satisfaction counts, and per-constraint `[SAT]`/`[UNS]`/`[UNV]` breakdown |
1239
1245
  | `/health [action]` | — | Unified runtime health review and repair entry point |
1240
1246
  | `/guidance [action]` | — | Contextual operational guidance without cluttering the conversation |
1241
1247
  | `/remote [action]` | — | Distributed peer, node-host contract, work-queue, and artifact control room |
@@ -3,7 +3,7 @@
3
3
  "product": {
4
4
  "id": "goodvibes",
5
5
  "surface": "operator",
6
- "version": "0.22.0"
6
+ "version": "0.23.2"
7
7
  },
8
8
  "auth": {
9
9
  "modes": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-tui",
3
- "version": "0.19.22",
3
+ "version": "0.19.23",
4
4
  "description": "Terminal-native GoodVibes product for coding, operations, automation, knowledge, channels, and daemon-backed control-plane workflows.",
5
5
  "type": "module",
6
6
  "main": "src/main.ts",
@@ -90,7 +90,7 @@
90
90
  "@anthropic-ai/vertex-sdk": "^0.16.0",
91
91
  "@ast-grep/napi": "^0.42.0",
92
92
  "@aws/bedrock-token-generator": "^1.1.0",
93
- "@pellux/goodvibes-sdk": "0.22.0",
93
+ "@pellux/goodvibes-sdk": "0.23.2",
94
94
  "bash-language-server": "^5.6.0",
95
95
  "fuse.js": "^7.1.0",
96
96
  "graphql": "^16.13.2",
@@ -207,6 +207,8 @@ export class InputHandler {
207
207
  agentManager: uiServices.agents.agentManager,
208
208
  agentMessageBus: uiServices.agents.agentMessageBus,
209
209
  sessionLogPathResolver: (agentId) => uiServices.environment.shellPaths.resolveProjectPath('tui', 'sessions', `${agentId}.jsonl`),
210
+ // SDK 0.23.0: supply wrfcController so the modal can show constraint data
211
+ wrfcController: uiServices.agents.wrfcController,
210
212
  });
211
213
  this.bookmarkModal = new BookmarkModal(uiServices.shell.bookmarkManager);
212
214
  this.sessionPickerModal = new SessionPickerModal(uiServices.sessions.sessionManager);
@@ -317,6 +317,10 @@ export class AgentInspectorPanel extends BasePanel {
317
317
  [formatMs(elapsed), DEFAULT_PANEL_PALETTE.value],
318
318
  [' Tools ', DEFAULT_PANEL_PALETTE.label],
319
319
  [String(rec.toolCallCount), DEFAULT_PANEL_PALETTE.info],
320
+ // SDK 0.23.0: show addendum indicator when WRFC injected a constraint addendum
321
+ ...(rec.systemPromptAddendum
322
+ ? [[' Addendum ', DEFAULT_PANEL_PALETTE.label] as [string, string], ['yes', DEFAULT_PANEL_PALETTE.info] as [string, string]]
323
+ : []),
320
324
  [' Task ', DEFAULT_PANEL_PALETTE.label],
321
325
  [taskDisplay, DEFAULT_PANEL_PALETTE.value],
322
326
  ]);
@@ -1,5 +1,6 @@
1
1
  import type { Line } from '../types/grid.ts';
2
2
  import type { WrfcChain, WrfcState, QualityGateResult } from '@pellux/goodvibes-sdk/platform/agents/wrfc-types';
3
+ import type { Constraint, ConstraintFinding } from '@pellux/goodvibes-sdk/platform/agents/completion-report';
3
4
  import type { WrfcController } from '@pellux/goodvibes-sdk/platform/agents/wrfc-controller';
4
5
  import { BasePanel } from './base-panel.ts';
5
6
  import type { WorkflowEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/index';
@@ -48,6 +49,10 @@ const C = {
48
49
  issueSug: '#6b7280',
49
50
  gatePass: '#22c55e',
50
51
  gateFail: '#ef4444',
52
+ // constraint status
53
+ constraintSat: '#22c55e', // green — satisfied
54
+ constraintUnsat:'#ef4444', // red — unsatisfied
55
+ constraintUnv: '#4b5563', // grey — unverified (no finding yet)
51
56
  } as const;
52
57
 
53
58
  // ---------------------------------------------------------------------------
@@ -117,6 +122,37 @@ export function truncate(s: string, max: number): string {
117
122
  return s.slice(0, max - 3) + '...';
118
123
  }
119
124
 
125
+ // ---------------------------------------------------------------------------
126
+ // Constraint helpers
127
+ // ---------------------------------------------------------------------------
128
+
129
+ /**
130
+ * Returns display tag, foreground colour, and dim flag for a single constraint
131
+ * based on whether a reviewer finding exists for it.
132
+ */
133
+ export function constraintStatusMarker(
134
+ constraint: Constraint,
135
+ findings: ConstraintFinding[] | undefined,
136
+ ): { tag: string; fg: string; dim: boolean } {
137
+ const finding = findings?.find(f => f.constraintId === constraint.id);
138
+ if (!finding) {
139
+ return { tag: '[UNV]', fg: C.constraintUnv, dim: true };
140
+ }
141
+ if (finding.satisfied) {
142
+ return { tag: '[SAT]', fg: C.constraintSat, dim: false };
143
+ }
144
+ // Unsatisfied — use severity to pick colour and tag text
145
+ const sev = finding.severity ?? 'major';
146
+ let sevTag: string;
147
+ let fg: string;
148
+ switch (sev) {
149
+ case 'critical': sevTag = '[UNS CRIT]'; fg = C.issueCrit; break;
150
+ case 'minor': sevTag = '[UNS MINOR]'; fg = C.issueMin; break;
151
+ default: sevTag = '[UNS MAJOR]'; fg = C.issueMaj; break;
152
+ }
153
+ return { tag: sevTag, fg, dim: false };
154
+ }
155
+
120
156
  // ---------------------------------------------------------------------------
121
157
  // Panel
122
158
  // ---------------------------------------------------------------------------
@@ -228,6 +264,22 @@ export class WrfcPanel extends BasePanel {
228
264
  [' Scores ', DEFAULT_PANEL_PALETTE.label],
229
265
  [selectedChain.reviewScores.length > 0 ? selectedChain.reviewScores.map((score) => score.toFixed(0)).join(' -> ') : 'none', DEFAULT_PANEL_PALETTE.info],
230
266
  ]),
267
+ ...(selectedChain.constraints.length > 0 ? [
268
+ buildPanelLine(width, (() => {
269
+ const total = selectedChain.constraints.length;
270
+ const findings = selectedChain.reviewerReport?.constraintFindings;
271
+ const satisfied = findings ? findings.filter(f => f.satisfied).length : 0;
272
+ const inherited = selectedChain.constraints.filter(c => c.source === 'inherited').length;
273
+ const inheritedPart = inherited > 0 ? ` (${inherited} inherited)` : '';
274
+ const satFg = !findings || findings.length === 0
275
+ ? DEFAULT_PANEL_PALETTE.dim
276
+ : satisfied === total ? C.constraintSat : C.constraintUnsat;
277
+ return [
278
+ [' Constraints ', DEFAULT_PANEL_PALETTE.label],
279
+ [`${satisfied} sat / ${total} total${inheritedPart}`, satFg],
280
+ ] as Array<[string, string]>;
281
+ })()),
282
+ ] : []),
231
283
  ]
232
284
  : [];
233
285
 
@@ -300,7 +352,15 @@ export class WrfcPanel extends BasePanel {
300
352
  const latestScore = chain.reviewScores.length > 0
301
353
  ? ` ${chain.reviewScores[chain.reviewScores.length - 1].toFixed(1)}/10`
302
354
  : '';
303
- const rightInfo = `${latestScore}${fixes}${cycles} `;
355
+ // Constraint badge: c:sat/total — only when constraints exist
356
+ let constraintBadge = '';
357
+ if (chain.constraints.length > 0) {
358
+ const total = chain.constraints.length;
359
+ const findings = chain.reviewerReport?.constraintFindings;
360
+ const satisfied = findings ? findings.filter(f => f.satisfied).length : 0;
361
+ constraintBadge = ` c:${satisfied}/${total}`;
362
+ }
363
+ const rightInfo = `${latestScore}${fixes}${cycles}${constraintBadge} `;
304
364
 
305
365
  // Compute how much space the task text can use, then check if rightInfo fits.
306
366
  // If the terminal is narrow and rightInfo would overflow, omit it entirely
@@ -319,7 +379,32 @@ export class WrfcPanel extends BasePanel {
319
379
  ];
320
380
  if (remaining >= rightInfo.length + 1) {
321
381
  // Right-align rightInfo in the remaining space
322
- segments.push({ text: rightInfo.padStart(remaining), fg: isSelected ? fg : C.label });
382
+ // Colour the constraint badge separately when present
383
+ if (chain.constraints.length > 0 && !isSelected) {
384
+ const total = chain.constraints.length;
385
+ const findings = chain.reviewerReport?.constraintFindings;
386
+ const satisfied = findings ? findings.filter(f => f.satisfied).length : 0;
387
+ // Determine badge colour
388
+ let badgeFg: string;
389
+ if (!findings || findings.length === 0) {
390
+ badgeFg = C.constraintUnv;
391
+ } else if (satisfied === total) {
392
+ badgeFg = C.constraintSat;
393
+ } else if (findings.some(f => !f.satisfied)) {
394
+ badgeFg = C.constraintUnsat;
395
+ } else {
396
+ badgeFg = C.reviewing; // some unverified but none failed
397
+ }
398
+ // Split: everything before the badge, then the badge
399
+ const badgeText = ` c:${satisfied}/${total}`;
400
+ const beforeBadge = rightInfo.slice(0, rightInfo.length - badgeText.length - 1);
401
+ const padding = remaining - rightInfo.length;
402
+ segments.push({ text: beforeBadge.padStart(padding + beforeBadge.length), fg: isSelected ? fg : C.label });
403
+ segments.push({ text: badgeText, fg: badgeFg });
404
+ segments.push({ text: ' ', fg: '' });
405
+ } else {
406
+ segments.push({ text: rightInfo.padStart(remaining), fg: isSelected ? fg : C.label });
407
+ }
323
408
  }
324
409
  // else: no room — makeSegmentedLine will pad with spaces to fill width
325
410
 
@@ -358,6 +443,35 @@ export class WrfcPanel extends BasePanel {
358
443
  ]));
359
444
  }
360
445
 
446
+ // Constraints section (between Cycles and Gates)
447
+ if (chain.constraints.length > 0 && lines.length < maxLines) {
448
+ lines.push(buildStyledPanelLine(width, [{ text: `${indent}Constraints`, fg: C.label }]));
449
+ const MAX_CONSTRAINTS = 10;
450
+ const findings = chain.reviewerReport?.constraintFindings;
451
+ const displayed = chain.constraints.slice(0, MAX_CONSTRAINTS);
452
+ for (const constraint of displayed) {
453
+ if (lines.length >= maxLines) break;
454
+ const marker = constraintStatusMarker(constraint, findings);
455
+ const inheritedMark = constraint.source === 'inherited' ? ' *' : '';
456
+ const statusTag = `${marker.tag}${inheritedMark}`;
457
+ const rowPrefix = `${indent} ${statusTag} `;
458
+ const textMax = Math.max(8, width - rowPrefix.length);
459
+ const constraintText = truncate(constraint.text, textMax);
460
+ lines.push(buildStyledPanelLine(width, [
461
+ { text: `${indent} `, fg: C.dim },
462
+ { text: statusTag, fg: marker.fg, dim: marker.dim, bold: !marker.dim },
463
+ { text: ' ', fg: '' },
464
+ { text: constraintText, fg: C.value },
465
+ ]));
466
+ }
467
+ const remaining = chain.constraints.length - MAX_CONSTRAINTS;
468
+ if (remaining > 0 && lines.length < maxLines) {
469
+ lines.push(buildStyledPanelLine(width, [
470
+ { text: `${indent} (+${remaining} more)`, fg: C.dim, dim: true },
471
+ ]));
472
+ }
473
+ }
474
+
361
475
  // Quality gate results
362
476
  if (chain.gateResults && chain.gateResults.length > 0) {
363
477
  lines.push(buildStyledPanelLine(width, [{ text: `${indent}Gates`, fg: C.label }]));
@@ -374,6 +488,21 @@ export class WrfcPanel extends BasePanel {
374
488
  }
375
489
  }
376
490
 
491
+ // Synthetic issues injected by controller (continuity violations)
492
+ if (chain.syntheticIssues && chain.syntheticIssues.length > 0 && lines.length < maxLines) {
493
+ lines.push(buildStyledPanelLine(width, [{ text: `${indent}Controller flags`, fg: C.issueCrit, bold: true }]));
494
+ for (const synthetic of chain.syntheticIssues) {
495
+ if (lines.length >= maxLines) break;
496
+ const prefix = `${indent} [CRIT] `;
497
+ const descMax = Math.max(8, width - prefix.length);
498
+ const desc = truncate(synthetic.description, descMax);
499
+ lines.push(buildStyledPanelLine(width, [
500
+ { text: prefix, fg: C.issueCrit, bold: true },
501
+ { text: desc, fg: C.value },
502
+ ]));
503
+ }
504
+ }
505
+
377
506
  // Issues from reviewer
378
507
  const issues = chain.reviewerReport?.issues ?? [];
379
508
  if (issues.length > 0 && lines.length < maxLines) {
@@ -458,6 +587,7 @@ export class WrfcPanel extends BasePanel {
458
587
  this.workflowEvents.on('WORKFLOW_CHAIN_FAILED', refresh),
459
588
  this.workflowEvents.on('WORKFLOW_AUTO_COMMITTED', refresh),
460
589
  this.workflowEvents.on('WORKFLOW_CASCADE_ABORTED', refresh),
590
+ this.workflowEvents.on('WORKFLOW_CONSTRAINTS_ENUMERATED', refresh),
461
591
  );
462
592
  }
463
593
 
@@ -1,9 +1,9 @@
1
- import { join } from 'path';
2
1
  import { readFile } from 'fs/promises';
3
2
  import { type Line } from '../types/grid.ts';
4
3
  import { ModalFactory } from './modal-factory.ts';
5
4
  import type { AgentManager } from '@pellux/goodvibes-sdk/platform/tools/agent/index';
6
5
  import type { AgentMessageBus } from '@pellux/goodvibes-sdk/platform/agents/message-bus';
6
+ import type { WrfcController } from '@pellux/goodvibes-sdk/platform/agents/wrfc-controller';
7
7
  import { formatDuration } from './modal-utils.ts';
8
8
  import { logger } from '@pellux/goodvibes-sdk/platform/utils/logger';
9
9
  import { getOverlaySurfaceMetrics, getStableOverlayContentRows } from './overlay-viewport.ts';
@@ -19,6 +19,8 @@ export interface AgentDetailModalDeps {
19
19
  readonly agentManager: Pick<AgentManager, 'getStatus'>;
20
20
  readonly agentMessageBus: Pick<AgentMessageBus, 'getMessages'>;
21
21
  readonly sessionLogPathResolver: (agentId: string) => string;
22
+ /** Optional — when supplied, constraint data from the agent's WRFC chain is shown (SDK 0.23.0). */
23
+ readonly wrfcController?: Pick<WrfcController, 'getChain'>;
22
24
  }
23
25
 
24
26
  // ─── AgentDetailModal ─────────────────────────────────────────────────────────
@@ -176,6 +178,51 @@ export function renderAgentDetailModal(
176
178
  sections.push({ type: 'text', content: `Tool calls : ${rec.toolCallCount}` });
177
179
  sections.push({ type: 'text', content: `Est tokens : ~${tokenEst.toLocaleString()}` });
178
180
 
181
+ // SDK 0.23.0: systemPromptAddendum indicator — confirms WRFC constraint addendum was injected
182
+ if (rec.systemPromptAddendum) {
183
+ sections.push({
184
+ type: 'text',
185
+ content: 'Addendum : yes (WRFC constraint layer injected)',
186
+ style: { fg: '#aaffee' },
187
+ });
188
+ }
189
+
190
+ // SDK 0.23.0: constraint data from WRFC chain (engineer constraints + reviewer findings)
191
+ if (rec.wrfcId && modal.deps.wrfcController) {
192
+ try {
193
+ const chain = modal.deps.wrfcController.getChain(rec.wrfcId);
194
+ if (chain && chain.constraints.length > 0) {
195
+ sections.push({ type: 'separator' });
196
+ sections.push({
197
+ type: 'text',
198
+ content: `Constraints (${chain.constraints.length}):`,
199
+ style: { dim: true },
200
+ });
201
+ for (const c of chain.constraints) {
202
+ const src = c.source === 'inherited' ? ' [inherited]' : '';
203
+ const text = c.text.length > 80 ? c.text.slice(0, 77) + '…' : c.text;
204
+ sections.push({
205
+ type: 'text',
206
+ content: ` [${c.id}]${src} ${text}`,
207
+ style: { fg: '246' },
208
+ });
209
+ }
210
+ // Reviewer constraint findings (if review has completed)
211
+ const findings = chain.reviewerReport?.constraintFindings;
212
+ if (findings && findings.length > 0) {
213
+ const unsatisfied = findings.filter((f) => !f.satisfied);
214
+ sections.push({
215
+ type: 'text',
216
+ content: `Findings : ${findings.length} checked, ${unsatisfied.length} unsatisfied`,
217
+ style: { fg: unsatisfied.length > 0 ? '#ff6666' : '#44ff88' },
218
+ });
219
+ }
220
+ }
221
+ } catch {
222
+ // wrfcController.getChain throws when chain not found — normal during teardown
223
+ }
224
+ }
225
+
179
226
  // Progress
180
227
  if (rec.progress) {
181
228
  sections.push({ type: 'separator' });
@@ -60,7 +60,11 @@ function buildAgentLabel(rec: AgentRecord, deps: ProcessModalDeps): string {
60
60
  const attemptMatch = task.match(/Fix attempt:\s*(\d+)/);
61
61
  const attempt = attemptMatch ? attemptMatch[1] : '?';
62
62
  const desc = truncateFirst(originalTask ?? 'fix in progress', 45);
63
- return `[Fix #${attempt}] ${desc} (${fromScore} \u2192 ${toScore}/10)`;
63
+ // Show constraint count when the chain has constraints to target (SDK 0.23.0)
64
+ const chain = rec.wrfcId ? (() => { try { return deps.wrfcController.getChain(rec.wrfcId!); } catch { return null; } })() : null;
65
+ const constraintCount = chain && chain.constraints.length > 0 ? chain.constraints.length : 0;
66
+ const constraintSuffix = constraintCount > 0 ? ` [${constraintCount}c]` : '';
67
+ return `[Fix #${attempt}] ${desc} (${fromScore} \u2192 ${toScore}/10)${constraintSuffix}`;
64
68
  }
65
69
 
66
70
  // Regular agent — show template and truncated first line
@@ -292,6 +292,64 @@ export async function initializeBootstrapCore(
292
292
  wrfcController: services.wrfcController,
293
293
  });
294
294
 
295
+ // ── TUI-specific WRFC constraint-propagation event subscriptions (SDK 0.23.0) ──
296
+ // These supplement the SDK's registerBootstrapRuntimeEvents which handles the
297
+ // core WORKFLOW_REVIEW_COMPLETED / WORKFLOW_CHAIN_CREATED messages.
298
+ // The SDK does not surface constraint-specific system messages; the TUI layer
299
+ // adds them here so operators can observe constraint enumeration and violations
300
+ // in the SystemMessagesPanel and main conversation.
301
+ runtimeUnsubs.push(
302
+ runtimeBus.on<Extract<import('@pellux/goodvibes-sdk/platform/runtime/events/index').WorkflowEvent, { type: 'WORKFLOW_CONSTRAINTS_ENUMERATED' }>>(
303
+ 'WORKFLOW_CONSTRAINTS_ENUMERATED',
304
+ ({ payload }) => {
305
+ const router = systemMessageRouterRef.value;
306
+ if (!router) return;
307
+ const count = payload.constraints.length;
308
+ if (count > 0) {
309
+ router.wrfc(
310
+ `[WRFC] Engineer enumerated ${count} constraint${count !== 1 ? 's' : ''} for chain ${payload.chainId.slice(0, 12)}`,
311
+ 'low',
312
+ );
313
+ }
314
+ requestRender();
315
+ },
316
+ ),
317
+ );
318
+ runtimeUnsubs.push(
319
+ runtimeBus.on<Extract<import('@pellux/goodvibes-sdk/platform/runtime/events/index').WorkflowEvent, { type: 'WORKFLOW_FIX_ATTEMPTED' }>>(
320
+ 'WORKFLOW_FIX_ATTEMPTED',
321
+ ({ payload }) => {
322
+ const router = systemMessageRouterRef.value;
323
+ if (!router) return;
324
+ const targetIds = payload.targetConstraintIds;
325
+ if (targetIds && targetIds.length > 0) {
326
+ router.wrfc(
327
+ `[WRFC] Fix #${payload.attempt} targeting ${targetIds.length} constraint${targetIds.length !== 1 ? 's' : ''} on chain ${payload.chainId.slice(0, 12)}`,
328
+ 'low',
329
+ );
330
+ requestRender();
331
+ }
332
+ },
333
+ ),
334
+ );
335
+ runtimeUnsubs.push(
336
+ runtimeBus.on<Extract<import('@pellux/goodvibes-sdk/platform/runtime/events/index').WorkflowEvent, { type: 'WORKFLOW_REVIEW_COMPLETED' }>>(
337
+ 'WORKFLOW_REVIEW_COMPLETED',
338
+ ({ payload }) => {
339
+ const router = systemMessageRouterRef.value;
340
+ if (!router) return;
341
+ const unsatisfied = payload.unsatisfiedConstraintIds;
342
+ if (!payload.passed && unsatisfied && unsatisfied.length > 0) {
343
+ router.wrfc(
344
+ `[WRFC] ✗ Chain ${payload.chainId.slice(0, 12)}: ${unsatisfied.length} constraint violation${unsatisfied.length !== 1 ? 's' : ''} forced failure`,
345
+ 'high',
346
+ );
347
+ requestRender();
348
+ }
349
+ },
350
+ ),
351
+ );
352
+
295
353
  // Subscribe to companion main-chat messages received from the daemon's HTTP layer.
296
354
  // The daemon emits COMPANION_MESSAGE_RECEIVED on the runtime bus when a companion
297
355
  // POST /api/sessions/:id/messages with kind='message' arrives.
package/src/version.ts CHANGED
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  // The prebuild script updates the fallback value before compilation.
7
7
  // Uses import.meta.dir (Bun) to locate package.json relative to this file,
8
8
  // which is correct regardless of the process working directory.
9
- let _version = '0.19.22';
9
+ let _version = '0.19.23';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
12
12
  _version = pkg.version ?? _version;