@walwal-harness/cli 7.1.27 → 7.1.29

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.
@@ -20,6 +20,8 @@
20
20
  "next": "^15.0.0",
21
21
  "react": "^19.0.0",
22
22
  "react-dom": "^19.0.0",
23
+ "react-markdown": "^10.1.0",
24
+ "remark-gfm": "^4.0.1",
23
25
  "three": "^0.184.0"
24
26
  },
25
27
  "devDependencies": {
@@ -88,7 +88,7 @@ The Owner is the final acceptance reviewer, not a tester, QA worker, debugger, o
88
88
  4. CEO aggregates CXX outputs, routes to next CXX or reports to Owner.
89
89
  5. If the mission uses a runnable local, preview, Docker, or production-like environment, OPS watches the runtime while CQO evaluator workers run Playwright/E2E/API/visual/performance/regression checks.
90
90
 
91
- **Complete when:** `.harness/documents/goal-{index}-{name}/ceo.md` contains a final Owner report.
91
+ **Complete when:** `.harness/documents/goal-{index}-{name}/ceo.md` contains a final Owner report and `mission-state.json` is terminal (`complete`, `closed`, `cancelled`, or `superseded`) with `active:false`.
92
92
 
93
93
  ### Submission
94
94
 
@@ -98,7 +98,7 @@ The Owner is the final acceptance reviewer, not a tester, QA worker, debugger, o
98
98
  4. Relevant CXX update `.harness/conventions/` when the new requirement changes durable project rules.
99
99
  5. Workers report under `{owning-cxx}/workers/`; CXX summarize choices in `{cxx}.md`.
100
100
 
101
- **Complete when:** the submission child directory has CEO/CXX records, required worker evidence, and any convention updates required by the new requirement.
101
+ **Complete when:** the submission child directory has CEO/CXX records, required worker evidence, any convention updates required by the new requirement, and terminal `mission-state.json`.
102
102
 
103
103
  ### Hot Fix
104
104
 
@@ -150,9 +150,10 @@ Note: A docmeta skip decision on harness documents (ceo.md, cto.md, cqo.md, work
150
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
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
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.
153
+ 14. **Mission lifecycle is explicit** — Every goal, submission, and hot-fix directory must contain `mission-state.json` with `lifecycle` and `active`. Only one child mission under a goal may be active. Starting a newer submission/hot-fix closes, cancels, or supersedes the previous active child unless CEO records a deliberate TODO/resume plan.
154
+ 15. **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.
155
+ 16. **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.
156
+ 17. **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.
156
157
 
157
158
  ---
158
159
 
package/bin/init.js CHANGED
@@ -112,6 +112,22 @@ function chmodShellScripts(dir) {
112
112
  }
113
113
  }
114
114
 
115
+ function readJsonFileSafe(filePath) {
116
+ try {
117
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
118
+ } catch {
119
+ return null;
120
+ }
121
+ }
122
+
123
+ function readTextFileSafe(filePath) {
124
+ try {
125
+ return fs.readFileSync(filePath, 'utf8');
126
+ } catch {
127
+ return null;
128
+ }
129
+ }
130
+
115
131
  function fileExists(p) {
116
132
  return fs.existsSync(p);
117
133
  }
@@ -451,6 +467,99 @@ function migrateExistingDocs() {
451
467
  log(`Migration report: ${reportPath}`);
452
468
  }
453
469
 
470
+ function missionTypeFromDirName(name) {
471
+ if (/^goal[-_]/i.test(name)) return 'goal';
472
+ if (/^submission[-_]/i.test(name)) return 'submission';
473
+ if (/^hot[-_]?fix[-_]/i.test(name) || /^hotfix[-_]/i.test(name)) return 'hotfix';
474
+ return 'unknown';
475
+ }
476
+
477
+ function isTerminalMissionLifecycle(value) {
478
+ return ['closed', 'cancelled', 'superseded', 'complete', 'blocked'].includes(String(value || '').toLowerCase());
479
+ }
480
+
481
+ function collectMissionDirs(docsDir) {
482
+ const cxxDocs = new Set(['ceo.md', 'coo.md', 'cdo.md', 'cto.md', 'cqo.md', 'ops.md']);
483
+ const result = [];
484
+ const walk = (dir, rel = '') => {
485
+ if (!fs.existsSync(dir)) return;
486
+ let entries = [];
487
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
488
+ const hasMissionDoc = entries.some((entry) => entry.isFile() && cxxDocs.has(entry.name));
489
+ if (rel && hasMissionDoc) {
490
+ let mtime = 0;
491
+ try { mtime = fs.statSync(dir).mtimeMs; } catch {}
492
+ const name = path.basename(rel);
493
+ result.push({ abs: dir, rel, name, type: missionTypeFromDirName(name), mtime });
494
+ }
495
+ for (const entry of entries) {
496
+ if (!entry.isDirectory() || entry.name.startsWith('.')) continue;
497
+ if (['workers', 'coo', 'cdo', 'cto', 'cqo', 'ops'].includes(entry.name)) continue;
498
+ walk(path.join(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
499
+ }
500
+ };
501
+ walk(docsDir);
502
+ return result;
503
+ }
504
+
505
+ function ensureMissionLifecycleStates(opts = {}) {
506
+ const dryRun = opts.dryRun || false;
507
+ const backupDir = opts.backupDir || null;
508
+ const docsDir = path.join(HARNESS_DIR, 'documents');
509
+ if (!fs.existsSync(docsDir)) return { missing: 0, written: 0 };
510
+
511
+ const missions = collectMissionDirs(docsDir);
512
+ if (!missions.length) return { missing: 0, written: 0 };
513
+
514
+ const byGoal = new Map();
515
+ for (const mission of missions) {
516
+ const goalKey = mission.rel.split('/')[0];
517
+ if (!byGoal.has(goalKey)) byGoal.set(goalKey, []);
518
+ byGoal.get(goalKey).push(mission);
519
+ }
520
+
521
+ const writes = [];
522
+ for (const group of byGoal.values()) {
523
+ const children = group.filter((m) => m.rel.includes('/') && (m.type === 'submission' || m.type === 'hotfix'));
524
+ children.sort((a, b) => b.mtime - a.mtime);
525
+ const existingActiveChild = children.find((m) => {
526
+ const state = readJsonFileSafe(path.join(m.abs, 'mission-state.json'));
527
+ return state?.active === true && !isTerminalMissionLifecycle(state.lifecycle || state.status);
528
+ });
529
+ const activeChild = existingActiveChild || children[0] || null;
530
+
531
+ for (const mission of group) {
532
+ const statePath = path.join(mission.abs, 'mission-state.json');
533
+ if (fs.existsSync(statePath)) continue;
534
+ const isChild = mission.rel.includes('/');
535
+ const isActive = isChild
536
+ ? activeChild?.rel === mission.rel
537
+ : true;
538
+ writes.push({
539
+ path: statePath,
540
+ state: {
541
+ lifecycle: isActive ? 'active' : 'closed',
542
+ active: isActive,
543
+ },
544
+ });
545
+ }
546
+ }
547
+
548
+ if (dryRun) return { missing: writes.length, written: 0 };
549
+
550
+ for (const item of writes) {
551
+ if (backupDir) {
552
+ const rel = path.relative(HARNESS_DIR, item.path).replace(/[\\/]/g, '__');
553
+ if (fs.existsSync(item.path)) fs.writeFileSync(path.join(backupDir, rel), fs.readFileSync(item.path, 'utf8'));
554
+ }
555
+ fs.writeFileSync(item.path, JSON.stringify(item.state, null, 2) + '\n');
556
+ }
557
+ if (writes.length) {
558
+ log(`mission-state.json: ${writes.length}개 기존 mission에 lifecycle 보강`);
559
+ }
560
+ return { missing: writes.length, written: writes.length };
561
+ }
562
+
454
563
  // ─────────────────────────────────────────
455
564
  // 1. .harness/ scaffolding
456
565
  // ─────────────────────────────────────────
@@ -585,6 +694,7 @@ function scaffoldHarness() {
585
694
  if (!fileExists(p) || isForce) fs.writeFileSync(p, '');
586
695
  }
587
696
  ensureStructuredRuntimeFiles();
697
+ ensureMissionLifecycleStates();
588
698
 
589
699
  // Migrate progress.json v1 → v2 (add mode + team_state fields)
590
700
  const progressPath = path.join(HARNESS_DIR, 'progress.json');
@@ -1094,55 +1204,129 @@ function installAgentTeamsEnv() {
1094
1204
  }
1095
1205
  }
1096
1206
 
1097
- function setupAgentsMd() {
1207
+ const HARNESS_HEADER_MARKER = '<!-- walwal-harness:managed-header -->';
1208
+ const HARNESS_SECTION_START = '<!-- walwal-harness:agents:start -->';
1209
+ const HARNESS_SECTION_END = '<!-- walwal-harness:agents:end -->';
1210
+ const PRESERVED_CLAUDE_START = '<!-- walwal-harness:preserved-claude:start -->';
1211
+ const PRESERVED_CLAUDE_END = '<!-- walwal-harness:preserved-claude:end -->';
1212
+
1213
+ function backupDocPath(filePath, backupDir, label) {
1214
+ if (!filePath || !fs.existsSync(filePath)) return;
1215
+ ensureDir(backupDir);
1216
+ const safe = label.replace(/[^A-Za-z0-9_.-]/g, '_');
1217
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
1218
+ try {
1219
+ fs.copyFileSync(filePath, path.join(backupDir, `${safe}.${ts}.bak`));
1220
+ } catch {}
1221
+ }
1222
+
1223
+ function renderHarnessAgentsSection() {
1224
+ const templateSrc = path.join(PKG_ROOT, 'assets', 'templates', 'AGENTS.md.template');
1225
+ let content = fs.existsSync(templateSrc)
1226
+ ? fs.readFileSync(templateSrc, 'utf8')
1227
+ : '# walwal-harness\n\nUse `/goal`, `/submission`, and `/hot-fix` for CEO/CXX/worker flow.\n';
1228
+ content = content.replace(/\{\{DATE\}\}/g, new Date().toISOString().split('T')[0]);
1229
+ content = content.replace(/\{\{DATE_ISO\}\}/g, new Date().toISOString());
1230
+ return [
1231
+ HARNESS_SECTION_START,
1232
+ '',
1233
+ content.trim(),
1234
+ '',
1235
+ HARNESS_SECTION_END,
1236
+ ''
1237
+ ].join('\n');
1238
+ }
1239
+
1240
+ function ensureTopHarnessHeader(body) {
1241
+ if (body.includes(HARNESS_HEADER_MARKER)) return body;
1242
+ const header = [
1243
+ HARNESS_HEADER_MARKER,
1244
+ '> This project is walwal-harness enabled. Harness operating rules live in the marked walwal-harness section below.',
1245
+ ''
1246
+ ].join('\n');
1247
+ return header + body.replace(/^\s+/, '');
1248
+ }
1249
+
1250
+ function upsertMarkedBlock(body, start, end, block) {
1251
+ const startIdx = body.indexOf(start);
1252
+ const endIdx = body.indexOf(end);
1253
+ if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
1254
+ const afterEnd = endIdx + end.length;
1255
+ return body.slice(0, startIdx).replace(/\s*$/, '\n\n') + block.trim() + '\n' + body.slice(afterEnd).replace(/^\s*/, '\n');
1256
+ }
1257
+ const sep = body.trim() ? '\n\n' : '';
1258
+ return body.replace(/\s*$/, '') + sep + block.trim() + '\n';
1259
+ }
1260
+
1261
+ function normalizeDocBody(body) {
1262
+ return String(body || '').replace(/\r\n/g, '\n').trim();
1263
+ }
1264
+
1265
+ function ensureProjectAgentDocs(opts = {}) {
1266
+ const dryRun = opts.dryRun || false;
1267
+ const backupDir = opts.backupDir || path.join(HARNESS_DIR, 'archive', 'pre-harness-backup');
1098
1268
  const agentsMd = path.join(PROJECT_ROOT, 'AGENTS.md');
1099
1269
  const claudeMd = path.join(PROJECT_ROOT, 'CLAUDE.md');
1100
1270
 
1101
- // Run scan if no AGENTS.md
1102
- if (!fileExists(agentsMd) || isForce) {
1103
- log('Running project scan...');
1104
- try {
1105
- execSync(`bash "${path.join(PKG_ROOT, 'scripts', 'scan-project.sh')}" "${PROJECT_ROOT}"`, {
1106
- stdio: 'inherit'
1107
- });
1108
- execSync(`bash "${path.join(PKG_ROOT, 'scripts', 'init-agents-md.sh')}" "${PROJECT_ROOT}"`, {
1109
- stdio: 'inherit'
1110
- });
1111
- } catch (e) {
1112
- log('WARNING: Auto-scan failed. Run manually: bash scripts/scan-project.sh .');
1113
- // Create minimal AGENTS.md
1114
- const templateSrc = path.join(PKG_ROOT, 'assets', 'templates', 'AGENTS.md.template');
1115
- if (fs.existsSync(templateSrc)) {
1116
- let content = fs.readFileSync(templateSrc, 'utf8');
1117
- content = content.replace(/\{\{DATE\}\}/g, new Date().toISOString().split('T')[0]);
1118
- fs.writeFileSync(agentsMd, content);
1119
- }
1120
- }
1271
+ const agentsTarget = fs.existsSync(agentsMd) ? fs.realpathSync(agentsMd) : agentsMd;
1272
+ const existingAgents = readTextFileSafe(agentsMd) || '';
1273
+ const claudeExists = fs.existsSync(claudeMd) || fs.existsSync(claudeMd);
1274
+ const claudeIsSymlink = fs.existsSync(claudeMd) && fs.lstatSync(claudeMd).isSymbolicLink();
1275
+ const claudeTarget = claudeExists ? fs.realpathSync(claudeMd) : null;
1276
+ const claudeBody = claudeExists ? (readTextFileSafe(claudeMd) || '') : '';
1277
+
1278
+ let nextAgents = existingAgents;
1279
+ nextAgents = ensureTopHarnessHeader(nextAgents);
1280
+ nextAgents = upsertMarkedBlock(nextAgents, HARNESS_SECTION_START, HARNESS_SECTION_END, renderHarnessAgentsSection());
1281
+
1282
+ const normalizedClaude = normalizeDocBody(claudeBody);
1283
+ const claudePointsAtAgents = claudeIsSymlink && claudeTarget === agentsTarget;
1284
+ if (normalizedClaude && !claudePointsAtAgents) {
1285
+ const preservedBlock = [
1286
+ PRESERVED_CLAUDE_START,
1287
+ '',
1288
+ '## Preserved CLAUDE.md',
1289
+ '',
1290
+ normalizedClaude,
1291
+ '',
1292
+ PRESERVED_CLAUDE_END,
1293
+ ''
1294
+ ].join('\n');
1295
+ nextAgents = upsertMarkedBlock(nextAgents, PRESERVED_CLAUDE_START, PRESERVED_CLAUDE_END, preservedBlock);
1121
1296
  }
1122
1297
 
1123
- // Ensure CLAUDE.md symlink
1124
- if (fileExists(agentsMd)) {
1125
- try {
1126
- const stat = fs.lstatSync(claudeMd);
1127
- if (!stat.isSymbolicLink()) {
1128
- // Backup existing CLAUDE.md
1129
- const backupDir = path.join(HARNESS_DIR, 'archive', 'pre-harness-backup');
1130
- ensureDir(backupDir);
1131
- fs.copyFileSync(claudeMd, path.join(backupDir, `CLAUDE.md.${Date.now()}.bak`));
1132
- fs.unlinkSync(claudeMd);
1133
- fs.symlinkSync('AGENTS.md', claudeMd);
1134
- log('CLAUDE.md backed up and replaced with symlink → AGENTS.md');
1135
- }
1136
- } catch (e) {
1137
- // CLAUDE.md doesn't exist
1138
- try {
1139
- fs.symlinkSync('AGENTS.md', claudeMd);
1140
- log('Created symlink: CLAUDE.md → AGENTS.md');
1141
- } catch (e2) {}
1142
- }
1298
+ if (dryRun) {
1299
+ log(' (dry-run) AGENTS.md: walwal-harness marked section merge 예정');
1300
+ if (!claudePointsAtAgents) log(' (dry-run) CLAUDE.md: content preserved then symlink → AGENTS.md 예정');
1301
+ return;
1302
+ }
1303
+
1304
+ if (fs.existsSync(agentsTarget)) backupDocPath(agentsTarget, backupDir, 'AGENTS.md');
1305
+ if (claudeExists && claudeTarget && claudeTarget !== agentsTarget) {
1306
+ backupDocPath(claudeTarget, backupDir, 'CLAUDE.md');
1307
+ }
1308
+
1309
+ ensureDir(path.dirname(agentsMd));
1310
+ fs.writeFileSync(agentsMd, nextAgents.replace(/\s*$/, '\n'));
1311
+ log(fs.existsSync(agentsTarget) ? 'AGENTS.md merged with walwal-harness marked section' : 'AGENTS.md created from walwal-harness template');
1312
+
1313
+ try {
1314
+ if (fs.existsSync(claudeMd) || fs.lstatSync(claudeMd)) fs.unlinkSync(claudeMd);
1315
+ } catch {}
1316
+ try {
1317
+ fs.symlinkSync('AGENTS.md', claudeMd);
1318
+ log('CLAUDE.md replaced with symlink → AGENTS.md');
1319
+ } catch (e) {
1320
+ log(`WARNING: Could not create CLAUDE.md symlink (${e.message})`);
1143
1321
  }
1144
1322
  }
1145
1323
 
1324
+ function setupAgentsMd() {
1325
+ const agentsMd = path.join(PROJECT_ROOT, 'AGENTS.md');
1326
+ const claudeMd = path.join(PROJECT_ROOT, 'CLAUDE.md');
1327
+ ensureProjectAgentDocs({ backupDir: path.join(HARNESS_DIR, 'archive', 'pre-harness-backup') });
1328
+ }
1329
+
1146
1330
  // ─────────────────────────────────────────
1147
1331
  // 5. Playwright MCP check
1148
1332
  // ─────────────────────────────────────────
@@ -1300,6 +1484,8 @@ function detectMigrationNeeded() {
1300
1484
  harnessMdStale: false,
1301
1485
  agentsMissingOpsVerificationRules: false,
1302
1486
  agentsMissingCodexAdapterRules: false,
1487
+ agentsMissingMissionLifecycleRules: false,
1488
+ missionLifecycleMissing: false,
1303
1489
  rosterCodexPathsMissing: false,
1304
1490
  resourceIndexCodexWordingMissing: false,
1305
1491
  memoryMissingSystemEntries: [],
@@ -1427,8 +1613,19 @@ function detectMigrationNeeded() {
1427
1613
  ) {
1428
1614
  flags.agentsMissingCodexAdapterRules = true;
1429
1615
  }
1616
+ if (
1617
+ !agentsBody.includes('Mission Lifecycle Rules') &&
1618
+ !agentsBody.includes('mission-state.json')
1619
+ ) {
1620
+ flags.agentsMissingMissionLifecycleRules = true;
1621
+ }
1430
1622
  } catch {}
1431
1623
  }
1624
+ const docsDir = path.join(HARNESS_DIR, 'documents');
1625
+ if (fs.existsSync(docsDir)) {
1626
+ flags.missionLifecycleMissing = collectMissionDirs(docsDir)
1627
+ .some((mission) => !fs.existsSync(path.join(mission.abs, 'mission-state.json')));
1628
+ }
1432
1629
  if (fs.existsSync(rosterPath)) {
1433
1630
  try {
1434
1631
  const roster = JSON.parse(fs.readFileSync(rosterPath, 'utf8'));
@@ -1764,6 +1961,8 @@ function runMigrate(opts = {}) {
1764
1961
  !flags.agentsMissingOpsVerificationRules &&
1765
1962
  !flags.rosterCodexPathsMissing &&
1766
1963
  !flags.resourceIndexCodexWordingMissing &&
1964
+ !flags.agentsMissingMissionLifecycleRules &&
1965
+ !flags.missionLifecycleMissing &&
1767
1966
  (!flags.memoryMissingSystemEntries || flags.memoryMissingSystemEntries.length === 0) &&
1768
1967
  gotchaMissingTotal === 0 &&
1769
1968
  conventionMissingTotal === 0 &&
@@ -1785,6 +1984,14 @@ function runMigrate(opts = {}) {
1785
1984
  const backupDir = path.join(HARNESS_DIR, 'archive', `migration-${ts}`);
1786
1985
  if (!dryRun) ensureDir(backupDir);
1787
1986
  ensureStructuredRuntimeFiles({ dryRun });
1987
+ ensureProjectAgentDocs({ dryRun, backupDir });
1988
+ flags.agentsMissingOpsVerificationRules = false;
1989
+ flags.agentsMissingCodexAdapterRules = false;
1990
+ flags.agentsMissingMissionLifecycleRules = false;
1991
+ const missionStateResult = ensureMissionLifecycleStates({ dryRun, backupDir });
1992
+ if (dryRun && missionStateResult.missing > 0) {
1993
+ log(` (dry-run) mission-state.json: ${missionStateResult.missing}개 기존 mission에 lifecycle 보강 예정`);
1994
+ }
1788
1995
 
1789
1996
  // Runtime scripts are package-owned. Migrate must refresh them so existing
1790
1997
  // projects receive wake/meeting/OPS fixes without requiring a full init.
@@ -2099,6 +2306,31 @@ function runMigrate(opts = {}) {
2099
2306
  }
2100
2307
  }
2101
2308
 
2309
+ if (flags.agentsMissingMissionLifecycleRules) {
2310
+ const agentsPath = path.join(PROJECT_ROOT, 'AGENTS.md');
2311
+ if (fs.existsSync(agentsPath)) {
2312
+ const original = fs.readFileSync(agentsPath, 'utf8');
2313
+ const block = [
2314
+ '',
2315
+ '---',
2316
+ '',
2317
+ '## walwal-harness Mission Lifecycle Rules — v7.1.28',
2318
+ '',
2319
+ '- Every goal, submission, and hot-fix directory must contain `mission-state.json` with `lifecycle` and `active`.',
2320
+ '- Only one child mission under a goal may be active. Starting a newer submission/hot-fix closes, cancels, or supersedes the previous active child unless CEO records a deliberate TODO/resume plan.',
2321
+ '- CXX completion requires a Worker Evidence Manifest plus worker report paths under `{owning-cxx}/workers/`. A CXX report without hired worker evidence is a protocol violation.',
2322
+ '- Worker execution should be shown as worker activity; CXX activity is coordination only.',
2323
+ ''
2324
+ ].join('\n');
2325
+ log(' AGENTS.md: mission lifecycle / CXX hire-only rules append');
2326
+ if (!dryRun) {
2327
+ fs.writeFileSync(path.join(backupDir, 'AGENTS.mission-lifecycle.md'), original);
2328
+ const sep = original.endsWith('\n') ? '' : '\n';
2329
+ fs.writeFileSync(agentsPath, original + sep + block);
2330
+ }
2331
+ }
2332
+ }
2333
+
2102
2334
  // 7. Bundle version stamp — record which package version was last applied.
2103
2335
  if (flags.bundleVersionStale) {
2104
2336
  const { current, installed } = flags.bundleVersionStale;
package/commands/goal.md CHANGED
@@ -14,14 +14,16 @@ Codex adapter:
14
14
 
15
15
  Required flow:
16
16
  1. Create or update a goal under `.harness/documents/goal-{goal_index}-{goal_name}/`.
17
- 2. Record CEO decisions in `.harness/documents/goal-{goal_index}-{goal_name}/ceo.md`.
18
- 3. CEO decides whether brainstorming is needed or whether CXX questions can be issued immediately.
19
- 4. CEO asks COO, CDO, CTO, and CQO only the questions needed for this goal.
20
- 5. CXX roles must start in a fresh session context. Do not let the default model impersonate a missing worker.
21
- 6. CXX roles must not directly execute specialist deliverables. They must use hired workers for research, planning, design, implementation, QA, ops checks, and documentation.
22
- 7. If a required CXX or worker skill is missing, CEO must invoke the installed `harness-hiring` skill before assigning the work.
23
- 8. CEO must require worker report paths under `.harness/documents/goal-{goal_index}-{goal_name}/{owning-cxx}/workers/` before accepting CXX completion.
24
- 9. Do not invoke internal roles through slash commands; commands are Owner entrypoints only.
17
+ 2. Write `.harness/documents/goal-{goal_index}-{goal_name}/mission-state.json` with `{"lifecycle":"active","active":true}` before routing CXX.
18
+ 3. Record CEO decisions in `.harness/documents/goal-{goal_index}-{goal_name}/ceo.md`.
19
+ 4. CEO decides whether brainstorming is needed or whether CXX questions can be issued immediately.
20
+ 5. CEO asks COO, CDO, CTO, and CQO only the questions needed for this goal.
21
+ 6. CXX roles must start in a fresh session context. Do not let the default model impersonate a missing worker.
22
+ 7. CXX roles must not directly execute specialist deliverables. They must use hired workers for research, planning, design, implementation, QA, ops checks, and documentation.
23
+ 8. If a required CXX or worker skill is missing, CEO must invoke the installed `harness-hiring` skill before assigning the work.
24
+ 9. CEO must require a Worker Evidence Manifest and worker report paths under `.harness/documents/goal-{goal_index}-{goal_name}/{owning-cxx}/workers/` before accepting CXX completion.
25
+ 10. When the goal is accepted, cancelled, superseded, blocked, or closed, update `mission-state.json` to `complete`, `cancelled`, `superseded`, `blocked`, or `closed` and set `active:false`.
26
+ 11. Do not invoke internal roles through slash commands; commands are Owner entrypoints only.
25
27
 
26
28
  Note: A goal is the company's objective. Submissions and hot-fixes that happen while pursuing it should be recorded under that goal directory.
27
29
 
@@ -14,16 +14,18 @@ Codex adapter:
14
14
 
15
15
  Required flow:
16
16
  1. Locate the active goal document root under `.harness/documents/{goal_name}/`. If no active goal exists, CEO must create/select one before proceeding.
17
- 2. Create `.harness/documents/{goal_name}/hotfix-{hotfix_index}-{hotfix_name}/ceo.md`.
18
- 3. CEO summons CTO and CQO first; summon COO/CDO only when planning or UX decisions are involved.
19
- 4. CTO applies the smallest correct patch path through hired implementation skills.
20
- 5. CXX roles must start in a fresh session context. Do not let the default model impersonate a missing CXX or worker.
21
- 6. CXX roles must not directly execute specialist deliverables. They must use hired workers for implementation, QA, ops checks, and documentation even when the fix is small.
22
- 7. If a required worker is missing, CEO or the responsible CXX must invoke the installed `harness-hiring` skill before assigning the work.
23
- 8. CEO must require worker report paths under `.harness/documents/{goal_name}/hotfix-{hotfix_index}-{hotfix_name}/{owning-cxx}/workers/` before accepting CXX completion.
24
- 9. CQO must register durable lessons in `.harness/gotchas/`, `.harness/conventions/`, or `.harness/memories/`. This step is mandatory, not optional, even for small fixes.
25
- 10. Archive only after CQO has accepted the fix.
26
- 11. Do not invoke internal roles through slash commands; commands are Owner entrypoints only.
17
+ 2. If another child mission under the goal is active, update its `mission-state.json` to `closed`, `cancelled`, or `superseded` with `active:false` before starting the hot-fix.
18
+ 3. Create `.harness/documents/{goal_name}/hotfix-{hotfix_index}-{hotfix_name}/ceo.md`.
19
+ 4. Write `.harness/documents/{goal_name}/hotfix-{hotfix_index}-{hotfix_name}/mission-state.json` with `{"lifecycle":"active","active":true}`.
20
+ 5. CEO summons CTO and CQO first; summon COO/CDO only when planning or UX decisions are involved.
21
+ 6. CTO applies the smallest correct patch path through hired implementation skills.
22
+ 7. CXX roles must start in a fresh session context. Do not let the default model impersonate a missing CXX or worker.
23
+ 8. CXX roles must not directly execute specialist deliverables. They must use hired workers for implementation, QA, ops checks, and documentation even when the fix is small.
24
+ 9. If a required worker is missing, CEO or the responsible CXX must invoke the installed `harness-hiring` skill before assigning the work.
25
+ 10. CEO must require a Worker Evidence Manifest and worker report paths under `.harness/documents/{goal_name}/hotfix-{hotfix_index}-{hotfix_name}/{owning-cxx}/workers/` before accepting CXX completion.
26
+ 11. CQO must register durable lessons in `.harness/gotchas/`, `.harness/conventions/`, or `.harness/memories/`. This step is mandatory, not optional, even for small fixes.
27
+ 12. Archive only after CQO has accepted the fix, then update `mission-state.json` to `complete` with `active:false`.
28
+ 13. Do not invoke internal roles through slash commands; commands are Owner entrypoints only.
27
29
 
28
30
  Note: `/hot-fix` is a problem-fix flow while pursuing the active goal. It belongs under that goal in history.
29
31
 
@@ -14,15 +14,18 @@ Codex adapter:
14
14
 
15
15
  Required flow:
16
16
  1. Locate the active goal document root under `.harness/documents/{goal_name}/`. If no active goal exists, CEO must ask the Owner to create or select one with `/goal` before proceeding.
17
- 2. Create a submission record under `.harness/documents/{goal_name}/submission-{submission_index}-{submission_name}/`.
18
- 3. Record CEO decisions in `.harness/documents/{goal_name}/submission-{submission_index}-{submission_name}/ceo.md`.
19
- 4. CEO routes only to the CXX agents needed for this additional requirement.
20
- 5. CXX roles must start in a fresh session context. Do not let the default model impersonate a missing CXX or worker.
21
- 6. CXX roles must not directly execute specialist deliverables. They must use hired workers for research, planning, design, implementation, QA, ops checks, and documentation.
22
- 7. If a required worker is missing, the responsible CXX must invoke the installed `harness-hiring` skill before assigning the work.
23
- 8. CXX must manage conventions for new requirements. Durable changes in behavior, architecture, UI, process, or policy must be reflected in `.harness/conventions/` when accepted.
24
- 9. CEO must require worker report paths under `.harness/documents/{goal_name}/submission-{submission_index}-{submission_name}/{owning-cxx}/workers/` before accepting CXX completion.
25
- 10. Do not invoke internal roles through slash commands; commands are Owner entrypoints only.
17
+ 2. If another child mission under the goal is active, update its `mission-state.json` to `closed`, `cancelled`, or `superseded` with `active:false` before starting this submission.
18
+ 3. Create a submission record under `.harness/documents/{goal_name}/submission-{submission_index}-{submission_name}/`.
19
+ 4. Write `.harness/documents/{goal_name}/submission-{submission_index}-{submission_name}/mission-state.json` with `{"lifecycle":"active","active":true}`.
20
+ 5. Record CEO decisions in `.harness/documents/{goal_name}/submission-{submission_index}-{submission_name}/ceo.md`.
21
+ 6. CEO routes only to the CXX agents needed for this additional requirement.
22
+ 7. CXX roles must start in a fresh session context. Do not let the default model impersonate a missing CXX or worker.
23
+ 8. CXX roles must not directly execute specialist deliverables. They must use hired workers for research, planning, design, implementation, QA, ops checks, and documentation.
24
+ 9. If a required worker is missing, the responsible CXX must invoke the installed `harness-hiring` skill before assigning the work.
25
+ 10. CXX must manage conventions for new requirements. Durable changes in behavior, architecture, UI, process, or policy must be reflected in `.harness/conventions/` when accepted.
26
+ 11. CEO must require a Worker Evidence Manifest and worker report paths under `.harness/documents/{goal_name}/submission-{submission_index}-{submission_name}/{owning-cxx}/workers/` before accepting CXX completion.
27
+ 12. When the submission is accepted, cancelled, superseded, blocked, or closed, update `mission-state.json` to `complete`, `cancelled`, `superseded`, `blocked`, or `closed` and set `active:false`.
28
+ 13. Do not invoke internal roles through slash commands; commands are Owner entrypoints only.
26
29
 
27
30
  Note: `/submission` is not a new company goal and not an emergency fix. It is an additional requirement while pursuing the active goal. It belongs under that goal in history.
28
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walwal-harness/cli",
3
- "version": "7.1.27",
3
+ "version": "7.1.29",
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"
@@ -15,6 +15,7 @@ PROJECT_ROOT="${1:-.}"
15
15
  REASON="${2:-mission-complete}"
16
16
 
17
17
  PROGRESS="$PROJECT_ROOT/.harness/progress.json"
18
+ TODOS="$PROJECT_ROOT/.harness/todos/state.json"
18
19
  [ -f "$PROGRESS" ] || {
19
20
  echo "[company-complete] not found: $PROGRESS" >&2
20
21
  exit 1
@@ -40,4 +41,33 @@ bash "$SCRIPT_DIR/harness-progress-set.sh" "$PROJECT_ROOT" \
40
41
  .owner_prompt.status = "completed" |
41
42
  .owner_prompt.completed_at = (now | todate)'
42
43
 
44
+ if [ -f "$TODOS" ]; then
45
+ tmp="$(mktemp)"
46
+ jq '
47
+ .owners |= with_entries(
48
+ .value |= map(
49
+ if (.status == "active" or .status == "pending" or .status == "paused")
50
+ then .status = "done" | .updated_at = (now | todate)
51
+ else .
52
+ end
53
+ )
54
+ )
55
+ ' "$TODOS" > "$tmp" && mv "$tmp" "$TODOS"
56
+ fi
57
+
58
+ DOCS="$PROJECT_ROOT/.harness/documents"
59
+ if [ -d "$DOCS" ]; then
60
+ find "$DOCS" -name mission-state.json -type f -print0 | while IFS= read -r -d '' state; do
61
+ active="$(jq -r '.active // false' "$state" 2>/dev/null || echo false)"
62
+ lifecycle="$(jq -r '.lifecycle // .status // "unknown"' "$state" 2>/dev/null || echo unknown)"
63
+ case "$lifecycle" in
64
+ closed|cancelled|superseded|complete|blocked) continue ;;
65
+ esac
66
+ if [ "$active" = "true" ]; then
67
+ tmp="$(mktemp)"
68
+ jq '.lifecycle = "complete" | .active = false | .completed_at = (now | todate)' "$state" > "$tmp" && mv "$tmp" "$state"
69
+ fi
70
+ done
71
+ fi
72
+
43
73
  echo "[company-complete] marked complete: $REASON"