create-harness-vibe-coding 0.8.9 → 0.8.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README-CN.md CHANGED
@@ -61,7 +61,7 @@ Agent 读取本 README 后,按项目状态选择安装路径:
61
61
  | `/wf-max <任务>` | 任务可拆成多个互不冲突的部分,需要最大并行度 | 在完整 WF 链路上增加 CEO → Manager → Worker 分工和并行波次 | `/wf-max 并行升级前端、后端和文档` |
62
62
  | `/wf-auto` | 希望 Agent 持续自我优化,通过自适应探测选择 | 持续执行优化循环,每轮保留计划、证据和反馈 | `/wf-auto 优化这个项目的稳定性` |
63
63
  | `/wf-auto-spark` | 需要外部灵感、竞品方向或长期路线图 | 搜索外部 spark,绑定 North Star 和里程碑,限制偏离范围 | `/wf-auto-spark 探索产品增长方向` |
64
- | `/wf-review [重点]` | 需要第二意见、跨模型审查或上线前复核 | 调用其他 Agent 做独立审查,并按严重程度反馈 | `/wf-review 重点检查安全和数据丢失` |
64
+ | `/wf-review [重点]` | 需要第二意见、同行审查或上线前复核 | 优先调用可用 peer CLI;没有时使用独立 reviewer 角色,并按严重程度反馈 | `/wf-review 重点检查安全和数据丢失` |
65
65
  | `/wf-learn` | 同类错误反复出现,或一次任务结束后要沉淀经验 | 汇总上下文、记忆和项目经验,形成下一次可复用规则 | `/wf-learn 总结这次返修原因` |
66
66
  | `/wf-browser <任务>` | 浏览器冒烟、E2E、截图、表单或页面验证 | 使用真实浏览器完成操作并提供截图、追踪和验证证据 | `/wf-browser 验证登录和支付流程` |
67
67
  | `/wf-readme <任务>` | README、安装文档、架构图或项目说明需要重写 | 保留事实,整理结构,补充安装和使用说明 | `/wf-readme 优化中文 README` |
package/README.md CHANGED
@@ -72,7 +72,7 @@ When in doubt, use `/wf-help`. It returns the full command table. Use `/wf` for
72
72
  | `/wf-max <task>` | The task splits into independent work and needs maximum parallelism | Adds CEO → Manager → Worker roles and parallel waves to the full WF chain | `/wf-max upgrade frontend, backend, and docs in parallel` |
73
73
  | `/wf-auto` | You want continuous self-directed optimization with adaptive probe selection | Runs repeated optimization cycles with plans, evidence, and feedback | `/wf-auto improve this project's stability` |
74
74
  | `/wf-auto-spark` | You need external inspiration, competitive direction, or a long-term roadmap | Searches for sparks, anchors work to a North Star and milestones, and guards scope drift | `/wf-auto-spark explore product growth directions` |
75
- | `/wf-review [focus]` | You need a second opinion, cross-model review, or a pre-release check | Runs an independent review and classifies findings by severity | `/wf-review focus on security and data loss` |
75
+ | `/wf-review [focus]` | You need a second opinion, peer review, or a pre-release check | Uses a peer CLI when available, otherwise an independent reviewer role, and classifies findings by severity | `/wf-review focus on security and data loss` |
76
76
  | `/wf-learn` | The same mistakes keep recurring or a completed task needs to become reusable knowledge | Consolidates context, memory, and project lessons | `/wf-learn summarize why this task needed rework` |
77
77
  | `/wf-browser <task>` | Browser smoke tests, E2E, screenshots, forms, or UI verification | Uses a real browser and returns screenshots, traces, and evidence | `/wf-browser verify login and checkout` |
78
78
  | `/wf-readme <task>` | README, install docs, architecture diagrams, or project docs need work | Preserves facts while improving structure, setup, and usage guidance | `/wf-readme improve the Chinese README` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-harness-vibe-coding",
3
- "version": "0.8.9",
3
+ "version": "0.8.10",
4
4
  "description": "Scaffold a 0-1 product harness for AI-assisted research, PRD, planning, architecture, build, test, and feedback loops",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,6 +20,7 @@
20
20
  "build:version": "node scripts/build-version.mjs",
21
21
  "prepush": "node scripts/pre-push-check.mjs",
22
22
  "test": "node --test tests/*.test.js",
23
+ "test:e2e": "node tests/e2e-wf-scripts.test.mjs",
23
24
  "test:smoke": "node --test tests/cli-smoke.test.js",
24
25
  "pack:smoke": "node --test tests/pack-smoke.test.js"
25
26
  },
@@ -10,7 +10,7 @@ do not dispatch agents, and do not edit files.
10
10
  | `/wf-max <task>` | workflow skill | `/wf-max refactor auth module` | WF-Max-Useful default (fan-out only where independent), WF-Max-Strict override (unconditional fan-out). |
11
11
  | `/wf-auto` | workflow skill | `/wf-auto` | Perpetual adaptive auto-optimization using project evidence, dynamic probes, risk obligations, evidence ledger, and confirmation-based exhaustion. |
12
12
  | `/wf-auto-spark` | workflow skill | `/wf-auto-spark` | Perpetual inspiration mode with roadmap anchoring and external spark search. |
13
- | `/wf-review <focus>` | workflow skill | `/wf-review security and test coverage` | Cross-model peer review through the other CLI; use for second opinions and risk checks. |
13
+ | `/wf-review <focus>` | workflow skill | `/wf-review security and test coverage` | Peer CLI review through Claude/Codex/OpenCode, with reviewer subagent fallback. |
14
14
  | `/wf-learn` | workflow skill | `/wf-learn` | Force context-master -> memory-master learning cycle after repeated failures or closeout. |
15
15
  | `/wf-readme <task>` | workflow skill | `/wf-readme polish quickstart` | Preserve, merge, or improve README docs without trampling existing project documentation. |
16
16
  | `/wf-update` | direct command | `/wf-update` | Check/apply Harness scaffold updates with safe file classification and conflict handling. |
@@ -31,9 +31,9 @@
31
31
  ]
32
32
  },
