@walwal-harness/cli 7.1.25 → 7.1.27
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/CHANGELOG.md +10 -0
- package/HR-Resource/cdo/SKILL.md +4 -0
- package/HR-Resource/ceo/SKILL.md +9 -0
- package/HR-Resource/coo/SKILL.md +20 -4
- package/HR-Resource/cqo/SKILL.md +4 -0
- package/HR-Resource/cto/SKILL.md +4 -0
- package/HR-Resource/hiring/SKILL.md +5 -1
- package/HR-Resource/index.json +5 -1
- package/HR-Resource/ops/SKILL.md +4 -0
- package/HR-Resource/resource-manager/SKILL.md +4 -1
- package/HR-Resource/support-support-mcp-registry-steward/SKILL.md +77 -0
- package/README.md +11 -3
- package/apps/harness-dashboard/app/globals.css +35 -3
- package/apps/harness-dashboard/components/Drawer.tsx +7 -7
- package/apps/harness-dashboard/components/MissionTimeline.tsx +4 -4
- package/apps/harness-dashboard/components/OrgTree.tsx +7 -7
- package/apps/harness-dashboard/components/Scene.tsx +134 -3
- package/apps/harness-dashboard/lib/harness-state.ts +91 -0
- package/apps/harness-dashboard/lib/types.ts +27 -0
- package/assets/templates/AGENTS.md.template +9 -4
- package/assets/templates/HARNESS.md +6 -1
- package/assets/templates/config.json +10 -8
- package/assets/templates/todos-state.json.template +12 -0
- package/bin/init.js +184 -2
- package/conventions/README.md +3 -1
- package/conventions/coo.md +2 -1
- package/gotchas/README.md +3 -1
- package/package.json +1 -1
- package/scripts/harness-gotcha-register.sh +2 -0
- package/scripts/harness-user-prompt-submit.sh +42 -0
- package/scripts/lib/harness-structured-log.sh +59 -0
|
@@ -96,7 +96,11 @@ export function Scene({ snapshot: initial, lang = "ko" }: SceneProps) {
|
|
|
96
96
|
{snapshot.escalations.length > 0 && (
|
|
97
97
|
<EscalationStrip escalations={snapshot.escalations} />
|
|
98
98
|
)}
|
|
99
|
-
<div className="
|
|
99
|
+
<div className="mb-4 grid gap-3 xl:grid-cols-[1.25fr_1fr]">
|
|
100
|
+
<CommandDeck snapshot={snapshot} connectionState={connectionState} />
|
|
101
|
+
<ProcessPulse snapshot={snapshot} />
|
|
102
|
+
</div>
|
|
103
|
+
<div className="grid min-h-0 flex-1 min-w-0 gap-4 lg:grid-cols-[1.12fr_0.88fr]">
|
|
100
104
|
<div className="scrollbar-hidden min-h-0 min-w-0 overflow-y-auto pr-1">
|
|
101
105
|
<section className="min-w-0">
|
|
102
106
|
<div className="mb-3 flex flex-wrap items-end justify-between gap-3">
|
|
@@ -112,9 +116,9 @@ export function Scene({ snapshot: initial, lang = "ko" }: SceneProps) {
|
|
|
112
116
|
</div>
|
|
113
117
|
<div
|
|
114
118
|
data-testid="brick-office-stage"
|
|
115
|
-
className="w-full overflow-x-auto rounded-
|
|
119
|
+
className="glass-panel w-full overflow-x-auto rounded-lg"
|
|
116
120
|
>
|
|
117
|
-
<div className="pointer-events-none flex items-center justify-between gap-3 border-b border-white/10 bg-
|
|
121
|
+
<div className="pointer-events-none flex items-center justify-between gap-3 border-b border-white/10 bg-white/5 px-4 py-2 font-mono text-[10px] text-gray-300 backdrop-blur">
|
|
118
122
|
<span>Owner → CEO → CXX → Workers</span>
|
|
119
123
|
<span className="truncate">
|
|
120
124
|
{snapshot.missions?.[0]?.missionId ?? "no active mission"}
|
|
@@ -239,6 +243,133 @@ export function Scene({ snapshot: initial, lang = "ko" }: SceneProps) {
|
|
|
239
243
|
);
|
|
240
244
|
}
|
|
241
245
|
|
|
246
|
+
function CommandDeck({
|
|
247
|
+
snapshot,
|
|
248
|
+
connectionState,
|
|
249
|
+
}: {
|
|
250
|
+
snapshot: HarnessSnapshot;
|
|
251
|
+
connectionState: ConnectionState;
|
|
252
|
+
}) {
|
|
253
|
+
const activeMission = snapshot.missions?.[0];
|
|
254
|
+
const cxxCount = activeMission?.cxxPresent.length ?? 0;
|
|
255
|
+
const activeWorkers = activeMission?.workers.filter((w) => w.active).length ?? 0;
|
|
256
|
+
const activeTodos = snapshot.todos.filter((t) => t.status === "active").length;
|
|
257
|
+
const blockedSignals =
|
|
258
|
+
snapshot.escalations.length +
|
|
259
|
+
snapshot.incidents.length +
|
|
260
|
+
snapshot.dashboard.features.filter((f) => f.status === "blocked" || f.status === "failed").length;
|
|
261
|
+
const stateLabel = blockedSignals > 0 ? "Needs Attention" : activeWorkers > 0 ? "In Motion" : "Observing";
|
|
262
|
+
|
|
263
|
+
return (
|
|
264
|
+
<section className="glass-panel relative overflow-hidden rounded-lg px-5 py-4">
|
|
265
|
+
<div className="aurora-line absolute inset-x-0 top-0 h-1" />
|
|
266
|
+
<div className="relative flex flex-wrap items-start justify-between gap-4">
|
|
267
|
+
<div className="min-w-0">
|
|
268
|
+
<p className="font-mono text-[10px] uppercase tracking-[0.28em] text-cyan-200/80">
|
|
269
|
+
Company Harness Observatory
|
|
270
|
+
</p>
|
|
271
|
+
<h1 className="mt-1 text-2xl font-semibold tracking-tight text-white sm:text-3xl">
|
|
272
|
+
{snapshot.projectName || "walwal-harness"}
|
|
273
|
+
</h1>
|
|
274
|
+
<p className="mt-2 max-w-2xl text-sm leading-6 text-slate-300">
|
|
275
|
+
{activeMission?.missionId ?? snapshot.goal?.title ?? "No active mission detected"}
|
|
276
|
+
</p>
|
|
277
|
+
</div>
|
|
278
|
+
<div className="brutal-tile min-w-[170px] rounded-md px-3 py-2">
|
|
279
|
+
<div className="font-mono text-[9px] uppercase tracking-[0.2em] text-slate-500">state</div>
|
|
280
|
+
<div className="mt-1 text-lg font-semibold text-white">{stateLabel}</div>
|
|
281
|
+
<div className="mt-1 font-mono text-[10px] text-slate-400">
|
|
282
|
+
{connectionState} · {snapshot.runtime.currentAgent ?? snapshot.runtime.nextAgent ?? "idle"}
|
|
283
|
+
</div>
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
<div className="relative mt-4 grid gap-2 sm:grid-cols-4">
|
|
287
|
+
<MetricTile label="CXX Present" value={String(cxxCount)} tone="cyan" />
|
|
288
|
+
<MetricTile label="Active Todos" value={String(activeTodos)} tone="green" />
|
|
289
|
+
<MetricTile label="Open Incidents" value={String(snapshot.incidents.length)} tone="rose" />
|
|
290
|
+
<MetricTile label="Missions" value={String(snapshot.missions.length)} tone="violet" />
|
|
291
|
+
</div>
|
|
292
|
+
</section>
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function MetricTile({ label, value, tone }: { label: string; value: string; tone: "cyan" | "green" | "rose" | "violet" }) {
|
|
297
|
+
const toneClass = {
|
|
298
|
+
cyan: "text-cyan-200 border-cyan-300/35",
|
|
299
|
+
green: "text-emerald-200 border-emerald-300/35",
|
|
300
|
+
rose: "text-rose-200 border-rose-300/35",
|
|
301
|
+
violet: "text-violet-200 border-violet-300/35",
|
|
302
|
+
}[tone];
|
|
303
|
+
return (
|
|
304
|
+
<div className={`brutal-tile rounded-md px-3 py-2 ${toneClass}`}>
|
|
305
|
+
<div className="font-mono text-[9px] uppercase tracking-[0.18em] text-slate-500">{label}</div>
|
|
306
|
+
<div className="mt-1 text-2xl font-semibold">{value}</div>
|
|
307
|
+
</div>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function ProcessPulse({ snapshot }: { snapshot: HarnessSnapshot }) {
|
|
312
|
+
const roles = ["ceo", "coo", "cdo", "cto", "cqo", "ops"] as const;
|
|
313
|
+
const activeMission = snapshot.missions?.[0];
|
|
314
|
+
const activeRoles = new Set(activeMission?.cxxPresent ?? []);
|
|
315
|
+
const roleStatus = roles.map((role) => {
|
|
316
|
+
const todos = snapshot.todos.filter((t) => t.owner === role);
|
|
317
|
+
const activeTodos = todos.filter((t) => t.status === "active").length;
|
|
318
|
+
const blockedTodos = todos.filter((t) => t.status === "blocked").length;
|
|
319
|
+
const workerCount = activeMission?.workers.filter((w) => w.owner === role).length ?? 0;
|
|
320
|
+
const activeCount = activeMission?.workers.filter((w) => w.owner === role && w.active).length ?? 0;
|
|
321
|
+
return { role, workerCount, activeCount, activeTodos, blockedTodos, present: activeRoles.has(role) || todos.length > 0 };
|
|
322
|
+
});
|
|
323
|
+
return (
|
|
324
|
+
<section className="glass-panel rounded-lg px-4 py-4">
|
|
325
|
+
<div className="flex items-center justify-between gap-3">
|
|
326
|
+
<div>
|
|
327
|
+
<p className="font-mono text-[10px] uppercase tracking-[0.24em] text-fuchsia-200/75">
|
|
328
|
+
Process Monitor
|
|
329
|
+
</p>
|
|
330
|
+
<h2 className="mt-1 text-base font-semibold text-white">CXX queue readiness</h2>
|
|
331
|
+
</div>
|
|
332
|
+
<div className="rounded-full border border-white/15 bg-white/8 px-3 py-1 font-mono text-[10px] text-slate-300">
|
|
333
|
+
{snapshot.runtime.agentStatus}
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
<div className="mt-3 grid grid-cols-2 gap-2 sm:grid-cols-3">
|
|
337
|
+
{roleStatus.map((item) => (
|
|
338
|
+
<div key={item.role} className="brutal-tile rounded-md px-3 py-2">
|
|
339
|
+
<div className="flex items-center justify-between gap-2">
|
|
340
|
+
<span className="font-mono text-[10px] uppercase tracking-[0.18em] text-slate-400">{item.role}</span>
|
|
341
|
+
<span
|
|
342
|
+
className={`h-2 w-2 rounded-full ${
|
|
343
|
+
item.blockedTodos > 0
|
|
344
|
+
? "bg-rose-400"
|
|
345
|
+
: item.activeTodos > 0 || item.activeCount > 0
|
|
346
|
+
? "bg-cyan-300 animate-pulse"
|
|
347
|
+
: item.present
|
|
348
|
+
? "bg-emerald-300"
|
|
349
|
+
: "bg-slate-600"
|
|
350
|
+
}`}
|
|
351
|
+
/>
|
|
352
|
+
</div>
|
|
353
|
+
<div className="mt-2 text-xs text-slate-300">
|
|
354
|
+
{item.blockedTodos > 0
|
|
355
|
+
? `${item.blockedTodos} blocked`
|
|
356
|
+
: item.activeTodos > 0
|
|
357
|
+
? `${item.activeTodos} todo`
|
|
358
|
+
: item.activeCount > 0
|
|
359
|
+
? `${item.activeCount} active`
|
|
360
|
+
: item.workerCount > 0
|
|
361
|
+
? `${item.workerCount} workers`
|
|
362
|
+
: item.present
|
|
363
|
+
? "documented"
|
|
364
|
+
: "waiting"}
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
))}
|
|
368
|
+
</div>
|
|
369
|
+
</section>
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
242
373
|
function ActivityIndicator({
|
|
243
374
|
snapshot,
|
|
244
375
|
connectionState,
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
FeatureSummary,
|
|
19
19
|
GoalCard,
|
|
20
20
|
HarnessSnapshot,
|
|
21
|
+
HarnessEvent,
|
|
21
22
|
HypothesisEntry,
|
|
22
23
|
IncidentEntry,
|
|
23
24
|
MeetingCadence,
|
|
@@ -37,6 +38,7 @@ import type {
|
|
|
37
38
|
TrackStatus,
|
|
38
39
|
WorkerDocEntry,
|
|
39
40
|
WorkerSnapshot,
|
|
41
|
+
CxxTodo,
|
|
40
42
|
} from "./types";
|
|
41
43
|
|
|
42
44
|
const SNAPSHOT_VERSION = "1.2.0";
|
|
@@ -224,6 +226,8 @@ function emptySnapshot(banner: ErrorBanner | null = null, rootDir?: string): Har
|
|
|
224
226
|
missions: [],
|
|
225
227
|
ownerHistory: [],
|
|
226
228
|
gotchas: [],
|
|
229
|
+
todos: [],
|
|
230
|
+
events: [],
|
|
227
231
|
};
|
|
228
232
|
}
|
|
229
233
|
|
|
@@ -1315,6 +1319,89 @@ function readGotchas(rootDir: string): GotchaEntry[] {
|
|
|
1315
1319
|
return results.sort((a, b) => a.id.localeCompare(b.id));
|
|
1316
1320
|
}
|
|
1317
1321
|
|
|
1322
|
+
function normalizeTodoStatus(raw: unknown): CxxTodo["status"] {
|
|
1323
|
+
const value = String(raw ?? "pending").toLowerCase();
|
|
1324
|
+
if (["pending", "active", "paused", "blocked", "done"].includes(value)) return value;
|
|
1325
|
+
return value;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
function readTodos(rootDir: string): CxxTodo[] {
|
|
1329
|
+
const todoPath = path.join(rootDir, ".harness", "todos", "state.json");
|
|
1330
|
+
const result = readJsonSafe<{
|
|
1331
|
+
owners?: Record<string, Array<{
|
|
1332
|
+
id?: string;
|
|
1333
|
+
owner?: string;
|
|
1334
|
+
title?: string;
|
|
1335
|
+
status?: string;
|
|
1336
|
+
priority?: number;
|
|
1337
|
+
kind?: string;
|
|
1338
|
+
mission_path?: string | null;
|
|
1339
|
+
missionPath?: string | null;
|
|
1340
|
+
required_artifacts?: string[];
|
|
1341
|
+
requiredArtifacts?: string[];
|
|
1342
|
+
created_at?: string | null;
|
|
1343
|
+
createdAt?: string | null;
|
|
1344
|
+
updated_at?: string | null;
|
|
1345
|
+
updatedAt?: string | null;
|
|
1346
|
+
last_heartbeat_at?: string | null;
|
|
1347
|
+
lastHeartbeatAt?: string | null;
|
|
1348
|
+
blocked_reason?: string | null;
|
|
1349
|
+
blockedReason?: string | null;
|
|
1350
|
+
}>>;
|
|
1351
|
+
}>(todoPath);
|
|
1352
|
+
if (!result.ok) return [];
|
|
1353
|
+
const todos: CxxTodo[] = [];
|
|
1354
|
+
for (const [owner, list] of Object.entries(result.value.owners ?? {})) {
|
|
1355
|
+
for (const item of list ?? []) {
|
|
1356
|
+
const id = item.id ?? `${owner}-${todos.length + 1}`;
|
|
1357
|
+
todos.push({
|
|
1358
|
+
id,
|
|
1359
|
+
owner: item.owner ?? owner,
|
|
1360
|
+
title: item.title ?? id,
|
|
1361
|
+
status: normalizeTodoStatus(item.status),
|
|
1362
|
+
priority: item.priority ?? 0,
|
|
1363
|
+
kind: item.kind ?? "task",
|
|
1364
|
+
missionPath: item.mission_path ?? item.missionPath ?? null,
|
|
1365
|
+
requiredArtifacts: item.required_artifacts ?? item.requiredArtifacts ?? [],
|
|
1366
|
+
createdAt: item.created_at ?? item.createdAt ?? null,
|
|
1367
|
+
updatedAt: item.updated_at ?? item.updatedAt ?? null,
|
|
1368
|
+
lastHeartbeatAt: item.last_heartbeat_at ?? item.lastHeartbeatAt ?? null,
|
|
1369
|
+
blockedReason: item.blocked_reason ?? item.blockedReason ?? null,
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
return todos.sort((a, b) => (b.priority - a.priority) || ((b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")));
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
function readJsonlTail<T extends Record<string, unknown>>(filePath: string, limit: number): T[] {
|
|
1377
|
+
if (!existsSync(filePath)) return [];
|
|
1378
|
+
let raw = "";
|
|
1379
|
+
try { raw = readFileSync(filePath, "utf8"); } catch { return []; }
|
|
1380
|
+
const rows: T[] = [];
|
|
1381
|
+
for (const line of raw.split("\n")) {
|
|
1382
|
+
const trimmed = line.trim();
|
|
1383
|
+
if (!trimmed) continue;
|
|
1384
|
+
try {
|
|
1385
|
+
rows.push(JSON.parse(trimmed) as T);
|
|
1386
|
+
} catch {
|
|
1387
|
+
// Ignore malformed legacy/manual lines in append-only logs.
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
return rows.slice(-limit).reverse();
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
function readEvents(rootDir: string, limit = 40): HarnessEvent[] {
|
|
1394
|
+
return readJsonlTail<Record<string, unknown>>(path.join(rootDir, ".harness", "events.jsonl"), limit).map((e) => ({
|
|
1395
|
+
ts: typeof e.ts === "string" ? e.ts : null,
|
|
1396
|
+
type: typeof e.type === "string" ? e.type : "event",
|
|
1397
|
+
owner: typeof e.owner === "string" ? e.owner : null,
|
|
1398
|
+
command: typeof e.command === "string" ? e.command : null,
|
|
1399
|
+
summary: typeof e.summary === "string" ? e.summary : null,
|
|
1400
|
+
title: typeof e.title === "string" ? e.title : null,
|
|
1401
|
+
status: typeof e.status === "string" ? e.status : null,
|
|
1402
|
+
}));
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1318
1405
|
export function readHarnessState(rootDir: string): HarnessSnapshot {
|
|
1319
1406
|
const harnessDir = path.join(rootDir, ".harness");
|
|
1320
1407
|
if (!existsSync(harnessDir)) {
|
|
@@ -1344,6 +1431,8 @@ export function readHarnessState(rootDir: string): HarnessSnapshot {
|
|
|
1344
1431
|
snapshot.missions = readMissions(rootDir, null);
|
|
1345
1432
|
snapshot.ownerHistory = readOwnerHistory(rootDir);
|
|
1346
1433
|
snapshot.gotchas = readGotchas(rootDir);
|
|
1434
|
+
snapshot.todos = readTodos(rootDir);
|
|
1435
|
+
snapshot.events = readEvents(rootDir);
|
|
1347
1436
|
return snapshot;
|
|
1348
1437
|
}
|
|
1349
1438
|
|
|
@@ -1396,6 +1485,8 @@ export function readHarnessState(rootDir: string): HarnessSnapshot {
|
|
|
1396
1485
|
missions: readMissions(rootDir, progress),
|
|
1397
1486
|
ownerHistory: readOwnerHistory(rootDir),
|
|
1398
1487
|
gotchas: readGotchas(rootDir),
|
|
1488
|
+
todos: readTodos(rootDir),
|
|
1489
|
+
events: readEvents(rootDir),
|
|
1399
1490
|
};
|
|
1400
1491
|
}
|
|
1401
1492
|
|
|
@@ -292,6 +292,33 @@ export interface HarnessSnapshot {
|
|
|
292
292
|
missions: MissionDoc[];
|
|
293
293
|
ownerHistory: OwnerPromptEntry[];
|
|
294
294
|
gotchas: GotchaEntry[];
|
|
295
|
+
todos: CxxTodo[];
|
|
296
|
+
events: HarnessEvent[];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export interface CxxTodo {
|
|
300
|
+
id: string;
|
|
301
|
+
owner: "ceo" | "coo" | "cdo" | "cto" | "cqo" | "ops" | string;
|
|
302
|
+
title: string;
|
|
303
|
+
status: "pending" | "active" | "paused" | "blocked" | "done" | string;
|
|
304
|
+
priority: number;
|
|
305
|
+
kind: string;
|
|
306
|
+
missionPath?: string | null;
|
|
307
|
+
requiredArtifacts: string[];
|
|
308
|
+
createdAt: string | null;
|
|
309
|
+
updatedAt: string | null;
|
|
310
|
+
lastHeartbeatAt?: string | null;
|
|
311
|
+
blockedReason?: string | null;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export interface HarnessEvent {
|
|
315
|
+
ts: string | null;
|
|
316
|
+
type: string;
|
|
317
|
+
owner?: string | null;
|
|
318
|
+
command?: string | null;
|
|
319
|
+
summary?: string | null;
|
|
320
|
+
title?: string | null;
|
|
321
|
+
status?: string | null;
|
|
295
322
|
}
|
|
296
323
|
|
|
297
324
|
// Real harness mission documents
|
|
@@ -121,6 +121,9 @@ Note: A docmeta skip decision on harness documents (ceo.md, cto.md, cqo.md, work
|
|
|
121
121
|
| `.harness/documents/goal-{index}-{name}/` | Goal-level CXX decisions and worker reports |
|
|
122
122
|
| `.harness/documents/goal-{index}-{name}/submission-{index}-{name}/` | Additional requirement under the active goal |
|
|
123
123
|
| `.harness/documents/goal-{index}-{name}/hotfix-{index}-{name}/` | Emergency fix under the active goal |
|
|
124
|
+
| `.harness/events.jsonl` | Structured append-only runtime event stream |
|
|
125
|
+
| `.harness/todos/state.json` | Machine-readable CXX todo queues |
|
|
126
|
+
| `.harness/todos/events.jsonl` | Structured todo transition history |
|
|
124
127
|
| `.harness/conventions/` | Durable rules (CQO writes, survives missions) |
|
|
125
128
|
| `.harness/gotchas/` | Recurrence-prevention records (CQO writes per hot-fix) |
|
|
126
129
|
| `.harness/memories/` | Long-term shared context |
|
|
@@ -144,10 +147,12 @@ Note: A docmeta skip decision on harness documents (ceo.md, cto.md, cqo.md, work
|
|
|
144
147
|
8. **No CXX self-execution** — CXX agents coordinate and manage only. A CXX that produces deliverables without matching worker records has violated its scope. CEO must reject such reports.
|
|
145
148
|
9. **No verdict without worker evidence** — CQO cannot issue ACCEPTED/REJECTED without a Worker Evidence Manifest referencing at least one evaluator worker. Self-inspection by CQO is not valid evidence.
|
|
146
149
|
10. **Hierarchical worker ownership** — Hired workers are installed under `.claude/skills/{owning-cxx}/{worker}/` and `.codex/skills/{owning-cxx}/{worker}/`; mission worker reports live under `.harness/documents/{goal-or-child-mission}/{owning-cxx}/workers/`. Flat `{mission}/workers/` reports are legacy and signal an ownership violation unless explicitly migrated.
|
|
147
|
-
11. **
|
|
148
|
-
12. **
|
|
149
|
-
13. **
|
|
150
|
-
14. **
|
|
150
|
+
11. **Lazy convention/gotcha loading** — CXX roles read `.harness/conventions/shared.md`, `.harness/conventions/{cxx}.md`, `.harness/gotchas/shared.md`, and `.harness/gotchas/{cxx}.md`, then follow only the related topic links listed in those CXX files. Workers read only the related links supplied by their owning CXX.
|
|
151
|
+
12. **Implementation Notes required** — `ceo.md`, every `{cxx}.md`, and every worker report must end with one English `## Implementation Notes` section containing `Design Decisions`, `Deviations`, `Tradeoffs`, and `Open Questions`. Use `None` for empty subsections. Do not create a separate sidecar notes file; the notes belong at the bottom of the same role or worker report that produced the decision/evidence.
|
|
152
|
+
13. **Structured runtime state** — If the harness must parse it, record it in JSON or JSONL. CXX todo queues, event history, heartbeat timestamps, preemption/resume state, and completion evidence belong in `.harness/todos/*.json*` or `.harness/events.jsonl`, not in free-form Markdown tables. Markdown remains for instructions, conventions, gotchas, skills, and human-readable mission narrative.
|
|
153
|
+
14. **Owner is final acceptance only** — Owner is not a tester or QA substitute. CEO/CXX must not report "done, please check" until worker-backed verification proves the goal can be completed. Use unit tests, E2E, Playwright, test accounts, seeded data, build/run checks, logs, and CQO evidence before requesting Owner acceptance.
|
|
154
|
+
15. **OPS watches CQO verification** — For runnable products, CQO PASS is invalid unless OPS has monitored the verification runtime or documented why OPS watch is not applicable. Open OPS incidents, missing runtime mapping, required log gaps, service down, health mismatch, or an unmonitored tested service block Owner acceptance.
|
|
155
|
+
16. **Production incidents are company events** — After launch, user-impacting OPS signals route through CEO to CTO/CQO/OPS. CTO owns recovery, CQO owns regression confirmation, and OPS owns evidence plus close criteria.
|
|
151
156
|
|
|
152
157
|
---
|
|
153
158
|
|
|
@@ -51,11 +51,16 @@ Owner is the final acceptance reviewer, not a tester, QA substitute, debugger, o
|
|
|
51
51
|
| `.harness/shared/HR-Resource/` | Hireable worker skill pool |
|
|
52
52
|
| `.harness/shared/hr-roster.json` | Hired worker roster |
|
|
53
53
|
| `.harness/archive/` | CQO-approved completed mission records |
|
|
54
|
-
| `.harness/
|
|
54
|
+
| `.harness/events.jsonl` | Structured append-only runtime event stream |
|
|
55
|
+
| `.harness/todos/state.json` | Machine-readable CXX todo queues |
|
|
56
|
+
| `.harness/todos/events.jsonl` | Structured todo transition history |
|
|
57
|
+
| `.harness/progress.log` | Legacy human-readable command and status history |
|
|
55
58
|
| `.harness/progress.json` | Machine-readable current runtime state |
|
|
56
59
|
|
|
57
60
|
Flat `.harness/documents/{mission}/workers/` reports are legacy. New worker evidence must be under `{owning-cxx}/workers/`.
|
|
58
61
|
|
|
62
|
+
Runtime state that the harness must parse belongs in JSON or JSONL, not Markdown. Markdown remains valid for instructions, skills, conventions, gotchas, and human-facing mission narrative, but queues, events, heartbeats, preemption/resume state, and completion evidence must be machine-readable.
|
|
63
|
+
|
|
59
64
|
## Required Flow
|
|
60
65
|
|
|
61
66
|
### Goal
|
|
@@ -262,7 +262,7 @@
|
|
|
262
262
|
"actions": ".harness/actions",
|
|
263
263
|
"archive": ".harness/archive",
|
|
264
264
|
"gotchas": ".harness/gotchas",
|
|
265
|
-
"conventions": "
|
|
265
|
+
"conventions": ".harness/conventions",
|
|
266
266
|
"progress_state": ".harness/progress.json",
|
|
267
267
|
"progress_log": ".harness/progress.log"
|
|
268
268
|
},
|
|
@@ -284,16 +284,18 @@
|
|
|
284
284
|
"statuses": ["start", "complete", "fail", "pass", "skip", "warn"]
|
|
285
285
|
},
|
|
286
286
|
"conventions": {
|
|
287
|
-
"comment": "House-style registry.
|
|
287
|
+
"comment": "House-style registry. CXX files act as lazy-loading indexes that link to topic-specific convention/gotcha documents.",
|
|
288
288
|
"root_file": "CONVENTIONS.md",
|
|
289
289
|
"scoped_dir": ".harness/conventions/",
|
|
290
|
-
"
|
|
290
|
+
"gotcha_scoped_dir": ".harness/gotchas/",
|
|
291
|
+
"cxx_scopes": ["ceo", "coo", "cdo", "cto", "cqo", "ops", "hiring", "resource-manager", "brick-office"],
|
|
291
292
|
"entry_id_prefix": "C",
|
|
292
|
-
"read_by": "
|
|
293
|
-
"
|
|
294
|
-
"
|
|
295
|
-
"
|
|
296
|
-
"
|
|
293
|
+
"read_by": "CEO, CXX, and hired workers",
|
|
294
|
+
"cxx_read_order": [".harness/conventions/shared.md", ".harness/conventions/<cxx>.md", "follow relevant links in <cxx>.md", ".harness/gotchas/shared.md", ".harness/gotchas/<cxx>.md", "follow relevant links in <cxx>.md", ".harness/memory.md"],
|
|
295
|
+
"worker_read_order": ["links explicitly provided by owning CXX in the worker brief"],
|
|
296
|
+
"conflict_priority": "linked topic > owning-cxx > shared > memory > root",
|
|
297
|
+
"write_by": "CQO/gotcha-register, CXX, or Owner-approved manual edit",
|
|
298
|
+
"read_timing": "before each role or worker starts mission work",
|
|
297
299
|
"preserve_on_postinstall": true
|
|
298
300
|
},
|
|
299
301
|
"recommended_skills": {
|