@event4u/agent-config 1.12.0 → 1.14.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 (260) hide show
  1. package/.agent-src/commands/agent-handoff.md +3 -0
  2. package/.agent-src/commands/agent-status.md +3 -0
  3. package/.agent-src/commands/agents-audit.md +4 -0
  4. package/.agent-src/commands/agents-cleanup.md +6 -1
  5. package/.agent-src/commands/agents-prepare.md +3 -0
  6. package/.agent-src/commands/analyze-reference-repo.md +4 -0
  7. package/.agent-src/commands/bug-fix.md +5 -1
  8. package/.agent-src/commands/bug-investigate.md +4 -0
  9. package/.agent-src/commands/chat-history-checkpoint.md +126 -0
  10. package/.agent-src/commands/chat-history-clear.md +5 -0
  11. package/.agent-src/commands/chat-history-resume.md +5 -0
  12. package/.agent-src/commands/chat-history.md +5 -0
  13. package/.agent-src/commands/check-current-md.md +126 -0
  14. package/.agent-src/commands/commit-in-chunks.md +98 -0
  15. package/.agent-src/commands/commit.md +4 -0
  16. package/.agent-src/commands/compress.md +3 -0
  17. package/.agent-src/commands/context-create.md +4 -0
  18. package/.agent-src/commands/context-refactor.md +4 -0
  19. package/.agent-src/commands/copilot-agents-init.md +3 -0
  20. package/.agent-src/commands/copilot-agents-optimize.md +3 -0
  21. package/.agent-src/commands/create-pr-description.md +4 -0
  22. package/.agent-src/commands/create-pr.md +4 -0
  23. package/.agent-src/commands/do-and-judge.md +4 -1
  24. package/.agent-src/commands/do-in-steps.md +3 -0
  25. package/.agent-src/commands/e2e-heal.md +4 -0
  26. package/.agent-src/commands/e2e-plan.md +4 -0
  27. package/.agent-src/commands/estimate-ticket.md +4 -1
  28. package/.agent-src/commands/feature-dev.md +4 -0
  29. package/.agent-src/commands/feature-explore.md +4 -0
  30. package/.agent-src/commands/feature-plan.md +4 -0
  31. package/.agent-src/commands/feature-refactor.md +4 -0
  32. package/.agent-src/commands/feature-roadmap.md +6 -0
  33. package/.agent-src/commands/fix-ci.md +4 -0
  34. package/.agent-src/commands/fix-portability.md +3 -0
  35. package/.agent-src/commands/fix-pr-bot-comments.md +4 -0
  36. package/.agent-src/commands/fix-pr-comments.md +4 -0
  37. package/.agent-src/commands/fix-pr-developer-comments.md +4 -0
  38. package/.agent-src/commands/fix-references.md +3 -0
  39. package/.agent-src/commands/fix-seeder.md +4 -0
  40. package/.agent-src/commands/implement-ticket.md +39 -13
  41. package/.agent-src/commands/jira-ticket.md +4 -0
  42. package/.agent-src/commands/judge.md +3 -0
  43. package/.agent-src/commands/memory-add.md +5 -3
  44. package/.agent-src/commands/memory-full.md +5 -2
  45. package/.agent-src/commands/memory-promote.md +7 -6
  46. package/.agent-src/commands/mode.md +3 -0
  47. package/.agent-src/commands/module-create.md +4 -0
  48. package/.agent-src/commands/module-explore.md +4 -0
  49. package/.agent-src/commands/onboard.md +24 -0
  50. package/.agent-src/commands/optimize-agents.md +4 -0
  51. package/.agent-src/commands/optimize-augmentignore.md +3 -0
  52. package/.agent-src/commands/optimize-rtk-filters.md +3 -0
  53. package/.agent-src/commands/optimize-skills.md +4 -0
  54. package/.agent-src/commands/override-create.md +4 -0
  55. package/.agent-src/commands/override-manage.md +4 -0
  56. package/.agent-src/commands/package-reset.md +3 -0
  57. package/.agent-src/commands/package-test.md +3 -0
  58. package/.agent-src/commands/prepare-for-review.md +4 -0
  59. package/.agent-src/commands/project-analyze.md +4 -0
  60. package/.agent-src/commands/project-health.md +4 -0
  61. package/.agent-src/commands/propose-memory.md +6 -8
  62. package/.agent-src/commands/quality-fix.md +4 -0
  63. package/.agent-src/commands/refine-ticket.md +4 -1
  64. package/.agent-src/commands/review-changes.md +4 -0
  65. package/.agent-src/commands/review-routing.md +4 -0
  66. package/.agent-src/commands/roadmap-create.md +7 -0
  67. package/.agent-src/commands/roadmap-execute.md +12 -1
  68. package/.agent-src/commands/rule-compliance-audit.md +4 -0
  69. package/.agent-src/commands/set-cost-profile.md +3 -0
  70. package/.agent-src/commands/sync-agent-settings.md +3 -0
  71. package/.agent-src/commands/sync-gitignore.md +3 -0
  72. package/.agent-src/commands/tests-create.md +4 -0
  73. package/.agent-src/commands/tests-execute.md +4 -0
  74. package/.agent-src/commands/threat-model.md +4 -0
  75. package/.agent-src/commands/update-form-request-messages.md +4 -0
  76. package/.agent-src/commands/upstream-contribute.md +4 -0
  77. package/.agent-src/commands/work.md +161 -0
  78. package/.agent-src/guidelines/agent-infra/engineering-memory-data-format.md +2 -6
  79. package/.agent-src/guidelines/agent-infra/layered-settings.md +0 -1
  80. package/.agent-src/guidelines/agent-infra/memory-access.md +0 -7
  81. package/.agent-src/guidelines/agent-infra/role-contracts.md +2 -4
  82. package/.agent-src/guidelines/agent-infra/self-improvement-pipeline.md +0 -1
  83. package/.agent-src/guidelines/php/patterns/strategy.md +180 -2
  84. package/.agent-src/personas/README.md +0 -1
  85. package/.agent-src/rules/artifact-drafting-protocol.md +7 -2
  86. package/.agent-src/rules/artifact-engagement-recording.md +133 -0
  87. package/.agent-src/rules/ask-when-uncertain.md +18 -13
  88. package/.agent-src/rules/augment-portability.md +8 -0
  89. package/.agent-src/rules/autonomous-execution.md +158 -0
  90. package/.agent-src/rules/chat-history.md +147 -118
  91. package/.agent-src/rules/cli-output-handling.md +26 -3
  92. package/.agent-src/rules/command-suggestion.md +133 -0
  93. package/.agent-src/rules/commit-policy.md +99 -0
  94. package/.agent-src/rules/direct-answers.md +114 -0
  95. package/.agent-src/rules/docs-sync.md +36 -0
  96. package/.agent-src/rules/downstream-changes.md +10 -9
  97. package/.agent-src/rules/improve-before-implement.md +9 -6
  98. package/.agent-src/rules/language-and-tone.md +81 -6
  99. package/.agent-src/rules/non-destructive-by-default.md +117 -0
  100. package/.agent-src/rules/package-ci-checks.md +4 -0
  101. package/.agent-src/rules/preservation-guard.md +20 -0
  102. package/.agent-src/rules/roadmap-progress-sync.md +103 -30
  103. package/.agent-src/rules/scope-control.md +42 -1
  104. package/.agent-src/rules/size-enforcement.md +1 -3
  105. package/.agent-src/rules/skill-quality.md +3 -8
  106. package/.agent-src/rules/ui-audit-before-build.md +106 -0
  107. package/.agent-src/rules/user-interaction.md +81 -3
  108. package/.agent-src/scripts/update_roadmap_progress.py +48 -6
  109. package/.agent-src/skills/blade-ui/SKILL.md +30 -5
  110. package/.agent-src/skills/command-routing/SKILL.md +32 -0
  111. package/.agent-src/skills/command-writing/SKILL.md +41 -2
  112. package/.agent-src/skills/description-assist/SKILL.md +21 -0
  113. package/.agent-src/skills/estimate-ticket/SKILL.md +0 -1
  114. package/.agent-src/skills/existing-ui-audit/SKILL.md +187 -0
  115. package/.agent-src/skills/fe-design/SKILL.md +72 -60
  116. package/.agent-src/skills/finishing-a-development-branch/SKILL.md +4 -0
  117. package/.agent-src/skills/flux/SKILL.md +31 -4
  118. package/.agent-src/skills/guideline-writing/SKILL.md +24 -2
  119. package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +51 -9
  120. package/.agent-src/skills/livewire/SKILL.md +30 -4
  121. package/.agent-src/skills/md-language-check/SKILL.md +103 -0
  122. package/.agent-src/skills/php-coder/SKILL.md +24 -0
  123. package/.agent-src/skills/react-shadcn-ui/SKILL.md +121 -0
  124. package/.agent-src/skills/refine-prompt/SKILL.md +220 -0
  125. package/.agent-src/skills/refine-ticket/SKILL.md +2 -4
  126. package/.agent-src/skills/roadmap-management/SKILL.md +10 -3
  127. package/.agent-src/skills/rule-writing/SKILL.md +23 -1
  128. package/.agent-src/skills/skill-writing/SKILL.md +1 -3
  129. package/.agent-src/skills/upstream-contribute/SKILL.md +1 -1
  130. package/.agent-src/skills/using-git-worktrees/SKILL.md +3 -1
  131. package/.agent-src/templates/AGENTS.md +24 -6
  132. package/.agent-src/templates/agent-settings.md +149 -0
  133. package/.agent-src/templates/github-workflows/roadmap-progress-check.yml +63 -0
  134. package/.agent-src/templates/hooks/pre-commit-roadmap-progress +60 -0
  135. package/.agent-src/templates/roadmaps.md +8 -2
  136. package/.agent-src/templates/scripts/implement_ticket/__init__.py +63 -26
  137. package/.agent-src/templates/scripts/implement_ticket/__main__.py +8 -2
  138. package/.agent-src/templates/scripts/memory_lookup.py +382 -21
  139. package/.agent-src/templates/scripts/memory_status.py +110 -9
  140. package/.agent-src/templates/scripts/telemetry/__init__.py +42 -0
  141. package/.agent-src/templates/scripts/telemetry/aggregator.py +154 -0
  142. package/.agent-src/templates/scripts/telemetry/boundary.py +171 -0
  143. package/.agent-src/templates/scripts/telemetry/engagement.py +238 -0
  144. package/.agent-src/templates/scripts/telemetry/report_renderer.py +170 -0
  145. package/.agent-src/templates/scripts/telemetry/settings.py +112 -0
  146. package/.agent-src/templates/scripts/telemetry_record.py +166 -0
  147. package/.agent-src/templates/scripts/telemetry_report.py +161 -0
  148. package/.agent-src/templates/scripts/telemetry_status.py +142 -0
  149. package/.agent-src/templates/scripts/work_engine/__init__.py +58 -0
  150. package/.agent-src/templates/scripts/work_engine/__main__.py +9 -0
  151. package/.agent-src/templates/scripts/work_engine/cli.py +592 -0
  152. package/.agent-src/templates/scripts/{implement_ticket → work_engine}/delivery_state.py +7 -0
  153. package/.agent-src/templates/scripts/work_engine/directives/__init__.py +33 -0
  154. package/.agent-src/templates/scripts/work_engine/directives/backend/__init__.py +98 -0
  155. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/analyze.py +1 -1
  156. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/implement.py +2 -2
  157. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/memory.py +1 -1
  158. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/plan.py +1 -1
  159. package/.agent-src/templates/scripts/work_engine/directives/backend/refine.py +396 -0
  160. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/report.py +36 -4
  161. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/test.py +2 -2
  162. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/verify.py +2 -2
  163. package/.agent-src/templates/scripts/work_engine/directives/mixed/__init__.py +116 -0
  164. package/.agent-src/templates/scripts/work_engine/directives/mixed/contract.py +254 -0
  165. package/.agent-src/templates/scripts/work_engine/directives/mixed/stitch.py +229 -0
  166. package/.agent-src/templates/scripts/work_engine/directives/mixed/ui.py +231 -0
  167. package/.agent-src/templates/scripts/work_engine/directives/ui/__init__.py +113 -0
  168. package/.agent-src/templates/scripts/work_engine/directives/ui/_passthrough.py +44 -0
  169. package/.agent-src/templates/scripts/work_engine/directives/ui/apply.py +241 -0
  170. package/.agent-src/templates/scripts/work_engine/directives/ui/audit.py +414 -0
  171. package/.agent-src/templates/scripts/work_engine/directives/ui/design.py +335 -0
  172. package/.agent-src/templates/scripts/work_engine/directives/ui/polish.py +510 -0
  173. package/.agent-src/templates/scripts/work_engine/directives/ui/review.py +468 -0
  174. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/__init__.py +119 -0
  175. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/_skipped.py +37 -0
  176. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/apply.py +165 -0
  177. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/refine.py +66 -0
  178. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/report.py +62 -0
  179. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/test.py +115 -0
  180. package/.agent-src/templates/scripts/work_engine/dispatcher.py +331 -0
  181. package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +54 -0
  182. package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +32 -0
  183. package/.agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +103 -0
  184. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +44 -0
  185. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +42 -0
  186. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_heartbeat.py +50 -0
  187. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_turn_check.py +49 -0
  188. package/.agent-src/templates/scripts/work_engine/hooks/builtin/directive_set_guard.py +53 -0
  189. package/.agent-src/templates/scripts/work_engine/hooks/builtin/halt_surface_audit.py +50 -0
  190. package/.agent-src/templates/scripts/work_engine/hooks/builtin/state_shape_validation.py +52 -0
  191. package/.agent-src/templates/scripts/work_engine/hooks/builtin/trace.py +84 -0
  192. package/.agent-src/templates/scripts/work_engine/hooks/context.py +66 -0
  193. package/.agent-src/templates/scripts/work_engine/hooks/events.py +44 -0
  194. package/.agent-src/templates/scripts/work_engine/hooks/exceptions.py +79 -0
  195. package/.agent-src/templates/scripts/work_engine/hooks/registry.py +60 -0
  196. package/.agent-src/templates/scripts/work_engine/hooks/runner.py +73 -0
  197. package/.agent-src/templates/scripts/work_engine/hooks/settings.py +141 -0
  198. package/.agent-src/templates/scripts/work_engine/intent/__init__.py +47 -0
  199. package/.agent-src/templates/scripts/work_engine/intent/classify.py +280 -0
  200. package/.agent-src/templates/scripts/work_engine/migration/__init__.py +8 -0
  201. package/.agent-src/templates/scripts/work_engine/migration/v0_to_v1.py +199 -0
  202. package/.agent-src/templates/scripts/work_engine/resolvers/__init__.py +22 -0
  203. package/.agent-src/templates/scripts/work_engine/resolvers/diff.py +106 -0
  204. package/.agent-src/templates/scripts/work_engine/resolvers/file.py +113 -0
  205. package/.agent-src/templates/scripts/work_engine/resolvers/prompt.py +90 -0
  206. package/.agent-src/templates/scripts/work_engine/scoring/__init__.py +14 -0
  207. package/.agent-src/templates/scripts/work_engine/scoring/confidence.py +300 -0
  208. package/.agent-src/templates/scripts/work_engine/stack/__init__.py +31 -0
  209. package/.agent-src/templates/scripts/work_engine/stack/detect.py +187 -0
  210. package/.agent-src/templates/scripts/work_engine/state.py +641 -0
  211. package/.claude-plugin/marketplace.json +105 -2
  212. package/AGENTS.md +36 -8
  213. package/CHANGELOG.md +558 -0
  214. package/README.md +146 -4
  215. package/composer.json +3 -0
  216. package/config/agent-settings.template.yml +45 -0
  217. package/config/gitignore-block.txt +4 -0
  218. package/docs/architecture.md +28 -1
  219. package/docs/development.md +1 -1
  220. package/docs/getting-started.md +3 -2
  221. package/docs/installation.md +86 -0
  222. package/docs/showcase.md +204 -0
  223. package/package.json +9 -1
  224. package/scripts/agent-config +274 -0
  225. package/scripts/audit_cloud_compatibility.py +288 -0
  226. package/scripts/build_cloud_bundle.py +458 -0
  227. package/scripts/build_linear_digest.py +263 -0
  228. package/scripts/chat_history.py +796 -7
  229. package/scripts/check_compression.py +139 -0
  230. package/scripts/check_iron_law_prominence.py +143 -0
  231. package/scripts/check_md_language.py +159 -0
  232. package/scripts/check_portability.py +36 -0
  233. package/scripts/check_reply_consistency.py +140 -0
  234. package/scripts/command_suggester/__init__.py +51 -0
  235. package/scripts/command_suggester/cooldown.py +132 -0
  236. package/scripts/command_suggester/loader.py +70 -0
  237. package/scripts/command_suggester/match.py +180 -0
  238. package/scripts/command_suggester/rank.py +120 -0
  239. package/scripts/command_suggester/render.py +86 -0
  240. package/scripts/command_suggester/sanitize.py +113 -0
  241. package/scripts/command_suggester/settings.py +125 -0
  242. package/scripts/command_suggester/types.py +78 -0
  243. package/scripts/hooks/augment-chat-history.sh +56 -0
  244. package/scripts/install-hooks.sh +67 -0
  245. package/scripts/install.py +150 -33
  246. package/scripts/lint_marketplace.py +27 -0
  247. package/scripts/memory_lookup.py +143 -7
  248. package/scripts/memory_status.py +76 -14
  249. package/scripts/migrate_command_suggestions.py +151 -0
  250. package/scripts/postinstall.sh +16 -0
  251. package/scripts/schemas/command.schema.json +41 -0
  252. package/scripts/skill_linter.py +67 -0
  253. package/scripts/sync_agent_settings.py +42 -12
  254. package/templates/consumer-settings/augment-cli-hooks.json +54 -0
  255. package/templates/consumer-settings/claude-settings.json +55 -1
  256. package/.agent-src/templates/scripts/implement_ticket/cli.py +0 -171
  257. package/.agent-src/templates/scripts/implement_ticket/dispatcher.py +0 -134
  258. package/.agent-src/templates/scripts/implement_ticket/steps/__init__.py +0 -49
  259. package/.agent-src/templates/scripts/implement_ticket/steps/refine.py +0 -140
  260. /package/.agent-src/templates/scripts/{implement_ticket → work_engine}/persona_policy.py +0 -0
