@walwal-harness/cli 7.1.39 → 7.1.41

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.
Files changed (74) hide show
  1. package/HR-Resource/cdo/SKILL.md +9 -1
  2. package/HR-Resource/ceo/SKILL.md +33 -2
  3. package/HR-Resource/coo/SKILL.md +9 -1
  4. package/HR-Resource/cqo/SKILL.md +9 -1
  5. package/HR-Resource/cto/SKILL.md +10 -2
  6. package/HR-Resource/ops/SKILL.md +20 -2
  7. package/HR-Resource/ops-prompt-inspector/SKILL.md +105 -0
  8. package/HR-Resource/ops-prompt-inspector/agents/installation-agent.json +7 -0
  9. package/HR-Resource/ops-prompt-inspector/assets/PromptInspector.tsx +599 -0
  10. package/HR-Resource/ops-prompt-inspector/assets/prompt-inspector-static.js +180 -0
  11. package/HR-Resource/ops-prompt-inspector/references/handler-templates.md +255 -0
  12. package/HR-Resource/ops-prompt-inspector/scripts/discover_apis.py +509 -0
  13. package/HR-Resource/ops-prompt-inspector/scripts/install.sh +32 -0
  14. package/HR-Resource/ops-prompt-inspector/scripts/setup.py +381 -0
  15. package/apps/harness-dashboard/app/api/wake/route.ts +41 -0
  16. package/apps/harness-dashboard/components/Scene.tsx +568 -35
  17. package/apps/harness-dashboard/lib/__tests__/harness-state.test.ts +1 -202
  18. package/apps/harness-dashboard/lib/__tests__/sandbox-e2e.test.ts +490 -0
  19. package/apps/harness-dashboard/lib/harness-state.ts +107 -796
  20. package/apps/harness-dashboard/lib/types.ts +22 -222
  21. package/apps/harness-dashboard/playwright.config.ts +26 -7
  22. package/assets/templates/AGENTS-ko.md.template +1 -1
  23. package/assets/templates/AGENTS.md.template +12 -2
  24. package/assets/templates/HARNESS.md +2 -0
  25. package/assets/templates/company-pipeline-manifest.json +5 -14
  26. package/assets/templates/worker-report.md.template +38 -0
  27. package/bin/init.js +4 -3
  28. package/commands/goal.md +4 -2
  29. package/commands/hot-fix.md +1 -1
  30. package/commands/submission.md +1 -1
  31. package/package.json +1 -1
  32. package/scripts/conductor-tick.sh +12 -0
  33. package/scripts/harness-agenda.sh +105 -0
  34. package/scripts/harness-company-block.sh +89 -0
  35. package/scripts/harness-company-complete.sh +17 -8
  36. package/scripts/harness-company-cycle.sh +54 -0
  37. package/scripts/harness-progress-set.sh +8 -2
  38. package/scripts/harness-stop.sh +76 -0
  39. package/scripts/harness-wake-control.sh +151 -0
  40. package/scripts/harness-wake.sh +5 -0
  41. package/apps/harness-dashboard/components/Drawer.tsx +0 -146
  42. package/apps/harness-dashboard/components/Header.tsx +0 -40
  43. package/apps/harness-dashboard/components/MissionTimeline.tsx +0 -214
  44. package/apps/harness-dashboard/components/OrgTree.tsx +0 -459
  45. package/apps/harness-dashboard/components/drawer/AgentLogTab.tsx +0 -56
  46. package/apps/harness-dashboard/components/drawer/DesignPreviewTab.tsx +0 -160
  47. package/apps/harness-dashboard/components/drawer/GotchasTab.tsx +0 -89
  48. package/apps/harness-dashboard/components/drawer/HypothesisTab.tsx +0 -42
  49. package/apps/harness-dashboard/components/drawer/IncidentsTab.tsx +0 -50
  50. package/apps/harness-dashboard/components/drawer/MissionDocTab.tsx +0 -96
  51. package/apps/harness-dashboard/components/drawer/MissionFlowTab.tsx +0 -576
  52. package/apps/harness-dashboard/components/drawer/OwnerHistoryTab.tsx +0 -160
  53. package/apps/harness-dashboard/components/drawer/RoomMetricsTab.tsx +0 -90
  54. package/apps/harness-dashboard/components/drawer/TracksTab.tsx +0 -47
  55. package/apps/harness-dashboard/components/three/ArchiveBoxes.tsx +0 -95
  56. package/apps/harness-dashboard/components/three/DeptBoard.tsx +0 -175
  57. package/apps/harness-dashboard/components/three/Floor3D.tsx +0 -404
  58. package/apps/harness-dashboard/components/three/GoalCard3D.tsx +0 -123
  59. package/apps/harness-dashboard/components/three/HypothesisCards.tsx +0 -71
  60. package/apps/harness-dashboard/components/three/MeetingWhiteboard.tsx +0 -98
  61. package/apps/harness-dashboard/components/three/Minifig3D.tsx +0 -391
  62. package/apps/harness-dashboard/components/three/OpsSiren.tsx +0 -99
  63. package/apps/harness-dashboard/components/three/Stage3D.tsx +0 -104
  64. package/apps/harness-dashboard/components/three/TrackRibbons.tsx +0 -81
  65. package/apps/harness-dashboard/lib/__tests__/i18n.test.ts +0 -16
  66. package/apps/harness-dashboard/lib/__tests__/iso.test.ts +0 -42
  67. package/apps/harness-dashboard/lib/__tests__/state-mapping.test.ts +0 -183
  68. package/apps/harness-dashboard/lib/agent-roster.ts +0 -42
  69. package/apps/harness-dashboard/lib/furniture.ts +0 -165
  70. package/apps/harness-dashboard/lib/i18n.ts +0 -26
  71. package/apps/harness-dashboard/lib/iso.ts +0 -150
  72. package/apps/harness-dashboard/lib/markdown.tsx +0 -306
  73. package/apps/harness-dashboard/lib/path-planning.ts +0 -72
  74. package/apps/harness-dashboard/lib/state-mapping.ts +0 -146
@@ -1,46 +1,26 @@
1
1
  import { existsSync, readdirSync, statSync, readFileSync, type Dirent } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { readJsonSafe } from "./safe-json";
