@deftai/directive-content 0.95.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/QUICK-START.md +2 -0
- package/Taskfile.yml +28 -0
- package/UPGRADING.md +69 -0
- package/commands.md +30 -5
- package/contracts/test-boundary.md +18 -0
- package/docs/consumer-check-contract.md +27 -0
- package/docs/consumer-issue-label-kit.md +211 -0
- package/docs/delivery-attempt.md +173 -0
- package/docs/getting-started.md +2 -0
- package/docs/openclaw-agent-host.md +58 -7
- 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 +8 -11
- package/patterns/operator-log-hygiene.md +130 -0
- package/scm/github.md +35 -3
- package/skills/deft-directive-article-review/SKILL.md +17 -6
- package/skills/deft-directive-build/SKILL.md +41 -0
- package/skills/deft-directive-refinement/SKILL.md +1 -3
- package/skills/deft-directive-review-cycle/SKILL.md +166 -12
- package/skills/deft-directive-swarm/SKILL.md +36 -11
- package/skills/deft-directive-swarm/references/core-ops.md +16 -3
- package/skills/deft-directive-swarm/references/core-phase-0.md +40 -15
- package/skills/deft-directive-swarm/references/core-phase-3.md +18 -9
- package/skills/deft-directive-swarm/references/core-phase-4.md +21 -2
- package/skills/deft-directive-swarm/references/core-phase-5-6.md +18 -1
- package/skills/deft-directive-swarm/references/host-claude-code.md +86 -0
- package/skills/deft-directive-swarm/references/host-cursor.md +1 -1
- package/skills/deft-directive-swarm/references/host-openclaw.md +12 -4
- package/skills/deft-directive-triage/SKILL.md +19 -6
- package/skills/deft-directive-xbrief/SKILL.md +0 -1
- package/tasks/cache.yml +31 -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 +15 -4
- package/templates/agents-entry.md +11 -0
- package/templates/swarm-greptile-poller-prompt.md +4 -2
package/tasks/cache.yml
CHANGED
|
@@ -77,3 +77,34 @@ tasks:
|
|
|
77
77
|
- task: :engine:invoke
|
|
78
78
|
vars:
|
|
79
79
|
ENGINE_CMD: 'cache prune {{.CLI_ARGS}}'
|
|
80
|
+
|
|
81
|
+
# #1137 — reversible closed-entry archive (NOT TTL prune; never auto-wired)
|
|
82
|
+
archive-closed:
|
|
83
|
+
desc: "Reversible archive of closed github-issue cache entries (#1137). -- task triage:cache-archive -- [--dry-run] [--older-than-days 30] [--repo OWNER/NAME] [--json]"
|
|
84
|
+
dir: '{{.USER_WORKING_DIR}}'
|
|
85
|
+
deps:
|
|
86
|
+
- task: :engine:_ts-build
|
|
87
|
+
cmds:
|
|
88
|
+
- task: :engine:invoke
|
|
89
|
+
vars:
|
|
90
|
+
ENGINE_CMD: 'cache archive-closed --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
|
91
|
+
|
|
92
|
+
archive-list:
|
|
93
|
+
desc: "List archived github-issue cache entries (#1137). -- task triage:archive-list -- [--repo OWNER/NAME] [--format=json] [--since ISO] [--limit N]"
|
|
94
|
+
dir: '{{.USER_WORKING_DIR}}'
|
|
95
|
+
deps:
|
|
96
|
+
- task: :engine:_ts-build
|
|
97
|
+
cmds:
|
|
98
|
+
- task: :engine:invoke
|
|
99
|
+
vars:
|
|
100
|
+
ENGINE_CMD: 'cache archive-list --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
|
101
|
+
|
|
102
|
+
restore-from-archive:
|
|
103
|
+
desc: "Restore archived github-issue entry to live cache (#1137). -- task triage:restore-from-archive -- --issue N [--repo OWNER/NAME] [--force]"
|
|
104
|
+
dir: '{{.USER_WORKING_DIR}}'
|
|
105
|
+
deps:
|
|
106
|
+
- task: :engine:_ts-build
|
|
107
|
+
cmds:
|
|
108
|
+
- task: :engine:invoke
|
|
109
|
+
vars:
|
|
110
|
+
ENGINE_CMD: 'cache restore-from-archive --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
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}}'
|
|
@@ -73,13 +73,13 @@ When the operator supplies an ordered plan (delivery sequence, cohort, checklist
|
|
|
73
73
|
|
|
74
74
|
## 2.6 Provider-neutral worker metadata (#1531)
|
|
75
75
|
|
|
76
|
-
Heterogeneous swarm dispatch (#1531) assigns each worker a **dispatch provider** (the runtime primitive that launched the agent), a **worker role** (what the agent is allowed to do), and a **selected backend** or **routing policy** (how the harness maps that role to a concrete agent). These fields are provider-neutral: Composer-class coding agents, Grok Build (`spawn_subagent`), Cursor/cloud agents, OpenClaw (`sessions_spawn`, #2874 / #2879), and future adapters share the same contract.
|
|
76
|
+
Heterogeneous swarm dispatch (#1531) assigns each worker a **dispatch provider** (the runtime primitive that launched the agent), a **worker role** (what the agent is allowed to do), and a **selected backend** or **routing policy** (how the harness maps that role to a concrete agent). These fields are provider-neutral: Composer-class coding agents, Grok Build (`spawn_subagent`), Cursor/cloud agents, Claude Code (`claude-code` / `claude-agent`, #3134), OpenClaw (`sessions_spawn`, #2874 / #2879), and future adapters share the same contract.
|
|
77
77
|
|
|
78
78
|
! Every intentional backend-routed dispatch MUST carry a separate `## Worker metadata` section in the dispatch envelope, placed AFTER `## Allocation context` and BEFORE the task body. This section is advisory metadata for the worker and for audit; it does NOT replace, extend, or reorder the five-field #1378 `## Allocation context` recognition contract above.
|
|
79
79
|
|
|
80
80
|
When present, the section documents these fields in order:
|
|
81
81
|
|
|
82
|
-
- `dispatch_provider`: the runtime primitive that launched this worker -- e.g. `spawn_subagent`, `start_agent`, `sessions_spawn` (OpenClaw host; platform descriptor `openclaw` per #2874 / #2875), `cursor-composer`, `cursor-cloud-agent`, or a future adapter id. Names the harness surface, not the model.
|
|
82
|
+
- `dispatch_provider`: the runtime primitive that launched this worker -- e.g. `spawn_subagent`, `start_agent`, `sessions_spawn` (OpenClaw host; platform descriptor `openclaw` per #2874 / #2875), `cursor-composer`, `cursor-cloud-agent`, `claude-code` (Claude Code host; register primitive `claude-agent` per #3134), or a future adapter id. Names the harness surface, not the model.
|
|
83
83
|
- `worker_role`: the role boundary for this dispatch -- one of `leaf-implementation`, `orchestrator`, `review-monitor`, or `merge-release` (stable ids from `packages/core/src/swarm/routing.ts` `SWARM_WORKER_ROLES`). Tells the worker which preamble rules and skill surfaces apply.
|
|
84
84
|
- `selected_backend`: the stable backend id from `plan.policy.swarmSubagentBackend` / `task policy:subagent-backends` (accepted set today: `composer`, `grok-build`, `cursor-cloud` only — see `KNOWN_SUBAGENT_BACKEND_IDS`) | null -- which catalogued **coding** backend the operator selected for this role. OpenClaw is a **host / dispatch_provider** (`sessions_spawn` / descriptor `openclaw`), not a `swarmSubagentBackend` enum value; do not write `selected_backend: openclaw` into policy (#2879 Greptile P1).
|
|
85
85
|
- `routing_policy`: <path or reference to the operator's routing file / tiering policy> | null -- when backend selection is delegated to harness routing instead of a typed policy field, cite the policy handle here so postmortems can reconstruct the route. The canonical handle is the gitignored, per-machine `.deft/routing.local.json` (#1739), keyed by `(dispatch_provider, worker_role)`; set decisions with `task swarm:routing-set -- --role <role> (--model <slug> | --harness-default)`.
|
|
@@ -94,7 +94,7 @@ Populate `selected_backend` OR `routing_policy` (or both when the operator sets
|
|
|
94
94
|
- `orchestrator`, `review-monitor`, or `merge-release` + explicit backend routing: at least one MUST be non-null so strong-tier audit traces stay reconstructable.
|
|
95
95
|
- Any role on the harness-default agent with no tiering decision: both MAY be null; `dispatch_provider` and `worker_role` remain required.
|
|
96
96
|
|
|
97
|
-
**Role-boundary expectations (all providers):** the same boundaries apply whether the worker runs on Composer, Grok Build, Cursor/cloud, OpenClaw, or a future adapter:
|
|
97
|
+
**Role-boundary expectations (all providers):** the same boundaries apply whether the worker runs on Composer, Grok Build, Cursor/cloud, Claude Code, OpenClaw, or a future adapter:
|
|
98
98
|
|
|
99
99
|
- ! `leaf-implementation` workers implement scoped xBRIEF work in their assigned worktree only -- gates (`task check`, file-scope audit, Greptile review cycle) are model-agnostic and MUST still pass.
|
|
100
100
|
- ! `orchestrator`, `review-monitor`, and `merge-release` roles MUST run on strong or review-capable agents; dispatchers MUST NOT route these roles to cheap leaf backends.
|
|
@@ -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:
|
|
@@ -563,9 +571,12 @@ Every worker MUST send a final status message before exiting its tool loop, rega
|
|
|
563
571
|
- ! **Legal partial:** local work `done` + ship `not_started` / `blocked` **without** PR/SHA/CI/review fields and `proof_status: n/a-no-remote-claim` (or non-pass `status: partial`) is valid — do not invent ship state.
|
|
564
572
|
- Machine check (library): `validateHandoffEvidence` in `@deftai/directive-core` `handoff-evidence` (`packages/core/src/handoff-evidence/`). Skills: build / pre-pr / review-cycle final checklist.
|
|
565
573
|
|
|
566
|
-
! **Parent tool-first after leaf completion (#2943):** When a parent / monitor receives a leaf completion event (`subagent_announce`, parent-push, or host completion notify), its **first response** MUST be a **tool-first** ground-truth batch (`gh` / `git` / worktree or file status)
|
|
574
|
+
! **Parent tool-first after leaf completion (#2943 / hard-stop #3131):** When a parent / monitor receives a leaf completion event (`subagent_announce`, parent-push, or host completion notify), its **first response** MUST be exactly one of: (1) a **tool-first** ground-truth batch (`gh` / `git` / worktree or file status) then one consolidate, (2) a host **yield** (`sessions_yield` on OpenClaw, or equivalent), or (3) **one short user answer** that is **not** a repeated progress line. ⊗ Multi-sentence progress-only first response with zero tools / yield — the OpenClaw text-repetition hang class (#2943). ⊗ Emit **N>2** near-identical assistant sentences (or streaming text chunks) in one turn with no `tool_use` / yield — **FC14** illegal shape; hard-stop the turn (#3131). Soft skill prose is **not** sole mitigation.
|
|
575
|
+
|
|
576
|
+
! **Machine check (FC14 hard-stop):** `evaluateParentTurnShape` in `@deftai/directive-core` `parent-turn-shape` (`packages/core/src/parent-turn-shape/`). Feed ordered turn events (`assistant_text` / `tool_use` / `yield`); when `ok === false` and `failClass` is `FC14` (or post-announce `progress-only-no-tool`), abort / force tool-or-yield. Operator recovery for current OpenClaw beta pins: `docs/openclaw-agent-host.md` § Operator recovery — FC14.
|
|
567
577
|
|
|
568
578
|
⊗ Treat thin DONE (no PR URL / merge evidence) as success (#2943).
|
|
579
|
+
⊗ N>2 near-identical assistant sentences with no tool_use / yield, or soft-prose-only mitigation for the parent hang (FC14 / #3131).
|
|
569
580
|
⊗ Treat empty/unknown review-monitor settle as DONE without same-turn ground truth, or dual-spawn a second monitor while the first lease is live (#3044).
|
|
570
581
|
⊗ Emit freeform `review_cycle: started` / `pending` / `initiated` or L4 `status: pass` without `done` or verifiable `in_progress:<pr>#…` (#3090).
|
|
571
582
|
⊗ Claim `status: pass` (or equivalent process-green handoff) with PR URL / SHA / CI green / review score filled from memory without same-turn probe binding — **invented-done** (#3120).
|
|
@@ -67,11 +67,21 @@ 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
|
|
|
73
74
|
! On **through merge** / **drive to merge** / land-ship / **drive-to: merge-ready** story intent: parent MUST dispatch a `drive-to: merge-ready` worker (worktree, preflight, pre-pr, review-cycle, merge/`scope:complete`) via the **swarm/solo-worker launch path** even if **cohort size is 1** — parent MUST NOT implement as the leaf. Depth: swarm Phase 0 + skill-pin-policy (#3032 / #1880 Gap C).
|
|
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).
|
|
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`.
|
|
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.
|
|
75
85
|
|
|
76
86
|
## Review-surface precedence (#2308)
|
|
77
87
|
|
|
@@ -88,6 +98,7 @@ Legacy `vbrief/` read-accepted; `deft migrate:xbrief` for `xbrief/` (v0.6→v0.8
|
|
|
88
98
|
## Branch policy & branch verification
|
|
89
99
|
|
|
90
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.
|
|
91
102
|
|
|
92
103
|
## Branch Policy Disclosure (#746)
|
|
93
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:
|