@walwal-harness/cli 7.1.33 → 7.1.34

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/README.md CHANGED
@@ -283,6 +283,7 @@ harness-hiring → register and onboard worker
283
283
 
284
284
  | Version | Summary |
285
285
  |---|---|
286
+ | 7.1.34 | Fix dashboard 500 when `company_state.workers` is recorded as a keyed worker map instead of an array |
286
287
  | 7.1.33 | Dashboard keeps recently written worker reports active even when docmeta is present, and CXX skills now record worker activity telemetry for live dashboard state |
287
288
  | 7.1.31 | Persist dashboard heatmap activity to 7-day JSONL logs, load persisted samples in the dashboard, and backfill activity during migrate from existing mission docs/progress logs |
288
289
  | 7.1.29 | Idempotent AGENTS/CLAUDE doc merge with marked harness sections, mission lifecycle backfill for init/migrate, completion closes todos and mission-state, and Layer Activity now lists CXX/worker rows sorted by remaining work |
@@ -407,4 +407,41 @@ describe("readHarnessState", () => {
407
407
  active: true,
408
408
  });
409
409
  });
410
+
411
+ it("accepts keyed company_state.workers maps from manual CXX telemetry", () => {
412
+ const harnessDir = path.join(dir, ".harness");
413
+ const missionDir = path.join(harnessDir, "documents", "goal-1-dashboard");
414
+ mkdirSync(path.join(missionDir, "cqo", "workers"), { recursive: true });
415
+ writeFileSync(path.join(missionDir, "ceo.md"), "# Dashboard\n");
416
+ writeFileSync(
417
+ path.join(missionDir, "cqo", "workers", "evaluator.md"),
418
+ "---\ndocmeta:\n id: evaluator\n---\n\n# Worker Report\n"
419
+ );
420
+ writeFileSync(
421
+ path.join(harnessDir, "progress.json"),
422
+ JSON.stringify({
423
+ agent_status: "running",
424
+ company_state: {
425
+ active_workers: 1,
426
+ workers: {
427
+ "engineering-engineering-code-reviewer": {
428
+ owner: "cqo",
429
+ report: ".harness/documents/goal-1-dashboard/cqo/workers/evaluator.md",
430
+ status: "running",
431
+ },
432
+ },
433
+ },
434
+ })
435
+ );
436
+
437
+ const snap = readHarnessState(dir);
438
+ expect(snap.dashboard.workers[0]).toMatchObject({
439
+ agent: "engineering-engineering-code-reviewer",
440
+ status: "running",
441
+ });
442
+ expect(snap.missions[0].workers[0]).toMatchObject({
443
+ owner: "cqo",
444
+ active: true,
445
+ });
446
+ });
410
447
  });
@@ -157,6 +157,18 @@ interface RawProgress {
157
157
  spawn_status?: string;
158
158
  status?: string;
159
159
  pid?: number | null;
160
+ }> | Record<string, {
161
+ team?: number | string;
162
+ feature?: string;
163
+ agent?: string;
164
+ phase?: string;
165
+ prompt?: string | null;
166
+ log?: string | null;
167
+ spawn_status?: string;
168
+ status?: string;
169
+ pid?: number | null;
170
+ owner?: string;
171
+ report?: string | null;
160
172
  }>;
161
173
  last_dispatch?: Array<{
162
174
  team?: number | string;
@@ -784,7 +796,19 @@ function readFeatureTitles(rootDir: string): Map<string, string> {
784
796
  return map;
785
797
  }
786
798
 
787
- type RawWorker = NonNullable<NonNullable<RawProgress["company_state"]>["workers"]>[number];
799
+ interface RawWorker {
800
+ team?: number | string;
801
+ feature?: string;
802
+ agent?: string;
803
+ phase?: string;
804
+ prompt?: string | null;
805
+ log?: string | null;
806
+ spawn_status?: string;
807
+ status?: string;
808
+ pid?: number | null;
809
+ owner?: string;
810
+ report?: string | null;
811
+ }
788
812
  type WorkerOwner = WorkerDocEntry["owner"];
789
813
 
790
814
  interface HiredWorkerEntry {
@@ -839,13 +863,28 @@ function deriveWorkerProgress(worker: RawWorker): number | null {
839
863
  if (status === "blocked" || status === "failed") return 0.35;
840
864
  if (status === "recorded") return 0.15;
841
865
  if (status === "spawned" || status === "running") return 0.55;
842
- if (status === "completed" || status === "done") return 1;
866
+ if (status === "complete" || status === "completed" || status === "done") return 1;
843
867
  return null;
844
868
  }
845
869
 
870
+ function normalizeRawWorkers(value: RawWorker[] | Record<string, RawWorker> | null | undefined): RawWorker[] {
871
+ if (Array.isArray(value)) return value;
872
+ if (!value || typeof value !== "object") return [];
873
+ return Object.entries(value).map(([name, worker], idx) => {
874
+ const w = worker && typeof worker === "object" ? worker : {};
875
+ return {
876
+ ...w,
877
+ team: (w as RawWorker).team ?? idx + 1,
878
+ agent: (w as RawWorker).agent ?? name,
879
+ feature: (w as RawWorker).feature ?? name,
880
+ log: (w as RawWorker).log ?? (w as RawWorker & { report?: string | null }).report ?? null,
881
+ };
882
+ });
883
+ }
884
+
846
885
  function buildWorkers(rootDir: string, progress: RawProgress | null): WorkerSnapshot[] {
847
886
  const titles = readFeatureTitles(rootDir);
848
- let workers = progress?.company_state?.workers ?? [];
887
+ let workers = normalizeRawWorkers(progress?.company_state?.workers);
849
888
  if (workers.length === 0) {
850
889
  const queue = readJsonSafe<{
851
890
  teams?: Record<string, {
@@ -996,12 +1035,12 @@ function activeWorkerNames(progress: RawProgress | null): Set<string> {
996
1035
  const active = new Set<string>();
997
1036
  if (isHarnessRuntimeIdle(progress)) return active;
998
1037
  const workers = [
999
- ...(progress?.company_state?.workers ?? []),
1038
+ ...normalizeRawWorkers(progress?.company_state?.workers),
1000
1039
  ...(progress?.company_state?.last_dispatch ?? []),
1001
1040
  ];
1002
1041
  for (const worker of workers) {
1003
1042
  const rawStatus = (worker.status ?? worker.spawn_status ?? "").toLowerCase();
1004
- if (["completed", "done", "idle"].includes(rawStatus)) continue;
1043
+ if (["complete", "completed", "done", "idle"].includes(rawStatus)) continue;
1005
1044
  for (const value of [worker.agent, worker.feature]) {
1006
1045
  const normalized = normalizeWorkerName(value);
1007
1046
  if (normalized) active.add(normalized);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walwal-harness/cli",
3
- "version": "7.1.33",
3
+ "version": "7.1.34",
4
4
  "description": "Company-style AI agent harness for Claude and Codex. Installs commands, CXX agents, skills, HR-Resource hiring pool, and project-local .harness runtime state.",
5
5
  "bin": {
6
6
  "walwal-harness": "bin/init.js"