33
33
  "hooks": {
34
- "UserPromptSubmit": [
34
+ "SessionStart": [
35
35
  {
36
- "matcher": "",
36
+ "matcher": "startup|resume|clear",
37
37
  "hooks": [
38
38
  {
39
39
  "type": "command",
@@ -31,8 +31,8 @@ subagent surfaces; follow the same Harness role contract either way.
31
31
  - Codex: use the available subagent tool or role mechanism in the current
32
32
  surface. If unavailable, emulate the same roles as separate bounded passes.
33
33
  - WF-MAX cross-CLI overflow: prefer the current runtime's subagents first; if that pool
34
- is exhausted, overflow to the other CLI with explicit dispatch packets
35
- (Codex -> `claude -p`, Claude -> available Codex CLI such as `codex exec`)
34
+ is exhausted, overflow to a peer CLI with explicit dispatch packets
35
+ (`claude -p`, `codex exec`, or `opencode run --agent <role> --dir .`)
36
36
  before bounded-pass fallback.
37
37
  - In every runtime, record fallback and role coverage in the task plan.
38
38
 
@@ -70,9 +70,9 @@ WF-Max-Strict (explicit override): user says `--strict`, `strict wf-max`, or
70
70
  or global Codex config.
71
71
  - Keep `agents.max_depth = 1` unless the user explicitly approves recursive
72
72
  delegation.
73
- - If the current runtime is exhausted, overflow to the other CLI with explicit
74
- dispatch packets: Codex -> `claude -p`; Claude -> available Codex CLI such as
75
- `codex exec`.
73
+ - If the current runtime is exhausted, overflow to a peer CLI with explicit
74
+ dispatch packets: `claude -p`, `codex exec`, or
75
+ `opencode run --agent <role> --dir .`.
76
76
  - Do not rely on undocumented config, environment variables, forked/derived
77
77
  conversations, Codex++, local patches, or third-party forks as stable ways to
78
78
  remove subagent limits.
@@ -1,34 +1,85 @@
1
1
  ---
2
2
  name: wf-review
3
- description: Use for /wf-review in Claude Code, $wf-review or /skills wf-review in Codex, peer review, second opinion, or cross-model review.
3
+ description: Use for /wf-review in Claude Code or OpenCode, $wf-review or /skills wf-review in Codex, peer review, second opinion, cross-runtime review, or independent reviewer subagent review.
4
4
  ---
5
5
 
6
6
  # WF Review Adapter
7
7
 
8
- Use the other available agent CLI for independent review. Never simulate a
9
- cross-model review with the same model.
8
+ Prefer an independent peer runtime for review. If no other CLI is available,
9
+ use the installed reviewer role as a separate subagent context. Never label a
10
+ same-runtime reviewer or bounded role pass as cross-model review.
10
11
 
11
12
  ## Invocation
12
13
 
13
14
  - Claude Code: use `/wf-review [focus]` or select the `wf-review` skill.
14
15
  - Codex CLI or IDE: use `$wf-review` or `/skills` then choose `wf-review`.
16
+ - OpenCode: use `/wf-review [focus]`; the `.opencode/commands/wf-review.md`
17
+ wrapper routes here.
15
18
 
16
- ## CLI Direction
19
+ ## Authority
17
20
 
18
- | Runtime | Command |
19
- | --- | --- |
20
- | Claude Code -> Codex | `git diff | codex exec "<5-dimension review prompt>"` |
21
- | Codex -> Claude Code | `git diff | claude -p "<5-dimension review prompt>"` |
22
- | Only one CLI | Warn user. Do not self-review. |
21
+ The main agent is the controller. It owns final decisions, accepted/rejected
22
+ findings, fixes, release claims, and user-facing recommendations. Review
23
+ agents only provide evidence-backed suggestions.
24
+
25
+ ## Runtime Selection
26
+
27
+ 1. Build one review prompt containing the relevant diff, task acceptance
28
+ criteria, changed-file list, validation evidence, and the review dimensions.
29
+ 2. Detect available CLIs (`claude`, `codex`, `opencode`) with the platform's
30
+ normal command lookup.
31
+ 3. Prefer a CLI that is not the current runtime. If several exist, prefer a
32
+ different model/provider when known; otherwise use the first available in
33
+ the table below.
34
+ 4. If no other CLI exists, dispatch the installed `reviewer` role as a separate
35
+ subagent context. For WF-MAX or broad/high-risk changes, use
36
+ `review-manager` to split spec/code/security/performance dimensions when the
37
+ runtime supports nested subagents.
38
+ 5. If neither a peer CLI nor a subagent surface exists, warn that WF-REVIEW is
39
+ degraded and perform only a controller review; do not count it as
40
+ independent review.
41
+
42
+ | Current runtime | Preferred peer CLI | Secondary peer CLI | Same-runtime fallback |
43
+ | --- | --- | --- | --- |
44
+ | Claude Code | `codex exec "<review prompt>"` | `opencode run --agent reviewer --dir . "<review prompt>"` | Claude `reviewer` subagent |
45
+ | Codex | `claude -p "<review prompt>"` | `opencode run --agent reviewer --dir . "<review prompt>"` | Codex subagent or bounded `reviewer` role pass |
46
+ | OpenCode | `claude -p "<review prompt>"` | `codex exec "<review prompt>"` | `opencode run --agent reviewer --dir . "<review prompt>"` or OpenCode `reviewer` subagent |
47
+ | Unknown | any available peer CLI, preferring `claude`, then `codex`, then `opencode` | next available peer CLI | installed `reviewer` role subagent |
48
+
49
+ OpenCode note: `opencode run [message..]` is the non-interactive CLI path and
50
+ `--agent reviewer` selects the installed `.opencode/agents/reviewer.md` role.
23
51
 
24
52
  ## Review Dimensions
25
53
 
26
54
  Cover correctness, security, architecture, performance, and tests. Classify
27
- findings as Critical, High, Medium, or Low. Return raw peer output first, then
28
- your severity-classified synthesis.
55
+ findings as Critical, High, Medium, or Low. Return raw peer or subagent output
56
+ first, then the controller's severity-classified synthesis.
57
+
58
+ ## Reviewer Role Fallback
59
+
60
+ When using a same-runtime subagent fallback, dispatch a real role packet instead
61
+ of an ad hoc prompt:
62
+
63
+ ```text
64
+ Role: reviewer
65
+ AgentName: reviewer
66
+ Mode: read-only
67
+ Objective: review the current diff for correctness, security, architecture,
68
+ performance, tests, and spec/AC compliance
69
+ Read set: changed files, tests, task PLAN/PROGRESS, Harness/agent-workflow.md,
70
+ Harness/subagents.md, Harness/dispatch.md, architecture docs when affected
71
+ Write set: none
72
+ Forbidden: file edits, git mutations, formatting-only advice, ungrounded claims
73
+ ReturnSchema: findings by severity, file/line refs, missing verification,
74
+ open questions, closeout recommendation
75
+ ```
76
+
77
+ If the runtime exposes `review-manager`, use it only when the diff is broad
78
+ enough to benefit from multiple reviewer dimensions. The controller must
79
+ deduplicate reviewer output and decide what to accept.
29
80
 
30
81
  ## Context
31
82
 
32
83
  Include the relevant diff, `Harness/architecture.md` when architecture is in
33
84
  scope, and any task acceptance criteria. If the diff is too large, ask for a
34
- narrower scope before invoking the other CLI.
85
+ narrower scope before invoking a peer CLI or reviewer subagent.
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "hooks": {
3
- "UserPromptSubmit": [
3
+ "SessionStart": [
4
4
  {
5
+ "matcher": "startup|resume|clear",
5
6
  "hooks": [
6
7
  {
7
8
  "type": "command",
8
- "command": "node \"$(git rev-parse --show-toplevel)/Harness/scripts/wf-auto-update-prompt.mjs\" --format codex",
9
- "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -Command \"$root=(git rev-parse --show-toplevel 2>$null); if (-not $root) { $root=(Get-Location).Path }; node (Join-Path $root 'Harness/scripts/wf-auto-update-prompt.mjs') --format codex\"",
10
- "statusMessage": "Checking Harness updates",
11
- "timeout": 35
9
+ "command": "node -e \"const fs=require('fs'),p=require('path'),cp=require('child_process');let d=process.cwd();for(;;){const s=p.join(d,'Harness','scripts','wf-auto-update-prompt.mjs');if(fs.existsSync(s)){const r=cp.spawnSync(process.execPath,[s,'--format','codex'],{stdio:'inherit',cwd:d,env:process.env});process.exitCode=(r.status==null)?0:r.status;break}const u=p.dirname(d);if(u===d)break;d=u}\"",
10
+ "timeout": 35,
11
+ "statusMessage": "Checking Harness updates"
12
12
  }
13
13
  ]
14
14
  }
@@ -1,19 +1,19 @@
1
1
  {
2
- "generator": "0.8.9",
3
- "generated": "2026-07-17T09:52:12.208Z",
2
+ "generator": "0.8.10",
3
+ "generated": "2026-07-21T04:52:37.663Z",
4
4
  "options": [],
5
5
  "autoCheck": true,
6
6
  "source": "https://raw.githubusercontent.com/zingspark/create-harness-vibe-coding/main/templates/common/",
7
7
  "checksums": {
8
- ".agents/skills/subagent-orchestrator/SKILL.md": "sha256-a896ff4133fdde522be8a186e26efcf65969d5db279e6ad8bc561a9a2c111ad3",
8
+ ".agents/skills/subagent-orchestrator/SKILL.md": "sha256-8d9e8c0db4647bcf168a5f99c8b2ea97d154bfca30e78ffd1cc5a98ce185bb1d",
9
9
  ".agents/skills/tdd/SKILL.md": "sha256-1e2ec0b54a5cf5b071800b0f2e5672a128a42471e16f70f00bb4c0a07a3617cb",
10
10
  ".agents/skills/wf-auto-spark/SKILL.md": "sha256-a00cde5842e5f1d6d69826c59972ebdcfbc2c291b43fc37523e42ec02e4788e4",
11
11
  ".agents/skills/wf-auto/SKILL.md": "sha256-f3f288f52766a0f59ec4f2018029c8169782ac440b525f4c1040724ca8482bf3",
12
12
  ".agents/skills/wf-learn/SKILL.md": "sha256-2ca977967e62847ac3b763bcce00dc6bb2e8dec28569a1ab6ee413625f8e8779",
13
- ".agents/skills/wf-max/SKILL.md": "sha256-3449350938645945f54202200315ee5ec0c5f21ed34cdbaa856a5b669d17e73e",
13
+ ".agents/skills/wf-max/SKILL.md": "sha256-0cabb44a67ce47beaeae4c2bba9a8a31a9340199c7684b57a1f9e9b7e2eb622e",
14
14
  ".agents/skills/wf-readme/SKILL.md": "sha256-4d34ee3b6b305fa2581f5d8cb31e9152759f3df8bf8fa08efbb43782f128bdcd",
15
15
  ".agents/skills/wf-remove/SKILL.md": "sha256-21ebef1a1fa8645d094c8ceeca78b33f036b6c42cda6db8be425d38d9e91af75",
16
- ".agents/skills/wf-review/SKILL.md": "sha256-cc6d536ec6de25546a0ba96e686f195da50ed11bc03763a76ef46d77243d3537",
16
+ ".agents/skills/wf-review/SKILL.md": "sha256-ab1122cbc9612ba734f98736e5479d7777206293d011d5471aaa34d1da35dae6",
17
17
  ".agents/skills/wf-update/SKILL.md": "sha256-6fa16fd5a526969837b9fd5fcae6aed55eec5ccd755e0a9cddcb7065878f61d8",
18
18
  ".agents/skills/wf/SKILL.md": "sha256-53225645b593f4cf060781590f9b3025cf17315baad4e2acce850cc4cd972f97",
19
19
  ".claude/agents/architect-manager.md": "sha256-d98c3a2d044d5216d9bcde217b4359b1c33f5be152416e242807652ddcd2868b",
@@ -35,23 +35,23 @@
35
35
  ".claude/agents/tdd-guide.md": "sha256-d7c7ad3f61f6a6dc73cfa54ffe7e215ded0421a3058654a01731d1a5d5f4c336",
36
36
  ".claude/agents/test-writer.md": "sha256-724866d627f193caaf060ec6af97b1d54c2df2d5c5f937e78fc3c53c7f798b16",
37
37
  ".claude/agents/verifier.md": "sha256-d501a505b97e679daa89ee1d910ee185edc42ba57d58203e962b1c5a844c0872",
38
- ".claude/commands/wf-help.md": "sha256-3a9783211a72d5c490e6014d263b44aed39f45d93f7463ec26b0b3d79844094d",
38
+ ".claude/commands/wf-help.md": "sha256-aaaa6834c6cb61d2ecbeb41ad8fcf77de10ac1638b2009a3822284c01cf41fac",
39
39
  ".claude/commands/wf-update.md": "sha256-1988e82eb60311b8208367e3c8e22e6222ae05f4b0f645fa9a0e426790705c12",
40
40
  ".claude/rules/ecc/common.md": "sha256-e5f046d02ec9f5d326088f608c63758632d9a5f6c67688b20e065b6380884992",
41
- ".claude/settings.json": "sha256-7dd4a92963e2336cf738a10d1cf68fb6facc0c03f32c6af0ca62f388a48e14a3",
42
- ".claude/skills/subagent-orchestrator/SKILL.md": "sha256-a896ff4133fdde522be8a186e26efcf65969d5db279e6ad8bc561a9a2c111ad3",
41
+ ".claude/settings.json": "sha256-99e8552584b8e71e3e11b4b760c3bfbf8c1b18dadda7ec3f6e3c46f88d9ea513",
42
+ ".claude/skills/subagent-orchestrator/SKILL.md": "sha256-8d9e8c0db4647bcf168a5f99c8b2ea97d154bfca30e78ffd1cc5a98ce185bb1d",
43
43
  ".claude/skills/tdd/SKILL.md": "sha256-1e2ec0b54a5cf5b071800b0f2e5672a128a42471e16f70f00bb4c0a07a3617cb",
44
44
  ".claude/skills/wf-auto-spark/SKILL.md": "sha256-a00cde5842e5f1d6d69826c59972ebdcfbc2c291b43fc37523e42ec02e4788e4",
45
45
  ".claude/skills/wf-auto/SKILL.md": "sha256-f3f288f52766a0f59ec4f2018029c8169782ac440b525f4c1040724ca8482bf3",
46
46
  ".claude/skills/wf-learn/SKILL.md": "sha256-2ca977967e62847ac3b763bcce00dc6bb2e8dec28569a1ab6ee413625f8e8779",
47
- ".claude/skills/wf-max/SKILL.md": "sha256-3449350938645945f54202200315ee5ec0c5f21ed34cdbaa856a5b669d17e73e",
47
+ ".claude/skills/wf-max/SKILL.md": "sha256-0cabb44a67ce47beaeae4c2bba9a8a31a9340199c7684b57a1f9e9b7e2eb622e",
48
48
  ".claude/skills/wf-readme/SKILL.md": "sha256-4d34ee3b6b305fa2581f5d8cb31e9152759f3df8bf8fa08efbb43782f128bdcd",
49
49
  ".claude/skills/wf-remove/SKILL.md": "sha256-21ebef1a1fa8645d094c8ceeca78b33f036b6c42cda6db8be425d38d9e91af75",
50
- ".claude/skills/wf-review/SKILL.md": "sha256-cc6d536ec6de25546a0ba96e686f195da50ed11bc03763a76ef46d77243d3537",
50
+ ".claude/skills/wf-review/SKILL.md": "sha256-ab1122cbc9612ba734f98736e5479d7777206293d011d5471aaa34d1da35dae6",
51
51
  ".claude/skills/wf-update/SKILL.md": "sha256-6fa16fd5a526969837b9fd5fcae6aed55eec5ccd755e0a9cddcb7065878f61d8",
52
52
  ".claude/skills/wf/SKILL.md": "sha256-53225645b593f4cf060781590f9b3025cf17315baad4e2acce850cc4cd972f97",
53
53
  ".codex/config.toml": "sha256-e0af046982c6dbc535c52b19610d16ce9aba08a17dd29f11b74cc8dad8371145",
54
- ".codex/hooks.json": "sha256-b20527714f82130d67a7e3aca50ced148c8311c81a9731c91f23275a6248143c",
54
+ ".codex/hooks.json": "sha256-3200c42c2da434c07cd6ab1ce86f8845ab46a1ed9a25b65117b2b2b42056c725",
55
55
  ".opencode/agents/architect-manager.md": "sha256-7ea3ce5254638e15c9a9c342eafcaa1c98d5fbf0323ea2111cd6397580581eb1",
56
56
  ".opencode/agents/architect.md": "sha256-bd876678ef08ef848523ec639095e099b9d8e587794827fba4763dc82ed3f6c4",
57
57
  ".opencode/agents/codebase-explorer.md": "sha256-e72cf6f8c2f4247f2dfd027c94b43fe67f0fe5e6f851ec2400c8094ac343a7ef",
@@ -73,15 +73,15 @@
73
73
  ".opencode/agents/verifier.md": "sha256-7a415b3e8ae440838cfd113ef3cbd1c66ab6496e45c9ef90af0d878e3ebc1ee4",
74
74
  ".opencode/commands/wf-auto-spark.md": "sha256-febe76ec28b1650886a81d79e19ec53db725da3ed7777cf739ec8e2bbc5d6564",
75
75
  ".opencode/commands/wf-auto.md": "sha256-8e27d4d567a980d4d6a2ed0832c4f1f7f7439f172ea03a79c9d24468d01fdf38",
76
- ".opencode/commands/wf-help.md": "sha256-ddef1dcc671bcafb0f4f4adb08d0b0ab247e5228613e0f9b2e3b082c7db44652",
76
+ ".opencode/commands/wf-help.md": "sha256-2776d41e78577896f90f0de3ebdbc032ab9808b96c17ebecbff1319d4e6142b1",
77
77
  ".opencode/commands/wf-learn.md": "sha256-dc8e75ef4b2c2ef90c749a23dc2cd99eaf2727955d0670bdb2d6f24086f6d0ed",
78
78
  ".opencode/commands/wf-max.md": "sha256-39ebd1e0252b9b72a16a539c8f2afd678bf63aad38c4b964238b287c359a49a2",
79
79
  ".opencode/commands/wf-readme.md": "sha256-276e80255bd38f51e17b158650d182401304bf06388407d259f47850306556e8",
80
80
  ".opencode/commands/wf-remove.md": "sha256-fe9823152c4f74e66b151e352de7167436d1bc47376c2a688d190391931d300a",
81
- ".opencode/commands/wf-review.md": "sha256-c092f15ae3950c30598976005d41853c64daf2181be37bcc4bcefc61b727006e",
81
+ ".opencode/commands/wf-review.md": "sha256-4bd59f66bc96c2773868e63c37cbacf39dd521555e997c0b60d50538ef2d5dbd",
82
82
  ".opencode/commands/wf-update.md": "sha256-1988e82eb60311b8208367e3c8e22e6222ae05f4b0f645fa9a0e426790705c12",
83
83
  ".opencode/commands/wf.md": "sha256-d86cf20a78c37757f76aed7e252475fb82e7597cd55c6a4c916441b3b75fe9af",
84
- ".opencode/plugins/harness-wf-status.mjs": "sha256-ac36601165fd4a6acc54beb2a574148c2ff45c0f85b1a36ecac50435a9e94a02",
84
+ ".opencode/plugins/harness-wf-status.mjs": "sha256-ee0987cec5530c34784c619a803562d7085ae26fe5d199fde7ce42c2bed4724e",
85
85
  "AGENTS.md": "sha256-fad176931742a5080e073d1ddba99a3eabb67e8c42bebb132df99a51ca6a184d",
86
86
  "CLAUDE.md": "sha256-1aa2e672f7f423d0011f65b0dda707bdd7f9db2a83ad54650bb444fb086eabaa",
87
87
  "Harness/ACCEPTANCE_PROTOCOL.md": "sha256-0f7a21a1fbfdca6e5f28f3297b61c3446e3bdd552a2143c7032d6271a4e26103",
@@ -89,32 +89,32 @@
89
89
  "Harness/DEBUG_PROTOCOL.md": "sha256-ed80de0e3e1a40dbb9fd0bdead9174f1650e1833027f3d7fb4bb5884142e03f7",
90
90
  "Harness/ECC-GUIDE.md": "sha256-136e60327813609516c6c10e2a5254d7d300e1631ba80d33fb375fdf3800ca7e",
91
91
  "Harness/HARNESS_BRIDGE.md": "sha256-681dbeca9a590bbeb3a0c01b044e40e642e6d640632d8611064eb897ada8a48d",
92
- "Harness/MEMORY.md": "sha256-9b2e15ac95e067a735ed90e40946404509b58eafde38671dc6455f68c41f010d",
92
+ "Harness/MEMORY.md": "sha256-4cbb881b3e9893e917861dc250afaedf5b88da6c99626a9c7f6abb8132841b09",
93
93
  "Harness/MEMORY_PROTOCOL.md": "sha256-c8cda68bf25fbf7152c6049959a8bd6d39453976a651a1c4a9d3f7e349f823c4",
94
- "Harness/README.md": "sha256-8ea8e392900b89c2d7d90a466d61b742753bb0322541821d91dd0cb1e13f5b73",
94
+ "Harness/README.md": "sha256-4c8f30de9face43960088d240127860b43a2b821d1988c23a9cd1c0502ba35f7",
95
95
  "Harness/SETUP.md": "sha256-008472ad2be5681e2616af29ad57cccbca122065de7bddfe22c683e3470d9fd5",
96
96
  "Harness/TASK_ARCHIVE.md": "sha256-d815f2c99a7dc98b9956bb876e9ee07dfb66a283c758124d7b4eb8965dea1f6a",
97
97
  "Harness/TDD-GUIDE.md": "sha256-602084530fd0dea7499a023ce75738351e5d97a0c222b64d405f36da6b59a162",
98
- "Harness/WF-AUTO-ANGLES.md": "sha256-ff9c684860cef7a6d0c16aa4b825663a1cd18b10befe8a7320621c28bbfe4ae6",
98
+ "Harness/WF-AUTO-ANGLES.md": "sha256-b9641d871f728e8112ad0d8c7f87bcbec0a06aa572c5f3060efdd26496abdf9b",
99
99
  "Harness/WF-AUTO-SPARK.md": "sha256-8fc879261f71f81c5a64e37a6aed8b72fd78ca01c5c9f318a15f4a0a594b4783",
100
- "Harness/WF-AUTO.md": "sha256-dff89e2c2348b329cbbf42391667748b61fe152a991daab297d10db6a67f243f",
100
+ "Harness/WF-AUTO.md": "sha256-7898a38ee9ce9cf1c49aa7e3603c17afc68544981dca7d91e2e4149ca2fc5aea",
101
101
  "Harness/WF-KERNEL.md": "sha256-97c626574f62dceaa590829d99e46889b86cab5658d7827c704788e4afdda030",
102
- "Harness/WF-MAX.md": "sha256-861961501887b5c9b34444fce985aa567c5d32b94fcefd5f73db6a8c3e10d7cd",
102
+ "Harness/WF-MAX.md": "sha256-a125dddc1e6a854f71fc360dd4fff7d576dbdd4e6141d29de2f5787d6f48b5e0",
103
103
  "Harness/WF-STATE.md": "sha256-80fdaa4b133a9f632a252e4d2fca51bb96f9f9779dab2bc6f3d791d0d2d3aae8",
104
104
  "Harness/WF.md": "sha256-199f6c3899cf0d8c86d65fd61e90e85ee9f8aa4c8fc56889f995f05e11987816",
105
105
  "Harness/agent-workflow.md": "sha256-a91c3732ffb545506cec88c875d76ebe92248d0714da3e398d2a13b06349bef4",
106
106
  "Harness/context-loading.md": "sha256-7dacdc7cbf6748f063db65e31fff1ff7992dc7fd8c72c389f6560e7e0d65ff06",
107
- "Harness/dispatch.md": "sha256-03cd1a668293eb2f1992e472d170cc445b000adf5dd1c75e116deed0ad4deeba",
107
+ "Harness/dispatch.md": "sha256-36b2b0814241384ea1aa6ee6ebb0434aba63eeacd6abfbdd678c0032e16e3631",
108
108
  "Harness/extension.md": "sha256-ff93616aaa5ad232b6a32ac6c98bb1a3a08ee5da2ddad92eb9090f5b8e9e3e47",
109
109
  "Harness/lifecycle.md": "sha256-a2d025e52f3b4412d8439083aca0d2cf7a4458ad0e30de4fc6465f990379190f",
110
110
  "Harness/research/README.md": "sha256-956c154df01cb9376972eb8b88bb3be0fe4e2dda802a72457c0126e3476fa755",
111
111
  "Harness/scripts/archive-tasks.mjs": "sha256-07bba20c2f4b3baae737b8b9a951125a48e37dd482a1f1ca122b9cf88e90e956",
112
- "Harness/scripts/scan-clean.mjs": "sha256-b3530e825530a2673a40536d3844fabac94a7884480e751ddae2dffacdcbb189",
113
- "Harness/scripts/validate-harness.mjs": "sha256-72a020e3f469de08e8a75eb79c5038d9092a490b7b1ce5e80d71c8c066a4bb13",
114
- "Harness/scripts/wf-auto-update-prompt.mjs": "sha256-45861ab8285f28c883b23548ff2c64987c2a9b7cb7bb0fab01760147007b54b5",
115
- "Harness/scripts/wf-remove.mjs": "sha256-df7f3fa80150dca8e598bfc423e260aa9df9584d716f3b299fdc0fd79c2e1911",
116
- "Harness/scripts/wf-update-check.mjs": "sha256-44d15f59cbb98c320552df9e164f19f8a643448299b81c6274666dcd51f4c752",
117
- "Harness/subagents.md": "sha256-5468557fb9a4a0e0509518e1a5d92cb5324ff542ed83b526c6a1902745950a25",
112
+ "Harness/scripts/scan-clean.mjs": "sha256-76fb4212897ea3f366d33b7defee87611c01e9de75c745adfe828745c6fd0887",
113
+ "Harness/scripts/validate-harness.mjs": "sha256-53339f20c291666fe703cda73325b0b9bca0b4a67dc703d7876a4b1a4d8d6bd9",
114
+ "Harness/scripts/wf-auto-update-prompt.mjs": "sha256-14210304dbd8e648b8939fc4c5d3664f943052153e689264c16b05b7b81bd39e",
115
+ "Harness/scripts/wf-remove.mjs": "sha256-576bc92325bf63d4142eb6028d095f12abf9d845a1a178dfe247b813794476a9",
116
+ "Harness/scripts/wf-update-check.mjs": "sha256-41f7a3c9324d006dc745557031c31d4b9319eaf7fc78df5cfdec671a24c90838",
117
+ "Harness/subagents.md": "sha256-f2e81c21792b4d70c64c9bd22e9548d783d945dddb3504b4937e04ca1d04320d",
118
118
  "Harness/templates/ACCEPTANCE.template.md": "sha256-394a6c08b155b1d121f1d51da774f7988bdb891224c6d5f2ca2c131e8c95ffb3",
119
119
  "Harness/templates/API_CONTRACT.template.md": "sha256-e11ec9866576624982f35e77359e3f4ef3aea3e3f6b5895f6cc5419d29699fc5",
120
120
  "Harness/templates/PLAYWRIGHT_SPEC.template.ts": "sha256-bef5f3bbae833b31eeb8cab04fc5a5ff355e9eda4ba6909db70f83b403607115",
@@ -13,7 +13,7 @@ do not dispatch agents, and do not edit files.
13
13
  | `/wf-max <task>` | workflow skill | `/wf-max refactor auth module` | WF-Max-Useful default (fan-out only where independent), WF-Max-Strict override (unconditional fan-out). |
14
14
  | `/wf-auto` | workflow skill | `/wf-auto` | Perpetual adaptive auto-optimization using project evidence, dynamic probes, risk obligations, evidence ledger, and confirmation-based exhaustion. |
15
15
  | `/wf-auto-spark` | workflow skill | `/wf-auto-spark` | Perpetual inspiration mode with roadmap anchoring and external spark search. |
16
- | `/wf-review <focus>` | workflow skill | `/wf-review security and test coverage` | Cross-model peer review through the other CLI; use for second opinions and risk checks. |
16
+ | `/wf-review <focus>` | workflow skill | `/wf-review security and test coverage` | Peer CLI review through Claude/Codex/OpenCode, with reviewer subagent fallback. |
17
17
  | `/wf-learn` | workflow skill | `/wf-learn` | Force context-master -> memory-master learning cycle after repeated failures or closeout. |
18
18
  | `/wf-readme <task>` | workflow skill | `/wf-readme polish quickstart` | Preserve, merge, or improve README docs without trampling existing project documentation. |
19
19
  | `/wf-update` | direct command | `/wf-update` | Check/apply Harness scaffold updates with safe file classification and conflict handling. |
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Run cross-model peer review via the wf-review skill
2
+ description: Run peer CLI or reviewer-subagent review via the wf-review skill
3
3
  ---
4
4
 
5
5
  # /wf-review
@@ -9,7 +9,7 @@ static help or script command.
9
9
 
10
10
  1. Load `CLAUDE.md`, `Harness/MEMORY.md` (index only per Memory Preflight), then `Harness/README.md`.
11
11
  2. Execute per the skill adapter `.claude/skills/wf-review/SKILL.md` (mirror: `.agents/skills/wf-review/SKILL.md`).
12
- 3. Do not duplicate the workflow here. The skill adapter and the wf-review cross-model contract are authoritative.
12
+ 3. Do not duplicate the workflow here. The skill adapter and the wf-review peer-review contract are authoritative.
13
13
 
14
14
  If this runtime cannot invoke the skill directly, read
15
15
  `.claude/skills/wf-review/SKILL.md` and follow it in place.
@@ -1,6 +1,6 @@
1
1
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
2
2
  import { spawnSync } from 'node:child_process';
3
- import { dirname, resolve } from 'node:path';
3
+ import { resolve } from 'node:path';
4
4
 
5
5
  const WF_COMMANDS = new Map([
6
6
  ['wf', 'WF'],
@@ -53,14 +53,14 @@ function clearMode(root) {
53
53
  }
54
54
  }
55
55
 
56
- function runUpdatePrompt(root, prompt) {
56
+ function runUpdatePrompt(root, prompt = '', hookEventName = 'opencode.startup') {
57
57
  const script = resolve(root, 'Harness', 'scripts', 'wf-auto-update-prompt.mjs');
58
58
  if (!existsSync(script)) return null;
59
59
  const result = spawnSync(process.execPath, [script, '--format', 'json'], {
60
60
  cwd: root,
61
61
  input: JSON.stringify({
62
62
  cwd: root,
63
- hook_event_name: 'chat.message',
63
+ hook_event_name: hookEventName,
64
64
  prompt,
65
65
  }),
66
66
  encoding: 'utf-8',
@@ -74,13 +74,6 @@ function runUpdatePrompt(root, prompt) {
74
74
  }
75
75
  }
76
76
 
77
- function textFromParts(parts) {
78
- return (parts || [])
79
- .filter(part => part?.type === 'text' && typeof part.text === 'string')
80
- .map(part => part.text)
81
- .join('\n');
82
- }
83
-
84
77
  async function showToast(client, directory, body) {
85
78
  try {
86
79
  await client.tui.showToast({
@@ -94,19 +87,17 @@ async function showToast(client, directory, body) {
94
87
 
95
88
  export const HarnessWfStatusPlugin = async ({ client, directory, worktree }) => {
96
89
  const root = worktree || directory || process.cwd();
90
+ const startupUpdate = runUpdatePrompt(root);
91
+ if (startupUpdate?.message) {
92
+ await showToast(client, root, {
93
+ title: 'Harness update available',
94
+ message: startupUpdate.message,
95
+ variant: 'warning',
96
+ duration: 12000,
97
+ });
98
+ }
97
99
 
98
100
  return {
99
- 'chat.message': async (_input, output) => {
100
- const update = runUpdatePrompt(root, textFromParts(output.parts));
101
- if (!update?.message) return;
102
- await showToast(client, root, {
103
- title: 'Harness update available',
104
- message: update.message,
105
- variant: 'warning',
106
- duration: 12000,
107
- });
108
- },
109
-
110
101
  'command.execute.before': async (input) => {
111
102
  const mode = WF_COMMANDS.get(commandName(input.command));
112
103
  if (!mode) return;
@@ -33,10 +33,10 @@ Stack-specific agents can be added after the product shape is known.
33
33
  - [wf](../.claude/skills/wf/SKILL.md) - Claude Code WF skill command; mirrored for Codex at `../.agents/skills/wf/SKILL.md`.
34
34
  - [subagent-orchestrator](../.claude/skills/subagent-orchestrator/SKILL.md) - controller-led subagent orchestration, parallel read-only passes, review gates, and recovery handoffs.
35
35
  - [wf-readme](../.claude/skills/wf-readme/SKILL.md) - README preservation, append-only development sections, structured tables, and approved architecture diagrams.
36
- - [wf-review](../.claude/skills/wf-review/SKILL.md) - cross-model peer review: invoke the other agent CLI (Codex/Claude) for independent review.
36
+ - [wf-review](../.claude/skills/wf-review/SKILL.md) - peer review: prefer another agent CLI (Claude/Codex/OpenCode); otherwise use the installed reviewer role as an independent subagent context. Controller decides.
37
37
  - [wf-update](../.claude/skills/wf-update/SKILL.md) - GitHub-based incremental harness update, checksum comparison, and safe in-place updates.
38
38
  - [wf-learn](../.claude/skills/wf-learn/SKILL.md) - force memory learning cycle: context-master -> memory-master -> project + global memory.
39
- - [wf-max](../.claude/skills/wf-max/SKILL.md) - WF kernel + maximum safe fan-out: WF-Max-Useful by default (fan out only where independent), WF-Max-Strict only on explicit strict request; current runtime subagents first, cross-CLI overflow when available.
39
+ - [wf-max](../.claude/skills/wf-max/SKILL.md) - WF kernel + maximum safe fan-out: WF-Max-Useful by default (fan out only where independent), WF-Max-Strict only on explicit strict request; current runtime subagents first, peer-CLI overflow when available.
40
40
  - [wf-auto](../.claude/skills/wf-auto/SKILL.md) - perpetual adaptive auto-optimization: evidence-selected probes, dynamic obligations, intent checkpoints, evidence ledger.
41
41
  - [wf-auto-spark](../.claude/skills/wf-auto-spark/SKILL.md) - perpetual inspiration mode: external spark search, long-term roadmap with staged milestones, <=50% deviation guard.
42
42
  - [tdd](../.claude/skills/tdd/SKILL.md) - acceptance-driven TDD: AC-linked RED tests, real UI clicks for browser-visible behavior, Playwright/CDP evidence, and configured coverage gate.
@@ -104,7 +104,7 @@ Routing priority: **direct mode is the default** when no explicit WF token is pr
104
104
  | Need perpetual auto-optimization | /wf-auto, $wf-auto, /skills wf-auto (explicit user token only) | [WF-AUTO.md](WF-AUTO.md), [WF-AUTO-ANGLES.md](WF-AUTO-ANGLES.md), [subagents.md](subagents.md), [dispatch.md](dispatch.md) | perpetual loop, adaptive probe selection, dynamic risk obligations, spark search, intent checkpoint, evidence ledger; CEO never writes code |
105
105
  | Need perpetual inspiration mode | /wf-auto-spark, $wf-auto-spark, /skills wf-auto-spark (explicit user token only) | [WF-AUTO-SPARK.md](WF-AUTO-SPARK.md), [WF-AUTO.md](WF-AUTO.md), [subagents.md](subagents.md), [dispatch.md](dispatch.md) | roadmap-anchored: North Star + milestones; external spark search; <=50% deviation guard; never auto-stops |
106
106
  | Need WF-MAX mode (explicit only) | /wf-max, $wf-max, /skills wf-max (explicit user token only) | [WF-MAX.md](WF-MAX.md), [WF-KERNEL.md](WF-KERNEL.md), [subagents.md](subagents.md), [dispatch.md](dispatch.md) | /wf kernel + max safe fan-out (WF-Max-Useful default, WF-Max-Strict override) |
107
- | Need peer review | /wf-review, $wf-review, peer review, second opinion, cross-check, stuck | `.claude/skills/wf-review/SKILL.md`, `.agents/skills/wf-review/SKILL.md`, `Harness/README.md` | cross-model multi-dimension review with severity classification |
107
+ | Need peer review | /wf-review, $wf-review, peer review, second opinion, cross-check, stuck | `.claude/skills/wf-review/SKILL.md`, `.agents/skills/wf-review/SKILL.md`, `Harness/README.md`, `.opencode/commands/wf-review.md` | peer CLI when available; otherwise installed `reviewer` role subagent; controller decides |
108
108
  | Adding harness to existing project | existing project, onboarding, migrate, bootstrap, preserve, conflict | [extension.md](extension.md), [PROGRESS.md](PROGRESS.md), root `README.md` and package/CI files | discovered project facts, preserved config, manual registration plan |
109
109
  | README optimization | README, docs, quickstart, install docs, architecture diagram, command table, documentation polish | root `README.md`, `.claude/skills/wf-readme/SKILL.md`, [PROGRESS.md](PROGRESS.md), [architecture.md](architecture.md) as needed | approved README mode, preserved sections, proposed diff plan |
110
110
  | Need implementation plan | plan, task, write set, verify, milestone, progress | [PROGRESS.md](PROGRESS.md), the current task `tasks/<id>/PROGRESS.md` and `tasks/<id>/PLAN.md`, [agent-workflow.md](agent-workflow.md), [ACCEPTANCE_PROTOCOL.md](ACCEPTANCE_PROTOCOL.md) | tasks, AC IDs, write set, verification commands |
@@ -124,7 +124,7 @@ Routing priority: **direct mode is the default** when no explicit WF token is pr
124
124
  - Use `/wf <task>` in Claude Code or `$wf` in Codex, or `/wf-max [task]` / `$wf-max` for maximum-parallelism mode. WF mode is explicit only. WF-Light for low-risk, WF-Standard for multi-file, WF-Full for high-risk/cross-layer. `/wf-max` defaults to useful-fanout (WF-Max-Useful); use explicit `--strict` or "strict wf-max" for unconditional fan-out (WF-Max-Strict).
125
125
  - Use `/wf-auto` for perpetual self-directed optimization. It selects probes from project evidence and stops only after dynamic risk obligations and two different empty confirmation passes are recorded.
126
126
  - **WF-MAX Role Contract**: Three-layer architecture: global mode (`wf-max`), agent role (`ceo|manager|worker|verifier|reviewer|reflector`), dispatch permission (`writeSet`, `forbidden`, `verification`). CEO never writes source code. Workers edit only dispatch.writeSet. Compliance is checked through dispatch packets, independent review, validation evidence, and task capsules. See `CLAUDE.md#1a`.
127
- - **WF-REVIEW Anti-Self-Review**: Must invoke the OTHER CLI (Codex -> Claude, or Claude -> Codex). Same-model simulation is forbidden.
127
+ - **WF-REVIEW Independence**: Prefer a peer CLI (`claude`, `codex`, or `opencode`) that is not the current runtime. If none exists, dispatch the installed `reviewer` role as a separate subagent context. Same-runtime fallback is independent-context review, not cross-model review; the main agent owns final decisions.
128
128
  - WF-MAX has no runtime hook state. The durable state is the task capsule, dispatch table, review findings, and validation evidence. The only runtime hook exception in Harness is the optional `/wf-auto` bounded tick hook described in `WF-AUTO.md`.
129
129
  - Do not code before PRD-GATE, AC-GATE, CONTRACT-GATE, and TEST-GATE are satisfied or explicitly compressed into a documented fast lane.
130
130
  - PRD-derived Acceptance Criteria are the source of truth. Code, tests, reviews, validation, debug, and memory must trace to AC IDs.
@@ -176,7 +176,7 @@ Routing priority: **direct mode is the default** when no explicit WF token is pr
176
176
  | `/wf-max [task]` | `$wf-max [task]` | WF-Max-Useful default (fan-out only where independent), WF-Max-Strict override (unconditional fan-out) |
177
177
  | `/wf-auto` | `$wf-auto` | Perpetual adaptive auto-optimization: dynamic probes, risk obligations, evidence-based exhaustion |
178
178
  | `/wf-auto-spark` | `$wf-auto-spark` | Perpetual inspiration: spark search, roadmap-anchored, <=50% deviation guard, never auto-stops |
179
- | `/wf-review [focus]` | `$wf-review [focus]` | Cross-model peer review via Codex <-> Claude |
179
+ | `/wf-review [focus]` | `$wf-review [focus]` | Peer CLI review via Claude/Codex/OpenCode, or installed reviewer subagent fallback |
180
180
  | `/wf-learn` | `$wf-learn` | Force learning cycle: context-master -> memory-master |
181
181
  | `/wf-browser [task]` | `$wf-browser [task]` | AI-driven browser automation for E2E testing |
182
182
  | `/wf-readme [task]` | `$wf-readme [task]` | README preservation, merge, and documentation improvement workflow |
@@ -132,7 +132,7 @@ The A-GATE is evidence-based:
132
132
  5. the CEO records unresolved uncertainty, skipped probes, and why oracle or
133
133
  spark search was or was not needed.
134
134
 
135
- The cross-model oracle is triggered by unresolved high-risk uncertainty or a
135
+ The peer-review oracle is triggered by unresolved high-risk uncertainty or a
136
136
  borderline exhaustion result. Spark search is triggered by a genuine value gap,
137
137
  not merely because a catalog was empty. This prevents both premature stopping
138
138
  and pointless full-tree scanning.
@@ -205,7 +205,7 @@ TIER 2 — Did any selected probe find an actionable direction?
205
205
  └── NO → Run a confirmation pass with a different scan strategy.
206
206
 
207
207
  TIER 3 — Is uncertainty still high or coverage borderline?
208
- ├── YES → Re-run only the uncertain probe, or invoke the cross-model oracle.
208
+ ├── YES → Re-run only the uncertain probe, or invoke the peer-review oracle.
209
209
  └── NO → Record an empty confirmation pass.
210
210
 
211
211
  TIER 4 — Two different confirmation strategies are empty?
@@ -215,9 +215,10 @@ TIER 4 — Two different confirmation strategies are empty?
215
215
 
216
216
  **Oracle Rules (modeled on /wf-review):**
217
217
 
218
- - [ ] CEO detects which CLI is running: `which codex` / `which claude`
219
- - [ ] CEO invokes the OTHER CLI only when unresolved high-risk uncertainty or borderline coverage justifies it
220
- - [ ] If neither CLI is available, record "oracle unavailable" in PROGRESS.md and continue with local confirmation
218
+ - [ ] CEO detects available peer CLIs: `claude`, `codex`, and `opencode`
219
+ - [ ] CEO invokes a peer CLI only when unresolved high-risk uncertainty or borderline coverage justifies it
220
+ - [ ] If no peer CLI is available, dispatch the installed `reviewer` role as an independent subagent context
221
+ - [ ] If neither peer CLI nor subagent surface is available, record "oracle unavailable" in PROGRESS.md and continue with local confirmation
221
222
  - [ ] Oracle is invoked at most once per adaptive exhaustion event
222
223
  - [ ] Oracle findings are treated as severity=high by default (external model perspective gets extra weight)
223
224
 
@@ -360,7 +361,7 @@ CEO presents:
360
361
 
361
362
  ### Spark: External Candidate Provider
362
363
 
363
- Spark is NOT a separate optimization engine. It is a **candidate provider** plugged into W0, alongside the adaptive probe scan and the cross-model oracle. W1 still owns prioritization across ALL sources.
364
+ Spark is NOT a separate optimization engine. It is a **candidate provider** plugged into W0, alongside the adaptive probe scan and the peer-review oracle. W1 still owns prioritization across ALL sources.
364
365
 
365
366
  **When spark activates:**
366
367
  - W0 internal scan returns empty AND oracle also empty → `auto.spark` state
@@ -398,7 +399,7 @@ Spark is NOT a separate optimization engine. It is a **candidate provider** plug
398
399
 
399
400
  W1 prioritization now handles three source types:
400
401
  - `source=internal` — from adaptive probe scan
401
- - `source=oracle` — from cross-model review
402
+ - `source=oracle` — from peer CLI or reviewer-subagent review
402
403
  - `source=spark-<name>` — from external inspiration search
403
404
 
404
405
  Tie-breaking: internal > oracle > spark (local context beats external inspiration).
@@ -490,14 +491,14 @@ Mini PRD-derived AC IDs in `/wf-auto`.
490
491
  |-----------|-----|---------|----------|
491
492
  | Scope | Task-bounded | Task-bounded | Unbounded |
492
493
  | Stop condition | Task complete | Task complete | Dynamic obligations covered + two different empty confirmation passes |
493
- | Direction | User-specified | User-specified | AI-inferred + cross-model oracle + external spark |
494
+ | Direction | User-specified | User-specified | AI-inferred + peer-review oracle + external spark |
494
495
  | Organization | Flat (CEO + agents) | 3-tier (CEO→Mgr→Worker) | Flat (CEO + selected probes + oracle + spark searchers + build agents) |
495
496
  | Duration | One task | One task | Perpetual |
496
497
  | User interaction | At key gates | At key gates | Adaptive checkpoint (2→5→10 cycles), 2 questions only |
497
498
  | Cycle count | 1 | 1 (multi-wave) | ∞ (until exhaustion) |
498
499
  | Files/cycle | Per task | Per wave (many) | ≤3 per cycle |
499
500
  | Exploration | 3-5 agents once | 5-10 agents once | Dynamic probes + triggered spark sources per cycle |
500
- | Cross-model check | No (wf-review is separate) | No (wf-review is separate) | Yes — Cross-Model Oracle built into A-GATE Tier 1.5 |
501
+ | Peer review check | No (wf-review is separate) | No (wf-review is separate) | Yes — peer-review oracle built into A-GATE Tier 1.5 |
501
502
  | External inspiration | No | No | Yes — Spark candidate provider when internal sources empty |
502
503
  | Evidence tracking | Per task | Per task | Evidence ledger per cycle with measured impact |
503
504
 
@@ -80,7 +80,7 @@ D-GATE is mandatory before implementation waves per [WF-KERNEL.md](WF-KERNEL.md)
80
80
 
81
81
  1. Current runtime subagents first.
82
82
  2. Close completed agents; fill idle slots immediately.
83
- 3. Cross-CLI overflow: Codex `claude -p`, Claude `codex exec`.
83
+ 3. Cross-CLI overflow: use an available peer CLI with explicit dispatch packets: `claude -p`, `codex exec`, or `opencode run --agent <role> --dir .`.
84
84
  4. Bounded-pass fallback only when subagents and overflow are exhausted.
85
85
  5. Generated Codex config defaults to `agents.max_threads = 12` and `agents.max_depth = 1`. Ask the user before raising `agents.max_threads` above that default. Keep `max_depth = 1` unless recursive delegation is explicitly approved.
86
86
  6. Do not rely on Codex++, undocumented config, environment variables, forked/derived conversations, or third-party forks as stable capacity.
@@ -13,7 +13,7 @@ Use when work needs parallel reading, independent review, cross-layer analysis,
13
13
  - Important assumptions, decisions, blockers, evidence, and handoffs must be written to `Harness/tasks/<task-id>/PROGRESS.md` and `Harness/tasks/<task-id>/PLAN.md`, the current feature doc, `Harness/MEMORY.md`, or `Harness/memory/*` as appropriate.
14
14
  - Resumable state is governed by [WF-STATE.md](WF-STATE.md). On session start, the controller reads STATE.json before building a fresh dispatch table.
15
15
  - PRD-derived Acceptance Criteria are the source of truth. Dispatch packets must carry the relevant AC IDs and contracts.
16
- - Agent count: default (non-WF) <=3 active agents; `/wf` selects a tier dynamically per [WF-KERNEL.md](WF-KERNEL.md) (Light/Standard/Full); `/wf-max` inherits the selected tier and adds maximum safe fan-out (WF-Max-Useful default; WF-Max-Strict explicit only), removing the Harness default cap through the span formula. Real concurrency is still bounded by runtime thread budget, config, billing, and local resources. Use current runtime subagents first, close completed agents, then cross-CLI overflow. Generated Codex config defaults to `agents.max_threads = 12` and `agents.max_depth = 1`; ask the user before raising `agents.max_threads` above that default. See [WF.md](WF.md) and [WF-MAX.md](WF-MAX.md).
16
+ - Agent count: default (non-WF) <=3 active agents; `/wf` selects a tier dynamically per [WF-KERNEL.md](WF-KERNEL.md) (Light/Standard/Full); `/wf-max` inherits the selected tier and adds maximum safe fan-out (WF-Max-Useful default; WF-Max-Strict explicit only), removing the Harness default cap through the span formula. Real concurrency is still bounded by runtime thread budget, config, billing, and local resources. Use current runtime subagents first, close completed agents, then peer-CLI overflow (`claude -p`, `codex exec`, or `opencode run --agent <role> --dir .`). Generated Codex config defaults to `agents.max_threads = 12` and `agents.max_depth = 1`; ask the user before raising `agents.max_threads` above that default. See [WF.md](WF.md) and [WF-MAX.md](WF-MAX.md).
17
17
  - Read-only agents may run in parallel.
18
18
  - Writing agents run serially unless write sets are disjoint.
19
19
  - Use a worktree when two agents may touch overlapping files or long-running branches.
@@ -65,6 +65,7 @@ const MANAGED_SUBDIRS = new Set([
65
65
 
66
66
  /** Reject paths that escape ROOT (traversal, absolute, .., etc.). */
67
67
  function safePath(file) {
68
+ if (/^[A-Za-z]:/.test(file)) return null;
68
69
  let normalized = file.replace(/\\/g, '/').replace(/^\/+/, '');
69
70
  if (/\/\//.test(normalized)) return null;
70
71
  if (normalized.split('/').some(p => p === '..')) return null;
@@ -97,6 +97,7 @@ const required = [
97
97
  '.opencode/commands/wf-help.md',
98
98
  '.opencode/commands/wf-update.md',
99
99
  ...opencodeWorkflowCommands.map(command => `.opencode/commands/${command}.md`),
100
+ '.opencode/plugins/harness-wf-status.mjs',
100
101
  '.claude/rules/ecc/common.md',
101
102
  ...commonAgents.map(agent => `.claude/agents/${agent}.md`),
102
103
  ...commonAgents.map(agent => `.opencode/agents/${agent}.md`),
@@ -725,6 +726,11 @@ requireText('.claude/skills/tdd/SKILL.md', 'No syntax-only acceptance', 'tdd ski
725
726
  requireText('.claude/skills/wf-remove/SKILL.md', 'User-facing removal is the slash/skill command', 'wf-remove slash command is user-facing');
726
727
  requireText('.claude/skills/wf-remove/SKILL.md', 'agent-internal execution steps', 'wf-remove script commands are agent-internal');
727
728
  requireText('.claude/skills/wf-remove/SKILL.md', 'verify residual discovery folders', 'wf-remove residual discovery verification');
729
+ requireText('.claude/skills/wf-review/SKILL.md', 'opencode run --agent reviewer', 'wf-review OpenCode peer CLI path');
730
+ requireText('.claude/skills/wf-review/SKILL.md', 'Role: reviewer', 'wf-review installed reviewer role fallback');
731
+ requireText('.claude/skills/wf-review/SKILL.md', 'The main agent is the controller', 'wf-review controller final authority');
732
+ requireText('.opencode/commands/wf-review.md', 'peer-review contract', 'OpenCode wf-review wrapper peer-review contract');
733
+ requireText('Harness/subagents.md', 'For `/wf-review`, use the installed `reviewer` role', 'subagents wf-review role fallback');
728
734
  requireText('.claude/agents/tdd-guide.md', 'Browser Acceptance Rules', 'tdd-guide browser acceptance rules');
729
735
  requireText('.claude/agents/tdd-guide.md', 'real user actions', 'tdd-guide real user action requirement');
730
736
  requireText('.claude/agents/test-writer.md', 'Harness/ACCEPTANCE_PROTOCOL.md', 'test-writer loads acceptance protocol');
@@ -766,6 +772,12 @@ forbidText('.claude/settings.json', 'wf-mode-hook.mjs', 'Claude WF hook command
766
772
  forbidText('.codex/hooks.json', 'wf-mode-hook.mjs', 'Codex WF hook command registration');
767
773
  const codexHookConfig = read('.codex/hooks.json');
768
774
  const claudeSettings = read('.claude/settings.json');
775
+ requireText('.codex/hooks.json', '"SessionStart"', 'Codex startup-only update hook');
776
+ requireText('.claude/settings.json', '"SessionStart"', 'Claude startup-only update hook');
777
+ forbidText('.codex/hooks.json', 'UserPromptSubmit', 'Codex turn-by-turn update hook');
778
+ forbidText('.claude/settings.json', 'UserPromptSubmit', 'Claude turn-by-turn update hook');
779
+ requireText('.opencode/plugins/harness-wf-status.mjs', 'opencode.startup', 'OpenCode startup-only update check');
780
+ forbidText('.opencode/plugins/harness-wf-status.mjs', "'chat.message'", 'OpenCode turn-by-turn update hook');
769
781
  if (codexHookConfig && !codexHookConfig.includes('wf-auto')) {
770
782
  errors.push('.codex/hooks.json may only exist for a wf-auto hook configuration');
771
783
  }
@@ -142,7 +142,7 @@ function buildMessage(update) {
142
142
  `Harness update available: ${from} -> ${to}.`,
143
143
  `Before unrelated work, ask the user whether to run /wf-update.`,
144
144
  ];
145
- // Only include summary counts never list individual conflict files in hook output
145
+ // Only include summary counts; never list individual conflict files in hook output.
146
146
  if (update.conflict > 0) {
147
147
  parts.push(`${update.conflict} conflict file(s) will need agent/user merge decisions.`);
148
148
  }
@@ -174,7 +174,7 @@ function emit(message, payload = {}) {
174
174
  if (context) {
175
175
  console.log(JSON.stringify({
176
176
  hookSpecificOutput: {
177
- hookEventName: input.hook_event_name || 'UserPromptSubmit',
177
+ hookEventName: input.hook_event_name || 'SessionStart',
178
178
  additionalContext: context,
179
179
  },
180
180
  }));
@@ -214,6 +214,7 @@ const CLEANUP_DIRS = [
214
214
 
215
215
  /** Reject paths that escape ROOT (traversal, absolute, .., etc.). Sync with wf-update-check. */
216
216
  function safePath(file) {
217
+ if (/^[A-Za-z]:/.test(file)) return null;
217
218
  let normalized = file.replace(/\\/g, '/').replace(/^\/+/, '');
218
219
  if (normalized.includes('//')) return null; // double slash bypass
219
220
  if (normalized.split('/').some(p => p === '..')) return null;
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * wf-update-check.mjs Fast harness update comparison.
3
+ * wf-update-check.mjs - Fast harness update comparison.
4
4
  * Fetches remote checksums, compares locally, classifies all files instantly.
5
5
  * Only CONFLICT files need AI/user decision.
6
6
  *
@@ -23,9 +23,13 @@ import { fileURLToPath } from 'url';
23
23
  const __dirname = dirname(fileURLToPath(import.meta.url));
24
24
  const ROOT = process.env.WF_ROOT ? resolve(process.env.WF_ROOT) : resolve(__dirname, '..', '..');
25
25
  const VERSION_FILE = resolve(ROOT, 'Harness', '.harness-version');
26
- const DEFAULT_SOURCE_BASE = 'https://raw.githubusercontent.com/zingspark/create-harness-vibe-coding/main/templates/common/';
26
+ const GITHUB_REPO = 'zingspark/create-harness-vibe-coding';
27
+ const GITHUB_LATEST_STABLE = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
28
+ const RAW_GITHUB = `https://raw.githubusercontent.com/${GITHUB_REPO}`;
29
+ const TEMPLATE_SUBPATH = 'templates/common/';
30
+ const DEFAULT_SOURCE_BASE = `${RAW_GITHUB}/main/${TEMPLATE_SUBPATH}`;
27
31
 
28
- // ── Tier classification ──────────────────────────────────────────
32
+ // Tier classification
29
33
 
30
34
  /** Files we NEVER overwrite or delete. */
31
35
  const PRESERVE_PATTERNS = [
@@ -56,10 +60,15 @@ const OPTIONAL_REGISTRATION_FILES = new Set([
56
60
  'Harness/README.md',
57
61
  ]);
58
62
 
59
- // ── Helpers ────────────────────────────────────────────────────────
63
+ const BOOTSTRAP_ONLY_FILES = new Set([
64
+ 'Harness/SETUP.md',
65
+ ]);
66
+
67
+ // Helpers
60
68
 
61
69
  /** Reject paths that escape ROOT (traversal, absolute, .., etc.). */
62
70
  function safePath(file) {
71
+ if (/^[A-Za-z]:/.test(file)) return null;
63
72
  let normalized = file.replace(/\\/g, '/').replace(/^\/+/, '');
64
73
  if (/\/\//.test(normalized)) return null;
65
74
  if (normalized.split('/').some(p => p === '..')) return null;
@@ -136,7 +145,7 @@ function sha256(content) {
136
145
  function sha256File(path) {
137
146
  if (!existsSync(path)) return null;
138
147
  let content = readFileSync(path, 'utf-8');
139
- // Normalize CRLF LF
148
+ // Normalize CRLF to LF.
140
149
  content = content.replace(/\r\n/g, '\n');
141
150
  return sha256(content);
142
151
  }
@@ -146,16 +155,18 @@ function classify(file, localHash, storedHash) {
146
155
  for (const p of PRESERVE_PATTERNS) {
147
156
  if (p.test(file)) return 'PRESERVE';
148
157
  }
149
- // MERGE dual-purpose, check if user modified
158
+ // MERGE: dual-purpose, check if user modified.
150
159
  for (const p of MERGE_PATTERNS) {
151
160
  if (p.test(file)) {
152
161
  if (localHash === storedHash) return 'SAFE'; // unmodified, safe
153
162
  return 'CONFLICT'; // user modified, needs decision
154
163
  }
155
164
  }
156
- // Everything else is SAFE runtime file
157
- if (localHash === storedHash || localHash === null) return 'SAFE';
158
- return 'CONFLICT'; // modified runtime file unexpected
165
+ // Everything else is SAFE runtime file: always overwrite.
166
+ // Harness system files (scripts, skills, agents, commands, WF docs) are
167
+ // not user data; the template is authoritative. Only PRESERVE and MERGE
168
+ // files should ever require conflict resolution.
169
+ return 'SAFE';
159
170
  }
160
171
 
161
172
  async function fetchRemote(url, timeoutMs = 30000) {
@@ -174,7 +185,49 @@ async function fetchRemote(url, timeoutMs = 30000) {
174
185
  }
175
186
  }
176
187
 
177
- // ── Main ───────────────────────────────────────────────────────────
188
+ // Main
189
+
190
+ function isPrerelease(v) {
191
+ if (!v || typeof v !== 'string') return false;
192
+ return /-[0-9A-Za-z.-]+/.test(v.replace(/^[^0-9]*/, ''));
193
+ }
194
+
195
+ async function resolveStableSourceBase() {
196
+ try {
197
+ const controller = new AbortController();
198
+ const timer = setTimeout(() => controller.abort(), 15000);
199
+ let res;
200
+ try {
201
+ res = await fetch(GITHUB_LATEST_STABLE, {
202
+ signal: controller.signal,
203
+ headers: { Accept: 'application/vnd.github+json', 'User-Agent': 'harness-wf-update-check' },
204
+ });
205
+ } finally {
206
+ clearTimeout(timer);
207
+ }
208
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
209
+ const data = JSON.parse(await res.text());
210
+ const tag = data && data.tag_name;
211
+ if (!tag || data.prerelease) return null;
212
+ const source = `${RAW_GITHUB}/${tag}/${TEMPLATE_SUBPATH}`;
213
+ const versionController = new AbortController();
214
+ const versionTimer = setTimeout(() => versionController.abort(), 15000);
215
+ try {
216
+ const versionRes = await fetch(source + '.harness-version', {
217
+ signal: versionController.signal,
218
+ headers: { 'User-Agent': 'harness-wf-update-check' },
219
+ });
220
+ if (!versionRes.ok) return null;
221
+ const raw = await versionRes.text();
222
+ if (isTemplate(raw)) return null;
223
+ } finally {
224
+ clearTimeout(versionTimer);
225
+ }
226
+ return source;
227
+ } catch {
228
+ return null;
229
+ }
230
+ }
178
231
 
179
232
  async function main() {
180
233
  const args = process.argv.slice(2);
@@ -186,7 +239,16 @@ async function main() {
186
239
  const acceptMerged = readRepeatedFlagValues(args, '--accept-merged');
187
240
  const acceptTemplate = readRepeatedFlagValues(args, '--accept-template');
188
241
  const ignoreVersion = args.includes('--ignore-version') || args.includes('--force-check');
189
- const sourceBase = normalizeSourceBase(readFlagValue(args, '--source-base') || process.env.WF_SOURCE_BASE || DEFAULT_SOURCE_BASE);
242
+ const explicitSource = readFlagValue(args, '--source-base') || process.env.WF_SOURCE_BASE;
243
+ let sourceBase = normalizeSourceBase(explicitSource || DEFAULT_SOURCE_BASE);
244
+ let stableTagResolved = false;
245
+ if (!explicitSource) {
246
+ const resolved = await resolveStableSourceBase();
247
+ if (resolved) {
248
+ sourceBase = normalizeSourceBase(resolved);
249
+ stableTagResolved = true;
250
+ }
251
+ }
190
252
 
191
253
  // 1. Read local state
192
254
  if (!existsSync(VERSION_FILE)) {
@@ -248,9 +310,9 @@ async function main() {
248
310
  if (jsonOut) {
249
311
  console.log(JSON.stringify({ status: 'template-remote', message: 'Remote .harness-version has not been generated yet.' }));
250
312
  } else {
251
- console.log(' Remote .harness-version is a template (contains {{placeholders}}).');
313
+ console.log('WARN: Remote .harness-version is a template (contains {{placeholders}}).');
252
314
  console.log(' The generate step has not been run on the remote repo. No update possible.');
253
- console.log(' This is expected during development the update mechanism works once the remote is live.');
315
+ console.log(' This is expected during development; the update mechanism works once the remote is live.');
254
316
  }
255
317
  process.exitCode = 1;
256
318
  return;
@@ -267,10 +329,10 @@ async function main() {
267
329
  return;
268
330
  }
269
331
 
270
- // Compare versions warn if remote is older (downgrade prevention)
332
+ // Compare versions and prevent downgrades from explicit custom sources.
271
333
  function parseSemver(v) {
272
334
  if (!v || typeof v !== 'string') return [0, 0, 0];
273
- return v.replace(/^[^0-9]*/, '').split('-')[0].split('.').map(Number);
335
+ return v.replace(/^[^0-9]*/, '').split('.').slice(0, 3).map(n => Number(n) || 0);
274
336
  }
275
337
  function cmpSemver(a, b) {
276
338
  const va = parseSemver(a), vb = parseSemver(b);
@@ -280,27 +342,38 @@ async function main() {
280
342
 
281
343
  const localGen = localVersion.generator || '0.0.0';
282
344
  const remoteGen = remoteVersion.generator || '0.0.0';
345
+
346
+ if (!ignoreVersion && isPrerelease(remoteGen)) {
347
+ if (jsonOut) {
348
+ console.log(JSON.stringify({ status: 'up-to-date', version: localGen, remote: remoteGen, sourceBase }));
349
+ } else {
350
+ console.log(`Already up to date (v${localGen}). Remote ${remoteGen} is a prerelease and is ignored.`);
351
+ }
352
+ return;
353
+ }
354
+
283
355
  const versionCmp = cmpSemver(remoteGen, localGen);
284
356
 
285
357
  if (!ignoreVersion && versionCmp <= 0) {
358
+ const reportDowngrade = !stableTagResolved;
286
359
  if (jsonOut) {
287
360
  console.log(JSON.stringify({
288
- status: versionCmp < 0 ? 'downgrade-refused' : 'up-to-date',
361
+ status: (versionCmp < 0 && reportDowngrade) ? 'downgrade-refused' : 'up-to-date',
289
362
  version: localGen,
290
363
  remote: remoteGen,
291
364
  sourceBase,
292
365
  }));
293
- } else if (versionCmp < 0) {
294
- console.log(`⚠ Remote (v${remoteGen}) is OLDER than local (v${localGen}). Downgrade refused.`);
366
+ } else if (versionCmp < 0 && reportDowngrade) {
367
+ console.log(`WARN: Remote (v${remoteGen}) is OLDER than local (v${localGen}). Downgrade refused.`);
295
368
  } else {
296
- console.log(`✅ Already up to date (v${localGen})`);
369
+ console.log(`Already up to date (v${localGen})`);
297
370
  }
298
- if (versionCmp < 0) process.exitCode = 1;
371
+ if (versionCmp < 0 && reportDowngrade) process.exitCode = 1;
299
372
  return;
300
373
  }
301
374
 
302
375
  if (ignoreVersion) {
303
- if (!jsonOut) console.log('🔧 Version check bypassed (--ignore-version). Comparing files anyway.');
376
+ if (!jsonOut) console.log('Version check bypassed (--ignore-version). Comparing files anyway.');
304
377
  }
305
378
 
306
379
  const remoteChecksums = remoteVersion.checksums || {};
@@ -469,8 +542,13 @@ async function main() {
469
542
  continue;
470
543
  }
471
544
 
545
+ if (BOOTSTRAP_ONLY_FILES.has(canonical) && localHash === null) {
546
+ plan.skipped.push({ file, reason: 'bootstrap-only file already removed locally' });
547
+ continue;
548
+ }
549
+
472
550
  if (!storedHash) {
473
- // New file from remote if local file exists, it's a CONFLICT
551
+ // New file from remote: if local file exists, it is a CONFLICT.
474
552
  if (localHash) {
475
553
  if (localHash === remoteHash) {
476
554
  plan.adopted.push({ file, localHash, remoteHash, reason: 'new remote file already matches local file' });
@@ -483,10 +561,10 @@ async function main() {
483
561
  plan.conflict.push({ file, localHash, storedHash: 'none', remoteHash, reason: 'new remote file conflicts with existing local file' });
484
562
  continue;
485
563
  }
486
- // New file still respect PRESERVE classification
564
+ // New file: still respect PRESERVE classification.
487
565
  const tier = classify(canonical, null, null);
488
566
  if (tier === 'PRESERVE') {
489
- plan.skipped.push({ file, reason: 'PRESERVE new file would overwrite user data' });
567
+ plan.skipped.push({ file, reason: 'PRESERVE: new file would overwrite user data' });
490
568
  } else {
491
569
  plan.created.push({ file, remoteHash });
492
570
  }
@@ -517,7 +595,7 @@ async function main() {
517
595
  const tier = classify(canonical, localHash, storedHash);
518
596
 
519
597
  if (tier === 'PRESERVE') {
520
- plan.skipped.push({ file, reason: 'PRESERVE user data' });
598
+ plan.skipped.push({ file, reason: 'PRESERVE: user data' });
521
599
  } else if (tier === 'SAFE') {
522
600
  if (localHash === remoteHash) {
523
601
  plan.skipped.push({ file, reason: 'already current' });
@@ -564,14 +642,14 @@ async function main() {
564
642
  return;
565
643
  }
566
644
 
567
- console.log(`\n🔄 Update: v${localGen} v${remoteGen}`);
645
+ console.log(`\nUpdate: v${localGen} -> v${remoteGen}`);
568
646
  console.log(` ${plan.updated.length} safe update, ${plan.created.length} new, ${plan.conflict.length} conflict, ${plan.skipped.length} skipped\n`);
569
647
 
570
648
  // Show conflicts (these need AI/user decision)
571
649
  if (plan.conflict.length > 0) {
572
- console.log('CONFLICTS (need your decision):');
650
+ console.log('CONFLICTS (need your decision):');
573
651
  for (const c of plan.conflict) {
574
- console.log(` 📄 ${c.file} [${c.reason}]`);
652
+ console.log(` ! ${c.file} [${c.reason}]`);
575
653
  }
576
654
  if (plan.updated.length + plan.created.length > 0) {
577
655
  console.log(' Tip: run --apply-safe to apply SAFE/NEW files first, then merge conflicts.');
@@ -581,17 +659,17 @@ async function main() {
581
659
 
582
660
  // Show what will be auto-updated
583
661
  if (plan.updated.length + plan.created.length > 0) {
584
- console.log('AUTO (safe to apply):');
585
- for (const u of plan.updated) console.log(` ${u.file}`);
662
+ console.log('AUTO (safe to apply):');
663
+ for (const u of plan.updated) console.log(` ^ ${u.file}`);
586
664
  for (const c of plan.created) console.log(` + ${c.file}`);
587
665
  console.log('');
588
666
  }
589
667
 
590
668
  // 4. Apply if requested
591
669
  if (apply || applySafe) {
592
- // Refuse to apply when conflicts exist must resolve first
670
+ // Refuse to apply when conflicts exist; must resolve first.
593
671
  if (apply && !applySafe && plan.conflict.length > 0) {
594
- console.log(`❌ Cannot apply: ${plan.conflict.length} conflicts must be resolved first.`);
672
+ console.log(`Cannot apply: ${plan.conflict.length} conflicts must be resolved first.`);
595
673
  console.log(' Run --apply-safe to apply SAFE/NEW files first, or resolve conflicts manually then re-run --apply.');
596
674
  process.exitCode = 1;
597
675
  return plan;
@@ -607,23 +685,23 @@ async function main() {
607
685
  for (const u of plan.updated) {
608
686
  try {
609
687
  const dest = safePath(u.file);
610
- if (!dest) { console.error(` Traversal rejected: ${u.file}`); failed++; continue; }
611
- // Symlink rejection don't follow symlinks
688
+ if (!dest) { console.error(` x Traversal rejected: ${u.file}`); failed++; continue; }
689
+ // Symlink rejection: do not follow symlinks.
612
690
  if (lexists(dest)) {
613
- try { if (lstatSync(dest).isSymbolicLink()) { console.error(` Symlink rejected: ${u.file}`); failed++; continue; } } catch (_) {}
691
+ try { if (lstatSync(dest).isSymbolicLink()) { console.error(` x Symlink rejected: ${u.file}`); failed++; continue; } } catch (_) {}
614
692
  }
615
693
  const content = await fetchRemote(sourceBase + remotePath(u.file));
616
694
  const normalized = content.replace(/\r\n/g, '\n');
617
695
  const fetchedHash = sha256(normalized);
618
696
  if (fetchedHash !== u.remoteHash) {
619
- console.error(` Hash mismatch: ${u.file}`);
697
+ console.error(` x Hash mismatch: ${u.file}`);
620
698
  failed++; continue;
621
699
  }
622
700
  mkdirSync(dirname(dest), { recursive: true });
623
701
  writeFileSync(dest, normalized, 'utf-8');
624
702
  applied++;
625
703
  } catch (e) {
626
- console.error(` Failed: ${u.file} ${e.message}`);
704
+ console.error(` x Failed: ${u.file} - ${e.message}`);
627
705
  failed++;
628
706
  }
629
707
  }
@@ -631,25 +709,25 @@ async function main() {
631
709
  for (const c of plan.created) {
632
710
  try {
633
711
  const dest = safePath(c.file);
634
- if (!dest) { console.error(` Traversal rejected: ${c.file}`); failed++; continue; }
712
+ if (!dest) { console.error(` x Traversal rejected: ${c.file}`); failed++; continue; }
635
713
  // TOCTOU: recheck file didn't appear since planning
636
714
  if (lexists(dest)) {
637
- try { if (lstatSync(dest).isSymbolicLink()) { console.error(` Symlink rejected: ${c.file}`); failed++; continue; } } catch (_) {}
638
- console.error(` File created since plan: ${c.file} treating as CONFLICT`);
715
+ try { if (lstatSync(dest).isSymbolicLink()) { console.error(` x Symlink rejected: ${c.file}`); failed++; continue; } } catch (_) {}
716
+ console.error(` x File created since plan: ${c.file} - treating as CONFLICT`);
639
717
  failed++; continue;
640
718
  }
641
719
  const content = await fetchRemote(sourceBase + remotePath(c.file));
642
720
  const normalized = content.replace(/\r\n/g, '\n');
643
721
  const fetchedHash = sha256(normalized);
644
722
  if (fetchedHash !== c.remoteHash) {
645
- console.error(` Hash mismatch: ${c.file}`);
723
+ console.error(` x Hash mismatch: ${c.file}`);
646
724
  failed++; continue;
647
725
  }
648
726
  mkdirSync(dirname(dest), { recursive: true });
649
727
  writeFileSync(dest, normalized, 'utf-8');
650
728
  applied++;
651
729
  } catch (e) {
652
- console.error(` Failed: ${c.file} ${e.message}`);
730
+ console.error(` x Failed: ${c.file} - ${e.message}`);
653
731
  failed++;
654
732
  }
655
733
  }
@@ -676,12 +754,12 @@ async function main() {
676
754
  }
677
755
  writeFileSync(VERSION_FILE, JSON.stringify(localVersion, null, 2) + '\n', 'utf-8');
678
756
  if (plan.conflict.length === 0) {
679
- console.log(`✅ Applied ${applied} files. Version updated to ${remoteVersion.generator}.`);
757
+ console.log(`Applied ${applied} files. Version updated to ${remoteVersion.generator}.`);
680
758
  } else {
681
- console.log(`✅ Applied ${applied} SAFE/NEW files. Version remains ${localGen}; ${plan.conflict.length} conflicts still need merge.`);
759
+ console.log(`Applied ${applied} SAFE/NEW files. Version remains ${localGen}; ${plan.conflict.length} conflicts still need merge.`);
682
760
  }
683
761
  } else {
684
- console.log(`❌ ${failed} failures. NO files were version-tracked. Fix and re-run.`);
762
+ console.log(`${failed} failures. NO files were version-tracked. Fix and re-run.`);
685
763
  process.exitCode = 1;
686
764
  }
687
765
  }
@@ -124,7 +124,7 @@ Choose the cheapest coordination level that is safe.
124
124
  Max parallelism removes the Harness default cap, not the runtime's physical or
125
125
  account cap. For WF-MAX, record the current runtime budget, use native
126
126
  subagents first, close completed agents before declaring the pool exhausted,
127
- then overflow to the other CLI (`claude -p` or `codex exec`) with explicit
127
+ then overflow to a peer CLI (`claude -p`, `codex exec`, or `opencode run --agent <role> --dir .`) with explicit
128
128
  dispatch packets. Generated Harness Codex config defaults to
129
129
  `agents.max_threads = 12` and `agents.max_depth = 1`; if that becomes the
130
130
  bottleneck, ask the user before raising `agents.max_threads` and keep
@@ -192,6 +192,13 @@ matrix from running behavior and evidence, not from the implementer's summary.
192
192
 
193
193
  If either reviewer finds issues, the implementer or debugger fixes them and the same gate runs again. Do not move to final acceptance with open critical/high findings or without reflector PASS.
194
194
 
195
+ For `/wf-review`, use the installed `reviewer` role before inventing any
196
+ ad hoc review prompt. If no peer CLI is available, dispatch `reviewer` as an
197
+ independent same-runtime subagent context; for broad WF-MAX review, dispatch
198
+ `review-manager` when the runtime supports nested reviewer fan-out. Reviewer
199
+ agents report suggestions only. The controller accepts, rejects, or escalates
200
+ each finding and owns the final decision.
201
+
195
202
  ## Subagent Status Handling
196
203
 
197
204
  | Status | Controller Action |
@@ -45,4 +45,4 @@ The agentic engineering harness lives in `Harness/`.
45
45
  Tool discovery files stay at the repository root:
46
46
 
47
47
  - Claude Code: `.claude/settings.json`, `.claude/agents/`, and `.claude/skills/`.
48
- - Codex: `.agents/skills/` for repo skills and `.codex/` for config placeholders. Runtime hooks are absent by default; only `/wf-auto` may opt into a bounded tick hook.
48
+ - Codex: `.agents/skills/` for repo skills and `.codex/` for config placeholders. The bundled update reminder uses a startup-only hook; avoid turn-by-turn runtime hooks unless `/wf-auto` explicitly opts into a bounded tick hook.