@event4u/agent-config 1.13.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 (252) 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 +82 -50
  108. package/.agent-src/scripts/update_roadmap_progress.py +17 -5
  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/roadmaps.md +8 -2
  134. package/.agent-src/templates/scripts/implement_ticket/__init__.py +63 -26
  135. package/.agent-src/templates/scripts/implement_ticket/__main__.py +8 -2
  136. package/.agent-src/templates/scripts/telemetry/__init__.py +42 -0
  137. package/.agent-src/templates/scripts/telemetry/aggregator.py +154 -0
  138. package/.agent-src/templates/scripts/telemetry/boundary.py +171 -0
  139. package/.agent-src/templates/scripts/telemetry/engagement.py +238 -0
  140. package/.agent-src/templates/scripts/telemetry/report_renderer.py +170 -0
  141. package/.agent-src/templates/scripts/telemetry/settings.py +112 -0
  142. package/.agent-src/templates/scripts/telemetry_record.py +166 -0
  143. package/.agent-src/templates/scripts/telemetry_report.py +161 -0
  144. package/.agent-src/templates/scripts/telemetry_status.py +142 -0
  145. package/.agent-src/templates/scripts/work_engine/__init__.py +58 -0
  146. package/.agent-src/templates/scripts/work_engine/__main__.py +9 -0
  147. package/.agent-src/templates/scripts/work_engine/cli.py +592 -0
  148. package/.agent-src/templates/scripts/{implement_ticket → work_engine}/delivery_state.py +7 -0
  149. package/.agent-src/templates/scripts/work_engine/directives/__init__.py +33 -0
  150. package/.agent-src/templates/scripts/work_engine/directives/backend/__init__.py +98 -0
  151. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/analyze.py +1 -1
  152. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/implement.py +2 -2
  153. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/memory.py +1 -1
  154. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/plan.py +1 -1
  155. package/.agent-src/templates/scripts/work_engine/directives/backend/refine.py +396 -0
  156. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/report.py +36 -4
  157. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/test.py +2 -2
  158. package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/verify.py +2 -2
  159. package/.agent-src/templates/scripts/work_engine/directives/mixed/__init__.py +116 -0
  160. package/.agent-src/templates/scripts/work_engine/directives/mixed/contract.py +254 -0
  161. package/.agent-src/templates/scripts/work_engine/directives/mixed/stitch.py +229 -0
  162. package/.agent-src/templates/scripts/work_engine/directives/mixed/ui.py +231 -0
  163. package/.agent-src/templates/scripts/work_engine/directives/ui/__init__.py +113 -0
  164. package/.agent-src/templates/scripts/work_engine/directives/ui/_passthrough.py +44 -0
  165. package/.agent-src/templates/scripts/work_engine/directives/ui/apply.py +241 -0
  166. package/.agent-src/templates/scripts/work_engine/directives/ui/audit.py +414 -0
  167. package/.agent-src/templates/scripts/work_engine/directives/ui/design.py +335 -0
  168. package/.agent-src/templates/scripts/work_engine/directives/ui/polish.py +510 -0
  169. package/.agent-src/templates/scripts/work_engine/directives/ui/review.py +468 -0
  170. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/__init__.py +119 -0
  171. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/_skipped.py +37 -0
  172. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/apply.py +165 -0
  173. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/refine.py +66 -0
  174. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/report.py +62 -0
  175. package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/test.py +115 -0
  176. package/.agent-src/templates/scripts/work_engine/dispatcher.py +331 -0
  177. package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +54 -0
  178. package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +32 -0
  179. package/.agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +103 -0
  180. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +44 -0
  181. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +42 -0
  182. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_heartbeat.py +50 -0
  183. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_turn_check.py +49 -0
  184. package/.agent-src/templates/scripts/work_engine/hooks/builtin/directive_set_guard.py +53 -0
  185. package/.agent-src/templates/scripts/work_engine/hooks/builtin/halt_surface_audit.py +50 -0
  186. package/.agent-src/templates/scripts/work_engine/hooks/builtin/state_shape_validation.py +52 -0
  187. package/.agent-src/templates/scripts/work_engine/hooks/builtin/trace.py +84 -0
  188. package/.agent-src/templates/scripts/work_engine/hooks/context.py +66 -0
  189. package/.agent-src/templates/scripts/work_engine/hooks/events.py +44 -0
  190. package/.agent-src/templates/scripts/work_engine/hooks/exceptions.py +79 -0
  191. package/.agent-src/templates/scripts/work_engine/hooks/registry.py +60 -0
  192. package/.agent-src/templates/scripts/work_engine/hooks/runner.py +73 -0
  193. package/.agent-src/templates/scripts/work_engine/hooks/settings.py +141 -0
  194. package/.agent-src/templates/scripts/work_engine/intent/__init__.py +47 -0
  195. package/.agent-src/templates/scripts/work_engine/intent/classify.py +280 -0
  196. package/.agent-src/templates/scripts/work_engine/migration/__init__.py +8 -0
  197. package/.agent-src/templates/scripts/work_engine/migration/v0_to_v1.py +199 -0
  198. package/.agent-src/templates/scripts/work_engine/resolvers/__init__.py +22 -0
  199. package/.agent-src/templates/scripts/work_engine/resolvers/diff.py +106 -0
  200. package/.agent-src/templates/scripts/work_engine/resolvers/file.py +113 -0
  201. package/.agent-src/templates/scripts/work_engine/resolvers/prompt.py +90 -0
  202. package/.agent-src/templates/scripts/work_engine/scoring/__init__.py +14 -0
  203. package/.agent-src/templates/scripts/work_engine/scoring/confidence.py +300 -0
  204. package/.agent-src/templates/scripts/work_engine/stack/__init__.py +31 -0
  205. package/.agent-src/templates/scripts/work_engine/stack/detect.py +187 -0
  206. package/.agent-src/templates/scripts/work_engine/state.py +641 -0
  207. package/.claude-plugin/marketplace.json +105 -2
  208. package/AGENTS.md +36 -8
  209. package/CHANGELOG.md +534 -0
  210. package/README.md +125 -4
  211. package/config/agent-settings.template.yml +45 -0
  212. package/config/gitignore-block.txt +4 -0
  213. package/docs/architecture.md +28 -1
  214. package/docs/development.md +1 -1
  215. package/docs/getting-started.md +2 -2
  216. package/docs/installation.md +86 -0
  217. package/docs/showcase.md +204 -0
  218. package/package.json +1 -1
  219. package/scripts/agent-config +199 -0
  220. package/scripts/audit_cloud_compatibility.py +288 -0
  221. package/scripts/build_cloud_bundle.py +458 -0
  222. package/scripts/build_linear_digest.py +263 -0
  223. package/scripts/chat_history.py +796 -7
  224. package/scripts/check_compression.py +139 -0
  225. package/scripts/check_iron_law_prominence.py +143 -0
  226. package/scripts/check_md_language.py +159 -0
  227. package/scripts/check_portability.py +36 -0
  228. package/scripts/check_reply_consistency.py +140 -0
  229. package/scripts/command_suggester/__init__.py +51 -0
  230. package/scripts/command_suggester/cooldown.py +132 -0
  231. package/scripts/command_suggester/loader.py +70 -0
  232. package/scripts/command_suggester/match.py +180 -0
  233. package/scripts/command_suggester/rank.py +120 -0
  234. package/scripts/command_suggester/render.py +86 -0
  235. package/scripts/command_suggester/sanitize.py +113 -0
  236. package/scripts/command_suggester/settings.py +125 -0
  237. package/scripts/command_suggester/types.py +78 -0
  238. package/scripts/hooks/augment-chat-history.sh +56 -0
  239. package/scripts/install-hooks.sh +67 -0
  240. package/scripts/install.py +150 -33
  241. package/scripts/lint_marketplace.py +27 -0
  242. package/scripts/migrate_command_suggestions.py +151 -0
  243. package/scripts/schemas/command.schema.json +41 -0
  244. package/scripts/skill_linter.py +67 -0
  245. package/scripts/sync_agent_settings.py +42 -12
  246. package/templates/consumer-settings/augment-cli-hooks.json +54 -0
  247. package/templates/consumer-settings/claude-settings.json +55 -1
  248. package/.agent-src/templates/scripts/implement_ticket/cli.py +0 -171
  249. package/.agent-src/templates/scripts/implement_ticket/dispatcher.py +0 -134
  250. package/.agent-src/templates/scripts/implement_ticket/steps/__init__.py +0 -49
  251. package/.agent-src/templates/scripts/implement_ticket/steps/refine.py +0 -140
  252. /package/.agent-src/templates/scripts/{implement_ticket → work_engine}/persona_policy.py +0 -0
