@walwal-harness/cli 7.1.7 → 7.1.9
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/index.json +19 -43
- package/HR-Resource/marketing-marketing-cross-border-ecommerce/SKILL.md +108 -223
- package/HR-Resource/marketing-marketing-global-marketplace-operator/SKILL.md +175 -0
- package/HR-Resource/marketing-marketing-google-seo-specialist/SKILL.md +91 -0
- package/HR-Resource/marketing-marketing-instagram-commerce-strategist/SKILL.md +81 -0
- package/HR-Resource/marketing-marketing-korea-global-market-localization-strategist/SKILL.md +186 -0
- package/HR-Resource/marketing-marketing-livestream-commerce-coach/SKILL.md +88 -272
- package/HR-Resource/marketing-marketing-naver-seo-specialist/SKILL.md +88 -0
- package/HR-Resource/marketing-marketing-short-video-editing-coach/SKILL.md +99 -395
- package/HR-Resource/marketing-marketing-youtube-shopping-strategist/SKILL.md +82 -0
- package/README.md +41 -0
- package/apps/harness-dashboard/app/api/env/route.ts +62 -0
- package/apps/harness-dashboard/app/api/log/route.ts +19 -0
- package/apps/harness-dashboard/app/api/missions/route.ts +15 -0
- package/apps/harness-dashboard/app/api/snapshot/route.ts +15 -0
- package/apps/harness-dashboard/app/api/stream/route.ts +71 -0
- package/apps/harness-dashboard/app/globals.css +17 -0
- package/apps/harness-dashboard/app/layout.tsx +29 -0
- package/apps/harness-dashboard/app/page.tsx +35 -0
- package/apps/harness-dashboard/components/Drawer.tsx +97 -0
- package/apps/harness-dashboard/components/Header.tsx +40 -0
- package/apps/harness-dashboard/components/MissionTimeline.tsx +210 -0
- package/apps/harness-dashboard/components/OrgTree.tsx +330 -0
- package/apps/harness-dashboard/components/Scene.tsx +563 -0
- package/apps/harness-dashboard/components/Wordmark.tsx +14 -0
- package/apps/harness-dashboard/components/drawer/AgentLogTab.tsx +56 -0
- package/apps/harness-dashboard/components/drawer/GotchasTab.tsx +89 -0
- package/apps/harness-dashboard/components/drawer/HypothesisTab.tsx +42 -0
- package/apps/harness-dashboard/components/drawer/IncidentsTab.tsx +50 -0
- package/apps/harness-dashboard/components/drawer/MissionDocTab.tsx +92 -0
- package/apps/harness-dashboard/components/drawer/MissionFlowTab.tsx +517 -0
- package/apps/harness-dashboard/components/drawer/OwnerHistoryTab.tsx +156 -0
- package/apps/harness-dashboard/components/drawer/RoomMetricsTab.tsx +90 -0
- package/apps/harness-dashboard/components/drawer/TracksTab.tsx +47 -0
- package/apps/harness-dashboard/components/three/ArchiveBoxes.tsx +95 -0
- package/apps/harness-dashboard/components/three/DeptBoard.tsx +175 -0
- package/apps/harness-dashboard/components/three/Floor3D.tsx +404 -0
- package/apps/harness-dashboard/components/three/GoalCard3D.tsx +123 -0
- package/apps/harness-dashboard/components/three/HypothesisCards.tsx +71 -0
- package/apps/harness-dashboard/components/three/MeetingWhiteboard.tsx +98 -0
- package/apps/harness-dashboard/components/three/Minifig3D.tsx +391 -0
- package/apps/harness-dashboard/components/three/OpsSiren.tsx +99 -0
- package/apps/harness-dashboard/components/three/Stage3D.tsx +104 -0
- package/apps/harness-dashboard/components/three/TrackRibbons.tsx +81 -0
- package/apps/harness-dashboard/hooks/useHarnessStream.ts +53 -0
- package/apps/harness-dashboard/lib/__tests__/company-flow.test.ts +270 -0
- package/apps/harness-dashboard/lib/__tests__/harness-state.test.ts +194 -0
- package/apps/harness-dashboard/lib/__tests__/i18n.test.ts +16 -0
- package/apps/harness-dashboard/lib/__tests__/iso.test.ts +42 -0
- package/apps/harness-dashboard/lib/__tests__/safe-json.test.ts +46 -0
- package/apps/harness-dashboard/lib/__tests__/state-mapping.test.ts +159 -0
- package/apps/harness-dashboard/lib/__tests__/watcher.test.ts +48 -0
- package/apps/harness-dashboard/lib/agent-roster.ts +42 -0
- package/apps/harness-dashboard/lib/furniture.ts +165 -0
- package/apps/harness-dashboard/lib/harness-root.ts +20 -0
- package/apps/harness-dashboard/lib/harness-state.ts +1162 -0
- package/apps/harness-dashboard/lib/i18n.ts +26 -0
- package/apps/harness-dashboard/lib/iso.ts +150 -0
- package/apps/harness-dashboard/lib/log-filter.ts +23 -0
- package/apps/harness-dashboard/lib/markdown.tsx +306 -0
- package/apps/harness-dashboard/lib/path-planning.ts +72 -0
- package/apps/harness-dashboard/lib/safe-json.ts +24 -0
- package/apps/harness-dashboard/lib/state-mapping.ts +126 -0
- package/apps/harness-dashboard/lib/types.ts +329 -0
- package/apps/harness-dashboard/lib/watcher.ts +64 -0
- package/apps/harness-dashboard/next-env.d.ts +6 -0
- package/apps/harness-dashboard/next.config.mjs +9 -0
- package/apps/harness-dashboard/package-lock.json +4280 -0
- package/apps/harness-dashboard/package.json +38 -0
- package/apps/harness-dashboard/playwright.config.ts +23 -0
- package/apps/harness-dashboard/postcss.config.js +6 -0
- package/apps/harness-dashboard/tailwind.config.ts +49 -0
- package/apps/harness-dashboard/tsconfig.json +24 -0
- package/apps/harness-dashboard/vitest.config.ts +15 -0
- package/assets/templates/AGENTS-ko /341/204/207/341/205/251/341/206/250/341/204/211/341/205/241/341/204/207/341/205/251/341/206/253.md" +114 -0
- package/package.json +11 -1
- package/scripts/harness-dashboard-up.sh +24 -4
- package/HR-Resource/engineering-engineering-feishu-integration-developer/SKILL.md +0 -615
- package/HR-Resource/engineering-engineering-wechat-mini-program-developer/SKILL.md +0 -367
- package/HR-Resource/marketing-marketing-baidu-seo-specialist/SKILL.md +0 -243
- package/HR-Resource/marketing-marketing-bilibili-content-strategist/SKILL.md +0 -216
- package/HR-Resource/marketing-marketing-china-ecommerce-operator/SKILL.md +0 -300
- package/HR-Resource/marketing-marketing-china-market-localization-strategist/SKILL.md +0 -300
- package/HR-Resource/marketing-marketing-douyin-strategist/SKILL.md +0 -166
- package/HR-Resource/marketing-marketing-kuaishou-strategist/SKILL.md +0 -240
- package/HR-Resource/marketing-marketing-wechat-official-account/SKILL.md +0 -162
- package/HR-Resource/marketing-marketing-weibo-strategist/SKILL.md +0 -257
- package/HR-Resource/marketing-marketing-xiaohongshu-specialist/SKILL.md +0 -155
- package/HR-Resource/marketing-marketing-zhihu-strategist/SKILL.md +0 -179
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { AgentId, Dept, MinifigState } from "./types";
|
|
2
|
+
|
|
3
|
+
export interface ProgressSlice {
|
|
4
|
+
current_agent?: AgentId | null;
|
|
5
|
+
next_agent?: AgentId | null;
|
|
6
|
+
agent_status?: string;
|
|
7
|
+
failure?: { agent?: AgentId | null; message?: string | null } | null;
|
|
8
|
+
meetings?: { active?: AgentId[] };
|
|
9
|
+
service_ops?: {
|
|
10
|
+
incident?: { open?: Array<{ id: string; dept?: string }> };
|
|
11
|
+
monitor?: { stream_active?: boolean; stream_target?: string | null };
|
|
12
|
+
requested_mode?: string | null;
|
|
13
|
+
};
|
|
14
|
+
// v6 NEXUS parallel mode (CLAUDE.md §Parallel Tracks). When tracks have
|
|
15
|
+
// owners with status=running, those agents are concurrently working —
|
|
16
|
+
// not just current_agent. The dashboard treats every running owner as
|
|
17
|
+
// typing so the parallel reality is visible.
|
|
18
|
+
conductor?: {
|
|
19
|
+
state?: string;
|
|
20
|
+
current_action?: string | null;
|
|
21
|
+
tracks?: Array<{ owner?: string; status?: string }>;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DeriveContext {
|
|
26
|
+
// Distinct departments with at least one open incident — pre-computed by
|
|
27
|
+
// harness-state.ts so the mapping doesn't iterate the array per agent.
|
|
28
|
+
incidentDepts?: Set<string>;
|
|
29
|
+
// The agent's own department (passed in because we map by department, not
|
|
30
|
+
// by the bare AgentId).
|
|
31
|
+
dept?: Dept | "Operations";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// `conductor.current_action` carries strings like:
|
|
35
|
+
// "spawn:generator-frontend"
|
|
36
|
+
// "spawn:coo-developer:F12-006"
|
|
37
|
+
// "spawn:generator-backend (foundation F-101)"
|
|
38
|
+
// Pull out the agent id from the head.
|
|
39
|
+
export function parseSpawnAgent(action: string | null | undefined): string | null {
|
|
40
|
+
if (!action) return null;
|
|
41
|
+
const m = /^spawn:([a-z][a-z0-9-]*)/i.exec(action);
|
|
42
|
+
return m ? m[1].toLowerCase() : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isAgentRunningTrack(
|
|
46
|
+
agentId: string,
|
|
47
|
+
conductor: ProgressSlice["conductor"]
|
|
48
|
+
): boolean {
|
|
49
|
+
const tracks = conductor?.tracks ?? [];
|
|
50
|
+
return tracks.some(
|
|
51
|
+
(t) =>
|
|
52
|
+
t.owner === agentId &&
|
|
53
|
+
(t.status === "running" || t.status === "in_progress" || t.status === "active")
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isAgentQueuedTrack(
|
|
58
|
+
agentId: string,
|
|
59
|
+
conductor: ProgressSlice["conductor"]
|
|
60
|
+
): boolean {
|
|
61
|
+
const tracks = conductor?.tracks ?? [];
|
|
62
|
+
return tracks.some((t) => t.owner === agentId && t.status === "pending");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Priority: red-alert > talking > typing > queued > idle.
|
|
66
|
+
// red-alert wins because incidents/failures must dominate the visual field.
|
|
67
|
+
// talking wins over typing because meetings teleport overrides desk presence.
|
|
68
|
+
// typing now reflects v6 NEXUS *parallel* mode — multiple agents can be
|
|
69
|
+
// concurrently working, all of them get the typing aura.
|
|
70
|
+
// queued makes autonomous handoff visible: when the company already assigned
|
|
71
|
+
// a next department, that department sits at its desk even before the worker
|
|
72
|
+
// process emits its first log line.
|
|
73
|
+
export function deriveMinifigState(
|
|
74
|
+
agentId: AgentId,
|
|
75
|
+
progress: ProgressSlice | null,
|
|
76
|
+
ctx: DeriveContext = {}
|
|
77
|
+
): MinifigState {
|
|
78
|
+
if (!progress) return "idle";
|
|
79
|
+
if (progress.failure?.agent === agentId) return "red-alert";
|
|
80
|
+
if (ctx.dept && ctx.incidentDepts && ctx.incidentDepts.size > 0) {
|
|
81
|
+
if (ctx.dept === "Operations" || ctx.incidentDepts.has(ctx.dept)) {
|
|
82
|
+
return "red-alert";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const inMeeting = progress.meetings?.active?.includes(agentId) ?? false;
|
|
86
|
+
if (inMeeting) return "talking";
|
|
87
|
+
|
|
88
|
+
// Parallel-typing signals (any one of these means the agent is working).
|
|
89
|
+
if (
|
|
90
|
+
progress.current_agent === agentId &&
|
|
91
|
+
progress.agent_status === "running"
|
|
92
|
+
) {
|
|
93
|
+
return "typing";
|
|
94
|
+
}
|
|
95
|
+
if (isAgentRunningTrack(agentId, progress.conductor)) {
|
|
96
|
+
return "typing";
|
|
97
|
+
}
|
|
98
|
+
if (parseSpawnAgent(progress.conductor?.current_action) === agentId) {
|
|
99
|
+
return "typing";
|
|
100
|
+
}
|
|
101
|
+
// G-006: service-ops co-spawned in monitor mode is actively streaming.
|
|
102
|
+
if (
|
|
103
|
+
agentId === "service-ops" &&
|
|
104
|
+
progress.service_ops?.monitor?.stream_active === true
|
|
105
|
+
) {
|
|
106
|
+
return "typing";
|
|
107
|
+
}
|
|
108
|
+
if (
|
|
109
|
+
progress.next_agent === agentId &&
|
|
110
|
+
progress.agent_status !== "blocked" &&
|
|
111
|
+
progress.agent_status !== "failed"
|
|
112
|
+
) {
|
|
113
|
+
return "queued";
|
|
114
|
+
}
|
|
115
|
+
if (isAgentQueuedTrack(agentId, progress.conductor)) {
|
|
116
|
+
return "queued";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return "idle";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function pickTalkingPreview(agentId: AgentId): string {
|
|
123
|
+
// Sprint 2 will pull this from progress.log; for Sprint 1 we surface a stub
|
|
124
|
+
// so the speech-bubble component has a sensible default.
|
|
125
|
+
return `${agentId}…`;
|
|
126
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
export type AgentId =
|
|
2
|
+
| "dispatcher"
|
|
3
|
+
| "brainstormer"
|
|
4
|
+
| "conductor"
|
|
5
|
+
| "meeting-manager"
|
|
6
|
+
| "planner"
|
|
7
|
+
| "coo-developer"
|
|
8
|
+
| "documentationer"
|
|
9
|
+
| "cto"
|
|
10
|
+
| "generator-backend"
|
|
11
|
+
| "generator-frontend"
|
|
12
|
+
| "generator-designer"
|
|
13
|
+
| "generator-devops"
|
|
14
|
+
| "cqo"
|
|
15
|
+
| "evaluator-functional"
|
|
16
|
+
| "evaluator-visual"
|
|
17
|
+
| "evaluator-code-quality"
|
|
18
|
+
| "evaluator-architecture"
|
|
19
|
+
| "evaluator-security"
|
|
20
|
+
| "service-ops";
|
|
21
|
+
|
|
22
|
+
export type RoomId =
|
|
23
|
+
| "ceo"
|
|
24
|
+
| "meeting"
|
|
25
|
+
| "coo"
|
|
26
|
+
| "cto-team"
|
|
27
|
+
| "cqo-team"
|
|
28
|
+
| "service-ops"
|
|
29
|
+
| "archive";
|
|
30
|
+
|
|
31
|
+
export type Dept = "CEO" | "Meeting" | "Planner" | "CTO" | "CQO" | "Operations";
|
|
32
|
+
|
|
33
|
+
export type MinifigState = "idle" | "queued" | "typing" | "talking" | "red-alert";
|
|
34
|
+
|
|
35
|
+
export interface AgentState {
|
|
36
|
+
id: AgentId;
|
|
37
|
+
name: string;
|
|
38
|
+
dept: Dept;
|
|
39
|
+
/** The agent's *current* room (after meeting teleport). */
|
|
40
|
+
room: RoomId;
|
|
41
|
+
/** The agent's home room — never changes. */
|
|
42
|
+
homeRoom: RoomId;
|
|
43
|
+
minifigState: MinifigState;
|
|
44
|
+
lastActivity: string | null;
|
|
45
|
+
talkingPreview?: string;
|
|
46
|
+
alertReason?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface RoomMetrics {
|
|
50
|
+
sprint_number?: number;
|
|
51
|
+
last_review?: string | null;
|
|
52
|
+
last_audit?: string | null;
|
|
53
|
+
last_check?: string | null;
|
|
54
|
+
sprint_verdict?: "pending" | "PASS" | "FAIL";
|
|
55
|
+
open_alerts?: number;
|
|
56
|
+
open_arch_risks?: number;
|
|
57
|
+
open_regressions?: number;
|
|
58
|
+
cadence?: MeetingCadence;
|
|
59
|
+
next_scheduled?: string | null;
|
|
60
|
+
active_tracks?: number;
|
|
61
|
+
active_hypothesis?: number;
|
|
62
|
+
active_workers?: number;
|
|
63
|
+
open_incidents?: number;
|
|
64
|
+
pass_rate?: number | null;
|
|
65
|
+
contract_signed?: { be?: boolean; fe?: boolean };
|
|
66
|
+
eval_scores?: EvalScores | null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface SeatLayout {
|
|
70
|
+
seats: number;
|
|
71
|
+
occupants: AgentId[];
|
|
72
|
+
overflow: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface RoomState {
|
|
76
|
+
id: RoomId;
|
|
77
|
+
label_ko: string;
|
|
78
|
+
label_en: string;
|
|
79
|
+
dept: Dept | "Archive";
|
|
80
|
+
metrics?: RoomMetrics;
|
|
81
|
+
seatLayout?: SeatLayout;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface GoalCard {
|
|
85
|
+
id: string;
|
|
86
|
+
title: string;
|
|
87
|
+
description_truncated?: string;
|
|
88
|
+
adherence: number | null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface ArchiveEntry {
|
|
92
|
+
dir: string;
|
|
93
|
+
label: string;
|
|
94
|
+
result: "PASS" | "FAIL" | "unknown";
|
|
95
|
+
ts: string | null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface ArchiveStat {
|
|
99
|
+
sprintCount: number;
|
|
100
|
+
recent: ArchiveEntry[];
|
|
101
|
+
all: ArchiveEntry[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type MeetingCadence = "light" | "normal" | "heavy";
|
|
105
|
+
export type MeetingType =
|
|
106
|
+
| "standup"
|
|
107
|
+
| "sprint-review"
|
|
108
|
+
| "spec-review"
|
|
109
|
+
| "incident-war-room"
|
|
110
|
+
| "all-hands"
|
|
111
|
+
| "followup-review";
|
|
112
|
+
|
|
113
|
+
export interface CurrentMeeting {
|
|
114
|
+
type: MeetingType;
|
|
115
|
+
topic?: string;
|
|
116
|
+
convened_at?: string | null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface MeetingsState {
|
|
120
|
+
active: AgentId[];
|
|
121
|
+
cadence: MeetingCadence;
|
|
122
|
+
next_scheduled: string | null;
|
|
123
|
+
current: CurrentMeeting | null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type TrackStatus = "dispatched" | "in_progress" | "joined" | "blocked";
|
|
127
|
+
|
|
128
|
+
export interface ParallelTrack {
|
|
129
|
+
id: string;
|
|
130
|
+
from_meeting: string;
|
|
131
|
+
to_dept: Dept | "Operations" | "Multi";
|
|
132
|
+
to_room: RoomId;
|
|
133
|
+
status: TrackStatus;
|
|
134
|
+
label?: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type IncidentSeverity = "low" | "medium" | "high" | "critical";
|
|
138
|
+
|
|
139
|
+
export interface IncidentEntry {
|
|
140
|
+
id: string;
|
|
141
|
+
dept: Dept | "Operations" | "Multi" | string;
|
|
142
|
+
severity: IncidentSeverity;
|
|
143
|
+
message?: string;
|
|
144
|
+
ts?: string | null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export type HypothesisVerdict = "pending" | "valid" | "invalid";
|
|
148
|
+
|
|
149
|
+
export interface HypothesisEntry {
|
|
150
|
+
id: string;
|
|
151
|
+
brief: string;
|
|
152
|
+
verdict: HypothesisVerdict;
|
|
153
|
+
ts?: string | null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export type EscalationReason =
|
|
157
|
+
| "three-fail"
|
|
158
|
+
| "incident"
|
|
159
|
+
| "goal-violation"
|
|
160
|
+
| "budget"
|
|
161
|
+
| "other";
|
|
162
|
+
|
|
163
|
+
export interface EscalationEntry {
|
|
164
|
+
id: string;
|
|
165
|
+
reason: EscalationReason | string;
|
|
166
|
+
message?: string;
|
|
167
|
+
ts?: string | null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export type Pipeline = "FULLSTACK" | "FE-ONLY" | "BE-ONLY" | "META_REFACTOR" | null;
|
|
171
|
+
|
|
172
|
+
export interface ContractSnapshot {
|
|
173
|
+
sprint_number: number | null;
|
|
174
|
+
pipeline: Pipeline;
|
|
175
|
+
api_version: string | null;
|
|
176
|
+
feature_total: number;
|
|
177
|
+
feature_passed: number;
|
|
178
|
+
feature_failed: number;
|
|
179
|
+
contract_signed: { be: boolean; fe: boolean };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface FeatureSummary {
|
|
183
|
+
id: string;
|
|
184
|
+
title: string;
|
|
185
|
+
status: "ready" | "blocked" | "in_progress" | "passed" | "failed" | "unknown";
|
|
186
|
+
passes: string[];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface EvalScores {
|
|
190
|
+
functional?: number | null;
|
|
191
|
+
visual?: number | null;
|
|
192
|
+
code_quality?: number | null;
|
|
193
|
+
architecture?: number | null;
|
|
194
|
+
security?: number | null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface ErrorBanner {
|
|
198
|
+
level: "info" | "warn" | "error";
|
|
199
|
+
message_ko: string;
|
|
200
|
+
message_en: string;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface WorkerSnapshot {
|
|
204
|
+
team: number | string;
|
|
205
|
+
feature: string;
|
|
206
|
+
title?: string;
|
|
207
|
+
agent: AgentId | string;
|
|
208
|
+
phase?: string;
|
|
209
|
+
status: "spawned" | "recorded" | "running" | "idle" | "blocked" | "unknown";
|
|
210
|
+
pid?: number | null;
|
|
211
|
+
prompt?: string | null;
|
|
212
|
+
log?: string | null;
|
|
213
|
+
progress: number | null;
|
|
214
|
+
summary: string;
|
|
215
|
+
next_material?: string | null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface MeetingRecord {
|
|
219
|
+
id: string;
|
|
220
|
+
path: string;
|
|
221
|
+
ts: string | null;
|
|
222
|
+
title: string;
|
|
223
|
+
verdict?: string | null;
|
|
224
|
+
summary: string;
|
|
225
|
+
content: string;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface OpsServiceHealth {
|
|
229
|
+
name: string;
|
|
230
|
+
host: string;
|
|
231
|
+
port: number;
|
|
232
|
+
status: "ok" | "degraded" | "down" | "unknown";
|
|
233
|
+
port_state?: string | null;
|
|
234
|
+
health_status?: number | null;
|
|
235
|
+
health_path?: string | null;
|
|
236
|
+
recent_errors?: number | null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface EnvKeySummary {
|
|
240
|
+
key: string;
|
|
241
|
+
category: "secret" | "endpoint" | "port" | "mode" | "other";
|
|
242
|
+
masked: string;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export interface EnvFileSummary {
|
|
246
|
+
path: string;
|
|
247
|
+
updated_at: string | null;
|
|
248
|
+
key_count: number;
|
|
249
|
+
keys: EnvKeySummary[];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export interface OperationsDashboard {
|
|
253
|
+
workers: WorkerSnapshot[];
|
|
254
|
+
features: FeatureSummary[];
|
|
255
|
+
recentMeetings: MeetingRecord[];
|
|
256
|
+
opsHealth: OpsServiceHealth[];
|
|
257
|
+
envFiles: EnvFileSummary[];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export interface HarnessSnapshot {
|
|
261
|
+
version: string;
|
|
262
|
+
ts: string;
|
|
263
|
+
projectName: string;
|
|
264
|
+
projectPath: string;
|
|
265
|
+
agents: AgentState[];
|
|
266
|
+
rooms: RoomState[];
|
|
267
|
+
goal: GoalCard | null;
|
|
268
|
+
archive: ArchiveStat;
|
|
269
|
+
meetings: MeetingsState;
|
|
270
|
+
tracks: ParallelTrack[];
|
|
271
|
+
incidents: IncidentEntry[];
|
|
272
|
+
hypothesis: HypothesisEntry[];
|
|
273
|
+
escalations: EscalationEntry[];
|
|
274
|
+
contract: ContractSnapshot;
|
|
275
|
+
evalScores: EvalScores | null;
|
|
276
|
+
errorBanner: ErrorBanner | null;
|
|
277
|
+
dashboard: OperationsDashboard;
|
|
278
|
+
missions: MissionDoc[];
|
|
279
|
+
ownerHistory: OwnerPromptEntry[];
|
|
280
|
+
gotchas: GotchaEntry[];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Real harness mission documents
|
|
284
|
+
export interface WorkerDocEntry {
|
|
285
|
+
name: string;
|
|
286
|
+
content: string;
|
|
287
|
+
status: "COMPLETE" | "IN_PROGRESS" | "unknown";
|
|
288
|
+
owner: "cto" | "cqo" | "coo" | "cdo" | "ops" | "unknown";
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export interface MissionDoc {
|
|
292
|
+
missionId: string;
|
|
293
|
+
ts: string;
|
|
294
|
+
type: "hotfix" | "feature" | "unknown";
|
|
295
|
+
label: string;
|
|
296
|
+
ceo: string | null;
|
|
297
|
+
cto: string | null;
|
|
298
|
+
cqo: string | null;
|
|
299
|
+
coo: string | null;
|
|
300
|
+
cdo: string | null;
|
|
301
|
+
ops: string | null;
|
|
302
|
+
workers: WorkerDocEntry[];
|
|
303
|
+
cxxPresent: string[];
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export interface OwnerPromptEntry {
|
|
307
|
+
ts: string;
|
|
308
|
+
content: string;
|
|
309
|
+
type: "goal" | "hot-fix" | "other";
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export interface GotchaEntry {
|
|
313
|
+
id: string;
|
|
314
|
+
title: string;
|
|
315
|
+
content: string;
|
|
316
|
+
tags: string[];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export type OrgRole = "owner" | "ceo" | "coo" | "cdo" | "cto" | "cqo" | "ops";
|
|
320
|
+
|
|
321
|
+
export interface OrgNodeDef {
|
|
322
|
+
id: string;
|
|
323
|
+
role: OrgRole;
|
|
324
|
+
label: string;
|
|
325
|
+
sublabel?: string;
|
|
326
|
+
status: MinifigState;
|
|
327
|
+
activity: string | null;
|
|
328
|
+
agentIds: AgentId[];
|
|
329
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import chokidar from "chokidar";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export interface WatchHandle {
|
|
5
|
+
close(): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface WatchOptions {
|
|
9
|
+
/** Coalesce rapid-fire events within this window (ms). */
|
|
10
|
+
debounceMs?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Watches the .harness/ directory for changes that affect the dashboard
|
|
14
|
+
// snapshot — progress.json (state), progress.log (activity), actions/* (sprint
|
|
15
|
+
// artifacts), archive/* (sprint history).
|
|
16
|
+
export function watchHarness(
|
|
17
|
+
rootDir: string,
|
|
18
|
+
onChange: (changedPath: string) => void,
|
|
19
|
+
opts: WatchOptions = {}
|
|
20
|
+
): WatchHandle {
|
|
21
|
+
const debounceMs = opts.debounceMs ?? 50;
|
|
22
|
+
const harnessDir = path.join(rootDir, ".harness");
|
|
23
|
+
const watcher = chokidar.watch(
|
|
24
|
+
[
|
|
25
|
+
path.join(harnessDir, "progress.json"),
|
|
26
|
+
path.join(harnessDir, "progress.log"),
|
|
27
|
+
path.join(harnessDir, "actions"),
|
|
28
|
+
path.join(harnessDir, "archive"),
|
|
29
|
+
],
|
|
30
|
+
{
|
|
31
|
+
ignoreInitial: true,
|
|
32
|
+
awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 },
|
|
33
|
+
// Vitest creates and deletes temp harness dirs quickly; polling avoids
|
|
34
|
+
// macOS FSEvents EMFILE noise after close().
|
|
35
|
+
usePolling: process.env.VITEST === "true",
|
|
36
|
+
// Don't crawl deeply into archive — recent dirs only.
|
|
37
|
+
depth: 4,
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
let pending: NodeJS.Timeout | null = null;
|
|
42
|
+
let lastPath = "";
|
|
43
|
+
|
|
44
|
+
const fire = (changedPath: string) => {
|
|
45
|
+
lastPath = changedPath;
|
|
46
|
+
if (pending) return;
|
|
47
|
+
pending = setTimeout(() => {
|
|
48
|
+
pending = null;
|
|
49
|
+
onChange(lastPath);
|
|
50
|
+
}, debounceMs);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
watcher.on("add", fire).on("change", fire).on("unlink", fire);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
async close() {
|
|
57
|
+
if (pending) {
|
|
58
|
+
clearTimeout(pending);
|
|
59
|
+
pending = null;
|
|
60
|
+
}
|
|
61
|
+
await watcher.close();
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="next" />
|
|
2
|
+
/// <reference types="next/image-types/global" />
|
|
3
|
+
/// <reference path="./.next/types/routes.d.ts" />
|
|
4
|
+
|
|
5
|
+
// NOTE: This file should not be edited
|
|
6
|
+
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|