@deftai/directive-content 0.107.0 → 0.108.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 (34) hide show
  1. package/Taskfile.yml +1 -1
  2. package/UPGRADING.md +22 -3
  3. package/commands.md +24 -2
  4. package/contracts/design-critique.md +117 -16
  5. package/docs/delivery-attempt.md +2 -1
  6. package/docs/freshness-contract.md +6 -1
  7. package/docs/getting-started.md +10 -11
  8. package/docs/hook-runtime-unavailable.md +54 -0
  9. package/docs/orphan-active-verdict-basis.md +166 -0
  10. package/package.json +1 -1
  11. package/packs/skills/skills-pack-0.1.json +7 -7
  12. package/scm/github.md +31 -1
  13. package/skills/deft-directive-build/SKILL.md +1 -1
  14. package/skills/deft-directive-cost/SKILL.md +7 -11
  15. package/skills/deft-directive-design-critique/SKILL.md +8 -1
  16. package/skills/deft-directive-design-critique/references/motion-shape.md +19 -0
  17. package/skills/deft-directive-interview/SKILL.md +10 -10
  18. package/skills/deft-directive-release/SKILL.md +10 -6
  19. package/skills/deft-directive-review-cycle/SKILL.md +33 -0
  20. package/skills/deft-directive-setup/SKILL.md +53 -22
  21. package/skills/deft-directive-swarm/references/core-ops.md +4 -0
  22. package/skills/deft-directive-swarm/references/core-phase-1-2.md +1 -1
  23. package/skills/deft-directive-swarm/references/core-phase-3.md +3 -1
  24. package/skills/deft-directive-swarm/references/core-phase-4.md +11 -8
  25. package/skills/deft-directive-swarm/references/host-cursor.md +1 -0
  26. package/skills/deft-directive-swarm/references/host-grok-build.md +1 -0
  27. package/tasks/engine.yml +2 -0
  28. package/tasks/occupancy.yml +24 -2
  29. package/tasks/prd.yml +4 -5
  30. package/tasks/session.yml +3 -3
  31. package/tasks/toolchain.yml +2 -2
  32. package/tasks/verify.yml +11 -1
  33. package/templates/agent-prompt-preamble.md +9 -2
  34. package/templates/agents-entry.md +6 -1