@@ -0,0 +1,116 @@
1
+ """Mixed (backend + UI) directive set — Phase 4 wiring.
2
+
3
+ Phase 4 of ``agents/roadmaps/road-to-product-ui-track.md``: ``mixed`` is
4
+ the directive set for tickets that touch both layers. Its plan slot
5
+ locks the backend contract (data shape + API surface) before any UI
6
+ work begins; its implement slot delegates the full UI sub-flow once
7
+ the contract is confirmed; its test slot stitches the seam with
8
+ end-to-end smoke scenarios.
9
+
10
+ Slot mapping (mirrors :mod:`work_engine.directives.backend.get_steps`):
11
+
12
+ - ``refine`` → :mod:`work_engine.directives.backend.refine` — intent
13
+ classification + ticket / prompt gate (shared with backend).
14
+ - ``memory`` → :mod:`work_engine.directives.backend.memory` —
15
+ engineering-memory pull (shared with backend).
16
+ - ``analyze`` → :mod:`work_engine.directives.backend.analyze` —
17
+ backend analysis precondition for the contract lock.
18
+ - ``plan`` → :mod:`.contract` — backend contract lock (Phase 4 Step 1).
19
+ - ``implement`` → :mod:`.ui` — delegate to UI sub-flow (Phase 4 Step 2).
20
+ - ``test`` → :mod:`.stitch` — integration verification (Phase 4 Step 3).
21
+ - ``verify`` → :mod:`work_engine.directives.backend.verify` — four-judge
22
+ review on the merged diff (shared with backend).
23
+ - ``report`` → :mod:`work_engine.directives.backend.report` — delivery
24
+ markdown (shared with backend).
25
+
26
+ The shared steps are reused by reference, not by duplication: the
27
+ backend versions already gate on ``test`` / ``verify`` outcomes via
28
+ the dispatcher's ``state.outcomes`` dict, so they slot into the mixed
29
+ flow without modification.
30
+ """
31
+ from __future__ import annotations
32
+
33
+ from collections.abc import Mapping
34
+
35
+ from ...delivery_state import Step
36
+ from ..backend import analyze as backend_analyze
37
+ from ..backend import memory as backend_memory
38
+ from ..backend import refine as backend_refine
39
+ from ..backend import report as backend_report
40
+ from ..backend import verify as backend_verify
41
+ from . import contract, stitch, ui
42
+
43
+ DIRECTIVE_SET_NAME = "mixed"
44
+ """External name carried in ``state.directive_set`` for this set."""
45
+
46
+ ROADMAP = "agents/roadmaps/road-to-product-ui-track.md"
47
+ """Roadmap that promotes the Phase 4 stub to working handlers."""
48
+
49
+ SUPPORTED_KINDS: tuple[str, ...] = ("ticket", "prompt")
50
+ """Input kinds this directive set accepts.
51
+
52
+ ``mixed`` accepts the same envelope shapes as ``backend``: ticket
53
+ payloads (refined by the ticket flow) and free-form prompts
54
+ (refined by ``refine-prompt``). The ``diff`` / ``file`` envelopes
55
+ stay UI-only since they describe an existing screen, not a backend
56
+ contract surface.
57
+ """
58
+
59
+
60
+ def _build_step_map() -> dict[str, Step]:
61
+ """Wire the eight-step dispatcher slots for the mixed set.
62
+
63
+ ``refine`` / ``memory`` / ``analyze`` / ``verify`` / ``report``
64
+ reuse the backend handlers verbatim; ``plan`` / ``implement`` /
65
+ ``test`` are the mixed-specific contract → ui → stitch chain.
66
+ """
67
+ return {
68
+ "refine": backend_refine.run,
69
+ "memory": backend_memory.run,
70
+ "analyze": backend_analyze.run,
71
+ "plan": contract.run,
72
+ "implement": ui.run,
73
+ "test": stitch.run,
74
+ "verify": backend_verify.run,
75
+ "report": backend_report.run,
76
+ }
77
+
78
+
79
+ def get_steps() -> Mapping[str, Step]:
80
+ """Return the ``{step_name: handler}`` mapping the dispatcher walks.
81
+
82
+ Mirrors :func:`work_engine.directives.backend.get_steps` and
83
+ :func:`work_engine.directives.ui.get_steps`.
84
+ """
85
+ return _build_step_map()
86
+
87
+
88
+ def all_ambiguities() -> dict[str, tuple[dict[str, str], ...]]:
89
+ """Per-step ambiguity declarations.
90
+
91
+ Each handler re-exports its own ``AMBIGUITIES`` tuple. The mapping
92
+ is rebuilt per call (cheap; documentation generators and the
93
+ ``test_ambiguity_coverage`` suite invoke this once per run).
94
+ """
95
+ return {
96
+ "refine": backend_refine.AMBIGUITIES,
97
+ "memory": backend_memory.AMBIGUITIES,
98
+ "analyze": backend_analyze.AMBIGUITIES,
99
+ "plan": contract.AMBIGUITIES,
100
+ "implement": ui.AMBIGUITIES,
101
+ "test": stitch.AMBIGUITIES,
102
+ "verify": backend_verify.AMBIGUITIES,
103
+ "report": backend_report.AMBIGUITIES,
104
+ }
105
+
106
+
107
+ __all__ = [
108
+ "DIRECTIVE_SET_NAME",
109
+ "ROADMAP",
110
+ "SUPPORTED_KINDS",
111
+ "all_ambiguities",
112
+ "contract",
113
+ "get_steps",
114
+ "stitch",
115
+ "ui",
116
+ ]
@@ -0,0 +1,254 @@
1
+ """``contract`` step — locks data_model + api_surface before any UI work.
2
+
3
+ Phase 4 Step 1 of ``agents/roadmaps/road-to-product-ui-track.md``: in the
4
+ ``mixed`` directive set the ``plan`` slot is the contract step. It
5
+ resolves the **backend contract** the UI will consume — entity shape,
6
+ endpoint signatures, validation surface — *before* the UI track runs.
7
+ The mixed ``ui`` step gates on ``state.contract.contract_confirmed is
8
+ True``; without that sentinel the UI directive refuses to start (per
9
+ roadmap risk: "Mixed flow's contract halt is bypassed").
10
+
11
+ Routes on ``state.contract`` shape:
12
+
13
+ - **Empty / None** — first pass. Emit ``@agent-directive: contract-plan``
14
+ delegating to ``feature-plan`` with a contract-only scope; on the
15
+ rebound the contract lands in ``state.contract``.
16
+ - **Populated, missing required keys** — halt listing the gaps
17
+ (``data_model``, ``api_surface``) so the next pass writes the slots
18
+ the UI will read.
19
+ - **Populated, well-formed, ``contract_confirmed`` missing or False** —
20
+ halt with a numbered-options summary so the user signs off on the
21
+ contract before any UI work begins.
22
+ - **Populated, well-formed, ``contract_confirmed`` True** — return
23
+ ``SUCCESS``; the dispatcher advances to the mixed ``ui`` step.
24
+
25
+ ``analyze`` is a precondition: the step refuses to plan a contract
26
+ when the upstream gate did not succeed, mirroring ``backend.plan``.
27
+
28
+ Idempotent: a re-entry with ``contract_confirmed=True`` round-trips
29
+ through ``SUCCESS`` without re-emitting a halt the user already
30
+ answered.
31
+ """
32
+ from __future__ import annotations
33
+
34
+ from typing import Any
35
+
36
+ from ...delivery_state import (
37
+ DeliveryState,
38
+ Outcome,
39
+ StepResult,
40
+ agent_directive,
41
+ )
42
+
43
+ REQUIRED_CONTRACT_KEYS: tuple[str, ...] = (
44
+ "data_model",
45
+ "api_surface",
46
+ )
47
+ """Top-level keys every well-formed contract must carry.
48
+
49
+ ``data_model`` lists the entities (and their fields) the UI will
50
+ read or write. ``api_surface`` lists the endpoints / actions the UI
51
+ will call. Both are non-empty lists when populated; the schema
52
+ validator in ``state._validate_contract`` enforces only the type, the
53
+ content check lives here.
54
+ """
55
+
56
+ AMBIGUITIES: tuple[dict[str, str], ...] = (
57
+ {
58
+ "code": "upstream_analyze_failed",
59
+ "trigger": "`analyze` outcome is not `success`",
60
+ "resolution": "re-run the mixed flow from the start",
61
+ },
62
+ {
63
+ "code": "contract_missing",
64
+ "trigger": "state.contract is None or empty — contract has not been produced",
65
+ "resolution": "agent directive `contract-plan` → `feature-plan` skill "
66
+ "writes the contract into state.contract",
67
+ },
68
+ {
69
+ "code": "contract_incomplete",
70
+ "trigger": "contract is populated but missing required keys "
71
+ "(`data_model`, `api_surface`) or one of them is empty",
72
+ "resolution": "agent re-runs `feature-plan` with contract-only scope; "
73
+ "halt lists the missing slots",
74
+ },
75
+ {
76
+ "code": "contract_unconfirmed",
77
+ "trigger": "contract is well-formed but contract_confirmed is unset/False",
78
+ "resolution": "user reviews the contract summary and sets "
79
+ "state.contract.contract_confirmed = True (or asks for "
80
+ "revisions, which loops back to contract-plan)",
81
+ },
82
+ )
83
+ """Declared ambiguity surfaces. Every BLOCKED return maps to one code."""
84
+
85
+
86
+ def run(state: DeliveryState) -> StepResult:
87
+ """Apply the contract-first lock to ``state.contract``."""
88
+ if state.outcomes.get("analyze") != Outcome.SUCCESS.value:
89
+ return _blocked_on_precondition(state)
90
+
91
+ contract = state.contract
92
+ if not _is_populated(contract):
93
+ return _delegate_to_feature_plan(state)
94
+
95
+ missing = _missing_required_keys(contract)
96
+ if missing:
97
+ return _halt_incomplete_contract(state, missing)
98
+
99
+ if contract.get("contract_confirmed") is True:
100
+ return StepResult(outcome=Outcome.SUCCESS)
101
+
102
+ return _halt_unconfirmed(state, contract)
103
+
104
+
105
+ def _is_populated(contract: Any) -> bool:
106
+ """True when ``contract`` carries an actionable backend lock.
107
+
108
+ Non-dict and empty-dict shapes are treated as "skill has not run"
109
+ so the first-pass directive fires. Once the skill writes a
110
+ contract, the dict carries at least one of the required keys;
111
+ from there :func:`_missing_required_keys` decides whether the
112
+ contract is complete enough to advance.
113
+ """
114
+ if not isinstance(contract, dict):
115
+ return False
116
+ if not contract:
117
+ return False
118
+ return any(key in contract for key in REQUIRED_CONTRACT_KEYS)
119
+
120
+
121
+ def _missing_required_keys(contract: dict[str, Any]) -> list[str]:
122
+ """Return required top-level keys that are missing or empty.
123
+
124
+ Schema validation (``state._validate_contract``) already rejects
125
+ non-list ``data_model`` / ``api_surface``; here we treat empty
126
+ lists the same as missing — an empty data model or an empty
127
+ surface gives the UI nothing to consume.
128
+ """
129
+ missing: list[str] = []
130
+ for key in REQUIRED_CONTRACT_KEYS:
131
+ value = contract.get(key)
132
+ if value is None or value == [] or value == {}:
133
+ missing.append(key)
134
+ return missing
135
+
136
+
137
+ def _preview_input(state: DeliveryState) -> str:
138
+ """Render a one-line preview of the input being contracted."""
139
+ data = state.ticket or {}
140
+ raw = data.get("raw")
141
+ if isinstance(raw, str) and raw.strip():
142
+ text = " ".join(raw.split())
143
+ else:
144
+ title = data.get("title")
145
+ text = title if isinstance(title, str) else (data.get("id") or "(no title)")
146
+ if len(text) <= 80:
147
+ return text
148
+ return text[:79].rstrip() + "\u2026"
149
+
150
+
151
+ def _blocked_on_precondition(state: DeliveryState) -> StepResult:
152
+ """BLOCKED halt \u2014 upstream ``analyze`` did not succeed."""
153
+ return StepResult(
154
+ outcome=Outcome.BLOCKED,
155
+ questions=[
156
+ "> `analyze` did not succeed; cannot lock the contract until "
157
+ "the upstream investigation lands.",
158
+ "> 1. Re-run \u2014 restart the mixed flow from the start",
159
+ "> 2. Abort \u2014 drop this request",
160
+ ],
161
+ message="contract step gated on analyze; upstream outcome is not success.",
162
+ )
163
+
164
+
165
+ def _delegate_to_feature_plan(state: DeliveryState) -> StepResult:
166
+ """Halt with an agent directive so the orchestrator runs ``feature-plan``.
167
+
168
+ The directive carries a ``contract-only`` scope marker so the
169
+ skill produces just the data model and API surface \u2014 no UI
170
+ plan yet (Phase 4 Step 2 owns that).
171
+ """
172
+ preview = _preview_input(state)
173
+ return StepResult(
174
+ outcome=Outcome.BLOCKED,
175
+ questions=[
176
+ agent_directive("contract-plan"),
177
+ f"> Input: {preview}",
178
+ "> No backend contract yet \u2014 producing one now. The contract "
179
+ "locks the data model and API surface the UI will consume; "
180
+ "the mixed `ui` step refuses to start without it.",
181
+ "> Scope: contract-only (no UI plan, no implementation).",
182
+ "> 1. Continue \u2014 produce the contract via `feature-plan`",
183
+ "> 2. Abort \u2014 drop this mixed request",
184
+ ],
185
+ message="Mixed contract missing; delegating to feature-plan (contract-only).",
186
+ )
187
+
188
+
189
+ def _halt_incomplete_contract(
190
+ state: DeliveryState,
191
+ missing: list[str],
192
+ ) -> StepResult:
193
+ """BLOCKED halt \u2014 contract is missing required keys."""
194
+ preview = _preview_input(state)
195
+ lines = [
196
+ agent_directive("contract-plan"),
197
+ f"> Input: {preview}",
198
+ "> Backend contract is incomplete. Missing required slots:",
199
+ ]
200
+ for path in missing:
201
+ lines.append(f"> - `{path}`")
202
+ lines.append(
203
+ "> Re-run `feature-plan` (contract-only) so every required slot "
204
+ "has a final value before the UI track starts.",
205
+ )
206
+ return StepResult(
207
+ outcome=Outcome.BLOCKED,
208
+ questions=lines,
209
+ message=(
210
+ f"Mixed contract incomplete; {len(missing)} required slot(s) missing."
211
+ ),
212
+ )
213
+
214
+
215
+ def _halt_unconfirmed(
216
+ state: DeliveryState,
217
+ contract: dict[str, Any],
218
+ ) -> StepResult:
219
+ """BLOCKED halt \u2014 contract is well-formed; user must sign off."""
220
+ preview = _preview_input(state)
221
+ data_model = contract.get("data_model") or []
222
+ api_surface = contract.get("api_surface") or []
223
+
224
+ entity_count = len(data_model) if isinstance(data_model, list) else 0
225
+ endpoint_count = len(api_surface) if isinstance(api_surface, list) else 0
226
+
227
+ lines = [
228
+ f"> Input: {preview}",
229
+ "> Backend contract is ready. Summary:",
230
+ f"> - Entities (data model): {entity_count}",
231
+ f"> - Endpoints / actions (api surface): {endpoint_count}",
232
+ "> 1. Confirm \u2014 lock this contract and advance to the UI track",
233
+ "> 2. Revise \u2014 send feedback; loops back to `contract-plan`",
234
+ "> 3. Abort \u2014 drop this mixed request",
235
+ "",
236
+ "**Recommendation: 1 \u2014 Confirm** \u2014 the contract covers both "
237
+ "required slots. Caveat: flip to 2 only if an entity field or "
238
+ "endpoint signature is wrong; the UI track will treat this "
239
+ "contract as immutable input.",
240
+ ]
241
+ return StepResult(
242
+ outcome=Outcome.BLOCKED,
243
+ questions=lines,
244
+ message=(
245
+ "Mixed contract ready; halting for user confirmation before UI track."
246
+ ),
247
+ )
248
+
249
+
250
+ __all__ = [
251
+ "AMBIGUITIES",
252
+ "REQUIRED_CONTRACT_KEYS",
253
+ "run",
254
+ ]
@@ -0,0 +1,229 @@
1
+ """``stitch`` step — integration verification across the contract / UI seam.
2
+
3
+ Phase 4 Step 3 of ``agents/roadmaps/road-to-product-ui-track.md``: in the
4
+ ``mixed`` directive set the ``test`` slot is the integration boundary.
5
+ The dispatcher does not run integration scenarios itself — the agent
6
+ invokes the ``integration-test`` skill which drives end-to-end smokes
7
+ (fill form → server validation → response → UI update) and writes the
8
+ verdict back to ``state.stitch``.
9
+
10
+ ``state.stitch`` contract when populated:
11
+
12
+ - Must be a dict.
13
+ - Must carry a ``verdict`` key — one of ``success``, ``blocked``,
14
+ ``partial`` (mirrors the ``Outcome`` vocabulary used in
15
+ ``backend.verify``).
16
+ - Optional ``scenarios`` list documents the smoke cases run.
17
+ - Optional ``integration_confirmed`` boolean — when ``True``, the user
18
+ has signed off on a partial / blocked verdict and the flow may
19
+ advance regardless. Treated as an explicit override.
20
+
21
+ Routing:
22
+
23
+ - **Upstream ``implement`` (mixed.ui) outcome not success** — refuse;
24
+ the contract / UI handoff did not complete.
25
+ - **``state.stitch`` empty** — emit ``@agent-directive: integration-test``
26
+ so the orchestrator runs the smoke scenarios.
27
+ - **Stitch shape malformed** — halt for re-run; verdict cannot be
28
+ trusted.
29
+ - **Verdict ``success``** — return ``SUCCESS``; dispatcher advances to
30
+ ``verify``.
31
+ - **Verdict ``blocked`` / ``partial`` and not user-confirmed** — halt
32
+ with three numbered options (fix / override / abort).
33
+ - **Verdict ``blocked`` / ``partial`` and ``integration_confirmed=True``**
34
+ — return ``SUCCESS`` (explicit user override).
35
+ """
36
+ from __future__ import annotations
37
+
38
+ from typing import Any
39
+
40
+ from ...delivery_state import (
41
+ DeliveryState,
42
+ Outcome,
43
+ StepResult,
44
+ agent_directive,
45
+ )
46
+
47
+ INTEGRATION_TEST_DIRECTIVE = "integration-test"
48
+ """Agent directive that drives end-to-end smoke scenarios.
49
+
50
+ The skill fills forms, hits the locked endpoints, and asserts the UI
51
+ reflects the response. It writes ``state.stitch`` with a verdict and
52
+ the list of scenarios it ran. The mixed flow routes on the verdict.
53
+ """
54
+
55
+ _ALLOWED_VERDICTS = ("success", "blocked", "partial")
56
+
57
+ AMBIGUITIES: tuple[dict[str, str], ...] = (
58
+ {
59
+ "code": "upstream_ui_failed",
60
+ "trigger": "`implement` (mixed.ui) outcome is not `success`",
61
+ "resolution": "re-run the mixed flow; UI track must finish before stitch",
62
+ },
63
+ {
64
+ "code": "empty_stitch_delegate",
65
+ "trigger": "`state.stitch` empty — integration-test skill has not run yet",
66
+ "resolution": "agent directive `integration-test` → end-to-end smokes",
67
+ },
68
+ {
69
+ "code": "malformed_stitch",
70
+ "trigger": (
71
+ "`state.stitch` is not a dict or `verdict` is not one of "
72
+ "success / blocked / partial"
73
+ ),
74
+ "resolution": "re-run `integration-test` and record a clean verdict",
75
+ },
76
+ {
77
+ "code": "bad_stitch_verdict",
78
+ "trigger": "`state.stitch['verdict']` is `blocked` or `partial` "
79
+ "and `integration_confirmed` is not True",
80
+ "resolution": (
81
+ "address findings and re-run `integration-test`, or set "
82
+ "`integration_confirmed=True` to override (rare)"
83
+ ),
84
+ },
85
+ )
86
+ """Declared ambiguity surfaces. Every BLOCKED return maps to one code."""
87
+
88
+
89
+ def run(state: DeliveryState) -> StepResult:
90
+ """Gate on ``implement``, then validate ``state.stitch``."""
91
+ if state.outcomes.get("implement") != Outcome.SUCCESS.value:
92
+ return _blocked_on_precondition(state)
93
+
94
+ stitch = state.stitch
95
+ if not stitch:
96
+ return _delegate_to_integration_test(state)
97
+
98
+ shape_issues = _diagnose_stitch(stitch)
99
+ if shape_issues:
100
+ return _blocked_on_shape(state, shape_issues)
101
+
102
+ verdict = stitch.get("verdict")
103
+ if verdict == "success":
104
+ return StepResult(outcome=Outcome.SUCCESS)
105
+
106
+ if stitch.get("integration_confirmed") is True:
107
+ return StepResult(
108
+ outcome=Outcome.SUCCESS,
109
+ message=(
110
+ f"stitch verdict `{verdict}` overridden by "
111
+ "integration_confirmed=True."
112
+ ),
113
+ )
114
+
115
+ return _blocked_on_bad_verdict(state, verdict, stitch)
116
+
117
+
118
+ def _diagnose_stitch(stitch: Any) -> list[str]:
119
+ """Return shape errors; empty list when ``stitch`` is well-formed."""
120
+ if not isinstance(stitch, dict):
121
+ return [f"state.stitch must be a dict, got {type(stitch).__name__}"]
122
+ verdict = stitch.get("verdict")
123
+ if verdict not in _ALLOWED_VERDICTS:
124
+ return [
125
+ f"state.stitch['verdict'] must be one of "
126
+ f"{', '.join(_ALLOWED_VERDICTS)}; got {verdict!r}",
127
+ ]
128
+ return []
129
+
130
+
131
+ def _preview_input(state: DeliveryState) -> str:
132
+ """One-line preview of the original input for halt bodies."""
133
+ data = state.ticket or {}
134
+ raw = data.get("raw")
135
+ if isinstance(raw, str) and raw.strip():
136
+ text = " ".join(raw.split())
137
+ else:
138
+ title = data.get("title")
139
+ text = title if isinstance(title, str) else (data.get("id") or "(no title)")
140
+ if len(text) <= 80:
141
+ return text
142
+ return text[:79].rstrip() + "\u2026"
143
+
144
+
145
+ def _blocked_on_precondition(state: DeliveryState) -> StepResult:
146
+ """BLOCKED halt — upstream UI step did not succeed."""
147
+ return StepResult(
148
+ outcome=Outcome.BLOCKED,
149
+ questions=[
150
+ "> Mixed `stitch` step gated on the UI step; "
151
+ "the implement outcome is not success.",
152
+ "> 1. Re-run \u2014 restart the mixed flow from the start",
153
+ "> 2. Abort \u2014 drop this request",
154
+ ],
155
+ message="stitch step gated on implement; upstream UI outcome is not success.",
156
+ )
157
+
158
+
159
+ def _delegate_to_integration_test(state: DeliveryState) -> StepResult:
160
+ """Halt with an agent directive so the orchestrator runs smokes."""
161
+ preview = _preview_input(state)
162
+ contract = state.contract if isinstance(state.contract, dict) else {}
163
+ api_surface = contract.get("api_surface") or []
164
+ endpoint_count = len(api_surface) if isinstance(api_surface, list) else 0
165
+
166
+ return StepResult(
167
+ outcome=Outcome.BLOCKED,
168
+ questions=[
169
+ agent_directive(INTEGRATION_TEST_DIRECTIVE),
170
+ f"> Input: {preview}",
171
+ "> UI track finished; the contract / UI seam needs end-to-end "
172
+ "verification before the delivery report:",
173
+ f"> - Endpoints / actions to smoke: {endpoint_count}",
174
+ "> Scenarios cover the full round-trip \u2014 fill form \u2192 "
175
+ "server validation \u2192 response \u2192 UI update. Unit-level "
176
+ "passes from the UI review do **not** substitute for this gate.",
177
+ "> 1. Continue \u2014 run `integration-test` now",
178
+ "> 2. Abort \u2014 skip integration verification (NOT recommended)",
179
+ ],
180
+ message="Mixed UI complete; delegating to integration-test for stitch.",
181
+ )
182
+
183
+
184
+ def _blocked_on_shape(state: DeliveryState, issues: list[str]) -> StepResult:
185
+ """BLOCKED halt — recorded stitch verdict is malformed."""
186
+ return StepResult(
187
+ outcome=Outcome.BLOCKED,
188
+ questions=[
189
+ "> Recorded stitch output is malformed: "
190
+ + "; ".join(issues)
191
+ + ".",
192
+ "> 1. Re-run `integration-test` and resume",
193
+ "> 2. Abort \u2014 stitch verdict cannot be trusted",
194
+ ],
195
+ message=f"Mixed stitch shape invalid: {'; '.join(issues)}.",
196
+ )
197
+
198
+
199
+ def _blocked_on_bad_verdict(
200
+ state: DeliveryState,
201
+ verdict: Any,
202
+ stitch: dict[str, Any],
203
+ ) -> StepResult:
204
+ """BLOCKED halt — verdict is blocked / partial and not user-confirmed."""
205
+ scenarios = stitch.get("scenarios") or []
206
+ scenario_count = len(scenarios) if isinstance(scenarios, list) else 0
207
+ return StepResult(
208
+ outcome=Outcome.BLOCKED,
209
+ questions=[
210
+ f"> `integration-test` reported `{verdict}` after running "
211
+ f"{scenario_count} scenario(s). The delivery report cannot "
212
+ "claim completion on a non-success integration verdict.",
213
+ "> 1. Address the findings and re-run `integration-test`",
214
+ "> 2. Override \u2014 set `state.stitch.integration_confirmed=true` "
215
+ "and resume (rare; document why)",
216
+ "> 3. Abort",
217
+ ],
218
+ message=(
219
+ f"Mixed stitch verdict was `{verdict}`, not success; "
220
+ "user override required to continue."
221
+ ),
222
+ )
223
+
224
+
225
+ __all__ = [
226
+ "AMBIGUITIES",
227
+ "INTEGRATION_TEST_DIRECTIVE",
228
+ "run",
229
+ ]