@@ -0,0 +1,231 @@
1
+ """``ui`` step — delegates to the UI track once the contract is locked.
2
+
3
+ Phase 4 Step 2 of ``agents/roadmaps/road-to-product-ui-track.md``: in the
4
+ ``mixed`` directive set the ``implement`` slot is the UI handoff. It
5
+ gates on the upstream contract sentinel
6
+ (``state.contract.contract_confirmed is True``) and then delegates the
7
+ full audit → design → apply → review → polish sub-flow to the UI track
8
+ via an ``@agent-directive: ui-track`` halt. The locked contract is
9
+ treated as immutable input — the UI track reads ``state.contract`` for
10
+ entity shapes and endpoint signatures but never mutates them.
11
+
12
+ Routes on the UI sub-flow's terminal sentinel
13
+ (``state.ui_review.review_clean``):
14
+
15
+ - **Contract sentinel missing or False** — defense-in-depth halt. The
16
+ ``plan`` slot (``mixed.contract``) is the canonical gate; this step
17
+ refuses to start without the sentinel even if outcomes say
18
+ otherwise, mirroring the roadmap risk: "Mixed flow's contract halt
19
+ is bypassed".
20
+ - **UI sub-flow not started** (``state.ui_review`` empty / missing
21
+ ``review_clean``) — emit ``@agent-directive: ui-track`` so the
22
+ orchestrator runs audit → design → apply → review → polish with the
23
+ contract as input. On rebound the dispatcher walks back here.
24
+ - **UI sub-flow finished, ``review_clean`` True** — return ``SUCCESS``;
25
+ the dispatcher advances to the mixed ``stitch`` step.
26
+ - **UI sub-flow finished, ``review_clean`` False** — halt with three
27
+ numbered options (re-run / hand off / abort). Polish-ceiling
28
+ semantics live in the UI track's polish step; if the user reaches
29
+ this halt the UI track has already given up.
30
+
31
+ Idempotent: a re-entry with a clean review round-trips through
32
+ ``SUCCESS`` without re-emitting the delegation directive.
33
+ """
34
+ from __future__ import annotations
35
+
36
+ from typing import Any
37
+
38
+ from ...delivery_state import (
39
+ DeliveryState,
40
+ Outcome,
41
+ StepResult,
42
+ agent_directive,
43
+ )
44
+
45
+ UI_TRACK_DIRECTIVE = "ui-track"
46
+ """Agent directive that triggers the full UI sub-flow.
47
+
48
+ The orchestrator runs the ``ui`` directive set's audit → design →
49
+ apply → review → polish sequence with ``state.contract`` as the
50
+ locked input. On rebound, ``state.ui_review.review_clean`` carries
51
+ the verdict the mixed step routes on.
52
+ """
53
+
54
+ AMBIGUITIES: tuple[dict[str, str], ...] = (
55
+ {
56
+ "code": "upstream_contract_failed",
57
+ "trigger": "`plan` (contract) outcome is not `success`",
58
+ "resolution": "re-run the mixed flow from the start",
59
+ },
60
+ {
61
+ "code": "contract_sentinel_missing",
62
+ "trigger": "state.contract.contract_confirmed is missing or False — "
63
+ "defense-in-depth check refuses to start the UI track",
64
+ "resolution": "loop back to the contract step; user must confirm "
65
+ "the data_model + api_surface lock before UI work begins",
66
+ },
67
+ {
68
+ "code": "ui_track_not_started",
69
+ "trigger": "state.ui_review is empty or missing `review_clean` — "
70
+ "the UI sub-flow has not run yet",
71
+ "resolution": "agent directive `ui-track` → orchestrator runs "
72
+ "audit → design → apply → review → polish with the contract as "
73
+ "immutable input",
74
+ },
75
+ {
76
+ "code": "ui_track_review_unclean",
77
+ "trigger": "UI sub-flow finished but `review_clean` is False — "
78
+ "polish ceiling reached or findings remain",
79
+ "resolution": "user picks re-run / hand off / abort; polish-ceiling "
80
+ "semantics already fired inside the UI track",
81
+ },
82
+ )
83
+ """Declared ambiguity surfaces. Every BLOCKED return maps to one code."""
84
+
85
+
86
+ def run(state: DeliveryState) -> StepResult:
87
+ """Delegate the UI sub-flow once the contract is locked."""
88
+ if state.outcomes.get("plan") != Outcome.SUCCESS.value:
89
+ return _blocked_on_precondition(state)
90
+
91
+ if not _contract_confirmed(state):
92
+ return _blocked_on_contract_sentinel(state)
93
+
94
+ review = state.ui_review if isinstance(state.ui_review, dict) else None
95
+ if review is None or "review_clean" not in review:
96
+ return _delegate_to_ui_track(state)
97
+
98
+ if review.get("review_clean") is True:
99
+ return StepResult(outcome=Outcome.SUCCESS)
100
+
101
+ return _halt_review_unclean(state, review)
102
+
103
+
104
+ def _contract_confirmed(state: DeliveryState) -> bool:
105
+ """True when the contract sentinel is explicitly ``True``.
106
+
107
+ Mirrors the gate in :func:`work_engine.directives.mixed.contract.run`
108
+ so a hand-rolled state file (or a partial rebound) cannot race the
109
+ UI track ahead of the user's contract sign-off.
110
+ """
111
+ contract = state.contract
112
+ if not isinstance(contract, dict):
113
+ return False
114
+ return contract.get("contract_confirmed") is True
115
+
116
+
117
+ def _preview_input(state: DeliveryState) -> str:
118
+ """One-line preview of the original input for halt bodies."""
119
+ data = state.ticket or {}
120
+ raw = data.get("raw")
121
+ if isinstance(raw, str) and raw.strip():
122
+ text = " ".join(raw.split())
123
+ else:
124
+ title = data.get("title")
125
+ text = title if isinstance(title, str) else (data.get("id") or "(no title)")
126
+ if len(text) <= 80:
127
+ return text
128
+ return text[:79].rstrip() + "\u2026"
129
+
130
+
131
+ def _blocked_on_precondition(state: DeliveryState) -> StepResult:
132
+ """BLOCKED halt — upstream contract step did not succeed."""
133
+ return StepResult(
134
+ outcome=Outcome.BLOCKED,
135
+ questions=[
136
+ "> Mixed `ui` step gated on the contract step; the contract "
137
+ "outcome is not success.",
138
+ "> 1. Re-run \u2014 restart the mixed flow from the start",
139
+ "> 2. Abort \u2014 drop this request",
140
+ ],
141
+ message="ui step gated on plan; upstream contract outcome is not success.",
142
+ )
143
+
144
+
145
+ def _blocked_on_contract_sentinel(state: DeliveryState) -> StepResult:
146
+ """BLOCKED halt — contract sentinel missing despite plan success.
147
+
148
+ Defense-in-depth: the dispatcher should not advance here without
149
+ ``contract_confirmed=True``, but a partial state-file edit could
150
+ skip the sentinel. Refuse loudly so the bypass is visible.
151
+ """
152
+ return StepResult(
153
+ outcome=Outcome.BLOCKED,
154
+ questions=[
155
+ "> Contract sentinel missing \u2014 `state.contract.contract_confirmed` "
156
+ "is not `True`. The UI track will not start until the data "
157
+ "model and API surface are locked and confirmed.",
158
+ "> 1. Loop back \u2014 re-enter the contract step and confirm",
159
+ "> 2. Abort \u2014 drop this mixed request",
160
+ ],
161
+ message=(
162
+ "ui step refused; contract_confirmed sentinel missing despite plan success."
163
+ ),
164
+ )
165
+
166
+
167
+ def _delegate_to_ui_track(state: DeliveryState) -> StepResult:
168
+ """Halt with an agent directive so the orchestrator runs the UI track."""
169
+ preview = _preview_input(state)
170
+ contract = state.contract if isinstance(state.contract, dict) else {}
171
+ data_model = contract.get("data_model") or []
172
+ api_surface = contract.get("api_surface") or []
173
+ entity_count = len(data_model) if isinstance(data_model, list) else 0
174
+ endpoint_count = len(api_surface) if isinstance(api_surface, list) else 0
175
+
176
+ return StepResult(
177
+ outcome=Outcome.BLOCKED,
178
+ questions=[
179
+ agent_directive(UI_TRACK_DIRECTIVE),
180
+ f"> Input: {preview}",
181
+ "> Contract is locked. Handing off to the UI track:",
182
+ f"> - Entities: {entity_count}",
183
+ f"> - Endpoints / actions: {endpoint_count}",
184
+ "> The UI track runs audit \u2192 design \u2192 apply \u2192 "
185
+ "review \u2192 polish with the contract as immutable input. "
186
+ "No new entities or endpoints \u2014 the contract drives the UI shape.",
187
+ "> 1. Continue \u2014 run the UI track",
188
+ "> 2. Abort \u2014 drop this mixed request",
189
+ ],
190
+ message="Mixed contract locked; delegating UI sub-flow to ui-track.",
191
+ )
192
+
193
+
194
+ def _halt_review_unclean(
195
+ state: DeliveryState,
196
+ review: dict[str, Any],
197
+ ) -> StepResult:
198
+ """BLOCKED halt — UI sub-flow finished but review is not clean.
199
+
200
+ Polish-ceiling semantics fire inside the UI track itself (Phase 3
201
+ Step 5). This halt is the mixed-flow's escalation when the UI
202
+ track gave up: the user picks re-run / hand off / abort instead
203
+ of the engine looping forever.
204
+ """
205
+ findings = review.get("findings") or []
206
+ finding_count = len(findings) if isinstance(findings, list) else 0
207
+ lines = [
208
+ f"> UI track finished but review is not clean. Findings remaining: {finding_count}.",
209
+ "> Polish ceiling already fired inside the UI track \u2014 the "
210
+ "engine cannot resolve the remaining findings without a user "
211
+ "decision.",
212
+ "> 1. Re-run UI track \u2014 hand back to `ui-track` for another "
213
+ "audit \u2192 design pass (rare; usually means the contract changed)",
214
+ "> 2. Hand off \u2014 ship as-is and let a human resolve the "
215
+ "remaining findings outside the engine",
216
+ "> 3. Abort \u2014 drop this mixed request",
217
+ ]
218
+ return StepResult(
219
+ outcome=Outcome.BLOCKED,
220
+ questions=lines,
221
+ message=(
222
+ f"Mixed ui step halted; UI track review unclean ({finding_count} findings)."
223
+ ),
224
+ )
225
+
226
+
227
+ __all__ = [
228
+ "AMBIGUITIES",
229
+ "UI_TRACK_DIRECTIVE",
230
+ "run",
231
+ ]
@@ -0,0 +1,113 @@
1
+ """UI directive set — Phase 6 wires every slot to a working handler.
2
+
3
+ Phase 1 of ``agents/roadmaps/road-to-product-ui-track.md`` landed the
4
+ intent classifier; Phase 2 promoted ``refine`` to the real audit gate
5
+ (:mod:`.audit`); Phase 3 added the four design / apply / review / polish
6
+ handlers; Phase 6 retires the deferral stub by wiring the remaining
7
+ ``memory``, ``plan``, and ``report`` slots — the first two as
8
+ :mod:`._passthrough` (the UI track has no memory retrieval, and the
9
+ design brief IS the plan), and the third as a re-export of
10
+ :func:`work_engine.directives.backend.report.run` (the renderer is pure
11
+ and state-driven, so the same Markdown contract serves both tracks).
12
+
13
+ The eight-step shape mirrors :mod:`work_engine.directives.backend`:
14
+
15
+ - ``refine`` → :mod:`.audit` — existing-UI inventory gate.
16
+ - ``memory`` → :mod:`._passthrough` — UI track does not consult memory.
17
+ - ``analyze`` → :mod:`.design` — produces the locked design brief.
18
+ - ``plan`` → :mod:`._passthrough` — design brief is the plan.
19
+ - ``implement`` → :mod:`.apply` — stack-dispatched render of the brief.
20
+ - ``test`` → :mod:`.review` — design-review pass produces findings.
21
+ - ``verify`` → :mod:`.polish` — bounded fix loop (≤ 2 rounds).
22
+ - ``report`` → :mod:`work_engine.directives.backend.report` — shared
23
+ delivery-Markdown renderer.
24
+ """
25
+ from __future__ import annotations
26
+
27
+ from collections.abc import Mapping
28
+
29
+ from ...delivery_state import Step
30
+ from ..backend import report
31
+ from . import _passthrough, apply, audit, design, polish, review
32
+
33
+ DIRECTIVE_SET_NAME = "ui"
34
+ """External name carried in ``state.directive_set`` for this set."""
35
+
36
+ ROADMAP = "agents/roadmaps/road-to-product-ui-track.md"
37
+ """Roadmap that promoted the deferral stub to fully wired handlers."""
38
+
39
+ SUPPORTED_KINDS: tuple[str, ...] = ("ticket", "prompt", "diff", "file")
40
+ """Input kinds this directive set knows how to handle.
41
+
42
+ Phase 1 wires every UI-classifiable input shape (ticket prose,
43
+ free-form prompt, ``diff`` / ``file`` improve-this-screen envelopes)
44
+ through to this set; Phase 3's design / apply / review / polish gates
45
+ keep the same tuple so input-routing stays unchanged.
46
+ """
47
+
48
+
49
+ def _build_step_map() -> dict[str, Step]:
50
+ """Wire the eight-step dispatcher slots for the UI set.
51
+
52
+ ``refine`` runs audit; ``memory`` and ``plan`` are pass-through
53
+ no-ops; ``analyze`` runs design; ``implement`` runs apply; ``test``
54
+ runs review; ``verify`` runs polish; ``report`` re-uses the shared
55
+ backend renderer. The mapping is rebuilt per call (cheap; the
56
+ dispatcher invokes :func:`get_steps` once per run).
57
+ """
58
+ passthrough = _passthrough.run
59
+ return {
60
+ "refine": audit.run,
61
+ "memory": passthrough,
62
+ "analyze": design.run,
63
+ "plan": passthrough,
64
+ "implement": apply.run,
65
+ "test": review.run,
66
+ "verify": polish.run,
67
+ "report": report.run,
68
+ }
69
+
70
+
71
+ def get_steps() -> Mapping[str, Step]:
72
+ """Return the ``{step_name: handler}`` mapping the dispatcher walks.
73
+
74
+ Mirrors :func:`work_engine.directives.backend.get_steps`.
75
+ """
76
+ return _build_step_map()
77
+
78
+
79
+ def all_ambiguities() -> dict[str, tuple[dict[str, str], ...]]:
80
+ """Per-step ambiguity declarations.
81
+
82
+ Mirrors :func:`work_engine.directives.backend.all_ambiguities`.
83
+ Each working handler re-exports its own ``AMBIGUITIES`` tuple; the
84
+ pass-through slots re-export :data:`_passthrough.AMBIGUITIES` (an
85
+ empty tuple) so doc generators see a uniform shape across all
86
+ eight steps. ``report`` borrows the backend renderer's surface.
87
+ """
88
+ passthrough = _passthrough.AMBIGUITIES
89
+ return {
90
+ "refine": audit.AMBIGUITIES,
91
+ "memory": passthrough,
92
+ "analyze": design.AMBIGUITIES,
93
+ "plan": passthrough,
94
+ "implement": apply.AMBIGUITIES,
95
+ "test": review.AMBIGUITIES,
96
+ "verify": polish.AMBIGUITIES,
97
+ "report": report.AMBIGUITIES,
98
+ }
99
+
100
+
101
+ __all__ = [
102
+ "DIRECTIVE_SET_NAME",
103
+ "ROADMAP",
104
+ "SUPPORTED_KINDS",
105
+ "all_ambiguities",
106
+ "apply",
107
+ "audit",
108
+ "design",
109
+ "get_steps",
110
+ "polish",
111
+ "report",
112
+ "review",
113
+ ]
@@ -0,0 +1,44 @@
1
+ """Pass-through handler for UI directive slots that have no work.
2
+
3
+ Phase 6 of ``agents/roadmaps/road-to-product-ui-track.md`` retired the
4
+ Phase 3 deferral stub once design / apply / review / polish landed. Two
5
+ slots remain semantically empty for the UI track:
6
+
7
+ - ``memory`` — the UI track does not consult the four memory types the
8
+ backend retrieves over (``domain-invariants``, ``architecture-decisions``,
9
+ ``incident-learnings``, ``historical-patterns``). UI work pivots on the
10
+ audit findings in ``state.ui_audit`` instead, which the audit gate has
11
+ already populated by the time this handler runs.
12
+ - ``plan`` — :mod:`.design` produces the locked design brief that
13
+ :mod:`.apply` follows verbatim. The brief IS the plan; a separate plan
14
+ step would duplicate state with no user-visible payoff.
15
+
16
+ Both slots therefore return :data:`Outcome.SUCCESS` without writing to
17
+ state. The dispatcher advances to the next slot and the locked transcripts
18
+ record the no-op cycle as a clean rebound.
19
+
20
+ This module is ``directives/ui``-internal — the underscore prefix marks
21
+ it as a private slot filler, not a published handler. The backend track
22
+ keeps its own real ``memory`` and ``plan`` modules.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ from ...delivery_state import DeliveryState, Outcome, StepResult
27
+
28
+ AMBIGUITIES: tuple[dict[str, str], ...] = ()
29
+ """Pass-through never blocks — empty surface, declared intent."""
30
+
31
+
32
+ def run(state: DeliveryState) -> StepResult:
33
+ """Return ``SUCCESS`` without mutating state.
34
+
35
+ The handler is intentionally pure and side-effect-free: it neither
36
+ reads nor writes ``state``. The dispatcher records the step as
37
+ successful in ``state.outcomes`` (its own bookkeeping) and advances
38
+ to the next slot.
39
+ """
40
+ del state # explicitly unused — the slot is a no-op by design
41
+ return StepResult(outcome=Outcome.SUCCESS)
42
+
43
+
44
+ __all__ = ["AMBIGUITIES", "run"]
@@ -0,0 +1,241 @@
1
+ """``apply`` step — stack-dispatched UI implementation.
2
+
3
+ Phase 3 Step 2 of ``agents/roadmaps/road-to-product-ui-track.md``: the
4
+ apply step turns the locked design brief into actual files. Routes on
5
+ ``state.stack.frontend`` to the appropriate implementation skill bundle:
6
+
7
+ - ``blade-livewire-flux`` → ``ui-apply-blade-livewire-flux`` (composes
8
+ ``flux`` + ``livewire`` + ``blade-ui``)
9
+ - ``react-shadcn`` → ``ui-apply-react-shadcn`` (new skill, Phase 3 Step 3)
10
+ - ``vue`` → ``ui-apply-vue``
11
+ - ``plain`` → ``ui-apply-plain`` (``blade-ui`` + Tailwind base)
12
+
13
+ Routes on ``state.ticket["ui_apply"]`` shape:
14
+
15
+ - **Empty / None** — first pass. Emit the stack-specific
16
+ ``@agent-directive:`` halt; on the rebound the agent writes the
17
+ apply envelope back.
18
+ - **Populated, rendered text contains placeholder patterns** — emit a
19
+ rejection halt. The design-brief lock failed; placeholder strings
20
+ (``<placeholder>``, ``Lorem``, ``TODO:``) must never reach apply
21
+ output. Halt forces the agent to re-render with the locked microcopy.
22
+ - **Populated, well-formed** — record changes from the envelope,
23
+ return ``SUCCESS``.
24
+
25
+ Apply does **not** re-validate the design brief itself — that is the
26
+ design step's job. It validates the *output* against the brief's
27
+ microcopy lock so a mid-loop hallucination is caught at the boundary.
28
+ """
29
+ from __future__ import annotations
30
+
31
+ from typing import Any
32
+
33
+ from ...delivery_state import (
34
+ DeliveryState,
35
+ Outcome,
36
+ StepResult,
37
+ agent_directive,
38
+ )
39
+ from .design import PLACEHOLDER_PATTERNS
40
+
41
+ STACK_DIRECTIVES: dict[str, str] = {
42
+ "blade-livewire-flux": "ui-apply-blade-livewire-flux",
43
+ "react-shadcn": "ui-apply-react-shadcn",
44
+ "vue": "ui-apply-vue",
45
+ "plain": "ui-apply-plain",
46
+ }
47
+ """Map ``state.stack.frontend`` → agent-directive skill name.
48
+
49
+ Mirrors the heuristic table in :mod:`work_engine.stack.detect`. An
50
+ unknown stack falls through to ``ui-apply-plain`` (Tailwind defaults)
51
+ rather than raising — a wrong skill pick is recoverable, a crash
52
+ mid-dispatch is not.
53
+ """
54
+
55
+ DEFAULT_DIRECTIVE = "ui-apply-plain"
56
+ """Fallback directive when ``state.stack`` is missing or malformed."""
57
+
58
+ AMBIGUITIES: tuple[dict[str, str], ...] = (
59
+ {
60
+ "code": "apply_envelope_missing",
61
+ "trigger": "state.ticket['ui_apply'] unset — first pass, "
62
+ "stack-specific skill has not run yet",
63
+ "resolution": "agent directive `ui-apply-<stack>` → skill "
64
+ "bundle implements the brief and writes the envelope back",
65
+ },
66
+ {
67
+ "code": "apply_placeholders_in_output",
68
+ "trigger": "rendered text in apply envelope contains "
69
+ "placeholder patterns (<placeholder>, Lorem, TODO:, TBD, XXX) "
70
+ "— design-brief lock failed mid-loop",
71
+ "resolution": "agent re-renders the components with the locked "
72
+ "microcopy verbatim from state.ui_design.microcopy",
73
+ },
74
+ )
75
+ """Declared ambiguity surfaces for this step."""
76
+
77
+
78
+ def run(state: DeliveryState) -> StepResult:
79
+ """Apply the stack-dispatched implementation gate."""
80
+ envelope = _apply_envelope(state)
81
+ if envelope is None:
82
+ return _delegate_to_stack_skill(state)
83
+
84
+ violations = _placeholder_violations_in_output(envelope)
85
+ if violations:
86
+ return _halt_placeholders(state, violations)
87
+
88
+ _record_changes(state, envelope)
89
+ return StepResult(outcome=Outcome.SUCCESS)
90
+
91
+
92
+ def _apply_envelope(state: DeliveryState) -> dict[str, Any] | None:
93
+ """Return the agent-written ``ui_apply`` envelope, or ``None``."""
94
+ data = state.ticket or {}
95
+ envelope = data.get("ui_apply")
96
+ if isinstance(envelope, dict) and envelope:
97
+ return envelope
98
+ return None
99
+
100
+
101
+ def _resolve_directive(state: DeliveryState) -> str:
102
+ """Pick the agent directive for the project's frontend stack."""
103
+ stack = getattr(state, "stack", None) or {}
104
+ if isinstance(stack, dict):
105
+ frontend = stack.get("frontend")
106
+ if isinstance(frontend, str) and frontend in STACK_DIRECTIVES:
107
+ return STACK_DIRECTIVES[frontend]
108
+ return DEFAULT_DIRECTIVE
109
+
110
+
111
+ def _placeholder_violations_in_output(
112
+ envelope: dict[str, Any],
113
+ ) -> list[str]:
114
+ """Return paths into ``envelope['rendered']`` whose text matches
115
+ a placeholder pattern.
116
+
117
+ The agent writes ``rendered`` as a flat ``{path: text}`` map (or
118
+ nested dict of strings). Walked recursively, lower-cased, and
119
+ matched against :data:`PLACEHOLDER_PATTERNS` from the design step
120
+ so the gate stays consistent with the brief's lock.
121
+ """
122
+ rendered = envelope.get("rendered")
123
+ if not isinstance(rendered, dict):
124
+ return []
125
+ violations: list[str] = []
126
+ _walk_rendered(rendered, prefix="", violations=violations)
127
+ return violations
128
+
129
+
130
+ def _walk_rendered(
131
+ node: dict[str, Any],
132
+ *,
133
+ prefix: str,
134
+ violations: list[str],
135
+ ) -> None:
136
+ """Recursive helper for :func:`_placeholder_violations_in_output`."""
137
+ for key, value in node.items():
138
+ path = f"{prefix}.{key}" if prefix else str(key)
139
+ if isinstance(value, dict):
140
+ _walk_rendered(value, prefix=path, violations=violations)
141
+ continue
142
+ if not isinstance(value, str):
143
+ continue
144
+ lowered = value.lower()
145
+ for pattern in PLACEHOLDER_PATTERNS:
146
+ if pattern in lowered:
147
+ violations.append(path)
148
+ break
149
+
150
+
151
+ def _delegate_to_stack_skill(state: DeliveryState) -> StepResult:
152
+ """First-pass halt — emit the stack-specific apply directive."""
153
+ directive = _resolve_directive(state)
154
+ stack_label = _stack_label(state)
155
+ return StepResult(
156
+ outcome=Outcome.BLOCKED,
157
+ questions=[
158
+ agent_directive(directive),
159
+ f"> Stack: `{stack_label}`. Implementing the locked design brief.",
160
+ "> Microcopy is locked \u2014 every button label, empty-state "
161
+ "message, and validation message must come verbatim from "
162
+ "`state.ui_design.microcopy`.",
163
+ "> 1. Continue \u2014 implement the brief and write a "
164
+ "`ui_apply` envelope back into state.ticket "
165
+ "(rendered: {path: text}, files: [...])",
166
+ "> 2. Abort \u2014 drop this UI request",
167
+ ],
168
+ message=(
169
+ f"UI apply pending; delegating to `{directive}` for "
170
+ f"stack `{stack_label}`."
171
+ ),
172
+ )
173
+
174
+
175
+ def _halt_placeholders(
176
+ state: DeliveryState,
177
+ violations: list[str],
178
+ ) -> StepResult:
179
+ """BLOCKED halt — rendered output still carries placeholder patterns."""
180
+ directive = _resolve_directive(state)
181
+ lines = [
182
+ agent_directive(directive),
183
+ "> Apply rejected: rendered output contains placeholder strings. "
184
+ "The design-brief microcopy lock failed mid-loop.",
185
+ "> Affected paths in `ui_apply.rendered`:",
186
+ ]
187
+ for path in violations:
188
+ lines.append(f"> - `{path}`")
189
+ lines.append(
190
+ "> Re-render with the locked microcopy verbatim from "
191
+ "`state.ui_design.microcopy`; apply will not write placeholder text.",
192
+ )
193
+ return StepResult(
194
+ outcome=Outcome.BLOCKED,
195
+ questions=lines,
196
+ message=(
197
+ f"UI apply rejected: {len(violations)} placeholder "
198
+ f"violation(s) in rendered output."
199
+ ),
200
+ )
201
+
202
+
203
+ def _record_changes(
204
+ state: DeliveryState,
205
+ envelope: dict[str, Any],
206
+ ) -> None:
207
+ """Append one ``state.changes`` entry per file in the apply envelope."""
208
+ files = envelope.get("files")
209
+ if not isinstance(files, list):
210
+ files = []
211
+ summary = envelope.get("summary") or "ui apply"
212
+ stack_label = _stack_label(state)
213
+ for path in files:
214
+ if not isinstance(path, str) or not path:
215
+ continue
216
+ state.changes.append(
217
+ {
218
+ "kind": "ui",
219
+ "stack": stack_label,
220
+ "file": path,
221
+ "summary": summary,
222
+ },
223
+ )
224
+
225
+
226
+ def _stack_label(state: DeliveryState) -> str:
227
+ """Return the frontend stack label, defaulting to ``plain``."""
228
+ stack = getattr(state, "stack", None) or {}
229
+ if isinstance(stack, dict):
230
+ frontend = stack.get("frontend")
231
+ if isinstance(frontend, str) and frontend:
232
+ return frontend
233
+ return "plain"
234
+
235
+
236
+ __all__ = [
237
+ "AMBIGUITIES",
238
+ "DEFAULT_DIRECTIVE",
239
+ "STACK_DIRECTIVES",
240
+ "run",
241
+ ]