aw-spec-cli 0.1.14 → 0.1.16

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 (32) hide show
  1. package/assets/.appweaver/STACK-PROFILES.md +1 -1
  2. package/assets/.appweaver/orchestrator/modules/spec-state-gate.orch.yaml +87 -0
  3. package/assets/.appweaver/orchestrator/orchestrator.py +5 -0
  4. package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +10 -3
  5. package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +38 -11
  6. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +2 -1
  7. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +672 -124
  8. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +700 -72
  9. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +531 -135
  10. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +573 -145
  11. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +78 -14
  12. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +600 -0
  13. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +721 -0
  14. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +3 -3
  15. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +119 -32
  16. package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +1147 -0
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +1121 -0
  18. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +2564 -38
  19. package/assets/.appweaver/scripts/bash/aw-xref.sh +2 -1
  20. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +2458 -22
  21. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +2 -1
  22. package/assets/.appweaver/scripts/python/aw-payload-parity.py +158 -0
  23. package/assets/.appweaver/scripts/python/aw-xref.py +431 -48
  24. package/assets/.appweaver/templates/design-template.md +88 -54
  25. package/assets/.appweaver/templates/spec-template.md +12 -10
  26. package/assets/.appweaver/templates/story-template.md +83 -12
  27. package/assets/.appweaver/templates/ui-design-template.md +71 -39
  28. package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +19 -7
  29. package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +35 -26
  30. package/package.json +1 -1
  31. package/assets/.appweaver/orchestrator/workflows/aw-spec-design-ui.orch.yaml +0 -386
  32. package/assets/.appweaver/orchestrator/workflows/aw-spec-design.orch.yaml +0 -382
