@deftai/directive-content 0.96.0 → 0.97.0
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/Taskfile.yml +6 -0
- package/commands.md +14 -1
- package/contracts/test-boundary.md +18 -0
- package/docs/consumer-check-contract.md +27 -0
- package/docs/delivery-attempt.md +173 -0
- package/docs/openclaw-agent-host.md +36 -1
- package/docs/operator-log-hygiene-checklist.md +57 -0
- package/docs/operator-log-hygiene-consumer-pack-stub.md +75 -0
- package/docs/scope-provenance.md +54 -0
- package/docs/test-boundary.md +43 -0
- package/main.md +29 -0
- package/package.json +1 -1
- package/packs/patterns/patterns-pack-0.1.json +10 -0
- package/packs/skills/skills-pack-0.1.json +4 -4
- package/patterns/operator-log-hygiene.md +130 -0
- package/scm/github.md +26 -0
- package/skills/deft-directive-article-review/SKILL.md +17 -6
- package/skills/deft-directive-build/SKILL.md +41 -0
- package/skills/deft-directive-review-cycle/SKILL.md +150 -0
- package/skills/deft-directive-swarm/SKILL.md +14 -0
- package/skills/deft-directive-swarm/references/core-ops.md +14 -2
- package/skills/deft-directive-swarm/references/core-phase-0.md +34 -1
- package/skills/deft-directive-swarm/references/core-phase-3.md +6 -0
- package/skills/deft-directive-swarm/references/core-phase-4.md +21 -2
- package/skills/deft-directive-swarm/references/core-phase-5-6.md +17 -0
- package/skills/deft-directive-swarm/references/host-openclaw.md +4 -0
- package/tasks/engine-invoke.cjs +22 -17
- package/tasks/engine-invoke.test.cjs +30 -0
- package/tasks/verify.yml +30 -0
- package/templates/agent-prompt-preamble.md +8 -0
- package/templates/agents-entry.md +10 -0
- package/templates/swarm-greptile-poller-prompt.md +4 -2
|
@@ -60,6 +60,25 @@ tools: explore=0 commit=3 verify=0 coordinate=0 unknown=1 | anomalies: commit-wi
|
|
|
60
60
|
~ Pair the status line with worktree git checks and `task pr:merge-ready` so operators see progress **and** tool mix without reading raw tool logs.
|
|
61
61
|
⊗ Treat raw “ran N tools” as a structured mix — always bucket when events are available (#2967).
|
|
62
62
|
|
|
63
|
+
### Dual stop on monitor and repair loops (#2442)
|
|
64
|
+
|
|
65
|
+
! Phase 4 monitoring is multi-iteration work. It MUST obey dual stop (`main.md` `## Dual Stop Rule (#2442)`): **success** (leaf DONE with PR/merge evidence, or clean gate) **and** a **failure/budget** stop. One-shot status probes are exempt.
|
|
66
|
+
|
|
67
|
+
**Default failure envelope (monitor / repair class):**
|
|
68
|
+
|
|
69
|
+
| Stop | Default |
|
|
70
|
+
|------|---------|
|
|
71
|
+
| max iterations | **3** repair actions for the same leaf/PR failure class (resume prompt, takeover complete-remaining-steps, re-dispatch replacement, review re-trigger) |
|
|
72
|
+
| no-progress | same error / same Greptile finding class / same idle stage **3+** times with no material worktree or review change |
|
|
73
|
+
| budget | honor `pr:watch` / poll max-wait and Greptile service-error single-retry+escalate caps; do not nest an unbounded poll outside them |
|
|
74
|
+
|
|
75
|
+
! When the failure stop fires: **halt** automatic repair/re-dispatch; emit an **operator-visible halt report** (what was tried, current stage, missing evidence, human decision needed). Prefer `BLOCKED:` over thin `DONE` when the unit cannot reach merge-ready inside the envelope.
|
|
76
|
+
|
|
77
|
+
! Composes with minimal-subgraph repair (#2439): repairs stay minimal **and** dual-stop bounded. Mechanical delivery/acceptance circuit breaker: **#3143** `packages/core/src/delivery-attempt/` (`evaluatePreDispatch`). Docs: `docs/delivery-attempt.md`.
|
|
78
|
+
|
|
79
|
+
⊗ Silently continue the monitor repair loop after the envelope is exhausted.
|
|
80
|
+
⊗ Count a worker swap or session handoff as a fresh unlimited budget when the same failure class remains.
|
|
81
|
+
|
|
63
82
|
### Takeover Triggers
|
|
64
83
|
|
|
65
84
|
! **Pre-spawn verification:** Before spawning a replacement agent, verify the original is truly unresponsive by waiting for an idle/blocked lifecycle event — verified via worktree state (`git status`, `git log --oneline -3`) and sub-agent lifecycle signals showing no in-flight work (for grok-build / spawn_subagent agents: polling is via worktree state + `get_command_or_subagent_output` rather than tab observation; for openclaw / sessions_spawn agents: worktree state + parent completion announce / heartbeat records, not Grok Build poll output). Do NOT spawn a replacement based solely on message timing, absence of recent commits, or a perceived delay — original agents (Warp tabs, spawn_subagent processes, or OpenClaw sessions) can resume after apparent failure, and spawning a new agent creates two concurrent agents on the same worktree (see Duplicate-Tab Failure Mode below).
|
|
@@ -69,9 +88,9 @@ tools: explore=0 commit=3 verify=0 coordinate=0 unknown=1 | anomalies: commit-wi
|
|
|
69
88
|
- Agent process has exited and PR has not been created
|
|
70
89
|
- Agent process has exited and Greptile review cycle was not started
|
|
71
90
|
- Agent is idle for >5 minutes after PR creation with no review activity
|
|
72
|
-
- Agent is stuck in an error loop (same error 3+ times)
|
|
91
|
+
- Agent is stuck in an error loop (same error 3+ times) — this is a dual-stop no-progress signal (#2442); after takeover, remaining repair actions still count against the default failure envelope above
|
|
73
92
|
|
|
74
|
-
When taking over: read the agent's current state (git log, diff, PR comments), complete remaining steps manually following the same deft process.
|
|
93
|
+
When taking over: read the agent's current state (git log, diff, PR comments), complete remaining steps manually following the same deft process. If takeover itself cannot clear the failure class within the dual-stop envelope, halt with the operator-visible report rather than thrashing.
|
|
75
94
|
|
|
76
95
|
### Duplicate-Agent Failure Mode (a.k.a. Duplicate-Tab Failure Mode)
|
|
77
96
|
|
|
@@ -39,6 +39,21 @@ For each agent's PR:
|
|
|
39
39
|
|
|
40
40
|
! **Completion latch after first consolidate (#3092):** After the parent has emitted **one** user/caller-visible consolidate for a child `runId` / settle batch (accept or reject), **identical or equivalent completion replay** for the same key MUST be **silent** (no tools, no re-QC, no second final; host silent token when defined, e.g. OpenClaw `NO_REPLY`). Re-open only on new `runId`/batch, principal explicit reopen, or **materially new** evidence (new HEAD, new blocker class) — not fat re-embeds of the same rollup. Replay storms: at most one fail-loud note, then silent. Full MUST + anti-patterns: `templates/agent-prompt-preamble.md` §11.5. Orthogonal to empty settle ≠ done (#3044).
|
|
41
41
|
|
|
42
|
+
### Human-merge observe path for `stop-at: pr-open` (#3153)
|
|
43
|
+
|
|
44
|
+
! When any cohort story was dispatched **`stop-at: pr-open`** (or thin-DONE recovery handed merge path to babysit) **and** merge authority is human-only (`requireHumanMerge` / no bot merge), the monitor is the **default durable owner** of post-merge `scope:complete` (review-cycle partner merge-path). Greptile CLEAN alone is **not** lifecycle complete.
|
|
45
|
+
|
|
46
|
+
! **Required observe path before declaring the cohort closed:**
|
|
47
|
+
|
|
48
|
+
1. ! Keep a machine-checkable list of open cohort PRs marked `awaiting-human-merge` (PR number, HEAD, story xBRIEF path) in the monitor checkpoint.
|
|
49
|
+
2. ! Until each listed PR is `MERGED` (or closed without merge → cancel path), the monitor MUST use one of:
|
|
50
|
+
- **Background merge observer** (preferred on Tier 1): Approach 1 poller or short sub-agent that probes `gh api repos/<owner>/<repo>/pulls/<N>` for `merged == true` / `state`, then signals the parent; parent runs Step 1.5 sweep for that story.
|
|
51
|
+
- **Parent-retained re-entry:** parent keeps `review_cycle: in_progress:<pr>#parent-retained` and **MUST** re-check merge state on every re-invocation / next tool turn until merged — first tool action on re-entry is the merge-state probe.
|
|
52
|
+
- **Phase 6 Step 1 pre-sweep re-poll (always):** Immediately before Step 1.5 `task swarm:complete-cohort` / `task swarm:finalize-cohort`, re-poll **every** cohort PR via REST `pulls/<N>` and refuse the sweep while any `awaiting-human-merge` PR is still open. ⊗ Run the completion sweep solely because Greptile was CLEAN earlier.
|
|
53
|
+
3. ! After merge is observed: run post-merge verification (closing keywords) then Step 1.5 sweep / `task scope:complete` for the story.
|
|
54
|
+
|
|
55
|
+
Cross-link: `skills/deft-directive-review-cycle/SKILL.md` § Partner merge-path / Post-CLEAN wake path. ⊗ Ownership-in-name-only (sticky lease with no observer).
|
|
56
|
+
|
|
42
57
|
### Complete xBRIEFs
|
|
43
58
|
|
|
44
59
|
! The cohort's story xBRIEFs are completed by the deterministic **cohort completion sweep** in Phase 6 (`task swarm:complete-cohort`, Phase 6 Step 1.5 below), which runs AFTER the merge cascade. Do NOT move story xBRIEFs out of `xbrief/active/` before their PRs merge — a pre-merge move creates premature state if the merge cascade fails. This section is where the monitor records, per story, what the post-merge sweep will finalize:
|
|
@@ -215,6 +230,8 @@ This is defense in depth -- run it even when the pre-merge inspection above pass
|
|
|
215
230
|
|
|
216
231
|
! **REQUIRED.** Once the cohort's PRs are merged (Step 1 complete), the monitor MUST run the deterministic cohort completion sweep so the finished swarm leaves NO stranded xBRIEFs. This step closes the gap where a completed cohort left its story xBRIEFs in `xbrief/active/` and their decompose-created epic parents in `xbrief/pending/` -- nothing in the swarm flow swept them to `completed/` (observed in the 2026-06-03 swarm: after the cohort's PRs merged, the child story xBRIEFs stayed in `active/` and their epic parents stayed in `pending/`).
|
|
217
232
|
|
|
233
|
+
! **Pre-sweep merge re-poll for human-merge / `stop-at: pr-open` (#3153):** Before invoking `task swarm:complete-cohort` or `task swarm:finalize-cohort`, re-read each cohort PR's merge state via REST. If any PR marked `awaiting-human-merge` is still open, **halt** the sweep, keep durable ownership, and continue the observe path (Phase 5 human-merge section) until merge or operator cancel. ⊗ Sweep on Greptile CLEAN alone while a human-merge PR is still open.
|
|
234
|
+
|
|
218
235
|
```pwsh path=null start=null
|
|
219
236
|
# Sweep the whole cohort by glob (typical close-out)...
|
|
220
237
|
task swarm:complete-cohort -- --cohort 'xbrief/active/*.xbrief.json'
|
|
@@ -35,6 +35,10 @@ Live miss: operator said “use review skill” + URL; agent stayed on host free
|
|
|
35
35
|
|
|
36
36
|
Package install alone does not put always-pins into `~/.openclaw/workspace/skills`. Operators / agents SHOULD run `deft doctor` (detect) and `deft doctor --fix` (symlink or copy the four pins) when main-workspace pins are missing. Multi-seat only with `--openclaw-all-agents`. Operator steps: [`../../docs/openclaw-agent-host.md`](../../docs/openclaw-agent-host.md) § Wire skills into OpenClaw workspace.
|
|
37
37
|
|
|
38
|
+
### Soft post-compact AGENTS re-bind (#3171)
|
|
39
|
+
|
|
40
|
+
OpenClaw does **not** claim file-host PreCompact hard re-arm alone. Soft re-bind is a **required** durable skill (`deft-directive-post-compact-rebind`) deposited by `deft doctor --fix` / init when OpenClaw is detected — same checklist SoT as Cursor/Claude/Grok. After deposit, restart gateway or start a new session. Full dual-surface matrix: [`../../docs/openclaw-agent-host.md`](../../docs/openclaw-agent-host.md) § Soft post-compact AGENTS re-bind; `commands.md` compact + soft section.
|
|
41
|
+
|
|
38
42
|
## Hard isolation before spawn (#2929)
|
|
39
43
|
|
|
40
44
|
! For **parallel** OpenClaw leaves (cohort size > 1):
|
package/tasks/engine-invoke.cjs
CHANGED
|
@@ -113,12 +113,13 @@ function main() {
|
|
|
113
113
|
// stdio inherit (not pipe): piped stdout/stderr deadlocks when the child emits
|
|
114
114
|
// more than the OS pipe buffer before exit — observed as greenfield smoke
|
|
115
115
|
// hanging then CI SIGTERM exit 143 with no output (#2554 / #2547).
|
|
116
|
+
// shell:false is a literal at the call site (not plan.shell) so static
|
|
117
|
+
// analyzers see a non-shell spawn; win32 global still uses a quoted cmd.exe
|
|
118
|
+
// wrapper inside buildSpawnPlan, never shell:true (#2911 / #3175).
|
|
116
119
|
const result = spawnSync(plan.command, plan.args, {
|
|
117
120
|
stdio: "inherit",
|
|
118
121
|
env: childEnv,
|
|
119
|
-
|
|
120
|
-
// win32 .cmd shim is reached through a tightly quoted cmd.exe wrapper below.
|
|
121
|
-
shell: plan.shell,
|
|
122
|
+
shell: false,
|
|
122
123
|
// CREATE_NO_WINDOW: hide console windows from Cursor Task / nested shells (#2563).
|
|
123
124
|
windowsHide: true,
|
|
124
125
|
});
|
|
@@ -135,6 +136,11 @@ function main() {
|
|
|
135
136
|
* quoted so metacharacters stay inside a single argv token — aligned with
|
|
136
137
|
* tasks/engine-pm-run.cjs executeAllowlisted().
|
|
137
138
|
*
|
|
139
|
+
* Vendored vs global branches are deliberately disjoint (#3175 / CodeQL alert
|
|
140
|
+
* #74): process.execPath (AbsolutePathSource) is only the vendored non-shell
|
|
141
|
+
* command and must never flow into the win32 cmd.exe `/c` string, where it
|
|
142
|
+
* would be shell-interpreted as part of a constructed command line.
|
|
143
|
+
*
|
|
138
144
|
* @param {string} mode
|
|
139
145
|
* @param {string} target
|
|
140
146
|
* @param {string[]} argv
|
|
@@ -143,26 +149,25 @@ function main() {
|
|
|
143
149
|
*/
|
|
144
150
|
function buildSpawnPlan(mode, target, argv, opts = {}) {
|
|
145
151
|
const platform = opts.platform || process.platform;
|
|
146
|
-
const nodePath = opts.nodePath || process.execPath;
|
|
147
152
|
|
|
148
|
-
let execPath;
|
|
149
|
-
let execArgv;
|
|
150
153
|
if (mode === "vendored") {
|
|
151
|
-
execPath
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
} else {
|
|
157
|
-
return null;
|
|
154
|
+
// Non-shell: Node binary + script path + operator argv. process.execPath is
|
|
155
|
+
// only used here as the executable name with shell:false — never joined into
|
|
156
|
+
// a cmd.exe command line (CodeQL js/shell-command-injection-from-environment).
|
|
157
|
+
const nodePath = opts.nodePath || process.execPath;
|
|
158
|
+
return { command: nodePath, args: [target, ...argv], shell: false };
|
|
158
159
|
}
|
|
159
160
|
|
|
160
|
-
if (mode === "global"
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
if (mode === "global") {
|
|
162
|
+
if (platform === "win32") {
|
|
163
|
+
// Only the global shim name/path and operator argv — no process.execPath.
|
|
164
|
+
const commandLine = [target, ...argv].map(quoteWin32Arg).join(" ");
|
|
165
|
+
return { command: "cmd.exe", args: ["/d", "/s", "/c", commandLine], shell: false };
|
|
166
|
+
}
|
|
167
|
+
return { command: target, args: argv, shell: false };
|
|
163
168
|
}
|
|
164
169
|
|
|
165
|
-
return
|
|
170
|
+
return null;
|
|
166
171
|
}
|
|
167
172
|
|
|
168
173
|
if (require.main === module) {
|
|
@@ -186,3 +186,33 @@ describe("buildSpawnPlan — other paths keep shell:false", () => {
|
|
|
186
186
|
assert.equal(buildSpawnPlan("bogus", "deft", ["release"], WIN32), null);
|
|
187
187
|
});
|
|
188
188
|
});
|
|
189
|
+
|
|
190
|
+
describe("buildSpawnPlan — CodeQL absolute-path isolation (#3175 / alert #74)", () => {
|
|
191
|
+
it("never places nodePath/process.execPath into the win32 cmd.exe command line", () => {
|
|
192
|
+
const evilNode = String.raw`C:\Program Files\nodejs\node.exe`;
|
|
193
|
+
const plan = buildSpawnPlan("global", "deft", ["release", "--summary", "ok"], {
|
|
194
|
+
platform: "win32",
|
|
195
|
+
nodePath: evilNode,
|
|
196
|
+
});
|
|
197
|
+
assert.equal(plan.shell, false);
|
|
198
|
+
assert.equal(plan.command, "cmd.exe");
|
|
199
|
+
assert.deepEqual(plan.args.slice(0, 3), ["/d", "/s", "/c"]);
|
|
200
|
+
// Global win32 must only join target + operator argv — not the Node binary.
|
|
201
|
+
assert.equal(plan.args[3].includes(evilNode), false);
|
|
202
|
+
assert.equal(plan.args[3].includes("Program Files"), false);
|
|
203
|
+
assert.equal(plan.args[3].includes("node.exe"), false);
|
|
204
|
+
assert.deepEqual(splitCmdTokens(plan.args[3]), ["deft", "release", "--summary", "ok"]);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("uses nodePath only as the non-shell vendored command (never cmd.exe)", () => {
|
|
208
|
+
const nodePath = String.raw`C:\Program Files\nodejs\node.exe`;
|
|
209
|
+
const plan = buildSpawnPlan("vendored", String.raw`C:\repo\packages\cli\dist\bin.js`, ["session:start"], {
|
|
210
|
+
platform: "win32",
|
|
211
|
+
nodePath,
|
|
212
|
+
});
|
|
213
|
+
assert.equal(plan.shell, false);
|
|
214
|
+
assert.equal(plan.command, nodePath);
|
|
215
|
+
assert.notEqual(plan.command, "cmd.exe");
|
|
216
|
+
assert.deepEqual(plan.args, [String.raw`C:\repo\packages\cli\dist\bin.js`, "session:start"]);
|
|
217
|
+
});
|
|
218
|
+
});
|
package/tasks/verify.yml
CHANGED
|
@@ -210,6 +210,36 @@ tasks:
|
|
|
210
210
|
vars:
|
|
211
211
|
ENGINE_CMD: 'verify:forward-coverage --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
|
212
212
|
|
|
213
|
+
test-boundary:
|
|
214
|
+
desc: "Test/source boundary gate (#3145): reject recognized test artifacts under production roots and production references to test/fixture roots unless allowlisted or classified production-liveness. Policy from .deft/test-boundary.policy.json or plan.policy.testBoundary; defaults are warn-only (migration). Flags: --enforce / --warn / --policy <path> / --quiet. Three-state exit (0 clean or warn / 1 violation / 2 config)."
|
|
215
|
+
dir: '{{.USER_WORKING_DIR}}'
|
|
216
|
+
deps:
|
|
217
|
+
- task: :engine:_ts-build
|
|
218
|
+
cmds:
|
|
219
|
+
- task: :engine:invoke
|
|
220
|
+
vars:
|
|
221
|
+
ENGINE_CMD: 'verify:test-boundary --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
|
222
|
+
|
|
223
|
+
scope-provenance:
|
|
224
|
+
desc: "Approved-scope provenance gate (#3145): same-PR active xBRIEF file_scope expansion cannot self-authorize new paths. Compares against .deft/approved-scope/<plan-id>.json digests; requires renewed human approval for expansion. Missing digests warn by default; pass --enforce to fail closed. Flags: --base-ref / --enforce / --quiet. Three-state exit (0 clean or warn / 1 self-auth / 2 config)."
|
|
225
|
+
dir: '{{.USER_WORKING_DIR}}'
|
|
226
|
+
deps:
|
|
227
|
+
- task: :engine:_ts-build
|
|
228
|
+
cmds:
|
|
229
|
+
- task: :engine:invoke
|
|
230
|
+
vars:
|
|
231
|
+
ENGINE_CMD: 'verify:scope-provenance --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
|
232
|
+
|
|
233
|
+
consumer-check-contract:
|
|
234
|
+
desc: "Consumer check-composition contract (#3145): fail when tasks/verify.yml or check aggregate omit required enforcement gates (verify:test-boundary, verify:scope-provenance, verify:consumer-check-contract). CI omissions warn by default. Flags: --framework-source / --enforce / --warn / --quiet. Three-state exit (0 clean or warn / 1 missing / 2 config)."
|
|
235
|
+
dir: '{{.USER_WORKING_DIR}}'
|
|
236
|
+
deps:
|
|
237
|
+
- task: :engine:_ts-build
|
|
238
|
+
cmds:
|
|
239
|
+
- task: :engine:invoke
|
|
240
|
+
vars:
|
|
241
|
+
ENGINE_CMD: 'verify:consumer-check-contract --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
|
242
|
+
|
|
213
243
|
vbrief-conformance:
|
|
214
244
|
desc: "Detect bare non-conformant vBRIEF keys (#1620). Scans tracked vbrief/**/*.vbrief.json and flags any document/plan/item key that is not 0.6 spec-core, x-directive/-namespaced, or x-vbrief/-namespaced. plan.policy + plan.completedNote carry a TEMPORARY allow-list pending vBRIEF #12. Defaults to --all; pass --staged for the pre-commit invocation."
|
|
215
245
|
dir: '{{.USER_WORKING_DIR}}'
|
|
@@ -461,11 +461,13 @@ These rules bind **orchestrators** dispatching implementation, fix, or review-cy
|
|
|
461
461
|
**Worker-owns-lifecycle (Gap C):**
|
|
462
462
|
|
|
463
463
|
- ! When dispatching an implementation worker, the dispatch envelope MUST declare the unit-of-work boundary explicitly: `stop-at: pr-open` (worker opens PR and exits) OR `drive-to: merge-ready` (worker owns PR + Greptile review cycle + fix batches through merge-ready as ONE unit of work, spawning its own review poller per `skills/deft-directive-review-cycle/SKILL.md` monitoring tiers). Default for story implementation dispatches is `drive-to: merge-ready`.
|
|
464
|
+
- ! **Envelope selection SLA (#3153):** Choose the boundary before spawn using the swarm decision tree (`skills/deft-directive-swarm/references/core-phase-0.md` — capacity stall, conf-only residual, wall-clock budget, large multi-gate, host nest limits). Happy-path default remains `drive-to: merge-ready`. A **deliberate** `stop-at: pr-open` is valid only with an immediate partner merge-path owner per `skills/deft-directive-review-cycle/SKILL.md` § Partner merge-path (babysit / Approach 1 lease / parent-retained — not freestyle global babysit). Under human-merge policy, that owner (or an explicit handoff recipient) remains responsible through merge + `scope:complete` — do not stand down at CLEAN alone. Consumer pin: `templates/agents-entry.md` § Envelope selection SLA. Silent PR-open handback for a worker already scoped merge-ready remains **forbidden**.
|
|
464
465
|
- ! **Cursor Task ownership split (#2797 / #2814):** A Cursor `Task` implementation leaf MUST NOT spawn another Cursor `Task` review-monitor: nested Task (leaf spawning leaf) is unsupported/unreliable. A Cursor `drive-to: merge-ready` leaf instead owns a blocking `task pr:watch -- <N>` in its own process. To use an Approach 1 monitor, scope the leaf `stop-at: pr-open`; the orchestrator that owns the Task primitive then launches the sibling monitor and runs `task review-monitor:register -- --pr <N> --monitor-agent-id <id> --platform-primitive cursor-task` (GitHub sticky `<!-- deft:review-owner -->` lease — not local JSON).
|
|
465
466
|
- ⊗ Let a Cursor leaf background `task pr:watch` and claim review monitoring is active. The process dies with the leaf and has no GitHub review-owner lease; treat that claim as a regression/eval failure and let `task verify:review-monitor -- --pr <N>` fail closed.
|
|
466
467
|
- ! **Post-merge scope lifecycle (#2321 / Gap C):** Workers scoped `stop-at: pr-open` MUST NOT run `task scope:complete` before exit — their activation checkpoint rides into master on merge. The **orchestrator** (or Phase 6 `task swarm:finalize-cohort` / `task swarm:complete-cohort` on the headless path) MUST run `task scope:complete` or `task scope:cancel` for each shipped story xBRIEF after its PR merges. Workers scoped `drive-to: merge-ready` (or `drive-to: merge`) MUST include `task scope:complete` on their active xBRIEF as part of the same unit of work (after merge when appropriate).
|
|
467
468
|
- ! Workers scoped `drive-to: merge-ready` MUST drive to merge-ready in their own tool loop — pre-PR, push, PR open, review-cycle poll/fix loop, and the #1259 Step 6 fail-closed exit — without handing back at PR-open for the orchestrator to re-dispatch separate leaf agents for review or fixes.
|
|
468
469
|
- ⊗ Re-dispatch a separate review-monitor or fix agent after an implementation worker exits at PR-open when the original envelope scoped `drive-to: merge-ready` — that split recreates cross-agent state-handoff hazards and terminal lifecycle gaps (#1878 / Gap C).
|
|
470
|
+
- ⊗ Dispatch `stop-at: pr-open` without a named review-cycle partner merge-path owner plan (#3153).
|
|
469
471
|
- ⊗ Leave an `xbrief/active/` brief with `plan.status == running` on master after the story's issue is closed or its PR merged — `task verify:orphan-active` fails closed on that signature (#2321).
|
|
470
472
|
|
|
471
473
|
**Background / independent dispatch (Gap D):**
|
|
@@ -526,6 +528,12 @@ The parent monitor watches the heartbeat file directly (three-state exit 0 ok /
|
|
|
526
528
|
- Monitors run `task verify:subagent-alive -- --require-agent <agent-id> [--scratch-dir <worktree>/.deft-scratch/subagent-status]` each poll iteration.
|
|
527
529
|
- Workers run `task agent:monitor` (raw sweep) or the gate verb above; both wrap `subagent-monitor` (#1365).
|
|
528
530
|
|
|
531
|
+
## 10.6 Dual stop for multi-iteration worker loops (#2442)
|
|
532
|
+
|
|
533
|
+
Multi-iteration implement, pre-PR, repair, and monitor loops require **two** stops: **success** (goal / AC / checker met) and **failure or budget** (max iterations, no-progress, or time/token budget). Single-turn tasks are exempt. Principle and defaults: `main.md` `## Dual Stop Rule (#2442)`; build skill dual-stop table; swarm Phase 4 / core-ops.
|
|
534
|
+
|
|
535
|
+
! On failure stop: halt; emit an operator-visible report (what was tried, what is missing, what human decision is needed). Prefer `BLOCKED:` over silent retry. ⊗ Thrash past the envelope. Durable delivery/acceptance mechanical enforcement is **#3143** (`packages/core/src/delivery-attempt/`; not prompt-only).
|
|
536
|
+
|
|
529
537
|
## 11. Mandatory DONE message even on early exit
|
|
530
538
|
|
|
531
539
|
Every worker MUST send a final status message before exiting its tool loop, regardless of outcome:
|
|
@@ -67,6 +67,7 @@ Legacy `vbrief/` read-accepted; `deft migrate:xbrief` for `xbrief/` (v0.6→v0.8
|
|
|
67
67
|
! Process-critical skills with false-negative risk MUST be named in AGENTS.md (always-pin tier) — tier definitions: `.deft/core/docs/skill-pin-policy.md` (#2508).
|
|
68
68
|
! **Default always-pins:** `deft-directive-build`, `deft-directive-pre-pr`, `deft-directive-review-cycle`, `deft-directive-swarm` — read each `SKILL.md` when that work type starts.
|
|
69
69
|
⊗ Pin entire language packs, deployment docs, or framework bulk into AGENTS.md — pins are for false-negative-sensitive process gates only (#2508).
|
|
70
|
+
! **Dual stop (#2442):** multi-iteration work MUST have success + failure/budget stop (max iters / no-progress / budget); single-turn exempt; halt with operator-visible report; ⊗ thrash. Defaults: build, swarm, review-cycle skills. See main.md Dual Stop Rule. Ledger #3143 (`packages/core/src/delivery-attempt/`).
|
|
70
71
|
|
|
71
72
|
## Through-merge worker dispatch (#3032)
|
|
72
73
|
|
|
@@ -74,6 +75,14 @@ Legacy `vbrief/` read-accepted; `deft migrate:xbrief` for `xbrief/` (v0.6→v0.8
|
|
|
74
75
|
⊗ Parent conversation implements or babysits product fix/CI loops for drive-to:merge-ready work when background subagent/worktree dispatch is available (#3032).
|
|
75
76
|
! After leaf announce: tool-first / yield / one short non-repeated answer; ⊗ N>2 near-identical zero-tool (FC14 / #3131). Machine: `evaluateParentTurnShape` (`parent-turn-shape`). Depth: preamble §11 + `docs/openclaw-agent-host.md`.
|
|
76
77
|
|
|
78
|
+
## Envelope selection SLA (#3153)
|
|
79
|
+
|
|
80
|
+
! Default story / through-merge unit of work is `drive-to: merge-ready`. Deliberate `stop-at: pr-open` is allowed only when a **partner merge-path owner** is planned (review-cycle babysit / Approach 1 lease / parent-retained) for Greptile + CI + post-merge `scope:complete` — triggers: capacity stall, wall-clock budget, large multi-gate, host nest limits (swarm Phase 0 decision tree). Depth: `deft-directive-swarm` + `deft-directive-review-cycle` partner merge-path.
|
|
81
|
+
! Under human-merge policy, a **durable** owner (parent/monitor sticky lease or Phase 6 closer) MUST remain for post-merge `scope:complete` — CLEAN alone is not lifecycle complete.
|
|
82
|
+
⊗ Silent PR-open handback for a worker already scoped `drive-to: merge-ready`.
|
|
83
|
+
⊗ `stop-at: pr-open` without a named babysit / merge-path owner, or dual review-monitor leases on recovery (#3044 / #2261).
|
|
84
|
+
⊗ Stand down at CLEAN under human-merge with no reachable post-merge `scope:complete` owner.
|
|
85
|
+
|
|
77
86
|
## Review-surface precedence (#2308)
|
|
78
87
|
|
|
79
88
|
! Route PR shepherding / review work through `deft-directive-review-cycle` — `.deft/core/.agents/skills/deft-directive-review-cycle/SKILL.md`; host `babysit` / `bugbot` / `security-review` advisory-only (#2308 / #2261).
|
|
@@ -89,6 +98,7 @@ Legacy `vbrief/` read-accepted; `deft migrate:xbrief` for `xbrief/` (v0.6→v0.8
|
|
|
89
98
|
## Branch policy & branch verification
|
|
90
99
|
|
|
91
100
|
! Feature branches — `deft verify:branch`, `deft verify:forward-coverage`, `deft coverage:hotspots`, hooks, `deft check` (#746 / #747) — `.deft/core/scm/github.md` § Branch policy.
|
|
101
|
+
! Test placement + scope provenance (#3145) — `deft verify:test-boundary`, `deft verify:scope-provenance`, `deft verify:consumer-check-contract` (docs: `docs/test-boundary.md`, `docs/scope-provenance.md`, `docs/consumer-check-contract.md`); defaults warn-only until authored policy.
|
|
92
102
|
|
|
93
103
|
## Branch Policy Disclosure (#746)
|
|
94
104
|
|
|
@@ -446,9 +446,11 @@ Send to parent:
|
|
|
446
446
|
|
|
447
447
|
`last_reviewed_sha` matches HEAD AND `has_blocking` is True. Do NOT exit on P2 -- those are non-blocking style suggestions per `skills/deft-directive-review-cycle/SKILL.md`.
|
|
448
448
|
|
|
449
|
-
Address the findings per Phase 2 Step 2-3 of the review-cycle skill: read every finding, plan a single coherent batch, run `task check`, commit with message `fix: address Greptile review findings (batch)`, push. After the push,
|
|
449
|
+
Address the findings per Phase 2 Step 2-3 of the review-cycle skill: read every finding, plan a single coherent batch, run `task check`, commit with message `fix: address Greptile review findings (batch)`, push. After the push, you MAY reset the **poll-wait timer** for the new HEAD (Greptile needs a fresh review window) — but you MUST NOT reset the dual-stop **fix-batch counter** (#2442).
|
|
450
450
|
|
|
451
|
-
|
|
451
|
+
**Ownership-wide dual-stop state (#2442):** `fix_batch_count` and `primary_fingerprint` span the **whole review ownership for this PR**, not one poller process. On start: read prior state from a sticky PR comment marker `<!-- deft:dual-stop fix_batches=N fingerprint=... -->` (or the review-owner lease body if that surface already carries it); default N=0 when absent. After each substantive fix push: increment N, write the marker back, then continue. When a parent re-dispatches a replacement poller, it MUST pass prior N/fingerprint in the prompt placeholders or rely on the sticky marker — a fresh poller that starts at 0 while the same fingerprint remains is a dual-stop violation. When `fix_batch_count` reaches **3** OR the same primary P0/P1 fingerprint reappears on **2** consecutive re-reviews with no material fix, exit BLOCKED (do not open a fourth automatic fix batch). Mechanical durable ledgers: #3143 `packages/core/src/delivery-attempt/`. Continue polling only while under the dual-stop envelope. Do NOT exit early on a successful fix mid-loop — this is the loop body of the review-cycle skill.
|
|
452
|
+
|
|
453
|
+
If the dual-stop envelope is exhausted (3 fix batches or 2 identical no-progress re-reviews), escalate to parent:
|
|
452
454
|
|
|
453
455
|
Subject: PR #{pr_number} escalation -- 3 review cycles still surfacing P0/P1
|
|
454
456
|
Body:
|