@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
@@ -1,277 +0,0 @@
1
- """Lifecycle folder routing for reconciled scope items (Agent B, #499).
2
-
3
- Single source of truth for the lifecycle <-> status mapping used by
4
- ``migrate:vbrief``. The mapping mirrors the authoritative table in master
5
- tracking issue #506 (Shared conventions) and the schema vocabulary in
6
- ``vbrief/schemas/vbrief-core.schema.json``:
7
-
8
- proposed/ <-> draft | proposed
9
- pending/ <-> approved | pending
10
- active/ <-> running | blocked
11
- completed/ <-> completed
12
- cancelled/ <-> cancelled
13
-
14
- The migrator MUST NOT emit the legacy value ``in_progress`` -- this was the
15
- critical correction to the original #499 issue body. Use ``running``.
16
-
17
- Exposes:
18
- * FOLDER_TO_STATUSES / STATUS_TO_FOLDER
19
- * folder_for_status(status) -> folder
20
- * default_status_for_folder(folder) -> status
21
- * plan_status_matches_folder(status, folder) -> bool
22
- * build_scope_vbrief_from_reconciled(reconciled, repo_url) -> dict
23
- """
24
-
25
- from __future__ import annotations
26
-
27
- import sys
28
- from datetime import UTC, datetime
29
- from pathlib import Path
30
- from typing import Any
31
-
32
- # Make the sibling ``_vbrief_build`` helper importable whether this module is
33
- # imported as part of the ``scripts/`` package layout or as a top-level module.
34
- sys.path.insert(0, str(Path(__file__).resolve().parent))
35
-
36
- from _vbrief_build import ( # noqa: E402
37
- MIGRATOR_METADATA_KEY as _MIGRATOR_METADATA_KEY,
38
- create_scope_vbrief as _create_scope_vbrief,
39
- )
40
-
41
-
42
- def _migration_timestamp() -> str:
43
- """Return an ISO-8601 UTC timestamp for ``vBRIEFInfo.updated`` stamps.
44
-
45
- Emitted at second precision. This helper is ONLY the fallback used when
46
- ``build_scope_vbrief_from_reconciled(..., migration_timestamp=None)`` is
47
- called directly. Under the normal ``migrate()`` entry point the caller
48
- always passes ``migration_timestamp=migrate_vbrief._MIGRATION_TIMESTAMP``
49
- (a module-level constant stamped once per migration run), so this helper
50
- is effectively unreachable there.
51
-
52
- Test-pinning knob: deterministic migrate() tests (for example the
53
- byte-for-byte golden-fixture suite in ``test_migrate_vbrief.py``) MUST
54
- monkeypatch ``migrate_vbrief._MIGRATION_TIMESTAMP`` -- NOT this helper --
55
- because the full migrate() path always threads the module-level constant
56
- through to ``build_scope_vbrief_from_reconciled(migration_timestamp=...)``.
57
- Callers that invoke ``build_scope_vbrief_from_reconciled`` directly can
58
- either pass ``migration_timestamp=`` explicitly or monkeypatch this helper.
59
- """
60
- return datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
61
-
62
- # ---------------------------------------------------------------------------
63
- # Lifecycle <-> status mapping (#506 Shared conventions, schema-locked)
64
- # ---------------------------------------------------------------------------
65
-
66
- FOLDER_TO_STATUSES: dict[str, tuple[str, ...]] = {
67
- "proposed": ("draft", "proposed"),
68
- "pending": ("approved", "pending"),
69
- "active": ("running", "blocked"),
70
- "completed": ("completed",),
71
- "cancelled": ("cancelled",),
72
- }
73
-
74
- STATUS_TO_FOLDER: dict[str, str] = {
75
- status: folder
76
- for folder, statuses in FOLDER_TO_STATUSES.items()
77
- for status in statuses
78
- }
79
-
80
- LIFECYCLE_FOLDERS: tuple[str, ...] = tuple(FOLDER_TO_STATUSES.keys())
81
-
82
- # Canonical default status the migrator emits when a folder is chosen but no
83
- # sharper signal exists (e.g. orphans routed to proposed/ use ``proposed`` not
84
- # ``draft``; reconciled-active with no explicit blocked signal uses ``running``).
85
- DEFAULT_STATUS_FOR_FOLDER: dict[str, str] = {
86
- "proposed": "proposed",
87
- "pending": "pending",
88
- "active": "running",
89
- "completed": "completed",
90
- "cancelled": "cancelled",
91
- }
92
-
93
-
94
- def folder_for_status(status: str) -> str:
95
- """Return the canonical lifecycle folder for a schema status.
96
-
97
- Raises ``ValueError`` for unknown statuses so callers can surface the
98
- corruption early rather than silently routing to ``pending/``.
99
- """
100
- try:
101
- return STATUS_TO_FOLDER[status]
102
- except KeyError as exc: # pragma: no cover - defensive
103
- raise ValueError(
104
- f"No lifecycle folder defined for status {status!r}; "
105
- f"expected one of {sorted(STATUS_TO_FOLDER)}."
106
- ) from exc
107
-
108
-
109
- def default_status_for_folder(folder: str) -> str:
110
- """Return the canonical default status the migrator uses for a folder."""
111
- try:
112
- return DEFAULT_STATUS_FOR_FOLDER[folder]
113
- except KeyError as exc: # pragma: no cover - defensive
114
- raise ValueError(
115
- f"Unknown lifecycle folder {folder!r}; expected one of "
116
- f"{sorted(DEFAULT_STATUS_FOR_FOLDER)}."
117
- ) from exc
118
-
119
-
120
- def plan_status_matches_folder(status: str, folder: str) -> bool:
121
- """Return True if ``status`` is permitted inside ``folder/`` per #506."""
122
- return status in FOLDER_TO_STATUSES.get(folder, ())
123
-
124
-
125
- # ---------------------------------------------------------------------------
126
- # Scope vBRIEF construction from reconciled item
127
- # ---------------------------------------------------------------------------
128
-
129
-
130
- def _narrative_str(value: Any) -> str:
131
- """Coerce a narrative field to a stripped string (schema requires strings)."""
132
- if value is None:
133
- return ""
134
- if isinstance(value, str):
135
- return value.strip()
136
- return str(value).strip()
137
-
138
-
139
- def build_scope_vbrief_from_reconciled(
140
- reconciled: dict,
141
- repo_url: str = "",
142
- migration_timestamp: str | None = None,
143
- ) -> dict:
144
- """Build a scope vBRIEF dict from a reconciled item (#496 + #499 + #616).
145
-
146
- ``reconciled`` is a dict with the following recognised keys (produced by
147
- ``_vbrief_reconciliation.reconcile_scope_items``):
148
-
149
- number, task_id, title, status, folder, description, description_source,
150
- status_source, title_source, phase, phase_description, tier, spec_phase,
151
- roadmap_summary, source_conflict, override_applied, references.
152
-
153
- The output preserves the ``_create_scope_vbrief`` envelope shape that
154
- tests already rely on. Per issue #616 (option A, scope-clamped) the
155
- user-visible ``plan.narratives`` is left almost EMPTY on per-issue
156
- scope vBRIEFs -- ROADMAP rows do not carry enough data to populate
157
- any canonical narrative key meaningfully. Reconciliation provenance
158
- (Description / Description_source / Status_source / Title_source /
159
- SpecPhase / RoadmapSummary / SourceConflict) is relocated to
160
- ``plan.metadata['x-migrator']`` so downstream tooling that cares
161
- about SPEC/ROADMAP lineage can still read it without the invented
162
- keys leaking into the user-facing summary surface.
163
-
164
- ``SourceSection`` is the named exception to the #616 clamp: it
165
- remains in ``plan.narratives`` because it is a deliberate,
166
- user-visible audit-trail narrative (``ROADMAP Completed section``
167
- vs ``ROADMAP active phase``) added by #593 so operators can audit
168
- the routing decision post-migration without re-running the
169
- migrator. Unlike the reconciler-internal provenance above (which
170
- really is plumbing noise), SourceSection is intentional signal.
171
- The Windows task-dispatch regression asserts
172
- ``plan.narratives.SourceSection`` specifically.
173
- """
174
- status = reconciled.get("status") or default_status_for_folder(
175
- reconciled.get("folder", "pending")
176
- )
177
-
178
- # Seed with the shared helper so origin-provenance (references) and the
179
- # vBRIEFInfo envelope stay consistent with non-reconciled scope vBRIEFs.
180
- seed_item = {
181
- "number": reconciled.get("number", ""),
182
- "title": reconciled.get("title", "Untitled"),
183
- "phase": reconciled.get("phase", ""),
184
- "tier": reconciled.get("tier", ""),
185
- }
186
- scope = _create_scope_vbrief(
187
- seed_item,
188
- repo_url=repo_url,
189
- status=status,
190
- phase_description=reconciled.get("phase_description", ""),
191
- )
192
-
193
- # #616: migrator provenance flows into plan.metadata['x-migrator'],
194
- # NOT plan.narratives. The shared ``create_scope_vbrief`` helper
195
- # already seeded Phase / Tier / PhaseDescription under the same key
196
- # when populated; we extend that bucket with reconciler-specific
197
- # fields so a single metadata blob captures the full lineage.
198
- plan_meta = scope["plan"].setdefault("metadata", {})
199
- migrator_meta = plan_meta.setdefault(_MIGRATOR_METADATA_KEY, {})
200
-
201
- def _store(key: str, value: Any) -> None:
202
- coerced = _narrative_str(value)
203
- if coerced:
204
- migrator_meta[key] = coerced
205
-
206
- _store("Description", reconciled.get("description"))
207
- _store("Description_source", reconciled.get("description_source"))
208
- _store("Status_source", reconciled.get("status_source"))
209
- _store("Title_source", reconciled.get("title_source"))
210
- _store("SpecPhase", reconciled.get("spec_phase"))
211
- _store("RoadmapSummary", reconciled.get("roadmap_summary"))
212
- _store("SourceConflict", reconciled.get("source_conflict"))
213
-
214
- # #593: SourceSection is the named exception to the #616 narrative
215
- # clamp -- it is a deliberate, user-visible audit-trail narrative
216
- # (``ROADMAP Completed section`` vs ``ROADMAP active phase``) that
217
- # operators need surfaced at the narrative level so the routing
218
- # decision is auditable without re-running the migrator. Unlike the
219
- # reconciler-internal provenance above (Description_source /
220
- # Status_source / ...), which is plumbing noise, SourceSection is
221
- # intended signal. The Windows task-dispatch regression asserts
222
- # ``plan.narratives.SourceSection`` specifically. Single source of
223
- # truth: we record it in narratives only, not duplicated under
224
- # x-migrator metadata.
225
- source_section = _narrative_str(reconciled.get("source_section"))
226
- if source_section:
227
- narratives = scope["plan"].setdefault("narratives", {})
228
- narratives["SourceSection"] = source_section
229
-
230
- # Clean up an empty migrator bucket so the emitted JSON doesn't
231
- # carry an empty ``metadata.x-migrator`` payload on fully bare
232
- # reconciled items (happens in unit tests that bypass the
233
- # reconciler). Mirrors the clean-up path in ``create_scope_vbrief``
234
- # where plan.metadata is only materialised when there is something
235
- # to store.
236
- if not migrator_meta:
237
- plan_meta.pop(_MIGRATOR_METADATA_KEY, None)
238
- if not plan_meta:
239
- scope["plan"].pop("metadata", None)
240
-
241
- # #593: stamp ``vBRIEFInfo.updated`` with the migration timestamp when
242
- # we route an item to ``completed/``. The vBRIEF carries completion
243
- # provenance in its envelope so downstream tooling that sorts by
244
- # completion time has a non-null date to work with. Active/pending/
245
- # proposed items do not receive an ``updated`` stamp because the
246
- # scope has not yet reached a terminal state. ``migration_timestamp``
247
- # is pinnable by callers (tests monkeypatch it for byte-for-byte
248
- # fixture determinism).
249
- if reconciled.get("status") == "completed":
250
- envelope = scope.setdefault("vBRIEFInfo", {})
251
- if isinstance(envelope, dict):
252
- envelope.setdefault(
253
- "updated", migration_timestamp or _migration_timestamp()
254
- )
255
-
256
- # Preserve any explicitly supplied references (e.g. spec back-link) on top
257
- # of the origin-provenance reference set by ``_create_scope_vbrief``.
258
- extra_refs = reconciled.get("references") or []
259
- if extra_refs:
260
- existing = scope["plan"].setdefault("references", [])
261
- for ref in extra_refs:
262
- if isinstance(ref, dict) and ref not in existing:
263
- existing.append(ref)
264
-
265
- return scope
266
-
267
-
268
- __all__ = [
269
- "DEFAULT_STATUS_FOR_FOLDER",
270
- "FOLDER_TO_STATUSES",
271
- "LIFECYCLE_FOLDERS",
272
- "STATUS_TO_FOLDER",
273
- "build_scope_vbrief_from_reconciled",
274
- "default_status_for_folder",
275
- "folder_for_status",
276
- "plan_status_matches_folder",
277
- ]