@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.
- package/.agent-src/commands/agent-handoff.md +3 -0
- package/.agent-src/commands/agent-status.md +3 -0
- package/.agent-src/commands/agents-audit.md +4 -0
- package/.agent-src/commands/agents-cleanup.md +6 -1
- package/.agent-src/commands/agents-prepare.md +3 -0
- package/.agent-src/commands/analyze-reference-repo.md +4 -0
- package/.agent-src/commands/bug-fix.md +5 -1
- package/.agent-src/commands/bug-investigate.md +4 -0
- package/.agent-src/commands/chat-history-checkpoint.md +126 -0
- package/.agent-src/commands/chat-history-clear.md +5 -0
- package/.agent-src/commands/chat-history-resume.md +5 -0
- package/.agent-src/commands/chat-history.md +5 -0
- package/.agent-src/commands/check-current-md.md +126 -0
- package/.agent-src/commands/commit-in-chunks.md +98 -0
- package/.agent-src/commands/commit.md +4 -0
- package/.agent-src/commands/compress.md +3 -0
- package/.agent-src/commands/context-create.md +4 -0
- package/.agent-src/commands/context-refactor.md +4 -0
- package/.agent-src/commands/copilot-agents-init.md +3 -0
- package/.agent-src/commands/copilot-agents-optimize.md +3 -0
- package/.agent-src/commands/create-pr-description.md +4 -0
- package/.agent-src/commands/create-pr.md +4 -0
- package/.agent-src/commands/do-and-judge.md +4 -1
- package/.agent-src/commands/do-in-steps.md +3 -0
- package/.agent-src/commands/e2e-heal.md +4 -0
- package/.agent-src/commands/e2e-plan.md +4 -0
- package/.agent-src/commands/estimate-ticket.md +4 -1
- package/.agent-src/commands/feature-dev.md +4 -0
- package/.agent-src/commands/feature-explore.md +4 -0
- package/.agent-src/commands/feature-plan.md +4 -0
- package/.agent-src/commands/feature-refactor.md +4 -0
- package/.agent-src/commands/feature-roadmap.md +6 -0
- package/.agent-src/commands/fix-ci.md +4 -0
- package/.agent-src/commands/fix-portability.md +3 -0
- package/.agent-src/commands/fix-pr-bot-comments.md +4 -0
- package/.agent-src/commands/fix-pr-comments.md +4 -0
- package/.agent-src/commands/fix-pr-developer-comments.md +4 -0
- package/.agent-src/commands/fix-references.md +3 -0
- package/.agent-src/commands/fix-seeder.md +4 -0
- package/.agent-src/commands/implement-ticket.md +39 -13
- package/.agent-src/commands/jira-ticket.md +4 -0
- package/.agent-src/commands/judge.md +3 -0
- package/.agent-src/commands/memory-add.md +5 -3
- package/.agent-src/commands/memory-full.md +5 -2
- package/.agent-src/commands/memory-promote.md +7 -6
- package/.agent-src/commands/mode.md +3 -0
- package/.agent-src/commands/module-create.md +4 -0
- package/.agent-src/commands/module-explore.md +4 -0
- package/.agent-src/commands/onboard.md +24 -0
- package/.agent-src/commands/optimize-agents.md +4 -0
- package/.agent-src/commands/optimize-augmentignore.md +3 -0
- package/.agent-src/commands/optimize-rtk-filters.md +3 -0
- package/.agent-src/commands/optimize-skills.md +4 -0
- package/.agent-src/commands/override-create.md +4 -0
- package/.agent-src/commands/override-manage.md +4 -0
- package/.agent-src/commands/package-reset.md +3 -0
- package/.agent-src/commands/package-test.md +3 -0
- package/.agent-src/commands/prepare-for-review.md +4 -0
- package/.agent-src/commands/project-analyze.md +4 -0
- package/.agent-src/commands/project-health.md +4 -0
- package/.agent-src/commands/propose-memory.md +6 -8
- package/.agent-src/commands/quality-fix.md +4 -0
- package/.agent-src/commands/refine-ticket.md +4 -1
- package/.agent-src/commands/review-changes.md +4 -0
- package/.agent-src/commands/review-routing.md +4 -0
- package/.agent-src/commands/roadmap-create.md +7 -0
- package/.agent-src/commands/roadmap-execute.md +12 -1
- package/.agent-src/commands/rule-compliance-audit.md +4 -0
- package/.agent-src/commands/set-cost-profile.md +3 -0
- package/.agent-src/commands/sync-agent-settings.md +3 -0
- package/.agent-src/commands/sync-gitignore.md +3 -0
- package/.agent-src/commands/tests-create.md +4 -0
- package/.agent-src/commands/tests-execute.md +4 -0
- package/.agent-src/commands/threat-model.md +4 -0
- package/.agent-src/commands/update-form-request-messages.md +4 -0
- package/.agent-src/commands/upstream-contribute.md +4 -0
- package/.agent-src/commands/work.md +161 -0
- package/.agent-src/guidelines/agent-infra/engineering-memory-data-format.md +2 -6
- package/.agent-src/guidelines/agent-infra/layered-settings.md +0 -1
- package/.agent-src/guidelines/agent-infra/memory-access.md +0 -7
- package/.agent-src/guidelines/agent-infra/role-contracts.md +2 -4
- package/.agent-src/guidelines/agent-infra/self-improvement-pipeline.md +0 -1
- package/.agent-src/guidelines/php/patterns/strategy.md +180 -2
- package/.agent-src/personas/README.md +0 -1
- package/.agent-src/rules/artifact-drafting-protocol.md +7 -2
- package/.agent-src/rules/artifact-engagement-recording.md +133 -0
- package/.agent-src/rules/ask-when-uncertain.md +18 -13
- package/.agent-src/rules/augment-portability.md +8 -0
- package/.agent-src/rules/autonomous-execution.md +158 -0
- package/.agent-src/rules/chat-history.md +147 -118
- package/.agent-src/rules/cli-output-handling.md +26 -3
- package/.agent-src/rules/command-suggestion.md +133 -0
- package/.agent-src/rules/commit-policy.md +99 -0
- package/.agent-src/rules/direct-answers.md +114 -0
- package/.agent-src/rules/docs-sync.md +36 -0
- package/.agent-src/rules/downstream-changes.md +10 -9
- package/.agent-src/rules/improve-before-implement.md +9 -6
- package/.agent-src/rules/language-and-tone.md +81 -6
- package/.agent-src/rules/non-destructive-by-default.md +117 -0
- package/.agent-src/rules/package-ci-checks.md +4 -0
- package/.agent-src/rules/preservation-guard.md +20 -0
- package/.agent-src/rules/roadmap-progress-sync.md +103 -30
- package/.agent-src/rules/scope-control.md +42 -1
- package/.agent-src/rules/size-enforcement.md +1 -3
- package/.agent-src/rules/skill-quality.md +3 -8
- package/.agent-src/rules/ui-audit-before-build.md +106 -0
- package/.agent-src/rules/user-interaction.md +82 -50
- package/.agent-src/scripts/update_roadmap_progress.py +17 -5
- package/.agent-src/skills/blade-ui/SKILL.md +30 -5
- package/.agent-src/skills/command-routing/SKILL.md +32 -0
- package/.agent-src/skills/command-writing/SKILL.md +41 -2
- package/.agent-src/skills/description-assist/SKILL.md +21 -0
- package/.agent-src/skills/estimate-ticket/SKILL.md +0 -1
- package/.agent-src/skills/existing-ui-audit/SKILL.md +187 -0
- package/.agent-src/skills/fe-design/SKILL.md +72 -60
- package/.agent-src/skills/finishing-a-development-branch/SKILL.md +4 -0
- package/.agent-src/skills/flux/SKILL.md +31 -4
- package/.agent-src/skills/guideline-writing/SKILL.md +24 -2
- package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +51 -9
- package/.agent-src/skills/livewire/SKILL.md +30 -4
- package/.agent-src/skills/md-language-check/SKILL.md +103 -0
- package/.agent-src/skills/php-coder/SKILL.md +24 -0
- package/.agent-src/skills/react-shadcn-ui/SKILL.md +121 -0
- package/.agent-src/skills/refine-prompt/SKILL.md +220 -0
- package/.agent-src/skills/refine-ticket/SKILL.md +2 -4
- package/.agent-src/skills/roadmap-management/SKILL.md +10 -3
- package/.agent-src/skills/rule-writing/SKILL.md +23 -1
- package/.agent-src/skills/skill-writing/SKILL.md +1 -3
- package/.agent-src/skills/upstream-contribute/SKILL.md +1 -1
- package/.agent-src/skills/using-git-worktrees/SKILL.md +3 -1
- package/.agent-src/templates/AGENTS.md +24 -6
- package/.agent-src/templates/agent-settings.md +149 -0
- package/.agent-src/templates/roadmaps.md +8 -2
- package/.agent-src/templates/scripts/implement_ticket/__init__.py +63 -26
- package/.agent-src/templates/scripts/implement_ticket/__main__.py +8 -2
- package/.agent-src/templates/scripts/telemetry/__init__.py +42 -0
- package/.agent-src/templates/scripts/telemetry/aggregator.py +154 -0
- package/.agent-src/templates/scripts/telemetry/boundary.py +171 -0
- package/.agent-src/templates/scripts/telemetry/engagement.py +238 -0
- package/.agent-src/templates/scripts/telemetry/report_renderer.py +170 -0
- package/.agent-src/templates/scripts/telemetry/settings.py +112 -0
- package/.agent-src/templates/scripts/telemetry_record.py +166 -0
- package/.agent-src/templates/scripts/telemetry_report.py +161 -0
- package/.agent-src/templates/scripts/telemetry_status.py +142 -0
- package/.agent-src/templates/scripts/work_engine/__init__.py +58 -0
- package/.agent-src/templates/scripts/work_engine/__main__.py +9 -0
- package/.agent-src/templates/scripts/work_engine/cli.py +592 -0
- package/.agent-src/templates/scripts/{implement_ticket → work_engine}/delivery_state.py +7 -0
- package/.agent-src/templates/scripts/work_engine/directives/__init__.py +33 -0
- package/.agent-src/templates/scripts/work_engine/directives/backend/__init__.py +98 -0
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/analyze.py +1 -1
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/implement.py +2 -2
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/memory.py +1 -1
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/plan.py +1 -1
- package/.agent-src/templates/scripts/work_engine/directives/backend/refine.py +396 -0
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/report.py +36 -4
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/test.py +2 -2
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/verify.py +2 -2
- package/.agent-src/templates/scripts/work_engine/directives/mixed/__init__.py +116 -0
- package/.agent-src/templates/scripts/work_engine/directives/mixed/contract.py +254 -0
- package/.agent-src/templates/scripts/work_engine/directives/mixed/stitch.py +229 -0
- package/.agent-src/templates/scripts/work_engine/directives/mixed/ui.py +231 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/__init__.py +113 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/_passthrough.py +44 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/apply.py +241 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/audit.py +414 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/design.py +335 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/polish.py +510 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui/review.py +468 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/__init__.py +119 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/_skipped.py +37 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/apply.py +165 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/refine.py +66 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/report.py +62 -0
- package/.agent-src/templates/scripts/work_engine/directives/ui_trivial/test.py +115 -0
- package/.agent-src/templates/scripts/work_engine/dispatcher.py +331 -0
- package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +54 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +32 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +103 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +44 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +42 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_heartbeat.py +50 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_turn_check.py +49 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/directive_set_guard.py +53 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/halt_surface_audit.py +50 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/state_shape_validation.py +52 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/trace.py +84 -0
- package/.agent-src/templates/scripts/work_engine/hooks/context.py +66 -0
- package/.agent-src/templates/scripts/work_engine/hooks/events.py +44 -0
- package/.agent-src/templates/scripts/work_engine/hooks/exceptions.py +79 -0
- package/.agent-src/templates/scripts/work_engine/hooks/registry.py +60 -0
- package/.agent-src/templates/scripts/work_engine/hooks/runner.py +73 -0
- package/.agent-src/templates/scripts/work_engine/hooks/settings.py +141 -0
- package/.agent-src/templates/scripts/work_engine/intent/__init__.py +47 -0
- package/.agent-src/templates/scripts/work_engine/intent/classify.py +280 -0
- package/.agent-src/templates/scripts/work_engine/migration/__init__.py +8 -0
- package/.agent-src/templates/scripts/work_engine/migration/v0_to_v1.py +199 -0
- package/.agent-src/templates/scripts/work_engine/resolvers/__init__.py +22 -0
- package/.agent-src/templates/scripts/work_engine/resolvers/diff.py +106 -0
- package/.agent-src/templates/scripts/work_engine/resolvers/file.py +113 -0
- package/.agent-src/templates/scripts/work_engine/resolvers/prompt.py +90 -0
- package/.agent-src/templates/scripts/work_engine/scoring/__init__.py +14 -0
- package/.agent-src/templates/scripts/work_engine/scoring/confidence.py +300 -0
- package/.agent-src/templates/scripts/work_engine/stack/__init__.py +31 -0
- package/.agent-src/templates/scripts/work_engine/stack/detect.py +187 -0
- package/.agent-src/templates/scripts/work_engine/state.py +641 -0
- package/.claude-plugin/marketplace.json +105 -2
- package/AGENTS.md +36 -8
- package/CHANGELOG.md +534 -0
- package/README.md +125 -4
- package/config/agent-settings.template.yml +45 -0
- package/config/gitignore-block.txt +4 -0
- package/docs/architecture.md +28 -1
- package/docs/development.md +1 -1
- package/docs/getting-started.md +2 -2
- package/docs/installation.md +86 -0
- package/docs/showcase.md +204 -0
- package/package.json +1 -1
- package/scripts/agent-config +199 -0
- package/scripts/audit_cloud_compatibility.py +288 -0
- package/scripts/build_cloud_bundle.py +458 -0
- package/scripts/build_linear_digest.py +263 -0
- package/scripts/chat_history.py +796 -7
- package/scripts/check_compression.py +139 -0
- package/scripts/check_iron_law_prominence.py +143 -0
- package/scripts/check_md_language.py +159 -0
- package/scripts/check_portability.py +36 -0
- package/scripts/check_reply_consistency.py +140 -0
- package/scripts/command_suggester/__init__.py +51 -0
- package/scripts/command_suggester/cooldown.py +132 -0
- package/scripts/command_suggester/loader.py +70 -0
- package/scripts/command_suggester/match.py +180 -0
- package/scripts/command_suggester/rank.py +120 -0
- package/scripts/command_suggester/render.py +86 -0
- package/scripts/command_suggester/sanitize.py +113 -0
- package/scripts/command_suggester/settings.py +125 -0
- package/scripts/command_suggester/types.py +78 -0
- package/scripts/hooks/augment-chat-history.sh +56 -0
- package/scripts/install-hooks.sh +67 -0
- package/scripts/install.py +150 -33
- package/scripts/lint_marketplace.py +27 -0
- package/scripts/migrate_command_suggestions.py +151 -0
- package/scripts/schemas/command.schema.json +41 -0
- package/scripts/skill_linter.py +67 -0
- package/scripts/sync_agent_settings.py +42 -12
- package/templates/consumer-settings/augment-cli-hooks.json +54 -0
- package/templates/consumer-settings/claude-settings.json +55 -1
- package/.agent-src/templates/scripts/implement_ticket/cli.py +0 -171
- package/.agent-src/templates/scripts/implement_ticket/dispatcher.py +0 -134
- package/.agent-src/templates/scripts/implement_ticket/steps/__init__.py +0 -49
- package/.agent-src/templates/scripts/implement_ticket/steps/refine.py +0 -140
- /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
|
+
]
|