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 +1 -1
- package/README.md +1 -1
- package/package.json +2 -1
- package/templates/common/.claude/commands/wf-help.md +1 -1
- package/templates/common/.claude/settings.json +2 -2
- package/templates/common/.claude/skills/subagent-orchestrator/SKILL.md +2 -2
- package/templates/common/.claude/skills/wf-max/SKILL.md +3 -3
- package/templates/common/.claude/skills/wf-review/SKILL.md +63 -12
- package/templates/common/.codex/hooks.json +5 -5
- package/templates/common/.harness-version +26 -26
- package/templates/common/.opencode/commands/wf-help.md +1 -1
- package/templates/common/.opencode/commands/wf-review.md +2 -2
- package/templates/common/.opencode/plugins/harness-wf-status.mjs +12 -21
- package/templates/common/Harness/MEMORY.md +2 -2
- package/templates/common/Harness/README.md +3 -3
- package/templates/common/Harness/WF-AUTO-ANGLES.md +1 -1
- package/templates/common/Harness/WF-AUTO.md +9 -8
- package/templates/common/Harness/WF-MAX.md +1 -1
- package/templates/common/Harness/dispatch.md +1 -1
- package/templates/common/Harness/scripts/scan-clean.mjs +1 -0
- package/templates/common/Harness/scripts/validate-harness.mjs +12 -0
- package/templates/common/Harness/scripts/wf-auto-update-prompt.mjs +2 -2
- package/templates/common/Harness/scripts/wf-remove.mjs +1 -0
- package/templates/common/Harness/scripts/wf-update-check.mjs +123 -45
- package/templates/common/Harness/subagents.md +8 -1
- package/templates/common/README.md +1 -1
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 [重点]` |
|
|
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,
|
|
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.
|
|
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` |
|
|
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,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
|
|
35
|
-
(
|
|
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
|
|
74
|
-
dispatch packets:
|
|
75
|
-
`
|
|
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,
|
|
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
|
-
|
|
9
|
-
|
|
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
|
-
##
|
|
19
|
+
## Authority
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
28
|
-
|
|
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
|
|
85
|
+
narrower scope before invoking a peer CLI or reviewer subagent.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"hooks": {
|
|
3
|
-
"
|
|
3
|
+
"SessionStart": [
|
|
4
4
|
{
|
|
5
|
+
"matcher": "startup|resume|clear",
|
|
5
6
|
"hooks": [
|
|
6
7
|
{
|
|
7
8
|
"type": "command",
|
|
8
|
-
"command": "node \"
|
|
9
|
-
"
|
|
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.
|
|
3
|
-
"generated": "2026-07-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
42
|
-
".claude/skills/subagent-orchestrator/SKILL.md": "sha256-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
92
|
+
"Harness/MEMORY.md": "sha256-4cbb881b3e9893e917861dc250afaedf5b88da6c99626a9c7f6abb8132841b09",
|
|
93
93
|
"Harness/MEMORY_PROTOCOL.md": "sha256-c8cda68bf25fbf7152c6049959a8bd6d39453976a651a1c4a9d3f7e349f823c4",
|
|
94
|
-
"Harness/README.md": "sha256-
|
|
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-
|
|
98
|
+
"Harness/WF-AUTO-ANGLES.md": "sha256-b9641d871f728e8112ad0d8c7f87bcbec0a06aa572c5f3060efdd26496abdf9b",
|
|
99
99
|
"Harness/WF-AUTO-SPARK.md": "sha256-8fc879261f71f81c5a64e37a6aed8b72fd78ca01c5c9f318a15f4a0a594b4783",
|
|
100
|
-
"Harness/WF-AUTO.md": "sha256-
|
|
100
|
+
"Harness/WF-AUTO.md": "sha256-7898a38ee9ce9cf1c49aa7e3603c17afc68544981dca7d91e2e4149ca2fc5aea",
|
|
101
101
|
"Harness/WF-KERNEL.md": "sha256-97c626574f62dceaa590829d99e46889b86cab5658d7827c704788e4afdda030",
|
|
102
|
-
"Harness/WF-MAX.md": "sha256-
|
|
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-
|
|
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-
|
|
113
|
-
"Harness/scripts/validate-harness.mjs": "sha256-
|
|
114
|
-
"Harness/scripts/wf-auto-update-prompt.mjs": "sha256-
|
|
115
|
-
"Harness/scripts/wf-remove.mjs": "sha256-
|
|
116
|
-
"Harness/scripts/wf-update-check.mjs": "sha256-
|
|
117
|
-
"Harness/subagents.md": "sha256-
|
|
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` |
|
|
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
|
|
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
|
|
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 {
|
|
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:
|
|
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) -
|
|
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,
|
|
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` |
|
|
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
|
|
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]` |
|
|
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
|
|
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
|
|
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
|
|
219
|
-
- [ ] CEO invokes
|
|
220
|
-
- [ ] If
|
|
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
|
|
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
|
|
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 +
|
|
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
|
-
|
|
|
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:
|
|
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
|
|
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
|
|
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 || '
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
158
|
-
|
|
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
|
-
//
|
|
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
|
|
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('
|
|
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
|
|
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
|
|
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('
|
|
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(
|
|
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(
|
|
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('
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(`\
|
|
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('
|
|
650
|
+
console.log('CONFLICTS (need your decision):');
|
|
573
651
|
for (const c of plan.conflict) {
|
|
574
|
-
console.log(`
|
|
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('
|
|
585
|
-
for (const u of plan.updated) console.log(`
|
|
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
|
|
670
|
+
// Refuse to apply when conflicts exist; must resolve first.
|
|
593
671
|
if (apply && !applySafe && plan.conflict.length > 0) {
|
|
594
|
-
console.log(
|
|
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(`
|
|
611
|
-
// Symlink rejection
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
638
|
-
console.error(`
|
|
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(`
|
|
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(`
|
|
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(
|
|
757
|
+
console.log(`Applied ${applied} files. Version updated to ${remoteVersion.generator}.`);
|
|
680
758
|
} else {
|
|
681
|
-
console.log(
|
|
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(
|
|
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
|
|
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.
|
|
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.
|