@deftai/directive-content 0.96.0 → 0.98.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.
Files changed (38) hide show
  1. package/.agents/skills/deft-directive-portfolio-priority/SKILL.md +11 -0
  2. package/Taskfile.yml +6 -0
  3. package/UPGRADING.md +9 -6
  4. package/commands.md +17 -2
  5. package/contracts/test-boundary.md +18 -0
  6. package/docs/consumer-check-contract.md +64 -0
  7. package/docs/deft-directive-disable.md +6 -0
  8. package/docs/delivery-attempt.md +173 -0
  9. package/docs/openclaw-agent-host.md +36 -1
  10. package/docs/operator-log-hygiene-checklist.md +57 -0
  11. package/docs/operator-log-hygiene-consumer-pack-stub.md +75 -0
  12. package/docs/scope-provenance.md +54 -0
  13. package/docs/test-boundary.md +43 -0
  14. package/main.md +46 -0
  15. package/meta/philosophy.md +10 -0
  16. package/package.json +1 -1
  17. package/packs/patterns/patterns-pack-0.1.json +10 -0
  18. package/packs/skills/skills-pack-0.1.json +22 -5
  19. package/patterns/operator-log-hygiene.md +130 -0
  20. package/scm/github.md +26 -0
  21. package/skills/deft-directive-article-review/SKILL.md +17 -6
  22. package/skills/deft-directive-build/SKILL.md +41 -0
  23. package/skills/deft-directive-portfolio-priority/SKILL.md +144 -0
  24. package/skills/deft-directive-release/SKILL.md +22 -5
  25. package/skills/deft-directive-review-cycle/SKILL.md +150 -0
  26. package/skills/deft-directive-swarm/SKILL.md +14 -0
  27. package/skills/deft-directive-swarm/references/core-ops.md +14 -2
  28. package/skills/deft-directive-swarm/references/core-phase-0.md +34 -1
  29. package/skills/deft-directive-swarm/references/core-phase-3.md +6 -0
  30. package/skills/deft-directive-swarm/references/core-phase-4.md +21 -2
  31. package/skills/deft-directive-swarm/references/core-phase-5-6.md +17 -0
  32. package/skills/deft-directive-swarm/references/host-openclaw.md +4 -0
  33. package/tasks/engine-invoke.cjs +22 -17
  34. package/tasks/engine-invoke.test.cjs +30 -0
  35. package/tasks/verify.yml +30 -0
  36. package/templates/agent-prompt-preamble.md +8 -0
  37. package/templates/agents-entry.md +10 -0
  38. package/templates/swarm-greptile-poller-prompt.md +4 -2
