aw-spec-cli 0.1.13 → 0.1.15

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