aw-spec-cli 0.1.22 → 0.1.24
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/assets/.appweaver/ORCH-DIALECT.md +3 -3
- package/assets/.appweaver/RULE-PARTITION.md +41 -28
- package/assets/.appweaver/orchestrator/orchestrator.py +139 -17
- package/assets/.appweaver/orchestrator/rev-templates/ai-rules.reference.md +3 -3
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +19 -12
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +25 -17
- package/assets/.appweaver/orchestrator/rev-templates/domain-requirements.skeleton.md +2 -2
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +10 -10
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +28 -17
- package/assets/.appweaver/orchestrator/rev-templates/technical-constitution.skeleton.md +22 -24
- package/assets/.appweaver/orchestrator/rev-templates/technical-rule-file.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +29 -19
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +27 -20
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +184 -37
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +18 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +19 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +18 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-dependency-tree.orch.yaml +4 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +5 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +58 -43
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +650 -205
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +19 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-domain.orch.yaml +3 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +5 -5
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +4 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-model.orch.yaml +34 -6
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-prototype.orch.yaml +40 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-workflow-model.orch.yaml +23 -4
- package/assets/.appweaver/scripts/bash/aw-rev-constitution.sh +1 -1
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +82 -10
- package/assets/.appweaver/scripts/powershell/aw-rev-constitution.ps1 +1 -1
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +67 -2
- package/assets/.appweaver/scripts/python/aw-inventory.py +1809 -156
- package/assets/.appweaver/scripts/python/aw-rev-constitution.py +106 -145
- package/assets/.appweaver/scripts/python/aw-stack.py +45 -1
- package/assets/.appweaver/scripts/python/aw-xref.py +73 -2
- package/assets/.appweaver/scripts/python/validate_okf.py +10 -5
- package/assets/.appweaver/templates/okf-concept-template.md +10 -2
- package/assets/.appweaver/templates/proposed-inventory-template.md +100 -48
- package/assets/.appweaver/templates/source-map-template.md +14 -6
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.pack.txt +3 -3
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.pack.txt +5 -123
- package/assets/foundations/java/Hexagonal/Hexagonal.pack.txt +784 -0
- package/assets/foundations/java/Hexagonal/pack-foundation.sh +357 -0
- package/assets/foundations/java/Hexagonal/unpack-foundation.sh +399 -0
- package/package.json +1 -1
|
@@ -119,6 +119,7 @@ vars:
|
|
|
119
119
|
lanes_declared: "false"
|
|
120
120
|
be_declared: "false"
|
|
121
121
|
fe_declared: "false"
|
|
122
|
+
fe_excluded: "false" # this feature records no surface in the declared frontend lane (feature-lanes)
|
|
122
123
|
be_lane_id: ""
|
|
123
124
|
fe_lane_id: ""
|
|
124
125
|
active_change: ""
|
|
@@ -235,6 +236,23 @@ workflow:
|
|
|
235
236
|
- { name: spec_file, from: "$.spec_file" }
|
|
236
237
|
- { name: spec_exists, from: "$.spec_exists" }
|
|
237
238
|
|
|
239
|
+
# THIS FEATURE'S LANES. lane_resolve above read the REPO's declaration; one feature may
|
|
240
|
+
# own no part of a lane's surface — adopt records `lanes: [backend]` in
|
|
241
|
+
# active_version.json for a service-only slice in a repo that also has a UI. Re-read with
|
|
242
|
+
# --feature-dir so a lane the feature records no surface in is absent here: never
|
|
243
|
+
# scaffolded, bootstrapped or delta-planned for it. With no feature resolved the argument
|
|
244
|
+
# is dropped and the repo's answer stands.
|
|
245
|
+
- id: feature_lanes
|
|
246
|
+
action: execute_script
|
|
247
|
+
on_error: continue
|
|
248
|
+
script: { ref: actions }
|
|
249
|
+
subcommand: lane-resolve
|
|
250
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
251
|
+
outputs:
|
|
252
|
+
- { name: be_declared, from: "$.backend_declared" }
|
|
253
|
+
- { name: fe_declared, from: "$.frontend_declared" }
|
|
254
|
+
- { name: fe_excluded, from: "$.frontend_feature_excluded" }
|
|
255
|
+
|
|
238
256
|
# The BOUNDED CONTEXT's domain model — where the aggregate root, its entities and its
|
|
239
257
|
# invariants (BR-n) live. The spec cites them by id; this phase records where each is
|
|
240
258
|
# ENFORCED, so the model has to be in context or the instruction to "follow it exactly"
|
|
@@ -1135,6 +1153,7 @@ outputs:
|
|
|
1135
1153
|
bounded_context: "{{context}}"
|
|
1136
1154
|
backend_plan: "{{backend_plan}}" # empty when no backend lane is declared
|
|
1137
1155
|
frontend_plan: "{{frontend_plan}}" # empty when no frontend lane is declared
|
|
1156
|
+
frontend_excluded: "{{fe_excluded}}" # true = a frontend lane is declared and this feature records no surface in it (feature-lanes)
|
|
1138
1157
|
domain_model: "{{domain_model}}"
|
|
1139
1158
|
plan_mode: "{{plan_mode}}"
|
|
1140
1159
|
plan_target: "{{plan_target}}"
|
|
@@ -80,4 +80,6 @@ outputs:
|
|
|
80
80
|
domain_rule_bindings: "{{map_path}}"
|
|
81
81
|
domain_rule_bindings_exists: "{{map_exists}}"
|
|
82
82
|
note: "Active project '{{domain}}' persisted to .appweaver/domain.json. It selects TWO rule maps under guardrails/projects/{{domain}}/ — domain-rule-bindings.md (DOMAIN rules; read by constitution + specify only, because the spec is the pivot) and tech-rule-bindings.md (TECHNICAL rules; read by 10 workflows incl. design, implement, build and run). The rule FILES themselves live in shared corpora — guardrails/technology/<stack>/ and guardrails/domain/<industry>/ — and the maps are what bind this project to them, so two projects can share a corpus and differ only in their maps and their own three requirements files. domain_rule_bindings_exists reports the domain half only. A map that is absent, or a command with no heading in one, loads nothing for that class and SOFT-CONTINUES — an absent heading is a valid state, never a failure."
|
|
83
|
-
|
|
83
|
+
# constitution comes BEFORE select-feature: it reads the domain rule map this
|
|
84
|
+
# workflow just bound, and specify declares its domain half as REQUIRED context.
|
|
85
|
+
next: "/appweaver constitution → select-feature → specify. /appweaver constitution authors the constitution halves from the domain rule map this run just bound — specify declares constitution.domain.md as REQUIRED context, and plan and implement read constitution.technical.md (plus constitution.ui.md on a frontend lane), so it runs before the first feature rather than after. /appweaver select-feature then picks the feature to build; every stage from specify onward reads that active feature and stops without it. Re-run /appweaver select-domain any time to switch projects; it rebinds both maps at once, and /appweaver constitution picks the new bindings up on its next run."
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
#
|
|
29
29
|
# list-features (specs/ ∪ features the OKF concepts serve) → user_select (existing | "+ create new") → [ask name] → set-feature
|
|
30
30
|
#
|
|
31
|
-
# Persists .appweaver/feature.json {"feature_directory":"specs
|
|
32
|
-
# later command operates on it: the forward loop (specify/plan/analyze-plan/
|
|
33
|
-
# build/analyze-slice/run/deploy) and the CR loop
|
|
34
|
-
#
|
|
35
|
-
# the delta docs themselves).
|
|
31
|
+
# Persists .appweaver/feature.json {"feature_directory":"specs/[<Context>/]<Feature>"} so
|
|
32
|
+
# every later command operates on it: the forward loop (specify/plan/analyze-plan/
|
|
33
|
+
# implement/build/analyze-slice/run/deploy), brownfield's adopt, and the CR loop
|
|
34
|
+
# (specify/plan/analyze-plan-delta/implement/build/analyze-slice-delta/finalize-delta/
|
|
35
|
+
# analyze-slice/close-delta — specify and plan author the delta docs themselves).
|
|
36
36
|
#
|
|
37
37
|
# Launch:
|
|
38
38
|
# /appweaver select-feature # interactive: list + add-new
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# aw-spec-select-stack (orchestrator) — declare the project's LANES
|
|
3
3
|
#
|
|
4
4
|
# THE FIRST COMMAND OF EVERY SEQUENCE. Greenfield runs it before constitution;
|
|
5
|
-
# brownfield runs it before
|
|
5
|
+
# brownfield runs it before inventory. What it writes decides which constitution
|
|
6
6
|
# halves are authored, which rule sets are generated, which subtree each phase may
|
|
7
7
|
# write into, and — in brownfield — which subtree onboard reverse-engineers. It
|
|
8
8
|
# answers one question per lane, twice:
|
|
@@ -317,4 +317,4 @@ outputs:
|
|
|
317
317
|
ui_detected: "{{ui_detected}}"
|
|
318
318
|
prototype: "{{prototype}}"
|
|
319
319
|
note: "Lanes persisted to .appweaver/stacks.json. Each lane carries a STACK and a SOURCE PATH, and both are load-bearing. The SOURCE PATH is ownership: implement's service lane writes its code only under the service lane's and its frontend lane only under the UI lane's, build/test/run act on it, and onboard reverse-engineers out of it. Each lane also owns a TEST PATH, taken from its stack profile — tests/ beside a .NET src/, the source path itself for a stack that keeps its tests inside — and implement writes the lane's tests there. Overlapping paths of either kind are refused here rather than discovered later as two generators writing one file. The build/test/run commands are project-supplied strings the engine shells out to, which is how it stays stdlib-only and stack-ignorant. api_detected / ui_detected report what this repo's own files pointed at, whether or not it was chosen, as "<stack> at <path>" — or "nothing detected" on a greenfield repo with nothing to detect. A source path that does not exist yet is NORMAL here and reported rather than refused: greenfield declares src/ before implement creates it. The on-disk check runs in the phases that read the subtree — implement, build, run and onboard all call lane-verify and STOP with the fix named. Declining a lane with (none) is a real declaration and removes any lane of that kind a previous run left behind; it is also what stops that lane's constitution half and rule files being authored. `prototype` is not a lane — it has a root and a run command so an operator can open it, and nothing else. Re-run this workflow any time to re-stack a lane, move a source path, or drop a lane with (none); it is the only workflow that may write this file."
|
|
320
|
-
next: "GREENFIELD: /appweaver foundation — once per lane, to scaffold a compiling project into each source path declared here (skip it for a lane whose code already exists, or scaffold with the stack's own tooling) — then /appweaver select-domain → constitution → select-feature → specify. BROWNFIELD: /appweaver
|
|
320
|
+
next: "GREENFIELD: /appweaver foundation — once per lane, to scaffold a compiling project into each source path declared here (skip it for a lane whose code already exists, or scaffold with the stack's own tooling) — then /appweaver select-domain → constitution → select-feature → specify. BROWNFIELD: /appweaver inventory — it scans the source paths declared here and proposes the bounded contexts and features as specs/proposed-inventory.md; edit that map, run /appweaver inventory apply, then /appweaver onboard <Context> once per bounded context, which reverse-engineers the governance out of the same source paths and activates it (no select-domain; onboard derives it from the code). Run this workflow FIRST in either sequence: foundation unpacks into the source paths declared here, constitution reads the declaration to decide which halves to author, and inventory and onboard read it to decide what to scan."
|
|
@@ -771,4 +771,7 @@ outputs:
|
|
|
771
771
|
checklist_file: "{{checklist_file}}"
|
|
772
772
|
checklist_quality: "{{checklist_passed}}/{{checklist_total}} items passing"
|
|
773
773
|
note: "Behaviour and structure were authored together: the spec says what the system must do, {{domain_model}} says what the domain IS. The aggregate root, its entities and its invariants (BR-n) live in the model and are cited from the spec by id — never restated there, because several specs act on one aggregate and a per-spec copy drifts. On the DELTA path neither master was touched: this run wrote changes/{{cr_id}}/delta-spec.md and {{delta_model}}, and that model delta sits BESIDE the master where every other open delta can see it while being authored. WAVE {{wave}} is its parallel-development level — deltas sharing a wave are independent, a higher wave is implemented only after the lower ones land. The wave is DERIVED from the model delta's `## Depends on`: one above the highest delta named there, stamped into both halves as soon as it was authored ({{wave_message}}). To move it, correct that section and re-run `.appweaver/scripts/bash/aw-spec-actions.sh cr-wave-derive --cr-id {{cr_id}} --stamp`; finalize-delta refuses a change whose wave contradicts it. {{open_count}} change(s) are open on this feature."
|
|
774
|
-
|
|
774
|
+
# Both paths spell out their WHOLE remaining chain. The bootstrap path named only
|
|
775
|
+
# `plan` and stopped there, which left a first greenfield run with no successor at
|
|
776
|
+
# the exact point the delta path was fully documented.
|
|
777
|
+
next: "/appweaver plan — BOOTSTRAP authors backend-plan.md from the spec + the domain model (plus frontend-plan.md where a UI lane is declared); a DELTA (this run opened change {{cr_id}}, wave {{wave}}) authors the delta plans for it. BOOTSTRAP then flows: plan → analyze-plan → implement → build → analyze-slice → run → deploy (analyze-plan is the spec↔plan gate and it repairs and stops only while NO change is open). A change instead flows: analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta (finalize-delta merges the delta into spec.md + domain-model.md — not the lane plans, which are derived from those two — but leaves the change open; analyze-slice verifies the slice against the merged master; close-delta archives the change and bumps the version). ⚠️ With more than one change open, plan, analyze-plan-delta and implement ASK which one they are for (pass the id as the argument to skip the prompt) and activate the answer; finalize-delta and close-delta still act on whichever change is active — so finish or close the others deliberately."
|
|
@@ -52,11 +52,29 @@ workflow:
|
|
|
52
52
|
outputs:
|
|
53
53
|
- { name: feature, from: "$.feature" }
|
|
54
54
|
- { name: feature_dir, from: "$.feature_dir" }
|
|
55
|
-
- { name: feature_slug, from: "$.feature_slug" }
|
|
55
|
+
- { name: feature_slug, from: "$.feature_slug" } # kebab-case route segment
|
|
56
56
|
- { name: spec_file, from: "$.spec_file" }
|
|
57
57
|
- { name: spec_exists, from: "$.spec_exists" }
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
|
|
59
|
+
# domain and prototype_root have their OWN resolvers — feature-paths answers for the
|
|
60
|
+
# feature's documents and nothing else, so binding them here left both empty and the
|
|
61
|
+
# emptiness reached the prompts as a blank path segment rather than an error.
|
|
62
|
+
- id: resolve_domain
|
|
63
|
+
action: execute_script
|
|
64
|
+
on_error: continue
|
|
65
|
+
script: { ref: actions }
|
|
66
|
+
subcommand: resolve-domain
|
|
67
|
+
args: ["--command", "/aw-spec-ui-model"]
|
|
68
|
+
outputs:
|
|
69
|
+
- { name: domain, from: "$.domain" }
|
|
70
|
+
|
|
71
|
+
- id: resolve_prototype_lane
|
|
72
|
+
action: execute_script
|
|
73
|
+
on_error: continue
|
|
74
|
+
script: { ref: actions }
|
|
75
|
+
subcommand: lane-resolve
|
|
76
|
+
outputs:
|
|
77
|
+
- { name: prototype_root, from: "$.prototype.root" }
|
|
60
78
|
|
|
61
79
|
- id: require_feature
|
|
62
80
|
action: verify_context
|
|
@@ -92,10 +110,14 @@ workflow:
|
|
|
92
110
|
vars:
|
|
93
111
|
feature_name: "{{feature}}"
|
|
94
112
|
feature_dir: "{{feature_dir}}"
|
|
113
|
+
domain: "{{domain}}"
|
|
95
114
|
user_preferences: "{{USER_INPUT}}"
|
|
96
115
|
context:
|
|
97
116
|
- "{{feature_dir}}/ui/workflow-model.md"
|
|
98
117
|
- "{{spec_file}}"
|
|
118
|
+
- { path: "guardrails/projects/{{domain}}/frontend/design.md", optional: true }
|
|
119
|
+
- { path: "guardrails/projects/{{domain}}/frontend/DESIGN.md", optional: true }
|
|
120
|
+
- { path: "guardrails/projects/{{domain}}/frontend/ui-requirements.md", optional: true }
|
|
99
121
|
- { path: ".appweaver/templates/ui-model-template.md", optional: true }
|
|
100
122
|
- { path: ".appweaver/memory/constitution.ui.md", optional: true }
|
|
101
123
|
|
|
@@ -117,9 +139,15 @@ workflow:
|
|
|
117
139
|
- Define supported interaction states per surface (`Loading`, `Empty`, `Populated`, `Filtered / Searched`, `Validation Error`, `Submitting`, `Success`, `Error`).
|
|
118
140
|
- Specify interactive controls, form fields, action buttons, and table/card layouts.
|
|
119
141
|
- Map explicit navigation & transition triggers (`On Action -> Opens Surface X`).
|
|
120
|
-
3.
|
|
121
|
-
|
|
122
|
-
|
|
142
|
+
3. **Natural Product Naming & Simplicity**:
|
|
143
|
+
- Use clean, concise, consumer-grade/modern B2B titles for all surfaces and actions (e.g. "Projects List", "New Project", "Project Details", "Edit Project", "Change Status").
|
|
144
|
+
- Avoid bloated, academic, or bureaucratic phrasing (do NOT write "Project Directory & Portfolio Catalog Surface", "Execute Lifecycle Status Transition Dialog", or "Update Project Metadata & Schedule Modal").
|
|
145
|
+
4. **Progressive Disclosure Architecture**:
|
|
146
|
+
- Structure primary views (cards, tables) to display only essential high-signal data points.
|
|
147
|
+
- Relegate deep governance details, audit logs, and technical metadata to secondary tabs, detail views, or slide-over drawers.
|
|
148
|
+
5. Remember the core rule: **Workflow != Single Screen**. A workflow can span multiple surfaces or reuse surfaces with state changes.
|
|
149
|
+
6. Format output according to `.appweaver/templates/ui-model-template.md`.
|
|
150
|
+
7. Write the authored markdown content to `{{feature_dir}}/ui/ui-model.md`.
|
|
123
151
|
|
|
124
152
|
User Guidance:
|
|
125
153
|
{{user_preferences}}
|
|
@@ -141,6 +141,9 @@ workflow:
|
|
|
141
141
|
- { path: "{{domain_model}}", optional: true }
|
|
142
142
|
- { path: "{{feature_dir}}/ui/workflow-model.md", optional: true }
|
|
143
143
|
- { path: "{{feature_dir}}/ui/ui-model.md", optional: true }
|
|
144
|
+
- { path: "guardrails/projects/{{domain}}/frontend/design.md", optional: true }
|
|
145
|
+
- { path: "guardrails/projects/{{domain}}/frontend/DESIGN.md", optional: true }
|
|
146
|
+
- { path: "guardrails/projects/{{domain}}/frontend/ui-requirements.md", optional: true }
|
|
144
147
|
- { path: ".appweaver/memory/constitution.domain.md", optional: true }
|
|
145
148
|
- { path: ".appweaver/memory/constitution.ui.md", optional: true }
|
|
146
149
|
- { path: "{{feature_dir}}/changes/{{active_change}}/delta-spec.md", optional: true }
|
|
@@ -197,6 +200,10 @@ workflow:
|
|
|
197
200
|
- The persona/role switcher must be styled and framed as a normal in-app affordance
|
|
198
201
|
(e.g. an account/role menu a real user of this kind of app would plausibly have),
|
|
199
202
|
not as an obvious dev/testing control bolted onto the UI.
|
|
203
|
+
- **Simplicity of language and visual calmness**:
|
|
204
|
+
- Use simple, everyday product language (e.g. "Projects", "Status", "Dates", "Budget").
|
|
205
|
+
- Avoid architectural meta-language or bureaucratic phrasing (e.g. do NOT say "Initiative Lifecycle Distribution", "Workspace Governance & Audit Stream", "Optimistic Concurrency Version").
|
|
206
|
+
- Keep dashboards clean and focused with 3–4 high-signal metrics and concise cards, not dense walls of text or redundant explanatory paragraphs.
|
|
200
207
|
|
|
201
208
|
**Styling must actually render — this is not optional:**
|
|
202
209
|
- Install and fully configure **Tailwind CSS** (latest stable version, matching whatever
|
|
@@ -247,6 +254,7 @@ workflow:
|
|
|
247
254
|
The feature specification is taken directly from the bounded context directory and feature path defined in `.appweaver/feature.json`:
|
|
248
255
|
`{{spec_file}}` (Bounded Context: `{{context_name}}`, Feature: `{{feature_name}}`).
|
|
249
256
|
Read this specification strictly from `{{spec_file}}`, along with the bounded context's canonical domain model (`{{domain_model}}`),
|
|
257
|
+
the project Design System if present (`guardrails/projects/{{domain}}/frontend/design.md` or `DESIGN.md`, or `{{feature_dir}}/ui/design.md`),
|
|
250
258
|
and when available, the Workflow Model (`{{feature_dir}}/ui/workflow-model.md`) and UI Model (`{{feature_dir}}/ui/ui-model.md`), plus `{{prototype_root}}/lib/screens-registry.ts` / `screens.json`.
|
|
251
259
|
If `workflow-model.md` and `ui-model.md` are not provided, derive all UI surfaces, workflows, and interaction states directly from `{{spec_file}}`.
|
|
252
260
|
|
|
@@ -266,9 +274,24 @@ workflow:
|
|
|
266
274
|
see the actual working application screen, fully styled and populated, matching
|
|
267
275
|
spec.md as if it shipped this way.
|
|
268
276
|
- Never surface "prototype/mock/demo/sample" language, or raw spec/traceability IDs
|
|
269
|
-
(`REQ-###`, `SPEC-SCREEN-###`,
|
|
277
|
+
(`REQ-###`, `SPEC-SCREEN-###`, `BR-###`, `FR-###`, `CN-###`, `DC-###`, `UX-###`) anywhere in this screen's rendered UI (titles,
|
|
270
278
|
headers, empty states, tooltips, button copy). Keep all such labeling out of
|
|
271
279
|
user-visible text entirely.
|
|
280
|
+
- **Natural product language over spec jargon**:
|
|
281
|
+
- Translate formal architectural and domain-driven design terms into clean, everyday product vocabulary:
|
|
282
|
+
- Say "Projects" (or simple domain noun) rather than "Portfolio Initiatives" or "Domain Aggregate".
|
|
283
|
+
- Say "Status" rather than "Governance State" or "Lifecycle Progression".
|
|
284
|
+
- Say "Timeline" or "Dates" rather than "Target Timeline Trajectory Bounds".
|
|
285
|
+
- Say "Budget" rather than "Allocated Capital Valuation".
|
|
286
|
+
- Say "Change Status" rather than "Execute Lifecycle Status Transition".
|
|
287
|
+
- Say "New Project" rather than "Create Initiative".
|
|
288
|
+
- Say "Activity" or "History" rather than "Immutable Governance Audit Stream".
|
|
289
|
+
- Avoid robotic, academic, or overly bureaucratic copy. Write like modern, top-tier SaaS products (e.g. Linear, Stripe, Notion).
|
|
290
|
+
- **Progressive disclosure and scannable information density**:
|
|
291
|
+
- Do NOT dump every requirement, invariant, and metadata attribute onto primary cards or lists.
|
|
292
|
+
- Keep primary cards and table rows clean and scannable — display only 3–5 essential, high-signal data points (e.g. Name, Status, Dates, Owner, Key Metric).
|
|
293
|
+
- Place deep operational details (audit histories, concurrency tokens, classification codes, full event trails) behind secondary inspection tabs, modals, or slide-over drawers — never on primary summary cards.
|
|
294
|
+
- Keep interfaces free of long instructional text blocks, redundant help banners, or cluttering meta-labels.
|
|
272
295
|
|
|
273
296
|
2. **Integrate this screen into the app like a real feature, not a spec entry**:
|
|
274
297
|
- `{{feature_name}}` and any spec title/ID are internal identifiers for you to read —
|
|
@@ -319,6 +342,9 @@ workflow:
|
|
|
319
342
|
Tailwind/CSS config from the shell is intact. If it's missing or broken, fix it
|
|
320
343
|
as part of this run and note that in your final summary; don't build a styled-looking
|
|
321
344
|
screen on top of a broken/absent styling pipeline.
|
|
345
|
+
- If a project visual design system is present in context (`guardrails/projects/{{domain}}/frontend/design.md` or `DESIGN.md`),
|
|
346
|
+
apply its visual design tokens (colors, accent, surface, border, typography, card elevation, and status states)
|
|
347
|
+
consistently across all cards, badges, buttons, forms, and headers on this screen.
|
|
322
348
|
- Use the shell's existing design tokens and Tailwind utility classes (or whatever
|
|
323
349
|
styling system the shell uses) rather than inline style objects or
|
|
324
350
|
ad-hoc CSS that bypasses the shared system — inline styles that "look fine" in
|
|
@@ -356,6 +382,17 @@ workflow:
|
|
|
356
382
|
original spec title/ID for traceability, add it as a separate non-rendered field
|
|
357
383
|
(e.g. `specRef`) rather than reusing `title` for it.
|
|
358
384
|
|
|
385
|
+
10. **Structured UI Specification Output (`ui-spec.md`)**:
|
|
386
|
+
- Generate or update `{{feature_dir}}/ui/ui-spec.md` alongside the prototype code.
|
|
387
|
+
- Format as a markdown document with a brief title and capture intro, followed by a SINGLE fenced `json` code block adhering strictly to the JSON schema defined in `skill/references/output-ui-spec.md`:
|
|
388
|
+
- `screen`: user-facing screen name and route
|
|
389
|
+
- `captureNotes`: viewport width, CSS px, DPR, and design system token origin (`guardrails/projects/{{domain}}/frontend/DESIGN.md` or `design.md`)
|
|
390
|
+
- `regions`: ordered list of UI regions (nav-bar, header, step-card, content, dialogs, drawers) with `layout` (`position`, `wrap`, `direction`) and `elements` array
|
|
391
|
+
- `elements`: each element detailing `id`, `kind` (`text`, `button`, `badge`, `avatar`, `card`, `input`, etc.), verbatim `content`, `style` (`shape`, `colorToken`, `typographyToken`, `spacingPx`), `state` (`default`, `active`, `hover`), and `fields`
|
|
392
|
+
- `navigation`: ordered nav items, activeItem, activeStyle
|
|
393
|
+
- `openQuestions`: roll-up of ambiguities, edge cases, and responsive variations
|
|
394
|
+
- Ensure all `colorToken` and `typographyToken` values reference design tokens from the domain design system.
|
|
395
|
+
|
|
359
396
|
User Preferences / Guidance:
|
|
360
397
|
{{user_preferences}}
|
|
361
398
|
|
|
@@ -367,6 +404,7 @@ workflow:
|
|
|
367
404
|
"feature": "{{feature_name}}",
|
|
368
405
|
"slug": "{{feature_slug}}",
|
|
369
406
|
"route": "/{{feature_slug}}",
|
|
407
|
+
"ui_spec": "{{feature_dir}}/ui/ui-spec.md",
|
|
370
408
|
"prototype_root": "{{prototype_root}}",
|
|
371
409
|
"launch_command": "cd {{prototype_root}} && npm run dev"
|
|
372
410
|
}
|
|
@@ -380,6 +418,7 @@ outputs:
|
|
|
380
418
|
context_dir: "{{context_dir}}"
|
|
381
419
|
feature: "{{feature}}"
|
|
382
420
|
spec_file: "{{spec_file}}"
|
|
421
|
+
ui_spec_file: "{{feature_dir}}/ui/ui-spec.md"
|
|
383
422
|
route: "/{{feature_slug}}"
|
|
384
423
|
prototype_root: "{{prototype_root}}"
|
|
385
424
|
launch_hint: "First time: cd {{prototype_root}} && npm install && npm run dev — after that, just rerun ui-prototype per feature and refresh the browser."
|
|
@@ -49,11 +49,29 @@ workflow:
|
|
|
49
49
|
outputs:
|
|
50
50
|
- { name: feature, from: "$.feature" }
|
|
51
51
|
- { name: feature_dir, from: "$.feature_dir" }
|
|
52
|
-
- { name: feature_slug, from: "$.feature_slug" }
|
|
52
|
+
- { name: feature_slug, from: "$.feature_slug" } # kebab-case route segment
|
|
53
53
|
- { name: spec_file, from: "$.spec_file" }
|
|
54
54
|
- { name: spec_exists, from: "$.spec_exists" }
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
|
|
56
|
+
# domain and prototype_root have their OWN resolvers — feature-paths answers for the
|
|
57
|
+
# feature's documents and nothing else, so binding them here left both empty and the
|
|
58
|
+
# emptiness reached the prompts as a blank path segment rather than an error.
|
|
59
|
+
- id: resolve_domain
|
|
60
|
+
action: execute_script
|
|
61
|
+
on_error: continue
|
|
62
|
+
script: { ref: actions }
|
|
63
|
+
subcommand: resolve-domain
|
|
64
|
+
args: ["--command", "/aw-spec-workflow-model"]
|
|
65
|
+
outputs:
|
|
66
|
+
- { name: domain, from: "$.domain" }
|
|
67
|
+
|
|
68
|
+
- id: resolve_prototype_lane
|
|
69
|
+
action: execute_script
|
|
70
|
+
on_error: continue
|
|
71
|
+
script: { ref: actions }
|
|
72
|
+
subcommand: lane-resolve
|
|
73
|
+
outputs:
|
|
74
|
+
- { name: prototype_root, from: "$.prototype.root" }
|
|
57
75
|
|
|
58
76
|
- id: require_feature
|
|
59
77
|
action: verify_context
|
|
@@ -105,8 +123,9 @@ workflow:
|
|
|
105
123
|
1. Read the feature specification (`spec.md` + active `delta-spec.md`) in context.
|
|
106
124
|
2. Identify ALL discrete user workflows required to fulfill the feature's capabilities, functional requirements (FRs), business rules (BRs), and user personas.
|
|
107
125
|
3. For EACH user workflow (WF-1, WF-2, ...), detail:
|
|
126
|
+
- **Workflow Title**: Name concisely after real, plain-English user actions (e.g. "WF-1: Browse Projects", "WF-2: Create Project", "WF-3: View Project Details", "WF-4: Edit Project", "WF-5: Change Status" — avoid bureaucratic or formal titles like "Execute Lifecycle Status Transition").
|
|
108
127
|
- **Actor / Persona**: Which role executes this flow (e.g. Project Manager, Delivery Head).
|
|
109
|
-
- **Goal**: Clear statement of user goal.
|
|
128
|
+
- **Goal**: Clear statement of user goal in natural language.
|
|
110
129
|
- **Preconditions**: Initial system state, permissions, or prerequisite conditions.
|
|
111
130
|
- **Primary Flow**: Step-by-step numbered interaction steps from start to completion.
|
|
112
131
|
- **Decision Points & Alternative Paths**: Clear branch logic (e.g. If user cancels, If user requests correction).
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# ONCE in ../python/aw-rev-constitution.py; this wrapper exists only so the
|
|
7
7
|
# orchestrator's execute_script (sh:/ps1: dispatch) can invoke it. Args pass through.
|
|
8
8
|
#
|
|
9
|
-
# aw-rev-constitution.sh
|
|
9
|
+
# aw-rev-constitution.sh project-context [--root <dir>] [--bundle <dir>]
|
|
10
10
|
# aw-rev-constitution.sh project-setup --name <domain> [--fallback <name>] [--root <dir>]
|
|
11
11
|
set -euo pipefail
|
|
12
12
|
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -35,9 +35,12 @@
|
|
|
35
35
|
# [--build <c>] [--test <c>] [--run <c>] [--prototype-root <p>] [--prototype-run <c>] [--no-prototype]
|
|
36
36
|
# -> {declared, lane, lanes[], lane_count, source_path, root_exists, prototype, written}
|
|
37
37
|
# (SOLE WRITER of .appweaver/stacks.json; callers = select-stack, onboard)
|
|
38
|
-
# lane-resolve [--kind <k>] [--id <lane>]
|
|
39
|
-
#
|
|
40
|
-
#
|
|
38
|
+
# lane-resolve [--kind <k>] [--id <lane>] [--feature-dir <path>]
|
|
39
|
+
# -> {declared, lanes[], found, lane, root, source_path, build, test, run,
|
|
40
|
+
# backend_*/frontend_* (declared, root, source_path, root_exists, build, test, run,
|
|
41
|
+
# feature_excluded), feature_lanes[], prototype}
|
|
42
|
+
# (absence is a valid state: no stacks.json -> declared:false, exit 0;
|
|
43
|
+
# --feature-dir reports a lane the feature records no surface in as not declared)
|
|
41
44
|
# lane-verify [--require backend|frontend]… [--allow-missing-root] [--command <phase>]
|
|
42
45
|
# -> {ok, declared, issues[], issue_count, message, backend_*/frontend_*}
|
|
43
46
|
# (the GATE: is the declaration usable for the phase about to run.
|
|
@@ -49,7 +52,9 @@
|
|
|
49
52
|
# version-read [--feature-dir <path>] -> {feature, version, active_change, exists, bootstrap_pending}
|
|
50
53
|
# version-init [--feature-dir <path>] [--bootstrap] -> {…, created, bootstrap_pending} (create active_version.json v1.0; --bootstrap marks a FIRST draft in progress)
|
|
51
54
|
# version-bootstrap-done [--feature-dir <path>] -> {…, cleared} (the ONLY clearer of the --bootstrap marker; call it when the first draft is finished)
|
|
52
|
-
# feature-
|
|
55
|
+
# feature-lanes [--feature-dir <path>] [--set backend|frontend|backend,frontend] [--clear] -> {…, lanes[]}
|
|
56
|
+
# (the declared lanes this feature has a surface in; empty = every declared lane)
|
|
57
|
+
# feature-paths -> {feature, feature_slug, feature_dir, spec_file, backend_plan, frontend_plan, *_exists,
|
|
53
58
|
# design_file/ui_design_file (aliases of the two plans), spec_exists, bootstrap_pending, needs_bootstrap}
|
|
54
59
|
# checklist-read --file <path> -> {exists, total, checked, items[]}
|
|
55
60
|
# checklist-apply --file <path> --results <json>-> {exists, before_checked, after_checked, total, newly_passing[], regressions[]}
|
|
@@ -798,7 +803,10 @@ cmd_lane_resolve() {
|
|
|
798
803
|
while [ $# -gt 0 ]; do
|
|
799
804
|
case "$1" in
|
|
800
805
|
--kind|--id) fwd+=("$1" "$2"); shift 2 ;;
|
|
801
|
-
|
|
806
|
+
# An EMPTY --feature-dir is dropped: a workflow renders the argument whole, and a
|
|
807
|
+
# feature that is not resolved yet means "the repo's declaration as it stands".
|
|
808
|
+
--feature-dir) if [ -n "$2" ]; then fwd+=("--feature-dir" "$2"); fi; shift 2 ;;
|
|
809
|
+
--help|-h) echo "lane-resolve [--kind backend|frontend] [--id <lane>] [--feature-dir <path>]"; return 0 ;;
|
|
802
810
|
*) echo "lane-resolve: unknown arg '$1'" >&2; return 1 ;;
|
|
803
811
|
esac
|
|
804
812
|
done
|
|
@@ -1470,15 +1478,15 @@ _resolve_feature_dir() { # $1 = --feature-dir arg (may be empty), $2 = repo_ro
|
|
|
1470
1478
|
printf '%s' "$fd"
|
|
1471
1479
|
}
|
|
1472
1480
|
|
|
1473
|
-
# Shared reader/mutator: op in {read,init,bootstrap-done,open,activate,close}. Prints one
|
|
1481
|
+
# Shared reader/mutator: op in {read,init,bootstrap-done,lanes,open,activate,close}. Prints one
|
|
1474
1482
|
# JSON object.
|
|
1475
1483
|
# MANY CHANGES MAY BE OPEN ON ONE FEATURE. open_changes[] is the record; active_change is
|
|
1476
1484
|
# the most recently opened OR the one a later stage activated, and is kept correct for
|
|
1477
1485
|
# every stage that still resolves "the" change through it. `force` is accepted and unused
|
|
1478
1486
|
# on open — the guard it overrode is gone — and is kept so existing callers do not have to
|
|
1479
1487
|
# change.
|
|
1480
|
-
_active_version() { # $1 = abs feature_dir, $2 = op, $3 = cr_id (open/close), $4 = force (accepted, inert), $5 = bootstrap ("1", init only)
|
|
1481
|
-
AW_FD="$1" AW_OP="$2" AW_CR="${3:-}" AW_FORCE="${4:-}" AW_BOOT="${5:-}" python3 - <<'PY'
|
|
1488
|
+
_active_version() { # $1 = abs feature_dir, $2 = op, $3 = cr_id (open/close), $4 = force (accepted, inert), $5 = bootstrap ("1", init only), $6 = lanes (lanes only)
|
|
1489
|
+
AW_FD="$1" AW_OP="$2" AW_CR="${3:-}" AW_FORCE="${4:-}" AW_BOOT="${5:-}" AW_LANES="${6:-}" python3 - <<'PY'
|
|
1482
1490
|
import json, os, re, datetime, sys
|
|
1483
1491
|
fd = os.environ["AW_FD"]; op = os.environ["AW_OP"]; cr = os.environ.get("AW_CR", "")
|
|
1484
1492
|
force = os.environ.get("AW_FORCE", "")
|
|
@@ -1529,6 +1537,31 @@ elif op == "init":
|
|
|
1529
1537
|
d["bootstrap_pending"] = True
|
|
1530
1538
|
save(d)
|
|
1531
1539
|
out["created"] = (not existed)
|
|
1540
|
+
elif op == "lanes":
|
|
1541
|
+
# WHICH DECLARED LANES THIS FEATURE HAS A SURFACE IN. Absent means every lane the repo
|
|
1542
|
+
# declares, which is the ordinary case and needs no record. A feature that genuinely has
|
|
1543
|
+
# no part of one lane's surface — a job-only or shared-kernel slice in a repo that also
|
|
1544
|
+
# has a UI — records the kinds it does have, and `lane-resolve --feature-dir` then reports
|
|
1545
|
+
# the other lane as not declared FOR THIS FEATURE, so no phase plans, gates or builds a
|
|
1546
|
+
# surface the feature does not own. `all` removes the record; an empty value only reads.
|
|
1547
|
+
# Refuses to create the file: init is its one creator, and a record with no version
|
|
1548
|
+
# spine would read as a feature nobody specified.
|
|
1549
|
+
want = os.environ.get("AW_LANES", "").strip()
|
|
1550
|
+
if want and not existed:
|
|
1551
|
+
out["ok"] = False
|
|
1552
|
+
out["error"] = "no active_version.json for this feature yet — it is created by specify or adopt"
|
|
1553
|
+
elif want == "all":
|
|
1554
|
+
d.pop("lanes", None)
|
|
1555
|
+
save(d)
|
|
1556
|
+
elif want:
|
|
1557
|
+
kinds = [k.strip() for k in want.split(",") if k.strip()]
|
|
1558
|
+
bad = [k for k in kinds if k not in ("backend", "frontend")]
|
|
1559
|
+
if bad or not kinds:
|
|
1560
|
+
out["ok"] = False
|
|
1561
|
+
out["error"] = "lanes takes backend, frontend or both (comma-separated), or all; got '%s'" % want
|
|
1562
|
+
else:
|
|
1563
|
+
d["lanes"] = [k for k in ("backend", "frontend") if k in kinds]
|
|
1564
|
+
save(d)
|
|
1532
1565
|
elif op == "bootstrap-done":
|
|
1533
1566
|
# THE ONLY THING THAT CLEARS THE MARKER, called by the phase that raised it once the
|
|
1534
1567
|
# first draft is actually finished. It is a separate op, not a side effect of writing
|
|
@@ -1628,6 +1661,7 @@ elif op == "close":
|
|
|
1628
1661
|
out["previous"] = prev; out["closed_change"] = closed
|
|
1629
1662
|
out["open_changes"] = opens; out["open_count"] = len(opens)
|
|
1630
1663
|
out["bootstrap_pending"] = bool(d.get("bootstrap_pending"))
|
|
1664
|
+
out["lanes"] = list(d.get("lanes") or []) # empty = every declared lane
|
|
1631
1665
|
out["has_change"] = bool(out.get("active_change", "")) # clean branch signal for design
|
|
1632
1666
|
# What a close from THIS state would produce. One bump() authority, read by BOTH
|
|
1633
1667
|
# finalize-delta (to write the right number into the Change History rows it appends)
|
|
@@ -1642,6 +1676,33 @@ PY
|
|
|
1642
1676
|
cmd_version_read() { _version_cmd "read" "$@"; }
|
|
1643
1677
|
cmd_version_init() { _version_cmd "init" "$@"; }
|
|
1644
1678
|
cmd_version_bootstrap_done() { _version_cmd "bootstrap-done" "$@"; }
|
|
1679
|
+
|
|
1680
|
+
# The lane kinds this feature has a surface in, recorded in its active_version.json. adopt
|
|
1681
|
+
# records `backend` for a slice that owns no part of a declared frontend's surface, and
|
|
1682
|
+
# `lane-resolve --feature-dir` reads it back. No argument reads; --clear drops the record so
|
|
1683
|
+
# the feature follows every declared lane again.
|
|
1684
|
+
cmd_feature_lanes() {
|
|
1685
|
+
local fd_arg="" lanes=""
|
|
1686
|
+
while [ $# -gt 0 ]; do
|
|
1687
|
+
case "$1" in
|
|
1688
|
+
--feature-dir) fd_arg="$2"; shift 2 ;;
|
|
1689
|
+
--set) lanes="$2"; shift 2 ;;
|
|
1690
|
+
--clear) lanes="all"; shift ;;
|
|
1691
|
+
--help|-h) echo "feature-lanes [--feature-dir <path>] [--set backend|frontend|backend,frontend] [--clear]"; return 0 ;;
|
|
1692
|
+
*) echo "feature-lanes: unknown arg '$1'" >&2; return 1 ;;
|
|
1693
|
+
esac
|
|
1694
|
+
done
|
|
1695
|
+
local repo_root; repo_root=$(get_repo_root)
|
|
1696
|
+
local fd; fd=$(_resolve_feature_dir "$fd_arg" "$repo_root") || { echo "feature-lanes: no feature set and no --feature-dir" >&2; return 1; }
|
|
1697
|
+
local out; out=$(_active_version "$fd" "lanes" "" "" "" "$lanes")
|
|
1698
|
+
printf '%s\n' "$out"
|
|
1699
|
+
# A refused write is a failure the caller has to see: the orchestrator reports a
|
|
1700
|
+
# failed script from stderr, so the reason goes there too.
|
|
1701
|
+
if printf '%s' "$out" | grep -q '"ok": false'; then
|
|
1702
|
+
printf '%s' "$out" | python3 -c 'import json,sys; print("feature-lanes: " + json.load(sys.stdin).get("error", "refused"), file=sys.stderr)'
|
|
1703
|
+
return 1
|
|
1704
|
+
fi
|
|
1705
|
+
}
|
|
1645
1706
|
_version_cmd() { # $1 = op (read|init), then --feature-dir arg
|
|
1646
1707
|
local op="$1"; shift
|
|
1647
1708
|
local fd_arg="" boot=""
|
|
@@ -1676,6 +1737,16 @@ cmd_feature_paths() {
|
|
|
1676
1737
|
fi
|
|
1677
1738
|
[[ "$fd" != /* ]] && fd="$repo_root/$fd"
|
|
1678
1739
|
local feature; feature=$(basename "$fd") # the aggregate name (feature = specs/<Feature>)
|
|
1740
|
+
# feature_slug is the URL-safe form of that name, and it is what the UI stages use
|
|
1741
|
+
# as a route segment and a directory name (app/<slug>/page.tsx). It is derived here
|
|
1742
|
+
# rather than per workflow so every reader gets the same segment for one feature.
|
|
1743
|
+
# CamelCase splits on the case boundary before the non-alphanumeric squeeze, so
|
|
1744
|
+
# OrderManagement and "Order Management" both reach order-management.
|
|
1745
|
+
local feature_slug
|
|
1746
|
+
feature_slug=$(printf '%s' "$feature" \
|
|
1747
|
+
| sed -E 's/([a-z0-9])([A-Z])/\1-\2/g; s/([A-Z]+)([A-Z][a-z])/\1-\2/g' \
|
|
1748
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
1749
|
+
| sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//')
|
|
1679
1750
|
# The master documents of a feature: spec.md (WHAT/WHY) + one LANE PLAN per declared
|
|
1680
1751
|
# lane (each lane's HOW) — backend-plan.md for the service lane, frontend-plan.md for
|
|
1681
1752
|
# the frontend lane. `plan` writes them; nothing else does.
|
|
@@ -1708,8 +1779,8 @@ cmd_feature_paths() {
|
|
|
1708
1779
|
local needs_bootstrap=false
|
|
1709
1780
|
[ "$spec_exists" = false ] && needs_bootstrap=true
|
|
1710
1781
|
[ "$bootstrap_pending" = true ] && needs_bootstrap=true
|
|
1711
|
-
printf '{"feature":"%s","feature_dir":"%s","spec_file":"%s","backend_plan":"%s","frontend_plan":"%s","backend_plan_exists":%s,"frontend_plan_exists":%s,"design_file":"%s","ui_design_file":"%s","spec_exists":%s,"design_exists":%s,"ui_design_exists":%s,"bootstrap_pending":%s,"needs_bootstrap":%s}\n' \
|
|
1712
|
-
"$(json_escape "$feature")" "$(json_escape "$fd")" "$(json_escape "$spec")" \
|
|
1782
|
+
printf '{"feature":"%s","feature_slug":"%s","feature_dir":"%s","spec_file":"%s","backend_plan":"%s","frontend_plan":"%s","backend_plan_exists":%s,"frontend_plan_exists":%s,"design_file":"%s","ui_design_file":"%s","spec_exists":%s,"design_exists":%s,"ui_design_exists":%s,"bootstrap_pending":%s,"needs_bootstrap":%s}\n' \
|
|
1783
|
+
"$(json_escape "$feature")" "$(json_escape "$feature_slug")" "$(json_escape "$fd")" "$(json_escape "$spec")" \
|
|
1713
1784
|
"$(json_escape "$design")" "$(json_escape "$ui_design")" "$design_exists" "$ui_design_exists" \
|
|
1714
1785
|
"$(json_escape "$design")" "$(json_escape "$ui_design")" "$spec_exists" "$design_exists" "$ui_design_exists" \
|
|
1715
1786
|
"$bootstrap_pending" "$needs_bootstrap"
|
|
@@ -4987,6 +5058,7 @@ case "$sub" in
|
|
|
4987
5058
|
version-read) cmd_version_read "$@" ;;
|
|
4988
5059
|
version-init) cmd_version_init "$@" ;;
|
|
4989
5060
|
version-bootstrap-done) cmd_version_bootstrap_done "$@" ;;
|
|
5061
|
+
feature-lanes) cmd_feature_lanes "$@" ;;
|
|
4990
5062
|
feature-paths) cmd_feature_paths "$@" ;;
|
|
4991
5063
|
design-scaffold) cmd_design_scaffold "$@" ;;
|
|
4992
5064
|
# The plan phase's name for the same op. Both spellings reach one implementation:
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# ONCE in ..\python\aw-rev-constitution.py; this wrapper exists only so the
|
|
6
6
|
# orchestrator's execute_script (sh:/ps1: dispatch) can invoke it. Args pass through.
|
|
7
7
|
#
|
|
8
|
-
# aw-rev-constitution.ps1
|
|
8
|
+
# aw-rev-constitution.ps1 project-context [--root <dir>] [--bundle <dir>]
|
|
9
9
|
# aw-rev-constitution.ps1 project-setup --name <domain> [--fallback <name>] [--root <dir>]
|
|
10
10
|
$ErrorActionPreference = "Stop"
|
|
11
11
|
$core = Join-Path $PSScriptRoot "..\python\aw-rev-constitution.py"
|