4
- import { AGENT_ROSTER, ROOM_LABELS } from "./agent-roster";
5
- import { deriveMinifigState, pickTalkingPreview } from "./state-mapping";
6
4
  import type {
7
5
  AgentId,
8
- AgentState,
9
- ArchiveEntry,
10
- ArchiveStat,
11
- ContractSnapshot,
12
- CurrentMeeting,
13
- Dept,
14
6
  ErrorBanner,
15
- EnvFileSummary,
16
- EscalationEntry,
17
7
  EvalScores,
18
- FeatureSummary,
19
- GoalCard,
20
8
  HarnessSnapshot,
21
9
  HarnessEvent,
22
- HypothesisEntry,
23
10
  IncidentEntry,
24
11
  MeetingCadence,
25
- MeetingRecord,
26
- MeetingsState,
27
12
  GotchaEntry,
28
13
  ConventionEntry,
29
14
  MissionDoc,
30
- OperationsDashboard,
31
- OpsServiceHealth,
32
15
  OwnerPromptEntry,
33
- ParallelTrack,
34
16
  Pipeline,
35
17
  RoomId,
36
- RoomMetrics,
37
- RoomState,
38
18
  RuntimeSnapshot,
39
19
  TrackStatus,
40
20
  WorkerDocEntry,
41
- WorkerSnapshot,
42
21
  CxxTodo,
43
22
  ActivitySample,
23
+ HarnessFileEntry,
44
24
  } from "./types";
45
25
 
46
26
  const SNAPSHOT_VERSION = "1.2.0";
