@event4u/agent-config 1.13.0 → 1.15.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 +4 -1
- 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 +7 -3
- 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 +6 -1
- package/.agent-src/commands/chat-history-resume.md +7 -2
- package/.agent-src/commands/chat-history.md +7 -2
- package/.agent-src/commands/check-current-md.md +137 -0
- package/.agent-src/commands/commit-in-chunks.md +118 -0
- package/.agent-src/commands/commit.md +4 -0
- package/.agent-src/commands/compress.md +37 -2
- 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 +5 -2
- 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 +33 -0
- package/.agent-src/commands/optimize-agents.md +4 -0
- package/.agent-src/commands/optimize-augmentignore.md +12 -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 +12 -7
- package/.agent-src/commands/review-changes.md +39 -8
- package/.agent-src/commands/review-routing.md +4 -0
- package/.agent-src/commands/roadmap-create.md +18 -0
- package/.agent-src/commands/roadmap-execute.md +14 -1
- package/.agent-src/commands/rule-compliance-audit.md +4 -0
- package/.agent-src/commands/set-cost-profile.md +11 -0
- package/.agent-src/commands/sync-agent-settings.md +12 -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 +6 -3
- 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 +64 -37
- package/.agent-src/rules/autonomous-execution.md +158 -0
- package/.agent-src/rules/chat-history-cadence.md +109 -0
- package/.agent-src/rules/chat-history-ownership.md +123 -0
- package/.agent-src/rules/chat-history-visibility.md +96 -0
- package/.agent-src/rules/cli-output-handling.md +27 -4
- package/.agent-src/rules/command-suggestion.md +134 -0
- package/.agent-src/rules/commit-policy.md +109 -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 +85 -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 +159 -27
- package/.agent-src/rules/role-mode-adherence.md +1 -1
- package/.agent-src/rules/scope-control.md +42 -1
- package/.agent-src/rules/size-enforcement.md +2 -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 +107 -51
- package/.agent-src/scripts/update_roadmap_progress.py +73 -9
- package/.agent-src/skills/blade-ui/SKILL.md +47 -3
- package/.agent-src/skills/command-routing/SKILL.md +32 -0
- package/.agent-src/skills/command-writing/SKILL.md +52 -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 +202 -0
- package/.agent-src/skills/fe-design/SKILL.md +78 -61
- package/.agent-src/skills/file-editor/SKILL.md +9 -0
- 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 +49 -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 +32 -28
- package/.agent-src/skills/roadmap-management/SKILL.md +24 -11
- package/.agent-src/skills/rule-writing/SKILL.md +23 -1
- package/.agent-src/skills/skill-writing/SKILL.md +3 -5
- package/.agent-src/skills/upstream-contribute/SKILL.md +3 -3
- 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 +11 -4
- 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/memory_lookup.py +1 -1
- 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 +195 -0
- package/.agent-src/templates/scripts/work_engine/cli_args.py +116 -0
- package/.agent-src/templates/scripts/{implement_ticket → work_engine}/delivery_state.py +10 -3
- 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 +3 -3
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/memory.py +2 -2
- package/.agent-src/templates/scripts/{implement_ticket/steps → work_engine/directives/backend}/plan.py +2 -2
- 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 +37 -5
- 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/emitters.py +43 -0
- package/.agent-src/templates/scripts/work_engine/errors.py +19 -0
- package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +76 -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/input_builders.py +163 -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 +231 -0
- package/.agent-src/templates/scripts/{implement_ticket → work_engine}/persona_policy.py +1 -1
- 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/.agent-src/templates/scripts/work_engine/state_io.py +202 -0
- package/.claude-plugin/marketplace.json +105 -2
- package/AGENTS.md +38 -8
- package/CHANGELOG.md +609 -0
- package/README.md +136 -14
- package/config/agent-settings.template.yml +45 -0
- package/config/gitignore-block.txt +4 -0
- package/docs/MIGRATION.md +122 -0
- package/docs/architecture.md +111 -35
- package/docs/contracts/STABILITY.md +95 -0
- package/docs/contracts/adr-chat-history-split.md +132 -0
- package/docs/contracts/adr-command-suggestion.md +146 -0
- package/docs/contracts/adr-implement-ticket-runtime.md +122 -0
- package/docs/contracts/adr-product-ui-track.md +384 -0
- package/docs/contracts/adr-prompt-driven-execution.md +187 -0
- package/docs/contracts/agent-memory-contract.md +149 -0
- package/docs/contracts/artifact-engagement-flow.md +262 -0
- package/docs/contracts/command-clusters.md +126 -0
- package/docs/contracts/command-suggestion-flow.md +148 -0
- package/docs/contracts/implement-ticket-flow.md +628 -0
- package/docs/contracts/linear-ai-rules-inclusion.md +143 -0
- package/docs/contracts/linear-ai-three-layers.md +131 -0
- package/docs/contracts/rule-interactions.md +107 -0
- package/docs/contracts/rule-interactions.yml +142 -0
- package/docs/contracts/ui-stack-extension.md +236 -0
- package/docs/contracts/ui-track-flow.md +338 -0
- package/docs/development.md +1 -1
- package/docs/getting-started.md +3 -3
- package/docs/installation.md +124 -2
- package/docs/migrations/commands-1.15.0.md +112 -0
- package/docs/showcase.md +204 -0
- package/docs/ui-track-mental-model.md +121 -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 +38 -0
- package/scripts/check_public_links.py +185 -0
- package/scripts/check_references.py +1 -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/lint_no_new_atomic_commands.py +179 -0
- package/scripts/lint_rule_interactions.py +149 -0
- package/scripts/memory_lookup.py +1 -1
- package/scripts/migrate_command_suggestions.py +151 -0
- package/scripts/release.py +297 -64
- package/scripts/schemas/command.schema.json +41 -0
- package/scripts/skill_linter.py +81 -0
- package/scripts/sync_agent_settings.py +42 -12
- package/scripts/update_counts.py +10 -0
- package/templates/consumer-settings/augment-cli-hooks.json +54 -0
- package/templates/consumer-settings/claude-settings.json +55 -1
- package/.agent-src/rules/chat-history.md +0 -171
- 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
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
"""``review`` step — stack-dispatched design-review pass.
|
|
2
|
+
|
|
3
|
+
Phase 3 Step 4 of ``agents/roadmaps/road-to-product-ui-track.md``: the
|
|
4
|
+
review step compares the rendered components from ``apply`` against
|
|
5
|
+
the locked design brief and produces a structured findings list. It
|
|
6
|
+
does **not** apply fixes — that is the polish step's job. Review's
|
|
7
|
+
single output is ``state.ui_review`` carrying ``findings`` (a list of
|
|
8
|
+
zero or more issue records) and ``review_clean`` (a bool that mirrors
|
|
9
|
+
``len(findings) == 0`` once the agent finalises the pass).
|
|
10
|
+
|
|
11
|
+
Routes on ``state.ui_review`` shape:
|
|
12
|
+
|
|
13
|
+
- **Empty / None / non-dict** — first pass. Emit
|
|
14
|
+
``@agent-directive: ui-design-review-<stack>`` delegating to the
|
|
15
|
+
stack-specific review skill; on the rebound the envelope lands in
|
|
16
|
+
``state.ui_review``.
|
|
17
|
+
- **Populated, missing ``findings``** — partial envelope, the skill
|
|
18
|
+
has to finish the pass. Halt with the same directive so the agent
|
|
19
|
+
re-runs the review.
|
|
20
|
+
- **Populated, ``findings`` present, ``review_clean`` missing or not
|
|
21
|
+
a bool** — halt asking the agent to set the flag explicitly. Polish
|
|
22
|
+
reads it and would short-circuit the loop on a wrong value.
|
|
23
|
+
- **Populated, well-formed** — return ``SUCCESS``; the dispatcher
|
|
24
|
+
advances to ``verify`` (polish), which decides whether to loop.
|
|
25
|
+
|
|
26
|
+
Review does **not** enforce ``review_clean == (len(findings) == 0)``.
|
|
27
|
+
That looks tempting but it blocks the legitimate "ship as-is with
|
|
28
|
+
open findings" replay path: polish's ceiling halt asks the user to
|
|
29
|
+
set ``review_clean = True`` while findings are still present, the
|
|
30
|
+
dispatcher advances to report, and a later replay of the state file
|
|
31
|
+
would re-enter review with that envelope. Honesty of the flag is the
|
|
32
|
+
producer's contract (the review skill on first pass; the polish skill
|
|
33
|
+
on ship-as-is); review only checks the shape.
|
|
34
|
+
|
|
35
|
+
Idempotent: a re-entry with the same well-formed envelope round-trips
|
|
36
|
+
through ``SUCCESS`` without re-emitting a halt.
|
|
37
|
+
"""
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
from typing import Any
|
|
41
|
+
|
|
42
|
+
from ...delivery_state import (
|
|
43
|
+
DeliveryState,
|
|
44
|
+
Outcome,
|
|
45
|
+
StepResult,
|
|
46
|
+
agent_directive,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
STACK_DIRECTIVES: dict[str, str] = {
|
|
50
|
+
"blade-livewire-flux": "ui-design-review-blade-livewire-flux",
|
|
51
|
+
"react-shadcn": "ui-design-review-react-shadcn",
|
|
52
|
+
"vue": "ui-design-review-vue",
|
|
53
|
+
"plain": "ui-design-review-plain",
|
|
54
|
+
}
|
|
55
|
+
"""Map ``state.stack.frontend`` → agent-directive skill name.
|
|
56
|
+
|
|
57
|
+
Mirrors :data:`work_engine.directives.ui.apply.STACK_DIRECTIVES` so
|
|
58
|
+
review fires the matching review skill for the stack apply targeted.
|
|
59
|
+
An unknown stack falls through to ``ui-design-review-plain``.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
DEFAULT_DIRECTIVE = "ui-design-review-plain"
|
|
63
|
+
"""Fallback directive when ``state.stack`` is missing or malformed."""
|
|
64
|
+
|
|
65
|
+
SEVERITY_ORDER: dict[str, int] = {
|
|
66
|
+
"minor": 0,
|
|
67
|
+
"moderate": 1,
|
|
68
|
+
"serious": 2,
|
|
69
|
+
"critical": 3,
|
|
70
|
+
}
|
|
71
|
+
"""R4 a11y severity ranking — mirrors axe-core's impact levels."""
|
|
72
|
+
|
|
73
|
+
DEFAULT_SEVERITY_FLOOR = "moderate"
|
|
74
|
+
"""R4 a11y default severity floor — violations strictly below this are
|
|
75
|
+
informational; violations at or above are actionable."""
|
|
76
|
+
|
|
77
|
+
AMBIGUITIES: tuple[dict[str, str], ...] = (
|
|
78
|
+
{
|
|
79
|
+
"code": "review_envelope_missing",
|
|
80
|
+
"trigger": "state.ui_review unset / empty — review skill has not run yet",
|
|
81
|
+
"resolution": "agent directive `ui-design-review-<stack>` → "
|
|
82
|
+
"skill compares rendered components against state.ui_design "
|
|
83
|
+
"and writes `findings` + `review_clean` back",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"code": "review_findings_missing",
|
|
87
|
+
"trigger": "state.ui_review populated but `findings` key absent",
|
|
88
|
+
"resolution": "agent re-runs the review skill with the same "
|
|
89
|
+
"directive; review only succeeds once findings is a list",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"code": "review_clean_missing",
|
|
93
|
+
"trigger": "state.ui_review.findings is set but review_clean "
|
|
94
|
+
"is missing or not a bool — polish needs an explicit flag",
|
|
95
|
+
"resolution": "agent sets state.ui_review.review_clean to "
|
|
96
|
+
"True or False before returning the envelope; review does "
|
|
97
|
+
"not infer it from findings count",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"code": "review_a11y_pending",
|
|
101
|
+
"trigger": "state.ui_audit declared an `a11y_baseline` but "
|
|
102
|
+
"state.ui_review.a11y is missing — the review skill ran but "
|
|
103
|
+
"did not produce an a11y envelope",
|
|
104
|
+
"resolution": "agent re-runs the review skill so it captures "
|
|
105
|
+
"axe-core (or equivalent) findings into "
|
|
106
|
+
"`state.ui_review.a11y.violations`; the gate then filters "
|
|
107
|
+
"against the baseline and the severity floor",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"code": "preview_render_failed",
|
|
111
|
+
"trigger": "state.ui_review.preview.render_ok is False — the "
|
|
112
|
+
"stack-specific review skill tried to render the changed "
|
|
113
|
+
"components and the headless browser reported an error",
|
|
114
|
+
"resolution": "user picks Retry (re-run the review skill so it "
|
|
115
|
+
"renders again), Skip (write `state.ui_review.preview.skipped = "
|
|
116
|
+
"true` so the gate stops asking this run), or Abort",
|
|
117
|
+
},
|
|
118
|
+
)
|
|
119
|
+
"""Declared ambiguity surfaces for this step."""
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def run(state: DeliveryState) -> StepResult:
|
|
123
|
+
"""Apply the design-review gate to ``state.ui_review``."""
|
|
124
|
+
review = state.ui_review
|
|
125
|
+
if not _is_populated(review):
|
|
126
|
+
return _delegate_to_review_skill(state)
|
|
127
|
+
|
|
128
|
+
if "findings" not in review or not isinstance(review["findings"], list):
|
|
129
|
+
return _halt_findings_missing(state)
|
|
130
|
+
|
|
131
|
+
findings = review["findings"]
|
|
132
|
+
if not isinstance(review.get("review_clean"), bool):
|
|
133
|
+
return _halt_clean_missing(state, findings_count=len(findings))
|
|
134
|
+
|
|
135
|
+
a11y_halt = _apply_a11y_gate(state, review)
|
|
136
|
+
if a11y_halt is not None:
|
|
137
|
+
return a11y_halt
|
|
138
|
+
|
|
139
|
+
preview_halt = _apply_preview_gate(state, review)
|
|
140
|
+
if preview_halt is not None:
|
|
141
|
+
return preview_halt
|
|
142
|
+
|
|
143
|
+
return StepResult(outcome=Outcome.SUCCESS)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _is_populated(review: Any) -> bool:
|
|
147
|
+
"""True when ``review`` is a dict with at least one own key.
|
|
148
|
+
|
|
149
|
+
Non-dict and empty-dict shapes are treated as "skill has not run"
|
|
150
|
+
so the first-pass directive fires.
|
|
151
|
+
"""
|
|
152
|
+
return isinstance(review, dict) and bool(review)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _resolve_directive(state: DeliveryState) -> str:
|
|
156
|
+
"""Pick the agent directive for the project's frontend stack."""
|
|
157
|
+
stack = getattr(state, "stack", None) or {}
|
|
158
|
+
if isinstance(stack, dict):
|
|
159
|
+
frontend = stack.get("frontend")
|
|
160
|
+
if isinstance(frontend, str) and frontend in STACK_DIRECTIVES:
|
|
161
|
+
return STACK_DIRECTIVES[frontend]
|
|
162
|
+
return DEFAULT_DIRECTIVE
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _stack_label(state: DeliveryState) -> str:
|
|
166
|
+
"""Return the frontend stack label, defaulting to ``plain``."""
|
|
167
|
+
stack = getattr(state, "stack", None) or {}
|
|
168
|
+
if isinstance(stack, dict):
|
|
169
|
+
frontend = stack.get("frontend")
|
|
170
|
+
if isinstance(frontend, str) and frontend:
|
|
171
|
+
return frontend
|
|
172
|
+
return "plain"
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _delegate_to_review_skill(state: DeliveryState) -> StepResult:
|
|
176
|
+
"""First-pass halt — emit the stack-specific review directive."""
|
|
177
|
+
directive = _resolve_directive(state)
|
|
178
|
+
stack_label = _stack_label(state)
|
|
179
|
+
return StepResult(
|
|
180
|
+
outcome=Outcome.BLOCKED,
|
|
181
|
+
questions=[
|
|
182
|
+
agent_directive(directive),
|
|
183
|
+
f"> Stack: `{stack_label}`. Reviewing rendered components "
|
|
184
|
+
"against the locked design brief.",
|
|
185
|
+
"> The review pass compares `state.ticket.ui_apply.rendered` "
|
|
186
|
+
"against `state.ui_design` (microcopy, states, a11y, layout) "
|
|
187
|
+
"and produces a structured `findings` list.",
|
|
188
|
+
"> 1. Continue \u2014 run the review and write "
|
|
189
|
+
"`{findings: [...], review_clean: bool}` into "
|
|
190
|
+
"`state.ui_review`",
|
|
191
|
+
"> 2. Abort \u2014 drop this UI request",
|
|
192
|
+
],
|
|
193
|
+
message=(
|
|
194
|
+
f"UI review pending; delegating to `{directive}` for "
|
|
195
|
+
f"stack `{stack_label}`."
|
|
196
|
+
),
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _halt_findings_missing(state: DeliveryState) -> StepResult:
|
|
201
|
+
"""BLOCKED halt — envelope present but ``findings`` slot is unset."""
|
|
202
|
+
directive = _resolve_directive(state)
|
|
203
|
+
return StepResult(
|
|
204
|
+
outcome=Outcome.BLOCKED,
|
|
205
|
+
questions=[
|
|
206
|
+
agent_directive(directive),
|
|
207
|
+
"> Review envelope is partial: `findings` list is missing.",
|
|
208
|
+
"> Re-run the review skill so `state.ui_review.findings` "
|
|
209
|
+
"is a list (empty when nothing is wrong).",
|
|
210
|
+
],
|
|
211
|
+
message="UI review envelope incomplete; `findings` missing.",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _halt_clean_missing(
|
|
216
|
+
state: DeliveryState,
|
|
217
|
+
*,
|
|
218
|
+
findings_count: int,
|
|
219
|
+
) -> StepResult:
|
|
220
|
+
"""BLOCKED halt — ``review_clean`` is missing or not a bool."""
|
|
221
|
+
directive = _resolve_directive(state)
|
|
222
|
+
return StepResult(
|
|
223
|
+
outcome=Outcome.BLOCKED,
|
|
224
|
+
questions=[
|
|
225
|
+
agent_directive(directive),
|
|
226
|
+
"> Review envelope is incomplete: `review_clean` is missing "
|
|
227
|
+
"or not a boolean.",
|
|
228
|
+
f"> Findings count: {findings_count}. Set "
|
|
229
|
+
"`state.ui_review.review_clean` to `True` (no further "
|
|
230
|
+
"polish needed) or `False` (polish loop should run).",
|
|
231
|
+
],
|
|
232
|
+
message=(
|
|
233
|
+
"UI review envelope incomplete; `review_clean` must be a bool."
|
|
234
|
+
),
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def _apply_a11y_gate(
|
|
239
|
+
state: DeliveryState,
|
|
240
|
+
review: dict[str, Any],
|
|
241
|
+
) -> StepResult | None:
|
|
242
|
+
"""R4 Phase 1: enforce a11y gate after the basic shape gates pass.
|
|
243
|
+
|
|
244
|
+
The gate is **opt-in via the audit baseline**: if
|
|
245
|
+
``state.ui_audit.a11y_baseline`` is present (a list, possibly empty)
|
|
246
|
+
the audit declared this UI surface to be a11y-tracked. The review
|
|
247
|
+
skill must then populate ``state.ui_review.a11y.violations``;
|
|
248
|
+
missing → ``review_a11y_pending`` halt. Pre-R4 envelopes (no
|
|
249
|
+
baseline) bypass the gate so existing fixtures keep working.
|
|
250
|
+
|
|
251
|
+
When the envelope is present the gate filters violations against
|
|
252
|
+
the baseline (pre-existing issues are ignored), against the
|
|
253
|
+
accepted list (user-acknowledged issues from a previous polish
|
|
254
|
+
halt), and against the severity floor (default ``moderate``). Any
|
|
255
|
+
*actionable* leftover violations are synthesised as
|
|
256
|
+
``a11y_violation`` findings on ``review.findings`` and
|
|
257
|
+
``review_clean`` is forced to ``False`` so polish picks them up.
|
|
258
|
+
|
|
259
|
+
Returns ``None`` to advance the dispatcher, or a ``BLOCKED``
|
|
260
|
+
``StepResult`` for the pending halt.
|
|
261
|
+
|
|
262
|
+
Side effects on ``review`` are deduplicated by ``(kind, rule)`` so
|
|
263
|
+
a re-entry round-trips without growing the findings list.
|
|
264
|
+
"""
|
|
265
|
+
audit = getattr(state, "ui_audit", None)
|
|
266
|
+
has_baseline = isinstance(audit, dict) and "a11y_baseline" in audit
|
|
267
|
+
a11y = review.get("a11y")
|
|
268
|
+
|
|
269
|
+
if a11y is None:
|
|
270
|
+
if has_baseline:
|
|
271
|
+
return _halt_a11y_pending(state)
|
|
272
|
+
return None
|
|
273
|
+
|
|
274
|
+
violations = a11y.get("violations") or []
|
|
275
|
+
baseline = audit["a11y_baseline"] if has_baseline else []
|
|
276
|
+
accepted = a11y.get("accepted_violations") or []
|
|
277
|
+
floor = a11y.get("severity_floor") or DEFAULT_SEVERITY_FLOOR
|
|
278
|
+
|
|
279
|
+
new_violations = _filter_known(violations, baseline)
|
|
280
|
+
new_violations = _filter_known(new_violations, accepted)
|
|
281
|
+
actionable = [v for v in new_violations if _at_or_above_floor(v, floor)]
|
|
282
|
+
|
|
283
|
+
if not actionable:
|
|
284
|
+
return None
|
|
285
|
+
|
|
286
|
+
_synthesize_a11y_findings(review["findings"], actionable)
|
|
287
|
+
review["review_clean"] = False
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _filter_known(
|
|
292
|
+
violations: list[Any],
|
|
293
|
+
known: list[Any],
|
|
294
|
+
) -> list[Any]:
|
|
295
|
+
"""Drop violations whose ``(rule, selector)`` matches ``known``.
|
|
296
|
+
|
|
297
|
+
Used for both the baseline filter (pre-existing violations stay
|
|
298
|
+
ignored) and the accepted filter (user-acknowledged violations
|
|
299
|
+
after a ``polish_a11y_blocking`` halt). Non-dict entries in either
|
|
300
|
+
list are skipped — schema only enforces list shape.
|
|
301
|
+
"""
|
|
302
|
+
if not known:
|
|
303
|
+
return list(violations)
|
|
304
|
+
keys: set[tuple[Any, Any]] = set()
|
|
305
|
+
for entry in known:
|
|
306
|
+
if isinstance(entry, dict):
|
|
307
|
+
keys.add((entry.get("rule"), entry.get("selector")))
|
|
308
|
+
if not keys:
|
|
309
|
+
return list(violations)
|
|
310
|
+
return [
|
|
311
|
+
v for v in violations
|
|
312
|
+
if not (
|
|
313
|
+
isinstance(v, dict)
|
|
314
|
+
and (v.get("rule"), v.get("selector")) in keys
|
|
315
|
+
)
|
|
316
|
+
]
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _at_or_above_floor(violation: Any, floor: str) -> bool:
|
|
320
|
+
"""``True`` when ``violation.severity`` is at or above ``floor``.
|
|
321
|
+
|
|
322
|
+
Unknown severities default to ``moderate`` rather than dropping
|
|
323
|
+
the violation — a malformed envelope must not silently weaken the
|
|
324
|
+
gate. The floor itself is schema-validated, so a bogus floor never
|
|
325
|
+
reaches this helper.
|
|
326
|
+
"""
|
|
327
|
+
if not isinstance(violation, dict):
|
|
328
|
+
return False
|
|
329
|
+
severity = violation.get("severity")
|
|
330
|
+
sev_rank = SEVERITY_ORDER.get(
|
|
331
|
+
severity if isinstance(severity, str) else "",
|
|
332
|
+
SEVERITY_ORDER[DEFAULT_SEVERITY_FLOOR],
|
|
333
|
+
)
|
|
334
|
+
floor_rank = SEVERITY_ORDER.get(floor, SEVERITY_ORDER[DEFAULT_SEVERITY_FLOOR])
|
|
335
|
+
return sev_rank >= floor_rank
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _synthesize_a11y_findings(
|
|
339
|
+
findings: list[Any],
|
|
340
|
+
actionable: list[Any],
|
|
341
|
+
) -> None:
|
|
342
|
+
"""Append ``a11y_violation`` findings, deduped by ``(rule, selector)``.
|
|
343
|
+
|
|
344
|
+
Polish reads these as ordinary findings; the ``kind`` discriminator
|
|
345
|
+
lets Phase 2's ``polish_a11y_blocking`` gate isolate the a11y
|
|
346
|
+
subset at the polish ceiling.
|
|
347
|
+
"""
|
|
348
|
+
existing: set[tuple[Any, Any]] = {
|
|
349
|
+
(f.get("rule"), f.get("selector"))
|
|
350
|
+
for f in findings
|
|
351
|
+
if isinstance(f, dict) and f.get("kind") == "a11y_violation"
|
|
352
|
+
}
|
|
353
|
+
for v in actionable:
|
|
354
|
+
if not isinstance(v, dict):
|
|
355
|
+
continue
|
|
356
|
+
key = (v.get("rule"), v.get("selector"))
|
|
357
|
+
if key in existing:
|
|
358
|
+
continue
|
|
359
|
+
findings.append({
|
|
360
|
+
"kind": "a11y_violation",
|
|
361
|
+
"rule": v.get("rule"),
|
|
362
|
+
"selector": v.get("selector"),
|
|
363
|
+
"severity": v.get("severity"),
|
|
364
|
+
})
|
|
365
|
+
existing.add(key)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _halt_a11y_pending(state: DeliveryState) -> StepResult:
|
|
369
|
+
"""BLOCKED halt — audit declared a baseline but review has no a11y."""
|
|
370
|
+
directive = _resolve_directive(state)
|
|
371
|
+
return StepResult(
|
|
372
|
+
outcome=Outcome.BLOCKED,
|
|
373
|
+
questions=[
|
|
374
|
+
agent_directive(directive),
|
|
375
|
+
"> Review envelope is incomplete: the audit declared an "
|
|
376
|
+
"`a11y_baseline` but `state.ui_review.a11y` is missing.",
|
|
377
|
+
"> Re-run the review skill so it captures axe-core (or "
|
|
378
|
+
"equivalent) findings into "
|
|
379
|
+
"`state.ui_review.a11y.violations`. The gate filters "
|
|
380
|
+
"against the baseline and the severity floor "
|
|
381
|
+
f"(default `{DEFAULT_SEVERITY_FLOOR}`).",
|
|
382
|
+
],
|
|
383
|
+
message=(
|
|
384
|
+
"UI review envelope incomplete; `a11y` envelope missing "
|
|
385
|
+
"(audit declared a baseline)."
|
|
386
|
+
),
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _apply_preview_gate(
|
|
391
|
+
state: DeliveryState,
|
|
392
|
+
review: dict[str, Any],
|
|
393
|
+
) -> StepResult | None:
|
|
394
|
+
"""R4 Phase 3: validate the visual-preview envelope written by the skill.
|
|
395
|
+
|
|
396
|
+
Contract: the **engine never renders**. Stack-specific review skills
|
|
397
|
+
(Playwright + axe-core for ``react-shadcn``, equivalent for
|
|
398
|
+
``blade-livewire-flux``) produce ``state.ui_review.preview`` with
|
|
399
|
+
``render_ok`` plus optional ``screenshot_path``, ``dom_dump_path``,
|
|
400
|
+
and ``error``. The gate inspects shape only.
|
|
401
|
+
|
|
402
|
+
Branches:
|
|
403
|
+
|
|
404
|
+
- ``preview`` missing or not a dict → no-op (opt-in; pre-R4 envelopes
|
|
405
|
+
and stacks that do not produce previews flow through silently).
|
|
406
|
+
- ``preview.skipped`` truthy → no-op (idempotent re-entry after the
|
|
407
|
+
user picked the Skip option on a previous halt).
|
|
408
|
+
- ``preview.render_ok`` missing → no-op (envelope still in progress;
|
|
409
|
+
schema validates type when present, content gates wait for an
|
|
410
|
+
explicit signal).
|
|
411
|
+
- ``preview.render_ok is False`` → ``preview_render_failed`` halt
|
|
412
|
+
with Retry / Skip / Abort options.
|
|
413
|
+
- ``preview.render_ok is True`` → no-op; ``report.run`` will surface
|
|
414
|
+
``screenshot_path`` / ``dom_dump_path`` as a delivery artifact.
|
|
415
|
+
|
|
416
|
+
Trivial path (``directive_set == "ui-trivial"``) never reaches this
|
|
417
|
+
handler — the dispatcher routes ``review`` to ``_skipped.run`` for
|
|
418
|
+
that set, so the preview envelope is bypassed by construction.
|
|
419
|
+
"""
|
|
420
|
+
preview = review.get("preview")
|
|
421
|
+
if not isinstance(preview, dict):
|
|
422
|
+
return None
|
|
423
|
+
if preview.get("skipped"):
|
|
424
|
+
return None
|
|
425
|
+
if "render_ok" not in preview:
|
|
426
|
+
return None
|
|
427
|
+
if preview["render_ok"] is False:
|
|
428
|
+
return _halt_preview_failed(state, preview)
|
|
429
|
+
return None
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def _halt_preview_failed(
|
|
433
|
+
state: DeliveryState,
|
|
434
|
+
preview: dict[str, Any],
|
|
435
|
+
) -> StepResult:
|
|
436
|
+
"""BLOCKED halt — render reported failure; user picks the next step."""
|
|
437
|
+
directive = _resolve_directive(state)
|
|
438
|
+
error = preview.get("error")
|
|
439
|
+
error_line = (
|
|
440
|
+
f"> Render error: `{error}`."
|
|
441
|
+
if isinstance(error, str) and error
|
|
442
|
+
else "> Render error: `(none reported)`."
|
|
443
|
+
)
|
|
444
|
+
return StepResult(
|
|
445
|
+
outcome=Outcome.BLOCKED,
|
|
446
|
+
questions=[
|
|
447
|
+
agent_directive(directive),
|
|
448
|
+
"> Visual preview failed: "
|
|
449
|
+
"`state.ui_review.preview.render_ok` is `False`.",
|
|
450
|
+
error_line,
|
|
451
|
+
"> 1. Retry — re-run the review skill so it renders again "
|
|
452
|
+
"and writes a fresh `preview` envelope",
|
|
453
|
+
"> 2. Skip — set `state.ui_review.preview.skipped = true` "
|
|
454
|
+
"so this run ships without a screenshot artifact",
|
|
455
|
+
"> 3. Abort — drop this UI request",
|
|
456
|
+
],
|
|
457
|
+
message="UI preview render failed; awaiting user decision.",
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
__all__ = [
|
|
462
|
+
"AMBIGUITIES",
|
|
463
|
+
"DEFAULT_DIRECTIVE",
|
|
464
|
+
"DEFAULT_SEVERITY_FLOOR",
|
|
465
|
+
"SEVERITY_ORDER",
|
|
466
|
+
"STACK_DIRECTIVES",
|
|
467
|
+
"run",
|
|
468
|
+
]
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""UI-trivial directive set — single-file ≤5-line micro-edit path.
|
|
2
|
+
|
|
3
|
+
Phase 2 Step 6 of ``agents/roadmaps/road-to-product-ui-track.md``: the
|
|
4
|
+
short-circuit path for changes that provably cannot need the audit /
|
|
5
|
+
design / review / polish loop. The dispatcher routes here when Phase
|
|
6
|
+
1's intent classifier landed ``ui-trivial`` (color tweak, copy change,
|
|
7
|
+
single-class swap, one-prop adjustment).
|
|
8
|
+
|
|
9
|
+
The eight-step shape mirrors :mod:`work_engine.directives.backend` /
|
|
10
|
+
:mod:`work_engine.directives.ui` — eight slots, fixed order, no
|
|
11
|
+
branching. The trivial path fills them as follows:
|
|
12
|
+
|
|
13
|
+
- ``refine`` → :mod:`.refine` — confirm intent gate.
|
|
14
|
+
- ``memory`` → :mod:`._skipped` — bypassed.
|
|
15
|
+
- ``analyze`` → :mod:`._skipped` — bypassed.
|
|
16
|
+
- ``plan`` → :mod:`._skipped` — bypassed.
|
|
17
|
+
- ``implement`` → :mod:`.apply` — hard preconditions; reclassify
|
|
18
|
+
to ``ui-improve`` (full audit gate) when violated.
|
|
19
|
+
- ``test`` → :mod:`.test` — smoke-test delegate.
|
|
20
|
+
- ``verify`` → :mod:`._skipped` — bypassed.
|
|
21
|
+
- ``report`` → :mod:`.report` — one-line delivery summary.
|
|
22
|
+
|
|
23
|
+
The directory uses an underscore (``ui_trivial``) because Python
|
|
24
|
+
packages cannot contain hyphens. The schema carries the external
|
|
25
|
+
hyphenated name ``"ui-trivial"``; the dispatcher's loader is the
|
|
26
|
+
single place that translates between them.
|
|
27
|
+
"""
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from collections.abc import Mapping
|
|
31
|
+
|
|
32
|
+
from ...delivery_state import Step
|
|
33
|
+
from . import _skipped, apply, refine, report, test
|
|
34
|
+
|
|
35
|
+
DIRECTIVE_SET_NAME = "ui-trivial"
|
|
36
|
+
"""External name carried in ``state.directive_set`` for this set.
|
|
37
|
+
|
|
38
|
+
Note the hyphen \u2014 this is the schema/wire form, not the Python
|
|
39
|
+
module name. The module name (``ui_trivial``) is an implementation
|
|
40
|
+
detail of the loader.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
ROADMAP = "agents/roadmaps/road-to-product-ui-track.md"
|
|
44
|
+
"""Roadmap that defines this directive bundle (Phase 2 Step 6)."""
|
|
45
|
+
|
|
46
|
+
SUPPORTED_KINDS: tuple[str, ...] = ("ticket", "prompt", "diff", "file")
|
|
47
|
+
"""Input kinds this directive set knows how to handle.
|
|
48
|
+
|
|
49
|
+
Phase 1's intent classifier reaches ``ui-trivial`` from any of the
|
|
50
|
+
four input kinds; the trivial set keeps the same tuple so input
|
|
51
|
+
routing stays unchanged once the intent label has landed.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _build_step_map() -> dict[str, Step]:
|
|
56
|
+
"""Wire the eight-step dispatcher slots for the trivial set.
|
|
57
|
+
|
|
58
|
+
``refine`` validates the intent gate; ``implement``, ``test``,
|
|
59
|
+
and ``report`` carry the trivial-path behavior; the four bypassed
|
|
60
|
+
slots share :mod:`._skipped` so the dispatcher's completeness
|
|
61
|
+
check is satisfied without inventing per-slot stubs. The mapping
|
|
62
|
+
is rebuilt per call (cheap; the dispatcher invokes
|
|
63
|
+
:func:`get_steps` once per run).
|
|
64
|
+
"""
|
|
65
|
+
skipped = _skipped.run
|
|
66
|
+
return {
|
|
67
|
+
"refine": refine.run,
|
|
68
|
+
"memory": skipped,
|
|
69
|
+
"analyze": skipped,
|
|
70
|
+
"plan": skipped,
|
|
71
|
+
"implement": apply.run,
|
|
72
|
+
"test": test.run,
|
|
73
|
+
"verify": skipped,
|
|
74
|
+
"report": report.run,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_steps() -> Mapping[str, Step]:
|
|
79
|
+
"""Return the ``{step_name: handler}`` mapping the dispatcher walks.
|
|
80
|
+
|
|
81
|
+
Mirrors :func:`work_engine.directives.backend.get_steps`. ``refine``,
|
|
82
|
+
``implement``, ``test``, and ``report`` carry trivial-path behavior;
|
|
83
|
+
the four bypassed slots delegate to :mod:`._skipped`.
|
|
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
|
+
Mirrors :func:`work_engine.directives.backend.all_ambiguities`.
|
|
92
|
+
The four bypassed slots re-export :data:`_skipped.AMBIGUITIES`
|
|
93
|
+
(an empty tuple) so doc generators see a uniform shape across all
|
|
94
|
+
eight steps.
|
|
95
|
+
"""
|
|
96
|
+
skipped = _skipped.AMBIGUITIES
|
|
97
|
+
return {
|
|
98
|
+
"refine": refine.AMBIGUITIES,
|
|
99
|
+
"memory": skipped,
|
|
100
|
+
"analyze": skipped,
|
|
101
|
+
"plan": skipped,
|
|
102
|
+
"implement": apply.AMBIGUITIES,
|
|
103
|
+
"test": test.AMBIGUITIES,
|
|
104
|
+
"verify": skipped,
|
|
105
|
+
"report": report.AMBIGUITIES,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
__all__ = [
|
|
110
|
+
"DIRECTIVE_SET_NAME",
|
|
111
|
+
"ROADMAP",
|
|
112
|
+
"SUPPORTED_KINDS",
|
|
113
|
+
"all_ambiguities",
|
|
114
|
+
"apply",
|
|
115
|
+
"get_steps",
|
|
116
|
+
"refine",
|
|
117
|
+
"report",
|
|
118
|
+
"test",
|
|
119
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Pass-through handler for slots the trivial path skips.
|
|
2
|
+
|
|
3
|
+
Phase 2 Step 6 of ``agents/roadmaps/road-to-product-ui-track.md``: the
|
|
4
|
+
``ui-trivial`` directive set short-circuits the audit / design / review
|
|
5
|
+
/ polish loop. Per the roadmap (Phase 1 Step 3, Phase 2 Step 6) the
|
|
6
|
+
trivial path "skips audit + design + review; runs apply + smoke-test
|
|
7
|
+
only; emits short delivery report".
|
|
8
|
+
|
|
9
|
+
The dispatcher's ``STEP_ORDER`` is fixed (eight slots, no branching),
|
|
10
|
+
so the trivial set fills the unused slots — ``memory``, ``analyze``,
|
|
11
|
+
``plan``, ``verify`` — with this no-op handler. It returns ``SUCCESS``
|
|
12
|
+
without touching state, mutates nothing, and declares zero
|
|
13
|
+
ambiguities. The audit gate is **not** weakened: trivial bypass is
|
|
14
|
+
gated upstream by ``apply``'s hard preconditions, which reclassify
|
|
15
|
+
to ``ui-improve`` (and the full audit gate) when violated.
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from ...delivery_state import DeliveryState, Outcome, StepResult
|
|
20
|
+
|
|
21
|
+
AMBIGUITIES: tuple[dict[str, str], ...] = ()
|
|
22
|
+
"""No ambiguities — the slot is unconditionally skipped on the trivial path."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def run(state: DeliveryState) -> StepResult:
|
|
26
|
+
"""Return ``SUCCESS`` without touching ``state``.
|
|
27
|
+
|
|
28
|
+
Used as a shared handler for the slots that the trivial path
|
|
29
|
+
intentionally bypasses. Keeping the slot wired (rather than
|
|
30
|
+
raising ``NotImplementedError``) preserves the dispatcher's
|
|
31
|
+
completeness-check invariant: every slot in :data:`STEP_ORDER`
|
|
32
|
+
has a callable handler, every directive set has a uniform shape.
|
|
33
|
+
"""
|
|
34
|
+
return StepResult(outcome=Outcome.SUCCESS)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__all__ = ["AMBIGUITIES", "run"]
|