@deftai/directive-content 0.59.0 → 0.60.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 (184) hide show
  1. package/.githooks/pre-push +10 -9
  2. package/Taskfile.yml +48 -58
  3. package/UPGRADING.md +1 -1
  4. package/docs/assets/directive-lifecycle-diagram.png +0 -0
  5. package/docs/directive-lifecycle.md +73 -0
  6. package/docs/getting-started.md +5 -1
  7. package/package.json +3 -3
  8. package/packs/skills/skills-pack-0.1.json +22 -22
  9. package/scm/github.md +20 -2
  10. package/tasks/change.yml +16 -31
  11. package/tasks/ci.yml +8 -0
  12. package/tasks/commit.yml +12 -19
  13. package/tasks/core.yml +10 -0
  14. package/tasks/engine.yml +42 -0
  15. package/tasks/framework.yml +3 -0
  16. package/tasks/install.yml +20 -19
  17. package/tasks/migrate.yml +26 -15
  18. package/tasks/project.yml +16 -0
  19. package/tasks/toolchain.yml +15 -5
  20. package/tasks/vbrief.yml +4 -3
  21. package/tasks/verify.yml +12 -14
  22. package/scripts/_agents_md.py +0 -494
  23. package/scripts/_cache_fetch.py +0 -635
  24. package/scripts/_cache_quota.py +0 -529
  25. package/scripts/_cache_refresh.py +0 -163
  26. package/scripts/_cache_validate.py +0 -209
  27. package/scripts/_content_root.py +0 -42
  28. package/scripts/_doctor_state.py +0 -277
  29. package/scripts/_event_detect.py +0 -305
  30. package/scripts/_events.py +0 -514
  31. package/scripts/_lifecycle_hygiene.py +0 -568
  32. package/scripts/_pathspec.py +0 -91
  33. package/scripts/_policy_show_cli.py +0 -266
  34. package/scripts/_precutover.py +0 -92
  35. package/scripts/_project_context.py +0 -224
  36. package/scripts/_project_definition_io.py +0 -164
  37. package/scripts/_relocate_snapshot.py +0 -209
  38. package/scripts/_relocate_states.py +0 -343
  39. package/scripts/_resolve_preflight_path.py +0 -152
  40. package/scripts/_safe_subprocess.py +0 -167
  41. package/scripts/_session_start_hook.py +0 -205
  42. package/scripts/_sor_gate_diff.py +0 -365
  43. package/scripts/_stdio_utf8.py +0 -59
  44. package/scripts/_triage_bootstrap_gitignore.py +0 -904
  45. package/scripts/_triage_classify_cli.py +0 -122
  46. package/scripts/_triage_queue_cli.py +0 -625
  47. package/scripts/_triage_scope_cli.py +0 -343
  48. package/scripts/_triage_scope_drift_cli.py +0 -121
  49. package/scripts/_triage_scope_ignores.py +0 -286
  50. package/scripts/_triage_scope_milestone.py +0 -432
  51. package/scripts/_triage_scope_mutations.py +0 -337
  52. package/scripts/_triage_scope_renderers.py +0 -207
  53. package/scripts/_triage_smoketest_stages.py +0 -674
  54. package/scripts/_triage_subscribe_cli.py +0 -140
  55. package/scripts/_triage_welcome_cli.py +0 -421
  56. package/scripts/_vbrief_build.py +0 -239
  57. package/scripts/_vbrief_fidelity.py +0 -479
  58. package/scripts/_vbrief_legacy.py +0 -589
  59. package/scripts/_vbrief_reconciliation.py +0 -883
  60. package/scripts/_vbrief_routing.py +0 -277
  61. package/scripts/_vbrief_safety.py +0 -778
  62. package/scripts/_vbrief_sources.py +0 -312
  63. package/scripts/_vbrief_speckit.py +0 -262
  64. package/scripts/_vbrief_story_quality.py +0 -353
  65. package/scripts/_vbrief_validation.py +0 -299
  66. package/scripts/build_dist.py +0 -412
  67. package/scripts/cache.py +0 -1078
  68. package/scripts/cache_scanner.py +0 -745
  69. package/scripts/candidates_log.py +0 -432
  70. package/scripts/capacity_backfill.py +0 -680
  71. package/scripts/capacity_show.py +0 -653
  72. package/scripts/ci_local.py +0 -689
  73. package/scripts/code_structure_validate.py +0 -765
  74. package/scripts/codebase_default_extractor.py +0 -495
  75. package/scripts/codebase_map.py +0 -304
  76. package/scripts/codebase_map_fresh.py +0 -104
  77. package/scripts/codebase_projection_registry.py +0 -94
  78. package/scripts/codebase_provider.py +0 -582
  79. package/scripts/doctor.py +0 -2552
  80. package/scripts/framework_commands.py +0 -505
  81. package/scripts/gh_rest.py +0 -882
  82. package/scripts/github_auth_modes.py +0 -437
  83. package/scripts/github_body.py +0 -292
  84. package/scripts/ip_risk.py +0 -531
  85. package/scripts/issue_emit.py +0 -670
  86. package/scripts/issue_ingest.py +0 -1064
  87. package/scripts/migrate_preflight.py +0 -418
  88. package/scripts/migrate_vbrief.py +0 -2677
  89. package/scripts/monitor_pr.py +0 -401
  90. package/scripts/pack_migrate_lessons.py +0 -336
  91. package/scripts/pack_migrate_patterns.py +0 -254
  92. package/scripts/pack_migrate_rules.py +0 -350
  93. package/scripts/pack_migrate_skills.py +0 -423
  94. package/scripts/pack_migrate_strategies.py +0 -311
  95. package/scripts/pack_migrate_swarm_spec.py +0 -250
  96. package/scripts/pack_render.py +0 -434
  97. package/scripts/packs_slice.py +0 -712
  98. package/scripts/platform_capabilities.py +0 -336
  99. package/scripts/policy.py +0 -2826
  100. package/scripts/policy_set.py +0 -324
  101. package/scripts/pr_check_closing_keywords.py +0 -524
  102. package/scripts/pr_check_protected_issues.py +0 -267
  103. package/scripts/pr_merge_readiness.py +0 -1004
  104. package/scripts/pr_wait_mergeable.py +0 -669
  105. package/scripts/prd_render.py +0 -159
  106. package/scripts/preflight_architecture_sor.py +0 -974
  107. package/scripts/preflight_branch.py +0 -289
  108. package/scripts/preflight_cache.py +0 -974
  109. package/scripts/preflight_gh.py +0 -721
  110. package/scripts/preflight_implementation.py +0 -272
  111. package/scripts/preflight_story_start.py +0 -838
  112. package/scripts/preflight_wip_cap.py +0 -149
  113. package/scripts/probe_session.py +0 -545
  114. package/scripts/project_render.py +0 -293
  115. package/scripts/quarantine_ext.py +0 -237
  116. package/scripts/reconcile_issues.py +0 -1442
  117. package/scripts/refresh-path.ps1 +0 -107
  118. package/scripts/release.py +0 -2030
  119. package/scripts/release_e2e.py +0 -1011
  120. package/scripts/release_publish.py +0 -486
  121. package/scripts/release_rollback.py +0 -980
  122. package/scripts/relocate.py +0 -1034
  123. package/scripts/resolve_changelog_unreleased.py +0 -667
  124. package/scripts/resolve_version.py +0 -490
  125. package/scripts/resume_conditions.py +0 -706
  126. package/scripts/ritual_sentinel.py +0 -609
  127. package/scripts/roadmap_render.py +0 -635
  128. package/scripts/rule_ownership_lint.py +0 -325
  129. package/scripts/scm.py +0 -591
  130. package/scripts/scope_audit_log.py +0 -387
  131. package/scripts/scope_decompose.py +0 -654
  132. package/scripts/scope_demote.py +0 -509
  133. package/scripts/scope_lifecycle.py +0 -1126
  134. package/scripts/scope_undo.py +0 -772
  135. package/scripts/session_start.py +0 -406
  136. package/scripts/setup_ghx.py +0 -339
  137. package/scripts/setup_windows.ps1 +0 -220
  138. package/scripts/slice_audit.py +0 -585
  139. package/scripts/slice_record.py +0 -530
  140. package/scripts/slice_record_existing.py +0 -692
  141. package/scripts/slug_normalize.py +0 -178
  142. package/scripts/spec_render.py +0 -477
  143. package/scripts/spec_validate.py +0 -238
  144. package/scripts/subagent_monitor.py +0 -658
  145. package/scripts/swarm_complete_cohort.py +0 -644
  146. package/scripts/swarm_launch.py +0 -1206
  147. package/scripts/swarm_readiness.py +0 -554
  148. package/scripts/swarm_verify_review_clean.py +0 -438
  149. package/scripts/swarm_worktrees.py +0 -497
  150. package/scripts/toolchain-check.py +0 -52
  151. package/scripts/triage_actions.py +0 -871
  152. package/scripts/triage_bootstrap.py +0 -1153
  153. package/scripts/triage_bulk.py +0 -630
  154. package/scripts/triage_classify.py +0 -932
  155. package/scripts/triage_help.py +0 -1685
  156. package/scripts/triage_queue.py +0 -1944
  157. package/scripts/triage_reconcile.py +0 -581
  158. package/scripts/triage_refresh.py +0 -643
  159. package/scripts/triage_scope.py +0 -999
  160. package/scripts/triage_scope_drift.py +0 -575
  161. package/scripts/triage_smoketest.py +0 -396
  162. package/scripts/triage_subscribe.py +0 -399
  163. package/scripts/triage_summary.py +0 -1011
  164. package/scripts/triage_welcome.py +0 -1178
  165. package/scripts/ts_check_lane.py +0 -86
  166. package/scripts/validate-links.py +0 -64
  167. package/scripts/validate_strategy_output.py +0 -212
  168. package/scripts/vbrief_activate.py +0 -228
  169. package/scripts/vbrief_migrate_conformance.py +0 -368
  170. package/scripts/vbrief_reconcile_graph.py +0 -306
  171. package/scripts/vbrief_reconcile_labels.py +0 -460
  172. package/scripts/vbrief_reconcile_umbrellas.py +0 -741
  173. package/scripts/vbrief_validate.py +0 -1144
  174. package/scripts/verify-stubs.py +0 -61
  175. package/scripts/verify_capacity.py +0 -160
  176. package/scripts/verify_encoding.py +0 -699
  177. package/scripts/verify_hooks_installed.py +0 -206
  178. package/scripts/verify_investigation.py +0 -360
  179. package/scripts/verify_judgment_gates.py +0 -827
  180. package/scripts/verify_no_task_runtime.py +0 -171
  181. package/scripts/verify_scm_boundary.py +0 -509
  182. package/scripts/verify_session_ritual.py +0 -389
  183. package/scripts/verify_tools.py +0 -426
  184. 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
- ]