@walwal-harness/cli 7.1.40 → 7.1.42
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/HR-Resource/cdo/SKILL.md +9 -1
- package/HR-Resource/ceo/SKILL.md +33 -2
- package/HR-Resource/coo/SKILL.md +9 -1
- package/HR-Resource/cqo/SKILL.md +9 -1
- package/HR-Resource/cto/SKILL.md +10 -2
- package/HR-Resource/ops/SKILL.md +12 -4
- package/HR-Resource/ops-prompt-inspector/SKILL.md +11 -6
- package/HR-Resource/ops-prompt-inspector/assets/prompt-inspector-static.js +180 -0
- package/HR-Resource/ops-prompt-inspector/scripts/setup.py +66 -3
- package/apps/harness-dashboard/app/api/metrics/route.ts +17 -0
- package/apps/harness-dashboard/app/api/wake/route.ts +41 -0
- package/apps/harness-dashboard/app/brick.css +482 -0
- package/apps/harness-dashboard/app/layout.tsx +13 -0
- package/apps/harness-dashboard/app/page.tsx +2 -2
- package/apps/harness-dashboard/components/Scene.tsx +690 -36
- package/apps/harness-dashboard/components/brick/BrickDashboard.tsx +149 -0
- package/apps/harness-dashboard/components/brick/reports.tsx +291 -0
- package/apps/harness-dashboard/components/brick/tweaks.tsx +738 -0
- package/apps/harness-dashboard/components/brick/ui.tsx +463 -0
- package/apps/harness-dashboard/components/brick/views-graph.tsx +305 -0
- package/apps/harness-dashboard/components/brick/views-grid.tsx +543 -0
- package/apps/harness-dashboard/components/brick/views-timeline.tsx +238 -0
- package/apps/harness-dashboard/lib/__tests__/harness-state.test.ts +1 -202
- package/apps/harness-dashboard/lib/__tests__/sandbox-e2e.test.ts +490 -0
- package/apps/harness-dashboard/lib/brick/adapter.ts +423 -0
- package/apps/harness-dashboard/lib/brick/contract.ts +247 -0
- package/apps/harness-dashboard/lib/harness-state.ts +107 -796
- package/apps/harness-dashboard/lib/metrics/sampler.ts +545 -0
- package/apps/harness-dashboard/lib/types.ts +22 -222
- package/apps/harness-dashboard/playwright.config.ts +26 -7
- package/assets/templates/AGENTS-ko.md.template +1 -1
- package/assets/templates/AGENTS.md.template +12 -2
- package/assets/templates/HARNESS.md +2 -0
- package/assets/templates/company-pipeline-manifest.json +5 -14
- package/assets/templates/worker-report.md.template +38 -0
- package/bin/init.js +2 -1
- package/commands/goal.md +4 -2
- package/commands/hot-fix.md +1 -1
- package/commands/submission.md +1 -1
- package/package.json +1 -1
- package/scripts/conductor-tick.sh +12 -0
- package/scripts/harness-agenda.sh +105 -0
- package/scripts/harness-company-block.sh +89 -0
- package/scripts/harness-company-complete.sh +17 -8
- package/scripts/harness-company-cycle.sh +54 -0
- package/scripts/harness-progress-set.sh +8 -2
- package/scripts/harness-stop.sh +76 -0
- package/scripts/harness-wake-control.sh +151 -0
- package/scripts/harness-wake.sh +5 -0
- package/scripts/harness-worker-dispatch.sh +31 -7
- package/apps/harness-dashboard/components/Drawer.tsx +0 -146
- package/apps/harness-dashboard/components/Header.tsx +0 -40
- package/apps/harness-dashboard/components/MissionTimeline.tsx +0 -214
- package/apps/harness-dashboard/components/OrgTree.tsx +0 -459
- package/apps/harness-dashboard/components/drawer/AgentLogTab.tsx +0 -56
- package/apps/harness-dashboard/components/drawer/DesignPreviewTab.tsx +0 -160
- package/apps/harness-dashboard/components/drawer/GotchasTab.tsx +0 -89
- package/apps/harness-dashboard/components/drawer/HypothesisTab.tsx +0 -42
- package/apps/harness-dashboard/components/drawer/IncidentsTab.tsx +0 -50
- package/apps/harness-dashboard/components/drawer/MissionDocTab.tsx +0 -96
- package/apps/harness-dashboard/components/drawer/MissionFlowTab.tsx +0 -576
- package/apps/harness-dashboard/components/drawer/OwnerHistoryTab.tsx +0 -160
- package/apps/harness-dashboard/components/drawer/RoomMetricsTab.tsx +0 -90
- package/apps/harness-dashboard/components/drawer/TracksTab.tsx +0 -47
- package/apps/harness-dashboard/components/three/ArchiveBoxes.tsx +0 -95
- package/apps/harness-dashboard/components/three/DeptBoard.tsx +0 -175
- package/apps/harness-dashboard/components/three/Floor3D.tsx +0 -404
- package/apps/harness-dashboard/components/three/GoalCard3D.tsx +0 -123
- package/apps/harness-dashboard/components/three/HypothesisCards.tsx +0 -71
- package/apps/harness-dashboard/components/three/MeetingWhiteboard.tsx +0 -98
- package/apps/harness-dashboard/components/three/Minifig3D.tsx +0 -391
- package/apps/harness-dashboard/components/three/OpsSiren.tsx +0 -99
- package/apps/harness-dashboard/components/three/Stage3D.tsx +0 -104
- package/apps/harness-dashboard/components/three/TrackRibbons.tsx +0 -81
- package/apps/harness-dashboard/lib/__tests__/i18n.test.ts +0 -16
- package/apps/harness-dashboard/lib/__tests__/iso.test.ts +0 -42
- package/apps/harness-dashboard/lib/__tests__/state-mapping.test.ts +0 -183
- package/apps/harness-dashboard/lib/agent-roster.ts +0 -42
- package/apps/harness-dashboard/lib/furniture.ts +0 -165
- package/apps/harness-dashboard/lib/i18n.ts +0 -26
- package/apps/harness-dashboard/lib/iso.ts +0 -150
- package/apps/harness-dashboard/lib/markdown.tsx +0 -306
- package/apps/harness-dashboard/lib/path-planning.ts +0 -72
- package/apps/harness-dashboard/lib/state-mapping.ts +0 -146
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/* =============================================================
|
|
4
|
+
walwal-harness · VIEW 3 — TIMELINE (cadence heatmap + swimlanes)
|
|
5
|
+
Ported from harness/views-timeline.jsx (design handoff).
|
|
6
|
+
Exports: TimelineView, Cadence
|
|
7
|
+
============================================================= */
|
|
8
|
+
|
|
9
|
+
import React, { useRef } from "react";
|
|
10
|
+
import type { CSSProperties } from "react";
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
AgentRole,
|
|
14
|
+
ContractState,
|
|
15
|
+
ContractWorker,
|
|
16
|
+
DocTarget,
|
|
17
|
+
} from "../../lib/brick/contract";
|
|
18
|
+
import { StatusDot, Label } from "./ui";
|
|
19
|
+
|
|
20
|
+
const SPAN_MS = 12 * 3600 * 1000; // 12h window, now=left
|
|
21
|
+
|
|
22
|
+
/** Inline style that also carries the per-agent `--hue` CSS custom property. */
|
|
23
|
+
type HueStyle = CSSProperties & { "--hue": string };
|
|
24
|
+
|
|
25
|
+
/* ---- Cadence (also rendered by the COMMAND view) ---------------------- */
|
|
26
|
+
|
|
27
|
+
export interface CadenceProps {
|
|
28
|
+
/** 24 hourly buckets = owner prompts/hour. */
|
|
29
|
+
cadence: number[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function Cadence({ cadence }: CadenceProps) {
|
|
33
|
+
const max = Math.max(...cadence, 1);
|
|
34
|
+
return (
|
|
35
|
+
<div className="cadence">
|
|
36
|
+
<Label right={"max " + max + " · 24h"}>CADENCE · owner prompts / h</Label>
|
|
37
|
+
<div className="cad-row">
|
|
38
|
+
{cadence.map((v, i) => (
|
|
39
|
+
<div
|
|
40
|
+
key={i}
|
|
41
|
+
className="cad-cell"
|
|
42
|
+
title={v + " / h"}
|
|
43
|
+
style={{
|
|
44
|
+
background:
|
|
45
|
+
v === 0 ? "transparent" : `rgba(229,72,77,${0.28 + 0.55 * (v / max)})`,
|
|
46
|
+
borderColor: v === 0 ? "var(--bd)" : "rgba(229,72,77,0.5)",
|
|
47
|
+
}}
|
|
48
|
+
/>
|
|
49
|
+
))}
|
|
50
|
+
</div>
|
|
51
|
+
<div className="cad-axis">
|
|
52
|
+
<span>now</span>
|
|
53
|
+
<span>−12h</span>
|
|
54
|
+
<span>−24h</span>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* ---- TimelineView ----------------------------------------------------- */
|
|
61
|
+
|
|
62
|
+
export interface TimelineViewProps {
|
|
63
|
+
/** The single source-of-truth contract state. */
|
|
64
|
+
s: ContractState;
|
|
65
|
+
/** Open a document (agent report or worker brief) in the DocViewer. */
|
|
66
|
+
openDoc?: (target: DocTarget) => void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type SwimMark = { ago: number; dur: number };
|
|
70
|
+
|
|
71
|
+
type SwimRow = {
|
|
72
|
+
kind: "exec" | "worker";
|
|
73
|
+
id: string;
|
|
74
|
+
label: string;
|
|
75
|
+
hue: string;
|
|
76
|
+
agent: AgentRole;
|
|
77
|
+
status: string;
|
|
78
|
+
progress?: number | null;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export function TimelineView({ s, openDoc }: TimelineViewProps) {
|
|
82
|
+
// stable seeded marks per row id
|
|
83
|
+
const markCache = useRef<Record<string, SwimMark[]>>({});
|
|
84
|
+
function seedMarks(id: string, density: number): SwimMark[] {
|
|
85
|
+
const cached = markCache.current[id];
|
|
86
|
+
if (cached) return cached;
|
|
87
|
+
const arr: SwimMark[] = [];
|
|
88
|
+
const n = Math.floor(density * (4 + Math.random() * 7));
|
|
89
|
+
for (let i = 0; i < n; i++) {
|
|
90
|
+
arr.push({ ago: Math.random() * SPAN_MS, dur: 40000 + Math.random() * 600000 });
|
|
91
|
+
}
|
|
92
|
+
markCache.current[id] = arr;
|
|
93
|
+
return arr;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// build rows: exec + its workers
|
|
97
|
+
const order: AgentRole[] = ["ceo", "coo", "cto", "cqo", "cdo", "ops"];
|
|
98
|
+
const rows: SwimRow[] = [];
|
|
99
|
+
order.forEach((id) => {
|
|
100
|
+
const a = s.agents.find((x) => x.id === id);
|
|
101
|
+
if (!a) return;
|
|
102
|
+
rows.push({ kind: "exec", id: a.id, label: a.name, hue: a.hue, agent: a.id, status: a.status });
|
|
103
|
+
s.workers
|
|
104
|
+
.filter((w) => w.agent === id)
|
|
105
|
+
.forEach((w) => {
|
|
106
|
+
rows.push({
|
|
107
|
+
kind: "worker",
|
|
108
|
+
id: w.id,
|
|
109
|
+
label: w.name,
|
|
110
|
+
hue: a.hue,
|
|
111
|
+
agent: a.id,
|
|
112
|
+
status: w.status,
|
|
113
|
+
progress: w.progress,
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// live marks from recent events (last 6 min) keyed to exec rows
|
|
119
|
+
const liveByAgent: Record<string, number[]> = {};
|
|
120
|
+
s.events.forEach((e) => {
|
|
121
|
+
if (e.at == null || e.agent === "") return;
|
|
122
|
+
const age = s.now - e.at;
|
|
123
|
+
if (age < 6 * 60 * 1000) {
|
|
124
|
+
(liveByAgent[e.agent] = liveByAgent[e.agent] || []).push(age);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const xOf = (ago: number): number => Math.max(0, Math.min(100, (ago / SPAN_MS) * 100));
|
|
129
|
+
|
|
130
|
+
// axis ticks
|
|
131
|
+
const ticks: number[] = [];
|
|
132
|
+
for (let h = 0; h <= 12; h += 2) ticks.push(h);
|
|
133
|
+
|
|
134
|
+
const openRow = (r: SwimRow) => {
|
|
135
|
+
if (!openDoc) return;
|
|
136
|
+
if (r.kind === "exec") {
|
|
137
|
+
openDoc({ type: "agent", agent: r.agent });
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const worker: ContractWorker | undefined = s.workers.find((w) => w.id === r.id);
|
|
141
|
+
if (worker) openDoc({ type: "worker", agent: r.agent, worker });
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<div className="timelineview">
|
|
146
|
+
<Cadence cadence={s.cadence} />
|
|
147
|
+
<div className="swim">
|
|
148
|
+
<Label
|
|
149
|
+
right={
|
|
150
|
+
rows.filter((r) => r.kind === "worker").length +
|
|
151
|
+
" workers · " +
|
|
152
|
+
order.length +
|
|
153
|
+
" execs"
|
|
154
|
+
}
|
|
155
|
+
>
|
|
156
|
+
ORCHESTRATION TIMELINE · now → −12h
|
|
157
|
+
</Label>
|
|
158
|
+
<div className="swim-axis">
|
|
159
|
+
<div className="swim-axis-pad" />
|
|
160
|
+
<div className="swim-axis-track">
|
|
161
|
+
{ticks.map((h) => (
|
|
162
|
+
<span
|
|
163
|
+
key={h}
|
|
164
|
+
className="swim-tick"
|
|
165
|
+
style={{ left: (h / 12) * 100 + "%" }}
|
|
166
|
+
>
|
|
167
|
+
{h === 0 ? "now" : "−" + h + "h"}
|
|
168
|
+
</span>
|
|
169
|
+
))}
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
<div className="swim-rows">
|
|
173
|
+
{rows.map((r) => {
|
|
174
|
+
const marks = seedMarks(r.id, r.kind === "exec" ? 1.4 : 0.8);
|
|
175
|
+
const live = r.kind === "exec" ? liveByAgent[r.agent] || [] : [];
|
|
176
|
+
return (
|
|
177
|
+
<div
|
|
178
|
+
key={r.id}
|
|
179
|
+
className={"swim-row " + (r.kind === "exec" ? "is-exec" : "is-worker")}
|
|
180
|
+
onClick={() => openRow(r)}
|
|
181
|
+
>
|
|
182
|
+
<div className="swim-label" style={{ "--hue": r.hue } as HueStyle}>
|
|
183
|
+
{r.kind === "exec" ? (
|
|
184
|
+
<span
|
|
185
|
+
className="glyph glyph-sm"
|
|
186
|
+
style={{ color: r.hue, borderColor: r.hue + "55" }}
|
|
187
|
+
>
|
|
188
|
+
{r.label}
|
|
189
|
+
</span>
|
|
190
|
+
) : (
|
|
191
|
+
<span className="swim-wlabel">
|
|
192
|
+
<span className="swim-tree">└</span>
|
|
193
|
+
{r.label}
|
|
194
|
+
</span>
|
|
195
|
+
)}
|
|
196
|
+
{r.kind === "exec" && <StatusDot status={r.status} size={6} />}
|
|
197
|
+
</div>
|
|
198
|
+
<div className="swim-track">
|
|
199
|
+
<div className="swim-gridlines">
|
|
200
|
+
{ticks.map((h) => (
|
|
201
|
+
<span key={h} style={{ left: (h / 12) * 100 + "%" }} />
|
|
202
|
+
))}
|
|
203
|
+
</div>
|
|
204
|
+
{marks.map((m, i) => (
|
|
205
|
+
<span
|
|
206
|
+
key={i}
|
|
207
|
+
className="swim-block"
|
|
208
|
+
style={{
|
|
209
|
+
left: xOf(m.ago) + "%",
|
|
210
|
+
width: Math.max(0.6, (m.dur / SPAN_MS) * 100) + "%",
|
|
211
|
+
background: r.hue,
|
|
212
|
+
opacity: r.kind === "exec" ? 0.85 : 0.55,
|
|
213
|
+
}}
|
|
214
|
+
/>
|
|
215
|
+
))}
|
|
216
|
+
{live.map((age, i) => (
|
|
217
|
+
<span
|
|
218
|
+
key={"l" + i}
|
|
219
|
+
className="swim-live"
|
|
220
|
+
style={{ left: xOf(age) + "%", background: r.hue }}
|
|
221
|
+
/>
|
|
222
|
+
))}
|
|
223
|
+
{r.kind === "worker" && r.progress != null && (
|
|
224
|
+
<span
|
|
225
|
+
className="swim-now-prog"
|
|
226
|
+
style={{ background: r.hue }}
|
|
227
|
+
title={Math.round(r.progress) + "%"}
|
|
228
|
+
/>
|
|
229
|
+
)}
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
);
|
|
233
|
+
})}
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
);
|
|
238
|
+
}
|
|
@@ -3,7 +3,6 @@ import { mkdtempSync, mkdirSync, rmSync, utimesSync, writeFileSync } from "node:
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { readHarnessState } from "../harness-state";
|
|
6
|
-
import { AGENT_ROSTER } from "../agent-roster";
|
|
7
6
|
|
|
8
7
|
describe("readHarnessState", () => {
|
|
9
8
|
let dir: string;
|
|
@@ -16,54 +15,9 @@ describe("readHarnessState", () => {
|
|
|
16
15
|
rmSync(dir, { recursive: true, force: true });
|
|
17
16
|
});
|
|
18
17
|
|
|
19
|
-
it("valid progress.json: derives typing for current_agent, talking for meetings.active, idle for the rest", () => {
|
|
20
|
-
mkdirSync(path.join(dir, ".harness"), { recursive: true });
|
|
21
|
-
writeFileSync(
|
|
22
|
-
path.join(dir, ".harness", "progress.json"),
|
|
23
|
-
JSON.stringify({
|
|
24
|
-
sprint: { number: 1 },
|
|
25
|
-
current_agent: "generator-backend",
|
|
26
|
-
agent_status: "running",
|
|
27
|
-
meetings: { active: ["planner"], cadence: "normal", next_scheduled: null },
|
|
28
|
-
cto: { last_review: "2026-05-07", open_arch_risks: 0 },
|
|
29
|
-
cqo: { last_audit: null, sprint_verdict: "pending", open_regressions: 0 },
|
|
30
|
-
service_ops: { monitor: { last_check: "2026-05-07", alerts_this_sprint: 0 } },
|
|
31
|
-
goals: {
|
|
32
|
-
active_id: "g1",
|
|
33
|
-
list: [{ id: "g1", title: "Brick Office MVP", description: "Phase C launch" }],
|
|
34
|
-
current_adherence: 0.6,
|
|
35
|
-
},
|
|
36
|
-
})
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
const snap = readHarnessState(dir);
|
|
40
|
-
expect(snap.errorBanner).toBeNull();
|
|
41
|
-
expect(snap.agents.length).toBe(AGENT_ROSTER.length);
|
|
42
|
-
expect(snap.agents.map((a) => a.id).sort()).toEqual(
|
|
43
|
-
AGENT_ROSTER.map((a) => a.id).sort()
|
|
44
|
-
);
|
|
45
|
-
const stateOf = (id: string) => snap.agents.find((a) => a.id === id)!.minifigState;
|
|
46
|
-
expect(stateOf("generator-backend")).toBe("typing");
|
|
47
|
-
expect(stateOf("planner")).toBe("talking");
|
|
48
|
-
expect(stateOf("dispatcher")).toBe("idle");
|
|
49
|
-
expect(snap.rooms.length).toBe(7);
|
|
50
|
-
|
|
51
|
-
const planner = snap.agents.find((a) => a.id === "planner")!;
|
|
52
|
-
expect(planner.room).toBe("meeting");
|
|
53
|
-
expect(planner.homeRoom).toBe("coo");
|
|
54
|
-
|
|
55
|
-
const meetingRoom = snap.rooms.find((r) => r.id === "meeting")!;
|
|
56
|
-
expect(meetingRoom.seatLayout).toBeDefined();
|
|
57
|
-
expect(meetingRoom.seatLayout!.occupants).toContain("planner");
|
|
58
|
-
|
|
59
|
-
expect(snap.goal).not.toBeNull();
|
|
60
|
-
expect(snap.goal!.title).toBe("Brick Office MVP");
|
|
61
|
-
});
|
|
62
|
-
|
|
63
18
|
it("missing .harness: returns error banner, no crash", () => {
|
|
64
19
|
const snap = readHarnessState(dir);
|
|
65
20
|
expect(snap.errorBanner?.level).toBe("error");
|
|
66
|
-
expect(snap.agents.length).toBe(AGENT_ROSTER.length);
|
|
67
21
|
});
|
|
68
22
|
|
|
69
23
|
it("corrupt progress.json: returns error banner, no crash", () => {
|
|
@@ -72,124 +26,6 @@ describe("readHarnessState", () => {
|
|
|
72
26
|
const snap = readHarnessState(dir);
|
|
73
27
|
expect(snap.errorBanner?.level).toBe("error");
|
|
74
28
|
expect(snap.errorBanner?.message_en).toContain("corrupt");
|
|
75
|
-
expect(snap.agents.length).toBe(AGENT_ROSTER.length);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("v6 NEXUS: roster includes CTO, CQO, Brainstormer", () => {
|
|
79
|
-
const ids = AGENT_ROSTER.map((a) => a.id);
|
|
80
|
-
expect(ids).toContain("cto");
|
|
81
|
-
expect(ids).toContain("cqo");
|
|
82
|
-
expect(ids).toContain("brainstormer");
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("parallel tracks, hypothesis, escalations, incidents land in the snapshot", () => {
|
|
86
|
-
mkdirSync(path.join(dir, ".harness"), { recursive: true });
|
|
87
|
-
writeFileSync(
|
|
88
|
-
path.join(dir, ".harness", "progress.json"),
|
|
89
|
-
JSON.stringify({
|
|
90
|
-
sprint: { number: 3 },
|
|
91
|
-
pipeline: "FULLSTACK",
|
|
92
|
-
current_agent: "conductor",
|
|
93
|
-
agent_status: "running",
|
|
94
|
-
meetings: {
|
|
95
|
-
active: [],
|
|
96
|
-
cadence: "heavy",
|
|
97
|
-
next_scheduled: "2026-05-08T10:00Z",
|
|
98
|
-
current: { type: "incident-war-room", topic: "P0 prod outage" },
|
|
99
|
-
},
|
|
100
|
-
cto: {
|
|
101
|
-
last_review: "2026-05-07",
|
|
102
|
-
open_arch_risks: 1,
|
|
103
|
-
contract_signed: { be: true, fe: false },
|
|
104
|
-
},
|
|
105
|
-
cqo: {
|
|
106
|
-
last_audit: "2026-05-07",
|
|
107
|
-
sprint_verdict: "pending",
|
|
108
|
-
open_regressions: 2,
|
|
109
|
-
last_scores: {
|
|
110
|
-
functional: 2.9,
|
|
111
|
-
visual: 2.8,
|
|
112
|
-
code_quality: 2.5,
|
|
113
|
-
architecture: 2.95,
|
|
114
|
-
security: 3.0,
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
service_ops: {
|
|
118
|
-
monitor: { last_check: "2026-05-07", alerts_this_sprint: 4 },
|
|
119
|
-
incident: {
|
|
120
|
-
open: [
|
|
121
|
-
{ id: "INC-1", dept: "CTO", severity: "critical", message: "DB down" },
|
|
122
|
-
{ id: "INC-2", dept: "Operations", severity: "medium" },
|
|
123
|
-
],
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
parallel_tracks: [
|
|
127
|
-
{
|
|
128
|
-
id: "T-1",
|
|
129
|
-
from_meeting: "spec-review",
|
|
130
|
-
to_dept: "CTO",
|
|
131
|
-
to_room: "cto-team",
|
|
132
|
-
status: "in_progress",
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
id: "T-2",
|
|
136
|
-
from_meeting: "spec-review",
|
|
137
|
-
to_dept: "Planner",
|
|
138
|
-
to_room: "coo",
|
|
139
|
-
status: "dispatched",
|
|
140
|
-
},
|
|
141
|
-
],
|
|
142
|
-
hypothesis: {
|
|
143
|
-
active: [
|
|
144
|
-
{ id: "H-1", brief: "Cache hit > 80%", verdict: "pending" },
|
|
145
|
-
{ id: "H-2", brief: "p95 < 200ms", verdict: "valid" },
|
|
146
|
-
],
|
|
147
|
-
},
|
|
148
|
-
escalations: {
|
|
149
|
-
open: [{ id: "E-1", reason: "three-fail", message: "regression x3" }],
|
|
150
|
-
},
|
|
151
|
-
contracts: {
|
|
152
|
-
api: { version: "v2.1.0" },
|
|
153
|
-
feature_list: { total: 16, passed: 12, failed: 1 },
|
|
154
|
-
},
|
|
155
|
-
})
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
const snap = readHarnessState(dir);
|
|
159
|
-
expect(snap.errorBanner).toBeNull();
|
|
160
|
-
|
|
161
|
-
expect(snap.tracks.map((t) => t.id)).toEqual(["T-1", "T-2"]);
|
|
162
|
-
expect(snap.incidents.length).toBe(2);
|
|
163
|
-
expect(snap.hypothesis.length).toBe(2);
|
|
164
|
-
expect(snap.escalations.length).toBe(1);
|
|
165
|
-
expect(snap.contract.pipeline).toBe("FULLSTACK");
|
|
166
|
-
expect(snap.contract.sprint_number).toBe(3);
|
|
167
|
-
expect(snap.contract.feature_total).toBe(16);
|
|
168
|
-
expect(snap.contract.contract_signed).toEqual({ be: true, fe: false });
|
|
169
|
-
expect(snap.evalScores?.code_quality).toBe(2.5);
|
|
170
|
-
expect(snap.meetings.cadence).toBe("heavy");
|
|
171
|
-
expect(snap.meetings.current?.type).toBe("incident-war-room");
|
|
172
|
-
|
|
173
|
-
// CTO has an open incident → CTO-dept agents go red-alert; Service-Ops
|
|
174
|
-
// is on-call and is *always* red when any incident is open.
|
|
175
|
-
const stateOf = (id: string) => snap.agents.find((a) => a.id === id)!.minifigState;
|
|
176
|
-
expect(stateOf("cto")).toBe("red-alert");
|
|
177
|
-
expect(stateOf("service-ops")).toBe("red-alert");
|
|
178
|
-
expect(stateOf("evaluator-visual")).toBe("idle"); // CQO dept untouched
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it("buildArchive reads verdict.json when present", () => {
|
|
182
|
-
const sprintDir = path.join(dir, ".harness", "archive", "sprint-1");
|
|
183
|
-
mkdirSync(sprintDir, { recursive: true });
|
|
184
|
-
writeFileSync(path.join(sprintDir, "verdict.json"), JSON.stringify({ result: "PASS" }));
|
|
185
|
-
mkdirSync(path.join(dir, ".harness"), { recursive: true });
|
|
186
|
-
writeFileSync(
|
|
187
|
-
path.join(dir, ".harness", "progress.json"),
|
|
188
|
-
JSON.stringify({ sprint: { number: 1 } })
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
const snap = readHarnessState(dir);
|
|
192
|
-
expect(snap.archive.all.find((e) => e.dir === "sprint-1")?.result).toBe("PASS");
|
|
193
29
|
});
|
|
194
30
|
|
|
195
31
|
it("marks in-place goal document updates as submission only after the owner prompt", () => {
|
|
@@ -403,44 +239,11 @@ describe("readHarnessState", () => {
|
|
|
403
239
|
const workers = readHarnessState(dir).missions[0].workers;
|
|
404
240
|
expect(workers[0]).toMatchObject({
|
|
405
241
|
name: "frontend-worker",
|
|
406
|
-
status: "
|
|
242
|
+
status: "unknown",
|
|
407
243
|
active: false,
|
|
408
244
|
});
|
|
409
245
|
});
|
|
410
246
|
|
|
411
|
-
it("reads manual worker telemetry with name, progress, eta, and report_path", () => {
|
|
412
|
-
const harnessDir = path.join(dir, ".harness");
|
|
413
|
-
mkdirSync(harnessDir, { recursive: true });
|
|
414
|
-
writeFileSync(
|
|
415
|
-
path.join(harnessDir, "progress.json"),
|
|
416
|
-
JSON.stringify({
|
|
417
|
-
agent_status: "running",
|
|
418
|
-
company_state: {
|
|
419
|
-
active_workers: 2,
|
|
420
|
-
workers: [
|
|
421
|
-
{
|
|
422
|
-
name: "frontend-engineer-multi-strategy",
|
|
423
|
-
status: "running",
|
|
424
|
-
progress: "70%",
|
|
425
|
-
eta: "2026-05-31 17:00",
|
|
426
|
-
report_path: ".harness/documents/goal-1/cto/workers/frontend-engineer-multi-strategy.md",
|
|
427
|
-
},
|
|
428
|
-
],
|
|
429
|
-
},
|
|
430
|
-
})
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
const worker = readHarnessState(dir).dashboard.workers[0];
|
|
434
|
-
expect(worker).toMatchObject({
|
|
435
|
-
agent: "frontend-engineer-multi-strategy",
|
|
436
|
-
feature: "frontend-engineer-multi-strategy",
|
|
437
|
-
status: "running",
|
|
438
|
-
log: ".harness/documents/goal-1/cto/workers/frontend-engineer-multi-strategy.md",
|
|
439
|
-
progress: 0.7,
|
|
440
|
-
});
|
|
441
|
-
expect(worker.summary).toContain("2026-05-31 17:00");
|
|
442
|
-
});
|
|
443
|
-
|
|
444
247
|
it("accepts keyed company_state.workers maps from manual CXX telemetry", () => {
|
|
445
248
|
const harnessDir = path.join(dir, ".harness");
|
|
446
249
|
const missionDir = path.join(harnessDir, "documents", "goal-1-dashboard");
|
|
@@ -468,10 +271,6 @@ describe("readHarnessState", () => {
|
|
|
468
271
|
);
|
|
469
272
|
|
|
470
273
|
const snap = readHarnessState(dir);
|
|
471
|
-
expect(snap.dashboard.workers[0]).toMatchObject({
|
|
472
|
-
agent: "engineering-engineering-code-reviewer",
|
|
473
|
-
status: "running",
|
|
474
|
-
});
|
|
475
274
|
expect(snap.missions[0].workers[0]).toMatchObject({
|
|
476
275
|
owner: "cqo",
|
|
477
276
|
active: true,
|