bullswarm 0.18.2 → 0.18.4
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 +12 -0
- package/package.json +1 -1
- package/src/workflow/dashboard.js +23 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# bullswarm changelog
|
|
2
2
|
|
|
3
|
+
## 0.18.4 — reliable auto-follow tail
|
|
4
|
+
|
|
5
|
+
- Timestamp-aligned mobile auto-follow now preserves the newest workflow
|
|
6
|
+
milestone instead of replacing it with a spurious newer-rows marker.
|
|
7
|
+
|
|
8
|
+
## 0.18.3 — mobile timeline polish
|
|
9
|
+
|
|
10
|
+
- Auto-follow now begins at a timestamped milestone instead of exposing an
|
|
11
|
+
orphaned detail line when a long timeline is clipped on a small terminal.
|
|
12
|
+
- Qualified terminal runs state the number of concerns and direct the user to
|
|
13
|
+
review them in the result envelope.
|
|
14
|
+
|
|
3
15
|
## 0.18.2 — truthful recovery timeline and verifier cleanup
|
|
4
16
|
|
|
5
17
|
- Dependency-blocked plan branches now appear as a single skipped phase in the
|
package/package.json
CHANGED
|
@@ -559,13 +559,20 @@ function renderWorkflowOverviewPanel(model, width, height, spinnerFrame, timelin
|
|
|
559
559
|
const timelineRows = Math.max(1, contentRows - liveRows - nextRows);
|
|
560
560
|
const maxTimelineScroll = Math.max(0, timeline.lines.length - timelineRows);
|
|
561
561
|
const scroll = clamp(timelineScroll, 0, maxTimelineScroll);
|
|
562
|
-
const
|
|
563
|
-
let
|
|
564
|
-
if (start > 0
|
|
565
|
-
|
|
562
|
+
const end = Math.max(0, timeline.lines.length - scroll);
|
|
563
|
+
let start = Math.max(0, end - timelineRows);
|
|
564
|
+
if (start > 0) {
|
|
565
|
+
start = Math.max(0, end - Math.max(0, timelineRows - 1));
|
|
566
|
+
while (start < end && !/^\d{2}:\d{2}\s/.test(timeline.lines[start])) start += 1;
|
|
566
567
|
}
|
|
567
|
-
|
|
568
|
-
|
|
568
|
+
let visibleTimeline = timeline.lines.slice(start, end);
|
|
569
|
+
if (start > 0) {
|
|
570
|
+
visibleTimeline.unshift(dimText(`↑ ${start} earlier timeline rows`, inner));
|
|
571
|
+
}
|
|
572
|
+
if (end < timeline.lines.length && visibleTimeline.length) {
|
|
573
|
+
const marker = dimText(`↓ ${timeline.lines.length - end} newer timeline rows`, inner);
|
|
574
|
+
if (visibleTimeline.length >= timelineRows) visibleTimeline[visibleTimeline.length - 1] = marker;
|
|
575
|
+
else visibleTimeline.push(marker);
|
|
569
576
|
}
|
|
570
577
|
const visibleLive = live.lines.slice(0, liveRows);
|
|
571
578
|
const visibleNext = next.slice(0, nextRows);
|
|
@@ -784,14 +791,16 @@ function workflowLiveLines(model, width, spinnerFrame) {
|
|
|
784
791
|
lines.push('');
|
|
785
792
|
}
|
|
786
793
|
if (!lines.length) lines.push(state.finishedAt
|
|
787
|
-
? `${statusIcon(state.status)} No agents running · ${terminalWorkflowLabel(state.status)}`
|
|
794
|
+
? `${statusIcon(state.status)} No agents running · ${terminalWorkflowLabel(state.status, state.outcome?.concerns?.length)}`
|
|
788
795
|
: '⧖ Waiting for the next dispatch');
|
|
789
796
|
return { lines, running, waiting };
|
|
790
797
|
}
|
|
791
798
|
|
|
792
|
-
function terminalWorkflowLabel(status) {
|
|
799
|
+
function terminalWorkflowLabel(status, concernCount = 0) {
|
|
793
800
|
if (status === 'completed') return 'workflow finished';
|
|
794
|
-
if (status === 'completed_with_concerns') return
|
|
801
|
+
if (status === 'completed_with_concerns') return concernCount
|
|
802
|
+
? `workflow finished with ${concernCount} concern${concernCount === 1 ? '' : 's'}`
|
|
803
|
+
: 'workflow finished with concerns';
|
|
795
804
|
if (status === 'blocked') return 'workflow stopped with blockers';
|
|
796
805
|
if (status === 'failed') return 'workflow failed';
|
|
797
806
|
if (status === 'cancelled') return 'workflow cancelled';
|
|
@@ -802,14 +811,17 @@ function terminalWorkflowLabel(status) {
|
|
|
802
811
|
function workflowNextLines(model, width) {
|
|
803
812
|
const { state, orchestrator } = model;
|
|
804
813
|
if (state.finishedAt) {
|
|
805
|
-
const
|
|
814
|
+
const concernCount = state.outcome?.concerns?.length ?? 0;
|
|
815
|
+
const next = state.status === 'completed'
|
|
806
816
|
? 'result ready'
|
|
817
|
+
: state.status === 'completed_with_concerns'
|
|
818
|
+
? concernCount ? `review ${concernCount} concern${concernCount === 1 ? '' : 's'} in result` : 'review concerns in result'
|
|
807
819
|
: state.status === 'blocked' ? 'review blockers and partial work'
|
|
808
820
|
: state.status === 'failed' ? 'inspect the failure before using partial work'
|
|
809
821
|
: state.status === 'cancelled' ? 'review any partial work'
|
|
810
822
|
: state.status === 'interrupted' ? 'resume the workflow or inspect partial work'
|
|
811
823
|
: 'inspect the workflow result';
|
|
812
|
-
const label = terminalWorkflowLabel(state.status);
|
|
824
|
+
const label = terminalWorkflowLabel(state.status, concernCount);
|
|
813
825
|
return [truncate(`${statusIcon(state.status)} ${label[0].toUpperCase()}${label.slice(1)} · ${next}`, width)];
|
|
814
826
|
}
|
|
815
827
|
const ledger = state.actionLedger ?? [];
|