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