@deftai/directive-content 0.59.0 → 0.61.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 (190) hide show
  1. package/.githooks/pre-commit +10 -128
  2. package/.githooks/pre-push +8 -108
  3. package/Taskfile.yml +48 -58
  4. package/UPGRADING.md +19 -3
  5. package/docs/assets/directive-lifecycle-diagram.png +0 -0
  6. package/docs/directive-lifecycle.md +73 -0
  7. package/docs/getting-started.md +5 -1
  8. package/package.json +3 -3
  9. package/packs/skills/skills-pack-0.1.json +1 -1
  10. package/packs/strategies/strategies-pack-0.1.json +19 -19
  11. package/scm/github.md +37 -6
  12. package/skills/deft-directive-setup/SKILL.md +24 -15
  13. package/strategies/speckit.md +14 -14
  14. package/strategies/v0-20-contract.md +12 -1
  15. package/tasks/change.yml +16 -31
  16. package/tasks/ci.yml +8 -0
  17. package/tasks/commit.yml +12 -19
  18. package/tasks/core.yml +10 -0
  19. package/tasks/engine.yml +42 -0
  20. package/tasks/framework.yml +3 -0
  21. package/tasks/install.yml +20 -19
  22. package/tasks/migrate.yml +26 -15
  23. package/tasks/project.yml +26 -0
  24. package/tasks/toolchain.yml +15 -5
  25. package/tasks/vbrief.yml +4 -3
  26. package/tasks/verify.yml +12 -14
  27. package/templates/agents-entry.md +1 -1
  28. package/scripts/_agents_md.py +0 -494
  29. package/scripts/_cache_fetch.py +0 -635
  30. package/scripts/_cache_quota.py +0 -529
  31. package/scripts/_cache_refresh.py +0 -163
  32. package/scripts/_cache_validate.py +0 -209
  33. package/scripts/_content_root.py +0 -42
  34. package/scripts/_doctor_state.py +0 -277
  35. package/scripts/_event_detect.py +0 -305
  36. package/scripts/_events.py +0 -514
  37. package/scripts/_lifecycle_hygiene.py +0 -568
  38. package/scripts/_pathspec.py +0 -91
  39. package/scripts/_policy_show_cli.py +0 -266
  40. package/scripts/_precutover.py +0 -92
  41. package/scripts/_project_context.py +0 -224
  42. package/scripts/_project_definition_io.py +0 -164
  43. package/scripts/_relocate_snapshot.py +0 -209
  44. package/scripts/_relocate_states.py +0 -343
  45. package/scripts/_resolve_preflight_path.py +0 -152
  46. package/scripts/_safe_subprocess.py +0 -167
  47. package/scripts/_session_start_hook.py +0 -205
  48. package/scripts/_sor_gate_diff.py +0 -365
  49. package/scripts/_stdio_utf8.py +0 -59
  50. package/scripts/_triage_bootstrap_gitignore.py +0 -904
  51. package/scripts/_triage_classify_cli.py +0 -122
  52. package/scripts/_triage_queue_cli.py +0 -625
  53. package/scripts/_triage_scope_cli.py +0 -343
  54. package/scripts/_triage_scope_drift_cli.py +0 -121
  55. package/scripts/_triage_scope_ignores.py +0 -286
  56. package/scripts/_triage_scope_milestone.py +0 -432
  57. package/scripts/_triage_scope_mutations.py +0 -337
  58. package/scripts/_triage_scope_renderers.py +0 -207
  59. package/scripts/_triage_smoketest_stages.py +0 -674
  60. package/scripts/_triage_subscribe_cli.py +0 -140
  61. package/scripts/_triage_welcome_cli.py +0 -421
  62. package/scripts/_vbrief_build.py +0 -239
  63. package/scripts/_vbrief_fidelity.py +0 -479
  64. package/scripts/_vbrief_legacy.py +0 -589
  65. package/scripts/_vbrief_reconciliation.py +0 -883
  66. package/scripts/_vbrief_routing.py +0 -277
  67. package/scripts/_vbrief_safety.py +0 -778
  68. package/scripts/_vbrief_sources.py +0 -312
  69. package/scripts/_vbrief_speckit.py +0 -262
  70. package/scripts/_vbrief_story_quality.py +0 -353
  71. package/scripts/_vbrief_validation.py +0 -299
  72. package/scripts/build_dist.py +0 -412
  73. package/scripts/cache.py +0 -1078
  74. package/scripts/cache_scanner.py +0 -745
  75. package/scripts/candidates_log.py +0 -432
  76. package/scripts/capacity_backfill.py +0 -680
  77. package/scripts/capacity_show.py +0 -653
  78. package/scripts/ci_local.py +0 -689
  79. package/scripts/code_structure_validate.py +0 -765
  80. package/scripts/codebase_default_extractor.py +0 -495
  81. package/scripts/codebase_map.py +0 -304
  82. package/scripts/codebase_map_fresh.py +0 -104
  83. package/scripts/codebase_projection_registry.py +0 -94
  84. package/scripts/codebase_provider.py +0 -582
  85. package/scripts/doctor.py +0 -2552
  86. package/scripts/framework_commands.py +0 -505
  87. package/scripts/gh_rest.py +0 -882
  88. package/scripts/github_auth_modes.py +0 -437
  89. package/scripts/github_body.py +0 -292
  90. package/scripts/ip_risk.py +0 -531
  91. package/scripts/issue_emit.py +0 -670
  92. package/scripts/issue_ingest.py +0 -1064
  93. package/scripts/migrate_preflight.py +0 -418
  94. package/scripts/migrate_vbrief.py +0 -2677
  95. package/scripts/monitor_pr.py +0 -401
  96. package/scripts/pack_migrate_lessons.py +0 -336
  97. package/scripts/pack_migrate_patterns.py +0 -254
  98. package/scripts/pack_migrate_rules.py +0 -350
  99. package/scripts/pack_migrate_skills.py +0 -423
  100. package/scripts/pack_migrate_strategies.py +0 -311
  101. package/scripts/pack_migrate_swarm_spec.py +0 -250
  102. package/scripts/pack_render.py +0 -434
  103. package/scripts/packs_slice.py +0 -712
  104. package/scripts/platform_capabilities.py +0 -336
  105. package/scripts/policy.py +0 -2826
  106. package/scripts/policy_set.py +0 -324
  107. package/scripts/pr_check_closing_keywords.py +0 -524
  108. package/scripts/pr_check_protected_issues.py +0 -267
  109. package/scripts/pr_merge_readiness.py +0 -1004
  110. package/scripts/pr_wait_mergeable.py +0 -669
  111. package/scripts/prd_render.py +0 -159
  112. package/scripts/preflight_architecture_sor.py +0 -974
  113. package/scripts/preflight_branch.py +0 -289
  114. package/scripts/preflight_cache.py +0 -974
  115. package/scripts/preflight_gh.py +0 -721
  116. package/scripts/preflight_implementation.py +0 -272
  117. package/scripts/preflight_story_start.py +0 -838
  118. package/scripts/preflight_wip_cap.py +0 -149
  119. package/scripts/probe_session.py +0 -545
  120. package/scripts/project_render.py +0 -293
  121. package/scripts/quarantine_ext.py +0 -237
  122. package/scripts/reconcile_issues.py +0 -1442
  123. package/scripts/refresh-path.ps1 +0 -107
  124. package/scripts/release.py +0 -2030
  125. package/scripts/release_e2e.py +0 -1011
  126. package/scripts/release_publish.py +0 -486
  127. package/scripts/release_rollback.py +0 -980
  128. package/scripts/relocate.py +0 -1034
  129. package/scripts/resolve_changelog_unreleased.py +0 -667
  130. package/scripts/resolve_version.py +0 -490
  131. package/scripts/resume_conditions.py +0 -706
  132. package/scripts/ritual_sentinel.py +0 -609
  133. package/scripts/roadmap_render.py +0 -635
  134. package/scripts/rule_ownership_lint.py +0 -325
  135. package/scripts/scm.py +0 -591
  136. package/scripts/scope_audit_log.py +0 -387
  137. package/scripts/scope_decompose.py +0 -654
  138. package/scripts/scope_demote.py +0 -509
  139. package/scripts/scope_lifecycle.py +0 -1126
  140. package/scripts/scope_undo.py +0 -772
  141. package/scripts/session_start.py +0 -406
  142. package/scripts/setup_ghx.py +0 -339
  143. package/scripts/setup_windows.ps1 +0 -220
  144. package/scripts/slice_audit.py +0 -585
  145. package/scripts/slice_record.py +0 -530
  146. package/scripts/slice_record_existing.py +0 -692
  147. package/scripts/slug_normalize.py +0 -178
  148. package/scripts/spec_render.py +0 -477
  149. package/scripts/spec_validate.py +0 -238
  150. package/scripts/subagent_monitor.py +0 -658
  151. package/scripts/swarm_complete_cohort.py +0 -644
  152. package/scripts/swarm_launch.py +0 -1206
  153. package/scripts/swarm_readiness.py +0 -554
  154. package/scripts/swarm_verify_review_clean.py +0 -438
  155. package/scripts/swarm_worktrees.py +0 -497
  156. package/scripts/toolchain-check.py +0 -52
  157. package/scripts/triage_actions.py +0 -871
  158. package/scripts/triage_bootstrap.py +0 -1153
  159. package/scripts/triage_bulk.py +0 -630
  160. package/scripts/triage_classify.py +0 -932
  161. package/scripts/triage_help.py +0 -1685
  162. package/scripts/triage_queue.py +0 -1944
  163. package/scripts/triage_reconcile.py +0 -581
  164. package/scripts/triage_refresh.py +0 -643
  165. package/scripts/triage_scope.py +0 -999
  166. package/scripts/triage_scope_drift.py +0 -575
  167. package/scripts/triage_smoketest.py +0 -396
  168. package/scripts/triage_subscribe.py +0 -399
  169. package/scripts/triage_summary.py +0 -1011
  170. package/scripts/triage_welcome.py +0 -1178
  171. package/scripts/ts_check_lane.py +0 -86
  172. package/scripts/validate-links.py +0 -64
  173. package/scripts/validate_strategy_output.py +0 -212
  174. package/scripts/vbrief_activate.py +0 -228
  175. package/scripts/vbrief_migrate_conformance.py +0 -368
  176. package/scripts/vbrief_reconcile_graph.py +0 -306
  177. package/scripts/vbrief_reconcile_labels.py +0 -460
  178. package/scripts/vbrief_reconcile_umbrellas.py +0 -741
  179. package/scripts/vbrief_validate.py +0 -1144
  180. package/scripts/verify-stubs.py +0 -61
  181. package/scripts/verify_capacity.py +0 -160
  182. package/scripts/verify_encoding.py +0 -699
  183. package/scripts/verify_hooks_installed.py +0 -206
  184. package/scripts/verify_investigation.py +0 -360
  185. package/scripts/verify_judgment_gates.py +0 -827
  186. package/scripts/verify_no_task_runtime.py +0 -171
  187. package/scripts/verify_scm_boundary.py +0 -509
  188. package/scripts/verify_session_ritual.py +0 -389
  189. package/scripts/verify_tools.py +0 -426
  190. package/scripts/verify_vbrief_conformance.py +0 -478