@@ -122,6 +102,10 @@ interface RawProgress {
122
102
  // Real-harness conductor track shape (CLAUDE.md §parallel-tracks). Used when
123
103
  // top-level `parallel_tracks` is absent.
124
104
  conductor?: {
105
+ state?: string | null;
106
+ current_action?: string | null;
107
+ stop_chain_count?: number | null;
108
+ last_stop_chain_at?: string | null;
125
109
  tracks?: Array<{
126
110
  id: string;
127
111
  owner?: string;
@@ -145,6 +129,7 @@ interface RawProgress {
145
129
  feature_list?: { total?: number; passed?: number; failed?: number };
146
130
  };
147
131
  company_state?: {
132
+ state?: string | null;
148
133
  active_workers?: number;
149
134
  workers?: Array<{
150
135
  name?: string;
@@ -193,52 +178,13 @@ interface RawProgress {
193
178
  };
194
179
  }
195
180
 
196
- function emptyContract(): ContractSnapshot {
197
- return {
198
- sprint_number: null,
199
- pipeline: null,
200
- api_version: null,
201
- feature_total: 0,
202
- feature_passed: 0,
203
- feature_failed: 0,
204
- contract_signed: { be: false, fe: false },
205
- };
206
- }
207
-
208
181
  function emptySnapshot(banner: ErrorBanner | null = null, rootDir?: string): HarnessSnapshot {
209
- const agents: AgentState[] = AGENT_ROSTER.map((entry) => ({
210
- id: entry.id,
211
- name: entry.name,
212
- dept: entry.dept,
213
- room: entry.room,
214
- homeRoom: entry.room,
215
- minifigState: "idle",
216
- lastActivity: null,
217
- }));
218
- const rooms: RoomState[] = (Object.keys(ROOM_LABELS) as RoomId[]).map((id) => ({
219
- id,
220
- label_ko: ROOM_LABELS[id].ko,
221
- label_en: ROOM_LABELS[id].en,
222
- dept: ROOM_LABELS[id].dept,
223
- }));
224
182
  return {
225
183
  version: SNAPSHOT_VERSION,
226
184
  ts: new Date().toISOString(),
227
185
  projectName: rootDir ? path.basename(rootDir) : "(unknown)",
228
186
  projectPath: rootDir ?? "",
229
- agents,
230
- rooms,
231
- goal: null,
232
- archive: { sprintCount: 0, recent: [], all: [] },
233
- meetings: { active: [], cadence: "normal", next_scheduled: null, current: null },
234
- tracks: [],
235
- incidents: [],
236
- hypothesis: [],
237
- escalations: [],
238
- contract: emptyContract(),
239
- evalScores: null,
240
187
  errorBanner: banner,
241
- dashboard: { workers: [], features: [], recentMeetings: [], opsHealth: [], envFiles: [] },
242
188
  runtime: {
243
189
  currentAgent: null,
244
190
  agentStatus: "unknown",
@@ -246,6 +192,7 @@ function emptySnapshot(banner: ErrorBanner | null = null, rootDir?: string): Har
246
192
  updatedAt: null,
247
193
  ownerPrompt: null,
248
194
  },
195
+ incidents: [],
249
196
  missions: [],
250
197
  ownerHistory: [],
251
198
  gotchas: [],
@@ -253,6 +200,7 @@ function emptySnapshot(banner: ErrorBanner | null = null, rootDir?: string): Har
253
200
  todos: [],
254
201
  events: [],
255
202
  activitySamples: [],
203
+ files: [],
256
204
  };
257
205
  }
258
206
 
@@ -278,279 +226,6 @@ function truncateText(text: string, max = 150): string {
278
226
  return `${cleaned.slice(0, max - 1)}…`;
279
227
  }
280
228
 
281
- function titleFromMarkdown(text: string | null): string | null {
282
- if (!text) return null;
283
- const h1 = text.match(/^#\s+(.+)$/m)?.[1]?.trim();
284
- if (h1) return h1;
285
- const mission = text.match(/(?:mission|goal|title)\s*[:=]\s*(.+)$/im)?.[1]?.trim();
286
- return mission || null;
287
- }
288
-
289
- function buildGoal(rootDir: string, progress: RawProgress | null): GoalCard | null {
290
- const list = progress?.goals?.list ?? [];
291
- const activeId = progress?.goals?.active_id;
292
- const goalFile = readTextSafe(path.join(rootDir, ".harness", "actions", "goals.md"));
293
- const mission = (progress?.goals as { mission?: string; title?: string; description?: string } | undefined)?.mission;
294
- const topLevelTitle = (progress?.goals as { title?: string } | undefined)?.title;
295
- if (!activeId && !mission && !topLevelTitle && !goalFile) return null;
296
- if (!activeId) {
297
- const title = mission ?? topLevelTitle ?? titleFromMarkdown(goalFile) ?? "Active autonomous company goal";
298
- return {
299
- id: "goal",
300
- title,
301
- description_truncated: truncateText(
302
- (progress?.goals as { description?: string } | undefined)?.description ?? goalFile ?? "",
303
- GOAL_DESC_TRUNCATE,
304
- ),
305
- adherence: progress?.goals?.current_adherence ?? null,
306
- };
307
- }
308
- const found = list.find((g) => g.id === activeId);
309
- if (!found) {
310
- const title = mission ?? topLevelTitle ?? titleFromMarkdown(goalFile) ?? activeId;
311
- return {
312
- id: activeId,
313
- title,
314
- description_truncated: truncateText(
315
- (progress?.goals as { description?: string } | undefined)?.description ?? goalFile ?? "",
316
- GOAL_DESC_TRUNCATE,
317
- ),
318
- adherence: progress?.goals?.current_adherence ?? null,
319
- };
320
- }
321
- const desc = found.description ?? "";
322
- const truncated =
323
- desc.length > GOAL_DESC_TRUNCATE
324
- ? desc.slice(0, GOAL_DESC_TRUNCATE) + "…"
325
- : desc;
326
- return {
327
- id: found.id,
328
- title: found.title ?? found.id,
329
- description_truncated: truncated,
330
- adherence: progress?.goals?.current_adherence ?? null,
331
- };
332
- }
333
-
334
- function readArchiveVerdict(sprintDir: string): "PASS" | "FAIL" | "unknown" {
335
- // Sprint verdict can live in a handful of well-known spots. Try them in
336
- // order; first hit wins. Returns "unknown" if nothing definitive is found.
337
- const verdictJson = path.join(sprintDir, "verdict.json");
338
- if (existsSync(verdictJson)) {
339
- const r = readJsonSafe<{ result?: string; verdict?: string }>(verdictJson);
340
- if (r.ok) {
341
- const v = (r.value.result ?? r.value.verdict ?? "").toUpperCase();
342
- if (v === "PASS" || v === "FAIL") return v;
343
- }
344
- }
345
- // CQO sprint summary is the canonical source if verdict.json is absent.
346
- try {
347
- const candidates = readdirSync(sprintDir).filter(
348
- (n) => n.startsWith("cqo-audit-") || n === "evaluation-summary.md"
349
- );
350
- for (const name of candidates) {
351
- const text = readFileSync(path.join(sprintDir, name), "utf-8");
352
- if (/sprint[_\s-]*verdict\s*[:=]\s*PASS/i.test(text)) return "PASS";
353
- if (/sprint[_\s-]*verdict\s*[:=]\s*FAIL/i.test(text)) return "FAIL";
354
- }
355
- } catch {
356
- // ignore
357
- }
358
- return "unknown";
359
- }
360
-
361
- function buildArchive(harnessRoot: string): ArchiveStat {
362
- const archiveDir = path.join(harnessRoot, ".harness", "archive");
363
- if (!existsSync(archiveDir)) {
364
- return { sprintCount: 0, recent: [], all: [] };
365
- }
366
- let entries: string[] = [];
367
- try {
368
- entries = readdirSync(archiveDir).filter((name) => name.startsWith("sprint-"));
369
- } catch {
370
- return { sprintCount: 0, recent: [], all: [] };
371
- }
372
- const all: ArchiveEntry[] = entries.map((name) => {
373
- const sprintDir = path.join(archiveDir, name);
374
- let ts: string | null = null;
375
- try {
376
- ts = statSync(sprintDir).mtime.toISOString();
377
- } catch {
378
- ts = null;
379
- }
380
- const result = readArchiveVerdict(sprintDir);
381
- return { dir: name, label: name, result, ts };
382
- });
383
- all.sort((a, b) => (b.ts ?? "").localeCompare(a.ts ?? ""));
384
- return { sprintCount: all.length, recent: all.slice(0, 3), all };
385
- }
386
-
387
- function passAxis(pass: unknown): string | null {
388
- if (typeof pass === "string") return pass;
389
- if (pass && typeof pass === "object") {
390
- const p = pass as { axis?: string; by?: string; status?: string };
391
- if (p.status && p.status.toUpperCase() !== "PASS") return null;
392
- return p.axis ?? p.by ?? null;
393
- }
394
- return null;
395
- }
396
-
397
- function buildFeatureSummaries(rootDir: string): FeatureSummary[] {
398
- const featurePath = path.join(rootDir, ".harness", "actions", "feature-list.json");
399
- const queuePath = path.join(rootDir, ".harness", "actions", "feature-queue.json");
400
- const featureResult = readJsonSafe<{
401
- features?: Array<{
402
- id?: string;
403
- title?: string;
404
- name?: string;
405
- description?: string;
406
- status?: string;
407
- passes?: unknown[];
408
- }>;
409
- }>(featurePath);
410
- const queueResult = readJsonSafe<{
411
- queue?: {
412
- ready?: string[];
413
- blocked?: Record<string, unknown>;
414
- in_progress?: Record<string, unknown>;
415
- passed?: string[];
416
- failed?: string[];
417
- };
418
- }>(queuePath);
419
- const queue = queueResult.ok ? queueResult.value.queue : undefined;
420
- const statusById = new Map<string, FeatureSummary["status"]>();
421
- for (const id of queue?.ready ?? []) statusById.set(id, "ready");
422
- for (const id of Object.keys(queue?.blocked ?? {})) statusById.set(id, "blocked");
423
- for (const id of Object.keys(queue?.in_progress ?? {})) statusById.set(id, "in_progress");
424
- for (const id of queue?.passed ?? []) statusById.set(id, "passed");
425
- for (const id of queue?.failed ?? []) statusById.set(id, "failed");
426
-
427
- if (!featureResult.ok) {
428
- return [...statusById.entries()].map(([id, status]) => ({
429
- id,
430
- title: id,
431
- status,
432
- passes: [],
433
- }));
434
- }
435
- return (featureResult.value.features ?? [])
436
- .filter((f) => !!f.id)
437
- .map((f) => {
438
- const passes = (f.passes ?? []).map(passAxis).filter((x): x is string => !!x);
439
- const explicitStatus = (f.status ?? "").toLowerCase();
440
- const status =
441
- explicitStatus === "pass" || explicitStatus === "passed" || explicitStatus === "done"
442
- ? "passed"
443
- : explicitStatus === "fail" || explicitStatus === "failed"
444
- ? "failed"
445
- : statusById.get(f.id!) ?? (passes.length > 0 ? "passed" : "unknown");
446
- return {
447
- id: f.id!,
448
- title: f.title ?? f.name ?? f.description ?? f.id!,
449
- status,
450
- passes,
451
- };
452
- });
453
- }
454
-
455
- function buildContract(rootDir: string, progress: RawProgress | null, features: FeatureSummary[]): ContractSnapshot {
456
- const total = progress?.contracts?.feature_list?.total ?? features.length;
457
- const passed =
458
- progress?.contracts?.feature_list?.passed ??
459
- features.filter((f) => f.status === "passed" || f.passes.length > 0).length;
460
- const failed =
461
- progress?.contracts?.feature_list?.failed ??
462
- features.filter((f) => f.status === "failed").length;
463
- return {
464
- sprint_number: progress?.sprint?.number ?? null,
465
- pipeline: (progress?.pipeline ?? null) as Pipeline,
466
- api_version: progress?.contracts?.api?.version ?? null,
467
- feature_total: total,
468
- feature_passed: passed,
469
- feature_failed: failed,
470
- contract_signed: {
471
- be: !!progress?.cto?.contract_signed?.be,
472
- fe: !!progress?.cto?.contract_signed?.fe,
473
- },
474
- };
475
- }
476
-
477
- // Map a conductor-track owner (real-harness schema) to a dashboard room.
478
- // Owners are agent IDs / dept names; we collapse them to the room their
479
- // minifig sits in.
480
- function ownerToRoom(owner: string | undefined): RoomId {
481
- if (!owner) return "cto-team";
482
- const o = owner.toLowerCase();
483
- if (o === "planner" || o === "coo" || o.includes("documentation")) return "coo";
484
- if (o === "cto" || o.startsWith("generator-") || o === "conductor") return "cto-team";
485
- if (o === "cqo" || o.startsWith("evaluator-")) return "cqo-team";
486
- if (o === "service-ops" || o === "ops") return "service-ops";
487
- if (o === "meeting-manager") return "meeting";
488
- if (o === "dispatcher" || o === "ceo") return "ceo";
489
- return "cto-team";
490
- }
491
-
492
- function ownerToDept(owner: string | undefined): ParallelTrack["to_dept"] {
493
- if (!owner) return "Multi";
494
- const o = owner.toLowerCase();
495
- if (o === "planner" || o === "coo") return "Planner";
496
- if (o === "cto" || o.startsWith("generator-")) return "CTO";
497
- if (o === "cqo" || o.startsWith("evaluator-")) return "CQO";
498
- if (o === "service-ops" || o === "ops") return "Operations";
499
- if (o === "meeting-manager") return "Meeting";
500
- if (o === "dispatcher" || o === "ceo") return "CEO";
501
- return "Multi";
502
- }
503
-
504
- // Real-harness track status values use a different vocabulary. Normalize so
505
- // the visualization and tests stay on the same enum.
506
- function normalizeTrackStatus(raw: string | undefined): TrackStatus {
507
- const allowed: TrackStatus[] = ["dispatched", "in_progress", "joined", "blocked"];
508
- if (raw && allowed.includes(raw as TrackStatus)) return raw as TrackStatus;
509
- switch ((raw ?? "").toLowerCase()) {
510
- case "running":
511
- case "active":
512
- return "in_progress";
513
- case "completed":
514
- case "done":
515
- case "succeeded":
516
- return "joined";
517
- case "blocked":
518
- case "failed":
519
- case "error":
520
- return "blocked";
521
- case "pending":
522
- case "queued":
523
- case "scheduled":
524
- default:
525
- return "dispatched";
526
- }
527
- }
528
-
529
- function buildTracks(progress: RawProgress | null): ParallelTrack[] {
530
- // Prefer the explicit dashboard schema if the harness opts in; otherwise
531
- // fall back to the real-harness `conductor.tracks[]` shape.
532
- const explicit = progress?.parallel_tracks;
533
- if (explicit && explicit.length > 0) {
534
- return explicit.map((t) => ({
535
- id: t.id,
536
- from_meeting: t.from_meeting ?? "meeting",
537
- to_dept: (t.to_dept ?? "Multi") as ParallelTrack["to_dept"],
538
- to_room: t.to_room ?? "cto-team",
539
- status: normalizeTrackStatus(t.status as string | undefined),
540
- label: t.label,
541
- }));
542
- }
543
- const conductorTracks = progress?.conductor?.tracks ?? [];
544
- return conductorTracks.map((t) => ({
545
- id: t.id,
546
- from_meeting: progress?.conductor?.fork_meeting_id ?? "meeting",
547
- to_dept: ownerToDept(t.owner),
548
- to_room: ownerToRoom(t.owner),
549
- status: normalizeTrackStatus(t.status),
550
- label: t.action_type ?? t.deliverable,
551
- }));
552
- }
553
-
554
229
  function buildIncidents(progress: RawProgress | null): IncidentEntry[] {
555
230
  const list = progress?.service_ops?.incident?.open ?? [];
556
231
  return list.map((it) => {
@@ -569,241 +244,6 @@ function buildIncidents(progress: RawProgress | null): IncidentEntry[] {
569
244
  });
570
245
  }
571
246
 
572
- function buildHypothesis(progress: RawProgress | null): HypothesisEntry[] {
573
- const list = progress?.hypothesis?.active ?? [];
574
- return list.map((h) => {
575
- const v = (h.verdict ?? "pending").toLowerCase();
576
- const verdict =
577
- v === "valid" || v === "invalid" ? (v as HypothesisEntry["verdict"]) : "pending";
578
- return {
579
- id: h.id,
580
- brief: h.brief ?? h.id,
581
- verdict,
582
- ts: h.ts ?? null,
583
- };
584
- });
585
- }
586
-
587
- function buildEscalations(progress: RawProgress | null): EscalationEntry[] {
588
- return (progress?.escalations?.open ?? []).map((e) => ({
589
- id: e.id,
590
- reason: e.reason ?? "other",
591
- message: e.message,
592
- ts: e.ts ?? null,
593
- }));
594
- }
595
-
596
- function buildCurrentMeeting(progress: RawProgress | null): CurrentMeeting | null {
597
- const m = progress?.meetings;
598
- if (!m) return null;
599
- // Prefer explicit `current` (dashboard schema), then real-harness
600
- // `requested_type` (currently being convened), then `last_type` (most-recent).
601
- const rawType =
602
- (m.current && m.current.type) ||
603
- m.requested_type ||
604
- m.last_type ||
605
- null;
606
- if (!rawType) return null;
607
- const allowed: CurrentMeeting["type"][] = [
608
- "standup",
609
- "sprint-review",
610
- "spec-review",
611
- "incident-war-room",
612
- "all-hands",
613
- "followup-review",
614
- ];
615
- const type = allowed.includes(rawType as CurrentMeeting["type"])
616
- ? (rawType as CurrentMeeting["type"])
617
- : "standup";
618
- // Topic falls back to the decision summary when the harness hasn't written
619
- // a free-text topic yet.
620
- const decisionTopic = m.decision?.owner
621
- ? `${m.decision.owner}${m.decision.action_type ? ` · ${m.decision.action_type}` : ""}`
622
- : undefined;
623
- return {
624
- type,
625
- topic: m.current?.topic ?? m.requested_reason ?? m.last_reason ?? decisionTopic,
626
- convened_at: m.current?.convened_at ?? null,
627
- };
628
- }
629
-
630
- function deriveCadence(raw: string | undefined): MeetingCadence {
631
- if (raw === "light" || raw === "normal" || raw === "heavy") return raw;
632
- return "normal";
633
- }
634
-
635
- function deptToRoom(dept: string): RoomId | null {
636
- const map: Record<string, RoomId> = {
637
- CEO: "ceo",
638
- Meeting: "meeting",
639
- Planner: "coo",
640
- COO: "coo",
641
- CTO: "cto-team",
642
- CQO: "cqo-team",
643
- Operations: "service-ops",
644
- };
645
- return map[dept] ?? null;
646
- }
647
-
648
- function buildRooms(
649
- progress: RawProgress | null,
650
- meetingActive: AgentId[],
651
- meetings: MeetingsState,
652
- tracks: ParallelTrack[],
653
- incidents: IncidentEntry[],
654
- hypothesisList: HypothesisEntry[],
655
- contract: ContractSnapshot,
656
- evalScores: EvalScores | null,
657
- ): RoomState[] {
658
- const cto = progress?.cto;
659
- const cqo = progress?.cqo;
660
- const ops = progress?.service_ops;
661
- const sprintNumber = progress?.sprint?.number;
662
-
663
- const passRate =
664
- contract.feature_total > 0
665
- ? contract.feature_passed / contract.feature_total
666
- : null;
667
-
668
- const tracksByRoom = new Map<RoomId, number>();
669
- for (const t of tracks) {
670
- tracksByRoom.set(t.to_room, (tracksByRoom.get(t.to_room) ?? 0) + 1);
671
- }
672
-
673
- const incidentByDept = new Map<string, number>();
674
- for (const it of incidents) {
675
- incidentByDept.set(it.dept, (incidentByDept.get(it.dept) ?? 0) + 1);
676
- }
677
-
678
- const metrics: Partial<Record<RoomId, RoomMetrics>> = {
679
- ceo: {
680
- sprint_number: sprintNumber,
681
- },
682
- meeting: {
683
- cadence: meetings.cadence,
684
- next_scheduled: meetings.next_scheduled,
685
- active_tracks: tracks.length,
686
- },
687
- coo: {
688
- sprint_number: sprintNumber,
689
- active_hypothesis: hypothesisList.length,
690
- },
691
- "cto-team": {
692
- sprint_number: sprintNumber,
693
- last_review: cto?.last_review ?? null,
694
- open_arch_risks: cto?.open_arch_risks ?? 0,
695
- active_workers: progress?.company_state?.active_workers ?? 0,
696
- contract_signed: {
697
- be: !!cto?.contract_signed?.be,
698
- fe: !!cto?.contract_signed?.fe,
699
- },
700
- pass_rate: passRate,
701
- },
702
- "cqo-team": {
703
- sprint_number: sprintNumber,
704
- last_audit: cqo?.last_audit ?? null,
705
- sprint_verdict: cqo?.sprint_verdict ?? "pending",
706
- open_regressions: cqo?.open_regressions ?? 0,
707
- pass_rate: passRate,
708
- eval_scores: evalScores,
709
- },
710
- "service-ops": {
711
- sprint_number: sprintNumber,
712
- last_check: ops?.monitor?.last_check ?? null,
713
- open_alerts: ops?.monitor?.alerts_this_sprint ?? 0,
714
- open_incidents: incidents.length,
715
- },
716
- };
717
-
718
- return (Object.keys(ROOM_LABELS) as RoomId[]).map((id) => {
719
- const base: RoomState = {
720
- id,
721
- label_ko: ROOM_LABELS[id].ko,
722
- label_en: ROOM_LABELS[id].en,
723
- dept: ROOM_LABELS[id].dept,
724
- };
725
- if (metrics[id]) base.metrics = metrics[id];
726
- if (id === "meeting") {
727
- const seats = 7;
728
- const occupants = meetingActive.slice(0, seats);
729
- const overflow = Math.max(0, meetingActive.length - seats);
730
- base.seatLayout = { seats, occupants, overflow };
731
- }
732
- return base;
733
- });
734
- }
735
-
736
- function buildAgents(
737
- progress: RawProgress | null,
738
- meetingActive: AgentId[],
739
- incidents: IncidentEntry[],
740
- ): AgentState[] {
741
- const meetingSet = new Set<AgentId>(meetingActive);
742
- const incidentDepts = new Set(incidents.map((i) => i.dept));
743
- return AGENT_ROSTER.map((entry) => {
744
- const minifigState = deriveMinifigState(entry.id, progress ?? null, {
745
- incidentDepts,
746
- dept: entry.dept,
747
- });
748
- const inMeeting = meetingSet.has(entry.id);
749
- const room = inMeeting ? "meeting" : entry.room;
750
- const lastActivity =
751
- progress?.current_agent === entry.id
752
- ? `current_agent (${progress.agent_status ?? "unknown"})`
753
- : progress?.next_agent === entry.id
754
- ? "next_agent queued"
755
- : null;
756
- const matchedIncident = incidents.find((i) => i.dept === entry.dept);
757
- return {
758
- id: entry.id,
759
- name: entry.name,
760
- dept: entry.dept,
761
- room,
762
- homeRoom: entry.room,
763
- minifigState,
764
- lastActivity,
765
- talkingPreview: minifigState === "talking" ? pickTalkingPreview(entry.id) : undefined,
766
- alertReason:
767
- minifigState === "red-alert"
768
- ? matchedIncident?.message ??
769
- progress?.failure?.message ??
770
- "incident"
771
- : undefined,
772
- };
773
- });
774
- }
775
-
776
- function readFeatureTitles(rootDir: string): Map<string, string> {
777
- const map = new Map<string, string>();
778
- const candidates = [
779
- path.join(rootDir, ".harness", "actions", "feature-list.json"),
780
- path.join(rootDir, ".harness", "actions", "feature-queue.json"),
781
- ];
782
- for (const file of candidates) {
783
- const result = readJsonSafe<{
784
- features?: Array<{ id?: string; title?: string; summary?: string }>;
785
- queue?: {
786
- ready?: Array<{ id?: string; title?: string; summary?: string }>;
787
- in_progress?: Array<{ id?: string; title?: string; summary?: string }>;
788
- passed?: Array<{ id?: string; title?: string; summary?: string }>;
789
- failed?: Array<{ id?: string; title?: string; summary?: string }>;
790
- };
791
- }>(file);
792
- if (!result.ok) continue;
793
- for (const f of result.value.features ?? []) {
794
- if (f.id) map.set(f.id, f.title ?? f.summary ?? f.id);
795
- }
796
- const q = result.value.queue;
797
- for (const list of [q?.ready, q?.in_progress, q?.passed, q?.failed]) {
798
- if (!Array.isArray(list)) continue;
799
- for (const f of list) {
800
- if (f.id) map.set(f.id, f.title ?? f.summary ?? f.id);
801
- }
802
- }
803
- }
804
- return map;
805
- }
806
-
807
247
  interface RawWorker {
808
248
  name?: string;
809
249
  team?: number | string;
@@ -838,6 +278,7 @@ function normalizeMissionLifecycle(raw: unknown): MissionLifecycle {
838
278
  const value = String(raw ?? "").toLowerCase().replace(/[_\s-]+/g, "-");
839
279
  if (
840
280
  value === "active" ||
281
+ value === "operating" ||
841
282
  value === "closed" ||
842
283
  value === "cancelled" ||
843
284
  value === "superseded" ||
@@ -848,6 +289,7 @@ function normalizeMissionLifecycle(raw: unknown): MissionLifecycle {
848
289
  }
849
290
  if (value === "done" || value === "completed") return "complete";
850
291
  if (value === "canceled") return "cancelled";
292
+ if (value === "monitoring" || value === "perpetual") return "operating";
851
293
  return "unknown";
852
294
  }
853
295
 
@@ -909,59 +351,6 @@ function normalizeRawWorkers(value: RawWorker[] | Record<string, RawWorker> | nu
909
351
  });
910
352
  }
911
353
 
912
- function buildWorkers(rootDir: string, progress: RawProgress | null): WorkerSnapshot[] {
913
- const titles = readFeatureTitles(rootDir);
914
- let workers = normalizeRawWorkers(progress?.company_state?.workers);
915
- if (workers.length === 0) {
916
- const queue = readJsonSafe<{
917
- teams?: Record<string, {
918
- status?: string;
919
- feature?: string | null;
920
- pid?: number | null;
921
- agent?: string;
922
- phase?: string;
923
- prompt?: string | null;
924
- log?: string | null;
925
- spawn_status?: string;
926
- }>;
927
- }>(path.join(rootDir, ".harness", "actions", "feature-queue.json"));
928
- if (queue.ok) {
929
- workers = Object.entries(queue.value.teams ?? {})
930
- .filter(([, t]) => t.status === "busy" && !!t.feature)
931
- .map(([team, t]) => ({ ...t, team, feature: t.feature ?? undefined }));
932
- }
933
- }
934
- if (workers.length === 0) {
935
- workers = progress?.company_state?.last_dispatch ?? [];
936
- }
937
- return workers.map((w, idx) => {
938
- const feature = w.feature ?? `worker-${idx + 1}`;
939
- const promptText = w.prompt ? readTextSafe(path.join(rootDir, w.prompt)) : null;
940
- const logText = w.log ? readTextSafe(path.join(rootDir, w.log)) : null;
941
- const eta = w.eta ? ` ETA ${w.eta}` : "";
942
- const summarySource = firstLine(logText) || firstLine(promptText) || `${w.agent ?? w.name ?? "worker"} ${w.status ?? "assigned"}${eta}`;
943
- const material = w.prompt ?? w.log ?? null;
944
- const rawStatus = (w.status ?? w.spawn_status ?? "unknown").toLowerCase();
945
- const status = ["spawned", "recorded", "running", "idle", "blocked"].includes(rawStatus)
946
- ? (rawStatus as WorkerSnapshot["status"])
947
- : "unknown";
948
- return {
949
- team: w.team ?? idx + 1,
950
- feature,
951
- title: titles.get(feature) ?? feature,
952
- agent: w.agent ?? "unknown",
953
- phase: w.phase,
954
- status,
955
- pid: w.pid ?? null,
956
- prompt: w.prompt ?? null,
957
- log: w.log ?? null,
958
- progress: deriveWorkerProgress(w),
959
- summary: truncateText(summarySource, 140),
960
- next_material: material,
961
- };
962
- });
963
- }
964
-
965
354
  function normalizeWorkerName(value: string | null | undefined): string {
966
355
  return (value ?? "")
967
356
  .toLowerCase()
@@ -1077,137 +466,6 @@ function activeWorkerNames(progress: RawProgress | null): Set<string> {
1077
466
  return active;
1078
467
  }
1079
468
 
1080
- function meetingTitleFromMarkdown(text: string, fallback: string): string {
1081
- const heading = text.match(/^#\s+(.+)$/m)?.[1]?.trim();
1082
- return heading || fallback;
1083
- }
1084
-
1085
- function meetingVerdict(text: string): string | null {
1086
- const explicit = text.match(/verdict:\s*([^\n]+)/i)?.[1]?.trim();
1087
- if (explicit) return explicit;
1088
- if (/incident/i.test(text)) return "incident";
1089
- if (/PASS|passed|working|continue autonomous/i.test(text)) return "working";
1090
- return null;
1091
- }
1092
-
1093
- function buildRecentMeetings(rootDir: string): MeetingRecord[] {
1094
- const meetingsDir = path.join(rootDir, ".harness", "actions", "meetings");
1095
- if (!existsSync(meetingsDir)) return [];
1096
- const records: MeetingRecord[] = [];
1097
- for (const dir of readdirSync(meetingsDir)) {
1098
- const fullDir = path.join(meetingsDir, dir);
1099
- try {
1100
- if (!statSync(fullDir).isDirectory()) continue;
1101
- } catch {
1102
- continue;
1103
- }
1104
- const md = readdirSync(fullDir).find((name) => name.startsWith("meeting-") && name.endsWith(".md"));
1105
- if (!md) continue;
1106
- const fullPath = path.join(fullDir, md);
1107
- const text = readTextSafe(fullPath) ?? "";
1108
- let ts: string | null = null;
1109
- try {
1110
- ts = statSync(fullPath).mtime.toISOString();
1111
- } catch {
1112
- ts = null;
1113
- }
1114
- const summary =
1115
- text.match(/## Decision\s+([\s\S]*?)(?=\n## |\n# |$)/i)?.[1] ??
1116
- text.match(/## Summary\s+([\s\S]*?)(?=\n## |\n# |$)/i)?.[1] ??
1117
- text.split("\n").slice(0, 12).join(" ");
1118
- records.push({
1119
- id: dir,
1120
- path: path.relative(rootDir, fullPath),
1121
- ts,
1122
- title: meetingTitleFromMarkdown(text, dir),
1123
- verdict: meetingVerdict(text),
1124
- summary: truncateText(summary, 220),
1125
- content: text,
1126
- });
1127
- }
1128
- records.sort((a, b) => (b.ts ?? "").localeCompare(a.ts ?? ""));
1129
- return records.slice(0, 6);
1130
- }
1131
-
1132
- function buildOpsHealth(progress: RawProgress | null): OpsServiceHealth[] {
1133
- return (progress?.service_ops?.health ?? []).map((h) => {
1134
- const statusRaw = (h.status ?? "unknown").toLowerCase();
1135
- const status = statusRaw === "ok" || statusRaw === "degraded" || statusRaw === "down"
1136
- ? statusRaw
1137
- : "unknown";
1138
- return {
1139
- name: h.name ?? "service",
1140
- host: h.host ?? "127.0.0.1",
1141
- port: h.port ?? 0,
1142
- status,
1143
- port_state: h.port_state ?? null,
1144
- health_status: h.health_status ?? null,
1145
- health_path: h.health_path ?? null,
1146
- recent_errors: h.log?.recent_errors ?? null,
1147
- };
1148
- });
1149
- }
1150
-
1151
- function categorizeEnvKey(key: string): EnvFileSummary["keys"][number]["category"] {
1152
- if (/TOKEN|SECRET|KEY|PASSWORD|PASS|COOKIE|JWT/i.test(key)) return "secret";
1153
- if (/URL|HOST|ORIGIN|ENDPOINT/i.test(key)) return "endpoint";
1154
- if (/PORT/i.test(key)) return "port";
1155
- if (/MODE|ENV|NODE_ENV|LIVE|PAPER/i.test(key)) return "mode";
1156
- return "other";
1157
- }
1158
-
1159
- function maskEnvValue(value: string, category: EnvFileSummary["keys"][number]["category"]): string {
1160
- if (!value) return "(empty)";
1161
- if (category === "secret") return value.length <= 4 ? "••••" : `${value.slice(0, 2)}••••${value.slice(-2)}`;
1162
- if (value.length <= 18) return value;
1163
- return `${value.slice(0, 10)}…${value.slice(-5)}`;
1164
- }
1165
-
1166
- function buildEnvFiles(rootDir: string): EnvFileSummary[] {
1167
- const names = [".env", ".env.local", ".env.production", ".env.development", ".env.example"];
1168
- return names
1169
- .map((name) => {
1170
- const fullPath = path.join(rootDir, name);
1171
- if (!existsSync(fullPath)) return null;
1172
- const text = readTextSafe(fullPath) ?? "";
1173
- const keys = text
1174
- .split("\n")
1175
- .map((line) => line.trim())
1176
- .filter((line) => line && !line.startsWith("#") && line.includes("="))
1177
- .map((line) => {
1178
- const idx = line.indexOf("=");
1179
- const key = line.slice(0, idx).trim();
1180
- const value = line.slice(idx + 1).trim().replace(/^['"]|['"]$/g, "");
1181
- const category = categorizeEnvKey(key);
1182
- return { key, category, masked: maskEnvValue(value, category) };
1183
- });
1184
- let updated_at: string | null = null;
1185
- try {
1186
- updated_at = statSync(fullPath).mtime.toISOString();
1187
- } catch {
1188
- updated_at = null;
1189
- }
1190
- return {
1191
- path: name,
1192
- updated_at,
1193
- key_count: keys.length,
1194
- keys: keys.slice(0, 12),
1195
- };
1196
- })
1197
- .filter((x): x is EnvFileSummary => x !== null);
1198
- }
1199
-
1200
- function buildDashboard(rootDir: string, progress: RawProgress | null): OperationsDashboard {
1201
- const features = buildFeatureSummaries(rootDir);
1202
- return {
1203
- workers: buildWorkers(rootDir, progress),
1204
- features,
1205
- recentMeetings: buildRecentMeetings(rootDir),
1206
- opsHealth: buildOpsHealth(progress),
1207
- envFiles: buildEnvFiles(rootDir),
1208
- };
1209
- }
1210
-
1211
469
  function buildRuntime(progress: RawProgress | null): RuntimeSnapshot {
1212
470
  const ownerPrompt = progress?.owner_prompt
1213
471
  ? {
@@ -1222,6 +480,11 @@ function buildRuntime(progress: RawProgress | null): RuntimeSnapshot {
1222
480
  agentStatus: progress?.agent_status ?? "unknown",
1223
481
  nextAgent: progress?.next_agent ?? null,
1224
482
  updatedAt: progress?.updated_at ?? null,
483
+ conductorState: progress?.conductor?.state ?? null,
484
+ currentAction: progress?.conductor?.current_action ?? null,
485
+ companyState: progress?.company_state?.state ?? null,
486
+ activeWorkers: progress?.company_state?.active_workers ?? 0,
487
+ lastDispatchAt: progress?.company_state?.last_dispatch_at ?? null,
1225
488
  ownerPrompt,
1226
489
  };
1227
490
  }
@@ -1295,14 +558,13 @@ function readMissions(rootDir: string, progress: RawProgress | null = null, limi
1295
558
  updatedAt = mtime.toISOString();
1296
559
  } catch { /* ignore */ }
1297
560
  const statusMatch = content.match(/##\s*Status\s*\n+([A-Z_]+)/);
1298
- const hasDocmeta = /^---[\s\S]*?docmeta:[\s\S]*?---/.test(content);
1299
561
  // Runtime state is the source of truth for live activity. A
1300
562
  // recently touched docmeta-only draft is not enough to prove that a
1301
- // detached worker session is still running.
563
+ // detached worker session is still running, nor that it has
564
+ // completed — leave it "unknown" unless the doc has an explicit
565
+ // ## Status section.
1302
566
  const status: WorkerDocEntry["status"] = statusMatch
1303
567
  ? (statusMatch[1] as WorkerDocEntry["status"])
1304
- : hasDocmeta
1305
- ? "COMPLETE"
1306
568
  : "unknown";
1307
569
  const entry: WorkerDocEntry = {
1308
570
  name,
@@ -1367,6 +629,16 @@ function readMissions(rootDir: string, progress: RawProgress | null = null, limi
1367
629
  }
1368
630
  }
1369
631
 
632
+ // Perpetual (operating) goals carry an agenda.json the CXX co-write.
633
+ const agenda = readJsonSafe<{
634
+ items?: Array<{ status?: string }>;
635
+ cycles?: number;
636
+ }>(path.join(missionPath, "agenda.json"));
637
+ const agendaOpen = agenda.ok
638
+ ? (agenda.value.items ?? []).filter((it) => (it.status ?? "") !== "done").length
639
+ : undefined;
640
+ const operatingCycles = agenda.ok ? agenda.value.cycles ?? undefined : undefined;
641
+
1370
642
  const id = rel;
1371
643
  const label = rel.split("/").pop() ?? rel;
1372
644
  return {
@@ -1386,6 +658,8 @@ function readMissions(rootDir: string, progress: RawProgress | null = null, limi
1386
658
  ops: readMd("ops.md"),
1387
659
  workers,
1388
660
  cxxPresent: [...cxxPresent],
661
+ agendaOpen,
662
+ operatingCycles,
1389
663
  } satisfies MissionDoc;
1390
664
  })
1391
665
  .sort((a, b) => new Date(b.ts).getTime() - new Date(a.ts).getTime());
@@ -1614,6 +888,80 @@ function readActivitySamples(rootDir: string): ActivitySample[] {
1614
888
  return rows.sort((a, b) => Date.parse(a.ts) - Date.parse(b.ts));
1615
889
  }
1616
890
 
891
+ function fileCategory(relPath: string): HarnessFileEntry["category"] {
892
+ if (relPath.startsWith(".harness/documents/")) return "documents";
893
+ if (relPath.startsWith(".harness/todos/")) return "todos";
894
+ if (relPath.startsWith(".harness/activity/")) return "activity";
895
+ if (relPath.startsWith(".harness/gotchas/") || relPath.startsWith(".harness/conventions/")) return "knowledge";
896
+ if (relPath.startsWith(".harness/runtime/")) return "runtime";
897
+ if (relPath === ".harness/progress.json" || relPath === ".harness/config.json") return "config";
898
+ return "other";
899
+ }
900
+
901
+ function shouldSkipHarnessDir(relPath: string): boolean {
902
+ return (
903
+ relPath === ".harness/dashboard" ||
904
+ relPath.startsWith(".harness/dashboard/") ||
905
+ relPath === ".harness/shared/HR-Resource" ||
906
+ relPath.startsWith(".harness/shared/HR-Resource/")
907
+ );
908
+ }
909
+
910
+ function readHarnessFiles(rootDir: string, limit = 180): HarnessFileEntry[] {
911
+ const harnessDir = path.join(rootDir, ".harness");
912
+ if (!existsSync(harnessDir)) return [];
913
+
914
+ const entries: HarnessFileEntry[] = [];
915
+ const visit = (absDir: string, relDir: string, depth: number) => {
916
+ if (entries.length >= limit || depth > 5 || shouldSkipHarnessDir(relDir)) return;
917
+ let dirents: Dirent[];
918
+ try {
919
+ dirents = readdirSync(absDir, { withFileTypes: true });
920
+ } catch {
921
+ return;
922
+ }
923
+ dirents.sort((a, b) => {
924
+ if (a.isDirectory() !== b.isDirectory()) return a.isDirectory() ? -1 : 1;
925
+ return a.name.localeCompare(b.name);
926
+ });
927
+ for (const entry of dirents) {
928
+ if (entries.length >= limit || entry.name === ".DS_Store") break;
929
+ const abs = path.join(absDir, entry.name);
930
+ const rel = `${relDir}/${entry.name}`;
931
+ if (shouldSkipHarnessDir(rel)) continue;
932
+ let stat = null as ReturnType<typeof statSync> | null;
933
+ try {
934
+ stat = statSync(abs);
935
+ } catch {
936
+ continue;
937
+ }
938
+ entries.push({
939
+ path: rel,
940
+ name: entry.name,
941
+ kind: entry.isDirectory() ? "dir" : "file",
942
+ depth,
943
+ size: entry.isDirectory() ? null : stat.size,
944
+ updatedAt: stat.mtime.toISOString(),
945
+ category: fileCategory(rel),
946
+ });
947
+ if (entry.isDirectory()) visit(abs, rel, depth + 1);
948
+ }
949
+ };
950
+
951
+ entries.push({
952
+ path: ".harness",
953
+ name: ".harness",
954
+ kind: "dir",
955
+ depth: 0,
956
+ size: null,
957
+ updatedAt: null,
958
+ category: "runtime",
959
+ });
960
+ visit(harnessDir, ".harness", 1);
961
+
962
+ return entries;
963
+ }
964
+
1617
965
  export function readHarnessState(rootDir: string): HarnessSnapshot {
1618
966
  const harnessDir = path.join(rootDir, ".harness");
1619
967
  if (!existsSync(harnessDir)) {
@@ -1639,7 +987,6 @@ export function readHarnessState(rootDir: string): HarnessSnapshot {
1639
987
  message_en: "progress.json is corrupt — check the file.",
1640
988
  };
1641
989
  const snapshot = emptySnapshot(banner, rootDir);
1642
- snapshot.archive = buildArchive(rootDir);
1643
990
  snapshot.missions = readMissions(rootDir, null);
1644
991
  snapshot.ownerHistory = readOwnerHistory(rootDir);
1645
992
  snapshot.gotchas = readGotchas(rootDir);
@@ -1647,55 +994,21 @@ export function readHarnessState(rootDir: string): HarnessSnapshot {
1647
994
  snapshot.todos = readTodos(rootDir);
1648
995
  snapshot.events = readEvents(rootDir);
1649
996
  snapshot.activitySamples = readActivitySamples(rootDir);
997
+ snapshot.files = readHarnessFiles(rootDir);
1650
998
  return snapshot;
1651
999
  }
1652
1000
 
1653
1001
  const progress = result.value;
1654
- const meetingActive = progress.meetings?.active ?? [];
1655
- const meetings: MeetingsState = {
1656
- active: meetingActive,
1657
- cadence: deriveCadence(progress.meetings?.cadence as string | undefined),
1658
- next_scheduled: progress.meetings?.next_scheduled ?? null,
1659
- current: buildCurrentMeeting(progress),
1660
- };
1661
-
1662
- const tracks = buildTracks(progress);
1663
1002
  const incidents = buildIncidents(progress);
1664
- const hypothesisList = buildHypothesis(progress);
1665
- const escalations = buildEscalations(progress);
1666
- const features = buildFeatureSummaries(rootDir);
1667
- const contract = buildContract(rootDir, progress, features);
1668
- const evalScores = progress.cqo?.last_scores ?? null;
1669
- const dashboard = buildDashboard(rootDir, progress);
1670
1003
 
1671
1004
  return {
1672
1005
  version: SNAPSHOT_VERSION,
1673
1006
  ts: new Date().toISOString(),
1674
1007
  projectName: path.basename(rootDir),
1675
1008
  projectPath: rootDir,
1676
- agents: buildAgents(progress, meetingActive, incidents),
1677
- rooms: buildRooms(
1678
- progress,
1679
- meetingActive,
1680
- meetings,
1681
- tracks,
1682
- incidents,
1683
- hypothesisList,
1684
- contract,
1685
- evalScores,
1686
- ),
1687
- goal: buildGoal(rootDir, progress),
1688
- archive: buildArchive(rootDir),
1689
- meetings,
1690
- tracks,
1691
- incidents,
1692
- hypothesis: hypothesisList,
1693
- escalations,
1694
- contract,
1695
- evalScores,
1696
1009
  errorBanner: null,
1697
- dashboard,
1698
1010
  runtime: buildRuntime(progress),
1011
+ incidents,
1699
1012
  missions: readMissions(rootDir, progress),
1700
1013
  ownerHistory: readOwnerHistory(rootDir),
1701
1014
  gotchas: readGotchas(rootDir),
@@ -1703,8 +1016,6 @@ export function readHarnessState(rootDir: string): HarnessSnapshot {
1703
1016
  todos: readTodos(rootDir),
1704
1017
  events: readEvents(rootDir),
1705
1018
  activitySamples: readActivitySamples(rootDir),
1019
+ files: readHarnessFiles(rootDir),
1706
1020
  };
1707
1021
  }
1708
-
1709
- // Exported for tests — silences the unused warning when only the type is used.
1710
- export type { Dept };