@@ -0,0 +1,130 @@
1
+ # Operator-log hygiene — structured operator-facing logs (#1940)
2
+
3
+ Guidance for consumer services that humans operate. Declare and keep
4
+ structured, operator-facing logs so outages are diagnosable without a
5
+ week of reactive firefighting.
6
+
7
+ Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
8
+
9
+ **Load when:** implementing or reviewing services with operator dashboards,
10
+ WARN/ERROR paths operators triage, multi-process / parent-child workers,
11
+ or story AC / probe locked decisions about logging.
12
+
13
+ **Not this pattern:**
14
+ - Directive Product Insights / remote usage analytics — #2603
15
+ - LLM-call telemetry — `patterns/llm-app.md`, `tools/telemetry.md` / #481
16
+ - Framework DDD glossary — `glossary.md` / `deft-directive-glossary`
17
+
18
+ **⚠️ See also**:
19
+ - [../docs/operator-log-hygiene-checklist.md](../docs/operator-log-hygiene-checklist.md) — copy-paste build/probe checklist
20
+ - [../docs/operator-log-hygiene-consumer-pack-stub.md](../docs/operator-log-hygiene-consumer-pack-stub.md) — optional consumer Taskfile/validator stub
21
+ - [./llm-app.md](./llm-app.md) — LLM-specific observability (different lane)
22
+ - [../tools/telemetry.md](../tools/telemetry.md) — general telemetry guidance
23
+
24
+ ## Honest value claim
25
+
26
+ This pattern speeds **second systems**, shared language across services, and
27
+ regression prevention when you opt in. It does **not** claim it would have
28
+ avoided any first production week, or that the framework knows your field
29
+ names. Log **shape** stays consumer-owned.
30
+
31
+ ## Failure modes (case study)
32
+
33
+ When operator logging is left implicit, the same six deficiencies recur:
34
+
35
+ 1. **Happy-path-only terminals** — sessions finish externally (checks, UI)
36
+ while durable logs omit tail events on skip, timeout, supersede, cancel,
37
+ or error exits.
38
+ 2. **Missing correlation context** — multi-worker / parent-child designs ship
39
+ without slot, phase, or job-scoped IDs; forensics need archaeology.
40
+ 3. **Infrastructure treated as debug-only** — log rotation, flush-before-exit,
41
+ IPC handoff, and boot supervision lack fail-open guards and structured
42
+ events until production crashes.
43
+ 4. **Opaque operator text** — WARN/ERROR carry ticket numbers and engineer
44
+ `msg` strings; operators cannot triage severity or whether work continued.
45
+ 5. **Dishonest parent/child contracts** — parent logs imply success when IPC
46
+ never delivered; metrics and pollers infer wrong outcomes.
47
+ 6. **Hygiene lags the log line** — glossary and plain-English summaries land
48
+ in follow-up issues, not the same PR as the log line.
49
+
50
+ ## Positive rules
51
+
52
+ - ! MUST emit terminal / completion events on **all** exit paths for a unit
53
+ of work that has operator-visible lifecycle (success, skip, timeout,
54
+ supersede, cancel, failure) — not only the happy path
55
+ - ! MUST include correlation context for multi-process or pool designs
56
+ (for example job id, slot, phase, parent id) so one incident can be
57
+ reconstructed without log archaeology
58
+ - ! MUST treat infrastructure paths that affect operator truth (rotation,
59
+ flush, IPC handoff, boot supervision) as first-class: fail-open where
60
+ possible, with structured events when they degrade
61
+ - ~ SHOULD give operator-facing WARN/ERROR a stable, queryable shape
62
+ (stable event id + short operator-readable summary fields are common)
63
+ without requiring a Directive-owned schema
64
+ - ~ SHOULD update operator log glossary / plain-English docs in the **same
65
+ PR** that adds or changes operator-facing log lines
66
+ - ? MAY wire a consumer-owned validator into *your* `task check` aggregate
67
+ (see the consumer pack stub) — optional, never default-on in core
68
+
69
+ ## Anti-patterns
70
+
71
+ - ⊗ Terminal events only on success while skip/timeout/error paths stay silent
72
+ - ⊗ Multi-worker systems with no job/slot/phase correlation on log lines
73
+ - ⊗ Treating rotation, flush, IPC, or boot supervision as "debug detail"
74
+ with no structured signal when they fail
75
+ - ⊗ Operator-facing WARN/ERROR that only carry ticket numbers or engineer
76
+ free-text with no stable event identity
77
+ - ⊗ Parent logs that claim success when the child message was never delivered
78
+ - ⊗ Landing operator log lines without same-PR hygiene (docs/glossary/shape)
79
+ - ⊗ Assuming Directive core will enforce your log schema or fail `deft check`
80
+ for every consumer by default
81
+
82
+ ## Explicit non-goals (thin v1)
83
+
84
+ - ⊗ No core `plan.observability` / setup Phase 2 interview defaults in this
85
+ issue's close path
86
+ - ⊗ No core `deft check` hard-fail for consumers who never opted in
87
+ - ⊗ No prescription or validation of a fixed field set (`operatorSummary`,
88
+ glossary JSON schema, etc.) inside Directive core
89
+ - ⊗ No default-on magic for all "service" project types
90
+ - ⊗ No import of any external project's glossary JSON or validate task as
91
+ framework SoT
92
+ - ⊗ Not Product Insights (#2603) and not LLM-call telemetry (#481)
93
+
94
+ Hard-fail enforcement is allowed only when a **consumer** wires their own
95
+ gate. Log shape remains consumer-owned.
96
+
97
+ ## External reference (SLizard — reference only)
98
+
99
+ A production case study lives in **deftai/slizard** (June 2026 postmortem and
100
+ follow-on hygiene work). Cite it as an **external reference implementation**,
101
+ not as Directive schema:
102
+
103
+ | External pointer | Role |
104
+ |------------------|------|
105
+ | SLizard `docs/operator-log-hygiene.md` | Project hygiene write-up |
106
+ | SLizard `docs/operator-log-glossary.json` | Consumer-owned glossary shape |
107
+ | SLizard `task operator-log:validate` | Consumer ship-gate example |
108
+ | SLizard origin issues (e.g. #1394 LD-8, #1402) | How hygiene landed under fire |
109
+
110
+ - ! MUST treat those paths and schemas as **pointers only**
111
+ - ⊗ MUST NOT copy SLizard glossary JSON or validate schema into Directive
112
+ core as a mandatory contract
113
+ - ⊗ MUST NOT close framework work by importing that repo's field names as
114
+ universal requirements
115
+
116
+ ## Build and probe
117
+
118
+ Copy-paste AC / probe bullets:
119
+ [`docs/operator-log-hygiene-checklist.md`](../docs/operator-log-hygiene-checklist.md).
120
+
121
+ Optional consumer Taskfile + validator skeleton:
122
+ [`docs/operator-log-hygiene-consumer-pack-stub.md`](../docs/operator-log-hygiene-consumer-pack-stub.md).
123
+
124
+ ## Cross-references
125
+
126
+ - #1940 — consumer operator-log hygiene thin v1 (this pattern)
127
+ - #2603 — Product Insights (different lane)
128
+ - #481 — LLM-specific observability (different lane)
129
+ - #1516 — product-pulse health report (adjacent ops UX, not log contract)
130
+ - #829 — skill usage telemetry (framework internal)
package/scm/github.md CHANGED
@@ -399,6 +399,32 @@ Following a v1.0.0 release, commits:
399
399
  - ! Consumer scaffolds and `npm-publish.yml` stay on GitHub-hosted `ubuntu-latest` (Blacksmith is opt-in for consumer orgs; npm `--provenance` requires GH-hosted)
400
400
  - ! Agents seeing `runner_capacity_stall` / `RUNNER_CAPACITY_STALL` MUST wait for auto-failover — ⊗ `--skip-ci` as a capacity remedy
401
401
 
402
+ ### Platform status probe + outage attribution (#3180)
403
+
404
+ `pr:watch` / `pr:merge-ready` weather codes (`ci_never_scheduled`, `runner_capacity_stall`, `ci_cancelled_no_failover`, `ci_failures` — see #3167) classify **forge check-run shape**. They do **not** attribute the hold to an upstream platform outage vs repo config. When weather codes fire, CI never starts for HEAD, or many PRs share an empty-check pattern:
405
+
406
+ ! **MUST probe public status pages** (v1: open in browser / operator view; gates surface static URLs — no network fetch required):
407
+
408
+ 1. **GitHub Status** (Actions, Webhooks): https://www.githubstatus.com/
409
+ 2. **Blacksmith Status** (and any Github→Actions / Webhooks mirrors shown there): https://status.blacksmith.sh/
410
+
411
+ **Attribution table** (`attribution` enum for handoffs):
412
+
413
+ | Observation | `attribution` | Agent action |
414
+ |-------------|---------------|--------------|
415
+ | GH Actions and/or Webhooks major/partial outage | `platform` | Treat as platform incident; ⊗ workflow drive-by edits; ⊗ empty-commit thrash past #3167 caps; wait + re-check runs for HEAD + local `task check` |
416
+ | Blacksmith components red while GH Actions green | `capacity` | Runner-provider incident; capacity/failover doctrine (#2672 / #3168) still applies |
417
+ | Both green + still `ci_never_scheduled` on **this PR only** | `repo_config` | Investigate workflow paths, branch filters, required-check names, Actions disabled / org policy |
418
+ | Status unclear or mixed signals | `unknown` | Cap thrash (#3167); BLOCKED with both status URLs; operator decision |
419
+
420
+ ! **Anti-thrash during attributed platform outage:** After thrash caps (max 2 re-triggers per #3167), stop automatic empty-commit / close-reopen / rebase loops. Remediation is wait + re-probe HEAD check-runs, not inventing workflow edits to "fix" a global outage.
421
+
422
+ ⊗ Merge or `--skip-ci` solely because a status page is red — status is **attribution for wait/thrash policy**, not a second branch-protection oracle (#3180 non-goal).
423
+ ⊗ Blame Blacksmith when status pages show GitHub Actions/Webhooks major outage and Blacksmith runners themselves operational.
424
+ ⊗ Edit workflows or re-push thrash to "fix" a documented global Actions/webhook outage without status-page probe.
425
+
426
+ **BLOCKED handoff fields** (extend `BLOCKED: ci_weather` in review-cycle): `platform_status_github`, `platform_status_blacksmith`, optional incident URL, `attribution: platform | capacity | repo_config | unknown`. Cross-links: #3167 (weather codes), #3168 (failover arms), #2672 (capacity stall), #2688 (Greptile CLEAN + CI holdout ownership).
427
+
402
428
  **Security**:
403
429
  - ! Use GitHub Secrets for CI/CD credentials
404
430
  - ⊗ Commit secrets to repo
@@ -112,12 +112,22 @@ Look for lessons applicable to projects that directive-guided agents build:
112
112
 
113
113
  ### Step 5: Cross-reference open issues
114
114
 
115
- - ! Run `gh issue list --repo deftai/directive --state open --limit 100` to retrieve the current open issue backlog
116
- - ! For each suggestion from Step 4, check whether an open issue already covers it — fully or partially
117
- - ! If a suggestion duplicates an open issue: drop it from the proposal and note the existing issue number
118
- - ! If a suggestion extends or relates to an open issue: flag it as "extends #N" rather than proposing a standalone new issue
115
+ Titles alone are insufficient for ownership verdicts (evidence: #3163). Body-level reads are mandatory.
116
+
117
+ - ! Shortlist open **issues** only: prefer `gh issue list --repo deftai/directive --state open --limit 100` (issues, not PRs). If using REST `gh api repos/deftai/directive/issues?state=open&per_page=100`, **exclude** entries that have a `pull_request` field GitHub's issues list mixes PRs in; never treat a PR as an owning issue for dedupe
118
+ - ! For each suggestion from Step 4, identify candidate related/owning issues (title scan, semantic match, prior citations) from the issue-only shortlist
119
+ - ! For every issue claimed as related or owning: read the **full issue body**. If the body is a stub or pointer-only, also read recent comments (REST `issues/<N>/comments`) before judging ownership
120
+ - ! Prefer local cache when present: `.deft-cache/github-issue/deftai/directive/<N>/` (or project-equivalent) may satisfy the body-read requirement offline **only when all** of: (1) usable body (and comments when the body is a stub); (2) **affirmative freshness** — readable cache age or TTL metadata is present **and** within the project's cache TTL (not expired). If age/TTL metadata is missing or unreadable, the entry is **not** body-read complete — re-fetch live when network is available; if offline with missing freshness metadata, do not claim body-level ownership from that cache hit alone. Do not skip body-level ownership solely because network is unavailable if a **complete and affirmatively fresh** cache entry exists
121
+ - ! Verify every issue number cited in the analysis **exists**, is an **issue** (not a PR), and that its **state** matches the claim (open vs closed) — anti-hallucination; fabricated or wrong-state citations are a known failure mode
122
+ - ! If a suggestion duplicates an open issue (body-level ownership): drop it from the proposal and note the existing issue number
123
+ - ! If a suggestion extends or relates to an open issue: flag it as "extends #N" / "related to #N" rather than proposing a standalone new issue
124
+ - ~ Prefer **amend/comment on an owning open issue** over filing a new issue; reserve new issues for verified-untracked findings
119
125
  - ~ Scan the open issue list for trends (e.g. a cluster of agent-safety issues, a cluster of pattern/ gaps) — use trends to sharpen framing or prioritization of remaining suggestions
120
- - ⊗ Propose a new issue for something already trackeddeduplication is mandatory
126
+ - ⊗ Decide ownership or file a "related to #N" claim from **titles only** body (and stub-comment) reads are required for related/owning claims
127
+ - ⊗ Treat pull requests from the REST issues list as ownership targets without filtering `pull_request`
128
+ - ⊗ Accept a cache hit as body-read complete when the entry lacks body (or required stub comments), lacks readable age/TTL freshness metadata, or is expired/stale — re-fetch live when network is available; without affirmative freshness do not use the cache for ownership verdicts
129
+ - ⊗ Propose a new issue for something already tracked — body-level deduplication is mandatory
130
+ - ⊗ Cite issue numbers without verifying existence and state
121
131
 
122
132
  ### Step 6: Present suggestions to the user
123
133
 
@@ -163,6 +173,7 @@ If yes, follow the thread. This may include fetching related URLs, evaluating re
163
173
  - ⊗ Summarizing without reading the full content
164
174
  - ⊗ Presenting unrated suggestions — every suggestion needs a confidence level
165
175
  - ⊗ Filing a single giant issue for all suggestions — one issue per distinct suggestion or related group
166
- - ⊗ Proposing a new issue without first checking whether it duplicates an open one
176
+ - ⊗ Proposing a new issue without first checking whether it duplicates an open one (body-level ownership, not titles alone)
177
+ - ⊗ Claiming related/owning issues from titles only, or citing issue numbers without verifying existence and state (#3163)
167
178
  - ⊗ Evaluating directive relevance without consulting the Directive Reference section above
168
179
  - ⊗ Download-and-execute installers from article or web CTAs during analysis — reject and cite `patterns/install-trust.md` (#2969)
@@ -258,6 +258,27 @@ All xBRIEFs (including those read from `xbrief/active/` and any new xBRIEFs this
258
258
 
259
259
  **Cost model (swarm-heavy path):** moves from roughly `O(commits × full-gate)` toward `O(merges × full-gate) + O(iterations × cheap-proxy)` when workers iterate with affected/static gates and run full `task check` only at PR/merge.
260
260
 
261
+ ### Dual stop — multi-iteration implement and pre-PR loops (#2442)
262
+
263
+ Multi-iteration implement-fix and pre-PR polish loops MUST carry **both** a success stop and a failure/budget stop (`main.md` `## Dual Stop Rule (#2442)`). Single-turn edits and one-shot probes are exempt.
264
+
265
+ **Defaults for this skill (override only with an explicit operator envelope or xBRIEF field):**
266
+
267
+ | Loop class | Success stop | Default failure stop |
268
+ |------------|--------------|----------------------|
269
+ | Implement / quality fix (tests, lint, typecheck, coverage, AC) | Affected/static gates green for the change; AC met | **max 5** fix iterations **or** **3** consecutive identical outcomes (same failing command + same primary error class) with no material code/config change |
270
+ | Pre-PR polish (`deft-directive-pre-pr` Read-Write-Lint-Diff) | Full pass with zero further edits | **max 3** polish passes **or** **2** consecutive no-diff / same-diff outcomes |
271
+ | Full `task check` re-run after a red merge chokepoint | `task check` green | Counts toward the implement/quality fix envelope above (do not open a separate unbounded check-retry loop) |
272
+
273
+ **On failure stop:**
274
+
275
+ - ! Halt the loop. Surface an **operator-visible halt report** with: (1) iterations attempted and which stop fired (max-iter / no-progress / budget), (2) commands and primary failure fingerprints tried, (3) what is still red or missing, (4) the human decision needed (unblock dependency, rescope AC, waive with audit, abandon).
276
+ - ! Prefer a structured `BLOCKED:` terminal (preamble §11 / #2843) when exiting a drive-to:merge-ready or parent-dispatched unit early because the envelope is exhausted.
277
+ - ⊗ Continue "one more fix" after the envelope is exhausted.
278
+ - ⊗ Reset the counter by opening a new commit, rewording the same change, or swapping workers while the same failure class remains.
279
+
280
+ **Enforcement note:** skill defaults are behavioral. Durable delivery/acceptance circuit-breaker: **#3143** `packages/core/src/delivery-attempt/` (`evaluatePreDispatch`, `.deft/delivery-attempts/`). Docs: `docs/delivery-attempt.md`. Route delivery/acceptance automatic retries through that gate; do not invent a parallel ledger in this skill.
281
+
261
282
  ## Step 3: Build Phase by Phase
262
283
 
263
284
  For each phase:
@@ -295,6 +316,23 @@ task test:coverage # >=85% or PROJECT-DEFINITION.xbrief.json override
295
316
  - ⊗ Re-run full install/session ceremony after offline seed when ritual is already complete (#3010) — use `session:ready` for recovery only.
296
317
 
297
318
 
319
+ ## Operator-log hygiene (lazy-load, #1940)
320
+
321
+ When the story touches **operator-facing** services (dashboards, multi-process
322
+ workers, WARN/ERROR operators triage):
323
+
324
+ - ~ SHOULD load `patterns/operator-log-hygiene.md` and apply the copy-paste
325
+ checklist in `docs/operator-log-hygiene-checklist.md` to story AC or probe
326
+ locked decisions before claiming logging done
327
+ - ⊗ MUST NOT treat this as Product Insights (#2603) or LLM-call telemetry
328
+ (#481) — those are different lanes
329
+ - ⊗ MUST NOT assume core `deft check` enforces a log schema by default —
330
+ consumer-owned shape; optional pack stub under
331
+ `docs/operator-log-hygiene-consumer-pack-stub.md`
332
+
333
+ Discovery keywords: operator log, operator-facing logs, observability checklist
334
+ — also indexed in `REFERENCES.md`.
335
+
298
336
  ## Coding Standards (Summary)
299
337
 
300
338
  Read full files when you need detail:
@@ -343,6 +381,7 @@ feat(phase-2): add REST API endpoints with integration tests
343
381
  - ! Lint/type errors → fix them; ≉ add ignore comments without documented reason
344
382
  - ! Scope xBRIEF ambiguous -> ask user; ⊗ guess
345
383
  - ! Scope needs changes -> propose, get approval, update the scope xBRIEF first
384
+ - ! Multi-iteration fix loops obey dual-stop defaults above (#2442); on envelope exhaustion halt with an operator-visible report -- do not thrash
346
385
 
347
386
 
348
387
  ## Probe-then-fill remote claims (#3120)
@@ -388,3 +427,5 @@ feat(phase-2): add REST API endpoints with integration tests
388
427
  - ⊗ Add a prohibition (`!` or `⊗`) without scanning the same file for conflicting softer-strength rules (`~`, `≉`) that reference the same term
389
428
  - ⊗ Invent remote PR/SHA/CI/review claims in handoff evidence without same-turn probe binding — invented-done (#3120)
390
429
  - ⊗ Fill remote ship/gate fields from memory when only local work completed; legal partial omits PR fields (#3120)
430
+ - ⊗ Run multi-iteration implement / pre-PR loops without a failure stop (max iterations and/or no-progress) or without an operator-visible halt report when the envelope is exhausted (#2442)
431
+ - ⊗ Silently continue after dual-stop failure halt — escalate; do not thrash (#2442)
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: deft-directive-portfolio-priority
3
+ description: >-
4
+ Pre-promotion portfolio prioritization: cluster competing open RFCs/issues
5
+ from the local github-issue cache, emit a propose-not-apply priority brief
6
+ (conflict matrix, shortlist, park), with epistemic citation gates, then hand
7
+ off for operator dispose. Use when the operator asks for portfolio priority,
8
+ a priority brief, competing RFCs, or to cluster open issues. Do NOT trigger
9
+ on single-item "what's next?" ranking (deft-directive-triage / triage:queue)
10
+ or post-promotion capacity prioritization (#1419/#1511).
11
+ triggers:
12
+ - portfolio priority
13
+ - priority brief
14
+ - competing RFCs
15
+ - cluster open issues
16
+ - pre-promotion portfolio
17
+ - portfolio prioritization
18
+ - rank this backlog slice
19
+ metadata:
20
+ clawdbot:
21
+ requires:
22
+ bins: ["gh"]
23
+ ---
24
+ <!-- AUTO-GENERATED by task packs:render -- DO NOT EDIT MANUALLY -->
25
+ <!-- Purpose: rendered skill -->
26
+ <!-- Source of truth: packs/skills/skills-pack-0.1.json -->
27
+ <!-- Regenerate with: task packs:render -->
28
+ <!-- Edit the source, not this file. Slice instead of loading every SKILL.md: task packs:slice skills by-trigger --trigger <kw> (or list) -->
29
+
30
+ # Deft Directive Portfolio Priority
31
+
32
+ Pre-promotion **portfolio prioritization pass**: cluster competing RFCs/issues from the local github-issue cache, name conflicts/supersession, emit a **propose-not-apply** priority brief (shortlist + park), then hand off for operator dispose. Parent process + dogfood: **#3198**. Worked example: **#3200** / [`docs/analysis/2026-08-07-portfolio-priority-brief-patterns-pilot.md`](../../../docs/analysis/2026-08-07-portfolio-priority-brief-patterns-pilot.md).
33
+
34
+ Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
35
+
36
+ ## When to Use
37
+
38
+ - Maintainer wants a **portfolio priority** brief across competing open issues / RFCs
39
+ - Operator says: portfolio priority, priority brief, competing RFCs, cluster open issues, pre-promotion portfolio, rank this backlog slice
40
+ - After a large classify / label-mirror partition when humans still need a shortlist (B-side; not the ranker)
41
+ - ⊗ Do NOT use for single-issue "what's next?" ranking — that is `deft-directive-triage` + `task triage:queue`
42
+ - ⊗ Do NOT use for post-promotion capacity buckets — that is #1419 / #1511
43
+ - ⊗ Do NOT use for review-handoff briefs — that is #1590
44
+
45
+ ## Stance (hard)
46
+
47
+ - ! **Propose-not-apply** (#3179): the brief proposes; the operator disposes
48
+ - ⊗ SCM label writes, mirror `--apply`, or re-enrich apply from this skill
49
+ - ⊗ `task triage:accept` / reject / defer / needs-ac / mark-duplicate / bulk-*
50
+ - ⊗ Scope lifecycle (`scope:promote` / `activate` / `complete` / `cancel`) from this skill
51
+ - ⊗ Treat the brief as a decision record — dispose into #1396 / plan-sequence / operator comment
52
+
53
+ ## Inputs
54
+
55
+ - ! Local `.deft-cache/github-issue/` (bodies available)
56
+ - ! Classify partition via `task triage:classify -- --mirror` (or cached JSON digest) as **FILTER only**
57
+ - ~ Optional theme slice: label family, title prefix, author, epic parent
58
+
59
+ ## Phases
60
+
61
+ ### Phase 0 — Cache freshness
62
+
63
+ 1. ! Run `task verify:cache-fresh`. Exit 0 → proceed. Exit 1 → refresh (`task cache:fetch-all -- --source=github-issue --repo OWNER/NAME`) or **disclose stale** and continue only with operator consent. Exit 2 → `task triage:bootstrap` first.
64
+ 2. ~ When zero cache entries, bootstrap/fetch before clustering (#2575).
65
+ 3. ⊗ Cluster against a silent stale cache without disclosing staleness.
66
+
67
+ ### Phase 1 — Classify as FILTER only
68
+
69
+ 1. ! Run `task triage:classify -- --mirror` (dry-run; optional `--json`) to partition dispositions (`no_match` / escalate / defer / already planned).
70
+ 2. ! Use classify outcomes only to **filter** the portfolio slice (e.g. deep-dive candidates from `no_match`; keep escalate on an interrupt queue).
71
+ 3. ⊗ Use classify disposition chips as the **ranker** or final shortlist order.
72
+ 4. ⊗ Pass `--apply` or `--re-enrich` with intent to write labels from this skill.
73
+
74
+ ### Phase 2 — Theme / slice selection
75
+
76
+ 1. ! Name an **explicit slice** (label family, title theme, author, or epic). Unattended full-backlog ranking is out of scope.
77
+ 2. ! Record scope counts: open in slice, classify partitions inside the slice, exclusions.
78
+ 3. ~ Prefer one coherent theme per brief (pilot: patterns + `no_match`).
79
+
80
+ ### Phase 3 — Cluster + conflict matrix + shortlist + park
81
+
82
+ 1. ! Group candidates into **families** (title/theme/body overlap, shared parent epic, layered pack).
83
+ 2. ! Build a **conflict / supersession matrix** per family: overlapping roots, pack-vs-atom, research-vs-product.
84
+ 3. ! Emit an ordered **shortlist** (deep dive / promote candidates) and a **park list** with park reasons.
85
+ 4. ! Separate **interrupt / non-portfolio** rows (escalate, hold, already dispositioned) so they are not ranked against park list.
86
+ 5. ⊗ Claim "#X supersedes #Y" from titles alone — see Phase 4 epistemic gates.
87
+
88
+ ### Phase 4 — Epistemic gates (MUST)
89
+
90
+ Before citing any `#N` in the brief:
91
+
92
+ 1. ! **Verify existence + state** for every cited issue via live `gh api repos/OWNER/NAME/issues/N` (REST) **or** a proven-fresh cache entry whose state matches the claim.
93
+ 2. ! **Read the body** of every issue used for shortlist, park-as-superseded, pack membership, or "decided" claims.
94
+ 3. ! When claiming decided / superseded / closed-by-comment, also **read comments** (REST `issues/N/comments`).
95
+ 4. ! State **open/closed accurately**; never invent issue numbers.
96
+ 5. ⊗ **Title-only supersession** or ownership claims.
97
+ 6. ⊗ Cite PRs as issues without filtering `pull_request` on mixed issue lists.
98
+
99
+ ### Phase 5 — Emit priority brief
100
+
101
+ ! Write markdown matching the pilot section shape (fill with this run's slice):
102
+
103
+ 1. **Header** — process issue (#3198 or project equivalent), parent tracker if any, generated timestamp, stance (propose-not-apply), epistemic method
104
+ 2. **Dispose path** — not a decision record; points at #1396 / plan-sequence / operator comment
105
+ 3. **Scope of this pass** — counts table (slice filters + partition sizes)
106
+ 4. **Interrupt / non-portfolio** — escalate/hold/already planned (do not rank here)
107
+ 5. **Conflict / family matrix** — families with verified state + body claims + recommendation
108
+ 6. **Shortlist** — ordered P1/P2/… with why
109
+ 7. **Park list** — classes, examples, park reasons
110
+ 8. **Epistemic limits** — what was cache-only, what needs follow-up probe
111
+ 9. **Operator dispose checklist** — accept/edit shortlist, accept park, record dispose, optional plan-sequence; do not treat as mirror apply
112
+
113
+ ~ Prefer writing under `docs/analysis/YYYY-MM-DD-portfolio-priority-brief-<slice>.md` for durable dogfood; chat-only is allowed if the operator declines a file.
114
+
115
+ ~ Link the worked example: #3200 and `docs/analysis/2026-08-07-portfolio-priority-brief-patterns-pilot.md` (may live on the pilot PR until merged).
116
+
117
+ ### Phase 6 — Dispose checklist (hand off)
118
+
119
+ 1. ! Present the dispose checklist to the operator (accept/edit shortlist + park; record dispose; optional plan-sequence for P1).
120
+ 2. ! Point dispose targets: **#1396**-shaped decision log (or interim issue comment), and/or `task plan-sequence:set`.
121
+ 3. ⊗ Auto-promote shortlist into plan-sequence without explicit operator dispose.
122
+ 4. ⊗ Exit treating the brief alone as durable prioritization memory (#2741 class).
123
+
124
+ ## Anti-Patterns
125
+
126
+ - ⊗ SCM label writes / mirror apply / re-enrich apply from this skill
127
+ - ⊗ `triage:accept` or any triage decision verb from this skill
128
+ - ⊗ Scope lifecycle mutations from this skill
129
+ - ⊗ Title-only supersession or unverified `#N` citations
130
+ - ⊗ Full open-backlog unattended ranking without an explicit slice
131
+ - ⊗ Replacing `triage:queue` for buildable work selection
132
+ - ⊗ Treating the brief as the decision record without dispose
133
+
134
+ ## EXIT
135
+
136
+ ! Confirm: `deft-directive-portfolio-priority complete -- exiting skill.`
137
+ ~ Chain: operator dispose (#1396 / plan-sequence) · `deft-directive-triage` for single-item queue work · `deft-directive-refinement` after accept on a different path · do **not** chain into mirror apply.
138
+ ⊗ Silent exit.
139
+
140
+ ## References
141
+
142
+ - #3198 process + dogfood · #3201 this skill · #3200 / patterns pilot brief
143
+ - #1396 decision log · #3179 propose-not-apply · #1423 / #3197 classify filter only
144
+ - Siblings: `deft-directive-triage`, `deft-directive-refinement` — not #1419/#1511 post-promotion
@@ -114,12 +114,24 @@ The release pipeline's Step 9/10/11 git mutations carry the bypass in subprocess
114
114
 
115
115
  See [`docs/RELEASING.md`](../../../docs/RELEASING.md) § Fixable check failure during release for the operator runbook and the existing `--allow-skip-ci=#N` incident contract.
116
116
 
117
- ### Step 5 branch-coverage threshold — open-issue ledger hatch (#2866)
117
+ ### Step 5 branch-coverage threshold — open-issue ledger hatch (#2866 / #3187)
118
118
 
119
- ! When **`task release` Step 5** fails on Vitest coverage below the 85% goal, apply this hatch **only** when **branches** is the **sole** metric below 85% (hairline branch miss — lines, functions, and statements all ≥ 85%). Confirm via the Step 5 failure output or `task coverage:hotspots` before hatching. If **any other metric** is also below 85%, or the failure is a hang / failing test / non-coverage defect, STOP — use § Fixable check failure — file-and-merge before resume (#2859) instead.
119
+ ! When **`task release` Step 5** fails on Vitest coverage below the 85% goal, apply this hatch **only** when **branches** is the **sole** metric below 85% (hairline branch miss — lines, functions, and statements all ≥ 85%). Confirm via the Step 5 failure output, `coverage/coverage-final.json`, or `task coverage:hotspots` before hatching. If **any other metric** is also below 85%, or the failure is a hang / failing test / non-coverage defect, STOP — use § Fixable check failure — file-and-merge before resume (#2859) instead.
120
120
 
121
121
  **Runtime disclosure (#2573):** `--allow-coverage-debt=#N` sets vitest coverage thresholds to zero for the release Step 5 run (`vitest.config.ts`). The hatch is justified only for branch-only hairlines; the filed debt issue MUST require restoring **all four metrics** (lines, functions, branches, statements) to ≥ 85% before close.
122
122
 
123
+ **Auto-hatch in `task release` Step 5 (#3187):** the pipeline classifies a non-zero Step 5 as `REAL_FAILURE` | `BRANCH_HAIRLINE` | `OTHER_COVERAGE` | `UNKNOWN` after **one** suite run.
124
+
125
+ 1. `REAL_FAILURE` | `OTHER_COVERAGE` | `UNKNOWN` → **fail closed** (no auto-issue). Route product defects via #2859.
126
+ 2. `BRANCH_HAIRLINE` + **any open** coverage-debt ledger entry → **fail closed** ("restore coverage; close `#N` first") — consecutive soft-pass ban (#2866).
127
+ 3. `BRANCH_HAIRLINE` + **empty** open debt ledger → **auto-file** `#N` with markers `coverage-debt` and `--allow-coverage-debt`, measured metrics, cut version, and acceptance (all four metrics ≥ 85%); mark Step 5 `PASS_WITH_DEBT(#N)`; **continue the cut without re-running vitest**. Loud stderr banner + cite `#N` in CHANGELOG / release notes.
128
+
129
+ ! **File before continue** — never soft-pass without a durable issue number.
130
+
131
+ ? Operators MAY still pass `--allow-coverage-debt=#N` manually (PowerShell-safe: `--allow-coverage-debt=N` or `--allow-coverage-debt="#N"` — #2621) when re-running after a pre-filed debt issue.
132
+
133
+ **SHA suite stamp (#3187, coordinates with #3188 check ordering):** after suite **green** or `PASS_WITH_DEBT`, write a local SHA-bound stamp under `.deft/release-suite-stamp.json` (gitignored). Re-entry at the **same clean HEAD** skips the suite; dirty tree or different HEAD invalidates the stamp. ⊗ CI never trusts the stamp (GHA has no committed stamp; `CI`/`GITHUB_ACTIONS` force miss).
134
+
123
135
  **Open-issue ledger (release-scoped only):**
124
136
 
125
137
  1. Check for an **open coverage-debt tracking issue** (unpaid hatch from a prior cut) — union all three probes and dedupe by issue number:
@@ -128,15 +140,18 @@ See [`docs/RELEASING.md`](../../../docs/RELEASING.md) § Fixable check failure d
128
140
  gh issue list --repo <owner>/<repo> --state open --search "coverage-debt in:title,body" --limit 20
129
141
  gh issue list --repo <owner>/<repo> --state open --search "allow-coverage-debt in:body" --limit 20
130
142
  ```
131
- - **CHANGELOG citation scan** (legacy hatch issues filed before markers were mandatory): parse `CHANGELOG.md` `[Unreleased]` plus the last three `## [version]` sections for `--allow-coverage-debt=#N` / `allow-coverage-debt=#N` citations; for each `#N`, run `gh issue view N --json state --jq .state` — `OPEN` counts as unpaid debt even when title/body lack the new markers (#2866).
132
- 2. If **no open coverage-debt issue exists** → file `#N` with title prefix `coverage-debt:` and body containing both markers `coverage-debt` and `--allow-coverage-debt`, documenting measured metrics, branch-only trigger, and acceptance criteria (restore **all four** coverage metrics ≥ 85%). Continue the cut with `--allow-coverage-debt=#N` on `task release` (PowerShell-safe: `--allow-coverage-debt=N` or `--allow-coverage-debt="#N"` — see Anti-Patterns #2621). The open `#N` remains WIP until coverage is restored and the issue is closed.
143
+ - **CHANGELOG citation scan** (legacy hatch issues filed before markers were mandatory): parse `CHANGELOG.md` `[Unreleased]` plus the last three `## [version]` sections for `--allow-coverage-debt=#N` / `allow-coverage-debt=#N` citations; for each `#N`, probe issue state via REST — `OPEN` (or unknown) counts as unpaid debt even when title/body lack the new markers (#2866).
144
+ 2. If **no open coverage-debt issue exists** → auto-hatch files `#N` (or operator files manually) with title prefix `coverage-debt:` and body containing both markers. The open `#N` remains WIP until coverage is restored and the issue is closed.
133
145
  3. If an **open coverage-debt issue from a prior hatch still exists** → ⊗ soft-pass again; restore real coverage (all four metrics ≥ 85%) and close the debt issue before the cut proceeds.
134
146
 
147
+ **Framework-release-first (#3187):** auto-hatch applies to framework `task release` Step 5 (e.g. deftai/directive). Consumer expansion of auto-hatch is blocked on project `plan.policy.coverageDebt` (#3189) — refuse when `status=unset` or `mode=off`. Do not auto-file debt on the framework repo from consumer trees; consumer ledger is always the consumer repo.
148
+
135
149
  ⊗ Auto-pass on a near-miss band without `#N` (#2573).
136
150
  ⊗ Silent soft-pass with no tracked issue.
137
151
  ⊗ File a debt issue without `coverage-debt` / `allow-coverage-debt` markers in title or body — the ledger query will miss it and permit a consecutive soft-pass (#2866).
138
152
  ⊗ Use this carve-out when lines, functions, or statements are also below 85%, or for hangs, failing tests, or non-coverage Step 5 failures — those stay under #2859 file-and-merge.
139
153
  ⊗ Treat file-debt-then-hatch as the default for ordinary PR / `task check` work outside a release cut — this hatch is release-scoped only.
154
+ ⊗ Trust a laptop suite stamp in CI or after HEAD/tree drift (#3187).
140
155
 
141
156
  See [`docs/RELEASING.md`](../../../docs/RELEASING.md) § Coverage debt hatch during release.
142
157
 
@@ -373,4 +388,6 @@ Where `<one-line guidance>` is one of:
373
388
  - ⊗ 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
374
389
  - ⊗ 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)
375
390
  - ⊗ 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"`
376
- - ⊗ Soft-pass coverage debt while an **open** coverage-debt issue from a prior hatch still exists (#2866 / #2573) -- restore real branch coverage >= 85% and close the debt issue before reusing `--allow-coverage-debt`; the ledger is open GitHub issues, not prior CHANGELOG citations (#2618 superseded by open-issue ledger)
391
+ - ⊗ Soft-pass coverage debt while an **open** coverage-debt issue from a prior hatch still exists (#2866 / #2573 / #3187) -- restore real branch coverage >= 85% and close the debt issue before reusing `--allow-coverage-debt` or expecting auto-hatch; the ledger is open GitHub issues, not prior CHANGELOG citations (#2618 superseded by open-issue ledger)
392
+ - ⊗ Re-run the full Step 5 suite after a legal branch-only hairline when auto-hatch already filed `#N` and continued (`PASS_WITH_DEBT`) — that is the ceremony tax #3187 removes
393
+ - ⊗ Trust `.deft/release-suite-stamp.json` in CI or after HEAD/tree drift (#3187)