package/scm/github.md CHANGED
@@ -214,9 +214,42 @@ Following a v1.0.0 release, commits:
214
214
  - ⊗ Allow force pushes
215
215
  - ⊗ Allow deletions
216
216
 
217
+ ## ghx cache proxy (#884)
218
+
219
+ [ghx](https://github.com/brunoborges/ghx) is a **supported, recommended** read-only cache proxy for the GitHub CLI. Deft's SCM layer (`resolveBinary` in `@deftai/directive-core/scm`) prefers `ghx` over `gh` when both are on PATH, so consumers benefit automatically once ghx is installed. ghx is optional for consumer projects — only `gh` is required — but strongly recommended for maintainers and multi-agent swarms that issue many read-only `gh api` / `gh issue view` calls.
220
+
221
+ **Install (consent-gated, default deny):**
222
+
223
+ ```bash
224
+ directive setup:ghx # interactive y/N prompt (default: no)
225
+ task setup:ghx # same via Taskfile
226
+ task setup:ghx -- --yes # non-interactive CI / scripted approval
227
+ ```
228
+
229
+ `task setup` runs a detection-only `--check` pass and nudges when `gh` is present but `ghx` is missing. Set `DEFT_SETUP_GHX_SKIP=1` to suppress the interactive install path in non-interactive shells.
230
+
231
+ **Surface rules:**
232
+
233
+ - ! Prefer `ghx` over `gh` for read-only GET operations when ghx is on PATH
234
+ - ! Use live `gh` for mutations (POST/PATCH/PUT/DELETE) and for immediate read-back after a mutation — ghx is a cached GET proxy only
235
+ - ⊗ Use `ghx api` for multi-arg write invocations — ghx accepts a single positional path arg; writes fall through to `gh`
236
+
237
+ ## Local git hooks (#747 / #2049)
238
+
239
+ Project-root `.githooks/` enforce branch policy and encoding gates through the **`deft` CLI only** — no Python `scripts/*.py` dispatch (#2049). Hooks are installed idempotently via `deft setup` (`git config core.hooksPath .githooks`).
240
+
241
+ | Hook | Dispatches | Purpose |
242
+ |------|------------|---------|
243
+ | `pre-commit` | `deft verify:branch`, `deft verify:encoding`, `deft verify:vbrief-conformance` (when `vbrief/` exists) | Default-branch commit refusal (#747), encoding gate (#798), staged vBRIEF conformance (#1620) |
244
+ | `pre-push` | `deft preflight-gh --pre-push-stdin` | Refspec-aware default-branch push refusal + destructive gh verb gate (#1019) |
245
+
246
+ - ! Verify wiring after install or framework upgrade: `deft verify:hooks-installed` (also wired into `deft check`).
247
+ - ! After upgrading the framework payload, run `deft update` from the project root to refresh `.githooks/` to the current TS-native templates (#2049). Stale hooks that still invoke `python scripts/preflight_branch.py` or other legacy paths fail `deft verify:hooks-installed`.
248
+ - ~ Recovery when hooks are stale or broken: `deft setup` (re-installs hooks path) or `deft update` (refreshes hook files from the deposited payload).
249
+
217
250
  ## Destructive gh verbs (#1019)
218
251
 
219
- A detection-bound gate (`scripts/preflight_gh.py`) refuses three classes of destructive surface before they execute, complementing the #747 branch-protection gate which already refuses commits to the default branch:
252
+ A detection-bound gate (`deft preflight-gh`) refuses three classes of destructive surface before they execute, complementing the #747 branch-protection gate which already refuses commits to the default branch:
220
253
 
221
254
  - `delete_repo` -- `gh repo delete <owner/repo>` and `gh api -X DELETE repos/<owner>/<repo>[/...]`. Irreversible.
222
255
  - `force_push_default` -- `git push --force` / `--force-with-lease` / `+refspec` targeting `master` or `main`.
@@ -224,9 +257,9 @@ A detection-bound gate (`scripts/preflight_gh.py`) refuses three classes of dest
224
257
 
225
258
  Three enforcement surfaces back the gate:
226
259
 
227
- 1. `.githooks/pre-push` invokes `preflight_gh.py --pre-push-stdin` after the #747 branch gate, refusing any push that touches the default branch (force-push or otherwise). Install via `task setup` (idempotent `git config core.hooksPath .githooks`); verify via `task verify:hooks-installed`.
228
- 2. `task verify:destructive-gh-verbs` is wired into the `task check` aggregate. It runs `preflight_gh.py --self-test`, which drives a built-in fixture table through the classifier so a future edit that introduces a false negative / false positive fails CI immediately.
229
- 3. Agent pre-execution callers can invoke `python scripts/preflight_gh.py --command "<full command>"` to classify a candidate verb before it executes. Three-state exit (0 allowed / 1 destructive refused / 2 config error) mirrors `scripts/preflight_branch.py`.
260
+ 1. `.githooks/pre-push` invokes `deft preflight-gh --pre-push-stdin` after the #747 branch gate (on pre-commit), refusing any push that touches the default branch (force-push or otherwise). Install via `deft setup` (idempotent `git config core.hooksPath .githooks`); verify via `deft verify:hooks-installed`.
261
+ 2. `deft verify:destructive-gh-verbs` (or `task verify:destructive-gh-verbs` in framework source repos) is wired into the `deft check` aggregate. It runs `deft preflight-gh --self-test`, which drives a built-in fixture table through the classifier so a future edit that introduces a false negative / false positive fails CI immediately.
262
+ 3. Agent pre-execution callers can invoke `deft preflight-gh --command "<full command>"` to classify a candidate verb before it executes. Three-state exit (0 allowed / 1 destructive refused / 2 config error) mirrors `deft verify:branch`.
230
263
 
231
264
  **Override paths:**
232
265
 
@@ -234,8 +267,6 @@ Three enforcement surfaces back the gate:
234
267
  - For repo deletion specifically: prefer the GitHub web UI's archive-or-delete prompt -- archiving is reversible, the gate is not opining on it.
235
268
  - For an admin merge: the canonical recovery is to request review through the normal flow. The `--admin` flag is gated because the most-common legitimate use case (release hot-fix) is rare enough that documenting an explicit bypass is cheaper than letting the verb pass by default.
236
269
 
237
- **Out of scope (v1):** a PATH-shim that intercepts `gh` at the command layer is deferred -- the consent-gated install path mirrors `setup_ghx.py` (#884) and lands additively when a follow-up issue is opened. The current gate is detection-bound (pre-push hook + agent-side `--command` classifier + self-test contract) rather than execution-bound, which is sufficient to refuse the recurring failure mode that motivated #1019 (an agent with an unrestricted `gh` token executing a destructive verb).
238
-
239
270
  ## Release Workflow (UCCPR)
240
271
 
241
272
  **UCCPR** = Update Changelog, Commit, Push, Release
@@ -12,6 +12,12 @@ description: >-
12
12
  <!-- Regenerate with: task packs:render -->
13
13
  <!-- Edit the source, not this file. Slice instead of loading every SKILL.md: task packs:slice skills by-trigger --trigger <kw> (or list) -->
14
14
 
15
+ <!-- AUTO-GENERATED by task packs:render -- DO NOT EDIT MANUALLY -->
16
+ <!-- Purpose: rendered skill -->
17
+ <!-- Source of truth: packs/skills/skills-pack-0.1.json -->
18
+ <!-- Regenerate with: task packs:render -->
19
+ <!-- Edit the source, not this file. Slice instead of loading every SKILL.md: task packs:slice skills by-trigger --trigger <kw> (or list) -->
20
+
15
21
  # Deft Directive Setup
16
22
 
17
23
  Agent-driven alternative to `.deft/core/run bootstrap && .deft/core/run project && .deft/core/run spec`.
@@ -497,7 +503,7 @@ omit = [
497
503
 
498
504
  ## Phase 3 — Specification
499
505
 
500
- **Goal:** Generate an implementable spec using the strategy chosen in Phase 2, producing a `specification.vbrief.json` draft for human approval before downstream generation.
506
+ **Goal:** Generate an implementable spec using the strategy chosen in Phase 2, producing scope vBRIEFs in `vbrief/proposed/` and PROJECT-DEFINITION narratives for human approval greenfield v0.20 does not create `specification.vbrief.json`.
501
507
 
502
508
  ! **Path Resolution Anchor**: Same rule as Phase 2 -- resolve ALL paths relative to the user's pwd at skill entry, never relative to the skill file, AGENTS.md, or any framework directory.
503
509
 
@@ -656,24 +662,27 @@ Per [strategies/interview.md](../../strategies/interview.md#interview-rules-shar
656
662
 
657
663
  ⊗ Drop the user at the end of Phase 3 with scope vBRIEFs in `vbrief/proposed/` and no forward pointer to the bridge. Without this section the user discovers the gap at runtime when the swarm Phase 0 Step 1 preflight rejects every candidate (`Invalid transition: 'activate' requires file in pending/`), as in the originating 2026-05-10 first-session consumer tic-tac-toe swarm (issue #1025).
658
664
 
659
- ### End-of-Phase-3 Export Prompt and Render Gate
665
+ ### End-of-Phase-3 Export Prompt (project:export-spec)
660
666
 
661
- ! After the human approval gate on `specification.vbrief.json` narratives but BEFORE handing off to `deft-directive-build` (or advancing speckit Phase 3 → Phase 4), ask the user whether to generate human-readable exports. This replaces the invisible skip-if-absent behavior of `task check` (#398) and closes the greenfield gap (#433). This is also the Phase 3 → Phase 4 transition gate required by [strategies/speckit.md Post-Phase 3 Transition Gate](../../strategies/speckit.md#post-phase-3-transition-gate-render-for-review) (#432).
667
+ ! After scope vBRIEFs are written to `vbrief/proposed/` and PROJECT-DEFINITION is populated, but BEFORE handing off to `deft-directive-build` (or advancing speckit Phase 3 → Phase 4), ask the user whether to generate human-readable exports. Greenfield v0.20 projects export via `task project:export-spec` (not legacy `task spec:render`). This replaces the invisible skip-if-absent behavior of `task check` (#398), closes the greenfield gap (#433), and is the Phase 3 → Phase 4 transition gate required by [strategies/speckit.md Post-Phase 3 Transition Gate](../../strategies/speckit.md#post-phase-3-transition-gate-export-for-review) (#432 / #2013).
662
668
 
663
- 1. ! Prompt: "Your `specification.vbrief.json` is approved. Generate `SPECIFICATION.md` and/or `PRD.md` now? (recommended for stakeholder review)"
664
- 1. Yes — render both
665
- 2. `SPECIFICATION.md` only
669
+ 1. ! Prompt: "Your scope vBRIEFs are ready. Generate a stakeholder-facing spec export and/or `PRD.md` now? (recommended for stakeholder review)"
670
+ 1. Yes — export spec (+ PRD if selected)
671
+ 2. Spec export only (`SPECIFICATION.md`)
666
672
  3. `PRD.md` only
667
- 4. Skip — Ill render later with `task spec:render` / `task prd:render`
668
- 2. ! Run the selected render command(s):
669
- - `task spec:render` → writes `SPECIFICATION.md`
670
- - `task prd:render` → writes `PRD.md`
671
- 3. ! If the user picked a speckit-strategy project: `task spec:render` is **mandatory** at this boundary — invoke it even if the user declined the prompt, because speckit Phase 3 Phase 4 is gated on `SPECIFICATION.md` existing and matching the current vBRIEF hash.
672
- 4. ! Confirm to the user which files were written and remind them that direct edits to `SPECIFICATION.md` / `PRD.md` are overwritten on the next render — edit `specification.vbrief.json` instead.
673
- 5. ~ If the user skipped rendering and is NOT on a speckit strategy, no-op and continue.
674
-
675
- Advance a speckit project to Phase 4 without running `task spec:render` at this gate `SPECIFICATION.md` is required for the Phase 3 transition criterion.
673
+ 4. Skip — I'll export later with `task project:export-spec` / `task prd:render`
674
+ 2. ! Run the selected export command(s):
675
+ - `task project:export-spec` → writes `SPECIFICATION.md` from PROJECT-DEFINITION + lifecycle scopes (greenfield default; stakeholder audience)
676
+ - `task project:export-spec -- --audience=internal` → same export but includes proposed scopes under `## Scope outlook` (use for setup/speckit internal handoff when proposed scopes need visibility)
677
+ - `task prd:render` → writes `PRD.md` (optional stakeholder review)
678
+ - Legacy `task spec:render` migrated trees only (when `vbrief/specification.vbrief.json` exists); do NOT use on greenfield v0.20 projects
679
+ 3. ! If the user picked a speckit-strategy project: export is **mandatory** at this boundary — invoke `task project:export-spec` (with `--audience=internal` when proposed scopes exist) even if the user declined the prompt, because speckit Phase 3 → Phase 4 is gated on **export succeeded** (exit 0), not on `specification.vbrief.json` approval.
680
+ 4. ! Confirm to the user which files were written and remind them that direct edits to `SPECIFICATION.md` / `PRD.md` are overwritten on the next export — edit vBRIEF narratives in `vbrief/proposed/` and PROJECT-DEFINITION instead.
681
+ 5. ~ If the user skipped export and is NOT on a speckit strategy, no-op and continue.
682
+
683
+ ⊗ Advance a speckit project to Phase 4 without a successful `task project:export-spec` at this gate — export must succeed (exit 0) for the Phase 3 transition criterion.
676
684
  ⊗ Silently skip the prompt — greenfield users who never open a PR will miss the exports without it.
685
+ ⊗ Invoke legacy `task spec:render` on a greenfield v0.20 project — use `task project:export-spec` instead (#2013).
677
686
 
678
687
  ### Handoff to deft-directive-build
679
688
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  A spec-driven development workflow inspired by [GitHub's spec-kit](https://github.com/github/spec-kit), with a Phase 4.5 readiness layer for decomposing broad implementation scopes into swarm-safe stories. Fully migrated to v0.20 (phases + stories emitted as date-prefixed vBRIEFs in proposed/; no legacy specification.vbrief.json).
10
10
 
11
- **v0.20 note (s5-migrate-speckit-rapid-enterprise / #1166):** Speckit now emits only the canonical v0.20 shape (date-prefixed phase/epic + story vBRIEFs in proposed/, full PROJECT-DEFINITION.vbrief.json via task project:render or spec:render post, seeded lifecycle folders, no legacy specification.vbrief.json). Phase 4/4.5 scopes go to proposed/ (not pending/). See the dedicated ## v0.20 Output Shape section, the Artifacts Summary updated to the contract table, and the canonical contract `strategies/v0-20-contract.md` (s1-contract of #1166).
11
+ **v0.20 note (s5-migrate-speckit-rapid-enterprise / #1166):** Speckit now emits only the canonical v0.20 shape (date-prefixed phase/epic + story vBRIEFs in proposed/, full PROJECT-DEFINITION.vbrief.json via task project:render post, seeded lifecycle folders, no legacy specification.vbrief.json). Phase 4/4.5 scopes go to proposed/ (not pending/). Review exports use `task project:export-spec` (gate: export succeeded). See the dedicated ## v0.20 Output Shape section, the Artifacts Summary updated to the contract table, and the canonical contract `strategies/v0-20-contract.md` (s1-contract of #1166).
12
12
 
13
13
  Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
14
14
 
@@ -49,7 +49,7 @@ flowchart LR
49
49
  style I fill:#f0abfc,stroke:#a855f7,color:#000
50
50
  ```
51
51
 
52
- (See ## v0.20 Output Shape for exact artifact rules, the mandatory `task project:render` / `task spec:render` post calls, and citation of strategies/v0-20-contract.md.)
52
+ (See ## v0.20 Output Shape for exact artifact rules, the mandatory `task project:render` post call and `task project:export-spec` for review exports, and citation of strategies/v0-20-contract.md.)
53
53
 
54
54
  ---
55
55
 
@@ -137,13 +137,13 @@ Add the following narrative keys to the proposed/ vBRIEF `plan.narratives`:
137
137
  - ⊗ Write implementation code
138
138
  - ⊗ Create `specs/` directories or standalone `plan.md` files -- all content goes in the proposed/ date-prefixed vBRIEF(s)
139
139
 
140
- ### Post-Phase 3 Transition Gate: Render for Review
140
+ ### Post-Phase 3 Transition Gate: Export for Review
141
141
 
142
- ! Phase 3 -> Phase 4 is gated on an explicit render-and-review step, mirroring the Phase 2 approval gate. Complete the steps below **in order** before advancing. [skills/deft-directive-setup/SKILL.md](../skills/deft-directive-setup/SKILL.md) is required to invoke `task spec:render` at this boundary when running speckit interactively; the gate fails silently otherwise (yolo-mode agents used to skip it -- that is what this gate exists to prevent).
142
+ ! Phase 3 -> Phase 4 is gated on a successful spec export for human review, mirroring the Phase 2 approval gate. Complete the steps below **in order** before advancing. [skills/deft-directive-setup/SKILL.md](../skills/deft-directive-setup/SKILL.md) is required to invoke `task project:export-spec` at this boundary when running speckit interactively; the gate fails silently otherwise (yolo-mode agents used to skip it -- that is what this gate exists to prevent).
143
143
 
144
- 1. ! Run `task spec:render` (or `task project:render`) to (re-)produce derivative views from the proposed/ vBRIEFs + PROJECT-DEFINITION if needed for human review.
145
- 2. ! Confirm any rendered `SPECIFICATION.md` (if emitted as derivative) exists at the project root and contains the `<!-- deft:deprecated-redirect -->` sentinel.
146
- 3. ! The proposed/ vBRIEFs + PROJECT-DEFINITION are the source of truth. Derivatives are read-only exports.
144
+ 1. ! Run `task project:export-spec` (use `--audience=internal` when proposed scopes must appear in the `## Scope outlook` section). Legacy migrated trees MAY use `task spec:render` when `vbrief/specification.vbrief.json` exists.
145
+ 2. ! Confirm export **succeeded** (command exit 0) and `SPECIFICATION.md` exists at the project root with the greenfield banner (`<!-- Source of truth: vbrief/PROJECT-DEFINITION.vbrief.json -->`) or full-spec banner as appropriate.
146
+ 3. ! The proposed/ vBRIEFs + PROJECT-DEFINITION are the source of truth. `SPECIFICATION.md` is a read-only export.
147
147
  4. ! Human reviewer approves (or requests changes). On approval, proceed to Phase 4.
148
148
 
149
149
  ### Transition Criteria
@@ -151,7 +151,7 @@ Add the following narrative keys to the proposed/ vBRIEF `plan.narratives`:
151
151
  - ! All gates pass (or exceptions documented)
152
152
  - ! Every spec requirement maps to a plan element
153
153
  - ! Architecture reviewed and approved
154
- - ! **Phase 3 -> Phase 4 transition criterion:** The proposed/ date-prefixed vBRIEF(s) + PROJECT-DEFINITION represent the approved spec (agents MUST NOT advance to Phase 4 without review of the v0.20 artifacts).
154
+ - ! **Phase 3 -> Phase 4 transition criterion:** `task project:export-spec` succeeded (exit 0) AND the proposed/ date-prefixed vBRIEF(s) + PROJECT-DEFINITION represent the approved spec (agents MUST NOT advance to Phase 4 without review of the v0.20 artifacts).
155
155
 
156
156
  ---
157
157
 
@@ -353,7 +353,7 @@ The readiness report lists ready stories, blocked stories, decomposition-needed
353
353
  | 1. Principles | `vbrief/PROJECT-DEFINITION.vbrief.json` | Governing rules (Principles narrative) |
354
354
  | 2. Specify | date-prefixed in `vbrief/proposed/` | WHAT/WHY narratives (v0.20) |
355
355
  | 3. Plan | date-prefixed in `vbrief/proposed/` | HOW narratives (enriches Phase 2; v0.20) |
356
- | 3b. Render (derivative) | `SPECIFICATION.md` (via `task spec:render`, sentinel only) | Read-only human review export (optional) |
356
+ | 3b. Export (review) | `SPECIFICATION.md` (via `task project:export-spec`) | Read-only human review export (optional; gate requires export succeeded for Phase 3→4) |
357
357
  | 3c. Render PRD (derivative) | `PRD.md` (via `task prd:render`, sentinel only) | Optional stakeholder-review export |
358
358
  | 4. Tasks | `./vbrief/proposed/YYYY-MM-DD-ip<NNN>-<slug>.vbrief.json` (one per IP/epic) | Phase/epic scope vBRIEFs (v0.20: proposed/) drive roadmap/project render + decomposition |
359
359
  | 4.5. Story decomposition | Child story vBRIEFs with `plan.metadata.swarm` in proposed/ | Swarm-ready executable units (v0.20) |
@@ -371,7 +371,7 @@ project/
371
371
  │ │ └── YYYY-MM-DD-ip001-....vbrief.json
372
372
  │ ├── plan.vbrief.json # Phase 4b: session todos (planRef to active scope)
373
373
  │ └── pending/ active/ etc. # Lifecycle (seeded empty or with promoted)
374
- ├── SPECIFICATION.md # Optional derivative (task spec:render; sentinel only)
374
+ ├── SPECIFICATION.md # Optional export (task project:export-spec)
375
375
  ├── PRD.md # Optional derivative (task prd:render; sentinel only)
376
376
  └── src/ # Phase 5
377
377
  ```
@@ -386,12 +386,12 @@ This strategy has been migrated to the full v0.20 output shape so speckit-genera
386
386
 
387
387
  - ! Seed the five lifecycle folders under `vbrief/` if any are missing: `proposed/`, `pending/`, `active/`, `completed/`, `cancelled/`.
388
388
  - ! Emit all scope items (principles context, spec phases/stories, implementation phases/epics) exclusively as date-prefixed scope vBRIEFs in `vbrief/proposed/YYYY-MM-DD-<kebab-slug>.vbrief.json` (or the ipNNN convention for phases per vbrief.md). For speckit, phases use `YYYY-MM-DD-ip<NNN>-<slug>.vbrief.json` in proposed/; stories from Phase 4.5 also in proposed/. Decompose plans into focused, buildable vBRIEFs (v0.6 schema) rather than a monolithic legacy spec.
389
- - ! After the proposed/ vBRIEFs are written (or at Phase 3/4 boundaries), invoke `task project:render` (or `task spec:render` post) from the repo root to generate/refresh the complete `vbrief/PROJECT-DEFINITION.vbrief.json` (items registry derived from the lifecycle folders).
389
+ - ! After the proposed/ vBRIEFs are written (or at Phase 3/4 boundaries), invoke `task project:render` from the repo root to generate/refresh the complete `vbrief/PROJECT-DEFINITION.vbrief.json` (items registry derived from the lifecycle folders). For human review at Phase 3→4, invoke `task project:export-spec` (or `--audience=internal` when proposed scopes must appear in `## Scope outlook`).
390
390
  - ⊗ Never emit `vbrief/specification.vbrief.json` (or any legacy dual-write).
391
- - ~ `SPECIFICATION.md` / `PRD.md` at the project root, if produced at all, must be only read-only derivatives that include the v0.20 deprecated-redirect sentinel. The source of truth is the vbrief/ lifecycle (proposed/ phases + stories) + PROJECT-DEFINITION.
391
+ - ~ `SPECIFICATION.md` / `PRD.md` at the project root, if produced at all, are read-only exports from `task project:export-spec` / `task prd:render`. The source of truth is the vbrief/ lifecycle (proposed/ phases + stories) + PROJECT-DEFINITION. Legacy `task spec:render` applies only to migrated trees with `vbrief/specification.vbrief.json`.
392
392
  - ! Before writing any proposed/ vBRIEFs or PROJECT-DEFINITION, follow the guards in [artifact-guards.md](./artifact-guards.md) (Preparatory Guard for scope items in proposed/; Spec-Generating Guard for PROJECT-DEFINITION).
393
393
  - ! Final output tree must pass the deterministic v0.20 strategy output validation gate (s2-deterministic-gate) and the build Pre-Cutover Detection Guard with zero warnings/errors. See full acceptance in the s5 vBRIEF (a1: date-prefixed stories in proposed/ + deterministic gate; a2: speckit story-level in proposed/ not only pending phases; a3: no legacy specification.vbrief.json) and the 1166 decomposition.
394
- - ! Cite the canonical contract `strategies/v0-20-contract.md` (s1-contract) for the exact shape and the per-strategy table row (speckit: Yes lifecycle; Yes PROJECT-DEFINITION Phase 1+; proposed/ (phases + stories date-prefixed); Never specification.vbrief.json; Omit (use task spec:render post) for SPEC/PRD).
394
+ - ! Cite the canonical contract `strategies/v0-20-contract.md` (s1-contract) for the exact shape and the per-strategy table row (speckit: Yes lifecycle; Yes PROJECT-DEFINITION Phase 1+; proposed/ (phases + stories date-prefixed); Never specification.vbrief.json; `task project:export-spec` for SPEC export).
395
395
 
396
396
  ---
397
397
 
@@ -404,7 +404,7 @@ This strategy has been migrated to the full v0.20 output shape so speckit-genera
404
404
  | `vbrief/PROJECT-DEFINITION.vbrief.json` | Principles + full items registry | Speckit Phase 1 + `task project:render` |
405
405
  | `vbrief/proposed/YYYY-MM-DD-*.vbrief.json` + `YYYY-MM-DD-ipNNN-*.vbrief.json` | All spec (WHAT/WHY/HOW) + phases/epics/stories (date-prefixed; per v0.20 contract and vbrief.md speckit convention) | Speckit Phases 2-4.5 |
406
406
  | `vbrief/{proposed,pending,active,completed,cancelled}/` | All five lifecycle folders seeded | Speckit |
407
- | (optional derivative) `SPECIFICATION.md` / `PRD.md` | Human-readable (includes deprecated-redirect sentinel only) | `task spec:render` / `task prd:render` (post) |
407
+ | (optional export) `SPECIFICATION.md` / `PRD.md` | Human-readable spec export | `task project:export-spec` / `task prd:render` |
408
408
  | `vbrief/plan.vbrief.json` | Session-level tactical plan (planRef to active) | Speckit (internal) |
409
409
 
410
410
  **Pre-v0.20 / legacy artifacts that MUST NOT be produced by this strategy:**
@@ -76,6 +76,17 @@ All five lifecycle folders MUST be present (even if empty). This is the cutover
76
76
  - ⊗ No v0.20 strategy may create or dual-write `vbrief/specification.vbrief.json` alongside the new model artifacts.
77
77
  - Existing legacy files are handled only by `task migrate:vbrief` (which ingests them into scope vBRIEFs + PROJECT-DEFINITION and leaves a redirect stub at root if needed).
78
78
 
79
+ ### Greenfield spec export (#2013 / #1502)
80
+
81
+ Greenfield projects (no `vbrief/specification.vbrief.json`) export stakeholder-facing spec text via `task project:export-spec`:
82
+
83
+ - ! Source of truth: `vbrief/PROJECT-DEFINITION.vbrief.json` product narratives + lifecycle scope bodies (never the legacy singular spec file).
84
+ - ! Default audience is **stakeholder** — proposed scopes are omitted; only pending/active/completed scopes appear under `## Scope outlook`.
85
+ - ! Internal handoff (setup Phase 3, speckit Phase 3→4 when proposed scopes must be visible) uses `task project:export-spec -- --audience=internal`, which adds `### Not yet accepted (proposed)` under `## Scope outlook` with a fixed disclaimer that proposed scopes are ideas, not approved backlog.
86
+ - ! Phase 3→4 transition gate: **export succeeded** (exit 0), not spec-file `approved` status — PROJECT-DEFINITION has no spec-approval lifecycle on greenfield trees.
87
+ - ~ Legacy migrated trees with `vbrief/specification.vbrief.json` MAY continue using `task spec:render` until fully cut over.
88
+ - ⊗ Invoke `task spec:render` on a greenfield tree that lacks `specification.vbrief.json` — use `task project:export-spec` instead.
89
+
79
90
  ### plan.vbrief.json and continue.vbrief.json
80
91
  - Session/tactical state files are permitted at vbrief/ root (they carry `planRef` links). They are not part of the "spec output" contract but strategies that maintain chaining state (e.g. interview) update them per their own rules.
81
92
 
@@ -85,7 +96,7 @@ All five lifecycle folders MUST be present (even if empty). This is the cutover
85
96
  |--------------|------------------|-------------------------------|-------------------------------|-----------------------------------------|-----------------------------|----------------------------------------|
86
97
  | interview | spec-generating | Yes | Yes (narratives + items) | proposed/YYYY-MM-DD-*.vbrief.json only | Never (post-migration) | Omit or deprecation redirect only |
87
98
  | yolo | spec-generating | Yes | Yes | proposed/YYYY-MM-DD-*.vbrief.json only | Never | Omit or deprecation redirect only |
88
- | speckit | spec-generating | Yes | Yes (Phase 1+) | proposed/YYYY-MM-DD-*.vbrief.json only (phases + stories) | Never | Omit (use task spec:render post) |
99
+ | speckit | spec-generating | Yes | Yes (Phase 1+) | proposed/YYYY-MM-DD-*.vbrief.json only (phases + stories) | Never | Omit or `task project:export-spec` (legacy: `task spec:render` on migrated trees) |
89
100
  | rapid | spec-generating | Yes | Yes | proposed/YYYY-MM-DD-*.vbrief.json only | Never | Omit or deprecation redirect only |
90
101
  | enterprise | spec-generating | Yes | Yes | proposed/YYYY-MM-DD-*.vbrief.json only | Never | Omit or deprecation redirect only |
91
102
  | preparatory (research, discuss, map, etc.) | preparatory | Yes (if first touch) | No (unless also spec path) | proposed/YYYY-MM-DD-*.vbrief.json (context/decision vBRIEFs) | N/A | N/A (preparatory only) |
package/tasks/change.yml CHANGED
@@ -3,44 +3,29 @@ version: '3'
3
3
  vars:
4
4
  # Per ../Taskfile.yml header: joinPath is evaluated eagerly by go-task
5
5
  # templating and yields a native-separator absolute path so {{.DEFT_ROOT}}
6
- # resolves correctly under uv on Windows (#566). Used here to pin
7
- # `uv --project` against ancestor pyproject.toml leakage (#1011).
6
+ # resolves correctly under node on Windows (#566).
8
7
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
9
8
 
10
9
  tasks:
10
+ _ts-build:
11
+ internal: true
12
+ desc: "Build @deftai/cli dist/ before TS-backed gates run (#1828 s2)."
13
+ dir: '{{.USER_WORKING_DIR}}'
14
+ cmds:
15
+ - pnpm --dir "{{.DEFT_ROOT}}" run build
16
+
11
17
  changelog:check:
12
18
  desc: Verify CHANGELOG.md has an [Unreleased] section with at least one entry
13
- env:
14
- PYTHONUTF8: "1"
19
+ dir: '{{.USER_WORKING_DIR}}'
20
+ deps:
21
+ - _ts-build
15
22
  cmds:
16
- - cmd: >-
17
- uv --project "{{.DEFT_ROOT}}" run python -c
18
- "import sys, pathlib, re;
19
- p = pathlib.Path('CHANGELOG.md');
20
- (not p.exists()) and (print('FAIL: CHANGELOG.md not found'), sys.exit(1));
21
- text = p.read_text('utf-8');
22
- m = re.search(r'## \[Unreleased\][ \t]*\n(.*?)(?=\n## \[|$)', text, re.DOTALL);
23
- (m is None) and (print('FAIL: No [Unreleased] section found in CHANGELOG.md'), sys.exit(1));
24
- body = m.group(1);
25
- entries = [l for l in body.splitlines() if l.strip().startswith('- ')];
26
- (len(entries) == 0) and (print('FAIL: [Unreleased] section has no entries (no lines starting with \"- \")'), sys.exit(1));
27
- print(f'OK: CHANGELOG.md [Unreleased] section has {len(entries)} entries')"
23
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" changelog-check --project-root "{{.USER_WORKING_DIR}}"
28
24
 
29
25
  change:init:
30
26
  desc: Create a new change proposal directory structure in history/changes/<name>/
27
+ dir: '{{.USER_WORKING_DIR}}'
28
+ deps:
29
+ - _ts-build
31
30
  cmds:
32
- - cmd: >-
33
- uv --project "{{.DEFT_ROOT}}" run python -c
34
- "import sys, pathlib, json, re;
35
- name = '{{.CLI_ARGS}}'.strip();
36
- (not name) and (print('FAIL: Usage: task change:init -- <name>'), sys.exit(1));
37
- (not re.match(r'^[\w][\w-]*$', name)) and (print('FAIL: Name must contain only alphanumeric characters, underscores, and hyphens'), sys.exit(1));
38
- base = pathlib.Path('history/changes') / name;
39
- base.exists() and (print(f'FAIL: {base} already exists'), sys.exit(1));
40
- (base / 'specs').mkdir(parents=True, exist_ok=True);
41
- proposal = {'vBRIEFInfo': {'version': '0.5'}, 'plan': {'title': name, 'status': 'draft', 'narratives': {'Problem': 'What is wrong or missing.', 'Change': 'What this proposal does about it.', 'Scope': 'In scope: ... Out of scope: ...', 'Impact': 'What existing code/specs are affected.', 'Risks': 'What could go wrong.', 'Approach': 'How to implement the change.', 'Alternatives': 'What else was considered and why not.', 'Dependencies': 'What must exist before this works.'}}};
42
- (base / 'proposal.vbrief.json').write_text(json.dumps(proposal, indent=2) + chr(10), encoding='utf-8');
43
- tasks = {'vBRIEFInfo': {'version': '0.5'}, 'plan': {'title': name, 'status': 'draft', 'items': [], 'edges': []}};
44
- (base / 'tasks.vbrief.json').write_text(json.dumps(tasks, indent=2) + chr(10), encoding='utf-8');
45
- print(f'OK: Created change proposal at {base}/');
46
- [print(f' - {f}') for f in ['proposal.vbrief.json', 'tasks.vbrief.json', 'specs/']]"
31
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" change-init --project-root "{{.USER_WORKING_DIR}}" --name {{.CLI_ARGS}}
package/tasks/ci.yml CHANGED
@@ -1,5 +1,13 @@
1
1
  version: '3'
2
2
 
3
+ # Maintainer-only local CI mirror (#1813 contributor path / #2022 Phase 2).
4
+ #
5
+ # `ci:local` shells into scripts/ci_local.py via `uv run python`. Release
6
+ # Step-5 pre-flight now uses the TS `task check` path (py-purge-ci-local-ts);
7
+ # this fragment remains for explicit maintainer local CI only. Included from
8
+ # the root Taskfile with `internal: true` — not wired into `check:consumer`
9
+ # or the default `task check` aggregate.
10
+
3
11
  vars:
4
12
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
5
13
 
package/tasks/commit.yml CHANGED
@@ -3,28 +3,21 @@ version: '3'
3
3
  vars:
4
4
  # Per ../Taskfile.yml header: joinPath is evaluated eagerly by go-task
5
5
  # templating and yields a native-separator absolute path so {{.DEFT_ROOT}}
6
- # resolves correctly under uv on Windows (#566). Used here to pin
7
- # `uv --project` against ancestor pyproject.toml leakage (#1011).
6
+ # resolves correctly under node on Windows (#566).
8
7
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
9
8
 
10
9
  tasks:
10
+ _ts-build:
11
+ internal: true
12
+ desc: "Build @deftai/cli dist/ before TS-backed gates run (#1828 s2)."
13
+ dir: '{{.USER_WORKING_DIR}}'
14
+ cmds:
15
+ - pnpm --dir "{{.DEFT_ROOT}}" run build
16
+
11
17
  commit:lint:
12
18
  desc: Validate HEAD commit message against conventional commit format
19
+ dir: '{{.USER_WORKING_DIR}}'
20
+ deps:
21
+ - _ts-build
13
22
  cmds:
14
- - cmd: >-
15
- uv --project "{{.DEFT_ROOT}}" run python -c
16
- "import sys, re, subprocess;
17
- result = subprocess.run(['git', 'log', '--format=%B', '-1'], capture_output=True, text=True);
18
- (result.returncode != 0) and (print('FAIL: Could not read HEAD commit message'), sys.exit(1));
19
- msg = result.stdout.strip();
20
- subject = msg.splitlines()[0] if msg else '';
21
- types = 'feat|fix|docs|chore|refactor|test|style|perf|ci|build|revert';
22
- pattern = r'^(' + types + r')(\(.+\))?!?: .+';
23
- ok = re.match(pattern, subject);
24
- (not ok) and (print('FAIL: Commit message does not match conventional commit format'),
25
- print(f' Got: {subject}'),
26
- print(f' Expected: type(scope): description'),
27
- print(f' Types: feat, fix, docs, chore, refactor, test, style, perf, ci, build, revert'),
28
- sys.exit(1));
29
- print(f'OK: Commit message is valid conventional commit');
30
- print(f' Subject: {subject}')"
23
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" commit-lint --project-root "{{.USER_WORKING_DIR}}"
package/tasks/core.yml CHANGED
@@ -1,5 +1,15 @@
1
1
  version: '3'
2
2
 
3
+ # Maintainer-only Python self-test lane (#1813 contributor path / #2022 Phase 2).
4
+ #
5
+ # pytest, ruff, black, and mypy run here via `uv run` for framework-source-repo
6
+ # pre-commit checks. This fragment is included from the root Taskfile with
7
+ # `internal: true` so `core:*` tasks are NOT listed or callable from the CLI on
8
+ # consumer installs — only `task check:framework-source` wires them as deps.
9
+ #
10
+ # Consumer `task check` dispatches to `check:consumer` (TS / deft verbs only).
11
+ # Do NOT add these tasks to the consumer check aggregate.
12
+
3
13
  vars:
4
14
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
5
15
 
@@ -0,0 +1,42 @@
1
+ version: '3'
2
+
3
+ # Consumer engine resolution (#2022 Phase 3).
4
+ #
5
+ # Framework-source checkouts invoke the vendored `packages/cli/dist/bin.js`.
6
+ # npm consumer deposits copy @deftai/directive-content only — no bundled CLI —
7
+ # so tasks fall back to the globally installed `deft` / `directive` command.
8
+
9
+ vars:
10
+ DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
11
+
12
+ tasks:
13
+ _ts-build:
14
+ internal: true
15
+ desc: "Build CLI dist when running from the framework source checkout; no-op on npm consumer deposits (#2022 Phase 3)."
16
+ dir: '{{.DEFT_ROOT}}'
17
+ cmds:
18
+ - |
19
+ set -eu
20
+ if [ -f packages/cli/dist/bin.js ]; then
21
+ pnpm run build
22
+ fi
23
+
24
+ invoke:
25
+ internal: true
26
+ desc: "Run a deft-ts verb from vendored bin.js (source checkout) or global deft (npm consumer deposit)."
27
+ # Run from the operator project root so deft verbs resolve USER_WORKING_DIR
28
+ # correctly; without this, included engine.yml defaults cwd to tasks/ (#2022).
29
+ dir: '{{.USER_WORKING_DIR}}'
30
+ cmds:
31
+ - |
32
+ set -eu
33
+ bin="{{.DEFT_ROOT}}/packages/cli/dist/bin.js"
34
+ if [ -f "$bin" ]; then
35
+ node "$bin" {{.ENGINE_CMD}}
36
+ elif command -v deft >/dev/null 2>&1; then
37
+ deft {{.ENGINE_CMD}}
38
+ else
39
+ echo "deft: neither {{.DEFT_ROOT}}/packages/cli/dist/bin.js nor a global deft command is available." >&2
40
+ echo " Install with: npm i -g @deftai/directive" >&2
41
+ exit 2
42
+ fi
@@ -13,6 +13,9 @@ version: '3'
13
13
  # `cmds:` skip would silently discard the recovery flag.
14
14
 
15
15
  vars:
16
+ # Per ../Taskfile.yml header: joinPath is evaluated eagerly by go-task
17
+ # templating and yields a native-separator absolute path so {{.DEFT_ROOT}}
18
+ # resolves correctly under node on Windows (#566).
16
19
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
17
20
 
18
21
  tasks:
package/tasks/install.yml CHANGED
@@ -3,11 +3,17 @@ version: '3'
3
3
  vars:
4
4
  # Per ../Taskfile.yml header: joinPath is evaluated eagerly by go-task
5
5
  # templating and yields a native-separator absolute path so {{.DEFT_ROOT}}
6
- # resolves correctly under uv on Windows (#566). Used here to pin
7
- # `uv --project` against ancestor pyproject.toml leakage (#1011).
6
+ # resolves correctly under node on Windows (#566).
8
7
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
9
8
 
10
9
  tasks:
10
+ _ts-build:
11
+ internal: true
12
+ desc: "Build @deftai/cli dist/ before TS-backed gates run (#1828 s2)."
13
+ dir: '{{.USER_WORKING_DIR}}'
14
+ cmds:
15
+ - pnpm --dir "{{.DEFT_ROOT}}" run build
16
+
11
17
  install:
12
18
  desc: Install deft (dev convenience -- end users should use the compiled binary from GitHub Releases)
13
19
  cmds:
@@ -15,23 +21,18 @@ tasks:
15
21
 
16
22
  uninstall:
17
23
  desc: Remove deft entry from AGENTS.md
24
+ dir: '{{.USER_WORKING_DIR}}'
25
+ deps:
26
+ - _ts-build
18
27
  cmds:
19
- - cmd: >-
20
- uv --project "{{.DEFT_ROOT}}" run python -c "from pathlib import Path;
21
- f=Path('AGENTS.md');
22
- t=f.read_text('utf-8') if f.exists() else '';
23
- lines=t.splitlines(keepends=True);
24
- out=''.join(l for l in lines if not l.startswith('See deft/main.md') and not l.startswith('Skills: deft/skills/'));
25
- f.write_text(out,'utf-8') if f.exists() else None;
26
- print('Removed deft entry from AGENTS.md' if out!=t else 'No deft entry found in AGENTS.md')"
28
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" install-uninstall --project-root "{{.USER_WORKING_DIR}}"
27
29
 
28
- # User-facing upgrade entrypoint (#1061). Wraps the canonical
29
- # ``run upgrade`` command (`cmd_upgrade` in ./run) which:
30
+ # User-facing upgrade entrypoint (#1061). Native deft-ts handler (#2022 Phase 2):
30
31
  # 1. Writes / refreshes the bare ``.deft-version`` marker.
31
32
  # 2. Writes the canonical YAML provenance manifest at
32
33
  # ``<install>/VERSION`` (#1046 PR-B AC-4) -- including the
33
34
  # ``install_root`` field added in #1062.
34
- # 3. Delegates to ``cmd_agents_refresh`` so the AGENTS.md managed
35
+ # 3. Delegates to ``agents:refresh`` so the AGENTS.md managed
35
36
  # section is brought to the current rendered template (v3 marker
36
37
  # with sha/refreshed/session attributes, #1046 PR-B AC-5).
37
38
  # This wrapper exists so the ``framework:doctor`` failure prose and
@@ -43,18 +44,18 @@ tasks:
43
44
  # ``generates:`` because the task is mutating + must run on every
44
45
  # invocation. CLI_ARGS is explicitly cleared (``vars: { CLI_ARGS: "" }``)
45
46
  # so any operator-supplied ``-- <flags>`` are deterministically dropped
46
- # before reaching ``run upgrade``: go-task only forwards ``{{.CLI_ARGS}}``
47
+ # before reaching the upgrade handler: go-task only forwards ``{{.CLI_ARGS}}``
47
48
  # when a ``cmds`` line references it, but pinning the var to empty makes
48
49
  # the contract explicit AND defends against a future edit that
49
50
  # accidentally appends ``{{.CLI_ARGS}}`` to the dispatch line (SLizard P1
50
- # on PR #1067 -- ``cmd_upgrade`` does not currently accept flags; future
51
+ # on PR #1067 -- the upgrade handler does not currently accept flags; future
51
52
  # flag additions land deliberately, not implicitly via Taskfile drift).
52
53
  upgrade:
53
- desc: "Upgrade deft framework: refresh AGENTS.md to current marker, write canonical install manifest, regenerate .deft-version derivative (#1061). Wraps `run upgrade`."
54
+ desc: "Upgrade deft framework: refresh AGENTS.md to current marker, write canonical install manifest, regenerate .deft-version derivative (#1061). Dispatches via deft-ts install-upgrade."
54
55
  dir: '{{.USER_WORKING_DIR}}'
55
- env:
56
- PYTHONUTF8: "1"
57
56
  vars:
58
57
  CLI_ARGS: ""
58
+ deps:
59
+ - _ts-build
59
60
  cmds:
60
- - uv --project "{{.DEFT_ROOT}}" run python "{{.DEFT_ROOT}}/run" upgrade
61
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" install-upgrade --project-root "{{.USER_WORKING_DIR}}" --framework-root "{{.DEFT_ROOT}}"
package/tasks/migrate.yml CHANGED
@@ -1,13 +1,22 @@
1
1
  version: '3'
2
2
 
3
3
  vars:
4
- # See deft/Taskfile.yml for the rationale behind per-subfile DEFT_ROOT
5
- # (go-task re-evaluates var templates at use site; joinPath is eager and
6
- # produces a clean, native-separator path so `uv --project "{{.DEFT_ROOT}}" run python` resolves it
7
- # correctly on Windows -- #566).
4
+ # Per ../Taskfile.yml header: joinPath is evaluated eagerly by go-task
5
+ # templating and yields a native-separator absolute path so {{.DEFT_ROOT}}
6
+ # resolves correctly under node on Windows (#566).
8
7
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
9
8
 
10
9
  tasks:
10
+ _ts-build:
11
+ internal: true
12
+ desc: "Build @deftai/cli dist/ before TS-backed gates run (#1828 s2)."
13
+ # Match scope.yml _ensure-ts: run from USER_WORKING_DIR with pnpm --dir so
14
+ # Windows consumer invocations via `-t <abs Taskfile>` do not double-prefix
15
+ # DEFT_ROOT under go-task's dir: handling (#566 / Windows CI regression).
16
+ dir: '{{.USER_WORKING_DIR}}'
17
+ cmds:
18
+ - pnpm --dir "{{.DEFT_ROOT}}" run build
19
+
11
20
  preflight:
12
21
  # Agent-side environment preflight for `task migrate:vbrief` (#793).
13
22
  # Reifies the prose contract documented in
@@ -18,16 +27,19 @@ tasks:
18
27
  # any destructive mutation runs. Three-state exit (0 ready / 1 not-ready / 2 config
19
28
  # error) mirrors `scripts/preflight_branch.py` (#747).
20
29
  #
30
+ # Consumer path dispatches through the native deft-ts handler (#2022 Phase 2);
31
+ # the legacy Python script remains for maintainer parity only.
32
+ #
21
33
  # NO `sources:` / `generates:` per `conventions/task-caching.md`: the
22
34
  # script forwards user-facing CLI flags (`--project-root`, `--deft-root`,
23
35
  # `--quiet`) via `{{.CLI_ARGS}}` and a cached cmds skip would silently
24
36
  # swallow them.
25
37
  desc: "Verify environment readiness for `task migrate:vbrief` (uv on PATH, v0.20+ layout, document model, git tree). Three-state exit (#793)."
26
38
  dir: '{{.USER_WORKING_DIR}}'
27
- env:
28
- PYTHONUTF8: "1"
39
+ deps:
40
+ - _ts-build
29
41
  cmds:
30
- - uv --project "{{.DEFT_ROOT}}" run --frozen python "{{.DEFT_ROOT}}/scripts/migrate_preflight.py" --project-root "{{.USER_WORKING_DIR}}" --deft-root "{{.DEFT_ROOT}}" {{.CLI_ARGS}}
42
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" migrate-preflight --project-root "{{.USER_WORKING_DIR}}" --deft-root "{{.DEFT_ROOT}}" {{.CLI_ARGS}}
31
43
 
32
44
  vbrief:
33
45
  desc: >
@@ -44,12 +56,15 @@ tasks:
44
56
  run::_check_upgrade_gate is consulted by the CLI flow). See
45
57
  events/registry.json for the payload contracts and `DEFT_EVENT_LOG` for
46
58
  opt-in JSON-line capture (#635).
59
+ #2013 HOLDOUT: `migrate:vbrief` remains the sole consumer-path Python
60
+ entrypoint until the spec-authority umbrella resolves the
61
+ `migrate_vbrief.py` port-vs-cutoff decision (#2022 Phase 2).
47
62
  deps:
48
63
  # #793: gate destructive migration on the environment preflight. A
49
64
  # non-zero exit from the preflight task aborts this task before any
50
65
  # `cmds:` line runs. Operator-supplied `{{.CLI_ARGS}}` (e.g.
51
66
  # `--dry-run`, `--force`, `--rollback`, `--strict`) belong to the
52
- # migrator and are NOT valid migrate_preflight.py flags. go-task
67
+ # migrator and are NOT valid migrate:preflight flags. go-task
53
68
  # forwards parent CLI_ARGS into dependency tasks by default, so we
54
69
  # explicitly clear CLI_ARGS here; otherwise `task migrate:vbrief --
55
70
  # --dry-run` reaches the preflight argparse surface as
@@ -63,11 +78,7 @@ tasks:
63
78
  env:
64
79
  PYTHONUTF8: "1"
65
80
  cmds:
66
- # Pass Taskfile CLI args through so operators can opt into safety flags
67
- # (#497) or --strict reconciliation (#496) without editing this file.
68
- # Script path uses {{.DEFT_ROOT}} (defined locally in this file's
69
- # `vars:` block above via `joinPath .TASKFILE_DIR ".."` -- see
70
- # ../Taskfile.yml for why root-level definition is avoided) rather
71
- # than {{.TASKFILE_DIR}}/.. to keep the path traversal-free; the
72
- # mixed-separator form breaks `uv --project "{{.DEFT_ROOT}}" run python` on Windows (#566).
81
+ # #2013 holdout: migrate_vbrief.py stays on the consumer path until the
82
+ # spec-authority umbrella closes the port-vs-cutoff decision (#2022).
83
+ # Preflight is TS-native; the migrator body remains Python for now.
73
84
  - uv --project "{{.DEFT_ROOT}}" run python "{{.DEFT_ROOT}}/scripts/migrate_vbrief.py" "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}