@@ -0,0 +1,600 @@
1
+ # =============================================================================
2
+ # aw-spec-plan-ui (orchestrator) — the spec-anchored Plan phase (frontend lane)
3
+ #
4
+ # Authors and MAINTAINS the frontend lane's single high-level plan —
5
+ # specs/<Aggregate>/frontend-plan.md — the experience twin of backend-plan.md and
6
+ # the MEDIATOR between the spec and the components implement-ui generates (formerly
7
+ # design.ui.md; the scaffold migrates a legacy design.ui.md by renaming it). Its
8
+ # sections are the experience, not the four clean-architecture layers. See
9
+ # .appweaver/DESIGN-FORMAT.md ("Frontend lane variant") +
10
+ # .appweaver/templates/ui-design-template.md.
11
+ #
12
+ # WHY A SEPARATE COMMAND, NOT A UI BRANCH INSIDE `plan`. The two lanes are governed
13
+ # by different rule sets (tech-rule-bindings gives each lane its own set for zero
14
+ # mechanism change); a UI branch inside plan would put the lane's structure under
15
+ # the service lane's rules and hand one phase the whole rule surface of both lanes.
16
+ #
17
+ # THREE MODES, resolved deterministically (self-detecting on the doc's existence):
18
+ # • bootstrap_gf (default) — no frontend-plan.md yet: author the master from the
19
+ # whole spec, shipped experience template (§0–§9).
20
+ # • bootstrap_bf — same, but onboard installed the code-derived ui-design-template
21
+ # override: author with the lane's AS-BUILT composition axes instead.
22
+ # • update — frontend-plan.md exists: conform it to what the spec's Change History
23
+ # ledger says moved SINCE THIS PLAN WAS LAST DERIVED, and nothing else. This
24
+ # replaces the former bootstrap-only refusal that routed to ⌛ delta-design-ui.
25
+ #
26
+ # TRACKING is identical to plan's: plan-check computes the state (fresh | untracked |
27
+ # clean | behind | edited | edited_behind) and the moved ids; plan-stamp is the only
28
+ # writer of the **Version** header, the doc's own Change History row and the body
29
+ # hash in .aw-plan-track.frontend-plan.json — its OWN track file, so a backend plan
30
+ # stamped at the same moment never contends with it. Hand edits are refused unless accept_manual:true
31
+ # folds them in. The model writes NO version anywhere.
32
+ #
33
+ # WHERE IT SITS. specify/sync-spec (⌛ sync-spec is not built yet; the hand route is
34
+ # .appweaver/SPEC-EDIT-PROTOCOL.md) move the spec; the developer then runs plan (the
35
+ # service lane) and plan-ui, which conform each lane's design to what moved. FE story
36
+ # designs READ this document for the ids they carry and NEVER write back — nothing
37
+ # merges into it, so it keeps exactly one writer. It is the FEATURE-LEVEL
38
+ # ARCHITECTURAL CONFIRMATION for the experience, with the concrete component-level
39
+ # work left to the per-story passes.
40
+ #
41
+ # IT READS THE CODE — ITS OWN LANE'S. concept-coverage resolves the feature's OKF concept
42
+ # to the `ui_source_files` realising its UI today (the service lane's `source_files` is a
43
+ # different key and a different tree) and the authoring steps declare `tools: Read`, so a plan over
44
+ # an existing frontend ADJUSTS to the composition, naming and state handling actually
45
+ # there. The code is the what-exists authority; the spec stays the only authority on
46
+ # required behaviour. source_count 0 = greenfield, and the phase never blocks on it.
47
+ #
48
+ # IT CONSUMES THE INTERFACE LOG — it never writes one. specs/<Aggregate>/interfaces.md
49
+ # is the service lane's published surface, published by /appweaver plan and APPEND-ONLY:
50
+ # §5 Contract Binding binds to the LATEST entry per interface (a `supersedes:` entry
51
+ # replaces the one it names), cited BY IF- id, never restated. A `proposed` entry is
52
+ # the designed contract and is bindable. What the experience needs and the surface
53
+ # lacks is a CONTRACT DELTA raised back to the service lane, never a client workaround.
54
+ #
55
+ # Launch: /appweaver plan-ui [guidance] (accept_manual:true folds hand edits in)
56
+ # Prereq: /appweaver specify; /appweaver plan (so interfaces.md exists to bind
57
+ # against); a frontend lane declared via /appweaver select-stack
58
+ # =============================================================================
59
+
60
+ name: aw-spec-plan-ui
61
+ description: "Author or update the frontend lane's high-level plan — specs/<Feature>/frontend-plan.md — from the spec: the experience twin of backend-plan.md, mediating between spec.md and the components implement-ui generates. Self-detects: no plan yet ⇒ bootstrap the master (shipped experience template, or the code-derived structure when onboard installed the ui-design-template override); plan exists ⇒ UPDATE — conform exactly the sections serving the ids the spec's Change History moved since this plan's derivation stamp, preserving the Assumptions/Workarounds/Change History ledgers. Realises the spec's UX- Experience Standards, which backend-plan.md excludes by contract; the service lane enters only at §5 Contract Binding, CONSUMING specs/<Feature>/interfaces.md — the append-only interface log /appweaver plan publishes, latest entry per id — cited by IF- id and never written to by this lane. Tracking is script-owned (plan-check / plan-stamp); hand edits are refused unless accept_manual folds them in. Technology-agnostic: the stack binds at implement-ui."
62
+ version: 2.1.0
63
+ agent_agnostic: true
64
+ dialect: aw-spec.orchestrator/v1
65
+
66
+ inputs:
67
+ user_input:
68
+ description: Optional plan guidance (flow preferences, screen decomposition hints, constraints).
69
+ required: false
70
+ bind: USER_INPUT
71
+ accept_manual:
72
+ description: "true ⇒ a hand-edited plan (tracking state edited/edited_behind) is folded in instead of refused: the update keeps the manual edits' intent, reconciles them with the spec, and the stamp re-baselines."
73
+ required: false
74
+ bind: ACCEPT_MANUAL
75
+
76
+ scripts:
77
+ actions:
78
+ sh: .appweaver/scripts/bash/aw-spec-actions.sh
79
+ ps1: .appweaver/scripts/powershell/aw-spec-actions.ps1
80
+ xref:
81
+ sh: .appweaver/scripts/bash/aw-xref.sh
82
+ ps1: .appweaver/scripts/powershell/aw-xref.ps1
83
+
84
+ # Sticky standards seed = the UNIVERSAL always-on rule only (ai-rules). The lane's
85
+ # FRONTEND rule files are CONFIGURATION, loaded per-command from
86
+ # guardrails/projects/<project>/tech-rule-bindings.md under `## /aw-spec-plan-ui` by the
87
+ # tech-select module. Per the rule-partition this phase loads NO domain rule file —
88
+ # the domain reaches it as spec ids, exactly as it reaches design.md. The lane's
89
+ # ratified base arrives via constitution.ui.md (referred to directly, as-is — NOT
90
+ # mapped). (missing files skipped at assembly time)
91
+ standards:
92
+ - { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
93
+
94
+ vars:
95
+ source_csv: "" # concept-coverage — the UI files realising this feature today
96
+ source_count: "0" # 0 ⇒ greenfield: no frontend code for this feature yet
97
+ okf_concept: "" # the feature's OKF concept doc
98
+ ui_plan_mode: "bootstrap_gf" # resolved below → bootstrap_gf | bootstrap_bf | update
99
+ ui_plan_created: "false" # scaffold.created — true ⇒ the doc was absent ⇒ bootstrap
100
+ ui_plan_migrated: "false" # scaffold renamed a legacy design.ui.md into frontend-plan.md this run
101
+ track_state: "" # plan-check → fresh | untracked | clean | behind | edited | edited_behind
102
+ derived_version: ""
103
+ current_version: ""
104
+ changed_csv: ""
105
+ changed_summary: ""
106
+ manual_ok: "true" # false ⇒ hand edits present and accept_manual not given
107
+ plan_stale: "true" # false ⇒ update mode with nothing to do (clean) — friendly stop
108
+ stamp_ok: "false"
109
+ stamp_error: ""
110
+
111
+ workflow:
112
+
113
+ # 0. FRONTEND rule files → sticky standards (configuration, not hardcoded).
114
+ # No blocking gate: a project that has not mapped the heading soft-continues,
115
+ # because constitution.ui.md still carries the lane's ratified base.
116
+ - id: tech_select
117
+ action: include
118
+ module: .appweaver/orchestrator/modules/tech-select.orch.yaml
119
+ with: { command: aw-spec-plan-ui }
120
+
121
+ # 1. The lane must EXIST. This phase writes a document, not code, so it asks the
122
+ # declaration whether there is a frontend lane at all — not the lane-gate module,
123
+ # whose on-disk check would refuse a greenfield project that has declared `web/`
124
+ # but not scaffolded it yet. A UI design is authored before the UI exists; that is
125
+ # the point of designing.
126
+ - id: lane_resolve
127
+ action: execute_script
128
+ on_error: continue # never crash raw on an undeclared lane — the gate below explains it
129
+ script: { ref: actions }
130
+ subcommand: lane-resolve
131
+ outputs:
132
+ - { name: fe_declared, from: "$.frontend_declared" }
133
+ - { name: lane_id, from: "$.frontend_lane_id" }
134
+ - { name: lane_title, from: "$.frontend_title" }
135
+ - { name: lane_language, from: "$.frontend_language" }
136
+ - { name: lane_ecosystem, from: "$.frontend_ecosystem" }
137
+ - { name: lane_root, from: "$.frontend_source_path" }
138
+
139
+ - id: require_lane
140
+ action: verify_context
141
+ require_true: fe_declared
142
+ on_false: stop
143
+ on_false_message: "No frontend lane is declared in .appweaver/stacks.json, so there is no experience to plan. Run /appweaver select-stack and declare the UI stack plus its source path, then re-run plan-ui. A project that genuinely has no UI plans the service lane alone with /appweaver plan."
144
+
145
+ # 2. Resolve the current feature and require its spec — this phase's only domain input.
146
+ - id: feature_paths
147
+ action: execute_script
148
+ on_error: continue # never crash raw on 'no feature set' — the gate below explains it
149
+ script: { ref: actions }
150
+ subcommand: feature-paths
151
+ outputs:
152
+ - { name: feature_dir, from: "$.feature_dir" }
153
+ - { name: spec_file, from: "$.spec_file" }
154
+ - { name: spec_exists, from: "$.spec_exists" }
155
+ - { name: service_design, from: "$.design_file" } # legacy name — a repo not yet migrated
156
+ - { name: service_plan, from: "$.backend_plan_file" } # the service lane's plan (post-rename)
157
+
158
+ - id: require_feature
159
+ action: verify_context
160
+ require_non_empty: feature_dir
161
+ on_empty: stop
162
+ on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the aggregate, e.g. Order), then re-run plan-ui."
163
+
164
+ - id: verify_spec
165
+ action: verify_context
166
+ require_true: spec_exists
167
+ on_false: stop
168
+ on_false_message: "No spec.md for the current feature. Run /appweaver specify first — the spec is this phase's only domain input, and its Experience Standards (UX-) are what frontend-plan.md realises."
169
+
170
+ # 1b. The spec's own ledger tripwire — a plan derived from an unledgered spec
171
+ # stamps a baseline the ledger cannot name (same fence as plan's).
172
+ - id: spec_guard
173
+ action: include
174
+ module: .appweaver/orchestrator/modules/spec-state-gate.orch.yaml
175
+ with: { command: "/appweaver plan-ui" }
176
+
177
+ # 2. Scaffold frontend-plan.md (idempotent). --lane frontend resolves the pair
178
+ # ui-design-template → frontend-plan.md, and MIGRATES a legacy design.ui.md by
179
+ # renaming it (created=false, migrated=true) — one document, never two.
180
+ - id: ui_plan_scaffold
181
+ action: execute_script
182
+ script: { ref: actions }
183
+ subcommand: design-scaffold
184
+ args: ["--feature-dir", "{{feature_dir}}", "--lane", "frontend"]
185
+ outputs:
186
+ - { name: ui_plan_doc, from: "$.design_doc" }
187
+ - { name: ui_override, from: "$.override_active" } # true ⇒ onboard installed the code-derived template
188
+ - { name: ui_plan_created, from: "$.created" }
189
+ - { name: ui_plan_migrated, from: "$.migrated" }
190
+
191
+ # 4a. Resolve the authoring MODE. Ordered override rather than a branch — the engine
192
+ # has no compound conditions: state the greenfield default, let the code-derived
193
+ # override replace it.
194
+ - id: mode_brownfield
195
+ condition: "{{ui_override}} == true"
196
+ action: set
197
+ vars: { ui_plan_mode: "bootstrap_bf" }
198
+ - id: mode_update
199
+ condition: "{{ui_plan_created}} == false"
200
+ action: set
201
+ vars: { ui_plan_mode: "update" }
202
+
203
+ # 3. Tracking: what moved since this plan was derived, and whether the doc was
204
+ # hand-edited since its stamp. Same machinery and fences as plan's.
205
+ - id: plan_track
206
+ action: execute_script
207
+ script: { ref: actions }
208
+ subcommand: plan-check
209
+ args: ["--feature-dir", "{{feature_dir}}", "--doc", "{{ui_plan_doc}}"]
210
+ outputs:
211
+ - { name: track_state, from: "$.state" }
212
+ - { name: derived_version, from: "$.derived_version" }
213
+ - { name: current_version, from: "$.current_version" }
214
+ - { name: changed_csv, from: "$.changed_csv" }
215
+ - { name: changed_summary, from: "$.changed_summary" }
216
+ - id: manual_flag_edited
217
+ condition: "{{track_state}} == edited"
218
+ action: set
219
+ vars: { manual_ok: "false" }
220
+ - id: manual_flag_edited_behind
221
+ condition: "{{track_state}} == edited_behind"
222
+ action: set
223
+ vars: { manual_ok: "false" }
224
+ - id: manual_accept
225
+ condition: "{{ACCEPT_MANUAL}} == true"
226
+ action: set
227
+ vars: { manual_ok: "true" }
228
+ - id: manual_fence
229
+ action: verify_context
230
+ require_true: manual_ok
231
+ on_false: stop
232
+ on_false_message: "frontend-plan.md was hand-edited since its last stamp (tracking state: {{track_state}}). Re-run plan-ui with accept_manual:true to fold the edits in (kept, reconciled with the spec, recorded in the ledgers, re-baselined) — or restore the document to its stamped state."
233
+ - id: clean_flag
234
+ condition: "{{track_state}} == clean"
235
+ action: set
236
+ vars: { plan_stale: "false" }
237
+ - id: clean_stop
238
+ action: verify_context
239
+ require_true: plan_stale
240
+ on_false: stop
241
+ on_false_message: "frontend-plan.md is in step with spec {{current_version}} — nothing to update. If the PLAN is wrong and the spec is right, hand-edit frontend-plan.md and re-run /appweaver plan-ui with accept_manual: true — that is what tells this command to keep the edit's intent and reconcile it rather than treat it as drift; without the flag the manual fence stops the run after the edit is already on disk."
242
+
243
+ # 5. Pre-execution hooks.
244
+ # THE CODE — THIS LANE'S. The UI plan is an architectural confirmation too, so it must
245
+ # know what this feature's experience already looks like on disk. concept-COVERAGE, not
246
+ # concept-paths: a feature has ONE OKF concept holding both lanes' file lists under
247
+ # different keys, and concept-paths returns the SERVICE lane's `source_files`. Reading it
248
+ # here planned the frontend against the backend's tree — a feature with a built frontend
249
+ # and no backend was planned as greenfield, and one with both handed the UI author the
250
+ # service lane's files to adjust to. concept-coverage splits the two and this binds
251
+ # `ui_source_files`. A PATH LIST, not inlined content (E60). on_error: continue — a
252
+ # greenfield feature has no concept doc and must never be blocked by one;
253
+ # source_count 0 is the greenfield signal the tasks read.
254
+ - id: concept_paths
255
+ action: execute_script
256
+ on_error: continue
257
+ script: { ref: xref }
258
+ subcommand: concept-coverage
259
+ args: ["--feature-dir", "{{feature_dir}}"]
260
+ outputs:
261
+ - { name: source_csv, from: "$.frontend_csv" }
262
+ - { name: source_count, from: "$.frontend_count" }
263
+ - { name: okf_concept, from: "$.concept" }
264
+
265
+ - id: resolve_pre_hooks
266
+ action: execute_script
267
+ script: { ref: actions }
268
+ subcommand: resolve-hooks
269
+ args: ["--phase", "before_design_ui"]
270
+ outputs:
271
+ - { name: before_hooks, from: "$.hooks" }
272
+ - id: dispatch_pre_hooks
273
+ action: dispatch_hooks
274
+ hooks_from_variable: before_hooks
275
+
276
+ # ═══════════ GREENFIELD — the shipped experience template (§0–§9) ═══════════
277
+ # build_context REPLACES the per-request envelope; the sticky frontend standards
278
+ # stay prepended. The ENGINE writes ui_design_doc.
279
+ - id: ctx_ui_plan
280
+ condition: "{{ui_plan_mode}} == bootstrap_gf"
281
+ action: build_context
282
+ context:
283
+ - { path: "{{ui_plan_doc}}" } # the copied template = section structure
284
+ - { path: "{{spec_file}}" } # the source of truth (UX/US/FR/CN/DC/IC/SC)
285
+ - { path: .appweaver/DESIGN-FORMAT.md } # the design-doc contract + its frontend lane variant
286
+ - { path: .appweaver/memory/constitution.ui.md, optional: true } # the lane's ratified UX-* base — optional: a lane may be declared before it is ratified
287
+ - { path: "{{feature_dir}}/interfaces.md", optional: true } # THE CONTRACT — the service lane's published surface, latest entry per id wins
288
+ - { path: "{{okf_concept}}", optional: true } # the feature's OKF concept: use-case log + source map
289
+ - { path: "{{service_plan}}", optional: true } # §5 ONLY — the service's structure; its API section only CITES the log
290
+ - { path: "{{service_design}}", optional: true } # the same surface under its legacy name (unmigrated repo)
291
+ - id: gen_ui_plan
292
+ condition: "{{ui_plan_mode}} == bootstrap_gf"
293
+ action: agent_author
294
+ tools: Read # the current frontend code arrives as {{source_csv}} paths, not inlined
295
+ output_target: { type: file, path: "{{ui_plan_doc}}" }
296
+ task: |
297
+ Author the MASTER UI plan (the target frontend-plan.md in context) for this
298
+ feature's EXPERIENCE by filling out the template from the feature spec + the
299
+ loaded frontend standards. This is the frontend lane's MEDIATOR between the spec
300
+ and the components `implement-ui` will generate. Guidance: {{USER_INPUT}}
301
+ Rules:
302
+ - THE SPEC IS THE ONLY DOMAIN INPUT. You have the spec + frontend standards, and
303
+ NO domain rule files. The domain arrives as spec ids only — realise them, and
304
+ invent no domain rule the spec omits (an unavoidable new concept → §8 DA-n).
305
+ - THE CODE IS THE WHAT-EXISTS AUTHORITY, and it is a DIFFERENT authority from the
306
+ spec. {{source_count}} file(s) realise this feature's experience today; read the
307
+ ones that bear on what you are deciding and ADJUST this plan to the composition,
308
+ naming and state handling that are actually there rather than to an idealised
309
+ shape. An empty list is greenfield — design from a clean slate and say so. The
310
+ code NEVER overrides the spec on required behaviour and introduces no rule the
311
+ spec omits: code contradicting the spec is recorded as a DA-n and designed to
312
+ the SPEC. A deliberate divergence from the as-built is a WA-n with its trigger.
313
+ {{source_csv}}
314
+ - Keep the template's section order and headings: §0 Design Overview, §1
315
+ Navigation & Screen Structure, §2 Client State, §3 Component Structure, §4
316
+ Interaction & Flow, §5 Contract Binding, §6 Cross-Cutting, §7 Traceability,
317
+ §8 Assumptions, §9 Workarounds, Change History.
318
+ - REALISE THE SPEC'S EXPERIENCE CONTENT:
319
+ • Experience Standards (UX-) are THIS document's to realise — backend-plan.md
320
+ excludes them by contract, so a UX- with no home here is a DROPPED
321
+ requirement, not a tidy omission. Every UX- gets a §7 row.
322
+ • Experience Scope (actors · channels · device/browser/AT floors) bounds §1
323
+ and §6 — design for the floors the spec names and no more.
324
+ • Each FR- with a user-facing surface gets a screen home in §1 and a flow in
325
+ §4. Such an FR- with no screen is a coverage failure. (Stories live in
326
+ stories/ and never appear in this document — it traces to requirements.)
327
+ • Interaction-state requirements are ordinary FR- ids (the EARS
328
+ unwanted-behaviour form). Each one gets a §4 row.
329
+ - REALISE THE LIFTED CONSTRAINTS, each citing its spec id:
330
+ • CN- → §6 Authorization surface and the §1 access conditions (what is hidden
331
+ vs disabled vs enforced server-side — the client never becomes the authority).
332
+ • DC- → §2 "what must NOT be held" and the §6 data-exposure row: what may not
333
+ reach the client, its logs, its storage or its URLs.
334
+ • IC-/SC- → §5 binding rows and the §6 performance row respectively.
335
+ - §4 INTERACTION & FLOW AND §6 CROSS-CUTTING ARE NEVER OMITTED, however thin the
336
+ feature. Loading, empty, error, permission-denied and in-flight states, plus
337
+ validation timing, focus placement and keyboard reachability, are where most UI
338
+ defects live. A state the UI can reach that the spec does not describe is a SPEC
339
+ gap: log it as a §8 DA-n and say it routes to `specify`; never invent the
340
+ requirement here.
341
+ - §5 IS THE LANE BOUNDARY, AND `interfaces.md` IS THE CONTRACT. The service
342
+ lane's published surface lives in that append-only log, which /appweaver plan
343
+ writes: bind against the LATEST entry per interface — an entry naming an
344
+ earlier id in `supersedes:` replaces it — plus the spec's IC- ids. CITE those
345
+ entries BY IF- id; do NOT restate, re-derive or contradict them, and introduce
346
+ NO server-side behaviour, persistence assumption or handler detail. An entry at
347
+ `status: proposed` is the designed contract and IS bindable; note the status
348
+ where it matters. NEVER WRITE TO THE LOG — this lane consumes it. Anything the
349
+ experience needs that the surface does not offer is a CONTRACT DELTA raised
350
+ back to the service lane under "Contract deltas needed" — never a workaround in
351
+ the client. Where the log is absent (the service lane has not planned this
352
+ feature yet), bind from the spec's IC- surface and record that as a §8 DA-n
353
+ saying so.
354
+ - CONSTITUTION-MANDATED CROSS-CUTTING (from the loaded UI constitution, NOT the
355
+ spec): a NON-NEGOTIABLE UX principle you must apply that has no per-feature spec
356
+ id. APPLY it, and because it is not spec-derived, LOG it as a §8 DA-n whose
357
+ basis cites the mandating principle ("per constitution.ui.md UX-<n>"); set the
358
+ corresponding §6 row's "Serves" to that DA-n. Do NOT fabricate a spec UX- for
359
+ it, and do NOT leave it uncited.
360
+ - TECHNOLOGY-AGNOSTIC: name no framework, router, state library, component library
361
+ or styling approach. Describe a component by its RESPONSIBILITY, its INPUTS as
362
+ field shapes, the EVENTS it raises and the STATES it can be in; describe routing
363
+ as a conceptual shape. The framework binds at `implement-ui`. Do NOT include
364
+ file placement, naming conventions or test mechanics — those are the UI
365
+ constitution's and `implement-ui`'s, identical for every feature.
366
+ - The header's Lane field is `{{lane_id}}`. That identifier is the ONLY thing you
367
+ are told about the lane: its framework and language are deliberately withheld,
368
+ because a design that knows them starts naming them.
369
+ - STAY IN THE LANE'S SCOPE. This feature's screens only. The app shell, global
370
+ layout and the design system belong to no single feature: cite them under
371
+ "Screens this feature does NOT own" / "Reused from the design system", and do
372
+ not claim or redesign them here.
373
+ - §7 TRACEABILITY IS THE SPINE — fill it completely, both ways: a row for EVERY
374
+ UX-, every FR- with a user-facing surface, and every CN-/DC-/IC-/SC- with an
375
+ experience obligation, so no
376
+ lifted constraint is silently dropped; and no UI element without a spec reason.
377
+ - Fill the two ledgers: §8 Assumptions (DA-n, to clarify later) and §9 Workarounds
378
+ (WA-n, to clear later). Empty → "None (as of v1.0)".
379
+ - Complete the template's INITIAL Change History row, and write NO other version
380
+ anywhere — the stamp step owns the **Version** header. No component code anywhere.
381
+
382
+ # ═══ BROWNFIELD — code-derived template, NEW feature in an EXISTING app ════
383
+ # Same contract as above (technology-agnostic, spec is the only domain input, full §7
384
+ # and both ledgers), with ONE difference: the STRUCTURE follows the code-derived
385
+ # template's own composition axes and the lane's as-built conventions, so a new
386
+ # feature slots into the app already there instead of introducing a second convention.
387
+ # Runs only when onboard generated the ui-design-template override.
388
+ - id: ctx_ui_plan_brownfield
389
+ condition: "{{ui_plan_mode}} == bootstrap_bf"
390
+ action: build_context
391
+ context:
392
+ - { path: "{{ui_plan_doc}}" } # the scaffolded CODE-DERIVED template = as-built section structure
393
+ - { path: "{{spec_file}}" } # the source of truth (UX/US/FR/CN/DC/IC/SC)
394
+ - { path: .appweaver/DESIGN-FORMAT.md } # the design-doc contract + its frontend lane variant
395
+ - { path: .appweaver/memory/constitution.ui.md, optional: true } # the lane's UX-* base, reversed by onboard from this same UI
396
+ - { path: knowledge/architecture/frontend.md, optional: true } # the as-built UI architecture + the SHARED surface no slice owns
397
+ - { path: "{{feature_dir}}/interfaces.md", optional: true } # THE CONTRACT — the service lane's published surface, latest entry per id wins
398
+ - { path: "{{okf_concept}}", optional: true } # the feature's OKF concept: use-case log + source map
399
+ - { path: "{{service_plan}}", optional: true } # §5 ONLY — the service's structure; its API section only CITES the log
400
+ - { path: "{{service_design}}", optional: true } # the same surface under its legacy name (unmigrated repo)
401
+ - id: gen_ui_plan_brownfield
402
+ condition: "{{ui_plan_mode}} == bootstrap_bf"
403
+ action: agent_author
404
+ tools: Read # the current frontend code arrives as {{source_csv}} paths, not inlined
405
+ output_target: { type: file, path: "{{ui_plan_doc}}" }
406
+ task: |
407
+ Author the MASTER UI plan (the target frontend-plan.md in context — it is this
408
+ project's CODE-DERIVED UI design template) for this NEW feature being added to an
409
+ EXISTING frontend, by filling the template from the feature spec + the loaded
410
+ frontend standards. It is the MEDIATOR between the spec and the components
411
+ `implement-ui` will generate. Guidance: {{USER_INPUT}}
412
+ Rules:
413
+ - THE SPEC IS THE ONLY DOMAIN INPUT. You have the spec + frontend standards + the
414
+ lane's as-built architecture reference, and NO domain rule files. The domain
415
+ arrives as spec ids only; an unavoidable new concept is an Assumption (DA-n).
416
+ - THE CODE IS THE WHAT-EXISTS AUTHORITY, and it is a DIFFERENT authority from the
417
+ spec. {{source_count}} file(s) realise this feature's experience today; read the
418
+ ones that bear on what you are deciding and ADJUST this plan to the composition,
419
+ naming and state handling that are actually there rather than to an idealised
420
+ shape. An empty list is greenfield — design from a clean slate and say so. The
421
+ code NEVER overrides the spec on required behaviour and introduces no rule the
422
+ spec omits: code contradicting the spec is recorded as a DA-n and designed to
423
+ the SPEC. A deliberate divergence from the as-built is a WA-n with its trigger.
424
+ {{source_csv}}
425
+ - STRUCTURE FOLLOWS THE TEMPLATE AND THE LANE AS-BUILT, not a fixed screen
426
+ taxonomy. Keep the template's own section order and headings, and create one
427
+ component subsection per composition axis THIS feature actually needs, titled
428
+ with the lane's OWN name for it. ALIGN this feature to the app already there —
429
+ read the as-built architecture reference in context and MATCH its screen
430
+ organisation, its state and data-fetching placement and its styling approach, so
431
+ the feature slots in rather than introducing a second convention.
432
+ - KEEP THE NEUTRAL ANCHORS the template pins: the navigation/screen inventory, the
433
+ client-state table, the interaction-and-flow section, the cross-cutting table
434
+ and the traceability spine exist in every UI design, whatever the axes are
435
+ called. INTERACTION & FLOW and CROSS-CUTTING are NEVER omitted, however thin the
436
+ feature — loading, empty, error, permission-denied and in-flight states, focus
437
+ placement and keyboard reachability are where most UI defects live.
438
+ - REALISE THE SPEC'S EXPERIENCE CONTENT: every Experience Standard (UX-) gets a
439
+ traceability row (backend-plan.md excludes them by contract, so this is their only
440
+ home), every User Story a screen and a flow, every interaction-state FR- a flow
441
+ row. Realise the lifted constraints citing their ids — CN- as the authorization
442
+ surface and access conditions, DC- as what may not reach the client or its logs,
443
+ storage and URLs, IC- as binding rows, SC- as the performance decision.
444
+ - THE LANE BOUNDARY, AND `interfaces.md` IS THE CONTRACT: bind against the
445
+ LATEST entry per interface in that append-only log (a `supersedes:` entry
446
+ replaces the one it names), plus the spec's IC- ids. Cite them BY IF- id; do
447
+ not restate, re-derive or contradict them, introduce no server-side behaviour,
448
+ and NEVER write to the log — this lane consumes it. What the experience needs
449
+ and the surface lacks is a contract delta raised back to the service lane,
450
+ never a client workaround. Where the log is absent, bind from the spec's IC-
451
+ surface and record a DA-n saying so.
452
+ - CONSTITUTION-MANDATED CROSS-CUTTING with no per-feature spec id: apply it, and
453
+ log it as a DA-n citing the mandating principle ("per constitution.ui.md
454
+ UX-<n>"). Do not fabricate a spec id for it and do not leave it uncited.
455
+ - TECHNOLOGY-AGNOSTIC: name no framework, router, state library, component library
456
+ or styling approach — responsibilities, field shapes, events and states only.
457
+ Naming the lane's as-built CONVENTION is not naming a framework; naming the
458
+ library that implements it is. The framework binds at `implement-ui`.
459
+ - The header's Lane field is `{{lane_id}}`. That identifier is the ONLY thing you
460
+ are told about the lane: its framework and language are deliberately withheld,
461
+ because a design that knows them starts naming them.
462
+ - THE SHARED SURFACE (app shell, global layout, design system) belongs to no
463
+ feature. Cite it; do not copy it in and do not claim it here.
464
+ - Fill the traceability spine completely, both ways, and both ledgers —
465
+ Assumptions (DA-n) and Workarounds (WA-n), "None (as of v1.0)" when empty.
466
+ - Complete the template's INITIAL Change History row, and write NO other version
467
+ anywhere — the stamp step owns the **Version** header. No component code anywhere.
468
+
469
+ # ══════════════════ UPDATE — conform the existing plan to the moved spec ═════
470
+ # The work list is COMPUTED, never guessed: the ledger rows above this plan's
471
+ # derivation stamp name exactly the ids to conform. Everything else stays
472
+ # byte-identical.
473
+ - id: ctx_ui_plan_update
474
+ condition: "{{ui_plan_mode}} == update"
475
+ action: build_context
476
+ context:
477
+ - { path: "{{ui_plan_doc}}" } # the CURRENT plan — the document being conformed
478
+ - { path: "{{spec_file}}" } # the source of truth at {{current_version}}
479
+ - { path: .appweaver/DESIGN-FORMAT.md } # the design-doc contract + its frontend lane variant
480
+ - { path: .appweaver/memory/constitution.ui.md, optional: true } # the lane's ratified UX-* base
481
+ - { path: knowledge/architecture/frontend.md, optional: true } # brownfield repos: the as-built UI architecture
482
+ - { path: "{{feature_dir}}/interfaces.md", optional: true } # THE CONTRACT — the service lane's published surface, latest entry per id wins
483
+ - { path: "{{okf_concept}}", optional: true } # the feature's OKF concept: use-case log + source map
484
+ - { path: "{{service_plan}}", optional: true } # §5 ONLY — the service's structure; its API section only CITES the log
485
+ - { path: "{{service_design}}", optional: true } # the same surface under its legacy name (unmigrated repo)
486
+ - id: gen_ui_plan_update
487
+ condition: "{{ui_plan_mode}} == update"
488
+ action: agent_author
489
+ tools: Read # the current frontend code arrives as {{source_csv}} paths, not inlined
490
+ output_target: { type: file, path: "{{ui_plan_doc}}" }
491
+ task: |
492
+ UPDATE the master UI plan (the target frontend-plan.md in context) so it
493
+ conforms to the CURRENT spec. Tracking state: {{track_state}}. The spec moved
494
+ from {{derived_version}} (this plan's derivation) to {{current_version}}; the
495
+ ledger rows above the derivation name what moved:
496
+ ids: {{changed_csv}}
497
+ changes: {{changed_summary}}
498
+ Guidance: {{USER_INPUT}}
499
+ Rules:
500
+ - SCOPE IS THE COMPUTED WORK LIST. Rework ONLY the screens, flows, state rows,
501
+ binding rows, cross-cutting rows and traceability rows that realise the ids
502
+ above. Every other line stays BYTE-IDENTICAL; an unrelated "improvement" is
503
+ drift. EXCEPTION — tracking state `untracked`: this document has no baseline
504
+ (a migrated legacy design.ui.md), so reconcile the WHOLE plan to the spec
505
+ once; the stamp then baselines it.
506
+ - accept_manual: {{ACCEPT_MANUAL}}. When true, the document carries hand edits
507
+ that were deliberately accepted: KEEP their intent, reconcile them with the
508
+ spec, and record each kept deviation honestly — a new concept as an
509
+ Assumptions DA-n, a deliberate shortcut as a Workarounds WA-n. Never silently
510
+ revert a hand edit.
511
+ - AN ID THE SPEC NO LONGER DEFINES LOSES ITS REALISATION; an id whose wording
512
+ moved is re-realised from the spec's CURRENT text — the spec is the wording.
513
+ A UX- is THIS document's to realise: a moved UX- with no home here after the
514
+ update is a DROPPED requirement.
515
+ - AN ID THE SPEC HAS NEWLY DEFINED GAINS ONE. An id in the work list that NO
516
+ existing section realises is an ADDITION: give it a home in the sections that
517
+ realise it and a traceability row like any other. The scope rule above says
518
+ which sections may be touched; it does not say a new requirement may not be
519
+ designed, and reading it that way would let every added UX- or FR- with a
520
+ user-facing surface select nothing to rework and land unrealised.
521
+ - THE CODE IS THE WHAT-EXISTS AUTHORITY, and it is a DIFFERENT authority from the
522
+ spec. {{source_count}} file(s) realise this feature's experience today; re-read the
523
+ ones the moved ids touch (code state is read FRESH every run, never assumed) and ADJUST this plan to the composition,
524
+ naming and state handling that are actually there rather than to an idealised
525
+ shape. An empty list is greenfield — design from a clean slate and say so. The
526
+ code NEVER overrides the spec on required behaviour and introduces no rule the
527
+ spec omits: code contradicting the spec is recorded as a DA-n and designed to
528
+ the SPEC. A deliberate divergence from the as-built is a WA-n with its trigger.
529
+ {{source_csv}}
530
+ - THE SPEC IS THE ONLY DOMAIN INPUT, and §5 stays the lane boundary: bind
531
+ against `interfaces.md` — the LATEST entry per interface, cited by IF- id —
532
+ introduce no server-side behaviour, and never write to the log. A moved id
533
+ whose interface changed appears there as a SUPERSEDING entry: rebind §5 to it.
534
+ What the experience needs and the surface lacks is a contract delta raised
535
+ back to the service lane.
536
+ - PRESERVE THE LEDGERS: Assumptions, Workarounds and Change History keep every
537
+ existing entry and their numbering — append, never rewrite. Write NO version
538
+ numbers anywhere and ADD NO Change History row: the stamp step appends the
539
+ row and moves the **Version** header.
540
+ - TRACEABILITY STAYS COMPLETE both ways after the update; TECHNOLOGY-AGNOSTIC,
541
+ no component code, same contract as at bootstrap.
542
+
543
+ # 5. STAMP — the only writer of the plan's version identity; refuses (no baseline)
544
+ # if the write dropped the Change History section.
545
+ - id: stamp_bootstrap
546
+ condition: "{{ui_plan_mode}} != update"
547
+ action: execute_script
548
+ script: { ref: actions }
549
+ subcommand: plan-stamp
550
+ args: ["--feature-dir", "{{feature_dir}}", "--doc", "{{ui_plan_doc}}", "--no-row"]
551
+ outputs:
552
+ - { name: stamp_ok, from: "$.ok" }
553
+ - { name: stamp_error, from: "$.error" }
554
+ - { name: current_version, from: "$.version" }
555
+ - id: stamp_update
556
+ condition: "{{ui_plan_mode}} == update"
557
+ action: execute_script
558
+ script: { ref: actions }
559
+ subcommand: plan-stamp
560
+ args: ["--feature-dir", "{{feature_dir}}", "--doc", "{{ui_plan_doc}}", "--ids", "{{changed_csv}}", "--note", "Conformed to spec {{current_version}}"]
561
+ outputs:
562
+ - { name: stamp_ok, from: "$.ok" }
563
+ - { name: stamp_error, from: "$.error" }
564
+ - id: stamp_fence
565
+ action: verify_context
566
+ require_true: stamp_ok
567
+ on_false: stop
568
+ on_false_message: "plan-stamp refused: {{stamp_error}} — frontend-plan.md has been written but NOT baselined. Restore the missing ledger section, then re-run plan-ui (or plan-stamp) to record the baseline."
569
+
570
+ # ═══════════ HANDOFF — authoring only; the gate is a SEPARATE stage ════════
571
+ # → /appweaver analyze-design gates this write. It is ONE command over BOTH
572
+ # lanes, not a per-lane pair: the coverage question spans them (a UX- has its
573
+ # only home here, a BR- only in backend-plan.md, an FR- with a surface needs
574
+ # both), so no per-lane gate could answer it. It reads §7 Traceability as the
575
+ # two-way spine and refuses to certify a plan the spec has moved past.
576
+ # → /appweaver implement-ui generates the components from this document alone.
577
+
578
+ # 6. Post-execution hooks.
579
+ - id: resolve_post_hooks
580
+ action: execute_script
581
+ script: { ref: actions }
582
+ subcommand: resolve-hooks
583
+ args: ["--phase", "after_design_ui"]
584
+ outputs:
585
+ - { name: after_hooks, from: "$.hooks" }
586
+ - id: dispatch_post_hooks
587
+ action: dispatch_hooks
588
+ hooks_from_variable: after_hooks
589
+
590
+ outputs:
591
+ feature_dir: "{{feature_dir}}"
592
+ ui_plan_doc: "{{ui_plan_doc}}"
593
+ ui_plan_mode: "{{ui_plan_mode}}"
594
+ source_files_read: "{{source_count}}"
595
+ migrated_legacy_design: "{{ui_plan_migrated}}"
596
+ lane_id: "{{lane_id}}"
597
+ lane_root: "{{lane_root}}"
598
+ derived_from_spec: "{{current_version}}"
599
+ conformed_ids: "{{changed_csv}}"
600
+ next: "frontend-plan.md written for the {{lane_id}} lane at {{ui_plan_doc}} (mode: {{ui_plan_mode}}). Gate this write: /appweaver analyze-design — one command over BOTH lanes, which is what proves no UX- fell between them; it reads §7 Traceability as the two-way spine. CHECK §5 CONTRACT BINDING: it binds to interfaces.md, which /appweaver plan publishes — if that log was absent this run, §5 was bound from the spec's IC- ids alone and a DA-n says so, which means running plan first and re-running this command is what turns an assumption into the real contract. Contract deltas needed goes back to the service lane, never into the client. source_files_read {{source_count}} is how many existing UI files this plan was written against; 0 means greenfield. Then /appweaver implement-ui."