@walwal-harness/cli 7.1.54 → 7.1.55

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.
@@ -176,7 +176,7 @@ All Playwright usage by CEO, CXX, and hired workers must run with a visible real
176
176
  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.
177
177
  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.
178
178
  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.
179
- 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.
179
+ 11. **Lazy convention/gotcha loading** — *What* to read. *When* to read it, and what to write about it, is Rule 20. 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.
180
180
  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.
181
181
  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.
182
182
  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.
@@ -185,6 +185,9 @@ All Playwright usage by CEO, CXX, and hired workers must run with a visible real
185
185
  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.
186
186
  18. **Playwright is visible by default** — All Playwright/browser automation must use headed/visible mode (`headless: false` or equivalent). Headless Playwright requires explicit Owner approval recorded in the mission.
187
187
  19. **The loop ends only via a runtime transition** — There are exactly two legitimate stop conditions: COMPLETE and BLOCKED-on-external-authority. The autonomous Stop loop and the dashboard read `progress.json` runtime state, not mission documents. After the final Owner report and terminal `mission-state.json`, CEO's last action MUST be `scripts/harness-company-complete.sh` (complete) or `scripts/harness-company-block.sh "<missing authority>"` (blocked). Never end a turn in the `running` state with no queued action, and never fire a terminal transition while real CXX/worker/verification work remains. Between these two endpoints the company keeps progressing autonomously — the Owner is not the pump.
188
+ 20. **Lessons precede planning** — Before any source edit, any measurement, and before any brief is issued, every CXX and every hired worker reads `.harness/conventions/shared.md`, `.harness/conventions/{role}.md`, `.harness/gotchas/shared.md`, and `.harness/gotchas/{role}.md`, follows only the topic links those files name, and writes a `## Lessons Preflight` section stating which items apply and why. Any requirement placed on a CXX that its workers must also satisfy is inserted **verbatim** into the worker brief — *a rule stated one layer above the layer that executes it does not apply.* Every `ceo.md`, every `{cxx}.md`, and every worker report carries a one-line `## Lessons Tally` naming which of those items actually fired, placed immediately before `## Implementation Notes`. **Zero fired is a valid tally and must be stated, not omitted** — a tally that only ever reports hits trains agents to manufacture them. Do not distill the corpus into a second checklist file: a derived corpus must be re-synced whenever any source file changes, and it becomes one more thing nobody reads before planning.
189
+ 21. **Every worker spawn declares its model** — Never inherit the CLI default. The spawning CXX names the model in the brief, in the Worker Evidence Manifest, and in `progress.json` `company_state.workers[]`. A worker terminated by a usage limit is indistinguishable, from the outside, from a worker that finished — so **a silent loop is a rate limit until proven otherwise.** Check the limit and its reset time before re-briefing, re-hiring, or rewriting the task.
190
+ 22. **Readers do not filter by content** — Any reader that scans a role document, worker report, or mission record for sections matches `^>?\s*#{1,6}` and returns every hit: blockquoted or plain, at any depth, with no content filter of any kind. Do not select headings by what they appear to say. A document is free to put verdicts, retractions, standing rules, and continuation lines in a heading, so a reader anchored on `^#` can return superseded content as current — worse than returning nothing. **The reader that decides what is important before reading is the failure.**
188
191
 
189
192
  ---
190
193
 
@@ -168,7 +168,9 @@
168
168
  "behavior": {
169
169
  "comment": "하네스 동작 플래그. UserPromptSubmit 훅이 이 값을 읽어 v7 CEO/CXX 라우팅 안내를 결정한다.",
170
170
  "auto_route_ceo": true,
171
- "auto_route_ceo_description": "true 이면 /goal 또는 /hot-fix 이후 Owner 입력을 v7 CEO/CXX mission flow 기준으로 안내한다. 사용자가 'harness skip' 등을 말하면 단일 메시지 한정으로 건너뛴다."
171
+ "auto_route_ceo_description": "true 이면 /goal 또는 /hot-fix 이후 Owner 입력을 v7 CEO/CXX mission flow 기준으로 안내한다. 사용자가 'harness skip' 등을 말하면 단일 메시지 한정으로 건너뛴다.",
172
+ "lessons_gate": true,
173
+ "lessons_gate_description": "true 이면 Stop 훅(scripts/harness-lessons-gate.sh)이 활성 미션의 role 문서에 '## Lessons Preflight'(적용 conventions/gotchas 항목과 이유)와 '## Lessons Tally'(실제 발동 항목, '0 fired' 도 유효)가 없으면 턴 종료를 막는다. AGENTS.md Hard Rule 20 — 교훈은 계획보다 먼저 읽는다. 아직 채택하지 않은 프로젝트는 false 로 opt-out."
172
174
  },
