@walwal-harness/cli 7.1.32 → 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/HR-Resource/cdo/SKILL.md +4 -0
- package/HR-Resource/coo/SKILL.md +4 -0
- package/HR-Resource/cqo/SKILL.md +4 -0
- package/HR-Resource/cto/SKILL.md +4 -0
- package/HR-Resource/ops/SKILL.md +4 -0
- package/README.md +2 -0
- package/apps/harness-dashboard/lib/__tests__/harness-state.test.ts +63 -0
- package/apps/harness-dashboard/lib/harness-state.ts +57 -16
- package/package.json +1 -1
package/HR-Resource/cdo/SKILL.md
CHANGED
|
@@ -25,6 +25,10 @@ Before design work, read `.harness/conventions/shared.md`, `.harness/conventions
|
|
|
25
25
|
8. Select the final direction and write a CDO preview artifact at `.harness/documents/{mission_name}/cdo/preview.html`.
|
|
26
26
|
9. Report to CEO and CTO with a concise preview summary and mention that the visual sample is available in the dashboard by clicking harness-cdo.
|
|
27
27
|
|
|
28
|
+
## Worker Activity Telemetry
|
|
29
|
+
|
|
30
|
+
Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running.
|
|
31
|
+
|
|
28
32
|
## Rule
|
|
29
33
|
|
|
30
34
|
Design output must be usable by implementation teams, not just descriptive.
|
package/HR-Resource/coo/SKILL.md
CHANGED
|
@@ -35,6 +35,10 @@ During planning, COO must determine whether the active runtime exposes MCP serve
|
|
|
35
35
|
7. Review worker reports against the goal.
|
|
36
36
|
8. Reassign work or report to CEO.
|
|
37
37
|
|
|
38
|
+
## Worker Activity Telemetry
|
|
39
|
+
|
|
40
|
+
Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running.
|
|
41
|
+
|
|
38
42
|
## Non-Execution Rule
|
|
39
43
|
|
|
40
44
|
COO must not directly produce research findings, sprint plans, feed lists, market conclusions, backtest scripts, or documentation deliverables. COO may only frame the question, select and brief workers, evaluate worker output, and record the accepted decision.
|
package/HR-Resource/cqo/SKILL.md
CHANGED
|
@@ -24,6 +24,10 @@ Before quality work, read `.harness/conventions/shared.md`, `.harness/convention
|
|
|
24
24
|
7. Monitor repeated issues and promote verified lessons to `.harness/conventions`, `.harness/gotchas`, `.harness/memories`, or `.harness/shared`.
|
|
25
25
|
8. Approve or reject archive based solely on worker-provided evidence and OPS runtime/watch evidence when the mission uses a runnable environment.
|
|
26
26
|
|
|
27
|
+
## Worker Activity Telemetry
|
|
28
|
+
|
|
29
|
+
Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running.
|
|
30
|
+
|
|
27
31
|
## Hard Rules
|
|
28
32
|
|
|
29
33
|
CQO must not directly execute QA, visual review, security review, performance testing, or regression checks. CQO may only define gates, select evaluators, review evidence, decide archive eligibility, and document worker names and report paths.
|
package/HR-Resource/cto/SKILL.md
CHANGED
|
@@ -26,6 +26,10 @@ Before engineering work, read `.harness/conventions/shared.md`, `.harness/conven
|
|
|
26
26
|
9. Delegate all implementation and technical deliverables to hired workers in fresh sessions.
|
|
27
27
|
10. Collect reports, resolve blockers, and hand completed work to CQO.
|
|
28
28
|
|
|
29
|
+
## Worker Activity Telemetry
|
|
30
|
+
|
|
31
|
+
Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running.
|
|
32
|
+
|
|
29
33
|
## Hard Rules
|
|
30
34
|
|
|
31
35
|
CTO must not directly write code, create build scripts, choose detailed implementation content, run technical QA as the evaluator, or produce final implementation artifacts. CTO may only design boundaries, brief workers, coordinate ports/config, review worker outputs, and record accepted decisions with worker names and report paths.
|
package/HR-Resource/ops/SKILL.md
CHANGED
|
@@ -66,6 +66,10 @@ After service launch, OPS continues the same monitoring duty against `runtime.pr
|
|
|
66
66
|
10. Classify exceptions as verification, build, backend, frontend, platform, external API, infrastructure, owner-config, production, or unknown.
|
|
67
67
|
11. Raise emergency events to CEO/CTO/CQO with evidence and the mapped environment record.
|
|
68
68
|
|
|
69
|
+
## Worker Activity Telemetry
|
|
70
|
+
|
|
71
|
+
Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running.
|
|
72
|
+
|
|
69
73
|
## Required Output Sections
|
|
70
74
|
|
|
71
75
|
1. Worker Task Briefs — monitoring/recovery task, capability needed, selected worker or hiring request, acceptance criteria.
|
package/README.md
CHANGED
|
@@ -283,6 +283,8 @@ 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 |
|
|
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 |
|
|
286
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 |
|
|
287
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 |
|
|
288
290
|
| 7.1.28 | Dashboard APM rewrite: top header consolidation, Knowledge Base panel (gotchas/conventions), Layer Activity (CEO/CXX/Worker TODO·DONE·Remain via goal-scope inference), Recent Report list (worker mtime desc), Cadence strip with click-to-prompt tooltip in local timezone, heatmap click-tooltip with empty-cell guard, Document Viewer expand-to-50vw with docmeta hidden, Command Log bubble popover |
|
|
@@ -381,4 +381,67 @@ describe("readHarnessState", () => {
|
|
|
381
381
|
active: false,
|
|
382
382
|
});
|
|
383
383
|
});
|
|
384
|
+
|
|
385
|
+
it("keeps recently written docmeta worker reports active without an explicit COMPLETE marker", () => {
|
|
386
|
+
const harnessDir = path.join(dir, ".harness");
|
|
387
|
+
const missionDir = path.join(harnessDir, "documents", "goal-1-dashboard");
|
|
388
|
+
mkdirSync(path.join(missionDir, "cto", "workers"), { recursive: true });
|
|
389
|
+
writeFileSync(path.join(missionDir, "ceo.md"), "# Dashboard\n");
|
|
390
|
+
writeFileSync(
|
|
391
|
+
path.join(missionDir, "cto", "workers", "frontend-worker.md"),
|
|
392
|
+
"---\ndocmeta:\n id: frontend-worker\n---\n\n# Worker Report\n\nEvidence is still being appended.\n"
|
|
393
|
+
);
|
|
394
|
+
writeFileSync(
|
|
395
|
+
path.join(harnessDir, "progress.json"),
|
|
396
|
+
JSON.stringify({
|
|
397
|
+
current_agent: "ceo",
|
|
398
|
+
agent_status: "running",
|
|
399
|
+
company_state: { active_workers: 0, workers: [] },
|
|
400
|
+
})
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
const workers = readHarnessState(dir).missions[0].workers;
|
|
404
|
+
expect(workers[0]).toMatchObject({
|
|
405
|
+
name: "frontend-worker",
|
|
406
|
+
status: "IN_PROGRESS",
|
|
407
|
+
active: true,
|
|
408
|
+
});
|
|
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
|
+
});
|
|
384
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
|
-
|
|
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);
|
|
@@ -1220,16 +1259,6 @@ function readMissions(rootDir: string, progress: RawProgress | null = null, limi
|
|
|
1220
1259
|
if (!f.endsWith(".md")) continue;
|
|
1221
1260
|
const reportAbs = path.join(missionPath, relDir, f);
|
|
1222
1261
|
const content = readMd(`${relDir}/${f}`) ?? "";
|
|
1223
|
-
const statusMatch = content.match(/##\s*Status\s*\n+([A-Z_]+)/);
|
|
1224
|
-
// Worker docs sometimes omit the "## Status" block but always carry a
|
|
1225
|
-
// docmeta frontmatter when the worker submitted its output. Treat the
|
|
1226
|
-
// presence of a docmeta block (or a non-empty body) as COMPLETE.
|
|
1227
|
-
const hasDocmeta = /^---[\s\S]*?docmeta:[\s\S]*?---/.test(content);
|
|
1228
|
-
const status: WorkerDocEntry["status"] = statusMatch
|
|
1229
|
-
? (statusMatch[1] as WorkerDocEntry["status"])
|
|
1230
|
-
: hasDocmeta
|
|
1231
|
-
? "COMPLETE"
|
|
1232
|
-
: "unknown";
|
|
1233
1262
|
const name = normalizeWorkerName(f);
|
|
1234
1263
|
const hired = hiredForMission(hiredWorkers, name, owner, rel);
|
|
1235
1264
|
let updatedAt: string | null = null;
|
|
@@ -1239,6 +1268,18 @@ function readMissions(rootDir: string, progress: RawProgress | null = null, limi
|
|
|
1239
1268
|
updatedAt = mtime.toISOString();
|
|
1240
1269
|
recentlyTouched = Date.now() - mtime.getTime() < RECENT_WORKER_ACTIVE_MS;
|
|
1241
1270
|
} catch { /* ignore */ }
|
|
1271
|
+
const statusMatch = content.match(/##\s*Status\s*\n+([A-Z_]+)/);
|
|
1272
|
+
const hasDocmeta = /^---[\s\S]*?docmeta:[\s\S]*?---/.test(content);
|
|
1273
|
+
// Worker docs often write docmeta before appending final evidence.
|
|
1274
|
+
// Without an explicit COMPLETE marker, keep recent writes visible as
|
|
1275
|
+
// active; after the activity window they become historical reports.
|
|
1276
|
+
const status: WorkerDocEntry["status"] = statusMatch
|
|
1277
|
+
? (statusMatch[1] as WorkerDocEntry["status"])
|
|
1278
|
+
: hasDocmeta && recentlyTouched && !runtimeIdle
|
|
1279
|
+
? "IN_PROGRESS"
|
|
1280
|
+
: hasDocmeta
|
|
1281
|
+
? "COMPLETE"
|
|
1282
|
+
: "unknown";
|
|
1242
1283
|
const entry: WorkerDocEntry = {
|
|
1243
1284
|
name,
|
|
1244
1285
|
displayName: workerDisplayName(content, hired, name),
|
|
@@ -1246,7 +1287,7 @@ function readMissions(rootDir: string, progress: RawProgress | null = null, limi
|
|
|
1246
1287
|
status,
|
|
1247
1288
|
owner: hired?.owner ?? owner,
|
|
1248
1289
|
hired: !!hired,
|
|
1249
|
-
active: !runtimeIdle && (activeWorkers.has(name) ||
|
|
1290
|
+
active: !runtimeIdle && (activeWorkers.has(name) || status === "IN_PROGRESS"),
|
|
1250
1291
|
sourcePath: hired?.sourcePath ?? null,
|
|
1251
1292
|
reportPath: path.relative(rootDir, reportAbs),
|
|
1252
1293
|
updatedAt,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "7.1.
|
|
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"
|