@deftai/directive-content 0.85.0 → 0.87.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.
@@ -50,98 +50,83 @@ Directive does not guess your mix: **ordered plan** (`task plan-sequence:*`) whe
50
50
  ! Probe cache freshness before doing any classification or selection. Stale cache reads produce stale decisions; the gate is the contract.
51
51
 
52
52
  1. ! Run `task verify:cache-fresh` (D5 / #1127). Exit 0 -> proceed to Phase 1. Exit 1 (stale or blocked) -> refresh per the printed remediation. Exit 2 (no bootstrap) -> run `task triage:bootstrap` first. When the cache has zero entries, read paths auto-fetch from GitHub first (#2575).
53
- 2. ~ Refresh path: `task cache:fetch-all -- --source=github-issue --repo OWNER/NAME` for an already-bootstrapped project (idempotent, TTL-aware, re-applies the #883 scanner v2 quarantine rules); `task triage:bootstrap` for a first-time seed.
54
- 3. ~ If `xbrief/active/*.xbrief.json` references are in play, run `task triage:refresh-active` to compare cached `meta.json.fetched_at` against live upstream `updatedAt` and surface drift before the queue is rendered.
55
- 4. ~ When the one-liner emitted by the session-start ritual carries a `[scope-drift] N` segment (D14 / #1133), run `task triage:scope-drift` to see the per-label / per-milestone breakdown of upstream signals on cached open issues that fall outside the active `plan.policy.triageScope[]` subscription. The output documents both opt-in (`task triage:subscribe -- --label=<L>`) and opt-out (`task triage:scope-drift -- --ignore-label=<L>`) paths -- pick one before walking the queue so the cohort reflects the operator's current intent rather than a stale subscription.
53
+ 2. ~ Refresh path: `task cache:fetch-all -- --source=github-issue --repo OWNER/NAME` for an already-bootstrapped project; `task triage:bootstrap` for a first-time seed.
54
+ 3. ~ If `xbrief/active/*.xbrief.json` references are in play, run `task triage:refresh-active` to surface drift before the queue is rendered.
55
+ 4. ~ When the session one-liner carries `[scope-drift] N` (D14 / #1133), run `task triage:scope-drift` and choose subscribe / ignore before walking the queue.
56
56
  5. ⊗ Walk the queue against a stale cache -- the audit log will record decisions against bodies the operator never actually saw.
57
57
 
58
58
  ## Phase 1 -- Classify
59
59
 
60
60
  ! Inspect the auto-classification audit log so manually-decided items are not re-walked, and surface anomalies before the queue render.
61
61
 
62
- 1. ! Run `task triage:classify --list` (D10 / #1129) to render the effective universal + consumer auto-classification rules and the active hold-marker list.
63
- 2. ! Walk recent entries in `xbrief/.eval/candidates.jsonl` for anomalies: classifier disagreements against the operator's prior decisions, repeated `defer` cycles on the same issue, or `needs-ac` records older than the freshness window. Surface anomalies to the operator before Phase 2; do NOT auto-fix.
64
- 3. ~ When the operator wants to widen / narrow the corpus, consult `task triage:scope --list` (D12 / #1131) to see the active `plan.policy.triageScope[]` subscription. Subscription edits belong in PROJECT-DEFINITION.xbrief.json, not in this skill.
65
- 4. ~ Issue-label hygiene: labels feed triage queue ranking, issue gauges, hygiene sweeps, and lifecycle reconciliation. When this skill exposes an unlabeled issue or a downstream issue-creation step, recommend choosing one or more suitable labels from the repository's existing label set via `gh label list` or the labels API, or explicitly note that no label was applied. This is a recommendation, not a gate.
66
- 5. ⊗ Re-classify items already terminally decided (accept / reject / mark-duplicate) without explicit operator approval -- the audit log is append-only and supersession runs through Layer 5 (`task triage:reset <N>`), not through silent re-walks.
67
- 6. ⊗ Block issue creation solely because no label was selected, or invent ad hoc labels outside the repository's existing label set.
62
+ 1. ! Run `task triage:classify --list` (D10 / #1129) to render effective rules and hold-markers.
63
+ 2. ! Walk recent `xbrief/.eval/candidates.jsonl` entries for anomalies (classifier disagreement, repeated defer, stale needs-ac); surface before Phase 2; do NOT auto-fix.
64
+ 3. ~ Scope widen/narrow via `task triage:scope --list` (D12 / #1131); edits belong in PROJECT-DEFINITION.
65
+ 4. ~ Label hygiene: recommend repo labels via `gh label list` when unlabeled; do not invent labels or block creation solely for missing labels.
66
+ 5. ⊗ Re-classify terminally decided items without operator approval -- supersession is `task triage:reset <N>` only.
68
67
 
69
68
  ## Phase 2 -- Present
70
69
 
71
70
  ! Apply the Work selection fork gate (#2542): when no ordered-plan is active, render `task triage:queue` before suggesting work (#1149). Active sequence yields to the ordered-plan entry (#2402).
72
71
 
73
- 1. ! Run `task triage:queue --limit=N` (D11 / #1128) -- default `N=10` per the umbrella Current Shape v3 WIP cap. Output is grouped `[RESUME]` -> `[URGENT]` -> untriaged -> other; within-group ordering follows the consumer-supplied `plan.policy.triageRankingLabels[]` (framework default empty per §12 boundary), tiebroken by `updated_at` descending.
74
- 2. ! For per-item detail, run `task triage:show <N>` -- prints the cached upstream payload, the latest triage decision, the audit timeline, and the active-xBRIEF reference flag. Exit 0 on hit, 1 on cache miss (re-sync per Phase 0).
75
- 3. ~ Present the ranked queue verbatim; do NOT silently re-rank, drop, or annotate beyond what the canonical renderer emits. If the operator wants a different ordering they edit `plan.policy.triageRankingLabels[]` and re-run.
76
- 4. ⊗ Recommend a specific issue without consulting `task triage:queue` first, or recommend an issue absent from the queue without first running `task triage:show` to surface why (cache miss / outside subscription / terminal decision).
72
+ 1. ! Run `task triage:queue --limit=N` (D11 / #1128) -- default `N=10`. Groups `[RESUME]` -> `[URGENT]` -> untriaged -> other; ranking via `plan.policy.triageRankingLabels[]`, tiebreak `updated_at` desc.
73
+ 2. ! For per-item detail, run `task triage:show <N>` (default) or `task triage:show --format=operator <N>` (#2890) -- cached payload, latest decision, audit timeline, active-xBRIEF flag; operator format is the pasteable Phase 3 brief backbone. Exit 0 on hit, 1 on cache miss (re-sync per Phase 0).
74
+ 3. ~ Present the ranked **queue listing** verbatim; do NOT silently re-rank, drop, or annotate the listing beyond the canonical renderer. This queue non-annotation rule does **not** forbid Phase 3 per-candidate operator briefs or leans (see Phase 3 / #2890).
75
+ 4. ⊗ Recommend a specific issue without `task triage:queue` first, or an issue absent from the queue without `task triage:show` to surface why.
77
76
 
78
77
  ## Phase 3 -- Decide
79
78
 
80
- ! Walk per-item decisions through the canonical `task triage:*` verbs. The skill does NOT reimplement the audit-log append, schema validation, or `xbrief/proposed/` write inline -- the tasks are the canonical implementation (mirrors the #537 ingest-task discipline).
79
+ ! Walk per-item decisions through the canonical `task triage:*` verbs (tasks own audit-log append / schema / `xbrief/proposed/` write).
81
80
 
82
- For each candidate the operator selects from the Phase 2 queue, render the canonical numbered action menu and dispatch the matching verb:
81
+ ! **Operator brief (same turn as menu) (#2890):** Before every per-item decision menu, present an operator brief in the **same operator-visible message/surface** as the menu, containing at least: issue `#N` + title + link; labels (or explicit none); 2–5 line problem/context summary; AC bullets or explicit "thin body / no AC"; agent **lean** + one-line why (Accept / Defer / Reject / Needs-AC / …). ~ Prefer `task triage:show --format=operator <N>` as the brief backbone; agent still owns lean. ⊗ Menu-only or chip-only Phase 3 turns without that brief. ⊗ Brief-only turn followed by a later chip/menu-only turn that does not restate the brief.
82
+
83
+ ! **Host structured-question adapter:** On chips / `ask_user` / similar UIs (e.g. OpenClaw `ask_user`), keep the prose brief in chat; structured options are **actions only** (Accept / Defer / Reject / Needs-AC / Mark duplicate / Discuss / Back). Option labels ≉ substitute for the brief.
84
+
85
+ For each candidate, render the canonical numbered action menu and dispatch:
83
86
 
84
87
  ```
85
88
  What would you like to do with this candidate?
86
- 1. Accept -- `task triage:accept <N>` (writes proposed/ xBRIEF + audit-log entry)
87
- 2. Reject -- `task triage:reject <N>` (audit-log entry only; terminal)
88
- 3. Defer -- `task triage:defer <N> [--resume-on <event>]` (non-terminal; resurfaces)
89
- 4. Needs-AC -- `task triage:needs-ac <N>` (non-terminal; flags missing acceptance criteria)
90
- 5. Mark duplicate -- `task triage:mark-duplicate <N> <of-issue>` (terminal; cross-links target)
89
+ 1. Accept -- `task triage:accept <N>`
90
+ 2. Reject -- `task triage:reject <N>`
91
+ 3. Defer -- `task triage:defer <N> [--resume-on <event>]`
92
+ 4. Needs-AC -- `task triage:needs-ac <N>`
93
+ 5. Mark duplicate -- `task triage:mark-duplicate <N> <of-issue>`
91
94
  6. Discuss
92
95
  7. Back
93
96
  ```
94
97
 
95
- - ! `--resume-on <event>` on `task triage:defer` (D3 / #1123 -- ships in parallel; reference but do not hard-depend) records a resume condition with the defer entry; the resume condition surfaces in `task triage:queue` once met. When D3 has not landed yet, omit the flag -- the verb stays terminal-shape-compatible.
96
98
  - ! Map user replies only to the displayed number (`1`-`7`) or exact displayed option text. ⊗ Do NOT infer from alphabetic host affordances or bare letters such as `d` / `b` unless those letters were visibly rendered as choices.
97
- - ! On `Discuss`, halt the action sequence immediately, prompt `What would you like to discuss?`, and resume only on an explicit user signal. ⊗ Implicit resumption.
98
- - ! On `Back`, un-buffer the prior candidate's selection and re-render its action menu -- permitted only before the action has dispatched to a `task triage:*` command. Once dispatched, the audit entry is committed; revisions go through Layer 5 (`task triage:reset`).
99
- - ~ Bulk patterns: `task triage:bulk-{accept,reject,defer,needs-ac}` (#845 Story 4) for clear label-driven sweeps; bulk results still flow through the audit log so history stays coherent.
100
- - ⊗ Write to `xbrief/proposed/` directly -- only `task triage:accept` (which delegates to `task issue:ingest`) is authorised for that surface.
99
+ - ! On `Discuss`, halt immediately, prompt `What would you like to discuss?`, resume only on explicit user signal. ⊗ Implicit resumption.
100
+ - ! On `Back`, un-buffer prior selection and re-render its action menu only before a `task triage:*` dispatch; after dispatch use `task triage:reset`.
101
+ - ~ Bulk: `task triage:bulk-{accept,reject,defer,needs-ac}`; results still flow through the audit log.
102
+ - ⊗ Write to `xbrief/proposed/` directly -- only `task triage:accept` is authorised.
101
103
 
102
104
  ## Phase 4 -- Audit
103
105
 
104
106
  ! Confirm the session's decisions landed coherently before exiting the skill.
105
107
 
106
- 1. ! Run `task triage:audit --format=json` (D11 / #1128) -- emits the stable `{generated_at, repo, vbrief_staleness, entry_count, entries: [...]}` schema; pipe through `jq` to surface this session's appended entries. For historical look-back, add the #1180 filters: `task triage:audit --since=30d --action=demote --format=json | jq` answers "how many demotes in the last 30 days?" in one call. `--since=<window>` accepts the framework duration grammar (`Nd` / `Nh` / `Nm` / `Nw` / `Ns` or ISO-8601 `PnDTnHnMnS`); `--action=<verb>` filters to a single decision verb (`accept` / `reject` / `defer` / `needs-ac` / `mark-duplicate` / `reset` / `resume-eligible`). Both filters compose with `--format=text` and `--format=json`. The framework deliberately does NOT compute trend lines or apply falsification gates -- the contract is read raw, transform with `jq`.
107
- 2. ! Run `task triage:summary` (D2 / #1122) -- prints the canonical one-liner `[triage] N untriaged · S stale-defer · M in-flight · WIP X/Y [⚠] [· [scope-drift] N]`. The WIP cap default is 20 per #2319, raised from the original 10 per the umbrella Current Shape v3 (overridable via typed `plan.policy.wipCap`). The `⚠` glyph fires only at-or-above cap. The `[scope-drift] N` segment (D14 / #1133) appears only when at least one unsubscribed label/milestone meets the framework `_DRIFT_MIN_ISSUES = 3` threshold; suppressed at zero.
108
- 3. ~ When the summary surfaces a non-zero `[scope-drift] N` (D14 / #1133), surface it to the operator alongside `task triage:scope-drift` output and the matching `task triage:subscribe` / `task triage:unsubscribe` / `task triage:scope-drift -- --ignore-label=<L>` remediation. Subscription mutations record a `subscription-change` audit entry under `xbrief/.eval/subscription-history.jsonl` (sidecar of the existing `candidates.jsonl` audit surface) so future operators can replay how the subscription evolved. After every mutation, run `task triage:bootstrap -- --resume` to backfill newly-subscribed entries / mark newly-out-of-scope entries.
109
- 4. ~ When the audit surfaces a stale acceptance (`accept` decision whose issue is no longer referenced by any `xbrief/active/`), surface it to the operator -- the typical fix is a fresh ingest via `task issue:ingest -- <N>` or a `task triage:reset <N>` if the acceptance was in error.
110
- 5. ⊗ Skip the Phase 4 audit -- silent exit leaves the operator without a record of what landed in `xbrief/proposed/` this session, which is the typical recurrence vector for "what did I just accept?" confusion.
111
-
112
- ! Before reporting an umbrella or epic's current status during triage (what is done, what blocks, wave order), fetch `repos/<owner>/<repo>/issues/<N>/comments` via REST, read the `## Current shape (as of pass-N)` comment and any linked context/`LockedDecisions` xBRIEF — never conclude status from the issue body alone (claim-cites-state-surface, #2066 / AGENTS.md #1152).
108
+ 1. ! Run `task triage:audit --format=json` (D11 / #1128); optional `#1180` filters `--since` / `--action`. Transform with `jq` -- framework does not compute trends.
109
+ 2. ! Run `task triage:summary` (D2 / #1122) -- `[triage] N untriaged · S stale-defer · M in-flight · WIP X/Y [⚠] [· [scope-drift] N]`.
110
+ 3. ~ Non-zero `[scope-drift]` surface `task triage:scope-drift` + subscribe/unsubscribe/ignore remediation; then `task triage:bootstrap -- --resume`.
111
+ 4. ~ Stale accept (no active xBRIEF ref) re-ingest or `task triage:reset`.
112
+ 5. ⊗ Skip Phase 4 audit.
113
+ 6. ! Umbrella/epic status: REST comments → `## Current shape (as of pass-N)` (#2066 / #1152); never body alone.
113
114
 
114
115
  ## Reversibility
115
116
 
116
- ! To undo a decision, run `task triage:reset <N>`. This writes a `reset` audit entry referencing the prior decision id; history is **never** deleted. `task triage:reset` is the canonical Layer 5 reversibility verb (resolves the V3 audit from 2026-05-13). After a reset, the candidate re-enters the untriaged group on the next `task triage:queue` render so it can be re-walked through Phase 3.
117
-
118
- ⊗ Edit or delete prior entries in `xbrief/.eval/candidates.jsonl` to "undo" a decision -- the log is append-only by design and any external mutation breaks the `merge=union` rebase ergonomic (#1144 / N4).
117
+ ! Undo via `task triage:reset <N>` (Layer 5; history never deleted). Edit/delete `xbrief/.eval/candidates.jsonl` to "undo".
119
118
 
120
119
  ## Anti-Patterns
121
120
 
122
- - ⊗ Recommend a specific issue without first consulting `task triage:queue` (binding under AGENTS.md `## Cache-as-authoritative work selection (#1149)`).
123
- - ⊗ Conclude "nothing to do" from `xbrief/{pending,active}` scans or live GitHub reads alone — `task triage:queue` is the ranked superset (#2576).
124
- - ⊗ Walk the queue against a stale cache (Phase 0 gate skipped).
125
- - ⊗ Reimplement audit-log append / `proposed/` write inline -- the `task triage:*` verbs own those surfaces (#845, #883).
126
- - ⊗ Treat `defer` / `needs-ac` as terminal -- they intentionally resurface on the next pass.
127
- - ⊗ Edit `xbrief/.eval/candidates.jsonl` directly to revoke a decision -- use `task triage:reset <N>`.
121
+ - ⊗ Recommend work without `task triage:queue` (#1149).
122
+ - ⊗ Conclude "nothing to do" from folder scans or live GitHub alone (#2576).
123
+ - ⊗ Stale-cache walk; reimplement audit/`proposed/` writes; treat defer/needs-ac as terminal; edit candidates.jsonl; menu-only Phase 3 without operator brief (#2890).
128
124
 
129
125
  ## EXIT
130
126
 
131
- ! When the operator opts out (queue exhausted, mid-session stop, or explicit "done"), confirm skill exit with the canonical phrasing: `deft-directive-triage complete -- exiting skill.`
132
-
133
- ! Provide chaining instructions:
134
-
135
- - **Ingestion / evaluation of accepted items**: chain into `skills/deft-directive-refinement/SKILL.md` -- refinement's Phase 1 ingests the `xbrief/proposed/` items this skill just wrote into the rest of the lifecycle.
136
- - **Cohort dispatch**: chain into `skills/deft-directive-swarm/SKILL.md` -- swarm Phase 0 is queue-driven (N2 / #1142) and consumes the same `task triage:queue` ordering you just walked.
137
- - **Fresh-state refresh before re-entry**: run `task cache:fetch-all -- --source=github-issue --repo OWNER/NAME` then re-enter this skill when ready to continue.
138
-
139
- ⊗ Exit silently without the canonical confirmation + chaining instruction -- the Skill Completion Gate in AGENTS.md is binding.
127
+ ! On opt-out: `deft-directive-triage complete -- exiting skill.` Chain: `deft-directive-refinement` (accepted items) · `deft-directive-swarm` (cohort) · `task cache:fetch-all` then re-enter. ⊗ Silent exit.
140
128
 
141
129
  ## References
142
130
 
143
- - Umbrella: #1119 (Wave-1 D6, this skill)
144
- - D2 #1122 (`task triage:summary`), D3 #1123 (`--resume-on`, parallel), D5 #1127 (`task verify:cache-fresh`), D10 #1129 (auto-classification), D11 #1128 (`task triage:queue` / `triage:show` / `triage:audit`), D12 #1131 (subscription scope)
145
- - Layer 5 reversibility verb: `scripts/triage_actions.py::reset` (already shipped under #845)
146
- - Sibling skills: `skills/deft-directive-refinement/SKILL.md`, `skills/deft-directive-swarm/SKILL.md`, `skills/deft-directive-sync/SKILL.md`
147
- - Stub author (replaced): #1149 (N9)
131
+ - #1119 D6; #1128 D11 (`triage:queue` / `show` / `audit`); #2890 Phase 3 operator brief; #1122 / #1123 / #1127 / #1129 / #1131
132
+ - Siblings: `deft-directive-refinement`, `deft-directive-swarm`, `deft-directive-sync`
@@ -36,7 +36,7 @@ tasks:
36
36
  ENGINE_CMD: 'triage-queue queue --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
37
37
 
38
38
  show:
39
- desc: "Per-issue triage detail (#1128 / D11). -- task triage:show -- <N> [--repo OWNER/NAME]"
39
+ desc: "Per-issue triage detail (#1128 / D11, #2890). -- task triage:show -- <N> [--format=default|operator] [--repo OWNER/NAME]"
40
40
  internal: true
41
41
  deps: [":engine:_ts-build"]
42
42
  dir: '{{.USER_WORKING_DIR}}'
package/tasks/verify.yml CHANGED
@@ -91,6 +91,18 @@ tasks:
91
91
  vars:
92
92
  ENGINE_CMD: 'verify-cursor-tier1 --project-root "{{.DEFT_ROOT}}"'
93
93
 
94
+ openclaw-tier1:
95
+ desc: "Framework-source content gate (#2875): assert OpenClaw (openclaw descriptor, sessions_spawn primitive) is enumerated as Tier 1 in the swarm Phase 3 capability matrix AND routing accepts openclaw as a dispatch_provider. Fails when a doc/code edit drops the OpenClaw descriptor and silently re-opens grok-build / generic-terminal misclassification. Three-state exit (0 clean / 1 missing marker / 2 config error)."
96
+ deps:
97
+ - task: :engine:_ts-build
98
+ # Framework-source-only gate: it scans THIS repo's swarm skill + routing
99
+ # module, so it targets DEFT_ROOT (not USER_WORKING_DIR). Mirrors
100
+ # verify:cursor-tier1 / verify:content-manifest.
101
+ cmds:
102
+ - task: :engine:invoke
103
+ vars:
104
+ ENGINE_CMD: 'verify-openclaw-tier1 --project-root "{{.DEFT_ROOT}}"'
105
+
94
106
  go-freeze:
95
107
  desc: "Tier-1 freeze gate for the legacy Go-installer bridge (#1912). Advisory while the Tier-0 SoT (lastGoInstaller) is null; once the operator pins it, fails when cmd/deft-install is bumped above the frozen tag. Three-state exit (0 ok / 1 violation / 2 config error). Emergency bypass: DEFT_ALLOW_GO_INSTALLER_BUMP=1."
96
108
  deps:
@@ -73,15 +73,15 @@ 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, 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, 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`, `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`, 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
- - `selected_backend`: the stable backend id from `plan.policy.swarmSubagentBackend` / `task policy:subagent-backends` (e.g. `composer`, `grok-build`, `cursor-cloud`) | null -- which catalogued coding backend the operator selected for this role.
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)`.
86
86
  - `resolved_model` (#1739): the concrete model slug the operator pinned for this `(provider, role)` | null for an explicit harness default. Resolved from `.deft/routing.local.json` and stamped into the `task swarm:launch` manifest. **This is the field the dispatch primitive must actually honor** -- see the threading rule below.
87
87
  - `model_source` (#1739): provenance of `resolved_model` -- e.g. `cursor-route`, `harness-default explicit`. Lets a postmortem tell a pinned model from a 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, or a future adapter:
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:
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.
@@ -460,6 +460,7 @@ These rules bind **orchestrators** dispatching implementation, fix, or review-cy
460
460
 
461
461
  - ! Long-running workers (expected >~3 min: implementation, fix batches, review-cycle owners, pollers) MUST be dispatched independently / in the background so the parent conversation channel stays interactive and the orchestrator is notified on completion (`DONE` / `BLOCKED` / `FAILED` per §11).
462
462
  - ! On Cursor, background dispatch means the Task tool's background path (`run_in_background: true` on the Task invocation) — NOT blocking the orchestrator's turn for the worker's full wall-clock.
463
+ - ! On OpenClaw, background dispatch means `sessions_spawn` (optionally with `visible` so the Control UI can watch the subagent) so the parent session stays interactive; the completion channel is **parent push / announce**, not `get_command_or_subagent_output` and not Cursor Task completion (#2874 / #2879). Nested leaf-spawn-leaf limits mirror Cursor #2797 when the platform does not support reliable nested `sessions_spawn`.
463
464
  - ⊗ Foreground/blocking dispatch for long-running implementation, fix, or review-cycle workers when a background/independent dispatch primitive is available — blocking locks the conversation and prevents user steerability (#1878 / Gap D).
464
465
  - ~ Foreground dispatch is reserved for short tasks (<~3 min): quick probes, single-command checks, terse status reads.
465
466
 
@@ -490,7 +491,7 @@ Reference: scope-expansion comment 4399553752 on issue #954.
490
491
 
491
492
  ## 10.5 Heartbeat contract (#1365)
492
493
 
493
- Long-running `spawn_subagent` review-cycle agents on the Grok Build hybrid swarm path can go completely dark from the monitor's perspective -- no commits, no PR comments, no completion notifications. The #1166 swarm session demonstrated the failure mode: two of three dispatched pollers produced zero observable signals; the monitor could not distinguish stalled from healthy.
494
+ Long-running `spawn_subagent` review-cycle agents on the Grok Build hybrid swarm path can go completely dark from the monitor's perspective -- no commits, no PR comments, no completion notifications. The same visibility gap applies to OpenClaw `sessions_spawn` and Cursor `Task` pollers. The #1166 swarm session demonstrated the failure mode: two of three dispatched pollers produced zero observable signals; the monitor could not distinguish stalled from healthy.
494
495
 
495
496
  The heartbeat contract closes that gap. Any sub-agent whose tool loop is expected to run for more than ~3 minutes (review-cycle pollers, watchdogs, long-running implementation agents) MUST emit a small JSON heartbeat at `<project-root>/.deft-scratch/subagent-status/<agent-id>.json` per `docs/subagent-heartbeat.md`.
496
497
 
@@ -506,6 +507,10 @@ The parent monitor watches the heartbeat file directly (three-state exit 0 ok /
506
507
 
507
508
  ! **Cursor false-alive / REDISPATCH_OK (#2824):** On the Cursor `Task` path, the host may report a leaf as "still running" after it has gone silent (empty transcript, no heartbeats, no DONE/FAILED). When `task verify:subagent-alive` exits `1` for a registered in-flight `drive-to: merge*` worker — missing heartbeat, STALE heartbeat, or no recent git/PR activity — the monitor MUST treat the worker as dead and print `REDISPATCH_OK` to authorize takeover re-dispatch. Do NOT block on host resume when the liveness gate has failed closed.
508
509
 
510
+ ! **OpenClaw `sessions_spawn` / heartbeat mapping (#2879):** Same file-heartbeat contract applies to OpenClaw review-monitors and long-running leaves. OpenClaw host session liveness, Control UI presence, or gateway channel reachability does NOT replace periodic heartbeats — those signals only prove the session exists, not that the tool loop is progressing. OpenClaw pollers write `.deft-scratch/subagent-status/<agent-id>.json` so `task agent:monitor` / `task verify:subagent-alive` can detect stalled monitors; OpenClaw-native session status MAY be a *supplementary* signal only. Host "still running" + missing/STALE heartbeat authorizes the same `REDISPATCH_OK` posture as Cursor #2824.
511
+
512
+ ! **Parent ensures scratch dir + startup grace before REDISPATCH_OK (#2879):** `task verify:subagent-alive` exits `2` (config error, no `REDISPATCH_OK`) when the scratch directory is **missing** and has no records. Parents MUST `mkdir` the worker worktree's `.deft-scratch/subagent-status/` at dispatch time so a later missing record is exit `1` + `REDISPATCH_OK`. Parents MUST ALSO wait a **startup grace** (default 3 minutes from dispatch, or until the first `phase: "starting"` heartbeat is observed) before treating a missing required-agent as takeover-eligible — probing an empty parent-created dir immediately races a healthy worker still writing its first heartbeat and can spawn a duplicate. Exit `2` remains reserved for true config errors (bad args / wrong path).
513
+
509
514
  - Monitors run `task verify:subagent-alive -- --require-agent <agent-id> [--scratch-dir <worktree>/.deft-scratch/subagent-status]` each poll iteration.
510
515
  - Workers run `task agent:monitor` (raw sweep) or the gate verb above; both wrap `subagent-monitor` (#1365).
511
516
 
@@ -518,6 +523,12 @@ Every worker MUST send a final status message before exiting its tool loop, rega
518
523
  - Failure: `FAILED: <reason> + recovery hint`
519
524
  - Stand-down: `STOOD-DOWN: <reason>` (e.g. user said "wait" with no follow-up dispatch)
520
525
 
526
+ ! **`drive-to: merge-ready` DONE reservation (#2843):** When the dispatch envelope scoped `drive-to: merge-ready` (or `drive-to: merge`), `DONE` is reserved for merge-ready completion — `task pr:merge-ready -- <N>` exit 0 on current HEAD, or merge + `task scope:complete` when the envelope included merge authority. Greptile P0/P1 outstanding, CI failure, branch behind, review-cycle cap, or any other merge blocker MUST NOT exit as `DONE`.
527
+
528
+ ! **Mid-cycle BLOCKED contract (#2843):** A `drive-to: merge-ready` worker that must exit before merge-ready (blocker, cap, context limit, host turn budget) MUST emit `BLOCKED:` (never `DONE`) with: PR number (or `no-pr`), HEAD SHA, blocker class (`greptile_p0_p1` / `ci_failures` / `behind_base` / `review_cycle_cap` / `context_limit` / other), worktree path, and `REDISPATCH_OK` when a continuation leaf should take over. Example: `BLOCKED: Greptile P1 on HEAD abc1234 (PR #2842, blocker greptile_p0_p1, worktree .deft-scratch/worktrees/2839-appsec, REDISPATCH_OK)`.
529
+
530
+ ⊗ Emit `DONE` from a `drive-to: merge-ready` worker while merge-ready is false — a false-terminal `DONE` pulls the cohort monitor into inline Greptile fixes and violates Gap D (#2843 monitor-as-implementer recurrence).
531
+
521
532
  Per-step acks during the run are noise. ONE start message, ONE final message; intermediate messages only on `BLOCKED` / `FAILED`. The final message lets the dispatcher distinguish a clean exit from a silent timeout when the lifecycle event arrives.
522
533
 
523
534
  ## 12. Session ritual + `task verify:cache-fresh` gates before `start_agent` (#1348 / #1127)
@@ -6,13 +6,14 @@ Canonical Greptile review-cycle / poller sub-agent prompt body.
6
6
  Used by parent monitor agents when delegating post-PR work via the platform
7
7
  dispatch primitive per the parent's Phase 3 runtime capability detection
8
8
  (`spawn_subagent` for "grok-build" / Grok Build TUI, `start_agent` for
9
- Warp-orchestrated, or equivalent in other environments). See
10
- `skills/deft-directive-swarm/SKILL.md` Phase 3 + #1342 slices 1-3 and the
11
- review-cycle skill's updated Approach 1.
9
+ Warp-orchestrated, `sessions_spawn` for OpenClaw, Cursor `Task` with
10
+ `run_in_background: true`, or equivalent in other environments). See
11
+ `skills/deft-directive-swarm/SKILL.md` Phase 3 + #1342 slices 1-3 / #2874
12
+ OpenClaw Tier 1 and the review-cycle skill's updated Approach 1.
12
13
 
13
14
  The parent reads this file and applies Python `str.format(...)` to substitute
14
15
  five placeholders, then passes the formatted prompt via the chosen dispatch
15
- primitive (spawn_subagent, start_agent, etc.):
16
+ primitive (spawn_subagent, start_agent, sessions_spawn, Cursor Task, etc.):
16
17
 
17
18
  from pathlib import Path
18
19
  prompt = Path("templates/swarm-greptile-poller-prompt.md").read_text(encoding="utf-8").format(
@@ -23,7 +24,9 @@ primitive (spawn_subagent, start_agent, etc.):
23
24
  parent_agent_id="<parent-id>",
24
25
  )
25
26
  # Dispatch via the detected primitive (example for start_agent shown;
26
- # use spawn_subagent + the launch adapter for grok-build per #1342)
27
+ # use spawn_subagent + the launch adapter for grok-build per #1342;
28
+ # use sessions_spawn for OpenClaw per #2874 / #2879 -- completion is
29
+ # parent push / announce, not get_command_or_subagent_output)
27
30
  start_agent(name=f"greptile-poller-{{N}}", prompt=prompt, execution_mode="local")
28
31
 
29
32
  This file is the proven prompt body. Hand-authored variants have repeatedly
@@ -31,10 +34,11 @@ missed two specific parsing bugs (markdown-link `Last reviewed commit:`,
31
34
  raw `\b(P0|P1)\b` substring scan with negation false-positive); the body
32
35
  below encodes the fixes inline. See #727 (canonical encoding) and
33
36
  `skills/deft-directive-swarm/SKILL.md` Phase 6 Sub-Agent Role Separation
34
- (#1342 platform adapter unification: the platform adapter dispatches via
35
- `spawn_subagent` on grok-build and `start_agent` on Warp-orchestrated, so
36
- this template is platform-agnostic by construction) for the rules that
37
- mandate using this template instead of hand-authoring.
37
+ (#1342 / #2879 platform adapter unification: the platform adapter dispatches
38
+ via `spawn_subagent` on grok-build, `start_agent` on Warp-orchestrated, and
39
+ `sessions_spawn` on OpenClaw, so this template is platform-agnostic by
40
+ construction) for the rules that mandate using this template instead of
41
+ hand-authoring.
38
42
 
39
43
  NOTE on `.format()` escaping: every literal curly brace in this file is
40
44
  doubled (`{{` / `}}`) so it survives the `str.format(...)` pass. The five
@@ -48,8 +52,12 @@ DO NOT STOP until ONE of the six terminal exit conditions below fires.
48
52
  ## Role posture
49
53
 
50
54
  - Single role: review-cycle agent. Read `skills/deft-directive-review-cycle/SKILL.md` and follow Phase 2 (Review/Fix Loop) end-to-end.
51
- - Parent agent ID for status messages: `{parent_agent_id}`. Send status updates via `send_message_to_agent` at start, on each terminal exit condition, and on any blocker.
52
- - Execution: local. Working directory: the worktree the parent gave you (or your `--cwd` if running under `oz agent run --cwd`).
55
+ - Parent agent ID for status messages: `{parent_agent_id}`. Send status updates at start, on each terminal exit condition, and on any blocker via the **host completion channel** for your dispatch primitive:
56
+ - Warp / `start_agent`: `send_message_to_agent`
57
+ - grok-build / `spawn_subagent`: spawn result channel / parent announce (parent may poll `get_command_or_subagent_output`)
58
+ - **OpenClaw / `sessions_spawn` (#2879):** parent push / announce (session announce or Control UI–visible completion) -- **not** `get_command_or_subagent_output`, and not Cursor Task completion semantics
59
+ - Cursor `Task` (background): Task completion-notification path
60
+ - Execution: local. Working directory: the worktree the parent gave you (or your `--cwd` if running under `oz agent run --cwd` / OpenClaw session cwd).
53
61
 
54
62
  ## Bounded poll loop
55
63
 
@@ -533,7 +541,7 @@ Send:
533
541
  - ! Set `$env:GIT_EDITOR = "true"` (Windows PowerShell) or `GIT_EDITOR=true` (Unix) BEFORE any git command that could open an editor (rebase, commit --amend) to prevent terminal lockup.
534
542
  - ! Use Python scripts (single `run_shell_command` call) for the poll loop, NEVER shell `Start-Sleep` + repeated tool calls. The Python script handles `time.sleep({poll_interval_seconds})` between polls and exits when a terminal condition fires.
535
543
  - ! Always pass `do_not_summarize_output: true` semantics when fetching `gh pr view --comments` -- summarizers silently drop the Outside-Diff section.
536
- - ! Send a status message to `{parent_agent_id}` at start (acknowledging the task) and at every terminal exit (CLEAN / NEW P0/P1 FINDINGS escalation / ERRORED / TIMEOUT / STALL). Do NOT silently complete.
544
+ - ! Send a status message to `{parent_agent_id}` at start (acknowledging the task) and at every terminal exit (CLEAN / NEW P0/P1 FINDINGS escalation / ERRORED / TIMEOUT / STALL / INFORMAL-CLEAN). Route it through the host completion channel for your primitive (see Role posture -- OpenClaw `sessions_spawn` uses parent push / announce). Do NOT silently complete.
537
545
 
538
546
  ## Implementation Notes
539
547
 
@@ -554,3 +562,4 @@ Dogfood lessons captured during the #727 self-review cycle. The template body ab
554
562
  - #727 -- this template's acceptance issue and the full anti-pattern record (rm-chaining, parsing-bug recurrence, role-conflation in implementation-agent prompts).
555
563
  - #1039 -- (5) STALL terminal exit + Tier 1 instrumentation + Tier 3 per-condition fail-loud (`clean_gate_holdout`); the third recurrence in this template's detector-gap chain after #910 (triple-tier) and #1035 (Tier 2.5 + confidence-heading).
556
564
  - #1364 -- cohort-level CLEAN verification gate (`task swarm:verify-review-clean`, `scripts/swarm_verify_review_clean.py`). The (1) CLEAN section's swarm-orchestrated terminal contract block declares that only the exact `PR #{pr_number} CLEAN -- ready for merge` subject with `confidence > 3` on current HEAD is an acceptable "review complete" signal for the swarm monitor's Phase 5 -> 6 transition; the cohort verifier picks up any other terminal exit ((2) NEW P0/P1 FINDINGS escalation, (3) ERRORED, (4) TIMEOUT, (5) STALL) and holds the merge gate until fresh poller re-dispatch or explicit user escalation resolves it. Recurrence record: #1166 swarm execution where multiple pollers exited with `clean_gate_holdout=confidence` (confidence == 3) and the monitor still raised the Phase 5 -> 6 gate because the trigger keyed on "all pollers have reported back" rather than "every PR in the cohort is objectively CLEAN".
565
+ - #2879 -- OpenClaw `sessions_spawn` + parent push/announce completion channel named in Role posture (alongside Warp `start_agent` and grok-build `spawn_subagent`); heartbeat mapping in `docs/subagent-heartbeat.md` and preamble §10.5. Epic #2874.