173
175
  "token_limit": {
174
176
  "comment": "모델 토큰 제한으로 작업이 중단됐을 때의 저비용 재개 정책. 별도 probe 호출 없이 시간 기반으로만 재개 알림을 계산한다.",
@@ -184,6 +186,8 @@
184
186
  "worker_concurrency": 3,
185
187
  "worker_spawn": "claude",
186
188
  "worker_spawn_description": "claude 이면 idle worker 배정 시 `claude -p` 백그라운드 프로세스를 띄운다. record 로 두면 prompt/log만 생성한다.",
189
+ "worker_model": "opus",
190
+ "worker_model_description": "worker spawn 이 명시적으로 선언하는 모델. AGENTS.md Hard Rule 21 — CLI 기본 모델 상속 금지. 사용량 한도로 종료된 worker 는 밖에서 보면 정상 완료와 구별되지 않으므로, 조용한 루프는 반증 전까지 rate limit 으로 취급한다. 비우면 CLI 기본값을 쓰지만 그 선택 자체를 기록해야 한다.",
187
191
  "hourly_wake_executor": "claude",
188
192
  "hourly_wake_executor_description": "claude 이면 `claude -p`, codex 이면 `codex exec` 로 매시간 autonomous tick 을 실행한다.",
189
193
  "hourly_wake_model": "",
@@ -1,5 +1,13 @@
1
1
  # {{WORKER_TITLE}} — Worker Report
2
2
 
3
+ <!--
4
+ Seeding contract: the owning CXX creates this file WITH EVERY SECTION ALREADY
5
+ PRESENT before the worker starts, and the worker fills it in incrementally as
6
+ the work happens. Do not assemble the report at the end. A worker that dies
7
+ mid-round (rate limit, crash, cancelled session) must leave a valid partial
8
+ report, never a stub.
9
+ -->
10
+
3
11
  ## Status
4
12
  IN_PROGRESS
5
13
 
@@ -23,6 +31,14 @@ The dashboard uses this line (not file timestamps) to show whether the worker is
23
31
  ## Result
24
32
  - Outcome and how it meets the acceptance criteria.
25
33
 
34
+ ## Lessons Tally
35
+ - Corpus items that actually fired during this task: {{LESSONS_FIRED}}
36
+ <!--
37
+ One line. Name the convention/gotcha items from the brief that actually changed
38
+ what you did. `0 fired` is a valid tally and must be stated, not omitted — a
39
+ tally that only ever reports hits trains workers to manufacture them.
40
+ -->
41
+
26
42
  ## Implementation Notes
27
43
 
28
44
  ### Design Decisions
package/bin/init.js CHANGED
@@ -167,6 +167,50 @@ function writeClaudeSettings(settings) {
167
167
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
168
168
  }
169
169
 
170
+ // Pre-accept the workspace-trust gate + MCP auto-approval in the user-level
171
+ // ~/.claude.json so unattended Agent Teams teammates don't block on the trust
172
+ // dialog ("this workspace has not been trusted" → settings.json ignored) or the
173
+ // per-project MCP-server prompt. These two flags live ONLY here — no project
174
+ // file can clear them, and --dangerously-skip-permissions does not touch them.
175
+ // Writes are atomic (temp + rename) with a one-time backup so a malformed write
176
+ // can never corrupt the user's global config; any failure is non-fatal to init.
177
+ function acceptWorkspaceTrust() {
178
+ const claudeJsonPath = path.join(require('os').homedir(), '.claude.json');
179
+ if (!fileExists(claudeJsonPath)) {
180
+ log('Skipped workspace-trust pre-accept: ~/.claude.json not found (run Claude here once)');
181
+ return;
182
+ }
183
+ let raw, cfg;
184
+ try {
185
+ raw = fs.readFileSync(claudeJsonPath, 'utf8');
186
+ cfg = JSON.parse(raw);
187
+ } catch (e) {
188
+ log('Skipped workspace-trust pre-accept: ~/.claude.json did not parse (left untouched)');
189
+ return;
190
+ }
191
+ if (!cfg.projects || typeof cfg.projects !== 'object') cfg.projects = {};
192
+ if (!cfg.projects[PROJECT_ROOT] || typeof cfg.projects[PROJECT_ROOT] !== 'object') {
193
+ cfg.projects[PROJECT_ROOT] = {};
194
+ }
195
+ const p = cfg.projects[PROJECT_ROOT];
196
+ if (p.hasTrustDialogAccepted === true && p.enableAllProjectMcpServers === true) {
197
+ log('Workspace already trusted + project MCP servers auto-approved');
198
+ return;
199
+ }
200
+ try {
201
+ const bak = `${claudeJsonPath}.walwal-bak`;
202
+ if (!fileExists(bak)) fs.writeFileSync(bak, raw);
203
+ p.hasTrustDialogAccepted = true;
204
+ p.enableAllProjectMcpServers = true;
205
+ const tmp = `${claudeJsonPath}.walwal.tmp.${process.pid}`;
206
+ fs.writeFileSync(tmp, JSON.stringify(cfg, null, 2) + '\n');
207
+ fs.renameSync(tmp, claudeJsonPath);
208
+ log('Workspace trust pre-accepted + project MCP auto-approved in ~/.claude.json');
209
+ } catch (e) {
210
+ log(`WARNING: could not update ~/.claude.json trust state: ${e.message}`);
211
+ }
212
+ }
213
+
170
214
  function mergeClaudePermissionAllow(settings, entries) {
171
215
  if (!settings.permissions || typeof settings.permissions !== 'object') {
172
216
  settings.permissions = {};
@@ -812,6 +856,17 @@ function scaffoldHarness() {
812
856
  copyFile(harnessMdSrc, harnessMdDest);
813
857
  }
814
858
 
859
+ // Worker report skeleton — ALWAYS update. CXX agents seed a worker report
860
+ // from this file BEFORE the worker starts, so a worker killed mid-round
861
+ // leaves a valid partial report instead of a stub. (Hard Rule 20 tally +
862
+ // Hard Rule 12 notes are already present in the skeleton.)
863
+ const workerTplSrc = path.join(PKG_ROOT, 'assets', 'templates', 'worker-report.md.template');
864
+ const workerTplDest = path.join(HARNESS_DIR, 'shared', 'templates', 'worker-report.md');
865
+ if (fs.existsSync(workerTplSrc)) {
866
+ ensureDir(path.dirname(workerTplDest));
867
+ copyFile(workerTplSrc, workerTplDest);
868
+ }
869
+
815
870
  // Copy memory.md (shared learnings)
816
871
  const memorySrc = path.join(PKG_ROOT, 'assets', 'templates', 'memory.md');
817
872
  const memoryDest = path.join(HARNESS_DIR, 'memory.md');
@@ -1347,6 +1402,17 @@ function installAgentTeamsEnv() {
1347
1402
  log('Agent Teams already enabled');
1348
1403
  }
1349
1404
 
1405
+ // Auto-approve project .mcp.json servers. Each Agent Teams teammate is a fresh
1406
+ // `claude` process that otherwise blocks on the interactive "New MCP server
1407
+ // found in this project" prompt (the launch command uses --permission-mode
1408
+ // auto, which --dangerously-skip-permissions cannot influence). This is the
1409
+ // persisted equivalent of choosing "Use this and all future MCP servers".
1410
+ if (settings.enableAllProjectMcpServers !== true) {
1411
+ settings.enableAllProjectMcpServers = true;
1412
+ changed = true;
1413
+ log('All project MCP servers auto-approved (enableAllProjectMcpServers=true)');
1414
+ }
1415
+
1350
1416
  // Add Claude Code permissions required for harness-driven edits, scripts,
1351
1417
  // and parallel worker isolation. Existing permissions are preserved.
1352
1418
  const harnessPerms = [
@@ -1366,6 +1432,10 @@ function installAgentTeamsEnv() {
1366
1432
  writeClaudeSettings(settings);
1367
1433
  log('Claude settings merged: harness permissions/env preserved with existing settings');
1368
1434
  }
1435
+
1436
+ // Clear the user-level gates that the above settings.json cannot satisfy on
1437
+ // their own (workspace trust + per-project MCP approval).
1438
+ acceptWorkspaceTrust();
1369
1439
  }
1370
1440
 
1371
1441
  const HARNESS_HEADER_MARKER = '<!-- walwal-harness:managed-header -->';
@@ -1646,6 +1716,7 @@ function detectMigrationNeeded() {
1646
1716
  configRuntimeVerificationMissing: false,
1647
1717
  configWakeModelMissing: false,
1648
1718
  configWriteOnSignalMissing: false,
1719
+ configLessonsGateMissing: false,
1649
1720
  coreSkillsStale: false,
1650
1721
  hrResourcePoolStale: false,
1651
1722
  harnessMdStale: false,
@@ -1758,6 +1829,12 @@ function detectMigrationNeeded() {
1758
1829
  if (c.company_mode && c.company_mode.write_on_signal === undefined) {
1759
1830
  flags.configWriteOnSignalMissing = true;
1760
1831
  }
1832
+ if (
1833
+ (c.behavior && c.behavior.lessons_gate === undefined) ||
1834
+ (c.company_mode && c.company_mode.worker_model === undefined)
1835
+ ) {
1836
+ flags.configLessonsGateMissing = true;
1837
+ }
1761
1838
  if (
1762
1839
  c.behavior?.auto_route_dispatcher !== undefined ||
1763
1840
  c.behavior?.auto_route_dispatcher_description ||
@@ -2044,6 +2121,9 @@ function showMigrationProposal(flags) {
2044
2121
  }
2045
2122
  if (flags.configWriteOnSignalMissing) {
2046
2123
  console.log(' • config.json: company_mode.write_on_signal 추가 가능');
2124
+ }
2125
+ if (flags.configLessonsGateMissing) {
2126
+ console.log(' • config.json: behavior.lessons_gate + company_mode.worker_model 추가 가능');
2047
2127
  console.log(' 무신호(델타 부재) 틱은 meeting 문서 대신 heartbeat 만 남겨 문서 폭증을 막습니다.');
2048
2128
  }
2049
2129
  if (flags.configLegacyRouting) {
@@ -2130,6 +2210,7 @@ function runMigrate(opts = {}) {
2130
2210
  !flags.configRuntimeVerificationMissing &&
2131
2211
  !flags.configWakeModelMissing &&
2132
2212
  !flags.configWriteOnSignalMissing &&
2213
+ !flags.configLessonsGateMissing &&
2133
2214
  !flags.coreSkillsStale &&
2134
2215
  !flags.hrResourcePoolStale &&
2135
2216
  !flags.harnessMdStale &&
@@ -2217,7 +2298,7 @@ function runMigrate(opts = {}) {
2217
2298
  // 2. config.json — inject company_mode/runtime verification from template if missing
2218
2299
  const configPath = path.join(HARNESS_DIR, 'config.json');
2219
2300
  const tplPath = path.join(PKG_ROOT, 'assets', 'templates', 'config.json');
2220
- if ((flags.configMissingCompanyMode || flags.configLegacyRouting || flags.configRuntimeVerificationMissing || flags.configWakeModelMissing || flags.configWriteOnSignalMissing) && fs.existsSync(configPath) && fs.existsSync(tplPath)) {
2301
+ if ((flags.configMissingCompanyMode || flags.configLegacyRouting || flags.configRuntimeVerificationMissing || flags.configWakeModelMissing || flags.configWriteOnSignalMissing || flags.configLessonsGateMissing) && fs.existsSync(configPath) && fs.existsSync(tplPath)) {
2221
2302
  const original = fs.readFileSync(configPath, 'utf8');
2222
2303
  const c = JSON.parse(original);
2223
2304
  const tpl = JSON.parse(fs.readFileSync(tplPath, 'utf8'));
@@ -2255,6 +2336,22 @@ function runMigrate(opts = {}) {
2255
2336
  configChanged = true;
2256
2337
  log(' config.json: write_on_signal (무신호 틱 문서 억제) 섹션 동기화');
2257
2338
  }
2339
+ if (flags.configLessonsGateMissing) {
2340
+ // Hard Rule 20/21: the Stop-hook lessons gate and the explicit worker
2341
+ // model. Both default on; a project can opt out of the gate later.
2342
+ c.behavior = c.behavior || {};
2343
+ if (c.behavior.lessons_gate === undefined) {
2344
+ c.behavior.lessons_gate = tpl.behavior?.lessons_gate ?? true;
2345
+ c.behavior.lessons_gate_description = tpl.behavior?.lessons_gate_description;
2346
+ }
2347
+ c.company_mode = c.company_mode || {};
2348
+ if (c.company_mode.worker_model === undefined) {
2349
+ c.company_mode.worker_model = tpl.company_mode?.worker_model ?? '';
2350
+ c.company_mode.worker_model_description = tpl.company_mode?.worker_model_description;
2351
+ }
2352
+ configChanged = true;
2353
+ log(' config.json: lessons_gate + worker_model (Hard Rule 20/21) 섹션 동기화');
2354
+ }
2258
2355
  if (flags.configRuntimeVerificationMissing && tpl.runtime?.verification) {
2259
2356
  c.runtime = c.runtime || {};
2260
2357
  c.runtime.verification = deepMerge(tpl.runtime.verification, c.runtime.verification || {});
package/commands/goal.md CHANGED
@@ -28,6 +28,8 @@ Required flow:
28
28
  11. Do not invoke internal roles through slash commands; commands are Owner entrypoints only.
29
29
  12. Do not ask the Owner whether to continue, hire workers, choose internal options, or start the next step. If CEO cannot decide alone, convene the relevant CXX agents and decide from their written recommendations. CEO may approve reversible routine operations such as local cron/launchd/wake automation, dashboard refresh, monitoring cadence, Telegram briefing format using existing credentials, and mission consolidation/supersede cleanup. Stop only for external authority such as new credentials/secrets, payment approval, legal/business acceptance, unavailable production access, destructive data action, or direct conflict with stated Owner direction.
30
30
 
31
+ Lessons before plan (AGENTS.md Hard Rule 20): CEO and every CXX read `.harness/conventions/{shared,role}.md` and `.harness/gotchas/{shared,role}.md`, follow only the topic links those files name, and write `## Lessons Preflight` **before** the first edit, the first measurement, and the first worker brief. Every role document and worker report closes with a one-line `## Lessons Tally` immediately above `## Implementation Notes` — `0 fired` is a valid tally and must be stated, not omitted. Requirements a CXX must satisfy that its workers must also satisfy go into the worker brief **verbatim**. The Stop hook enforces this; it is not advisory.
32
+
31
33
  Note: A goal is the company's objective. Submissions and hot-fixes that happen while pursuing it should be recorded under that goal directory.
32
34
 
33
35
  Harness documents (ceo.md, cto.md, cqo.md, worker reports) are mission records, not derived output documents. A docmeta skip decision on these files does not authorize skipping any harness protocol step.
@@ -28,6 +28,10 @@ Required flow:
28
28
  13. Do not invoke internal roles through slash commands; commands are Owner entrypoints only.
29
29
  14. Do not ask the Owner whether to continue, hire workers, choose internal options, or start the next step. If CEO cannot decide alone, convene the relevant CXX agents and decide from their written recommendations. CEO may approve reversible routine operations such as local cron/launchd/wake automation, dashboard refresh, monitoring cadence, Telegram briefing format using existing credentials, and mission consolidation/supersede cleanup. Stop only for external authority such as new credentials/secrets, payment approval, legal/business acceptance, unavailable production access, destructive data action, or direct conflict with stated Owner direction.
30
30
 
31
+ Lessons before plan (AGENTS.md Hard Rule 20): CEO and every CXX read `.harness/conventions/{shared,role}.md` and `.harness/gotchas/{shared,role}.md`, follow only the topic links those files name, and write `## Lessons Preflight` **before** the first edit, the first measurement, and the first worker brief. Every role document and worker report closes with a one-line `## Lessons Tally` immediately above `## Implementation Notes` — `0 fired` is a valid tally and must be stated, not omitted. Requirements a CXX must satisfy that its workers must also satisfy go into the worker brief **verbatim**. The Stop hook enforces this; it is not advisory.
32
+
33
+ Scale the read to the fix. A four-line patch pays the index files plus only the topic links that match the fix — not the whole corpus. The ordering constraint holds at every size; the depth does not.
34
+
31
35
  Note: `/hot-fix` is a problem-fix flow while pursuing the active goal. It belongs under that goal in history.
32
36
 
33
37
  Harness documents (ceo.md, cto.md, cqo.md, worker reports) are mission records, not derived output documents. A docmeta skip decision on these files does not authorize skipping any harness protocol step.
@@ -28,6 +28,8 @@ Required flow:
28
28
  13. Do not invoke internal roles through slash commands; commands are Owner entrypoints only.
29
29
  14. Do not ask the Owner whether to continue, hire workers, choose internal options, or start the next step. If CEO cannot decide alone, convene the relevant CXX agents and decide from their written recommendations. CEO may approve reversible routine operations such as local cron/launchd/wake automation, dashboard refresh, monitoring cadence, Telegram briefing format using existing credentials, and mission consolidation/supersede cleanup. Stop only for external authority such as new credentials/secrets, payment approval, legal/business acceptance, unavailable production access, destructive data action, or direct conflict with stated Owner direction.
30
30
 
31
+ Lessons before plan (AGENTS.md Hard Rule 20): CEO and every CXX read `.harness/conventions/{shared,role}.md` and `.harness/gotchas/{shared,role}.md`, follow only the topic links those files name, and write `## Lessons Preflight` **before** the first edit, the first measurement, and the first worker brief. Every role document and worker report closes with a one-line `## Lessons Tally` immediately above `## Implementation Notes` — `0 fired` is a valid tally and must be stated, not omitted. Requirements a CXX must satisfy that its workers must also satisfy go into the worker brief **verbatim**. The Stop hook enforces this; it is not advisory.
32
+
31
33
  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.
32
34
 
33
35
  Submission request:
@@ -5,3 +5,5 @@
5
5
  - CQO hires evaluators and reviewers before assigning specialist quality work.
6
6
  - No archive is accepted without evidence.
7
7
  - Verified recurring lessons are promoted to `.harness/conventions/`, `.harness/gotchas/`, `.harness/memories/`, or `.harness/shared/`.
8
+ - Negative evidence is inadmissible without a positive control that fires in the same run and varies the exact variable under suspicion. A verdict resting on an unproven negative is BLOCKED, not PASS.
9
+ - Where an instrument comes from a dependency rather than in-repo code, its filtering behaviour is read from source and quoted (package, version, file, line range), not inferred from observed output.
@@ -7,3 +7,4 @@
7
7
  - OPS records operating decisions in `.harness/documents/{mission_name}/ops.md`.
8
8
  - OPS writes daily logs under `.harness/logs/YYYY-MM-DD/`.
9
9
  - OPS logs every non-good-case result and raises emergency events to CEO, CTO, and CQO.
10
+ - OPS records the instrument behind every negative claim — tool, log level, filter, and the positive control that fired in the same run. An unrecorded instrument makes the run's negative results unusable.
@@ -24,6 +24,21 @@
24
24
  - CXX decisions use `{cxx}.md`.
25
25
  - Worker reports use `{owning-cxx}/workers/{worker-name}.md`.
26
26
 
27
+ ## Section-Scoped Reading
28
+
29
+ Any reader that scans a role document, worker report, or mission record for sections — a script, a hook, an agent following a protocol — matches:
30
+
31
+ ```
32
+ ^>?\s*#{1,6}
33
+ ```
34
+
35
+ and returns **every** hit. Blockquoted or plain, at any depth, with **no content filter of any kind**.
36
+
37
+ - **Do not filter headings by what they appear to say.** A filter encodes the reader's guess about which headings matter, and a document is free to put anything in a heading: verdicts, in-place retractions, standing rules, continuation lines.
38
+ - A verdict-token filter and a retraction-marker filter, measured against real CXX documents, caught 3/11 and 4/11 blockquoted headings; their union still missed 4 — including the second line of a verdict, whose first line was caught. **A filter that catches half a verdict is worse than one that catches none, because it reports success.**
39
+ - A reader anchored on `^#` reads a claim and never reaches the in-place retraction posted below it as `> ## …`. That is worse than missing a section: it returns superseded content as current.
40
+ - **The reader that decides what is important before reading is the failure.**
41
+
27
42
  ## DDD
28
43
 
29
44
  - Keep domain, application, interface, and infrastructure decisions distinct.
package/gotchas/cqo.md CHANGED
@@ -7,3 +7,15 @@ Do not approve archive based on narrative confidence. Require concrete quality e
7
7
  ## Unpromoted Recurrence
8
8
 
9
9
  Do not leave repeated failures only in chat or mission notes. Promote verified recurrence prevention to conventions, gotchas, memories, or shared state.
10
+
11
+ ## Negative Evidence Without A Positive Control
12
+
13
+ "No error was logged", "no stubbed 2xx was served", "no leak was detected" are claims about the instrument, not about the system, until a positive control fires in the same run and varies the exact variable under suspicion. Do not accept a clean negative result whose instrument was never shown to be able to see the failure.
14
+
15
+ ## Null Instrument Supplied By A Dependency
16
+
17
+ An instrument's filtering behaviour is read from source and quoted — package, version, file, line range — never inferred from observed output. A log filter that lives in a dependency is invisible to every in-repo search, so its absence from the project's own code proves nothing. When an instrument turns out to have been structurally null, re-open the affected claims **by their shape** — every claim of that form, not only the one that happened to be noticed.
18
+
19
+ ## Audit Questions That Offer Alternatives
20
+
21
+ An audit question that offers alternatives asserts that the alternatives are exhaustive. "Is it a skip rule **or** a status-conditional format?" cannot return "neither, it is upstream" — both branches locate the rule inside our own code, so the true answer is unreachable from the question's grammar. When an audit stalls, re-ask the question without the menu.
package/gotchas/cto.md CHANGED
@@ -7,3 +7,7 @@ Do not assign architecture, backend, frontend, DevOps, and evaluation to one gen
7
7
  ## Missing Boundary
8
8
 
9
9
  Do not start implementation before domain, API, platform, account, and integration boundaries are explicit enough for workers.
10
+
11
+ ## Stub Report From A Worker Killed Mid-Round
12
+
13
+ A report assembled at the end of a round becomes a stub when the round is cut short, and a stub halts the company. Create the worker report with every required section present **before** the worker starts, and require it to be filled in incrementally. Same failure, opposite outcome: an unseeded worker killed mid-round leaves a stub and costs a re-run; a seeded worker killed by the same limit leaves an intact partial report and costs nothing. The variable is a decision taken before the round.
package/gotchas/ops.md CHANGED
@@ -15,3 +15,7 @@ Do not monitor a production/service endpoint as live until the Owner-provided se
15
15
  ## Port Drift
16
16
 
17
17
  Do not let CXX agents choose arbitrary ports. CEO must set `HARNESS_BASE_PORT` as a `{xx}000` value in `.env`, and services must derive their ports above that base.
18
+
19
+ ## Clean Log From An Unproven Instrument
20
+
21
+ "Nothing bad appeared in the log" is worth nothing until a positive control proves the log could have shown it. Dev servers, log middleware, proxies, and test runners routinely drop successful or sub-threshold requests at a log level nobody chose deliberately, and that rule appears nowhere in the project's own code. Record the instrument — tool, log level, filter, control — in Environment Evidence, or report the observation as unverified rather than clean.
package/gotchas/shared.md CHANGED
@@ -11,3 +11,21 @@ Do not add internal slash commands for CXX or workers. Commands are Owner entryp
11
11
  ## Runtime State In Package Repo
12
12
 
13
13
  Do not create or commit project runtime `.harness/` state in this package repository.
14
+
15
+ ## Written, Indexed, And Still Too Late
16
+
17
+ A lesson that is written, indexed, and reachable still costs a mission if it is read *after* the mistake. The corpus is rarely the problem; the ordering is. Read `conventions/` and `gotchas/` **before** the first edit, the first measurement, and the first brief — then write the plan.
18
+
19
+ Do not answer this with a distilled checklist file. A derived corpus must be re-synced whenever any source file changes, goes stale silently, and becomes a second thing nobody reads before planning.
20
+
21
+ ## Silent Worker Is A Rate Limit
22
+
23
+ A worker terminated by a usage limit is indistinguishable, from the outside, from a worker that finished. A silent or truncated round is a rate limit until proven otherwise — check the limit and its reset time before re-briefing, re-hiring, or rewriting the task. This is why every spawn must declare its model: without a declared model there is nothing to check the limit against.
24
+
25
+ ## Heading Readers That Filter By Content
26
+
27
+ A reader anchored on `^#` misses the same heading written as `> ## …`, which is exactly how in-place retractions, verdict continuations, and standing rules get posted. Match `^>?\s*#{1,6}` and return every hit with no content filter. A filter that catches half a verdict is worse than one that catches none, because it reports success.
28
+
29
+ ## Rules Stated One Layer Above The Executing Layer
30
+
31
+ A requirement placed on a CXX that its workers must also satisfy does not reach the workers unless it is inserted **verbatim** into the worker brief. A rule stated one layer above the layer that executes it does not apply, and the layer below cannot infer a rule it was never given.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walwal-harness/cli",
3
- "version": "7.1.54",
3
+ "version": "7.1.55",
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"
@@ -309,7 +309,7 @@ fi
309
309
  # that needs execution, or a change in verdict since the last recorded review.
310
310
  # Control flow (conductor-tick.sh) routes off progress.json state, not this file,
311
311
  # so suppressing the markdown on quiet ticks is safe.
312
- write_on_signal="$(jq -r '.company_mode.write_on_signal // true' "$CONFIG" 2>/dev/null || echo true)"
312
+ write_on_signal="$(jq -r 'if .company_mode.write_on_signal == null then true else .company_mode.write_on_signal end' "$CONFIG" 2>/dev/null || echo true)"
313
313
  last_verdict="$(jq -r '.meetings.last_reason // ""' "$PROGRESS" 2>/dev/null || echo "")"
314
314
  has_signal=true
315
315
  if [ "$write_on_signal" = "true" ]; then
@@ -0,0 +1,130 @@
1
+ #!/bin/bash
2
+ # harness-lessons-gate.sh — enforce "lessons precede planning" where the Stop
3
+ # hook already stops the turn (AGENTS.md Hard Rule 20).
4
+ #
5
+ # A role document that was written without reading the corpus is invisible; a
6
+ # role document that names which corpus items it applied, and then tallies which
7
+ # of them actually fired, is not. Rules that live only in prose are followed
8
+ # when convenient — this one gets the mechanism that already works.
9
+ #
10
+ # Checks every role document of the in-scope mission for:
11
+ # ## Lessons Preflight — which convention/gotcha items apply, and why
12
+ # ## Lessons Tally — one line: which of them actually fired ("0 fired" is valid)
13
+ #
14
+ # Usage: harness-lessons-gate.sh <project-root> [text|json] [all|latest-active]
15
+ set -euo pipefail
16
+
17
+ PROJECT_ROOT="${1:-.}"
18
+ DOC_ROOT="$PROJECT_ROOT/.harness/documents"
19
+ CONFIG="$PROJECT_ROOT/.harness/config.json"
20
+
21
+ [ -d "$DOC_ROOT" ] || exit 0
22
+
23
+ mode="${2:-text}"
24
+ scope="${3:-all}"
25
+
26
+ # Opt-out for projects that have not adopted the sections yet.
27
+ if command -v jq >/dev/null 2>&1 && [ -f "$CONFIG" ]; then
28
+ # jq's `//` treats `false` as empty, so `.x // true` can never return false.
29
+ # Test for null explicitly or the opt-out silently does nothing.
30
+ enabled=$(jq -r 'if .behavior.lessons_gate == null then true else .behavior.lessons_gate end' "$CONFIG" 2>/dev/null || echo true)
31
+ if [ "$enabled" != "true" ]; then
32
+ [ "$mode" = "json" ] && jq -nc '{ok:true, violations:[], skipped:"lessons_gate disabled"}'
33
+ exit 0
34
+ fi
35
+ fi
36
+
37
+ violations=()
38
+
39
+ # Section-scoped reading (conventions/shared.md): match `^>?\s*#{1,6}` and take
40
+ # every hit — blockquoted or plain, at any depth, with no content filter.
41
+ HEADING2='^[[:space:]]*>?[[:space:]]*##[[:space:]]+'
42
+
43
+ has_section() {
44
+ local file="$1" title="$2"
45
+ [ -s "$file" ] || return 1
46
+ grep -Eq "${HEADING2}${title}[[:space:]]*$" "$file"
47
+ }
48
+
49
+ is_terminal_lifecycle() {
50
+ case "$1" in
51
+ closed|cancelled|superseded|complete|completed|blocked) return 0 ;;
52
+ *) return 1 ;;
53
+ esac
54
+ }
55
+
56
+ mission_mtime() {
57
+ stat -f '%m' "$1" 2>/dev/null || stat -c '%Y' "$1" 2>/dev/null || echo 0
58
+ }
59
+
60
+ mission_dirs=$(find "$DOC_ROOT" -type f \( -name 'ceo.md' -o -name 'coo.md' -o -name 'cdo.md' -o -name 'cto.md' -o -name 'cqo.md' -o -name 'ops.md' \) -exec dirname {} \; 2>/dev/null | sort -u)
61
+
62
+ if [ "$scope" = "latest-active" ]; then
63
+ latest_active_mission=""
64
+ latest_active_mtime=0
65
+ while IFS= read -r mission_dir; do
66
+ [ -n "$mission_dir" ] || continue
67
+ state_path="$mission_dir/mission-state.json"
68
+ [ -f "$state_path" ] || continue
69
+ command -v jq >/dev/null 2>&1 || continue
70
+ active=$(jq -r '.active // false' "$state_path" 2>/dev/null || echo false)
71
+ lifecycle=$(jq -r '.lifecycle // .status // "unknown"' "$state_path" 2>/dev/null || echo unknown)
72
+ if [ "$active" != "true" ] || is_terminal_lifecycle "$lifecycle"; then
73
+ continue
74
+ fi
75
+ mtime=$(mission_mtime "$mission_dir")
76
+ if [ "${mtime:-0}" -ge "${latest_active_mtime:-0}" ]; then
77
+ latest_active_mtime="$mtime"
78
+ latest_active_mission="$mission_dir"
79
+ fi
80
+ done <<EOF
81
+ $mission_dirs
82
+ EOF
83
+ mission_dirs="$latest_active_mission"
84
+ fi
85
+
86
+ while IFS= read -r mission_dir; do
87
+ [ -n "$mission_dir" ] || continue
88
+ [ -d "$mission_dir" ] || continue
89
+ mission_name="${mission_dir#"$DOC_ROOT"/}"
90
+
91
+ for role in ceo coo cdo cto cqo ops; do
92
+ role_path="$mission_dir/$role.md"
93
+ [ -s "$role_path" ] || continue
94
+ if ! has_section "$role_path" "Lessons Preflight"; then
95
+ violations+=("$mission_name:$role.md-missing-lessons-preflight")
96
+ fi
97
+ if ! has_section "$role_path" "Lessons Tally"; then
98
+ violations+=("$mission_name:$role.md-missing-lessons-tally")
99
+ fi
100
+ done
101
+ done <<EOF
102
+ $mission_dirs
103
+ EOF
104
+
105
+ if [ "${#violations[@]}" -eq 0 ]; then
106
+ if [ "$mode" = "json" ]; then
107
+ jq -nc '{ok:true, violations:[]}' 2>/dev/null || echo '{"ok":true,"violations":[]}'
108
+ fi
109
+ exit 0
110
+ fi
111
+
112
+ if [ "$mode" = "json" ]; then
113
+ printf '%s\n' "${violations[@]}" | jq -Rcs '
114
+ split("\n")[:-1]
115
+ | map(capture("(?<mission>[^:]+):(?<docs>.*)") | .docs = (.docs | split(" ")))
116
+ | {ok:false, violations:.}
117
+ '
118
+ else
119
+ echo "Lessons-before-plan violation (AGENTS.md Hard Rule 20):"
120
+ for violation in "${violations[@]}"; do
121
+ mission="${violation%%:*}"
122
+ docs="${violation#*:}"
123
+ echo "- mission: $mission"
124
+ echo " issue: $docs"
125
+ echo " required: '## Lessons Preflight' (which convention/gotcha items apply, and why)"
126
+ echo " '## Lessons Tally' (one line: which fired; '0 fired' is valid and must be stated)"
127
+ done
128
+ fi
129
+
130
+ exit 1
@@ -25,7 +25,9 @@ CONFIG="$CWD/.harness/config.json"
25
25
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
26
26
 
27
27
  # Opt-out
28
- AUTO_CHAIN=$(jq -r '.behavior.auto_chain_on_stop // true' "$CONFIG" 2>/dev/null || echo "true")
28
+ # `.x // true` would collapse an explicit `false` back to `true` (jq treats
29
+ # false as empty), which silently disabled this documented opt-out.
30
+ AUTO_CHAIN=$(jq -r 'if .behavior.auto_chain_on_stop == null then true else .behavior.auto_chain_on_stop end' "$CONFIG" 2>/dev/null || echo "true")
29
31
  if [ "$AUTO_CHAIN" != "true" ]; then exit 0; fi
30
32
 
31
33
  # 무한루프 방지: 한 sprint 안에서 stop_chain_count 가 상한을 넘으면 중단
@@ -167,6 +169,28 @@ if [ -x "$SCRIPT_DIR/harness-worker-evidence-validate.sh" ]; then
167
169
  fi
168
170
  fi
169
171
 
172
+ # Lessons-before-plan gate (AGENTS.md Hard Rule 20):
173
+ # 코퍼스를 읽고 계획을 세운 흔적이 role 문서에 없으면 턴을 끝내지 못하게 막는다.
174
+ # 산문으로만 존재하는 규칙은 편할 때만 지켜진다 — 이미 동작하는 정지 메커니즘에
175
+ # 검사를 붙인다. 활성 미션 1개로 스코프를 좁혀 legacy/archive 문서가 영구히
176
+ # Stop 을 막지 않게 한다. .harness/config.json 의 behavior.lessons_gate=false 로
177
+ # 아직 채택하지 않은 프로젝트는 opt-out 할 수 있다.
178
+ if [ -x "$SCRIPT_DIR/harness-lessons-gate.sh" ]; then
179
+ LESSONS_JSON=$("$SCRIPT_DIR/harness-lessons-gate.sh" "$CWD" json latest-active 2>/dev/null || true)
180
+ LESSONS_OK=$(echo "$LESSONS_JSON" | jq -r 'if has("ok") then .ok else true end' 2>/dev/null || echo true)
181
+ if [ "$LESSONS_OK" != "true" ]; then
182
+ REASON=$(echo "$LESSONS_JSON" | jq -r '
183
+ "교훈 선행 게이트(Hard Rule 20): " +
184
+ ([.violations[] | "\(.mission) → \(.docs | join(","))"] | join("; ")) +
185
+ ". 각 role 문서에 `## Lessons Preflight`(이 미션에 적용되는 conventions/gotchas 항목과 이유)와 " +
186
+ "그 아래 `## Implementation Notes` 바로 앞에 한 줄짜리 `## Lessons Tally`(실제로 발동한 항목; `0 fired` 도 유효하며 생략은 불가)를 " +
187
+ "추가한 뒤 계속하라. 코퍼스를 다시 요약한 별도 체크리스트 파일을 만들지 말 것 — 읽는 순서를 고치는 규칙이다."
188
+ ' 2>/dev/null || echo "교훈 선행 게이트(Hard Rule 20): active mission 의 role 문서에 ## Lessons Preflight / ## Lessons Tally 가 없습니다. 먼저 추가하세요.")
189
+ jq -nc --arg reason "$REASON" '{decision:"block", reason:$reason}'
190
+ exit 0
191
+ fi
192
+ fi
193
+
170
194
  # stop_chain_count 증가 (느슨한 카운터 — race 허용)
171
195
  NEW_COUNT=$((STOP_CHAIN_COUNT + 1))
172
196
  TMP=$(mktemp)
@@ -16,7 +16,7 @@ STRUCTURED_LIB="$SCRIPT_DIR/lib/harness-structured-log.sh"
16
16
  # 조건 2: opt-out 플래그 확인
17
17
  AUTO_ROUTE="true"
18
18
  if command -v jq >/dev/null 2>&1; then
19
- AUTO_ROUTE=$(jq -r '.behavior.auto_route_ceo // .behavior.auto_route_dispatcher // true' "$CWD/.harness/config.json" 2>/dev/null || echo "true")
19
+ AUTO_ROUTE=$(jq -r 'if .behavior.auto_route_ceo != null then .behavior.auto_route_ceo elif .behavior.auto_route_dispatcher != null then .behavior.auto_route_dispatcher else true end' "$CWD/.harness/config.json" 2>/dev/null || echo "true")
20
20
  fi
21
21
  if [ "$AUTO_ROUTE" != "true" ]; then exit 0; fi
22
22