aw-spec-cli 0.1.23 → 0.1.25
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 +92 -14
- 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-inventory.orch.yaml +55 -29
- 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-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-ui-model.orch.yaml +13 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-prototype.orch.yaml +40 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-workflow-model.orch.yaml +2 -1
- package/assets/.appweaver/scripts/bash/aw-rev-constitution.sh +1 -1
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +69 -7
- package/assets/.appweaver/scripts/powershell/aw-rev-constitution.ps1 +1 -1
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +58 -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/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}}"
|
|
@@ -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."
|
|
@@ -110,10 +110,14 @@ workflow:
|
|
|
110
110
|
vars:
|
|
111
111
|
feature_name: "{{feature}}"
|
|
112
112
|
feature_dir: "{{feature_dir}}"
|
|
113
|
+
domain: "{{domain}}"
|
|
113
114
|
user_preferences: "{{USER_INPUT}}"
|
|
114
115
|
context:
|
|
115
116
|
- "{{feature_dir}}/ui/workflow-model.md"
|
|
116
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 }
|
|
117
121
|
- { path: ".appweaver/templates/ui-model-template.md", optional: true }
|
|
118
122
|
- { path: ".appweaver/memory/constitution.ui.md", optional: true }
|
|
119
123
|
|
|
@@ -135,9 +139,15 @@ workflow:
|
|
|
135
139
|
- Define supported interaction states per surface (`Loading`, `Empty`, `Populated`, `Filtered / Searched`, `Validation Error`, `Submitting`, `Success`, `Error`).
|
|
136
140
|
- Specify interactive controls, form fields, action buttons, and table/card layouts.
|
|
137
141
|
- Map explicit navigation & transition triggers (`On Action -> Opens Surface X`).
|
|
138
|
-
3.
|
|
139
|
-
|
|
140
|
-
|
|
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`.
|
|
141
151
|
|
|
142
152
|
User Guidance:
|
|
143
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."
|
|
@@ -123,8 +123,9 @@ workflow:
|
|
|
123
123
|
1. Read the feature specification (`spec.md` + active `delta-spec.md`) in context.
|
|
124
124
|
2. Identify ALL discrete user workflows required to fulfill the feature's capabilities, functional requirements (FRs), business rules (BRs), and user personas.
|
|
125
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").
|
|
126
127
|
- **Actor / Persona**: Which role executes this flow (e.g. Project Manager, Delivery Head).
|
|
127
|
-
- **Goal**: Clear statement of user goal.
|
|
128
|
+
- **Goal**: Clear statement of user goal in natural language.
|
|
128
129
|
- **Preconditions**: Initial system state, permissions, or prerequisite conditions.
|
|
129
130
|
- **Primary Flow**: Step-by-step numbered interaction steps from start to completion.
|
|
130
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,6 +52,8 @@
|
|
|
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)
|
|
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)
|
|
52
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[]}
|
|
@@ -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=""
|
|
@@ -4997,6 +5058,7 @@ case "$sub" in
|
|
|
4997
5058
|
version-read) cmd_version_read "$@" ;;
|
|
4998
5059
|
version-init) cmd_version_init "$@" ;;
|
|
4999
5060
|
version-bootstrap-done) cmd_version_bootstrap_done "$@" ;;
|
|
5061
|
+
feature-lanes) cmd_feature_lanes "$@" ;;
|
|
5000
5062
|
feature-paths) cmd_feature_paths "$@" ;;
|
|
5001
5063
|
design-scaffold) cmd_design_scaffold "$@" ;;
|
|
5002
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"
|
|
@@ -631,6 +631,9 @@ function Invoke-LaneResolve {
|
|
|
631
631
|
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
632
632
|
switch ($Rest[$i]) {
|
|
633
633
|
{ $_ -in '--kind','--id' } { $fwd += $Rest[$i]; $fwd += $Rest[++$i] }
|
|
634
|
+
# An EMPTY --feature-dir is dropped: a workflow renders the argument whole, and a
|
|
635
|
+
# feature that is not resolved yet means "the repo's declaration as it stands".
|
|
636
|
+
'--feature-dir' { $v = $Rest[++$i]; if ($v) { $fwd += '--feature-dir'; $fwd += $v } }
|
|
634
637
|
default { Write-Error "lane-resolve: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
635
638
|
}
|
|
636
639
|
}
|
|
@@ -1084,6 +1087,31 @@ elif op == "init":
|
|
|
1084
1087
|
d["bootstrap_pending"] = True
|
|
1085
1088
|
save(d)
|
|
1086
1089
|
out["created"] = (not existed)
|
|
1090
|
+
elif op == "lanes":
|
|
1091
|
+
# WHICH DECLARED LANES THIS FEATURE HAS A SURFACE IN. Absent means every lane the repo
|
|
1092
|
+
# declares, which is the ordinary case and needs no record. A feature that genuinely has
|
|
1093
|
+
# no part of one lane's surface — a job-only or shared-kernel slice in a repo that also
|
|
1094
|
+
# has a UI — records the kinds it does have, and `lane-resolve --feature-dir` then reports
|
|
1095
|
+
# the other lane as not declared FOR THIS FEATURE, so no phase plans, gates or builds a
|
|
1096
|
+
# surface the feature does not own. `all` removes the record; an empty value only reads.
|
|
1097
|
+
# Refuses to create the file: init is its one creator, and a record with no version
|
|
1098
|
+
# spine would read as a feature nobody specified.
|
|
1099
|
+
want = os.environ.get("AW_LANES", "").strip()
|
|
1100
|
+
if want and not existed:
|
|
1101
|
+
out["ok"] = False
|
|
1102
|
+
out["error"] = "no active_version.json for this feature yet — it is created by specify or adopt"
|
|
1103
|
+
elif want == "all":
|
|
1104
|
+
d.pop("lanes", None)
|
|
1105
|
+
save(d)
|
|
1106
|
+
elif want:
|
|
1107
|
+
kinds = [k.strip() for k in want.split(",") if k.strip()]
|
|
1108
|
+
bad = [k for k in kinds if k not in ("backend", "frontend")]
|
|
1109
|
+
if bad or not kinds:
|
|
1110
|
+
out["ok"] = False
|
|
1111
|
+
out["error"] = "lanes takes backend, frontend or both (comma-separated), or all; got '%s'" % want
|
|
1112
|
+
else:
|
|
1113
|
+
d["lanes"] = [k for k in ("backend", "frontend") if k in kinds]
|
|
1114
|
+
save(d)
|
|
1087
1115
|
elif op == "bootstrap-done":
|
|
1088
1116
|
# THE ONLY THING THAT CLEARS THE MARKER, called by the phase that raised it once the
|
|
1089
1117
|
# first draft is actually finished. It is a separate op, not a side effect of writing
|
|
@@ -1183,6 +1211,7 @@ elif op == "close":
|
|
|
1183
1211
|
out["previous"] = prev; out["closed_change"] = closed
|
|
1184
1212
|
out["open_changes"] = opens; out["open_count"] = len(opens)
|
|
1185
1213
|
out["bootstrap_pending"] = bool(d.get("bootstrap_pending"))
|
|
1214
|
+
out["lanes"] = list(d.get("lanes") or []) # empty = every declared lane
|
|
1186
1215
|
out["has_change"] = bool(out.get("active_change", "")) # clean branch signal for design
|
|
1187
1216
|
# What a close from THIS state would produce. One bump() authority, read by BOTH
|
|
1188
1217
|
# finalize-delta (to write the right number into the Change History rows it appends)
|
|
@@ -1194,10 +1223,10 @@ print(json.dumps(out))
|
|
|
1194
1223
|
'@
|
|
1195
1224
|
|
|
1196
1225
|
function Invoke-ActiveVersion {
|
|
1197
|
-
param([string]$FeatureDir, [string]$Op, [string]$CrId = '', [string]$Force = '', [string]$Boot = '')
|
|
1226
|
+
param([string]$FeatureDir, [string]$Op, [string]$CrId = '', [string]$Force = '', [string]$Boot = '', [string]$Lanes = '')
|
|
1198
1227
|
$py = Get-Py; if (-not $py) { Write-Error "version-$Op`: python3 required"; exit 1 }
|
|
1199
1228
|
$env:AW_FD = $FeatureDir; $env:AW_OP = $Op; $env:AW_CR = $CrId; $env:AW_FORCE = $Force
|
|
1200
|
-
$env:AW_BOOT = $Boot
|
|
1229
|
+
$env:AW_BOOT = $Boot; $env:AW_LANES = $Lanes
|
|
1201
1230
|
# CrId names the change on BOTH open and close: many may be open at once, so a close
|
|
1202
1231
|
# has to say which one it is closing rather than assuming the only one.
|
|
1203
1232
|
$PyActiveVersion | & $py -
|
|
@@ -1216,6 +1245,32 @@ function Invoke-VersionState { # op in read|init (feature-dir only)
|
|
|
1216
1245
|
Invoke-ActiveVersion -FeatureDir $fd -Op $Op -Boot $boot
|
|
1217
1246
|
}
|
|
1218
1247
|
|
|
1248
|
+
# The lane kinds this feature has a surface in, recorded in its active_version.json. Twin
|
|
1249
|
+
# of bash cmd_feature_lanes: adopt records `backend` for a slice that owns no part of a
|
|
1250
|
+
# declared frontend's surface, and `lane-resolve --feature-dir` reads it back. No argument
|
|
1251
|
+
# reads; --clear drops the record so the feature follows every declared lane again.
|
|
1252
|
+
function Invoke-FeatureLanes {
|
|
1253
|
+
param([string[]]$Rest)
|
|
1254
|
+
$fdArg = ''; $lanes = ''
|
|
1255
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
1256
|
+
switch ($Rest[$i]) {
|
|
1257
|
+
{ $_ -in '--feature-dir','-FeatureDir' } { $fdArg = $Rest[++$i] }
|
|
1258
|
+
'--set' { $lanes = $Rest[++$i] }
|
|
1259
|
+
'--clear' { $lanes = 'all' }
|
|
1260
|
+
default { Write-Error "feature-lanes: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
$repo = Get-RepoRoot
|
|
1264
|
+
$fd = Resolve-FeatureDir -Arg $fdArg -RepoRoot $repo
|
|
1265
|
+
if (-not $fd) { Write-Error 'feature-lanes: no feature set and no --feature-dir'; exit 1 }
|
|
1266
|
+
$out = Invoke-ActiveVersion -FeatureDir $fd -Op 'lanes' -Lanes $lanes
|
|
1267
|
+
$out
|
|
1268
|
+
# A refused write is a failure the caller has to see: the orchestrator reports a failed
|
|
1269
|
+
# script from stderr, so the reason goes there too.
|
|
1270
|
+
$res = $out | ConvertFrom-Json
|
|
1271
|
+
if (-not $res.ok) { [Console]::Error.WriteLine("feature-lanes: $($res.error)"); exit 1 }
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1219
1274
|
|
|
1220
1275
|
|
|
1221
1276
|
# ─────────────────────────── feature-paths ─────────────────────────────────
|
|
@@ -4403,6 +4458,7 @@ switch ($sub) {
|
|
|
4403
4458
|
'version-read' { Invoke-VersionState 'read' $rest }
|
|
4404
4459
|
'version-init' { Invoke-VersionState 'init' $rest }
|
|
4405
4460
|
'version-bootstrap-done' { Invoke-VersionState 'bootstrap-done' $rest }
|
|
4461
|
+
'feature-lanes' { Invoke-FeatureLanes -Rest $rest }
|
|
4406
4462
|
'feature-paths' { Invoke-FeaturePaths -Rest $rest }
|
|
4407
4463
|
'design-scaffold' { Invoke-DesignScaffold -Rest $rest }
|
|
4408
4464
|
# The plan phase's name for the same op. Both spellings reach one implementation:
|