@pellux/goodvibes-tui 0.18.18 → 0.18.19
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 +27 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/renderer/panel-composite.ts +21 -1
- package/src/runtime/bootstrap-core.ts +24 -9
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,33 @@ All notable changes to GoodVibes TUI.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [0.18.19] — 2026-04-16
|
|
8
|
+
|
|
9
|
+
### Quality bump — address sub-10 dimensions from 0.18.18 review
|
|
10
|
+
|
|
11
|
+
The 0.18.18 review scored 9.76/10 with three dimensions below 10 (Error Handling 9.5, Testing 9.0, Maintainability 9.5). This release lifts each back to 10 per the now-10.0 WRFC score threshold.
|
|
12
|
+
|
|
13
|
+
### Error Handling
|
|
14
|
+
|
|
15
|
+
- **Render coalescer wraps both paths in try/catch** (`src/runtime/bootstrap-core.ts`): the `setImmediate` callback and the throttled `setTimeout` callback each now guard `renderRequestRef.value()` with try/catch. A thrown render exception no longer wedges the scheduler — `renderScheduled` is cleared unconditionally, the error is logged at error level, and the next `requestRender()` can still schedule. Previously a single render exception could leave `renderScheduled = true` (if thrown inside the callback) and deadlock the TUI until restart
|
|
16
|
+
|
|
17
|
+
### Testing
|
|
18
|
+
|
|
19
|
+
- **Added R1 16ms throttle-branch test** (`src/test/renderer/render-perf.test.ts`): exercises the `setTimeout` gated branch that fires when two bursts land within the 16ms window. Uses a monotonic clock to make timing deterministic. The previous test suite only covered the `setImmediate` immediate branch
|
|
20
|
+
- **Added R3 Compositor buffer-identity test** (`src/test/renderer/render-perf.test.ts`): drives the `Compositor` through 10 frames with identical dimensions and asserts the set of observed `frontBuffer`/`backBuffer` instances has cardinality exactly 2. Filters nulls so the brief post-swap null doesn't inflate the count. Proves the "2 TerminalBuffer instances per session" R3 claim that the 0.18.18 review flagged as claimed-but-untested
|
|
21
|
+
|
|
22
|
+
### Maintainability
|
|
23
|
+
|
|
24
|
+
- **Documented mid-render invalidation hazard** (`src/renderer/panel-composite.ts`): added a JSDoc block above the `renderPanel` cache explaining the race where an event listener firing during a panel's `render()` that sets `needsRender = true` would be clobbered by the trailing `markRendered()`. Includes a deferred-fix proposal (snapshot `needsRender` before calling `render()`) and documents why the current simpler implementation is acceptable
|
|
25
|
+
|
|
26
|
+
### Tests & Checks
|
|
27
|
+
|
|
28
|
+
- Test suite: 438/438 passing (11/11 in the expanded render-perf suite)
|
|
29
|
+
- Architecture check: passing
|
|
30
|
+
- Typecheck: clean
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
7
34
|
## [0.18.18] — 2026-04-16
|
|
8
35
|
|
|
9
36
|
### Performance
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](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
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-tui",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.19",
|
|
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",
|
|
@@ -7,7 +7,27 @@ import { createSplitPaneLayout } from './layout-engine.ts';
|
|
|
7
7
|
import { renderPanelTabBar } from './panel-tab-bar.ts';
|
|
8
8
|
import { renderPanelWorkspaceBar } from './panel-workspace-bar.ts';
|
|
9
9
|
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* R2: Per-panel render cache for dirty-flag skipping.
|
|
12
|
+
*
|
|
13
|
+
* Maintainability hazard — mid-render invalidation race:
|
|
14
|
+
*
|
|
15
|
+
* If an event listener (e.g. a runtime-bus subscriber) fires DURING a panel's
|
|
16
|
+
* `render()` call and mutates state such that `needsRender = true`, the
|
|
17
|
+
* trailing `panel.markRendered()` below will clobber that invalidation back
|
|
18
|
+
* to `false`, causing the next frame to serve stale cached output until
|
|
19
|
+
* something else invalidates the panel.
|
|
20
|
+
*
|
|
21
|
+
* In practice this is rare because `render()` is synchronous and short, and
|
|
22
|
+
* runtime-bus listeners that mutate panel state typically run outside the
|
|
23
|
+
* render pass. Panels that DO expect to mutate from within their own render
|
|
24
|
+
* path (e.g. deferred lazy-load) should call `this.invalidate()` AFTER the
|
|
25
|
+
* trailing work so the next frame picks it up.
|
|
26
|
+
*
|
|
27
|
+
* If this becomes a real bug, the fix is to snapshot `needsRender` before
|
|
28
|
+
* calling `render()` and only `markRendered()` if the snapshot was true —
|
|
29
|
+
* preserving any concurrent invalidation. Deferred pending more evidence.
|
|
30
|
+
*/
|
|
11
31
|
interface PanelRenderCache {
|
|
12
32
|
lines: Line[];
|
|
13
33
|
width: number;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ConversationManager } from '../core/conversation';
|
|
2
2
|
import { SelectionManager } from '../input/selection.ts';
|
|
3
|
+
import { logger } from '@pellux/goodvibes-sdk/platform/utils/logger';
|
|
3
4
|
import { ConfigManager, getConfiguredSystemPrompt } from '../config/index.ts';
|
|
4
5
|
import { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools/registry';
|
|
5
6
|
import { registerAllTools } from '@pellux/goodvibes-sdk/platform/tools/index';
|
|
@@ -235,19 +236,33 @@ export async function initializeBootstrapCore(
|
|
|
235
236
|
if (renderScheduled) return;
|
|
236
237
|
renderScheduled = true;
|
|
237
238
|
setImmediate(() => {
|
|
239
|
+
// Error Handling: the scheduler flag MUST be cleared even if the render
|
|
240
|
+
// callback throws; otherwise a single render exception would wedge the
|
|
241
|
+
// entire TUI (no future requestRender() call would schedule anything).
|
|
238
242
|
renderScheduled = false;
|
|
239
243
|
const now = Date.now();
|
|
240
244
|
const elapsed = now - lastRenderTime;
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
245
|
+
try {
|
|
246
|
+
if (elapsed < RENDER_INTERVAL_MS) {
|
|
247
|
+
// Too soon — debounce to the tail of the current 16ms window
|
|
248
|
+
const delay = RENDER_INTERVAL_MS - elapsed;
|
|
249
|
+
setTimeout(() => {
|
|
250
|
+
try {
|
|
251
|
+
lastRenderTime = Date.now();
|
|
252
|
+
renderRequestRef.value();
|
|
253
|
+
} catch (err) {
|
|
254
|
+
// Throttled-render error: swallow but log at error so the next
|
|
255
|
+
// requestRender() call can still schedule. The renderer itself
|
|
256
|
+
// is expected to surface failures via its own error path.
|
|
257
|
+
logger.error('Throttled render threw; next requestRender will reschedule', { error: String(err) });
|
|
258
|
+
}
|
|
259
|
+
}, delay);
|
|
260
|
+
} else {
|
|
261
|
+
lastRenderTime = now;
|
|
246
262
|
renderRequestRef.value();
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
renderRequestRef.value();
|
|
263
|
+
}
|
|
264
|
+
} catch (err) {
|
|
265
|
+
logger.error('Immediate render threw; next requestRender will reschedule', { error: String(err) });
|
|
251
266
|
}
|
|
252
267
|
});
|
|
253
268
|
};
|
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.18.
|
|
9
|
+
let _version = '0.18.19';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
|
|
12
12
|
_version = pkg.version ?? _version;
|