@deftai/directive-content 0.79.4 → 0.81.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 +3 -0
- package/UPGRADING.md +14 -0
- package/commands.md +7 -2
- package/contracts/runtime-authority.md +54 -0
- package/docs/product-signal.md +50 -0
- package/languages/typescript.md +40 -0
- package/package.json +1 -1
- package/packs/skills/skills-pack-0.1.json +16 -2
- package/scm/github.md +8 -0
- package/skills/deft-directive-product-signal/SKILL.md +94 -0
- package/skills/deft-directive-release/SKILL.md +15 -0
- package/skills/deft-directive-review-cycle/SKILL.md +9 -0
- package/tasks/product-signal.yml +55 -0
package/Taskfile.yml
CHANGED
|
@@ -330,6 +330,9 @@ includes:
|
|
|
330
330
|
value:
|
|
331
331
|
taskfile: ./tasks/value.yml
|
|
332
332
|
optional: true
|
|
333
|
+
product-signal:
|
|
334
|
+
taskfile: ./tasks/product-signal.yml
|
|
335
|
+
optional: true
|
|
333
336
|
# Pack-slicing surface (#1283 design, #1294 pilot, ADR-001 Layer B). Exposes
|
|
334
337
|
# `task packs:slice` (named-slice API), `task packs:render` (regenerate the
|
|
335
338
|
# meta/lessons.md projection), and `task packs:verify-drift` (the drift gate,
|
package/UPGRADING.md
CHANGED
|
@@ -12,6 +12,20 @@ Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
## TypeScript 7 side-by-side (pre-7.1) (#2591)
|
|
16
|
+
|
|
17
|
+
- **Applies when:** a Deft TypeScript project (or agent upgrading its toolchain) needs TypeScript 7 before ~7.1, while `typescript-eslint` and other programmatic consumers still require the TS 6 compiler API. This is **not** a Directive framework-version migration — read [languages/typescript.md](./languages/typescript.md) instead of treating it like a `deft update` step.
|
|
18
|
+
- **Safe to auto-run:** Yes for the alias + Dependabot-ignore edits in the project's own `package.json` / `.github/dependabot.yml` when following the documented pattern.
|
|
19
|
+
- **Restart required:** No. Re-run `task check` / `npm install` (or your package manager equivalent) after changing devDependencies.
|
|
20
|
+
- **Commands:**
|
|
21
|
+
- Read the full pattern: [languages/typescript.md — TypeScript 7 side-by-side (pre-7.1)](./languages/typescript.md#typescript-7-side-by-side-pre-71)
|
|
22
|
+
- After edits: `npm install` (or `pnpm install` / `yarn`) then `task check`
|
|
23
|
+
- **References:**
|
|
24
|
+
- [#2591](https://github.com/deftai/directive/issues/2591) — document side-by-side setup for Deft TypeScript projects.
|
|
25
|
+
- [deftai/cartograph#111](https://github.com/deftai/cartograph/pull/111) — precedent implementation.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
15
29
|
## Helped + health metrics relocation (#2545)
|
|
16
30
|
|
|
17
31
|
- **Applies when:** any project that upgraded to a release shipping #2545 and still has append logs under `<lifecycle-root>/.eval/results/crud-metrics.jsonl` or `health-history.jsonl` inside the git worktree.
|
package/commands.md
CHANGED
|
@@ -187,13 +187,16 @@ Use `task --list` for the exact current verify namespace.
|
|
|
187
187
|
|
|
188
188
|
### Agent-host direct-write hooks (#2438, #2596)
|
|
189
189
|
|
|
190
|
-
`directive init` and `deft update` idempotently merge Directive-owned entries into `.claude/settings.json`, `.grok/hooks/deft.json`, `.cursor/hooks.json`, and `.codex/hooks.json` while preserving unrelated settings. `SessionStart` refreshes resume bookkeeping on a non-blocking path. `PreToolUse` covers direct edit/write tools and denies them until both existing gates pass: a fresh gated session ritual and an active/running xBRIEF accepted by canonical preflight.
|
|
190
|
+
`directive init` and `deft update` idempotently merge Directive-owned entries into `.claude/settings.json`, `.grok/hooks/deft.json`, `.cursor/hooks.json`, and `.codex/hooks.json` while preserving unrelated settings. `SessionStart` refreshes resume bookkeeping on a non-blocking path. `PreToolUse` covers direct edit/write tools and denies them until both existing gates pass: a fresh gated session ritual and an active/running xBRIEF accepted by canonical preflight. A second `PreToolUse` matcher covers spawn/Task tools (`Task`, `SubagentStart`, `spawn_subagent`, `start_agent`, `CreateAgent`) with the same pre-`start_agent` gate stack; explore spawns (`subagent_type: explore`) pass without implementation gates.
|
|
191
|
+
|
|
192
|
+
- **Read-only explore (#1185):** Prefer Grok role deposit `default_capability_mode = "read-only"` (see [issue #1185](https://github.com/deftai/directive/issues/1185)). Hooks also deny direct writes when `DEFT_HOOK_READ_ONLY=1` or the host payload signals read-only capability. Implementation spawns remain blocked in read-only posture unless explicitly marked explore.
|
|
191
193
|
|
|
192
194
|
- Verify registration: `deft verify:hooks-installed --scope=agent` (or `--scope=all` for git + agent hooks).
|
|
193
195
|
- Repair missing/drifted entries: `deft update`.
|
|
196
|
+
- **Compact re-arm (#2113):** Cursor `preCompact` and Claude/Grok `PreCompact`/`PostCompact` call `deft hook:dispatch --event session.compact` to mark the gated session ritual stale after context compaction/resume; the existing PreToolUse gate then denies direct writes until `deft session:start` and `deft verify:session-ritual -- --tier=gated`. Codex has no native compact hook — operators must re-run the mutation ritual manually after compaction.
|
|
194
197
|
- Codex project hooks are trust-gated by Codex. Directive verifies only that the registrations are structurally current; after an install or changed hook hash, open `/hooks` in Codex and review/approve the project hook commands. Runtime trust cannot be inferred from the file alone.
|
|
195
198
|
- Directive writes only `.codex/hooks.json`; it does not parse or modify `.codex/config.toml`. Codex can also load inline hooks from `config.toml`, so avoid defining duplicate Directive commands there or they may run more than once. See the [Codex hooks documentation](https://learn.chatgpt.com/docs/hooks).
|
|
196
|
-
- The P0 hook slice does not classify shell-mediated writes, MCP mutations, richer unified-exec calls, WebSearch
|
|
199
|
+
- The P0 hook slice does not classify shell-mediated writes, MCP mutations, richer unified-exec calls, or WebSearch by default. **Runtime authority (#1394)** adds opt-in path allow/deny lists and graduated `scopes` (`edits`, `push`, `merge`) under `plan.policy.runtimeAuthority` — inspect with `deft policy:show --field=runtimeAuthority`. When `enabled: true`, PreToolUse denies classifiable direct-write targets outside `allowPaths` or matching `denyPaths` after ritual/scope/read-only gates; `scopes.edits` gates all direct writes. `push` / `merge` scopes are schema-only until Shell/MCP matchers land (host gap — TODO).
|
|
197
200
|
|
|
198
201
|
## Session-start ritual (#1149)
|
|
199
202
|
|
|
@@ -210,6 +213,8 @@ Full always-on contract for the interactive session-start ritual and its gated v
|
|
|
210
213
|
### Mutable ritual (mutation posture)
|
|
211
214
|
|
|
212
215
|
- ! On **mutation** session start, run `deft session:start` (or `task session:start` in framework source) after loading AGENTS.md. Records quick-tier ritual in `.deft/ritual-state.json`: alignment confirmation, branch-policy disclosure, `deft verify:tools` guidance, default-branch sync warnings, and `deft triage:welcome` one-liner. State is worktree- and HEAD-bound; stale after `plan.policy.sessionRitualStalenessHours` hours (default 4).
|
|
216
|
+
- ~ Mutable `deft session:start` also performs a bounded, non-fatal release-availability probe against the public npm registry after disclosing it. It skips read-only sessions, framework source checkouts, non-release pins, and `DEFT_NO_NETWORK=1`; identical latest-version notices throttle for 24 hours in `xbrief/.triage-cache/release-availability-state.json`. This is separate from `deft doctor`, whose bare and gated invocations remain offline by default (#2182). Refs #1692.
|
|
217
|
+
- ~ At safe idle points (clean tree, no in-flight story), mutation session start and `deft scope:complete` may also run the staleness tickler: an interactive, consent-based offer to upgrade Directive (`npm i -g @deftai/directive@latest`) and/or migrate xBRIEF (`deft migrate:xbrief`). Escalation tiers, snooze windows, and opt-out live under `plan.policy.stalenessTickler` — inspect with `deft policy:show --field=stalenessTickler`. State persists in `xbrief/.triage-cache/staleness-tickler-state.json`. Skips framework source checkouts, dirty trees, CI/headless (`DEFT_SESSION_RITUAL_SKIP=1`), and typed opt-out. Refs #2488 / #2489.
|
|
213
218
|
- ! Before any code-writing tool call or `start_agent` implementation dispatch, run `deft verify:session-ritual -- --tier=gated`. Gated tier fails closed unless quick-tier state is fresh; lazily records `deft doctor` and `deft verify:cache-fresh` entrypoints. Step 0 of the pre-`start_agent` gate stack.
|
|
214
219
|
- ? Postpone with `deft session:start -- --defer step=reason` (`alignment`, `branch_policy`, `triage_welcome`, `doctor`, `cache_fresh`).
|
|
215
220
|
- Headless workers / CI MAY set `DEFT_SESSION_RITUAL_SKIP=1`; verifier exits 0 but warns when bypass hides failure.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Runtime authority policy (#1394)
|
|
2
|
+
|
|
3
|
+
Typed session-level enforcement under `plan.policy.runtimeAuthority` in `xbrief/PROJECT-DEFINITION.xbrief.json`.
|
|
4
|
+
|
|
5
|
+
## Defaults
|
|
6
|
+
|
|
7
|
+
| Field | Default | Notes |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `enabled` | `false` | Opt-in — existing projects unchanged until enabled |
|
|
10
|
+
| `allowPaths` | `[]` | Empty = allow all paths (when enabled) |
|
|
11
|
+
| `denyPaths` | `[]` | Deny wins over allow |
|
|
12
|
+
| `scopes.edits` | `true` | Direct Write/Edit/StrReplace tools |
|
|
13
|
+
| `scopes.push` | `false` | Schema only — Shell matcher TODO |
|
|
14
|
+
| `scopes.merge` | `false` | Schema only — Shell matcher TODO |
|
|
15
|
+
|
|
16
|
+
## Path globs
|
|
17
|
+
|
|
18
|
+
Gitignore-style globs via the shared `matchPath` helper (`src/**`, `**/AGENTS.md`, etc.). Paths are normalized to project-relative POSIX before matching.
|
|
19
|
+
|
|
20
|
+
## Evaluation order (PreToolUse)
|
|
21
|
+
|
|
22
|
+
1. Ritual / scope / read-only / spawn gates (existing #2438 / #1185 stack)
|
|
23
|
+
2. Runtime authority path + `scopes.edits` (this policy)
|
|
24
|
+
|
|
25
|
+
Policy load failures fail open (host crash behavior unchanged).
|
|
26
|
+
|
|
27
|
+
## Inspection
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
deft policy:show --field=runtimeAuthority
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Example
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"plan": {
|
|
38
|
+
"policy": {
|
|
39
|
+
"runtimeAuthority": {
|
|
40
|
+
"enabled": true,
|
|
41
|
+
"allowPaths": ["src/**", "xbrief/**", "packages/**"],
|
|
42
|
+
"denyPaths": [".env", "secrets/**"],
|
|
43
|
+
"scopes": { "edits": true, "push": false, "merge": false }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Deferred (host gap)
|
|
51
|
+
|
|
52
|
+
- Shell/Bash `git push`, `gh pr merge`, MCP mutations — not on the direct-write PreToolUse matcher today. `scopes.push` / `scopes.merge` are reserved for a follow-on Shell matcher (#1394 phase 2).
|
|
53
|
+
|
|
54
|
+
Refs #2437 Core T1 Wave C.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Product signal (optional partner check-in)
|
|
2
|
+
|
|
3
|
+
Phase 1 consented product-improvement signal under epic #2603 (#2693). Defaults **off**.
|
|
4
|
+
|
|
5
|
+
## Enable (project)
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
task product-signal:enable -- --confirm
|
|
9
|
+
task policy:show -- --field=productSignal
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Capability-cost disclosure prints before `--confirm` applies `plan.policy.productSignal.enabled=true`.
|
|
13
|
+
|
|
14
|
+
## Consent (install / user)
|
|
15
|
+
|
|
16
|
+
Consent file: `%APPDATA%\\deft\\product-signal-consent.json` (Windows) or `~/.config/deft/product-signal-consent.json` (Unix).
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
task product-signal:consent -- --grant
|
|
20
|
+
task product-signal:consent -- --revoke
|
|
21
|
+
task product-signal:status
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Outbound requires **both** enable and consent.
|
|
25
|
+
|
|
26
|
+
## Submit (ops / tests)
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
task product-signal:submit -- --surface pulse|portrait [--dry-run] [--json] [--nps 0-10]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Sink bootstrap (maintainers)
|
|
33
|
+
|
|
34
|
+
Internal (org) inbox: `deftai/product-signal` (overridable via `plan.policy.productSignal.sinkRepo`). Not public.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
task product-signal:bootstrap-sink [-- --dry-run]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Creates the repo if needed, sets visibility to **internal**, and bootstraps D20 labels (`surface:*`, `nps:*`, `signal:gap`). Standing threads keyed by `(installId, actorName)`.
|
|
41
|
+
|
|
42
|
+
## Skill
|
|
43
|
+
|
|
44
|
+
Agents: `deft-directive-product-signal` — triggers `product check-in`, `product pulse`, `partner feedback`, `product signal`.
|
|
45
|
+
|
|
46
|
+
## Related
|
|
47
|
+
|
|
48
|
+
- Public gap escalation: `deft-directive-feedback` / `task feedback:file` (hard confirm)
|
|
49
|
+
- Skill telemetry emit: #829 (optional `skillsSummary` hook only; not required for pulse)
|
|
50
|
+
- HTTP transport: #2697 (Phase 2)
|
package/languages/typescript.md
CHANGED
|
@@ -124,6 +124,46 @@ Key settings: `@typescript-eslint/parser`, extends `recommended` + `recommended-
|
|
|
124
124
|
- ⊗ Empty `catch` blocks or `catch (e) {}` — log or re-throw
|
|
125
125
|
- ⊗ Returning `null`, `undefined`, or a neutral default to mask a thrown error
|
|
126
126
|
|
|
127
|
+
## TypeScript 7 side-by-side (pre-7.1)
|
|
128
|
+
|
|
129
|
+
TypeScript 7.0 ships without a stable programmatic compiler API. A direct `typescript` major bump to 7 breaks `typescript-eslint` (for example `ModuleKind.Cjs` is undefined). Until ~7.1, ! run TS 7 side-by-side with a TS 6 alias for tooling that still needs the compiler API — the same pattern as [deftai/cartograph#111](https://github.com/deftai/cartograph/pull/111).
|
|
130
|
+
|
|
131
|
+
### package.json aliases
|
|
132
|
+
|
|
133
|
+
Keep `typescript` on the TS 6 API for ESLint, `tsc --noEmit`, and other programmatic consumers. Install TS 7 under `@typescript/native`:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"devDependencies": {
|
|
138
|
+
"@typescript/native": "npm:typescript@^7.0.2",
|
|
139
|
+
"typescript": "npm:@typescript/typescript6@^6.0.2"
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Use `@typescript/native` (or `pnpm exec tsgo`) for TS 7-native typechecking when you opt in; keep existing `typescript` / `tsc` scripts on the alias until typescript-eslint and your toolchain support TS 7's programmatic surface.
|
|
145
|
+
|
|
146
|
+
### Dependabot
|
|
147
|
+
|
|
148
|
+
Because `typescript` is aliased to `@typescript/typescript6`, ! ignore Dependabot major bumps on the `typescript` dependency name until you intentionally migrate off the side-by-side layout:
|
|
149
|
+
|
|
150
|
+
```yaml
|
|
151
|
+
ignore:
|
|
152
|
+
- dependency-name: "typescript"
|
|
153
|
+
update-types: ["version-update:semver-major"]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Scaffold and doctor coverage
|
|
157
|
+
|
|
158
|
+
**Scaffold bake — deferred (#2591):** Directive does not ship a Deft-owned TypeScript `package.json` scaffold to mutate. New TypeScript projects copy the alias snippet from this section into their own `package.json`.
|
|
159
|
+
|
|
160
|
+
**Doctor hint — shipped (#2591):** `deft doctor` warns (advisory, exit-exempt) when `package.json` declares typescript-eslint (`typescript-eslint` or `@typescript-eslint/*`) and `eslint`, and `typescript` resolves to 7.x without the `@typescript/typescript6` alias — pointing here and at the Cartograph alias pattern above. Scaffold bake remains deferred.
|
|
161
|
+
|
|
162
|
+
### References
|
|
163
|
+
|
|
164
|
+
- [Announcing TypeScript 7.0 — side-by-side install](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/#side-by-side-installation) — official TS 7 side-by-side guidance.
|
|
165
|
+
- [typescript-eslint#12518](https://github.com/typescript-eslint/typescript-eslint/issues/12518) — typescript-eslint TS 7 / compiler API tracking.
|
|
166
|
+
|
|
127
167
|
## Compliance Checklist
|
|
128
168
|
|
|
129
169
|
- ! Include TSDoc comments for all exported APIs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive-content",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.0",
|
|
4
4
|
"description": "Shippable Directive framework content in the consumer .deft/core/ layout (C1 flatten), plus the engine surfaces (.githooks/, Taskfile.yml, tasks/) the deposit wires. Python-free per #2022 Phase 3. Refs #11, #1669, #1967.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -91,6 +91,20 @@
|
|
|
91
91
|
"body": "# Deft Directive Feedback -- gap escalation to upstream\n\nConversational batched flow for filing framework gaps discovered during consumer sessions. Mirrors the confirmation gate from `deft-directive-article-review` -- the agent drafts and dedups; the operator approves before any upstream issue is created.\n\nLegend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.\n\n## When to Use\n\n- Session end when `friction:*` ledger signals or operator reports a directive shortfall\n- Operator says \"file this upstream\", \"report a framework gap\", or \"directive feedback\"\n- After enabling `plan.policy.valueFeedback.upstreamPrompt` during onboarding\n\n## Preconditions\n\n- ! Run only from a **consumer project** -- the filing path no-ops inside the directive maintainer repo\n- ! `plan.policy.valueFeedback.upstreamPrompt` MUST be ON (`task policy:show --field=valueFeedback`)\n- ⊗ File upstream issues without explicit operator confirmation\n- ⊗ Invoke when `valueFeedback.enabled` is OFF\n\n## Phase 1 -- Collect (batched)\n\n- ! Gather concrete gap reports from the session: what was expected, what happened, and minimal reproduction context\n- ! Batch multiple friction items into one upstream issue when they share a root cause; otherwise prepare separate drafts\n- ~ Prefer attributed phrasing (\"encoding gate blocked a valid file\") over vague quality claims\n\n## Phase 2 -- Draft + dedup\n\n- ! For each candidate report, run a dry draft:\n\n```bash\ntask feedback:file -- --summary \"<one-line summary>\" --context \"<session context>\" --expected \"<expected>\" --actual \"<actual>\" --notes \"<optional>\"\n```\n\n- ! Read the printed draft title/body with the operator before proceeding\n- ! If the command reports a duplicate open issue, STOP and link the existing issue instead of filing again\n- ⊗ Proceed past a duplicate-detection block without operator override\n\n## Phase 3 -- Confirm + file\n\n- ! Present the final draft and ask for explicit yes/no confirmation\n- ! Only after approval, re-run with `--confirm`:\n\n```bash\ntask feedback:file -- --summary \"<one-line summary>\" --context \"<session context>\" --expected \"<expected>\" --actual \"<actual>\" --confirm\n```\n\n- ! Print the filed issue URL to the operator\n- ⊗ Use `Closes`/`Fixes`/`Resolves` in the upstream body -- use `Refs #1709` only\n\n## Phase 4 -- Handoff\n\n- ~ Record the upstream issue URL in the session handoff or continue checkpoint if the operator tracks follow-ups locally\n- ~ Return to the prior workflow; gap escalation does not block story completion\n\n## Anti-Patterns\n\n- ⊗ Filing from the maintainer framework repo (consumer-only guard)\n- ⊗ Skipping dedup review when the command reports an existing open issue\n- ⊗ Treating `--confirm` as implicit from broad session approval -- require an explicit filing confirmation step\n",
|
|
92
92
|
"frontmatter_extra": null
|
|
93
93
|
},
|
|
94
|
+
{
|
|
95
|
+
"id": "deft-directive-product-signal",
|
|
96
|
+
"description": "Consented agent-driven product check-in (pulse + portrait) with enable and consent gates, minimized local ledger attach, and private GitHub sink submit. Defaults off.",
|
|
97
|
+
"triggers": [
|
|
98
|
+
"product check-in",
|
|
99
|
+
"product pulse",
|
|
100
|
+
"partner feedback",
|
|
101
|
+
"product signal"
|
|
102
|
+
],
|
|
103
|
+
"path": "skills/deft-directive-product-signal/SKILL.md",
|
|
104
|
+
"version": "0.1",
|
|
105
|
+
"body": "# Deft Directive Product Signal — consented check-in (#2693)\n\nAgent-driven qualitative product-improvement signal for partner/trial installs. Lightweight interview, minimized local summaries (value/health/helped), optional skillsSummary hook — submits to private `deftai/product-signal` when enable + consent gates pass.\n\nLegend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.\n\n## When to Use\n\n- Operator says `product check-in`, `product pulse`, `partner feedback`, or `product signal`\n- Partner onboarding after ops enables `plan.policy.productSignal`\n- ⊗ Do NOT invoke from session:start or ambient rituals when disabled (#2693 D3)\n\n## Preconditions\n\n- ! `plan.policy.productSignal.enabled` MUST be true (`task policy:show --field=productSignal`)\n- ! Install-level consent MUST be recorded before outbound submit (`task product-signal:status`)\n- ⊗ Prompt or submit when `enabled` is false\n- ⊗ Block story work on sink errors — fail open (#2693 D16/D18)\n\n## Phase 1 — Gate\n\n```bash\ntask product-signal:status\ntask policy:show -- --field=productSignal\n```\n\n- ! If disabled: soft-skip with one line; exit skill\n- ! If headless/CI: fail open — no consent/interview prompts (#2693 D16)\n\n## Phase 2 — First-time consent (interactive only, D17)\n\nWhen enabled, interactive, and consent missing/revoked, ask:\n\n> May we collect usage metrics and related session signal from this install to improve Directive? This can include a short check-in plus minimized local summaries (value/health and related ledgers). Nothing is sent while this path is off or without your consent.\n>\n> If you are unsure, please check with your company before saying no — they are a Directive partner and may already expect this signal to be shared.\n>\n> Reply **yes** to consent, or **no** to decline.\n\n- **yes** → `task product-signal:consent -- --grant`; continue\n- **no** → soft-skip; do not badger every turn\n\n## Phase 3 — Interview (≤3 answers + optional NPS)\n\n- ! Ask at most three short questions plus optional NPS (0–10)\n- ~ Target ≤30s human time beyond first-time consent\n- ⊗ Paste secrets, paths, raw logs, or full USER.md into answers\n\n## Phase 4 — Assemble + validate + submit\n\n```bash\ntask product-signal:submit -- --surface pulse --dry-run\ntask product-signal:submit -- --surface pulse --nps 9\n```\n\n- ! On validation failure: show reason; no partial write\n- ! On sink ACL/network errors: soft-skip with hint; do not fail caller (#2693 D18)\n- ! Report issue URL on success\n\n## Private gaps (D19)\n\n- ! Record bugs/gaps as `Gap:` comments on the standing **pulse** thread (enable+consent sufficient)\n- ! Public promote to `deftai/directive` remains hard-confirm via `deft-directive-feedback` / `task feedback:file`\n\n## Revoke\n\n```bash\ntask product-signal:consent -- --revoke\n# and/or disable: set plan.policy.productSignal.enabled false\n```\n\n## Anti-Patterns\n\n- ⊗ Ambient consent nag when disabled\n- ⊗ Fail-closed headless behavior\n- ⊗ Sticky forever-fail when sink is down\n- ⊗ Auto-file public framework-gap issues from pulses\n\n## Exit\n\ndeft-directive-product-signal complete — exiting skill.\n",
|
|
106
|
+
"frontmatter_extra": null
|
|
107
|
+
},
|
|
94
108
|
{
|
|
95
109
|
"id": "deft-directive-gh-arch",
|
|
96
110
|
"description": "Explore a codebase for architectural improvement opportunities -- deepen shallow modules, generate competing interface designs in parallel via sub- agents, and file a refactor RFC as a GitHub Issue. Use when improving architecture, finding refactoring opportunities, or making a codebase more testable and AI-navigable. Requires the GitHub CLI (gh).",
|
|
@@ -206,7 +220,7 @@
|
|
|
206
220
|
],
|
|
207
221
|
"path": "skills/deft-directive-release/SKILL.md",
|
|
208
222
|
"version": "0.1",
|
|
209
|
-
"body": "# Deft Directive Release\n\nStructured 8-phase workflow for cutting a v0.X.Y release of the deft framework. Operationalizes the `task release` / `task release:publish` / `task release:rollback` / `task release:e2e` surface introduced in #716 (safety hardening of #74).\n\nLegend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.\n\n**See also**: [deft-directive-swarm](../deft-directive-swarm/SKILL.md) Phase 6 Step 5 (Slack announcement template re-used by Phase 8 below) | [deft-directive-review-cycle](../deft-directive-review-cycle/SKILL.md) (user-gate pattern) | [deft-directive-refinement](../deft-directive-refinement/SKILL.md) (conversational phased flow).\n\n## Platform Requirements\n\n! GitHub as the SCM platform; the **GitHub CLI (`gh`)** must be installed and authenticated. The full pipeline plus the rehearsal target (`task release:e2e`) all dispatch through `gh`.\n\n## Branch-Protection Policy Guard\n\n! Before any Phase 1 state mutation, run the skill-level branch-policy guard documented in `scripts/policy.py` / `scripts/preflight_branch.py` (#746 / #747). Releases run on the configured base branch (default `master`), so the operator MUST be on the explicit-opt-in side of the policy before the pipeline starts writing files.\n\n**Preferred path — typed direct-commit policy opt-out (#1553).** For a release session on the default branch, prefer the audited typed flag over the emergency env-var bypass:\n\n```\ntask policy:allow-direct-commits -- --confirm\n```\n\nThis writes `plan.policy.allowDirectCommitsToMaster = true` on `xbrief/PROJECT-DEFINITION.xbrief.json` with a capability-cost disclosure. After the release completes (or if the session aborts), restore enforcement **and commit the restore in the same closeout** (#2623):\n\n```\ntask policy:enforce-branches\n# enforce flips the typed flag to false locally — the commit that lands that\n# flip cannot use the typed opt-in anymore. Scope the emergency env bypass to\n# ONLY this closeout commit+push (do NOT export it for the whole session):\nDEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1 git add xbrief/PROJECT-DEFINITION.xbrief.json meta/policy-changes.log\nDEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1 git commit -m \"chore(policy): restore branch protection after vX.Y.Z\"\nDEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1 git push origin HEAD\n```\n\n⊗ Leave `allowDirectCommitsToMaster=true` on origin after publish. ⊗ Run `policy:enforce-branches` and leave the dirty restore under protection ON without committing (forces a follow-up PR — the v0.79.0 / #2619 failure mode).\n\n**Branch-guard probe (either path).** Regardless of which opt-out path you chose, confirm the guard passes before Phase 1 mutates state:\n\n```\nuv run python scripts/preflight_branch.py --project-root . --quiet || exit 1\n```\n\nor invoke `task verify:branch`. This is the canonical surface that surfaces the policy state to the operator before the pipeline starts writing files. The release pipeline's other safety surfaces (the dirty-tree guard, base-branch check, `task ci:local` gate) remain independent of this check.\n\n**Emergency env-var bypass — narrow scope only (#1553).** `DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1` is process-wide: every child process, nested test, and temporary repository spawned from the same shell inherits it. During the v0.43.0 release attempt, wrapping the entire `task release` invocation in this env var let the bypass leak into the Step 5 `task ci:local` preflight, which caused `TestWriteConsumerGitHooks_VendoredCommitBlocked_RealGit` to fail because the vendored test repo allowed a direct `master` commit the test expected the hook to block.\n\n- ! Prefer `task policy:allow-direct-commits -- --confirm` for release sessions instead of exporting `DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1` for the whole shell.\n- ⊗ Wrap `task release`, `task ci:local`, or `task check` in `DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1` -- the env var is inherited by every subprocess and can produce false preflight failures before any release mutation.\n- ? If the env-var path is unavoidable, scope it to a **single** branch-guard probe only (e.g. `DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1 task verify:branch`) and do NOT export it for the release session. The release pipeline itself passes the bypass only in scoped subprocess `env=` for its authorised commit/tag/push mutations (#867); operators MUST NOT mirror that pattern at the shell level.\n\nThe release pipeline's Step 9/10/11 git mutations carry the bypass in subprocess `env=` only (`scripts/release.py::_release_subprocess_env`, #867) so the parent shell stays clean. Operator-side env-var exports defeat that isolation.\n\n## Deterministic Questions Contract\n\n! Every numbered-menu prompt rendered in this skill (Phase 1 version-bump magnitude check, Phase 2 dry-run review `yes`/`back`/`quit`, Phase 5 optional `defer`/`rollback`/`Discuss` (happy path auto-publishes after draft QA)) MUST follow [`../../contracts/deterministic-questions.md`](../../contracts/deterministic-questions.md): the final two numbered options MUST be `Discuss` and `Back`, in that order. Existing `back`/`quit` options remain valid; this contract simply adds `Discuss` as a peer alongside `Back`. The Discuss-pause semantic is documented verbatim in the contract -- implicit resumption is forbidden.\n\n## When to Use\n\n- User says \"release\", \"cut release\", \"v0.X.Y\", \"publish release\", \"ship a release\"\n- The framework's `[Unreleased]` CHANGELOG section is non-empty and the operator wants to cut a tagged release\n- A previous release rehearsal succeeded and the operator is ready for the production cut\n\n## Phase 1 — Pre-flight\n\n! Validate the local + remote state before any irreversible action.\n\n~ **Frozen Go-installer bridge (#1912 / #1972 / #1987):** by default a release tag *above* the frozen line (the `LAST_GO_INSTALLER` constant in `packages/core/src/legacy-bridge/sot.ts`) will NOT rebuild the 6 Go binaries -- the CI `freeze-gate` job in `.github/workflows/release.yml` skips the build (the run stays green; npm still ships from the separate `npm-publish.yml`). If this release must rebuild the Go installer, follow the runbook in [`docs/RELEASING.md`](../../../docs/RELEASING.md) § Frozen Go-installer bridge: roll `LAST_GO_INSTALLER` forward to the cut tag BEFORE tagging (pinning to the exact cut tag both releases the gate AND re-freezes at the new line), then see that section's \"After the release\" step for the re-pin.\n\n1. ! Verify the operator is on the configured base branch (default `master`) and the working tree is clean\n2. ! Confirm the next version number (`X.Y.Z`) with the user. Major / minor / patch decision flows from the `[Unreleased]` content (breaking change → major; new feature → minor; fix-only → patch)\n3. ! Inspect `[Unreleased]` content vs the proposed version bump. If a breaking change appears in `### Changed` / `### Removed` but only a patch is proposed, surface the mismatch and ask the user to choose\n4. ! Verify `task ci:local` passes locally (or `task check` as the graceful-degradation fallback per `tasks/release.yml` line 9-10). The `task release` script will refuse to proceed otherwise -- but Phase 1 catches it earlier\n5. ! Verify `gh auth status` reports authenticated (`task release` will refuse otherwise)\n6. ! **Run `task reconcile:issues -- --apply-lifecycle-fixes` to clear any closed-issue / non-completed-folder xBRIEFs before invoking `task release`** (#734). The release pipeline carries the deterministic gate at Step 3 (`scripts/release.py::check_vbrief_lifecycle_sync`, refuses with `EXIT_VIOLATION` on any Section (c) mismatch), but Phase 1 is the operator's first-line defence -- running the apply-mode flag here is the canonical clean path; `--allow-vbrief-drift` on the pipeline exists only as the explicit-acknowledgment escape hatch (analogous to `--allow-dirty`). The recurrence record is the v0.21.0 cut, which surfaced 13 stranded xBRIEFs (8 cycle-relevant + 5 historical residue) post-publish; the gate now blocks that drift before any irreversible action\n7. ! **Verify the proposed `v<version>` tag is not already in use locally, on origin, or as a published GitHub release** (#784). The release pipeline carries the deterministic gate at Step 4 (`scripts/release.py::check_tag_available`, refuses with `EXIT_VIOLATION` before any state mutation -- CHANGELOG promotion, ROADMAP refresh, build, commit), but Phase 1 is the operator's first-line defence. Quickly probe with `git tag -l v<version>` (local), `git ls-remote --tags origin refs/tags/v<version>` (remote), and `gh release view v<version> --repo <owner>/<repo>` (release-only, where `gh release view` exits 0 only when the release exists). The recurrence record is the v0.22.0 → v0.23.0 release attempt on 2026-05-01: the operator typed `0.22.0` (the prior release from 12 hours earlier) and the legacy pipeline ran 8 steps before failing at `git tag` -- leaving a wrong-version local commit + `dist/deft-0.22.0.zip` orphan + manual `git reset --hard` recovery. The new pre-flight gate blocks that mode before any irreversible action\n8. ! **Verify the npm credential path is configured before cutting the tag** (#1910, #1909). A `v*` tag now auto-triggers `.github/workflows/npm-publish.yml`, which publishes the four `@deftai/directive*` packages with `npm publish --provenance`. Confirm the publish path can authenticate: either the `NPM_TOKEN` repo secret is present (`gh secret list --repo <owner>/<repo>` shows `NPM_TOKEN`) OR an npm OIDC trusted publisher is configured for the `@deftai/directive*` packages. If neither is in place, WARN loudly that the tag will fire a publish job that fails (red X on the tag, no packages) -- the operator may still proceed for a GitHub-only release, but the npm channel will not land until #1909's credential is provisioned. Cross-reference #1909.\n9. ! **Disclose npm irrevocability before any tag push (#1972, #2002).** A `v<version>` tag push is the **real npm publish gate** -- NOT Phase 5 or `task release:publish`. Tag push fires `.github/workflows/npm-publish.yml` in a separate workflow that is NOT draft-gated; npm packages ship immediately and **cannot be retracted** (`npm unpublish` is forbidden). Recovery is forward-only: deprecate, dist-tag, or ship a patch. The operator's explicit `yes` in Phase 2 (dry-run) and the decision to invoke `task release` in Phase 4 are the last human gates before npm goes live. Phase 5 only controls GitHub release visibility (draft → public); it does NOT gate npm.\n10. ~ Ask the operator for an optional one-line release **summary** (recommended 80-160 chars; can be skipped). The summary is the canonical narrative for THIS release across three audiences: (a) injected as a Markdown blockquote at the top of the promoted `CHANGELOG.md [<version>]` section, (b) auto-flowed into the GitHub release body via the existing `_section_for_version` pickup, and (c) populated VERBATIM into the Phase 8 Slack `*Summary*:` slot. Capture the wording once here; do NOT regenerate per-audience downstream\n\n⊗ Skip the version-bump magnitude check -- a patch release that ships breaking changes is the kind of regression that Repair Authority [AXIOM] (#709) is designed to prevent.\n\n⊗ Skip the xBRIEF-lifecycle-sync check (#734); the gate exists because operators consistently forget the manual `task scope:complete` move step. The v0.21.0 cut surfaced 13 stranded xBRIEFs (8 cycle-relevant + 5 historical residue) post-publish as the recurrence record this gate prevents. If `task release` reports `[3/13] Pre-flight xBRIEF lifecycle sync... FAIL (<count> mismatches; run task reconcile:issues -- --apply-lifecycle-fixes to fix)`, the canonical recovery is the apply-mode invocation -- `--allow-vbrief-drift` is reserved for cases where the operator has explicitly reviewed the drift and chosen to defer the lifecycle reconcile to the next refinement pass (e.g. an emergency hot-fix release).\n\n⊗ Skip the tag-availability check (#784); the gate exists because the legacy 12-step pipeline only invoked `git tag` at Step 9, after Steps 1-8 had already mutated state (CHANGELOG promoted, ROADMAP refreshed, dist built, release commit made locally). A duplicate-tag failure at Step 9 stranded the operator with an unpushed wrong-version commit + orphaned `dist/deft-<wrong>.zip` artifact + manual `git reset --hard` recovery (forbidden by AGENTS.md SCM rules without explicit permission). The recurrence record is the v0.22.0 → v0.23.0 release attempt on 2026-05-01. If `task release` reports `[4/13] Pre-flight tag availability... FAIL (<surface> tag v<version> already exists ...)`, the canonical recovery is to choose a different version (the most likely cause is operator typo of a prior release).\n\n⊗ Hand-write a different one-line narrative for each of the three downstream surfaces (CHANGELOG / GitHub release / Slack) -- that drift is exactly the gap the `--summary` flag is designed to close. If the operator insists on per-audience tone, populate the canonical `--summary` ONCE here and document the deviation in the Phase 8 anti-pattern.\n\n## Phase 2 — Dry-run review\n\n! Invoke `task release -- <version> --dry-run --skip-tag --skip-release` and present the plan to the user. If Phase 1 collected an operator summary, also pass `--summary \"<text>\"` so the dry-run preview reflects the canonical narrative the operator just authored.\n\n```\ntask release -- <version> --dry-run --skip-tag --skip-release --summary \"<text>\"\n```\n\nThe dry-run prints `[N/13] <step>... DRYRUN (would <action>)` for every pipeline step (Step 13 is the post-create verify-isDraft gate added by #724; Step 4 is the tag-availability pre-flight gate added by #784). Step 6 (CHANGELOG promotion) surfaces whether a summary was supplied (truncated to ~60 chars in the preview) so the operator can validate the wording before any file is written. Capture the output and present it to the user, then wait for explicit confirmation before continuing.\n\n! Wait for explicit user confirmation: `yes` / `back` / `quit`. Remind the operator that Phase 4's tag push will irrevocably publish npm (#1972) -- this `yes` is the last safe abort before that channel opens.\n- `yes` (or `confirmed` / `approve`) → proceed to Phase 3\n- `back` → return to Phase 1 for re-validation (e.g. user wants to amend the version or `[Unreleased]` content)\n- `quit` → abort the workflow cleanly; no state changes\n\n⊗ Skip the dry-run preview. The dry-run is the operator's last opportunity to catch a bad version number, malformed CHANGELOG, or wrong base branch before the pipeline starts writing files.\n\n## Phase 3 — E2E sanity\n\n! Invoke `task release:e2e` against an auto-created temp repo to verify the full pipeline shape works end-to-end before touching the real repo.\n\n```\ntask release:e2e\n```\n\nThe harness provisions `deftai/deftai-release-test-<ts>-<uuid6>`, runs the smoke-test rehearsal, and **by default keeps** the temp repo (#2572). Stderr always includes the full `owner/slug` and a copy-pasteable manual-cleanup command (`gh repo delete <owner>/<slug> --yes`). Privileged environments (CI or an operator with `delete_repo`) MAY pass `task release:e2e -- --destroy-repo` to attempt auto-delete; destroy failure emits a WARN and does **not** block Phase 4 when the rehearsal succeeded.\n\n! After Phase 3, the agent MUST NOT retry or escalate temp-repo deletion. Include any leftover temp repo(s) in the phase summary for the operator to clean up manually.\n\n! Treat a non-zero exit from `task release:e2e` as a hard refusal to proceed to Phase 4. Surface the diagnostic and ask whether to debug (return to Phase 1) or abort (`quit`).\n\n? **Skip allowed** when the operator has just run `task release:e2e` successfully against the same branch in the past 30 minutes. Note the prior run timestamp in the user-facing summary.\n\n! **`task release:e2e` now also rehearses the npm publish (#1910).** Unless `--skip-npm` is passed (or `npm` is absent from PATH, which soft-skips), the rehearsal runs `npm publish --dry-run --access public` for all four `@deftai/directive*` packages against the throwaway clone in dependency order (types -> core -> content -> cli), after `pnpm install` + `pnpm -w run build` and a version-alignment pass. This catches a broken `files` allowlist, a version-drift bug, or a dependency-order error BEFORE the real `v*` tag fires the publish workflow -- without touching the real registry. The install+build exceeds the <90s fast budget, so pass `task release:e2e -- --skip-npm` when you only need the GitHub-pipeline shape check.\n\n! **Tag -> npm coupling + irrevocability (#1910, #1972, #2002).** A `v<version>` tag is a TWO-channel action: the GitHub release (this skill's pipeline) AND `.github/workflows/npm-publish.yml`, which runs in a SEPARATE workflow that does NOT block the GitHub release and is NOT draft-gated. The npm workflow derives the published version from the tag (`${GITHUB_REF_NAME#v}`); this skill owns the version chosen in Phase 1. These MUST stay consistent -- the tag you cut IS the npm version that ships; there is no separate npm version bump. **npm publish is irrevocable** (#1972): once the tag fires, packages are live on npm and cannot be unpulled; `task release:rollback` does NOT retract npm (forward-only recovery). A red npm job on a green GitHub release means the npm channel did not ship (verify in Phase 5/7).\n\n## Phase 4 — Production draft\n\n! **Last human gate before npm (#1972, #2002).** Immediately before invoking `task release`, re-state that the tag push in this step will irrevocably publish all four `@deftai/directive*` packages to npm via `.github/workflows/npm-publish.yml`. There is no undo on npm; only forward recovery (deprecate / dist-tag / patch). Proceed only when the operator explicitly confirms.\n\n! Invoke `task release -- <version>` (NO `--dry-run`, NO `--skip-tag`, NO `--skip-release`, NO `--skip-ci`). If Phase 1 collected an operator summary, pass `--summary \"<text>\"` so the production cut writes the same blockquote the dry-run previewed.\n\n⊗ Use `--skip-ci` on a production cut except under explicit operator incident review — it skips Step 5 vitest coverage and ships untested npm builds (#2652). When unavoidable, pass `--allow-skip-ci=#N` citing the tracked issue; Step 5 emits a loud WARN. See [`docs/RELEASING.md`](../../../docs/RELEASING.md) § Vitest coverage hang recovery. The next patch after the hang fix must cut without `--skip-ci`.\n\n```\ntask release -- <version> --summary \"<text>\"\n```\n\nPer #716 default-draft hardening, this lands the release as a `--draft` on the real repo. Binaries upload via release.yml CI, but the artifact is NOT yet visible to consumers. The operator-authored summary becomes part of the promoted `CHANGELOG.md [<version>]` section AND the GitHub release body (auto-pickup via `_section_for_version`). The same wording is the canonical source for the Phase 8 Slack `*Summary*:` slot.\n\n! **Maintainer-mode release notes auto-lead with an \"Upgrading from an older version?\" banner (#1413).** When the cut targets the canonical framework repo (`deftai/directive`), `scripts/release.py` Step 12 prepends the banner from the editable template at `.github/release-notes/upgrade-banner.md` to the notes passed to `gh release create` (via `_prepend_upgrade_banner`). The banner points consumers at the canonical `deft-install --yes --upgrade --repo-root . --json` upgrade command and #1411. This is **GitHub-release-body-only** -- it is NEVER injected into `CHANGELOG.md`, so the CHANGELOG section and the release body intentionally differ by this leading block. To change the wording, edit the template file; do not hand-edit the published release body. **Consumer-mode releases (any non-`deftai/directive` repo) are unaffected** -- a downstream project that vendors the release pipeline never inherits deft's upgrade guidance. A missing/unreadable template degrades gracefully (notes ship without the banner; the cut is never blocked).\n\n! **Verify isDraft within 5 seconds; flip immediately if not (#724).** Immediately after `gh release create --draft` returns success, `scripts/release.py` Step 11 polls `gh release view v<version> --json isDraft` up to 5 times at 1-second intervals. If the release exists with `isDraft=false`, the pipeline auto-flips it via `gh release edit v<version> --draft=true` and emits a `WARNING: release landed as public; flipping to draft (defense-in-depth, see #724)` line. This closes the ~90-second public-exposure window observed during the v0.21.0 cut where a manual recovery created a public release before the operator noticed and flipped it. The verify gate is defense in depth even when `--draft` was passed correctly: it catches the case where `gh release create` partially succeeded (release record written, error returned) AND the operator-error variant where an alternate code path sent the release without `--draft`. A release-not-found-within-budget result emits a WARN and does NOT fail the pipeline (release.yml CI may still be processing).\n\n! Wait for `task release` to exit 0 before continuing. A non-zero exit means the pipeline halted partway through; consult Phase 7's `task release:rollback` recovery before retrying.\n\n⊗ Pass `--no-draft` here unless the operator has explicitly opted into direct-publish (e.g. automated security patch). The default-draft contract is the foundation of the safety hardening surface.\n\n⊗ Skip the post-create verify-isDraft gate -- the gate is the only reliable safety net against \"create call exited 0 but the release somehow landed as public\" variants (#724). If `task release` is invoked manually outside the canonical `scripts/release.py` flow, the operator MUST run `gh release view v<version> --json isDraft` followed by `gh release edit --draft=true` on `isDraft=false` BEFORE handing off to Phase 5.\n\n## Phase 5 — GitHub draft QA (optional; NOT the npm authority gate)\n\n! After `task release` exits 0, QA the **GitHub draft release** only. npm packages typically **already shipped** when the tag push in Phase 4 fired `.github/workflows/npm-publish.yml` (#1972, #2002). Phase 5 is NOT a \"user-only authority before going live\" gate for the release as a whole -- it is optional draft QA for GitHub assets, notes, and binaries.\n\n1. ! **Verify npm publish status FIRST (in parallel with draft inspection).** Run `gh run list --workflow=npm-publish.yml --repo <owner>/<repo> --limit 5` and confirm the tag run for `v<version>` is `completed`/`success`. If npm failed, surface immediately -- the GitHub draft QA is secondary to a red npm channel.\n2. ! Run `gh release view v<version> --json url,name,body,assets,isDraft --repo <owner>/<repo>` and present the output to the user\n3. ! Surface the asset list (size + filename) so the user can verify binaries uploaded correctly\n4. ! Surface the auto-generated release notes (or the CHANGELOG section that was promoted into the release body)\n\n### Happy path (default when npm succeeded and draft assets look correct)\n\n! When the npm workflow succeeded AND draft assets/notes pass inspection, **auto-proceed to Phase 6 Publish branch** -- run `task release:publish -- <version>` without a redundant human publish prompt (#2002). npm already shipped at tag push; waiting for a separate `publish` confirmation does not protect the npm channel.\n\n? **Operator override:** if the operator wants to hold the GitHub release in draft (e.g. embargo, last-minute notes edit), they MAY say `defer` before auto-publish runs.\n\n### Exception paths (operator-initiated)\n\n- `rollback` → proceed to Phase 6 (Rollback branch). **Reminder:** rollback unwinds the GitHub release only; npm packages already published at tag push are NOT retracted (#1972).\n- `defer` → halt and exit. Surface the draft URL so the operator can return later with `task release:publish -- <version>` or `task release:rollback -- <version>`.\n\n⊗ Treat Phase 5 as the npm publish-authority gate -- npm ships at tag push (Phase 4), not at `task release:publish`. A human `publish` prompt here is redundant when npm already succeeded and only delays flipping the GitHub draft to public.\n⊗ Skip npm workflow verification in Phase 5 and defer it entirely to post-publish Phase 7 -- npm status MUST be checked before or in parallel with the GitHub publish flip.\n\n## Phase 6 — Publish or rollback\n\n! Branch on the Phase 5 outcome. The happy path auto-enters the Publish branch when npm succeeded and draft QA passed (#2002).\n\n### Publish branch (happy path auto-run, or resumed after `defer`)\n\n```\ntask release:publish -- <version>\n```\n\nThe companion script flips `--draft=false`, then re-reads the release to verify `isDraft == false` actually flipped. State machine:\n- `draft` found → flip to public; verify; exit 0\n- already `published` → exit 0 no-op (idempotent re-runs are safe)\n- `not-found` → exit 1 (cannot publish a missing release)\n- gh-error → exit 1 with diagnostic\n\n! Wait for `task release:publish` to exit 0 before continuing. On the happy path this runs immediately after Phase 5 draft QA without a separate human publish prompt.\n\n### Rollback branch (user said `rollback`)\n\n```\ntask release:rollback -- <version>\n```\n\nThe state-aware unwind detects the post-release state and applies the matching tiered recovery. Time-windowed download-count guard:\n- release age `< 5 min` → threshold = 0 (rollback safe; nobody noticed yet)\n- release age `5-30 min` → threshold = max(`--allow-low-downloads`, 10) (filters bot fetches)\n- release age `> 30 min` → refuse without `--allow-data-loss`\n\nThree escape hatches (escalating warnings):\n- `--allow-low-downloads N` -- accept up to N downloads\n- `--allow-data-loss` -- accept any count (consumer impact)\n- `--force-strict-0` -- require exactly 0 regardless of release age\n\nRace-condition mitigation: `download_count` is double-read with a 5s sleep between reads; rollback only proceeds if both reads agree below threshold.\n\n! When the guard refuses, surface the recommendation to the user: rollback is risky on a released artifact with non-zero downloads. Prefer the **hot-fix path** (cut the next patch with a withdrawal note in `[Unreleased]/Changed` rather than deleting the broken release).\n\n! **`task release:rollback` does NOT retract npm (#1972, #2002).** Rollback unwinds GitHub release state (draft/public, tag, assets) only. npm packages published at tag push remain on the registry irrevocably. Recovery is forward-only: deprecate the bad version, move a dist-tag, or ship a patch release.\n\n## Phase 7 — Post-publish verification\n\n! Only enter Phase 7 if Phase 6 took the Publish branch (rollback branch ends here with the unwind log).\n\n1. ! **Re-verify npm publish landed (#1910, #1909, #2002).** Phase 5 checked workflow status before the GitHub publish flip; Phase 7 confirms registry truth AFTER `task release:publish`. For each of `@deftai/directive-types`, `@deftai/directive-core`, `@deftai/directive-content`, and `@deftai/directive`, run `npm view <pkg>@<version> version` (expect `<version>`) and confirm provenance on the npm page. A green GitHub release with missing npm packages means consumers cannot `npm i -g @deftai/directive@<version>` -- escalate immediately. (Real-registry verification depends on #1909's credential; until then verify workflow-run status and flag credential gaps.)\n2. ! Verify GitHub auto-closed the discrete-task issue(s) referenced via `Closes #N` in the release notes (mirrors `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 2)\n3. ! Run `gh issue view <N> --json state --jq .state` for each closed issue. If any didn't auto-close, manually close with `gh issue close <N> --comment \"Closed by release v<version> (squash auto-close did not trigger)\"` (Layer 1, #167)\n4. ! Verify ROADMAP.md correctness via `task roadmap:render` (the release pipeline already invoked this; Phase 7 is the second-pass sanity check)\n5. ! Verify binaries are downloadable from the public release URL: `gh release view v<version> --json assets --jq '.assets[].url'` and curl one to confirm 200 OK\n6. ! For any umbrella / staying-OPEN issue (`Refs #N`) referenced in the release notes, run the Layer 3 reopen sweep from `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 1: any protected issue that auto-closed MUST be reopened with a comment citing #701\n\n⊗ Skip the post-publish verification. The closing-keyword false-positive (Layer 1 / Layer 2 / Layer 3) and the incremental-renderer-drift (#641, #614) are exactly the kind of issues that surface only AFTER a release is public.\n\n## Phase 8 — Slack announcement\n\n! Generate the canonical Slack release announcement and present it to the user for copy-paste, re-using the template from `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 5.\n\nThe announcement block MUST include:\n\n```\n:rocket: *deft v<version>* -- <release title>\n\n*Summary*: <one-sentence description of the release scope>\n\n*Key Changes*:\n- <bullet per significant change, 3-5 items max>\n\n*Stats*: 1 release | ~<duration> elapsed | <N> commits since v<previous>\n*Release*: <GitHub release URL>\n```\n\n! Populate version from the freshly-published `gh release view v<version>` output. Populate release title from the CHANGELOG section heading (or the GitHub release title). Summarize key changes from the promoted `[Unreleased]` -> `[<version>]` CHANGELOG section (NOT raw commit messages). Populate stats from `git log v<previous>..v<version> --oneline | wc -l`.\n\n! Populate the `*Summary*:` slot VERBATIM from the operator-authored blockquote at the top of the CHANGELOG `[<version>]` section (the line beginning with `> ` immediately after the `## [<version>] - <date>` heading). The Phase 1 prompt + Phase 4 `--summary` flag exist precisely so this populate step is mechanical -- one canonical narrative authored once at Phase 1, propagated through Phase 4 promotion, and copy-pasted here without re-authoring. If the CHANGELOG section has no blockquote (operator skipped the Phase 1 prompt), generate a one-sentence summary from the `### Added` / `### Changed` bullets and surface to the operator that this is a regenerated narrative (NOT canonical) so they can decide whether to amend the CHANGELOG before publishing.\n\n! Present the block as a code-fenced snippet the user can copy directly. Do NOT post to Slack from inside this skill -- the user owns the actual broadcast.\n\n## Skill Completion\n\n! When Phase 8 completes (or when Phase 5 took the `defer` / `quit` path, or when Phase 6 completed the rollback branch), explicitly confirm skill exit:\n\n```\ndeft-directive-release complete -- exiting skill.\nNext: <one-line guidance>\n```\n\nWhere `<one-line guidance>` is one of:\n- \"release v<version> live -- monitor consumer reports for ~24h before cutting v<next>\"\n- \"release v<version> rolled back -- the underlying defect needs a hot-fix in the next CHANGELOG entry\"\n- \"release deferred -- resume by running `task release:publish -- <version>` (GitHub only; npm already shipped at tag push) or `task release:rollback -- <version>` (GitHub unwind only; npm is forward-recovery) when ready\"\n\n⊗ Exit silently without confirming completion or providing next-step guidance.\n\n## Anti-Patterns\n\n- ⊗ Run `task release` without a Phase 2 dry-run preview -- the dry-run is the only safe place to catch a bad version, malformed CHANGELOG, or wrong base branch\n- ⊗ Skip Phase 3 (e2e rehearsal) on the assumption that \"the dry-run is enough\" -- the e2e harness catches gh-CLI auth issues, repo permission gaps, and pipeline-shape regressions that the dry-run cannot detect\n- ⊗ Pass `--no-draft` to `task release` without explicit operator opt-in -- the default-draft contract is the foundation of the safety hardening surface\n- ⊗ Treat Phase 5 as the npm authority gate or require a redundant human `publish` prompt when npm already succeeded -- npm ships at tag push (#1972); Phase 5 is GitHub draft QA only\n- ⊗ Expect `task release:rollback` to retract npm packages -- rollback is GitHub-only; npm recovery is forward-only (deprecate / dist-tag / patch)\n- ⊗ Run `task release:rollback` against a release that has > 30 minutes of consumer-driven downloads without first weighing the hot-fix path -- a withdrawal note in the next patch is almost always less disruptive than deleting a public artifact\n- ⊗ Use `--allow-data-loss` without first reading the script docstring's hot-fix-path recommendation -- the flag is an explicit acknowledgment of consumer impact, not a default\n- ⊗ Skip the Phase 7 Layer 3 reopen sweep -- protected umbrellas can auto-close on a release-merge squash even when the release notes use `Refs #N` only\n- ⊗ Post the Phase 8 Slack announcement directly from this skill -- the user owns the broadcast; the skill only generates the template\n- ⊗ Hardcode `master` as the base branch -- delegate to the configured base branch from `task release --base-branch <branch>`\n- ⊗ Skip the post-create verify-isDraft gate (#724) -- a successful `gh release create` exit code does NOT prove the release actually landed in draft state; the 5-second poll-and-flip gate in `scripts/release.py` Step 11 is the only safety net against operator-error variants and partial-success races, and any manual recovery path that bypasses `scripts/release.py` MUST run `gh release view --json isDraft` followed by `gh release edit --draft=true` on `isDraft=false` before handing off to Phase 5\n- ⊗ Manually rewrite the Phase 8 Slack `*Summary*:` line to deviate from the CHANGELOG `[<version>]` blockquote -- the canonical narrative is authored ONCE at Phase 1 via `--summary` and propagates verbatim across all three audiences (CHANGELOG / GitHub release body / Slack). Per-audience hand-edits create documentation drift that the deterministic `--summary` flow is designed to prevent. If the operator wants Slack-specific tone, fold it into the canonical Phase 1 wording before passing `--summary`, OR amend the CHANGELOG blockquote BEFORE Phase 8 so all three surfaces stay aligned\n- ⊗ Export `DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1` for the entire release session or wrap `task release` / `task ci:local` in it (#1553) -- the env var is process-wide and leaks into nested tests and temporary repos, producing false preflight failures. Prefer `task policy:allow-direct-commits -- --confirm` and restore with `task policy:enforce-branches` after the cut (closeout commit+push may use a **scoped** env prefix on those three git commands only — see Branch-Protection Policy Guard, #2623)\n- ⊗ Pass `--allow-coverage-debt=#N` unquoted on Windows PowerShell (#2621) -- `#` starts a comment and silently drops the issue number. Use `--allow-coverage-debt=N` or `--allow-coverage-debt=\"#N\"`\n- ⊗ Soft-pass coverage debt on consecutive releases when the prior cut already cited debt (#2618 / #2573) -- restore real branch coverage >= 85% instead of reusing the escape hatch\n",
|
|
223
|
+
"body": "# Deft Directive Release\n\nStructured 8-phase workflow for cutting a v0.X.Y release of the deft framework. Operationalizes the `task release` / `task release:publish` / `task release:rollback` / `task release:e2e` surface introduced in #716 (safety hardening of #74).\n\nLegend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.\n\n**See also**: [deft-directive-swarm](../deft-directive-swarm/SKILL.md) Phase 6 Step 5 (Slack announcement template re-used by Phase 8 below) | [deft-directive-review-cycle](../deft-directive-review-cycle/SKILL.md) (user-gate pattern) | [deft-directive-refinement](../deft-directive-refinement/SKILL.md) (conversational phased flow).\n\n## Platform Requirements\n\n! GitHub as the SCM platform; the **GitHub CLI (`gh`)** must be installed and authenticated. The full pipeline plus the rehearsal target (`task release:e2e`) all dispatch through `gh`.\n\n## Branch-Protection Policy Guard\n\n! Before any Phase 1 state mutation, run the skill-level branch-policy guard documented in `scripts/policy.py` / `scripts/preflight_branch.py` (#746 / #747). Releases run on the configured base branch (default `master`), so the operator MUST be on the explicit-opt-in side of the policy before the pipeline starts writing files.\n\n**Preferred path — typed direct-commit policy opt-out (#1553).** For a release session on the default branch, prefer the audited typed flag over the emergency env-var bypass:\n\n```\ntask policy:allow-direct-commits -- --confirm\n```\n\nThis writes `plan.policy.allowDirectCommitsToMaster = true` on `xbrief/PROJECT-DEFINITION.xbrief.json` with a capability-cost disclosure. After the release completes (or if the session aborts), restore enforcement **and commit the restore in the same closeout** (#2623):\n\n```\ntask policy:enforce-branches\n# enforce flips the typed flag to false locally — the commit that lands that\n# flip cannot use the typed opt-in anymore. Scope the emergency env bypass to\n# ONLY this closeout commit+push (do NOT export it for the whole session):\nDEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1 git add xbrief/PROJECT-DEFINITION.xbrief.json meta/policy-changes.log\nDEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1 git commit -m \"chore(policy): restore branch protection after vX.Y.Z\"\nDEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1 git push origin HEAD\n```\n\n⊗ Leave `allowDirectCommitsToMaster=true` on origin after publish. ⊗ Run `policy:enforce-branches` and leave the dirty restore under protection ON without committing (forces a follow-up PR — the v0.79.0 / #2619 failure mode).\n\n**Branch-guard probe (either path).** Regardless of which opt-out path you chose, confirm the guard passes before Phase 1 mutates state:\n\n```\nuv run python scripts/preflight_branch.py --project-root . --quiet || exit 1\n```\n\nor invoke `task verify:branch`. This is the canonical surface that surfaces the policy state to the operator before the pipeline starts writing files. The release pipeline's other safety surfaces (the dirty-tree guard, base-branch check, `task ci:local` gate) remain independent of this check.\n\n**Emergency env-var bypass — narrow scope only (#1553).** `DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1` is process-wide: every child process, nested test, and temporary repository spawned from the same shell inherits it. During the v0.43.0 release attempt, wrapping the entire `task release` invocation in this env var let the bypass leak into the Step 5 `task ci:local` preflight, which caused `TestWriteConsumerGitHooks_VendoredCommitBlocked_RealGit` to fail because the vendored test repo allowed a direct `master` commit the test expected the hook to block.\n\n- ! Prefer `task policy:allow-direct-commits -- --confirm` for release sessions instead of exporting `DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1` for the whole shell.\n- ⊗ Wrap `task release`, `task ci:local`, or `task check` in `DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1` -- the env var is inherited by every subprocess and can produce false preflight failures before any release mutation.\n- ? If the env-var path is unavoidable, scope it to a **single** branch-guard probe only (e.g. `DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1 task verify:branch`) and do NOT export it for the release session. The release pipeline itself passes the bypass only in scoped subprocess `env=` for its authorised commit/tag/push mutations (#867); operators MUST NOT mirror that pattern at the shell level.\n\nThe release pipeline's Step 9/10/11 git mutations carry the bypass in subprocess `env=` only (`scripts/release.py::_release_subprocess_env`, #867) so the parent shell stays clean. Operator-side env-var exports defeat that isolation.\n\n## Deterministic Questions Contract\n\n! Every numbered-menu prompt rendered in this skill (Phase 1 version-bump magnitude check, Phase 2 dry-run review `yes`/`back`/`quit`, Phase 5 optional `defer`/`rollback`/`Discuss` (happy path auto-publishes after draft QA)) MUST follow [`../../contracts/deterministic-questions.md`](../../contracts/deterministic-questions.md): the final two numbered options MUST be `Discuss` and `Back`, in that order. Existing `back`/`quit` options remain valid; this contract simply adds `Discuss` as a peer alongside `Back`. The Discuss-pause semantic is documented verbatim in the contract -- implicit resumption is forbidden.\n\n## When to Use\n\n- User says \"release\", \"cut release\", \"v0.X.Y\", \"publish release\", \"ship a release\"\n- The framework's `[Unreleased]` CHANGELOG section is non-empty and the operator wants to cut a tagged release\n- A previous release rehearsal succeeded and the operator is ready for the production cut\n\n## Phase 1 — Pre-flight\n\n! Validate the local + remote state before any irreversible action.\n\n\n### Parallel prep — #1880 Gap D (#2692)\n\n! Phase 1 long steps (`task reconcile:issues -- --apply-lifecycle-fixes`, cache refresh when ritual-stale, `task ci:local` / `task check`) and Phase 3 `task release:e2e` MUST be backgrounded or subagent-dispatched when the host supports it (Cursor: Task tool `run_in_background: true`), with progress surfaced via DONE/heartbeat — same ownership as review-cycle / merge-ready workers (#1880 Gap D). The operator conversation MUST stay interactive for version magnitude confirmation, `--summary`, and the Phase 2 dry-run `yes`/`back`/`quit` gate while prep runs.\n\n! **Checklist:** Phase 1 prep parallelized — long prep started in background before (or while) collecting version magnitude / summary / npm irrevocability disclosure.\n\n! On Windows PowerShell, do NOT wrap long task output in `Select-Object -Last` (it buffers until the process exits); stream to the terminal or log to a file and read incrementally. See `scm/github.md` § #2646 / Windows encoding guidance for related PS pitfalls.\n\n⊗ Foreground-block the operator chat on reconcile / `ci:local` / `release:e2e` when background dispatch is available (#1880 Gap D / #2692).\n\n~ **Frozen Go-installer bridge (#1912 / #1972 / #1987):** by default a release tag *above* the frozen line (the `LAST_GO_INSTALLER` constant in `packages/core/src/legacy-bridge/sot.ts`) will NOT rebuild the 6 Go binaries -- the CI `freeze-gate` job in `.github/workflows/release.yml` skips the build (the run stays green; npm still ships from the separate `npm-publish.yml`). If this release must rebuild the Go installer, follow the runbook in [`docs/RELEASING.md`](../../../docs/RELEASING.md) § Frozen Go-installer bridge: roll `LAST_GO_INSTALLER` forward to the cut tag BEFORE tagging (pinning to the exact cut tag both releases the gate AND re-freezes at the new line), then see that section's \"After the release\" step for the re-pin.\n\n1. ! Verify the operator is on the configured base branch (default `master`) and the working tree is clean\n2. ! Confirm the next version number (`X.Y.Z`) with the user. Major / minor / patch decision flows from the `[Unreleased]` content (breaking change → major; new feature → minor; fix-only → patch)\n3. ! Inspect `[Unreleased]` content vs the proposed version bump. If a breaking change appears in `### Changed` / `### Removed` but only a patch is proposed, surface the mismatch and ask the user to choose\n4. ! Verify `task ci:local` passes locally (or `task check` as the graceful-degradation fallback per `tasks/release.yml` line 9-10). The `task release` script will refuse to proceed otherwise -- but Phase 1 catches it earlier\n5. ! Verify `gh auth status` reports authenticated (`task release` will refuse otherwise)\n6. ! **Run `task reconcile:issues -- --apply-lifecycle-fixes` to clear any closed-issue / non-completed-folder xBRIEFs before invoking `task release`** (#734). The release pipeline carries the deterministic gate at Step 3 (`scripts/release.py::check_vbrief_lifecycle_sync`, refuses with `EXIT_VIOLATION` on any Section (c) mismatch), but Phase 1 is the operator's first-line defence -- running the apply-mode flag here is the canonical clean path; `--allow-vbrief-drift` on the pipeline exists only as the explicit-acknowledgment escape hatch (analogous to `--allow-dirty`). The recurrence record is the v0.21.0 cut, which surfaced 13 stranded xBRIEFs (8 cycle-relevant + 5 historical residue) post-publish; the gate now blocks that drift before any irreversible action\n7. ! **Verify the proposed `v<version>` tag is not already in use locally, on origin, or as a published GitHub release** (#784). The release pipeline carries the deterministic gate at Step 4 (`scripts/release.py::check_tag_available`, refuses with `EXIT_VIOLATION` before any state mutation -- CHANGELOG promotion, ROADMAP refresh, build, commit), but Phase 1 is the operator's first-line defence. Quickly probe with `git tag -l v<version>` (local), `git ls-remote --tags origin refs/tags/v<version>` (remote), and `gh release view v<version> --repo <owner>/<repo>` (release-only, where `gh release view` exits 0 only when the release exists). The recurrence record is the v0.22.0 → v0.23.0 release attempt on 2026-05-01: the operator typed `0.22.0` (the prior release from 12 hours earlier) and the legacy pipeline ran 8 steps before failing at `git tag` -- leaving a wrong-version local commit + `dist/deft-0.22.0.zip` orphan + manual `git reset --hard` recovery. The new pre-flight gate blocks that mode before any irreversible action\n8. ! **Verify the npm credential path is configured before cutting the tag** (#1910, #1909). A `v*` tag now auto-triggers `.github/workflows/npm-publish.yml`, which publishes the four `@deftai/directive*` packages with `npm publish --provenance`. Confirm the publish path can authenticate: either the `NPM_TOKEN` repo secret is present (`gh secret list --repo <owner>/<repo>` shows `NPM_TOKEN`) OR an npm OIDC trusted publisher is configured for the `@deftai/directive*` packages. If neither is in place, WARN loudly that the tag will fire a publish job that fails (red X on the tag, no packages) -- the operator may still proceed for a GitHub-only release, but the npm channel will not land until #1909's credential is provisioned. Cross-reference #1909.\n9. ! **Disclose npm irrevocability before any tag push (#1972, #2002).** A `v<version>` tag push is the **real npm publish gate** -- NOT Phase 5 or `task release:publish`. Tag push fires `.github/workflows/npm-publish.yml` in a separate workflow that is NOT draft-gated; npm packages ship immediately and **cannot be retracted** (`npm unpublish` is forbidden). Recovery is forward-only: deprecate, dist-tag, or ship a patch. The operator's explicit `yes` in Phase 2 (dry-run) and the decision to invoke `task release` in Phase 4 are the last human gates before npm goes live. Phase 5 only controls GitHub release visibility (draft → public); it does NOT gate npm.\n10. ~ Ask the operator for an optional one-line release **summary** (recommended 80-160 chars; can be skipped). The summary is the canonical narrative for THIS release across three audiences: (a) injected as a Markdown blockquote at the top of the promoted `CHANGELOG.md [<version>]` section, (b) auto-flowed into the GitHub release body via the existing `_section_for_version` pickup, and (c) populated VERBATIM into the Phase 8 Slack `*Summary*:` slot. Capture the wording once here; do NOT regenerate per-audience downstream\n\n⊗ Skip the version-bump magnitude check -- a patch release that ships breaking changes is the kind of regression that Repair Authority [AXIOM] (#709) is designed to prevent.\n\n⊗ Skip the xBRIEF-lifecycle-sync check (#734); the gate exists because operators consistently forget the manual `task scope:complete` move step. The v0.21.0 cut surfaced 13 stranded xBRIEFs (8 cycle-relevant + 5 historical residue) post-publish as the recurrence record this gate prevents. If `task release` reports `[3/13] Pre-flight xBRIEF lifecycle sync... FAIL (<count> mismatches; run task reconcile:issues -- --apply-lifecycle-fixes to fix)`, the canonical recovery is the apply-mode invocation -- `--allow-vbrief-drift` is reserved for cases where the operator has explicitly reviewed the drift and chosen to defer the lifecycle reconcile to the next refinement pass (e.g. an emergency hot-fix release).\n\n⊗ Skip the tag-availability check (#784); the gate exists because the legacy 12-step pipeline only invoked `git tag` at Step 9, after Steps 1-8 had already mutated state (CHANGELOG promoted, ROADMAP refreshed, dist built, release commit made locally). A duplicate-tag failure at Step 9 stranded the operator with an unpushed wrong-version commit + orphaned `dist/deft-<wrong>.zip` artifact + manual `git reset --hard` recovery (forbidden by AGENTS.md SCM rules without explicit permission). The recurrence record is the v0.22.0 → v0.23.0 release attempt on 2026-05-01. If `task release` reports `[4/13] Pre-flight tag availability... FAIL (<surface> tag v<version> already exists ...)`, the canonical recovery is to choose a different version (the most likely cause is operator typo of a prior release).\n\n⊗ Hand-write a different one-line narrative for each of the three downstream surfaces (CHANGELOG / GitHub release / Slack) -- that drift is exactly the gap the `--summary` flag is designed to close. If the operator insists on per-audience tone, populate the canonical `--summary` ONCE here and document the deviation in the Phase 8 anti-pattern.\n\n## Phase 2 — Dry-run review\n\n! Invoke `task release -- <version> --dry-run --skip-tag --skip-release` and present the plan to the user. If Phase 1 collected an operator summary, also pass `--summary \"<text>\"` so the dry-run preview reflects the canonical narrative the operator just authored.\n\n```\ntask release -- <version> --dry-run --skip-tag --skip-release --summary \"<text>\"\n```\n\nThe dry-run prints `[N/13] <step>... DRYRUN (would <action>)` for every pipeline step (Step 13 is the post-create verify-isDraft gate added by #724; Step 4 is the tag-availability pre-flight gate added by #784). Step 6 (CHANGELOG promotion) surfaces whether a summary was supplied (truncated to ~60 chars in the preview) so the operator can validate the wording before any file is written. Capture the output and present it to the user, then wait for explicit confirmation before continuing.\n\n! Wait for explicit user confirmation: `yes` / `back` / `quit`. Remind the operator that Phase 4's tag push will irrevocably publish npm (#1972) -- this `yes` is the last safe abort before that channel opens.\n- `yes` (or `confirmed` / `approve`) → proceed to Phase 3\n- `back` → return to Phase 1 for re-validation (e.g. user wants to amend the version or `[Unreleased]` content)\n- `quit` → abort the workflow cleanly; no state changes\n\n⊗ Skip the dry-run preview. The dry-run is the operator's last opportunity to catch a bad version number, malformed CHANGELOG, or wrong base branch before the pipeline starts writing files.\n\n## Phase 3 — E2E sanity\n\n! Invoke `task release:e2e` against an auto-created temp repo to verify the full pipeline shape works end-to-end before touching the real repo.\n\n! **#1880 Gap D (#2692):** `task release:e2e` is a long-running step — MUST background / subagent-dispatch it when the host supports it so Phase 2 confirmation and other human gates stay interactive. Do not wrap its output in PowerShell `Select-Object -Last`.\n\n```\ntask release:e2e\n```\n\nThe harness provisions `deftai/deftai-release-test-<ts>-<uuid6>`, runs the smoke-test rehearsal, and **by default keeps** the temp repo (#2572). Stderr always includes the full `owner/slug` and a copy-pasteable manual-cleanup command (`gh repo delete <owner>/<slug> --yes`). Privileged environments (CI or an operator with `delete_repo`) MAY pass `task release:e2e -- --destroy-repo` to attempt auto-delete; destroy failure emits a WARN and does **not** block Phase 4 when the rehearsal succeeded.\n\n! After Phase 3, the agent MUST NOT retry or escalate temp-repo deletion. Include any leftover temp repo(s) in the phase summary for the operator to clean up manually.\n\n! Treat a non-zero exit from `task release:e2e` as a hard refusal to proceed to Phase 4. Surface the diagnostic and ask whether to debug (return to Phase 1) or abort (`quit`).\n\n? **Skip allowed** when the operator has just run `task release:e2e` successfully against the same branch in the past 30 minutes. Note the prior run timestamp in the user-facing summary.\n\n! **`task release:e2e` now also rehearses the npm publish (#1910).** Unless `--skip-npm` is passed (or `npm` is absent from PATH, which soft-skips), the rehearsal runs `npm publish --dry-run --access public` for all four `@deftai/directive*` packages against the throwaway clone in dependency order (types -> core -> content -> cli), after `pnpm install` + `pnpm -w run build` and a version-alignment pass. This catches a broken `files` allowlist, a version-drift bug, or a dependency-order error BEFORE the real `v*` tag fires the publish workflow -- without touching the real registry. The install+build exceeds the <90s fast budget, so pass `task release:e2e -- --skip-npm` when you only need the GitHub-pipeline shape check.\n\n! **Tag -> npm coupling + irrevocability (#1910, #1972, #2002).** A `v<version>` tag is a TWO-channel action: the GitHub release (this skill's pipeline) AND `.github/workflows/npm-publish.yml`, which runs in a SEPARATE workflow that does NOT block the GitHub release and is NOT draft-gated. The npm workflow derives the published version from the tag (`${GITHUB_REF_NAME#v}`); this skill owns the version chosen in Phase 1. These MUST stay consistent -- the tag you cut IS the npm version that ships; there is no separate npm version bump. **npm publish is irrevocable** (#1972): once the tag fires, packages are live on npm and cannot be unpulled; `task release:rollback` does NOT retract npm (forward-only recovery). A red npm job on a green GitHub release means the npm channel did not ship (verify in Phase 5/7).\n\n## Phase 4 — Production draft\n\n! **Last human gate before npm (#1972, #2002).** Immediately before invoking `task release`, re-state that the tag push in this step will irrevocably publish all four `@deftai/directive*` packages to npm via `.github/workflows/npm-publish.yml`. There is no undo on npm; only forward recovery (deprecate / dist-tag / patch). Proceed only when the operator explicitly confirms.\n\n! Invoke `task release -- <version>` (NO `--dry-run`, NO `--skip-tag`, NO `--skip-release`, NO `--skip-ci`). If Phase 1 collected an operator summary, pass `--summary \"<text>\"` so the production cut writes the same blockquote the dry-run previewed.\n\n⊗ Use `--skip-ci` on a production cut except under explicit operator incident review — it skips Step 5 vitest coverage and ships untested npm builds (#2652). When unavoidable, pass `--allow-skip-ci=#N` citing the tracked issue; Step 5 emits a loud WARN. See [`docs/RELEASING.md`](../../../docs/RELEASING.md) § Vitest coverage hang recovery. The next patch after the hang fix must cut without `--skip-ci`.\n\n```\ntask release -- <version> --summary \"<text>\"\n```\n\nPer #716 default-draft hardening, this lands the release as a `--draft` on the real repo. Binaries upload via release.yml CI, but the artifact is NOT yet visible to consumers. The operator-authored summary becomes part of the promoted `CHANGELOG.md [<version>]` section AND the GitHub release body (auto-pickup via `_section_for_version`). The same wording is the canonical source for the Phase 8 Slack `*Summary*:` slot.\n\n! **Maintainer-mode release notes auto-lead with an \"Upgrading from an older version?\" banner (#1413).** When the cut targets the canonical framework repo (`deftai/directive`), `scripts/release.py` Step 12 prepends the banner from the editable template at `.github/release-notes/upgrade-banner.md` to the notes passed to `gh release create` (via `_prepend_upgrade_banner`). The banner points consumers at the canonical `deft-install --yes --upgrade --repo-root . --json` upgrade command and #1411. This is **GitHub-release-body-only** -- it is NEVER injected into `CHANGELOG.md`, so the CHANGELOG section and the release body intentionally differ by this leading block. To change the wording, edit the template file; do not hand-edit the published release body. **Consumer-mode releases (any non-`deftai/directive` repo) are unaffected** -- a downstream project that vendors the release pipeline never inherits deft's upgrade guidance. A missing/unreadable template degrades gracefully (notes ship without the banner; the cut is never blocked).\n\n! **Verify isDraft within 5 seconds; flip immediately if not (#724).** Immediately after `gh release create --draft` returns success, `scripts/release.py` Step 11 polls `gh release view v<version> --json isDraft` up to 5 times at 1-second intervals. If the release exists with `isDraft=false`, the pipeline auto-flips it via `gh release edit v<version> --draft=true` and emits a `WARNING: release landed as public; flipping to draft (defense-in-depth, see #724)` line. This closes the ~90-second public-exposure window observed during the v0.21.0 cut where a manual recovery created a public release before the operator noticed and flipped it. The verify gate is defense in depth even when `--draft` was passed correctly: it catches the case where `gh release create` partially succeeded (release record written, error returned) AND the operator-error variant where an alternate code path sent the release without `--draft`. A release-not-found-within-budget result emits a WARN and does NOT fail the pipeline (release.yml CI may still be processing).\n\n! Wait for `task release` to exit 0 before continuing. A non-zero exit means the pipeline halted partway through; consult Phase 7's `task release:rollback` recovery before retrying.\n\n⊗ Pass `--no-draft` here unless the operator has explicitly opted into direct-publish (e.g. automated security patch). The default-draft contract is the foundation of the safety hardening surface.\n\n⊗ Skip the post-create verify-isDraft gate -- the gate is the only reliable safety net against \"create call exited 0 but the release somehow landed as public\" variants (#724). If `task release` is invoked manually outside the canonical `scripts/release.py` flow, the operator MUST run `gh release view v<version> --json isDraft` followed by `gh release edit --draft=true` on `isDraft=false` BEFORE handing off to Phase 5.\n\n## Phase 5 — GitHub draft QA (optional; NOT the npm authority gate)\n\n! After `task release` exits 0, QA the **GitHub draft release** only. npm packages typically **already shipped** when the tag push in Phase 4 fired `.github/workflows/npm-publish.yml` (#1972, #2002). Phase 5 is NOT a \"user-only authority before going live\" gate for the release as a whole -- it is optional draft QA for GitHub assets, notes, and binaries.\n\n1. ! **Verify npm publish status FIRST (in parallel with draft inspection).** Run `gh run list --workflow=npm-publish.yml --repo <owner>/<repo> --limit 5` and confirm the tag run for `v<version>` is `completed`/`success`. If npm failed, surface immediately -- the GitHub draft QA is secondary to a red npm channel.\n2. ! Run `gh release view v<version> --json url,name,body,assets,isDraft --repo <owner>/<repo>` and present the output to the user\n3. ! Surface the asset list (size + filename) so the user can verify binaries uploaded correctly\n4. ! Surface the auto-generated release notes (or the CHANGELOG section that was promoted into the release body)\n\n### Happy path (default when npm succeeded and draft assets look correct)\n\n! When the npm workflow succeeded AND draft assets/notes pass inspection, **auto-proceed to Phase 6 Publish branch** -- run `task release:publish -- <version>` without a redundant human publish prompt (#2002). npm already shipped at tag push; waiting for a separate `publish` confirmation does not protect the npm channel.\n\n? **Operator override:** if the operator wants to hold the GitHub release in draft (e.g. embargo, last-minute notes edit), they MAY say `defer` before auto-publish runs.\n\n### Exception paths (operator-initiated)\n\n- `rollback` → proceed to Phase 6 (Rollback branch). **Reminder:** rollback unwinds the GitHub release only; npm packages already published at tag push are NOT retracted (#1972).\n- `defer` → halt and exit. Surface the draft URL so the operator can return later with `task release:publish -- <version>` or `task release:rollback -- <version>`.\n\n⊗ Treat Phase 5 as the npm publish-authority gate -- npm ships at tag push (Phase 4), not at `task release:publish`. A human `publish` prompt here is redundant when npm already succeeded and only delays flipping the GitHub draft to public.\n⊗ Skip npm workflow verification in Phase 5 and defer it entirely to post-publish Phase 7 -- npm status MUST be checked before or in parallel with the GitHub publish flip.\n\n## Phase 6 — Publish or rollback\n\n! Branch on the Phase 5 outcome. The happy path auto-enters the Publish branch when npm succeeded and draft QA passed (#2002).\n\n### Publish branch (happy path auto-run, or resumed after `defer`)\n\n```\ntask release:publish -- <version>\n```\n\nThe companion script flips `--draft=false`, then re-reads the release to verify `isDraft == false` actually flipped. State machine:\n- `draft` found → flip to public; verify; exit 0\n- already `published` → exit 0 no-op (idempotent re-runs are safe)\n- `not-found` → exit 1 (cannot publish a missing release)\n- gh-error → exit 1 with diagnostic\n\n! Wait for `task release:publish` to exit 0 before continuing. On the happy path this runs immediately after Phase 5 draft QA without a separate human publish prompt.\n\n### Rollback branch (user said `rollback`)\n\n```\ntask release:rollback -- <version>\n```\n\nThe state-aware unwind detects the post-release state and applies the matching tiered recovery. Time-windowed download-count guard:\n- release age `< 5 min` → threshold = 0 (rollback safe; nobody noticed yet)\n- release age `5-30 min` → threshold = max(`--allow-low-downloads`, 10) (filters bot fetches)\n- release age `> 30 min` → refuse without `--allow-data-loss`\n\nThree escape hatches (escalating warnings):\n- `--allow-low-downloads N` -- accept up to N downloads\n- `--allow-data-loss` -- accept any count (consumer impact)\n- `--force-strict-0` -- require exactly 0 regardless of release age\n\nRace-condition mitigation: `download_count` is double-read with a 5s sleep between reads; rollback only proceeds if both reads agree below threshold.\n\n! When the guard refuses, surface the recommendation to the user: rollback is risky on a released artifact with non-zero downloads. Prefer the **hot-fix path** (cut the next patch with a withdrawal note in `[Unreleased]/Changed` rather than deleting the broken release).\n\n! **`task release:rollback` does NOT retract npm (#1972, #2002).** Rollback unwinds GitHub release state (draft/public, tag, assets) only. npm packages published at tag push remain on the registry irrevocably. Recovery is forward-only: deprecate the bad version, move a dist-tag, or ship a patch release.\n\n## Phase 7 — Post-publish verification\n\n! Only enter Phase 7 if Phase 6 took the Publish branch (rollback branch ends here with the unwind log).\n\n1. ! **Re-verify npm publish landed (#1910, #1909, #2002).** Phase 5 checked workflow status before the GitHub publish flip; Phase 7 confirms registry truth AFTER `task release:publish`. For each of `@deftai/directive-types`, `@deftai/directive-core`, `@deftai/directive-content`, and `@deftai/directive`, run `npm view <pkg>@<version> version` (expect `<version>`) and confirm provenance on the npm page. A green GitHub release with missing npm packages means consumers cannot `npm i -g @deftai/directive@<version>` -- escalate immediately. (Real-registry verification depends on #1909's credential; until then verify workflow-run status and flag credential gaps.)\n2. ! Verify GitHub auto-closed the discrete-task issue(s) referenced via `Closes #N` in the release notes (mirrors `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 2)\n3. ! Run `gh issue view <N> --json state --jq .state` for each closed issue. If any didn't auto-close, manually close with `gh issue close <N> --comment \"Closed by release v<version> (squash auto-close did not trigger)\"` (Layer 1, #167)\n4. ! Verify ROADMAP.md correctness via `task roadmap:render` (the release pipeline already invoked this; Phase 7 is the second-pass sanity check)\n5. ! Verify binaries are downloadable from the public release URL: `gh release view v<version> --json assets --jq '.assets[].url'` and curl one to confirm 200 OK\n6. ! For any umbrella / staying-OPEN issue (`Refs #N`) referenced in the release notes, run the Layer 3 reopen sweep from `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 1: any protected issue that auto-closed MUST be reopened with a comment citing #701\n\n⊗ Skip the post-publish verification. The closing-keyword false-positive (Layer 1 / Layer 2 / Layer 3) and the incremental-renderer-drift (#641, #614) are exactly the kind of issues that surface only AFTER a release is public.\n\n## Phase 8 — Slack announcement\n\n! Generate the canonical Slack release announcement and present it to the user for copy-paste, re-using the template from `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 5.\n\nThe announcement block MUST include:\n\n```\n:rocket: *deft v<version>* -- <release title>\n\n*Summary*: <one-sentence description of the release scope>\n\n*Key Changes*:\n- <bullet per significant change, 3-5 items max>\n\n*Stats*: 1 release | ~<duration> elapsed | <N> commits since v<previous>\n*Release*: <GitHub release URL>\n```\n\n! Populate version from the freshly-published `gh release view v<version>` output. Populate release title from the CHANGELOG section heading (or the GitHub release title). Summarize key changes from the promoted `[Unreleased]` -> `[<version>]` CHANGELOG section (NOT raw commit messages). Populate stats from `git log v<previous>..v<version> --oneline | wc -l`.\n\n! Populate the `*Summary*:` slot VERBATIM from the operator-authored blockquote at the top of the CHANGELOG `[<version>]` section (the line beginning with `> ` immediately after the `## [<version>] - <date>` heading). The Phase 1 prompt + Phase 4 `--summary` flag exist precisely so this populate step is mechanical -- one canonical narrative authored once at Phase 1, propagated through Phase 4 promotion, and copy-pasted here without re-authoring. If the CHANGELOG section has no blockquote (operator skipped the Phase 1 prompt), generate a one-sentence summary from the `### Added` / `### Changed` bullets and surface to the operator that this is a regenerated narrative (NOT canonical) so they can decide whether to amend the CHANGELOG before publishing.\n\n! Present the block as a code-fenced snippet the user can copy directly. Do NOT post to Slack from inside this skill -- the user owns the actual broadcast.\n\n## Skill Completion\n\n! When Phase 8 completes (or when Phase 5 took the `defer` / `quit` path, or when Phase 6 completed the rollback branch), explicitly confirm skill exit:\n\n```\ndeft-directive-release complete -- exiting skill.\nNext: <one-line guidance>\n```\n\nWhere `<one-line guidance>` is one of:\n- \"release v<version> live -- monitor consumer reports for ~24h before cutting v<next>\"\n- \"release v<version> rolled back -- the underlying defect needs a hot-fix in the next CHANGELOG entry\"\n- \"release deferred -- resume by running `task release:publish -- <version>` (GitHub only; npm already shipped at tag push) or `task release:rollback -- <version>` (GitHub unwind only; npm is forward-recovery) when ready\"\n\n⊗ Exit silently without confirming completion or providing next-step guidance.\n\n## Anti-Patterns\n\n- ⊗ Foreground-block the operator chat on Phase 1 long prep (`reconcile:issues`, cache refresh, `ci:local` / `check`) or Phase 3 `release:e2e` when background / subagent dispatch is available (#1880 Gap D / #2692) — the interactive channel must stay free for version confirmation, `--summary`, and the Phase 2 dry-run gate\n- ⊗ Wrap long release-prep task output in PowerShell `Select-Object -Last` — it buffers until exit and makes the session look hung (#2692)\n- ⊗ Run `task release` without a Phase 2 dry-run preview -- the dry-run is the only safe place to catch a bad version, malformed CHANGELOG, or wrong base branch\n- ⊗ Skip Phase 3 (e2e rehearsal) on the assumption that \"the dry-run is enough\" -- the e2e harness catches gh-CLI auth issues, repo permission gaps, and pipeline-shape regressions that the dry-run cannot detect\n- ⊗ Pass `--no-draft` to `task release` without explicit operator opt-in -- the default-draft contract is the foundation of the safety hardening surface\n- ⊗ Treat Phase 5 as the npm authority gate or require a redundant human `publish` prompt when npm already succeeded -- npm ships at tag push (#1972); Phase 5 is GitHub draft QA only\n- ⊗ Expect `task release:rollback` to retract npm packages -- rollback is GitHub-only; npm recovery is forward-only (deprecate / dist-tag / patch)\n- ⊗ Run `task release:rollback` against a release that has > 30 minutes of consumer-driven downloads without first weighing the hot-fix path -- a withdrawal note in the next patch is almost always less disruptive than deleting a public artifact\n- ⊗ Use `--allow-data-loss` without first reading the script docstring's hot-fix-path recommendation -- the flag is an explicit acknowledgment of consumer impact, not a default\n- ⊗ Skip the Phase 7 Layer 3 reopen sweep -- protected umbrellas can auto-close on a release-merge squash even when the release notes use `Refs #N` only\n- ⊗ Post the Phase 8 Slack announcement directly from this skill -- the user owns the broadcast; the skill only generates the template\n- ⊗ Hardcode `master` as the base branch -- delegate to the configured base branch from `task release --base-branch <branch>`\n- ⊗ Skip the post-create verify-isDraft gate (#724) -- a successful `gh release create` exit code does NOT prove the release actually landed in draft state; the 5-second poll-and-flip gate in `scripts/release.py` Step 11 is the only safety net against operator-error variants and partial-success races, and any manual recovery path that bypasses `scripts/release.py` MUST run `gh release view --json isDraft` followed by `gh release edit --draft=true` on `isDraft=false` before handing off to Phase 5\n- ⊗ Manually rewrite the Phase 8 Slack `*Summary*:` line to deviate from the CHANGELOG `[<version>]` blockquote -- the canonical narrative is authored ONCE at Phase 1 via `--summary` and propagates verbatim across all three audiences (CHANGELOG / GitHub release body / Slack). Per-audience hand-edits create documentation drift that the deterministic `--summary` flow is designed to prevent. If the operator wants Slack-specific tone, fold it into the canonical Phase 1 wording before passing `--summary`, OR amend the CHANGELOG blockquote BEFORE Phase 8 so all three surfaces stay aligned\n- ⊗ Export `DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1` for the entire release session or wrap `task release` / `task ci:local` in it (#1553) -- the env var is process-wide and leaks into nested tests and temporary repos, producing false preflight failures. Prefer `task policy:allow-direct-commits -- --confirm` and restore with `task policy:enforce-branches` after the cut (closeout commit+push may use a **scoped** env prefix on those three git commands only — see Branch-Protection Policy Guard, #2623)\n- ⊗ Pass `--allow-coverage-debt=#N` unquoted on Windows PowerShell (#2621) -- `#` starts a comment and silently drops the issue number. Use `--allow-coverage-debt=N` or `--allow-coverage-debt=\"#N\"`\n- ⊗ Soft-pass coverage debt on consecutive releases when the prior cut already cited debt (#2618 / #2573) -- restore real branch coverage >= 85% instead of reusing the escape hatch\n",
|
|
210
224
|
"frontmatter_extra": null
|
|
211
225
|
},
|
|
212
226
|
{
|
|
@@ -234,7 +248,7 @@
|
|
|
234
248
|
],
|
|
235
249
|
"path": "skills/deft-directive-review-cycle/SKILL.md",
|
|
236
250
|
"version": "0.1",
|
|
237
|
-
"body": "# Deft Directive Review Cycle\n\nStructured workflow for responding to bot reviewer (Greptile) findings on a PR.\n\nLegend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.\n\n## Platform Requirements\n\n! This skill requires **GitHub** as the SCM platform and the **GitHub CLI (`gh`)** to be installed and authenticated. PR comment fetching, check-run queries, and review submission all depend on `gh`.\n\n\n## Ordered-plan exit / advance (#2402)\n\n! When the review-cycle exit condition is met (no P0/P1 remaining / CLEAN verdict):\n\n1. Return control to the caller — do **not** discover adjacent backlog work.\n2. If an ordered-plan sequence is active and this PR matched the current entry, run `task plan-sequence:advance`.\n3. If another entry remains, the caller may continue **only** to that entry (one unit).\n4. If the sequence is exhausted, stop and ask. Do not open another PR from queue intuition or chaining text.\n\n⊗ Exit the review cycle into `task triage:queue` or an unrelated PR without fresh operator approval.\n\n## When to Use\n\n- User says \"review cycle\", \"check reviews\", or \"run review cycle\" on a PR\n- Operator asks to **babysit**, **shepherd**, or **watch** a PR -- including the Cursor product action **babysit-pull-request-in-cloud** (#2261 / #1862 intent-routing class)\n- A bot reviewer (Greptile) has posted findings on an open PR\n- Dispatching a cloud or background agent to monitor and resolve PR review findings until merge-ready\n\n## Cursor global babysit supersession (#2261)\n\nOn repos with `.deft/core/` installed, **PR shepherding intent** routes here -- not the Cursor-global `babysit` skill (`~/.cursor/skills-cursor/babysit/SKILL.md`). The product action **babysit-pull-request-in-cloud** shares the babysit name but MUST chain into this review-cycle surface on Deft-managed projects.\n\n- ! Orchestrators dispatching cloud/background workers for PR shepherding MUST include `templates/agent-prompt-preamble.md` (or reference it) and mandate reading this skill as the first workflow step.\n- ! Cloud babysit workers use `worker_role: review-monitor` (or `drive-to: merge-ready` implementation workers that own the review cycle) -- not a generic babysit loop without Phase 1 audit or Step 6 exit predicate (#1259).\n- ⊗ Route **babysit-pull-request-in-cloud** to Cursor global babysit when `.deft/core/` is present -- that bypasses xBRIEF preflight, `task check`, Greptile fail-closed exit, and CHANGELOG discipline (#2261 recurrence).\n\n## Branch-Protection Policy Guard\n\n! Before entering the review/fix loop, run the skill-level branch-policy guard (#746 / #747). Halt before any state mutation if the project's `plan.policy.allowDirectCommitsToMaster` is unresolvable AND the operator has not set `DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1`. Concretely:\n\n```\ntask verify:branch || exit 1\n```\n\nThe skill MUST NOT modify files, push, or comment on the PR until the guard passes -- this catches the case where a malformed PROJECT-DEFINITION quietly disabled the policy and the agent would have committed directly to master mid-review.\n\n## Deterministic Questions Contract\n\n! Every numbered-menu prompt rendered in this skill (Phase 1 audit gates, Phase 2 Step 4 monitoring approach selection, Phase 5->6 ready-to-merge gate, Step 6 exit-condition prompts) MUST follow [`../../contracts/deterministic-questions.md`](../../contracts/deterministic-questions.md): the final two numbered options MUST be `Discuss` and `Back`, in that order. The Discuss-pause semantic is documented verbatim there -- on `Discuss` selection the agent MUST halt the in-progress sequence immediately, prompt `What would you like to discuss?`, and resume only on an explicit user signal (re-asking the original question, saying `resume`/`continue`, or re-issuing the prior selection). Implicit resumption is forbidden.\n\n## Pre-Flight Check\n\n! Before entering the review/fix loop, verify the Greptile configuration supports it:\n\n1. ! `triggerOnUpdates` must be enabled (via Greptile dashboard or `.greptile/config.json`) — without this, Greptile only reviews the initial PR and never re-reviews after fix pushes, so the loop cannot reach the exit condition\n2. ~ `statusCheck` should be enabled so Greptile posts a `\"Greptile Review\"` check run on each commit — this is the signal the org ruleset uses to gate merges\n3. ? If Greptile does not re-review after a push despite `triggerOnUpdates` being enabled, comment `@greptileai` on the PR as a manual re-trigger fallback\n\n! Greptile posts **check runs** (GitHub Checks API), not **commit statuses** (Statuses API). To verify the check run is present on a commit:\n\n```\ngh api repos/<owner>/<repo>/commits/<sha>/check-runs --jq '.check_runs[] | select(.name == \"Greptile Review\")'\n```\n\n⊗ Use `commits/<sha>/statuses` to check for Greptile — that endpoint will always be empty.\n\n~ See `tools/greptile.md` for recommended dashboard and per-repo settings.\n\n## Phase 1 — Deft Process Audit\n\n! Before touching code, verify ALL prerequisites are satisfied. Fix any gaps first:\n\n1. ! Verify `skills/deft-directive-pre-pr/SKILL.md` was run before PR creation -- the PR branch should have passed at least one full RWLDL cycle. If not, run it now before proceeding.\n2. ! `PROJECT-DEFINITION.xbrief.json` and `xbrief/` lifecycle folders have scope xBRIEF coverage for all changes in the PR\n3. ! `CHANGELOG.md` has entries under `[Unreleased]` for the PR's changes\n4. ! `task check` passes fully (fmt + lint + typecheck + tests + coverage ≥75%)\n5. ! `.github/PULL_REQUEST_TEMPLATE.md` checklist is satisfied in the PR description\n6. ! If the PR touches 3+ files: verify a `/deft:change` `proposal.xbrief.json` exists in `history/changes/` for this branch and was explicitly confirmed by the user (affirmative response, not a broad 'proceed'), or document N/A with reason in the PR checklist\n7. ! Verify the PR is on a feature branch -- work MUST NOT have been committed directly to the default branch (master/main)\n\n~ **PR scope gate:** If the PR spans 3+ unrelated surfaces (e.g. a skill, a tool doc, and a strategy -- with no shared issue or scope xBRIEF linking them), warn the user that broad PRs increase review churn and Greptile noise. Recommend splitting into focused PRs unless all changes trace to the same scope xBRIEF or issue bundle.\n\n! Phase 1 audit gaps must be resolved before merging — but hold the fixes (do NOT commit or push them independently). Proceed to Phase 2 analysis to gather bot findings, then batch all Phase 1 + Phase 2 fixes into a single commit.\n⊗ Commit or push Phase 1 audit fixes independently before gathering Phase 2 findings.\n\n## Phase 2 — Review/Fix Loop\n\n### Step 1: Fetch ALL bot comments\n\n! Retrieve findings using BOTH methods — each catches different comment categories:\n\n```\ngh pr view <number> --comments\n```\n\n! Use `do_not_summarize_output: true` — summarizers silently drop the \"Comments Outside Diff\" section from large bot comments.\n\n~ **Oversized output fallback:** If `do_not_summarize_output: true` produces output too large to process, extract the relevant section with:\n\n- **PowerShell (Windows):** `gh pr view <number> --comments | Select-String \"Outside Diff\" -Context 50`\n- **Unix/macOS:** `gh pr view <number> --comments | grep -A 50 \"Outside Diff\"`\n\nBoth commands extract the \"Comments Outside Diff\" section with surrounding context, avoiding the need to process the full output.\n\n~ **Windows + Grok Build (#1353):** Avoid `|`, `>`, or `2>&1` in `run_terminal_command` strings -- use Python `pathlib`/`subprocess` or plain task commands instead.\n\n! **MCP capability probe** (mirrors deft-directive-swarm Phase 3 pattern): Before attempting MCP `get_review_comments`, probe whether MCP GitHub tools are available in the current session. Detection: attempt a lightweight MCP call (e.g. list available tools or a no-op query) -- if it succeeds, MCP is available; if it errors or the tool is not in the available set, MCP is unavailable.\n\n- **MCP available**: ! Use MCP `get_review_comments` as the second source to catch Comments Outside Diff.\n- **MCP unavailable** (e.g. non-MCP agents including `start_agent` / `spawn_subagent` (\"grok-build\") dispatch, cloud agents, `oz agent run`): ! Use `gh api repos/<owner>/<repo>/pulls/<number>/comments` as the explicit fallback for the second review source. Document in the commit message or PR comment why MCP was skipped (e.g. \"MCP unavailable in this session -- used gh api fallback for review comments\"). The platform descriptor from runtime detection determines MCP availability independently of the dispatch primitive.\n\n⊗ Report \"all comments resolved\" without verifying both sources.\n⊗ Skip the second review source without probing for MCP capability and documenting the fallback used.\n\n~ **Late-arriving bot review re-check:** If the initial dual-source fetch returns no bot review on the current HEAD SHA, wait ~60s and re-fetch before evaluating the Step 6 exit condition. Bot reviewers (Greptile) typically land within 3-7 min of PR creation/push; an empty first pass is more likely \"review pending\" than \"review clean\".\n\n⊗ Declare the exit condition met based on a single fetch that returned no bot review — re-fetch at least once after a ~60s delay first.\n\n~ This codifies a user-rule precedent on late-arriving bot reviews into the deft-internal deterministic tier. The [`templates/swarm-greptile-poller-prompt.md`](../../templates/swarm-greptile-poller-prompt.md) loop body already handles the same case for push-driven cycles via its per-poll fetch -- the rule above closes the orthogonal cold-start path where the one-shot review-cycle entry runs on a freshly-opened PR before any fix push has triggered the Step 4 polling loop.\n\n### Step 2: Analyze ALL findings before changing anything\n\n! Before making any changes:\n\n- Read every finding across all files\n- Identify cross-file dependencies (a term, value, or field mentioned in multiple files)\n- Categorize by severity (P0, P1, P2 — where P0 is critical/blocking, P1 is a real defect, P2 is a style or non-blocking suggestion)\n- Plan a single coherent batch of fixes\n\n⊗ Start fixing individual findings as you encounter them.\n\n### Step 3: Fix all findings in ONE batch commit\n\n! Apply ALL fixes across all files before committing:\n\n- ! For any fix that touches a value, term, or field appearing in multiple files: grep for it across the full PR file set and update every occurrence in the same commit\n- ! Validate structured data files locally before committing (e.g. `python3 -m json.tool` for JSON, YAML lint for YAML) — do not rely on the bot to catch syntax errors\n- ! Before committing any Greptile fix, re-read the FULL current Greptile review and confirm all P0/P1 issues are addressed in the staged changes — this is the pre-commit gate that prevents per-finding fix commits\n- ! Run `task check` before committing\n- ! **Fail-loud completion claim (#1006)**: when reporting fix-batch completion (to the user, in the commit message, in a PR comment, or in a status message to a parent agent), MUST surface the OUTCOMES not the intent -- name the P0/P1 finding count addressed (\"addressed 3/3 P0 findings, 2/2 P1 findings, 0 deferred\" -- NOT \"all findings addressed\"), report the `task check` result with the test-collection counts (\"task check: 412 collected, 412 passed, 0 skipped, 0 xfailed\" -- NOT \"task check passed\"), and explicitly call out any finding intentionally deferred with the reason. Apply `coding/coding.md` `## Fail Loud: Completion Claims Require Outcome Verification (#1006)` to every claim emitted during the review cycle\n- ⊗ Claim \"all Greptile findings addressed\" without verifying that the staged fix actually closes every P0/P1 currently surfaced in the review body -- a fix that addresses 3 out of 4 P1 findings and reports completion is the exact failure mode #1006 forbids\n- ⊗ Claim \"task check passes\" when any test was skipped, xfailed, or run with errors suppressed -- report the full collection counts instead (#1006)\n- ? **Pre-existing failure carve-out**: If `task check` fails due to a pre-existing issue unrelated to the PR's changes, a partial test suite run is acceptable ONLY if BOTH conditions are met: (a) the `task check` failure is pre-existing with an open GitHub issue number tracking it, AND (b) the PR description explicitly notes the failure and includes the issue reference (e.g. \"task check: test_foo fails due to #NNN (pre-existing)\"). Without both conditions, the full `task check` pass remains mandatory.\n- ~ Commit message: `fix: address Greptile review findings (batch)`\n\n⊗ Push individual fix commits per finding — always batch.\n\n### Step 3b: Proactive test coverage scan\n\n! After committing the fix batch but before pushing, scan the changed lines for untested code paths:\n\n1. ! Run `git --no-pager diff HEAD~1 HEAD --name-only` to identify files touched in the fix batch\n2. ! For each changed file that has a corresponding test file, review whether the fix introduced or modified logic that lacks test coverage\n3. ! If untested code paths are found, write tests and amend them into the fix batch commit (or add as a second commit in the same push)\n4. ! Run `task check` again after adding tests to verify they pass\n\n~ This eliminates one CI round-trip per fix cycle — catching coverage gaps before CI does.\n\n⊗ Push fix commits without scanning for untested code paths in changed files.\n\n### Step 4: Push and wait\n\n! Push the batch commit, then wait for the bot to review the latest commit.\n\n! After pushing, the agent MUST autonomously poll for review updates and continue the review cycle without stopping to ask the user. Do not pause for confirmation, do not ask \"should I continue?\", do not wait for user input between push and review completion. The review/fix loop is designed to run to the exit condition without human intervention.\n\n⊗ Push any additional commits — including unrelated fixes, doc updates, or lessons — while waiting for the bot to finish reviewing the current head. Every push re-triggers Greptile and resets the review clock. If you discover additional work while waiting, stage it locally but do NOT push until the current review completes.\n\n\n\n### Greptile CLEAN vs CI holdout (`pr:watch` / #2688)\n\n! When waiting on a Greptile verdict for a `drive-to: merge-ready` worker (or any review-cycle owner), prefer `task pr:watch -- <N>` (or `--one-shot --json`) over ad-hoc sleep loops (#1056). Parse `clean_gate_holdout` on every probe.\n\n! When `clean_gate_holdout=ci_failures` and Greptile otherwise satisfies the probe-side Step 6 fields (SHA match on HEAD, confidence > 3, no P0/P1, not errored): **MUST NOT** idle-poll hoping CI heals. Treat Greptile CLEAN + CI red with the **same ownership** as a Greptile P0 for a merge-ready worker — one fix batch, re-push, re-probe.\n\n! On persistent `ci_failures` holdout: exit the Greptile wait immediately, fetch failing check annotations (`gh pr checks <N>`, CodeQL / required-check details, or `ci_failed_checks` from `pr:watch --json`), fix or escalate with evidence, then re-enter the review loop after CI is green.\n\n! PR body \"Test plan\" checkboxes being `[x]` do **not** authorize idle wait — merge-ready is `pr:watch` CLEAN / the merge path only.\n\n⊗ Treat `pr:watch` TIMEOUT or long PENDING with `clean_gate_holdout=ci_failures` as \"Greptile still reviewing\" — it means **CI blocked the clean gate**.\n\n~ Surface the holdout to the user/parent on the first stable `ci_failures` probe (fail-loud), not after burning `max-wait-minutes`. See also [`templates/swarm-greptile-poller-prompt.md`](../../templates/swarm-greptile-poller-prompt.md) CLEAN gate evaluation (#1039).\n\n### Stall Detection Rubric (#564)\n\n! Track per poll: `startedAt` (timestamp of the first observation of the IN_PROGRESS check run for the current commit) and `commit.oid` (head SHA being reviewed). Both fields MUST be re-recorded every time the head SHA changes -- the rubric measures elapsed time on a single commit, not across the whole review cycle.\n\n! Expected duration baseline -- Greptile reviews typically complete in 2-5 minutes, with 7 minutes as the upper bound of normal. The escalation threshold is **3x expected = ~10 minutes** of continuous IN_PROGRESS on the same `commit.oid`. The 21-minute stall observed during the rc4 swarm cascade on PR #561 is the recurrence record; see [`../../meta/lessons.md`](../../../meta/lessons.md) `## Greptile Review Stall Detection (2026-04)`.\n\n! When elapsed time on the current `commit.oid` exceeds 10 minutes (3x expected) without the IN_PROGRESS check transitioning to a terminal state, the agent MUST escalate to the user. The escalation message MUST include: (1) the PR number; (2) the head SHA being reviewed; (3) the elapsed time since `startedAt`; (4) the four canonical user-decision options.\n\n! User-decision options at escalation -- render as a deterministic numbered menu per [`../../contracts/deterministic-questions.md`](../../contracts/deterministic-questions.md) (final two options `Discuss` + `Back`):\n\n 1. Wait another N minutes (user picks N).\n 2. Manually re-trigger Greptile by commenting `@greptileai` on the PR (logs the override in a PR comment for auditability per the next rule).\n 3. Skip the bot review for this cycle and exit the loop with a documented reason.\n 4. Cancel the review cycle entirely.\n 5. Discuss.\n 6. Back.\n\n! Auto-restart detection -- when the polling loop observes a NEW `startedAt` (Greptile dropped its prior check run and started a fresh one without any push from the agent, e.g. service-side restart), the agent MUST reset its elapsed-time clock to the new `startedAt` AND notify the user that an auto-restart was detected. Resetting the clock without notifying is forbidden -- the user needs to know the cycle effectively re-started.\n\n! **`INCOMPLETE_BUT_RATED` stall signature (#1259):** when a poll observes a parsed `Confidence Score: X/5` number BUT no terminal check-run (no `completed` status with a `{success, neutral}` conclusion) AND/OR no HEAD-matching `Last reviewed commit:` completion marker, classify the state as **`INCOMPLETE_BUT_RATED`** — Greptile has emitted a confidence rating against a review that has NOT terminally landed on the current HEAD. This is NOT an exit condition (the Step 6 fail-closed all-of resolves the missing fields to `unknown`); treat it as a stall signature and keep polling, escalating per the 10-minute threshold above if it persists. A confidence number is the single most common false-positive for a premature exit — `INCOMPLETE_BUT_RATED` names the trap so the agent does not mistake a rating for a verdict.\n\n⊗ Auto-retrigger Greptile (empty commits, force-pushes, agent-posted `@greptileai` comments, status-check rebuilds) without explicit user approval. The escalation menu's option 2 is the ONLY supported re-trigger path, and even that requires the user to pick it.\n\n! Document any user-approved override in a brief PR comment for auditability -- e.g. `Note: review-cycle stall detected at <SHA> after <N> min; user approved manual re-trigger via @greptileai per skills/deft-directive-review-cycle Stall Detection Rubric (#564).` This makes the override visible to humans reviewing the PR history and to future agents that resume the cycle.\n\n⊗ Treat a stall as silent -- if the elapsed clock crosses the 10-minute threshold the agent MUST surface the menu, even if the agent is mid-poll. Continuing to poll past the threshold without user input is forbidden.\n\n### Review Monitoring\n! **Background / independent dispatch (#1880 Gap D):** Long-running review-cycle owners and pollers (>~3 min) MUST be dispatched independently / in the background so the parent conversation stays interactive. On Cursor, use the Task tool background path (`run_in_background: true`). This generalizes the Approach-1 sub-agent monitor rule to implementation and fix workers as well — foreground dispatch is reserved for short tasks. The parent receives completion via `DONE` / `BLOCKED` / `FAILED` per `templates/agent-prompt-preamble.md` §11.\n\n! **Worker-owns-lifecycle (#1880 Gap C):** When this skill runs as part of an implementation worker scoped `drive-to: merge-ready`, the worker owns the full review/fix loop through merge-ready in its own tool loop — do NOT exit at PR-open expecting the orchestrator to spawn a separate review leaf.\n\n\n\n! Select the monitoring approach based on runtime capability detection (the matrix in `skills/deft-directive-swarm/SKILL.md` Phase 3 Step 1, extended per #1342 slices 1-2 for `spawn_subagent` / \"grok-build\" and per #1877 for Cursor as first-class Tier-1 tiers). Probe the environment (tool set + env vars) to obtain the stable platform descriptor (`grok-build`, `warp-orchestrated`, `warp-manual`, `cursor-composer`, `cursor-cloud-agent`, etc.) from the launch adapter / `get_platform_capabilities` and map the descriptor to the appropriate tier + dispatch primitive (`start_agent`, `spawn_subagent`, or the Cursor `Task` tool). The descriptor (not hard-coded tool presence) is the single source of truth for both launch and review monitoring.\n\n- **Tier 1 (orchestrated sub-agent)** → Approach 1 (spawn review-monitor sub-agent via the primitive matching the descriptor: `start_agent`, `spawn_subagent`, or the Cursor `Task` tool with `run_in_background: true`)\n- **Tier 2 (no sub-agent primitive, but scheduler/timer/auto-reinvocation)** → Approach 2 (yield-between-polls)\n- **Tier 3 (interactive session, nothing else)** → Approach 3 (blocking sleep loop as last resort)\n\n! Detection: use the full runtime capability matrix (swarm Phase 3 + launch adapter from #1342 slice 2). The old single-probe for `start_agent` is superseded; the returned platform descriptor determines both the orchestration path and the MCP surface (see MCP probe below). If the descriptor is `grok-build` (spawn_subagent present, start_agent + WARP_* absent), treat as Tier 1 with the spawn_subagent poller path. If the descriptor is `cursor-composer` / `cursor-cloud-agent` (Cursor `Task` tool present, start_agent + WARP_* + spawn_subagent absent), treat as **Tier 1 with the backgrounded Cursor `Task` poller path** (#1877) — NOT Tier 3. Cursor's `Task` tool is a first-class sub-agent primitive; degrading a Cursor session to the Approach-3 blocking poll is the misclassification #1877 closes.\n\n! Swarm agents (whether launched via `start_agent` or `spawn_subagent` per the platform descriptor) SHOULD prefer Approach 1 for their own review-monitor sub-agent. Approach 2's yield-between-polls is not self-sustaining for swarm agents (see warning below). Always include the canonical `templates/agent-prompt-preamble.md` (AGENTS.md read mandate, #810 xBRIEF gate, #798 PowerShell UTF-8, pre-PR + review-cycle mandates) when spawning a poller sub-agent.\n\n! **Deterministic review-monitor gate (#2655):** When Tier 1 is available, run `task verify:review-monitor -- --pr <N> [--call-site solo]` before yielding, entering Approach 3, or claiming review monitoring started. After spawning Approach 1, register with `task review-monitor:register -- --pr <N> --monitor-agent-id <id> --platform-primitive start_agent|spawn_subagent|cursor-task`. Exit `0` ready / `1` not ready / `2` config. Approach 3 on Tier 1 is a gate failure — use `--approach3 --approach3-warned` only on Tier 3 after the user warning.\n\n! **CI-holdout carve-out (#2688):** When `task pr:watch --one-shot --json` reports `clean_gate_holdout=ci_failures` with Greptile fields otherwise satisfied on current HEAD, do **not** freeze on `verify:review-monitor` / spawn-monitor as if Greptile latency were the blocker. Fix CI first (same ownership as Greptile P0). Keep or register a review-monitor only while still waiting on Greptile latency; a `BLOCKED: ci_failures` DONE handback to the implementation owner is correct.\n\n\n**Approach 1 (preferred -- sub-agent orchestration available per platform descriptor):**\n\n! **Background dispatch (#1880):** Spawn the review-monitor sub-agent via the matching primitive IN THE BACKGROUND (Cursor: Task `run_in_background: true`; Grok Build: `spawn_subagent` with parent yielding). The parent MUST remain interactive while the poller runs.\n\n! **Heartbeat contract for Cursor pollers (#1877 / #1166):** A Cursor `Task` review-monitor poller whose loop runs > ~3 min MUST honour the sub-agent heartbeat contract (`docs/subagent-heartbeat.md`), same as the `spawn_subagent` path — emit periodic progress so the parent can distinguish a live poller from a hung one.\n\n! When the platform descriptor indicates Tier 1 (sub-agent support), spawn a review-monitor sub-agent using the primitive matching the descriptor:\n\n1. ! Launch via the matching primitive: `start_agent` (Warp), `spawn_subagent` (grok-build / TUI / non-Warp), **or the Cursor `Task` tool with `run_in_background: true` (`cursor-composer` / `cursor-cloud-agent`, #1877)** with a prompt that instructs it to poll for Greptile completion. For `spawn_subagent` and the Cursor `Task` tool the prompt MUST reference the canonical poller template `templates/swarm-greptile-poller-prompt.md` (with placeholders filled) plus the agent preamble; the working directory / context must be the PR branch (worktree or equivalent for hybrid).\n2. ! The sub-agent polls using the mechanism for its primitive: for `spawn_subagent` use `get_command_or_subagent_output` (adaptive cadence: ~20-30s first check after push, ~60s second, ~90s thereafter; Greptile typically lands in 3-7 min); for `start_agent` the native messaging path; for the Cursor `Task` tool the backgrounded-task completion-notification path. Front-load the first check to catch fast reviews.\n3. ! When the exit condition is met (Greptile review current on the HEAD commit SHA, confidence > 3, no P0/P1 remaining), the sub-agent reports completion back to the parent (via `send_message_to_agent` or the spawn_subagent result channel).\n4. ! The main conversation pane stays fully interactive during monitoring -- the user (or parent monitor) can continue other work.\n5. ! On receiving the completion message / result, the parent re-fetches findings (both gh pr view --comments and the secondary source) and proceeds to Step 5.\n\n**Approach 2 (fallback -- no sub-agent primitive for the descriptor):**\n\n! When the platform descriptor indicates no sub-agent orchestration (or the primitive is unavailable), use discrete tool calls with a yield between checks. For `grok-build` / spawn_subagent descriptor this path is normally avoided in favor of Approach 1; it exists for pure interactive or limited runtimes.\n\n1. ! Use the current shell execution tool (`run_terminal_command` or equivalent in the runtime) in wait mode to run `gh pr view <number> --comments` and `gh pr checks <number>`.\n2. ! After each check, yield control (end all tool calls) -- the agent runtime will re-invoke after its interval or on next interaction.\n3. ! Target adaptive cadence (20-30s / 60s / 90s) where the runtime permits. The full cadence is easiest in Approach 1 (sub-agent) or 3 (blocking); pure yield is runtime-controlled.\n4. ! No blocking shell pane lock -- the conversation remains interactive between checks.\n5. ~ Approach 2 requires a periodic re-invocation trigger (timer, scheduler, user nudge, or external orchestrator for hybrid/worktree cases). Without it the poller stops after the first yield.\n6. ! When the exit condition is met, proceed to Step 5.\n\n⚠️ **Swarm / hybrid limitation**: Approach 2 is NOT autonomous for swarm agents or manual worktree setups. Yielding ends the turn with no self-wake; the parent monitor (or external scheduler) must detect idle and re-trigger or send a message. For true `grok-build` / spawn_subagent hybrids, prefer Approach 1 (spawn_subagent + get_command_or_subagent_output poller) exactly as the swarm launch adapter does.\n\n⊗ Use blocking `Start-Sleep` shell loops or `time.sleep()` loops EXCEPT as Approach 3 (see below) -- these lock the conversation and prevent user interaction.\n⊗ Poll more frequently than every 20 seconds -- use a real delay between checks, not back-to-back calls. Adaptive cadence (20-30s / 60s / 90s) replaces the fixed 60s minimum.\n\n**Approach 3 (last resort -- interactive session, no `start_agent`, no timer/scheduler):**\n\n! Approach 3 is a blocking sleep-poll loop used ONLY when both Approach 1 and Approach 2 are unavailable (interactive session with no `start_agent` and no auto-reinvocation mechanism). Uses PowerShell `sleep` / Unix `sleep` commands between polls.\n\n! **User warning gate:** Before activating Approach 3, the agent MUST warn the user that the conversation pane will be locked during polling and ask for explicit confirmation. Example: \"No sub-agent or auto-reinvocation available. I will poll in a blocking loop (~20-30s / 60s / 90s cadence). The conversation will be locked during polling. Proceed? (yes/no)\"\n\n⊗ Activate Approach 3 without first warning the user that it will lock the conversation pane.\n\n1. ! After receiving user confirmation, use a blocking shell loop with adaptive cadence:\n - First check: wait ~25 seconds (e.g. `sleep 25`), then poll\n - Second check: wait ~60 seconds, then poll\n - Subsequent checks: wait ~90 seconds, then poll\n2. ! Poll using `gh pr view <number> --comments` and `gh pr checks <number>` in the same shell session\n3. ! When the exit condition is met (Greptile review current, confidence > 3, no P0/P1), exit the loop and proceed to Step 5\n4. ! If the user interrupts (Ctrl+C or equivalent), exit gracefully and report current review status\n\n! Greptile may advance its review by **editing an existing PR issue comment** rather than creating a new PR review object. Do NOT rely solely on `pulls/{number}/reviews` — that endpoint may remain stale at an older commit SHA even after Greptile has reviewed the latest commit.\n\n! To confirm the review is current, check **both** surfaces:\n\n1. **PR issue comments** (primary signal) — Greptile edits its existing summary comment in place:\n - `gh pr view <number> --comments` (with `do_not_summarize_output: true`)\n - Or `gh api repos/<owner>/<repo>/issues/<number>/comments`\n - Parse the comment body for `Last reviewed commit` and compare to the pushed commit SHA\n - Check the comment's `updated_at` timestamp to confirm it was refreshed after your push\n2. **PR review objects** (secondary signal) — may or may not be updated:\n - `gh api repos/<owner>/<repo>/pulls/<number>/reviews`\n - Check `commit_id` on the latest review object\n\n! Treat an edited Greptile issue comment as a valid new review pass even if no new PR review object was created.\n\n! Fetch the full untruncated comment body or use MCP `get_comments` to get the actual commit URL containing the full SHA — do NOT rely on grepping truncated link text.\n\n⊗ Re-fetch or re-trigger while the bot's last review still targets an older commit on **both** surfaces.\n\n### Step 5: Re-fetch and analyze\n\n! Fetch the new review using both methods from Step 1.\n\n! Analyze all new findings before planning any changes.\n\n### Step 6: Exit condition check — fail-closed ReviewerStatus all-of (#1259)\n\n! The loop MAY exit clean ONLY when a SINGLE fresh fetch (not cached state, not a verdict assembled across earlier polls) satisfies ALL of the `ReviewerStatus` fields below. This is a **fail-closed all-of**: any field that is missing, unparsed, or ambiguous resolves to **`unknown`**, and `unknown` is NOT a pass — the agent stays in the loop and returns to Step 2. A PARTIAL or STALE Greptile review MUST NOT satisfy the exit predicate; the predicate is what prevents merging un-reviewed code while a P0/P1 finding is still in flight (#1259).\n\n1. ! **Terminal check-run** — the `Greptile Review` check run on the current HEAD has `status == \"completed\"` AND `conclusion` in `{success, neutral}`. The conclusions `null`, `cancelled`, `timed_out`, `stale`, `action_required`, and `failure` are explicitly NOT terminal-clean: any of them resolves to `unknown` and the loop continues. A check run still `queued` / `in_progress` is `unknown`.\n2. ! **HEAD-SHA pinned AT READ TIME** — read the current HEAD SHA in the SAME fetch used to evaluate this predicate (`gh api repos/<owner>/<repo>/pulls/<number> -q .head.sha`, read AT exit-evaluation time, NOT carried over from an earlier poll) and require `head_sha_reviewed == current HEAD`. A review whose reviewed SHA lags HEAD is `unknown`.\n3. ! **Completion marker present and matching** — the rolling-summary comment body carries `Last reviewed commit: <sha>` AND that `<sha>` matches the current HEAD. Extract the SHA with the markdown-link-aware NON-GREEDY regex below. Markdown link text can contain escaped brackets (e.g. a commit subject `add \\[Unreleased\\] entry`), so a greedy `[^\\]]*` stops at the first `]` and yields no match → false `unknown` on a clean review (#1326):\n\n ```\n Last reviewed commit:\\s*\\[.*?\\]\\(https?://github\\.com/[^/]+/[^/]+/commit/(?P<sha>[0-9a-f]{7,40})\n ```\n\n A missing or non-matching completion marker is `unknown`. See [`../../templates/swarm-greptile-poller-prompt.md`](../../templates/swarm-greptile-poller-prompt.md) `### Last reviewed commit:` for the canonical regex shared with the push-driven poller loop.\n4. ! **Confidence > 3** — the parsed `Confidence Score: X/5` is strictly greater than 3 (i.e. 4/5 or 5/5). A `confidence == 3`, an unparsed confidence, or an absent confidence is `unknown`.\n5. ! **No P0/P1 findings** — the triple-tier (+ Tier 2.5) detector reports zero P0 and zero P1 findings (P2 issues are non-blocking style suggestions and do not gate the loop).\n\n! All five fields MUST hold on the SAME single fresh fetch. The agent MUST NOT assemble a \"pass\" by combining a terminal check-run observed on one poll with a confidence parsed on an earlier poll — the read is atomic per the SHA-pinned-AT-READ-TIME rule above.\n\n? If the bot says \"all prior issues resolved\" but lists new issues, treat it as one final batch — not the start of another loop. Go back to Step 2 one more time, re-evaluate this all-of, then stop.\n\n⊗ Exit the loop on a confidence number alone while the check run is non-terminal (`queued` / `in_progress` / `cancelled` / `timed_out` / `stale` / `action_required`) — a confidence score is NOT a verdict without a terminal check-run AND a HEAD-matching completion marker (#1259).\n⊗ Exit the loop against a reviewed SHA that lags the current HEAD — a partial or stale review MUST resolve to `unknown`, never to a pass (#1259).\n\nIf the exit predicate is not met (any field `unknown`), go back to Step 2.\n\n## Pre-Merge Re-Poll Gate (#1259)\n\n! Immediately before any `gh pr merge` invocation, the agent MUST re-fetch reviewer state ONE more time — a fresh `gh pr view <number> --comments`, a fresh `gh api repos/<owner>/<repo>/commits/<HEAD>/check-runs`, and a fresh HEAD-SHA read — and re-evaluate the Step 6 fail-closed all-of against that fresh fetch. The exit-condition pass recorded at the end of the review loop is NOT sufficient authorization to merge: review state can go stale between the loop's last poll and the merge call (a new push, a Greptile re-trigger, a service-side check-run reset).\n\n! Treat the re-poll and the `gh pr merge` as an atomic freshness window. If the re-poll shows ANY field `unknown`, ABORT the merge and return to Step 2.\n\n⊗ Call `gh pr merge` on the strength of a review verdict observed earlier in the loop without an immediately-preceding re-poll that re-satisfies the Step 6 all-of — merging on cached review state is forbidden (#1259).\n\n### Informal-clean missing canonical fields (#1543)\n\nGreptile can post a **separate** informal clean reply that says prior issues are resolved and the current diff is clean while omitting the canonical rolling-summary fields Directive merge gates require: `Last reviewed commit:` and `Confidence Score: X/5`. `task pr:merge-ready` and `task swarm:verify-review-clean` correctly refuse merge-ready in this state -- prose alone cannot prove review currency or confidence.\n\n! When the latest Greptile bot comment is found, reports P0=0 and P1=0, but BOTH canonical fields are unparsed, classify the state as **`informal-clean missing-canonical-fields`** (see `task pr:merge-ready` diagnostic output) instead of treating it as \"review still writing\" or silently polling.\n\n! Recovery for informal-clean missing canonical fields -- route to ONE of these operator actions; do NOT keep polling:\n\n1. Comment `@greptileai review` on the PR to retrigger a canonical rolling summary on the current HEAD.\n2. Wait for Greptile to edit its primary rolling-summary comment with both canonical fields, then re-run `task pr:merge-ready -- <N>`.\n3. Document an explicit operator override per `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 1 (merge with rationale in the merge commit body).\n\n⊗ Treat informal clean Greptile prose (`current diff is clean`, `looks solid`, `no new issues`) as merge-ready without canonical `Last reviewed commit:` and `Confidence Score: X/5` evidence.\n\n⊗ Keep polling silently when `task pr:merge-ready` reports the informal-clean missing-canonical-fields diagnostic -- this is a blocked recovery state, not a late-arriving review.\n\n~ Swarm pollers MUST surface this state via the `### (6) INFORMAL-CLEAN` terminal exit in `templates/swarm-greptile-poller-prompt.md` instead of falling through to generic `(4) TIMEOUT` or `(5) STALL`.\n\n## Submitting GitHub Reviews\n\n! When submitting PR reviews via the GitHub MCP tool, always use `pull_request_review_write` with method `create` and the appropriate event:\n\n- `APPROVE` — formally approve the PR (shows green \"Approved\" status)\n- `REQUEST_CHANGES` — block the PR with requested changes\n- `COMMENT` — review feedback without approving or blocking\n\n⊗ Use `add_issue_comment` for review notes — that creates a regular comment, not a formal review. Review notes must always go in the review body via `pull_request_review_write`.\n\n## GitHub Interface Selection\n\n~ Use the most efficient interface for the task:\n\n- **MCP GitHub tool** — structured/programmatic operations (querying issues, creating PRs, bulk operations, filtering data)\n- **GitHub CLI (`gh`)** — quick ad-hoc commands and direct shell integration\n\nChoose whichever minimizes steps and maximizes clarity for the given task.\n\n~ When MCP is unavailable (agents without MCP tools in their dispatch environment, including `start_agent` / `spawn_subagent` (\"grok-build\") cases, cloud agents, `oz agent run`), `gh` CLI is sufficient as the sole interface. The dual-source requirement (MCP + `gh`) in Step 1 applies only when both are available -- agents without MCP access should use `gh pr view --comments` and `gh api` as their primary and only review detection surface. Runtime capability detection (swarm Phase 3 matrix) informs both orchestration tier and MCP surface choice.\n\n## Framework Events Emitted Here\n\n! When the user replies `yes` / `confirmed` / `approve` on a ready-to-merge PR thread (Phase 5 -> 6 gate per the canonical #642 workflow comment), emit a `plan:approved` framework event via `task lifecycle:event` so the approval is captured as a structural artifact rather than prose-only:\n\n```\ntask lifecycle:event -- emit plan:approved \\\n --plan-ref https://github.com/<owner>/<repo>/pull/<N> \\\n --approver <github-login> \\\n --approval-phrase <yes|confirmed|approve> \\\n --pr-number <N>\n```\n\n? Downstream consumers of `plan:approved` (auto-merge bots, status updates, audit reporting) are explicitly deferred to follow-up work; this event currently emits a record only (#635 events behavioral wiring).\n\n## Post-Merge Verification\n\n! After a PR is squash-merged, verify that all referenced issues were actually closed. Squash merges can silently fail to process closing keywords (`Closes #N`, `Fixes #N`) from the PR body (#167).\n\n1. ! For each issue referenced with a closing keyword in the PR body, run:\n ```\n gh issue view <N> --json state --jq .state\n ```\n2. ! If the issue state is not `CLOSED`, close it manually with a comment referencing the merged PR:\n ```\n gh issue close <N> --comment \"Closed by #<PR> (squash merge — auto-close did not trigger)\"\n ```\n3. ~ This step mirrors `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 2 and applies to ALL PR merges, not just swarm runs.\n4. ! For PRs that referenced any umbrella / staying-OPEN issue (`Refs #N`), the INVERSE check applies: any protected issue that auto-closed MUST be reopened with a comment citing #701 and the merged PR. See `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 1 protected-issue reopen sweep and `meta/lessons.md` `## GitHub Closing-Keyword False-Positive Layer 3` for the persistent `closingIssuesReferences` link case (Layer 3, #701).\n\n## Anti-Patterns\n\n- ⊗ Route PR shepherding to Cursor global `babysit` on Deft-managed repos when `.deft/core/` is installed -- use this review-cycle skill instead (#2261)\n- ⊗ Treat a passing SLizard/Greptile check run, a non-blocking review comment, or an ad hoc fix commit as the review-cycle exit predicate -- Step 6 fail-closed all-of (#1259) and multi-reviewer registry triage (#769) still apply\n- ⊗ Push individual fix commits per finding\n- ⊗ Start fixing before analyzing ALL findings\n- ⊗ Rely on the bot to catch syntax errors in structured data files\n- ⊗ Re-trigger a bot review before the previous one has updated\n- ⊗ Report \"all comments resolved\" without checking both `gh pr view --comments` and a second source (`get_review_comments` via MCP, or `gh api` fallback when MCP is unavailable)\n- ⊗ Use `add_issue_comment` for formal review submission\n- ⊗ Commit or push Phase 1 audit fixes independently — always batch with Phase 2 fixes\n- ⊗ Proceed to Phase 2 while any Phase 1 prerequisite is unmet\n- ⊗ Rely solely on `pulls/{number}/reviews` to detect whether Greptile has reviewed the latest commit — Greptile may update via an edited issue comment instead of a new review object\n- ⊗ Push additional commits while Greptile is reviewing the current head — each push re-triggers Greptile and resets the review clock\n- ⊗ Use blocking `Start-Sleep` shell loops or `time.sleep()` loops to poll for review updates when Approach 1 or 2 is available -- Approach 3 (blocking loop) is permitted only as a last resort with user warning\n- ⊗ Poll more frequently than every 20 seconds -- use a real delay between checks, not back-to-back calls; adaptive cadence (20-30s / 60s / 90s) replaces the fixed 60s minimum\n- ⊗ Stop and ask the user whether to continue after pushing -- the review/fix loop MUST run autonomously to the exit condition\n- ⊗ Push fix commits without scanning changed lines for untested code paths — always check test coverage before pushing\n- ⊗ Push a fix commit that addresses fewer findings than the current Greptile review surfaces — if Greptile flags 3 issues, all 3 must be fixed in one commit before pushing\n- ⊗ Push after fixing a P1 without first checking whether the same Greptile review contains additional P0 or P1 findings\n- ⊗ Assume squash merge auto-closed referenced issues — always verify with `gh issue view` after merge (#167)\n- ⊗ Assume Approach 2 (yield-between-polls) produces a self-sustaining polling loop -- yielding ends the agent's turn with no self-wake; swarm agents will silently stop polling\n- ⊗ Skip the second review source (MCP or `gh api` fallback) without probing for MCP capability and documenting the fallback used\n- ⊗ Run a partial test suite instead of `task check` without documenting the pre-existing failure reason and open issue number in the PR body\n- ⊗ Create a PR without running `skills/deft-directive-pre-pr/SKILL.md` first -- the pre-PR quality loop catches issues before they reach the reviewer\n- ⊗ Activate Approach 3 (blocking `Start-Sleep` loop) without first warning the user that it will lock the conversation pane and receiving confirmation\n- ⊗ Exit the review loop on a Greptile confidence number alone while the check run is non-terminal -- a confidence score is NOT a verdict without a terminal check-run (`completed` + `{success, neutral}`) AND a HEAD-matching `Last reviewed commit:` completion marker (#1259)\n- ⊗ Call `gh pr merge` on cached/earlier review state without an immediately-preceding pre-merge re-poll that re-satisfies the Step 6 fail-closed all-of (#1259)\n",
|
|
251
|
+
"body": "# Deft Directive Review Cycle\n\nStructured workflow for responding to bot reviewer (Greptile) findings on a PR.\n\nLegend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.\n\n## Platform Requirements\n\n! This skill requires **GitHub** as the SCM platform and the **GitHub CLI (`gh`)** to be installed and authenticated. PR comment fetching, check-run queries, and review submission all depend on `gh`.\n\n\n## Ordered-plan exit / advance (#2402)\n\n! When the review-cycle exit condition is met (no P0/P1 remaining / CLEAN verdict):\n\n1. Return control to the caller — do **not** discover adjacent backlog work.\n2. If an ordered-plan sequence is active and this PR matched the current entry, run `task plan-sequence:advance`.\n3. If another entry remains, the caller may continue **only** to that entry (one unit).\n4. If the sequence is exhausted, stop and ask. Do not open another PR from queue intuition or chaining text.\n\n⊗ Exit the review cycle into `task triage:queue` or an unrelated PR without fresh operator approval.\n\n## When to Use\n\n- User says \"review cycle\", \"check reviews\", or \"run review cycle\" on a PR\n- Operator asks to **babysit**, **shepherd**, or **watch** a PR -- including the Cursor product action **babysit-pull-request-in-cloud** (#2261 / #1862 intent-routing class)\n- A bot reviewer (Greptile) has posted findings on an open PR\n- Dispatching a cloud or background agent to monitor and resolve PR review findings until merge-ready\n\n## Cursor global babysit supersession (#2261)\n\nOn repos with `.deft/core/` installed, **PR shepherding intent** routes here -- not the Cursor-global `babysit` skill (`~/.cursor/skills-cursor/babysit/SKILL.md`). The product action **babysit-pull-request-in-cloud** shares the babysit name but MUST chain into this review-cycle surface on Deft-managed projects.\n\n- ! Orchestrators dispatching cloud/background workers for PR shepherding MUST include `templates/agent-prompt-preamble.md` (or reference it) and mandate reading this skill as the first workflow step.\n- ! Cloud babysit workers use `worker_role: review-monitor` (or `drive-to: merge-ready` implementation workers that own the review cycle) -- not a generic babysit loop without Phase 1 audit or Step 6 exit predicate (#1259).\n- ⊗ Route **babysit-pull-request-in-cloud** to Cursor global babysit when `.deft/core/` is present -- that bypasses xBRIEF preflight, `task check`, Greptile fail-closed exit, and CHANGELOG discipline (#2261 recurrence).\n\n## Branch-Protection Policy Guard\n\n! Before entering the review/fix loop, run the skill-level branch-policy guard (#746 / #747). Halt before any state mutation if the project's `plan.policy.allowDirectCommitsToMaster` is unresolvable AND the operator has not set `DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1`. Concretely:\n\n```\ntask verify:branch || exit 1\n```\n\nThe skill MUST NOT modify files, push, or comment on the PR until the guard passes -- this catches the case where a malformed PROJECT-DEFINITION quietly disabled the policy and the agent would have committed directly to master mid-review.\n\n## Deterministic Questions Contract\n\n! Every numbered-menu prompt rendered in this skill (Phase 1 audit gates, Phase 2 Step 4 monitoring approach selection, Phase 5->6 ready-to-merge gate, Step 6 exit-condition prompts) MUST follow [`../../contracts/deterministic-questions.md`](../../contracts/deterministic-questions.md): the final two numbered options MUST be `Discuss` and `Back`, in that order. The Discuss-pause semantic is documented verbatim there -- on `Discuss` selection the agent MUST halt the in-progress sequence immediately, prompt `What would you like to discuss?`, and resume only on an explicit user signal (re-asking the original question, saying `resume`/`continue`, or re-issuing the prior selection). Implicit resumption is forbidden.\n\n## Pre-Flight Check\n\n! Before entering the review/fix loop, verify the Greptile configuration supports it:\n\n1. ! `triggerOnUpdates` must be enabled (via Greptile dashboard or `.greptile/config.json`) — without this, Greptile only reviews the initial PR and never re-reviews after fix pushes, so the loop cannot reach the exit condition\n2. ~ `statusCheck` should be enabled so Greptile posts a `\"Greptile Review\"` check run on each commit — this is the signal the org ruleset uses to gate merges\n3. ? If Greptile does not re-review after a push despite `triggerOnUpdates` being enabled, comment `@greptileai` on the PR as a manual re-trigger fallback\n\n! Greptile posts **check runs** (GitHub Checks API), not **commit statuses** (Statuses API). To verify the check run is present on a commit:\n\n```\ngh api repos/<owner>/<repo>/commits/<sha>/check-runs --jq '.check_runs[] | select(.name == \"Greptile Review\")'\n```\n\n⊗ Use `commits/<sha>/statuses` to check for Greptile — that endpoint will always be empty.\n\n~ See `tools/greptile.md` for recommended dashboard and per-repo settings.\n\n## Phase 1 — Deft Process Audit\n\n! Before touching code, verify ALL prerequisites are satisfied. Fix any gaps first:\n\n1. ! Verify `skills/deft-directive-pre-pr/SKILL.md` was run before PR creation -- the PR branch should have passed at least one full RWLDL cycle. If not, run it now before proceeding.\n2. ! `PROJECT-DEFINITION.xbrief.json` and `xbrief/` lifecycle folders have scope xBRIEF coverage for all changes in the PR\n3. ! `CHANGELOG.md` has entries under `[Unreleased]` for the PR's changes\n4. ! `task check` passes fully (fmt + lint + typecheck + tests + coverage ≥75%)\n5. ! `.github/PULL_REQUEST_TEMPLATE.md` checklist is satisfied in the PR description\n6. ! If the PR touches 3+ files: verify a `/deft:change` `proposal.xbrief.json` exists in `history/changes/` for this branch and was explicitly confirmed by the user (affirmative response, not a broad 'proceed'), or document N/A with reason in the PR checklist\n7. ! Verify the PR is on a feature branch -- work MUST NOT have been committed directly to the default branch (master/main)\n\n~ **PR scope gate:** If the PR spans 3+ unrelated surfaces (e.g. a skill, a tool doc, and a strategy -- with no shared issue or scope xBRIEF linking them), warn the user that broad PRs increase review churn and Greptile noise. Recommend splitting into focused PRs unless all changes trace to the same scope xBRIEF or issue bundle.\n\n! Phase 1 audit gaps must be resolved before merging — but hold the fixes (do NOT commit or push them independently). Proceed to Phase 2 analysis to gather bot findings, then batch all Phase 1 + Phase 2 fixes into a single commit.\n⊗ Commit or push Phase 1 audit fixes independently before gathering Phase 2 findings.\n\n## Phase 2 — Review/Fix Loop\n\n### Step 1: Fetch ALL bot comments\n\n! Retrieve findings using BOTH methods — each catches different comment categories:\n\n```\ngh pr view <number> --comments\n```\n\n! Use `do_not_summarize_output: true` — summarizers silently drop the \"Comments Outside Diff\" section from large bot comments.\n\n~ **Oversized output fallback:** If `do_not_summarize_output: true` produces output too large to process, extract the relevant section with:\n\n- **PowerShell (Windows):** `gh pr view <number> --comments | Select-String \"Outside Diff\" -Context 50`\n- **Unix/macOS:** `gh pr view <number> --comments | grep -A 50 \"Outside Diff\"`\n\nBoth commands extract the \"Comments Outside Diff\" section with surrounding context, avoiding the need to process the full output.\n\n~ **Windows + Grok Build (#1353):** Avoid `|`, `>`, or `2>&1` in `run_terminal_command` strings -- use Python `pathlib`/`subprocess` or plain task commands instead.\n\n! **MCP capability probe** (mirrors deft-directive-swarm Phase 3 pattern): Before attempting MCP `get_review_comments`, probe whether MCP GitHub tools are available in the current session. Detection: attempt a lightweight MCP call (e.g. list available tools or a no-op query) -- if it succeeds, MCP is available; if it errors or the tool is not in the available set, MCP is unavailable.\n\n- **MCP available**: ! Use MCP `get_review_comments` as the second source to catch Comments Outside Diff.\n- **MCP unavailable** (e.g. non-MCP agents including `start_agent` / `spawn_subagent` (\"grok-build\") dispatch, cloud agents, `oz agent run`): ! Use `gh api repos/<owner>/<repo>/pulls/<number>/comments` as the explicit fallback for the second review source. Document in the commit message or PR comment why MCP was skipped (e.g. \"MCP unavailable in this session -- used gh api fallback for review comments\"). The platform descriptor from runtime detection determines MCP availability independently of the dispatch primitive.\n\n⊗ Report \"all comments resolved\" without verifying both sources.\n⊗ Skip the second review source without probing for MCP capability and documenting the fallback used.\n\n~ **Late-arriving bot review re-check:** If the initial dual-source fetch returns no bot review on the current HEAD SHA, wait ~60s and re-fetch before evaluating the Step 6 exit condition. Bot reviewers (Greptile) typically land within 3-7 min of PR creation/push; an empty first pass is more likely \"review pending\" than \"review clean\".\n\n⊗ Declare the exit condition met based on a single fetch that returned no bot review — re-fetch at least once after a ~60s delay first.\n\n~ This codifies a user-rule precedent on late-arriving bot reviews into the deft-internal deterministic tier. The [`templates/swarm-greptile-poller-prompt.md`](../../templates/swarm-greptile-poller-prompt.md) loop body already handles the same case for push-driven cycles via its per-poll fetch -- the rule above closes the orthogonal cold-start path where the one-shot review-cycle entry runs on a freshly-opened PR before any fix push has triggered the Step 4 polling loop.\n\n### Step 2: Analyze ALL findings before changing anything\n\n! Before making any changes:\n\n- Read every finding across all files\n- Identify cross-file dependencies (a term, value, or field mentioned in multiple files)\n- Categorize by severity (P0, P1, P2 — where P0 is critical/blocking, P1 is a real defect, P2 is a style or non-blocking suggestion)\n- Plan a single coherent batch of fixes\n\n⊗ Start fixing individual findings as you encounter them.\n\n### Step 3: Fix all findings in ONE batch commit\n\n! Apply ALL fixes across all files before committing:\n\n- ! For any fix that touches a value, term, or field appearing in multiple files: grep for it across the full PR file set and update every occurrence in the same commit\n- ! Validate structured data files locally before committing (e.g. `python3 -m json.tool` for JSON, YAML lint for YAML) — do not rely on the bot to catch syntax errors\n- ! Before committing any Greptile fix, re-read the FULL current Greptile review and confirm all P0/P1 issues are addressed in the staged changes — this is the pre-commit gate that prevents per-finding fix commits\n- ! Run `task check` before committing\n- ! **Fail-loud completion claim (#1006)**: when reporting fix-batch completion (to the user, in the commit message, in a PR comment, or in a status message to a parent agent), MUST surface the OUTCOMES not the intent -- name the P0/P1 finding count addressed (\"addressed 3/3 P0 findings, 2/2 P1 findings, 0 deferred\" -- NOT \"all findings addressed\"), report the `task check` result with the test-collection counts (\"task check: 412 collected, 412 passed, 0 skipped, 0 xfailed\" -- NOT \"task check passed\"), and explicitly call out any finding intentionally deferred with the reason. Apply `coding/coding.md` `## Fail Loud: Completion Claims Require Outcome Verification (#1006)` to every claim emitted during the review cycle\n- ⊗ Claim \"all Greptile findings addressed\" without verifying that the staged fix actually closes every P0/P1 currently surfaced in the review body -- a fix that addresses 3 out of 4 P1 findings and reports completion is the exact failure mode #1006 forbids\n- ⊗ Claim \"task check passes\" when any test was skipped, xfailed, or run with errors suppressed -- report the full collection counts instead (#1006)\n- ? **Pre-existing failure carve-out**: If `task check` fails due to a pre-existing issue unrelated to the PR's changes, a partial test suite run is acceptable ONLY if BOTH conditions are met: (a) the `task check` failure is pre-existing with an open GitHub issue number tracking it, AND (b) the PR description explicitly notes the failure and includes the issue reference (e.g. \"task check: test_foo fails due to #NNN (pre-existing)\"). Without both conditions, the full `task check` pass remains mandatory.\n- ~ Commit message: `fix: address Greptile review findings (batch)`\n\n⊗ Push individual fix commits per finding — always batch.\n\n### Step 3b: Proactive test coverage scan\n\n! After committing the fix batch but before pushing, scan the changed lines for untested code paths:\n\n1. ! Run `git --no-pager diff HEAD~1 HEAD --name-only` to identify files touched in the fix batch\n2. ! For each changed file that has a corresponding test file, review whether the fix introduced or modified logic that lacks test coverage\n3. ! If untested code paths are found, write tests and amend them into the fix batch commit (or add as a second commit in the same push)\n4. ! Run `task check` again after adding tests to verify they pass\n\n~ This eliminates one CI round-trip per fix cycle — catching coverage gaps before CI does.\n\n⊗ Push fix commits without scanning for untested code paths in changed files.\n\n### Step 4: Push and wait\n\n! Push the batch commit, then wait for the bot to review the latest commit.\n\n! After pushing, the agent MUST autonomously poll for review updates and continue the review cycle without stopping to ask the user. Do not pause for confirmation, do not ask \"should I continue?\", do not wait for user input between push and review completion. The review/fix loop is designed to run to the exit condition without human intervention.\n\n⊗ Push any additional commits — including unrelated fixes, doc updates, or lessons — while waiting for the bot to finish reviewing the current head. Every push re-triggers Greptile and resets the review clock. If you discover additional work while waiting, stage it locally but do NOT push until the current review completes.\n\n\n\n### Greptile CLEAN vs CI holdout (`pr:watch` / #2688)\n\n! When waiting on a Greptile verdict for a `drive-to: merge-ready` worker (or any review-cycle owner), prefer `task pr:watch -- <N>` (or `--one-shot --json`) over ad-hoc sleep loops (#1056). Parse `clean_gate_holdout` on every probe.\n\n! When `clean_gate_holdout=ci_failures` and Greptile otherwise satisfies the probe-side Step 6 fields (SHA match on HEAD, confidence > 3, no P0/P1, not errored): **MUST NOT** idle-poll hoping CI heals. Treat Greptile CLEAN + CI red with the **same ownership** as a Greptile P0 for a merge-ready worker — one fix batch, re-push, re-probe.\n\n! On persistent `ci_failures` holdout: exit the Greptile wait immediately, fetch failing check annotations (`gh pr checks <N>`, CodeQL / required-check details, or `ci_failed_checks` from `pr:watch --json`), fix or escalate with evidence, then re-enter the review loop after CI is green.\n\n! PR body \"Test plan\" checkboxes being `[x]` do **not** authorize idle wait — merge-ready is `pr:watch` CLEAN / the merge path only.\n\n⊗ Treat `pr:watch` TIMEOUT or long PENDING with `clean_gate_holdout=ci_failures` as \"Greptile still reviewing\" — it means **CI blocked the clean gate**.\n\n~ Surface the holdout to the user/parent on the first stable `ci_failures` probe (fail-loud), not after burning `max-wait-minutes`. See also [`templates/swarm-greptile-poller-prompt.md`](../../templates/swarm-greptile-poller-prompt.md) CLEAN gate evaluation (#1039).\n\n\n### Runner capacity stall (`runner_capacity_stall` / #2672)\n\n! Framework CI prefers Blacksmith with a timed GH-hosted failover (~20 minute stall budget). When `task pr:watch --json` / `task pr:merge-ready` reports `ci_ready_state=runner_capacity_stall` (or verdict `RUNNER_CAPACITY_STALL`, exit 2): **wait for auto-failover** to the ubuntu-latest lane and the authoritative aggregator check. Do **not** invent `--skip-ci` or merge with pending required checks.\n\n! `runner_capacity_stall` is distinct from ordinary `not_ready_yet` (under budget / `in_progress`) and from execution hangs (#2652). Capacity stall means required checks stayed `queued` with no runner claimed past the budget.\n\n⊗ Use `--skip-ci` / merge-with-pending because CI is capacity-stalled — the failover path is the unblock; skip-ci is an incident-only release escape hatch (#2652), not a runner-capacity remedy.\n\n### Stall Detection Rubric (#564)\n\n! Track per poll: `startedAt` (timestamp of the first observation of the IN_PROGRESS check run for the current commit) and `commit.oid` (head SHA being reviewed). Both fields MUST be re-recorded every time the head SHA changes -- the rubric measures elapsed time on a single commit, not across the whole review cycle.\n\n! Expected duration baseline -- Greptile reviews typically complete in 2-5 minutes, with 7 minutes as the upper bound of normal. The escalation threshold is **3x expected = ~10 minutes** of continuous IN_PROGRESS on the same `commit.oid`. The 21-minute stall observed during the rc4 swarm cascade on PR #561 is the recurrence record; see [`../../meta/lessons.md`](../../../meta/lessons.md) `## Greptile Review Stall Detection (2026-04)`.\n\n! When elapsed time on the current `commit.oid` exceeds 10 minutes (3x expected) without the IN_PROGRESS check transitioning to a terminal state, the agent MUST escalate to the user. The escalation message MUST include: (1) the PR number; (2) the head SHA being reviewed; (3) the elapsed time since `startedAt`; (4) the four canonical user-decision options.\n\n! User-decision options at escalation -- render as a deterministic numbered menu per [`../../contracts/deterministic-questions.md`](../../contracts/deterministic-questions.md) (final two options `Discuss` + `Back`):\n\n 1. Wait another N minutes (user picks N).\n 2. Manually re-trigger Greptile by commenting `@greptileai` on the PR (logs the override in a PR comment for auditability per the next rule).\n 3. Skip the bot review for this cycle and exit the loop with a documented reason.\n 4. Cancel the review cycle entirely.\n 5. Discuss.\n 6. Back.\n\n! Auto-restart detection -- when the polling loop observes a NEW `startedAt` (Greptile dropped its prior check run and started a fresh one without any push from the agent, e.g. service-side restart), the agent MUST reset its elapsed-time clock to the new `startedAt` AND notify the user that an auto-restart was detected. Resetting the clock without notifying is forbidden -- the user needs to know the cycle effectively re-started.\n\n! **`INCOMPLETE_BUT_RATED` stall signature (#1259):** when a poll observes a parsed `Confidence Score: X/5` number BUT no terminal check-run (no `completed` status with a `{success, neutral}` conclusion) AND/OR no HEAD-matching `Last reviewed commit:` completion marker, classify the state as **`INCOMPLETE_BUT_RATED`** — Greptile has emitted a confidence rating against a review that has NOT terminally landed on the current HEAD. This is NOT an exit condition (the Step 6 fail-closed all-of resolves the missing fields to `unknown`); treat it as a stall signature and keep polling, escalating per the 10-minute threshold above if it persists. A confidence number is the single most common false-positive for a premature exit — `INCOMPLETE_BUT_RATED` names the trap so the agent does not mistake a rating for a verdict.\n\n⊗ Auto-retrigger Greptile (empty commits, force-pushes, agent-posted `@greptileai` comments, status-check rebuilds) without explicit user approval. The escalation menu's option 2 is the ONLY supported re-trigger path, and even that requires the user to pick it.\n\n! Document any user-approved override in a brief PR comment for auditability -- e.g. `Note: review-cycle stall detected at <SHA> after <N> min; user approved manual re-trigger via @greptileai per skills/deft-directive-review-cycle Stall Detection Rubric (#564).` This makes the override visible to humans reviewing the PR history and to future agents that resume the cycle.\n\n⊗ Treat a stall as silent -- if the elapsed clock crosses the 10-minute threshold the agent MUST surface the menu, even if the agent is mid-poll. Continuing to poll past the threshold without user input is forbidden.\n\n### Review Monitoring\n! **Background / independent dispatch (#1880 Gap D):** Long-running review-cycle owners and pollers (>~3 min) MUST be dispatched independently / in the background so the parent conversation stays interactive. On Cursor, use the Task tool background path (`run_in_background: true`). This generalizes the Approach-1 sub-agent monitor rule to implementation and fix workers as well — foreground dispatch is reserved for short tasks. The parent receives completion via `DONE` / `BLOCKED` / `FAILED` per `templates/agent-prompt-preamble.md` §11.\n\n! **Worker-owns-lifecycle (#1880 Gap C):** When this skill runs as part of an implementation worker scoped `drive-to: merge-ready`, the worker owns the full review/fix loop through merge-ready in its own tool loop — do NOT exit at PR-open expecting the orchestrator to spawn a separate review leaf.\n\n\n\n! Select the monitoring approach based on runtime capability detection (the matrix in `skills/deft-directive-swarm/SKILL.md` Phase 3 Step 1, extended per #1342 slices 1-2 for `spawn_subagent` / \"grok-build\" and per #1877 for Cursor as first-class Tier-1 tiers). Probe the environment (tool set + env vars) to obtain the stable platform descriptor (`grok-build`, `warp-orchestrated`, `warp-manual`, `cursor-composer`, `cursor-cloud-agent`, etc.) from the launch adapter / `get_platform_capabilities` and map the descriptor to the appropriate tier + dispatch primitive (`start_agent`, `spawn_subagent`, or the Cursor `Task` tool). The descriptor (not hard-coded tool presence) is the single source of truth for both launch and review monitoring.\n\n- **Tier 1 (orchestrated sub-agent)** → Approach 1 (spawn review-monitor sub-agent via the primitive matching the descriptor: `start_agent`, `spawn_subagent`, or the Cursor `Task` tool with `run_in_background: true`)\n- **Tier 2 (no sub-agent primitive, but scheduler/timer/auto-reinvocation)** → Approach 2 (yield-between-polls)\n- **Tier 3 (interactive session, nothing else)** → Approach 3 (blocking sleep loop as last resort)\n\n! Detection: use the full runtime capability matrix (swarm Phase 3 + launch adapter from #1342 slice 2). The old single-probe for `start_agent` is superseded; the returned platform descriptor determines both the orchestration path and the MCP surface (see MCP probe below). If the descriptor is `grok-build` (spawn_subagent present, start_agent + WARP_* absent), treat as Tier 1 with the spawn_subagent poller path. If the descriptor is `cursor-composer` / `cursor-cloud-agent` (Cursor `Task` tool present, start_agent + WARP_* + spawn_subagent absent), treat as **Tier 1 with the backgrounded Cursor `Task` poller path** (#1877) — NOT Tier 3. Cursor's `Task` tool is a first-class sub-agent primitive; degrading a Cursor session to the Approach-3 blocking poll is the misclassification #1877 closes.\n\n! Swarm agents (whether launched via `start_agent` or `spawn_subagent` per the platform descriptor) SHOULD prefer Approach 1 for their own review-monitor sub-agent. Approach 2's yield-between-polls is not self-sustaining for swarm agents (see warning below). Always include the canonical `templates/agent-prompt-preamble.md` (AGENTS.md read mandate, #810 xBRIEF gate, #798 PowerShell UTF-8, pre-PR + review-cycle mandates) when spawning a poller sub-agent.\n\n! **Deterministic review-monitor gate (#2655):** When Tier 1 is available, run `task verify:review-monitor -- --pr <N> [--call-site solo]` before yielding, entering Approach 3, or claiming review monitoring started. After spawning Approach 1, register with `task review-monitor:register -- --pr <N> --monitor-agent-id <id> --platform-primitive start_agent|spawn_subagent|cursor-task`. Exit `0` ready / `1` not ready / `2` config. Approach 3 on Tier 1 is a gate failure — use `--approach3 --approach3-warned` only on Tier 3 after the user warning.\n\n! **CI-holdout carve-out (#2688):** When `task pr:watch --one-shot --json` reports `clean_gate_holdout=ci_failures` with Greptile fields otherwise satisfied on current HEAD, do **not** freeze on `verify:review-monitor` / spawn-monitor as if Greptile latency were the blocker. Fix CI first (same ownership as Greptile P0). Keep or register a review-monitor only while still waiting on Greptile latency; a `BLOCKED: ci_failures` DONE handback to the implementation owner is correct.\n\n\n**Approach 1 (preferred -- sub-agent orchestration available per platform descriptor):**\n\n! **Background dispatch (#1880):** Spawn the review-monitor sub-agent via the matching primitive IN THE BACKGROUND (Cursor: Task `run_in_background: true`; Grok Build: `spawn_subagent` with parent yielding). The parent MUST remain interactive while the poller runs.\n\n! **Heartbeat contract for Cursor pollers (#1877 / #1166):** A Cursor `Task` review-monitor poller whose loop runs > ~3 min MUST honour the sub-agent heartbeat contract (`docs/subagent-heartbeat.md`), same as the `spawn_subagent` path — emit periodic progress so the parent can distinguish a live poller from a hung one.\n\n! When the platform descriptor indicates Tier 1 (sub-agent support), spawn a review-monitor sub-agent using the primitive matching the descriptor:\n\n1. ! Launch via the matching primitive: `start_agent` (Warp), `spawn_subagent` (grok-build / TUI / non-Warp), **or the Cursor `Task` tool with `run_in_background: true` (`cursor-composer` / `cursor-cloud-agent`, #1877)** with a prompt that instructs it to poll for Greptile completion. For `spawn_subagent` and the Cursor `Task` tool the prompt MUST reference the canonical poller template `templates/swarm-greptile-poller-prompt.md` (with placeholders filled) plus the agent preamble; the working directory / context must be the PR branch (worktree or equivalent for hybrid).\n2. ! The sub-agent polls using the mechanism for its primitive: for `spawn_subagent` use `get_command_or_subagent_output` (adaptive cadence: ~20-30s first check after push, ~60s second, ~90s thereafter; Greptile typically lands in 3-7 min); for `start_agent` the native messaging path; for the Cursor `Task` tool the backgrounded-task completion-notification path. Front-load the first check to catch fast reviews.\n3. ! When the exit condition is met (Greptile review current on the HEAD commit SHA, confidence > 3, no P0/P1 remaining), the sub-agent reports completion back to the parent (via `send_message_to_agent` or the spawn_subagent result channel).\n4. ! The main conversation pane stays fully interactive during monitoring -- the user (or parent monitor) can continue other work.\n5. ! On receiving the completion message / result, the parent re-fetches findings (both gh pr view --comments and the secondary source) and proceeds to Step 5.\n\n**Approach 2 (fallback -- no sub-agent primitive for the descriptor):**\n\n! When the platform descriptor indicates no sub-agent orchestration (or the primitive is unavailable), use discrete tool calls with a yield between checks. For `grok-build` / spawn_subagent descriptor this path is normally avoided in favor of Approach 1; it exists for pure interactive or limited runtimes.\n\n1. ! Use the current shell execution tool (`run_terminal_command` or equivalent in the runtime) in wait mode to run `gh pr view <number> --comments` and `gh pr checks <number>`.\n2. ! After each check, yield control (end all tool calls) -- the agent runtime will re-invoke after its interval or on next interaction.\n3. ! Target adaptive cadence (20-30s / 60s / 90s) where the runtime permits. The full cadence is easiest in Approach 1 (sub-agent) or 3 (blocking); pure yield is runtime-controlled.\n4. ! No blocking shell pane lock -- the conversation remains interactive between checks.\n5. ~ Approach 2 requires a periodic re-invocation trigger (timer, scheduler, user nudge, or external orchestrator for hybrid/worktree cases). Without it the poller stops after the first yield.\n6. ! When the exit condition is met, proceed to Step 5.\n\n⚠️ **Swarm / hybrid limitation**: Approach 2 is NOT autonomous for swarm agents or manual worktree setups. Yielding ends the turn with no self-wake; the parent monitor (or external scheduler) must detect idle and re-trigger or send a message. For true `grok-build` / spawn_subagent hybrids, prefer Approach 1 (spawn_subagent + get_command_or_subagent_output poller) exactly as the swarm launch adapter does.\n\n⊗ Use blocking `Start-Sleep` shell loops or `time.sleep()` loops EXCEPT as Approach 3 (see below) -- these lock the conversation and prevent user interaction.\n⊗ Poll more frequently than every 20 seconds -- use a real delay between checks, not back-to-back calls. Adaptive cadence (20-30s / 60s / 90s) replaces the fixed 60s minimum.\n\n**Approach 3 (last resort -- interactive session, no `start_agent`, no timer/scheduler):**\n\n! Approach 3 is a blocking sleep-poll loop used ONLY when both Approach 1 and Approach 2 are unavailable (interactive session with no `start_agent` and no auto-reinvocation mechanism). Uses PowerShell `sleep` / Unix `sleep` commands between polls.\n\n! **User warning gate:** Before activating Approach 3, the agent MUST warn the user that the conversation pane will be locked during polling and ask for explicit confirmation. Example: \"No sub-agent or auto-reinvocation available. I will poll in a blocking loop (~20-30s / 60s / 90s cadence). The conversation will be locked during polling. Proceed? (yes/no)\"\n\n⊗ Activate Approach 3 without first warning the user that it will lock the conversation pane.\n\n1. ! After receiving user confirmation, use a blocking shell loop with adaptive cadence:\n - First check: wait ~25 seconds (e.g. `sleep 25`), then poll\n - Second check: wait ~60 seconds, then poll\n - Subsequent checks: wait ~90 seconds, then poll\n2. ! Poll using `gh pr view <number> --comments` and `gh pr checks <number>` in the same shell session\n3. ! When the exit condition is met (Greptile review current, confidence > 3, no P0/P1), exit the loop and proceed to Step 5\n4. ! If the user interrupts (Ctrl+C or equivalent), exit gracefully and report current review status\n\n! Greptile may advance its review by **editing an existing PR issue comment** rather than creating a new PR review object. Do NOT rely solely on `pulls/{number}/reviews` — that endpoint may remain stale at an older commit SHA even after Greptile has reviewed the latest commit.\n\n! To confirm the review is current, check **both** surfaces:\n\n1. **PR issue comments** (primary signal) — Greptile edits its existing summary comment in place:\n - `gh pr view <number> --comments` (with `do_not_summarize_output: true`)\n - Or `gh api repos/<owner>/<repo>/issues/<number>/comments`\n - Parse the comment body for `Last reviewed commit` and compare to the pushed commit SHA\n - Check the comment's `updated_at` timestamp to confirm it was refreshed after your push\n2. **PR review objects** (secondary signal) — may or may not be updated:\n - `gh api repos/<owner>/<repo>/pulls/<number>/reviews`\n - Check `commit_id` on the latest review object\n\n! Treat an edited Greptile issue comment as a valid new review pass even if no new PR review object was created.\n\n! Fetch the full untruncated comment body or use MCP `get_comments` to get the actual commit URL containing the full SHA — do NOT rely on grepping truncated link text.\n\n⊗ Re-fetch or re-trigger while the bot's last review still targets an older commit on **both** surfaces.\n\n### Step 5: Re-fetch and analyze\n\n! Fetch the new review using both methods from Step 1.\n\n! Analyze all new findings before planning any changes.\n\n### Step 6: Exit condition check — fail-closed ReviewerStatus all-of (#1259)\n\n! The loop MAY exit clean ONLY when a SINGLE fresh fetch (not cached state, not a verdict assembled across earlier polls) satisfies ALL of the `ReviewerStatus` fields below. This is a **fail-closed all-of**: any field that is missing, unparsed, or ambiguous resolves to **`unknown`**, and `unknown` is NOT a pass — the agent stays in the loop and returns to Step 2. A PARTIAL or STALE Greptile review MUST NOT satisfy the exit predicate; the predicate is what prevents merging un-reviewed code while a P0/P1 finding is still in flight (#1259).\n\n1. ! **Terminal check-run** — the `Greptile Review` check run on the current HEAD has `status == \"completed\"` AND `conclusion` in `{success, neutral}`. The conclusions `null`, `cancelled`, `timed_out`, `stale`, `action_required`, and `failure` are explicitly NOT terminal-clean: any of them resolves to `unknown` and the loop continues. A check run still `queued` / `in_progress` is `unknown`.\n2. ! **HEAD-SHA pinned AT READ TIME** — read the current HEAD SHA in the SAME fetch used to evaluate this predicate (`gh api repos/<owner>/<repo>/pulls/<number> -q .head.sha`, read AT exit-evaluation time, NOT carried over from an earlier poll) and require `head_sha_reviewed == current HEAD`. A review whose reviewed SHA lags HEAD is `unknown`.\n3. ! **Completion marker present and matching** — the rolling-summary comment body carries `Last reviewed commit: <sha>` AND that `<sha>` matches the current HEAD. Extract the SHA with the markdown-link-aware NON-GREEDY regex below. Markdown link text can contain escaped brackets (e.g. a commit subject `add \\[Unreleased\\] entry`), so a greedy `[^\\]]*` stops at the first `]` and yields no match → false `unknown` on a clean review (#1326):\n\n ```\n Last reviewed commit:\\s*\\[.*?\\]\\(https?://github\\.com/[^/]+/[^/]+/commit/(?P<sha>[0-9a-f]{7,40})\n ```\n\n A missing or non-matching completion marker is `unknown`. See [`../../templates/swarm-greptile-poller-prompt.md`](../../templates/swarm-greptile-poller-prompt.md) `### Last reviewed commit:` for the canonical regex shared with the push-driven poller loop.\n4. ! **Confidence > 3** — the parsed `Confidence Score: X/5` is strictly greater than 3 (i.e. 4/5 or 5/5). A `confidence == 3`, an unparsed confidence, or an absent confidence is `unknown`.\n5. ! **No P0/P1 findings** — the triple-tier (+ Tier 2.5) detector reports zero P0 and zero P1 findings (P2 issues are non-blocking style suggestions and do not gate the loop).\n\n! All five fields MUST hold on the SAME single fresh fetch. The agent MUST NOT assemble a \"pass\" by combining a terminal check-run observed on one poll with a confidence parsed on an earlier poll — the read is atomic per the SHA-pinned-AT-READ-TIME rule above.\n\n? If the bot says \"all prior issues resolved\" but lists new issues, treat it as one final batch — not the start of another loop. Go back to Step 2 one more time, re-evaluate this all-of, then stop.\n\n⊗ Exit the loop on a confidence number alone while the check run is non-terminal (`queued` / `in_progress` / `cancelled` / `timed_out` / `stale` / `action_required`) — a confidence score is NOT a verdict without a terminal check-run AND a HEAD-matching completion marker (#1259).\n⊗ Exit the loop against a reviewed SHA that lags the current HEAD — a partial or stale review MUST resolve to `unknown`, never to a pass (#1259).\n\nIf the exit predicate is not met (any field `unknown`), go back to Step 2.\n\n## Pre-Merge Re-Poll Gate (#1259)\n\n! Immediately before any `gh pr merge` invocation, the agent MUST re-fetch reviewer state ONE more time — a fresh `gh pr view <number> --comments`, a fresh `gh api repos/<owner>/<repo>/commits/<HEAD>/check-runs`, and a fresh HEAD-SHA read — and re-evaluate the Step 6 fail-closed all-of against that fresh fetch. The exit-condition pass recorded at the end of the review loop is NOT sufficient authorization to merge: review state can go stale between the loop's last poll and the merge call (a new push, a Greptile re-trigger, a service-side check-run reset).\n\n! Treat the re-poll and the `gh pr merge` as an atomic freshness window. If the re-poll shows ANY field `unknown`, ABORT the merge and return to Step 2.\n\n⊗ Call `gh pr merge` on the strength of a review verdict observed earlier in the loop without an immediately-preceding re-poll that re-satisfies the Step 6 all-of — merging on cached review state is forbidden (#1259).\n\n### Informal-clean missing canonical fields (#1543)\n\nGreptile can post a **separate** informal clean reply that says prior issues are resolved and the current diff is clean while omitting the canonical rolling-summary fields Directive merge gates require: `Last reviewed commit:` and `Confidence Score: X/5`. `task pr:merge-ready` and `task swarm:verify-review-clean` correctly refuse merge-ready in this state -- prose alone cannot prove review currency or confidence.\n\n! When the latest Greptile bot comment is found, reports P0=0 and P1=0, but BOTH canonical fields are unparsed, classify the state as **`informal-clean missing-canonical-fields`** (see `task pr:merge-ready` diagnostic output) instead of treating it as \"review still writing\" or silently polling.\n\n! Recovery for informal-clean missing canonical fields -- route to ONE of these operator actions; do NOT keep polling:\n\n1. Comment `@greptileai review` on the PR to retrigger a canonical rolling summary on the current HEAD.\n2. Wait for Greptile to edit its primary rolling-summary comment with both canonical fields, then re-run `task pr:merge-ready -- <N>`.\n3. Document an explicit operator override per `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 1 (merge with rationale in the merge commit body).\n\n⊗ Treat informal clean Greptile prose (`current diff is clean`, `looks solid`, `no new issues`) as merge-ready without canonical `Last reviewed commit:` and `Confidence Score: X/5` evidence.\n\n⊗ Keep polling silently when `task pr:merge-ready` reports the informal-clean missing-canonical-fields diagnostic -- this is a blocked recovery state, not a late-arriving review.\n\n~ Swarm pollers MUST surface this state via the `### (6) INFORMAL-CLEAN` terminal exit in `templates/swarm-greptile-poller-prompt.md` instead of falling through to generic `(4) TIMEOUT` or `(5) STALL`.\n\n## Submitting GitHub Reviews\n\n! When submitting PR reviews via the GitHub MCP tool, always use `pull_request_review_write` with method `create` and the appropriate event:\n\n- `APPROVE` — formally approve the PR (shows green \"Approved\" status)\n- `REQUEST_CHANGES` — block the PR with requested changes\n- `COMMENT` — review feedback without approving or blocking\n\n⊗ Use `add_issue_comment` for review notes — that creates a regular comment, not a formal review. Review notes must always go in the review body via `pull_request_review_write`.\n\n## GitHub Interface Selection\n\n~ Use the most efficient interface for the task:\n\n- **MCP GitHub tool** — structured/programmatic operations (querying issues, creating PRs, bulk operations, filtering data)\n- **GitHub CLI (`gh`)** — quick ad-hoc commands and direct shell integration\n\nChoose whichever minimizes steps and maximizes clarity for the given task.\n\n~ When MCP is unavailable (agents without MCP tools in their dispatch environment, including `start_agent` / `spawn_subagent` (\"grok-build\") cases, cloud agents, `oz agent run`), `gh` CLI is sufficient as the sole interface. The dual-source requirement (MCP + `gh`) in Step 1 applies only when both are available -- agents without MCP access should use `gh pr view --comments` and `gh api` as their primary and only review detection surface. Runtime capability detection (swarm Phase 3 matrix) informs both orchestration tier and MCP surface choice.\n\n## Framework Events Emitted Here\n\n! When the user replies `yes` / `confirmed` / `approve` on a ready-to-merge PR thread (Phase 5 -> 6 gate per the canonical #642 workflow comment), emit a `plan:approved` framework event via `task lifecycle:event` so the approval is captured as a structural artifact rather than prose-only:\n\n```\ntask lifecycle:event -- emit plan:approved \\\n --plan-ref https://github.com/<owner>/<repo>/pull/<N> \\\n --approver <github-login> \\\n --approval-phrase <yes|confirmed|approve> \\\n --pr-number <N>\n```\n\n? Downstream consumers of `plan:approved` (auto-merge bots, status updates, audit reporting) are explicitly deferred to follow-up work; this event currently emits a record only (#635 events behavioral wiring).\n\n## Post-Merge Verification\n\n! After a PR is squash-merged, verify that all referenced issues were actually closed. Squash merges can silently fail to process closing keywords (`Closes #N`, `Fixes #N`) from the PR body (#167).\n\n1. ! For each issue referenced with a closing keyword in the PR body, run:\n ```\n gh issue view <N> --json state --jq .state\n ```\n2. ! If the issue state is not `CLOSED`, close it manually with a comment referencing the merged PR:\n ```\n gh issue close <N> --comment \"Closed by #<PR> (squash merge — auto-close did not trigger)\"\n ```\n3. ~ This step mirrors `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 2 and applies to ALL PR merges, not just swarm runs.\n4. ! For PRs that referenced any umbrella / staying-OPEN issue (`Refs #N`), the INVERSE check applies: any protected issue that auto-closed MUST be reopened with a comment citing #701 and the merged PR. See `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 1 protected-issue reopen sweep and `meta/lessons.md` `## GitHub Closing-Keyword False-Positive Layer 3` for the persistent `closingIssuesReferences` link case (Layer 3, #701).\n\n## Anti-Patterns\n\n- ⊗ Route PR shepherding to Cursor global `babysit` on Deft-managed repos when `.deft/core/` is installed -- use this review-cycle skill instead (#2261)\n- ⊗ Treat a passing SLizard/Greptile check run, a non-blocking review comment, or an ad hoc fix commit as the review-cycle exit predicate -- Step 6 fail-closed all-of (#1259) and multi-reviewer registry triage (#769) still apply\n- ⊗ Push individual fix commits per finding\n- ⊗ Start fixing before analyzing ALL findings\n- ⊗ Rely on the bot to catch syntax errors in structured data files\n- ⊗ Re-trigger a bot review before the previous one has updated\n- ⊗ Report \"all comments resolved\" without checking both `gh pr view --comments` and a second source (`get_review_comments` via MCP, or `gh api` fallback when MCP is unavailable)\n- ⊗ Use `add_issue_comment` for formal review submission\n- ⊗ Commit or push Phase 1 audit fixes independently — always batch with Phase 2 fixes\n- ⊗ Proceed to Phase 2 while any Phase 1 prerequisite is unmet\n- ⊗ Rely solely on `pulls/{number}/reviews` to detect whether Greptile has reviewed the latest commit — Greptile may update via an edited issue comment instead of a new review object\n- ⊗ Push additional commits while Greptile is reviewing the current head — each push re-triggers Greptile and resets the review clock\n- ⊗ Use blocking `Start-Sleep` shell loops or `time.sleep()` loops to poll for review updates when Approach 1 or 2 is available -- Approach 3 (blocking loop) is permitted only as a last resort with user warning\n- ⊗ Poll more frequently than every 20 seconds -- use a real delay between checks, not back-to-back calls; adaptive cadence (20-30s / 60s / 90s) replaces the fixed 60s minimum\n- ⊗ Stop and ask the user whether to continue after pushing -- the review/fix loop MUST run autonomously to the exit condition\n- ⊗ Push fix commits without scanning changed lines for untested code paths — always check test coverage before pushing\n- ⊗ Push a fix commit that addresses fewer findings than the current Greptile review surfaces — if Greptile flags 3 issues, all 3 must be fixed in one commit before pushing\n- ⊗ Push after fixing a P1 without first checking whether the same Greptile review contains additional P0 or P1 findings\n- ⊗ Assume squash merge auto-closed referenced issues — always verify with `gh issue view` after merge (#167)\n- ⊗ Assume Approach 2 (yield-between-polls) produces a self-sustaining polling loop -- yielding ends the agent's turn with no self-wake; swarm agents will silently stop polling\n- ⊗ Skip the second review source (MCP or `gh api` fallback) without probing for MCP capability and documenting the fallback used\n- ⊗ Run a partial test suite instead of `task check` without documenting the pre-existing failure reason and open issue number in the PR body\n- ⊗ Create a PR without running `skills/deft-directive-pre-pr/SKILL.md` first -- the pre-PR quality loop catches issues before they reach the reviewer\n- ⊗ Activate Approach 3 (blocking `Start-Sleep` loop) without first warning the user that it will lock the conversation pane and receiving confirmation\n- ⊗ Exit the review loop on a Greptile confidence number alone while the check run is non-terminal -- a confidence score is NOT a verdict without a terminal check-run (`completed` + `{success, neutral}`) AND a HEAD-matching `Last reviewed commit:` completion marker (#1259)\n- ⊗ Call `gh pr merge` on cached/earlier review state without an immediately-preceding pre-merge re-poll that re-satisfies the Step 6 fail-closed all-of (#1259)\n",
|
|
238
252
|
"frontmatter_extra": null
|
|
239
253
|
},
|
|
240
254
|
{
|
package/scm/github.md
CHANGED
|
@@ -289,6 +289,14 @@ Following a v1.0.0 release, commits:
|
|
|
289
289
|
- ! Run `task check` for quality gates
|
|
290
290
|
- ~ Upload coverage reports
|
|
291
291
|
|
|
292
|
+
**Framework CI runners (#2672)**:
|
|
293
|
+
- ! `deftai/directive` required CI prefers Blacksmith (`blacksmith-4vcpu-ubuntu-2404`) for TypeScript and Go cost
|
|
294
|
+
- ! Capacity watchdog (~20 minute budget): if a Blacksmith primary job stays `queued` with `runner_name` null and no `started_at`, cancel that queued attempt (concurrency cancel-in-progress) and run the same suite on `ubuntu-latest`
|
|
295
|
+
- ! Branch-protection required check names (`TypeScript (build + lint + test)`, `Go (test + build)`) live **only** on the aggregator jobs — never on primary/failover lane names
|
|
296
|
+
- ⊗ Fail over `in_progress` jobs (execution hangs) — those stay timeout + fix (#2652); capacity failover is queue-stall only
|
|
297
|
+
- ! Consumer scaffolds and `npm-publish.yml` stay on GitHub-hosted `ubuntu-latest` (Blacksmith is opt-in for consumer orgs; npm `--provenance` requires GH-hosted)
|
|
298
|
+
- ! Agents seeing `runner_capacity_stall` / `RUNNER_CAPACITY_STALL` MUST wait for auto-failover — ⊗ `--skip-ci` as a capacity remedy
|
|
299
|
+
|
|
292
300
|
**Security**:
|
|
293
301
|
- ! Use GitHub Secrets for CI/CD credentials
|
|
294
302
|
- ⊗ Commit secrets to repo
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deft-directive-product-signal
|
|
3
|
+
description: >-
|
|
4
|
+
Consented agent-driven product check-in (pulse + portrait) with enable and
|
|
5
|
+
consent gates, minimized local ledger attach, and private GitHub sink
|
|
6
|
+
submit. Defaults off.
|
|
7
|
+
---
|
|
8
|
+
<!-- AUTO-GENERATED by task packs:render -- DO NOT EDIT MANUALLY -->
|
|
9
|
+
<!-- Purpose: rendered skill -->
|
|
10
|
+
<!-- Source of truth: packs/skills/skills-pack-0.1.json -->
|
|
11
|
+
<!-- Regenerate with: task packs:render -->
|
|
12
|
+
<!-- Edit the source, not this file. Slice instead of loading every SKILL.md: task packs:slice skills by-trigger --trigger <kw> (or list) -->
|
|
13
|
+
|
|
14
|
+
# Deft Directive Product Signal — consented check-in (#2693)
|
|
15
|
+
|
|
16
|
+
Agent-driven qualitative product-improvement signal for partner/trial installs. Lightweight interview, minimized local summaries (value/health/helped), optional skillsSummary hook — submits to private `deftai/product-signal` when enable + consent gates pass.
|
|
17
|
+
|
|
18
|
+
Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
|
|
19
|
+
|
|
20
|
+
## When to Use
|
|
21
|
+
|
|
22
|
+
- Operator says `product check-in`, `product pulse`, `partner feedback`, or `product signal`
|
|
23
|
+
- Partner onboarding after ops enables `plan.policy.productSignal`
|
|
24
|
+
- ⊗ Do NOT invoke from session:start or ambient rituals when disabled (#2693 D3)
|
|
25
|
+
|
|
26
|
+
## Preconditions
|
|
27
|
+
|
|
28
|
+
- ! `plan.policy.productSignal.enabled` MUST be true (`task policy:show --field=productSignal`)
|
|
29
|
+
- ! Install-level consent MUST be recorded before outbound submit (`task product-signal:status`)
|
|
30
|
+
- ⊗ Prompt or submit when `enabled` is false
|
|
31
|
+
- ⊗ Block story work on sink errors — fail open (#2693 D16/D18)
|
|
32
|
+
|
|
33
|
+
## Phase 1 — Gate
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
task product-signal:status
|
|
37
|
+
task policy:show -- --field=productSignal
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- ! If disabled: soft-skip with one line; exit skill
|
|
41
|
+
- ! If headless/CI: fail open — no consent/interview prompts (#2693 D16)
|
|
42
|
+
|
|
43
|
+
## Phase 2 — First-time consent (interactive only, D17)
|
|
44
|
+
|
|
45
|
+
When enabled, interactive, and consent missing/revoked, ask:
|
|
46
|
+
|
|
47
|
+
> May we collect usage metrics and related session signal from this install to improve Directive? This can include a short check-in plus minimized local summaries (value/health and related ledgers). Nothing is sent while this path is off or without your consent.
|
|
48
|
+
>
|
|
49
|
+
> If you are unsure, please check with your company before saying no — they are a Directive partner and may already expect this signal to be shared.
|
|
50
|
+
>
|
|
51
|
+
> Reply **yes** to consent, or **no** to decline.
|
|
52
|
+
|
|
53
|
+
- **yes** → `task product-signal:consent -- --grant`; continue
|
|
54
|
+
- **no** → soft-skip; do not badger every turn
|
|
55
|
+
|
|
56
|
+
## Phase 3 — Interview (≤3 answers + optional NPS)
|
|
57
|
+
|
|
58
|
+
- ! Ask at most three short questions plus optional NPS (0–10)
|
|
59
|
+
- ~ Target ≤30s human time beyond first-time consent
|
|
60
|
+
- ⊗ Paste secrets, paths, raw logs, or full USER.md into answers
|
|
61
|
+
|
|
62
|
+
## Phase 4 — Assemble + validate + submit
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
task product-signal:submit -- --surface pulse --dry-run
|
|
66
|
+
task product-signal:submit -- --surface pulse --nps 9
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- ! On validation failure: show reason; no partial write
|
|
70
|
+
- ! On sink ACL/network errors: soft-skip with hint; do not fail caller (#2693 D18)
|
|
71
|
+
- ! Report issue URL on success
|
|
72
|
+
|
|
73
|
+
## Private gaps (D19)
|
|
74
|
+
|
|
75
|
+
- ! Record bugs/gaps as `Gap:` comments on the standing **pulse** thread (enable+consent sufficient)
|
|
76
|
+
- ! Public promote to `deftai/directive` remains hard-confirm via `deft-directive-feedback` / `task feedback:file`
|
|
77
|
+
|
|
78
|
+
## Revoke
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
task product-signal:consent -- --revoke
|
|
82
|
+
# and/or disable: set plan.policy.productSignal.enabled false
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Anti-Patterns
|
|
86
|
+
|
|
87
|
+
- ⊗ Ambient consent nag when disabled
|
|
88
|
+
- ⊗ Fail-closed headless behavior
|
|
89
|
+
- ⊗ Sticky forever-fail when sink is down
|
|
90
|
+
- ⊗ Auto-file public framework-gap issues from pulses
|
|
91
|
+
|
|
92
|
+
## Exit
|
|
93
|
+
|
|
94
|
+
deft-directive-product-signal complete — exiting skill.
|
|
@@ -81,6 +81,17 @@ The release pipeline's Step 9/10/11 git mutations carry the bypass in subprocess
|
|
|
81
81
|
|
|
82
82
|
! Validate the local + remote state before any irreversible action.
|
|
83
83
|
|
|
84
|
+
|
|
85
|
+
### Parallel prep — #1880 Gap D (#2692)
|
|
86
|
+
|
|
87
|
+
! Phase 1 long steps (`task reconcile:issues -- --apply-lifecycle-fixes`, cache refresh when ritual-stale, `task ci:local` / `task check`) and Phase 3 `task release:e2e` MUST be backgrounded or subagent-dispatched when the host supports it (Cursor: Task tool `run_in_background: true`), with progress surfaced via DONE/heartbeat — same ownership as review-cycle / merge-ready workers (#1880 Gap D). The operator conversation MUST stay interactive for version magnitude confirmation, `--summary`, and the Phase 2 dry-run `yes`/`back`/`quit` gate while prep runs.
|
|
88
|
+
|
|
89
|
+
! **Checklist:** Phase 1 prep parallelized — long prep started in background before (or while) collecting version magnitude / summary / npm irrevocability disclosure.
|
|
90
|
+
|
|
91
|
+
! On Windows PowerShell, do NOT wrap long task output in `Select-Object -Last` (it buffers until the process exits); stream to the terminal or log to a file and read incrementally. See `scm/github.md` § #2646 / Windows encoding guidance for related PS pitfalls.
|
|
92
|
+
|
|
93
|
+
⊗ Foreground-block the operator chat on reconcile / `ci:local` / `release:e2e` when background dispatch is available (#1880 Gap D / #2692).
|
|
94
|
+
|
|
84
95
|
~ **Frozen Go-installer bridge (#1912 / #1972 / #1987):** by default a release tag *above* the frozen line (the `LAST_GO_INSTALLER` constant in `packages/core/src/legacy-bridge/sot.ts`) will NOT rebuild the 6 Go binaries -- the CI `freeze-gate` job in `.github/workflows/release.yml` skips the build (the run stays green; npm still ships from the separate `npm-publish.yml`). If this release must rebuild the Go installer, follow the runbook in [`docs/RELEASING.md`](../../../docs/RELEASING.md) § Frozen Go-installer bridge: roll `LAST_GO_INSTALLER` forward to the cut tag BEFORE tagging (pinning to the exact cut tag both releases the gate AND re-freezes at the new line), then see that section's "After the release" step for the re-pin.
|
|
85
96
|
|
|
86
97
|
1. ! Verify the operator is on the configured base branch (default `master`) and the working tree is clean
|
|
@@ -123,6 +134,8 @@ The dry-run prints `[N/13] <step>... DRYRUN (would <action>)` for every pipeline
|
|
|
123
134
|
|
|
124
135
|
! Invoke `task release:e2e` against an auto-created temp repo to verify the full pipeline shape works end-to-end before touching the real repo.
|
|
125
136
|
|
|
137
|
+
! **#1880 Gap D (#2692):** `task release:e2e` is a long-running step — MUST background / subagent-dispatch it when the host supports it so Phase 2 confirmation and other human gates stay interactive. Do not wrap its output in PowerShell `Select-Object -Last`.
|
|
138
|
+
|
|
126
139
|
```
|
|
127
140
|
task release:e2e
|
|
128
141
|
```
|
|
@@ -281,6 +294,8 @@ Where `<one-line guidance>` is one of:
|
|
|
281
294
|
|
|
282
295
|
## Anti-Patterns
|
|
283
296
|
|
|
297
|
+
- ⊗ Foreground-block the operator chat on Phase 1 long prep (`reconcile:issues`, cache refresh, `ci:local` / `check`) or Phase 3 `release:e2e` when background / subagent dispatch is available (#1880 Gap D / #2692) — the interactive channel must stay free for version confirmation, `--summary`, and the Phase 2 dry-run gate
|
|
298
|
+
- ⊗ Wrap long release-prep task output in PowerShell `Select-Object -Last` — it buffers until exit and makes the session look hung (#2692)
|
|
284
299
|
- ⊗ Run `task release` without a Phase 2 dry-run preview -- the dry-run is the only safe place to catch a bad version, malformed CHANGELOG, or wrong base branch
|
|
285
300
|
- ⊗ Skip Phase 3 (e2e rehearsal) on the assumption that "the dry-run is enough" -- the e2e harness catches gh-CLI auth issues, repo permission gaps, and pipeline-shape regressions that the dry-run cannot detect
|
|
286
301
|
- ⊗ Pass `--no-draft` to `task release` without explicit operator opt-in -- the default-draft contract is the foundation of the safety hardening surface
|
|
@@ -198,6 +198,15 @@ Both commands extract the "Comments Outside Diff" section with surrounding conte
|
|
|
198
198
|
|
|
199
199
|
~ Surface the holdout to the user/parent on the first stable `ci_failures` probe (fail-loud), not after burning `max-wait-minutes`. See also [`templates/swarm-greptile-poller-prompt.md`](../../templates/swarm-greptile-poller-prompt.md) CLEAN gate evaluation (#1039).
|
|
200
200
|
|
|
201
|
+
|
|
202
|
+
### Runner capacity stall (`runner_capacity_stall` / #2672)
|
|
203
|
+
|
|
204
|
+
! Framework CI prefers Blacksmith with a timed GH-hosted failover (~20 minute stall budget). When `task pr:watch --json` / `task pr:merge-ready` reports `ci_ready_state=runner_capacity_stall` (or verdict `RUNNER_CAPACITY_STALL`, exit 2): **wait for auto-failover** to the ubuntu-latest lane and the authoritative aggregator check. Do **not** invent `--skip-ci` or merge with pending required checks.
|
|
205
|
+
|
|
206
|
+
! `runner_capacity_stall` is distinct from ordinary `not_ready_yet` (under budget / `in_progress`) and from execution hangs (#2652). Capacity stall means required checks stayed `queued` with no runner claimed past the budget.
|
|
207
|
+
|
|
208
|
+
⊗ Use `--skip-ci` / merge-with-pending because CI is capacity-stalled — the failover path is the unblock; skip-ci is an incident-only release escape hatch (#2652), not a runner-capacity remedy.
|
|
209
|
+
|
|
201
210
|
### Stall Detection Rubric (#564)
|
|
202
211
|
|
|
203
212
|
! Track per poll: `startedAt` (timestamp of the first observation of the IN_PROGRESS check run for the current commit) and `commit.oid` (head SHA being reviewed). Both fields MUST be re-recorded every time the head SHA changes -- the rubric measures elapsed time on a single commit, not across the whole review cycle.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
version: '3'
|
|
2
|
+
|
|
3
|
+
vars:
|
|
4
|
+
DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
|
|
5
|
+
|
|
6
|
+
tasks:
|
|
7
|
+
status:
|
|
8
|
+
desc: "Show product-signal enable/consent/sink status (#2693). -- task product-signal:status"
|
|
9
|
+
dir: '{{.USER_WORKING_DIR}}'
|
|
10
|
+
deps:
|
|
11
|
+
- task: :engine:_ts-build
|
|
12
|
+
cmds:
|
|
13
|
+
- task: :engine:invoke
|
|
14
|
+
vars:
|
|
15
|
+
ENGINE_CMD: 'product-signal status --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
|
16
|
+
|
|
17
|
+
enable:
|
|
18
|
+
desc: "Enable plan.policy.productSignal after capability-cost disclosure (#2693). -- task product-signal:enable -- [--confirm]"
|
|
19
|
+
dir: '{{.USER_WORKING_DIR}}'
|
|
20
|
+
deps:
|
|
21
|
+
- task: :engine:_ts-build
|
|
22
|
+
cmds:
|
|
23
|
+
- task: :engine:invoke
|
|
24
|
+
vars:
|
|
25
|
+
ENGINE_CMD: 'product-signal enable --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
|
26
|
+
|
|
27
|
+
consent:
|
|
28
|
+
desc: "Grant or revoke install-level product-signal consent (#2693). -- task product-signal:consent -- --grant|--revoke"
|
|
29
|
+
dir: '{{.USER_WORKING_DIR}}'
|
|
30
|
+
deps:
|
|
31
|
+
- task: :engine:_ts-build
|
|
32
|
+
cmds:
|
|
33
|
+
- task: :engine:invoke
|
|
34
|
+
vars:
|
|
35
|
+
ENGINE_CMD: 'product-signal consent {{.CLI_ARGS}}'
|
|
36
|
+
|
|
37
|
+
submit:
|
|
38
|
+
desc: "Submit pulse/portrait payload to private sink when gated (#2693). -- task product-signal:submit -- [--surface pulse|portrait] [--dry-run] [--json]"
|
|
39
|
+
dir: '{{.USER_WORKING_DIR}}'
|
|
40
|
+
deps:
|
|
41
|
+
- task: :engine:_ts-build
|
|
42
|
+
cmds:
|
|
43
|
+
- task: :engine:invoke
|
|
44
|
+
vars:
|
|
45
|
+
ENGINE_CMD: 'product-signal submit --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
|
46
|
+
|
|
47
|
+
bootstrap-sink:
|
|
48
|
+
desc: "Create private deftai/product-signal repo and bootstrap D20 labels (#2693). -- task product-signal:bootstrap-sink [-- --dry-run]"
|
|
49
|
+
dir: '{{.USER_WORKING_DIR}}'
|
|
50
|
+
deps:
|
|
51
|
+
- task: :engine:_ts-build
|
|
52
|
+
cmds:
|
|
53
|
+
- task: :engine:invoke
|
|
54
|
+
vars:
|
|
55
|
+
ENGINE_CMD: 'product-signal bootstrap-sink {{.CLI_ARGS}}'
|