@@ -0,0 +1,166 @@
1
+ # `verify:orphan-active`: verdict basis and freshness (#3767)
2
+
3
+ `verify:orphan-active` decides whether an `xbrief/active/` brief with
4
+ `plan.status == running` is really still live work. Until #3767 it answered
5
+ that question from a triage-cache hit returned **unconditionally** — no age
6
+ bound, no re-validation. A cached `open` written twelve hours earlier beat
7
+ reality and suppressed the live read that would have corrected it, so the gate
8
+ exited 0 while scanning the very brief whose issue had already closed.
9
+
10
+ This document records what the gate now does, why, and what it still cannot
11
+ promise.
12
+
13
+ Legend (RFC2119): `!`=MUST, `~`=SHOULD, `⊗`=MUST NOT, `?`=MAY.
14
+
15
+ ---
16
+
17
+ ## Two modes, split by query shape
18
+
19
+ The gate has two callers with different obligations, so it has two mechanisms.
20
+
21
+ | | Scoped `--issue N` | Unscoped aggregate |
22
+ |---|---|---|
23
+ | Caller | after-merge DONE proof (#3429 / #3476) | `task check`, pre-commit, pre-push sweep |
24
+ | Mechanism | authoritative per-issue REST read | one complete open-issue inventory |
25
+ | N | 1 | one call regardless of brief count |
26
+ | Unknown state | **fail closed** (exit 1, `unresolved`) | **fail open** (exit 0, reported `unverified`) |
27
+ | Latency budget | 5 s | 15 s |
28
+
29
+ The unknown asymmetry is deliberate and predates this change. Scoped is a proof
30
+ obligation before a worker claims DONE about one named origin. The aggregate
31
+ sweep must not make offline work network-authorized — hard-failing every
32
+ aggregate unknown would break `--skip-gh`, offline runs, and fresh clones.
33
+
34
+ ⊗ Do not flatten the two unknown policies into one rule.
35
+
36
+ ---
37
+
38
+ ## The freshness choice
39
+
40
+ **Age bound plus re-validation, applied per mode.** Both options offered by
41
+ #3767 are used, because each mode needs a different one.
42
+
43
+ - A cache hit is honoured only when it is at most **15 minutes** old
44
+ (`ISSUE_CACHE_MAX_AGE_MS`). Older entries are not evidence.
45
+ - Scoped `--issue N` re-validates first: it takes the authoritative read and
46
+ falls back to the cache only inside the age bound, when the live read is
47
+ unavailable. Unknown after that is `unresolved`.
48
+ - The aggregate sweep resolves from the open-issue inventory when the network
49
+ is allowed, and uses an in-bound cache hit only under `--skip-gh` or when the
50
+ inventory itself is unavailable.
51
+
52
+ 15 minutes keeps a warm-cache offline run working while making an overnight
53
+ entry non-authoritative — the measured entry was roughly twelve hours old.
54
+
55
+ ---
56
+
57
+ ## The aggregate inventory
58
+
59
+ The sweep reuses `restIssueListOpenInventory` (#3752): one
60
+ `gh api --paginate --slurp repos/<owner>/<repo>/issues?state=open&per_page=100`
61
+ subprocess. It excludes pull-request rows and **fails closed** on command
62
+ failure, non-JSON output, a non-array payload, a malformed row, buffer
63
+ exhaustion, and the pagination cap.
64
+
65
+ ! The inventory MUST be complete. `probeCacheDrift`'s helper defaults to a
66
+ 1,000-item limit; reusing that capped set as a closed-state oracle would
67
+ misclassify open issue 1001+ in a larger repository.
68
+
69
+ Membership in a successful inventory means open. **Absence means "not open"**,
70
+ which is the direction that tells an operator to run `scope:complete` on what
71
+ may be live work — so absence is confirmed by one authoritative per-issue read
72
+ before the gate acts on it. That confirming result is reused for the rest of
73
+ the run when several briefs name the same issue. In the ordinary case
74
+ (everything open) that costs zero extra calls.
75
+
76
+ When the inventory is unavailable the gate reports `unverified` rather than
77
+ inferring closed. Fail-closed here means never manufacturing a false "closed",
78
+ not turning the sweep into a connectivity check.
79
+
80
+ ### Why not a live read per brief
81
+
82
+ Per-brief live reads are the mechanism #3752 removed. Measured: the inventory
83
+ is **4.2 s constant**, against **14.2 s** and **76.6 s** for sequential
84
+ per-brief reads at the WIP cap of 20 on two different hosts. Crossover is about
85
+ 5 briefs on `gh` and about 1 on `ghx`. `verify:orphan-active` sits in the
86
+ fast-preflight tier, where wall clock rather than REST quota is the constraint.
87
+
88
+ ---
89
+
90
+ ## Reported basis
91
+
92
+ Every run now says how it decided, so a verified pass is distinguishable from
93
+ an unverified one:
94
+
95
+ ```text
96
+ verify:orphan-active: no orphaned active/running xBRIEFs (scanned 3 running briefs in active/).
97
+ Basis: inventory 2, cache 1 (max age 4m).
98
+ ```
99
+
100
+ ```text
101
+ verify:orphan-active: no orphaned active/running xBRIEFs (scanned 1 running brief in active/).
102
+ Basis: unverified 1.
103
+ UNVERIFIED: state could not be established for the references below, so this run is
104
+ not evidence that they are unshipped:
105
+ - #8001 (open-issue inventory unavailable: gh api failed: ...)
106
+ ```
107
+
108
+ `EvaluateResult.basis` carries the same counts structurally
109
+ (`inventory`, `live`, `cache`, `unverified`, `maxCacheAgeMs`, `proxied`,
110
+ `elapsedMs`, `budgetMs`).
111
+
112
+ ⊗ Do not cite an exit 0 with `unverified > 0` as evidence that a tree is clean.
113
+
114
+ ---
115
+
116
+ ## The `ghx` caveat
117
+
118
+ "Live" is itself a cache. `defaultRunGh` resolves through `resolveBinary()`,
119
+ which prefers **`ghx`**, a cached read-only GET proxy whose age nothing in this
120
+ gate can inspect.
121
+
122
+ This gate therefore **pins plain `gh`** for its authoritative reads when `gh` is
123
+ on PATH. When only `ghx` is available the gate still runs, sets
124
+ `basis.proxied`, and prints:
125
+
126
+ ```text
127
+ Note: reads resolved through `ghx`, a cached GET proxy; freshness is bounded by that
128
+ proxy, which this gate cannot inspect (#3737).
129
+ ```
130
+
131
+ ⊗ Do not claim the gate detects a closed origin "regardless of cache age" while
132
+ `ghx` is in the path. Whether `resolveBinary` should prefer `ghx` at all is
133
+ [#3737](https://github.com/deftai/directive/issues/3737).
134
+
135
+ ---
136
+
137
+ ## Latency budgets
138
+
139
+ There were none before #3767, which is why "affordable" was unfalsifiable.
140
+
141
+ | Mode | Budget | Basis |
142
+ |---|---|---|
143
+ | Scoped `--issue N` | 5 s | one issue read plus at most one linked-PR read, measured ~0.75 s each |
144
+ | Unscoped aggregate | 15 s | one 4.2 s inventory plus confirming reads for apparent closes |
145
+
146
+ Exceeding a budget prints an advisory line and does **not** change the exit
147
+ code. Budget drift is a signal to re-measure the mechanism, not a new failure
148
+ mode for callers.
149
+
150
+ ---
151
+
152
+ ## Out of scope
153
+
154
+ - The offline crash when neither `gh` nor `ghx` is on PATH
155
+ ([#3774](https://github.com/deftai/directive/issues/3774)).
156
+ - Whether `resolveBinary` should prefer `ghx`
157
+ ([#3737](https://github.com/deftai/directive/issues/3737)).
158
+ - The `cache_fresh` forge-error fail-open decided on
159
+ [#3738](https://github.com/deftai/directive/issues/3738).
160
+
161
+ ## Related
162
+
163
+ - [#3429](https://github.com/deftai/directive/issues/3429) — the gate's contract
164
+ - [#3476](https://github.com/deftai/directive/issues/3476) — `verify:completed-tracked`
165
+ - [#3752](https://github.com/deftai/directive/issues/3752) — the open-inventory mechanism reused here
166
+ - [#3156](https://github.com/deftai/directive/issues/3156) — gate integrity; this was a deliberate gate-definition change
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive-content",
3
- "version": "0.107.0",
3
+ "version": "0.108.0",
4
4
  "deftConsumerDeposit": true,
5
5
  "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.",
6
6
  "license": "MIT",