aw-spec-cli 0.1.16 → 0.1.17

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 (70) hide show
  1. package/assets/.appweaver/DESIGN-FORMAT.md +18 -18
  2. package/assets/.appweaver/ORCH-DIALECT.md +1 -3
  3. package/assets/.appweaver/RULE-PARTITION.md +29 -13
  4. package/assets/.appweaver/STACK-PROFILES.md +10 -9
  5. package/assets/.appweaver/orchestrator/.state/smoke.state.json +32 -0
  6. package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +4 -1
  7. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +251 -0
  8. package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +104 -35
  9. package/assets/.appweaver/orchestrator/orchestrator.py +5 -4
  10. package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +94 -36
  11. package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +1 -1
  12. package/assets/.appweaver/orchestrator/rev-templates/domain-model-template.skeleton.md +115 -0
  13. package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +2 -9
  14. package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +16 -40
  15. package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +38 -17
  16. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +8 -8
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +10 -11
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +673 -0
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +535 -0
  20. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +643 -0
  21. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +84 -701
  22. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
  23. package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +7 -7
  24. package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +16 -14
  25. package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +2 -2
  26. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +243 -144
  27. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +877 -578
  28. package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +4 -4
  29. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +32 -91
  30. package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
  31. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +836 -514
  32. package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +2 -2
  33. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +132 -0
  34. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +38 -5
  35. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +3 -3
  36. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +271 -979
  37. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +1085 -3385
  38. package/assets/.appweaver/scripts/bash/aw-xref.sh +1 -2
  39. package/assets/.appweaver/scripts/bash/common.sh +47 -0
  40. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +914 -3220
  41. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +1 -2
  42. package/assets/.appweaver/scripts/python/aw-payload-parity.py +19 -3
  43. package/assets/.appweaver/scripts/python/aw-stack.py +6 -2
  44. package/assets/.appweaver/scripts/python/aw-xref.py +86 -350
  45. package/assets/.appweaver/templates/constitution-ui-template.md +1 -1
  46. package/assets/.appweaver/templates/delta-design-template.md +120 -30
  47. package/assets/.appweaver/templates/delta-domain-model-template.md +81 -0
  48. package/assets/.appweaver/templates/delta-spec-template.md +43 -17
  49. package/assets/.appweaver/templates/design-template.md +68 -98
  50. package/assets/.appweaver/templates/domain-model-template.md +130 -0
  51. package/assets/.appweaver/templates/okf-concept-template.md +2 -2
  52. package/assets/.appweaver/templates/source-map-template.md +3 -3
  53. package/assets/.appweaver/templates/spec-template.md +100 -122
  54. package/assets/.appweaver/templates/ui-delta-design-template.md +159 -0
  55. package/assets/.appweaver/templates/ui-design-template.md +46 -75
  56. package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +7 -18
  57. package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +34 -39
  58. package/assets/guardrails/requirement-spec.txt +2 -2
  59. package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
  60. package/package.json +1 -1
  61. package/assets/.appweaver/orchestrator/modules/delta-design.orch.yaml +0 -105
  62. package/assets/.appweaver/orchestrator/modules/spec-state-gate.orch.yaml +0 -87
  63. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-delta-design.orch.yaml +0 -251
  64. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +0 -813
  65. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +0 -253
  66. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +0 -663
  67. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +0 -600
  68. package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +0 -1147
  69. package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +0 -1121
  70. package/assets/.appweaver/templates/story-template.md +0 -178
@@ -76,7 +76,7 @@ workflow:
76
76
  with: { require: backend, command: "/appweaver run" }
77
77
 
78
78
  # The slice's source files, from its OKF concept — what the migration fix loop below is
79
- # handed. Replaces the retired `design-paths` fence scan (the tech-agnostic design.md
79
+ # handed. Replaces the retired `design-paths` fence scan (the tech-agnostic backend-plan.md
80
80
  # carries no ```lang path=… code blocks, so it returned nothing and the fixer got an
81
81
  # EMPTY context). Read-only, exits 0 with paths=[] when there is no concept yet, and
82
82
  # on_error: continue so a missing bundle can never block a migration.
@@ -144,7 +144,7 @@ workflow:
144
144
 
145
145
  # Record what the migration fix loop touched in the slice's OKF concept. Replaces the
146
146
  # retired `design-reconcile` (which copied file bodies back into design fences — a
147
- # silent no-op since design.md stopped carrying code). The fix loop can CREATE files
147
+ # silent no-op since backend-plan.md stopped carrying code). The fix loop can CREATE files
148
148
  # (a new migration, a config) that implement never recorded. UNION (--merge), never
149
149
  # replace. Advisory: conditioned + on_error: continue, never gated.
150
150
  - id: okf_merge_run_fixes
@@ -0,0 +1,132 @@
1
+ # =============================================================================
2
+ # aw-spec-select-bounded-context (orchestrator) — pick or create the active
3
+ # BOUNDED CONTEXT
4
+ #
5
+ # A bounded context is the container a feature lives in: specs/<Context>/<Feature>/.
6
+ # It sits between the domain (which project's rules apply) and the feature (which
7
+ # aggregate is being worked on), and it owns ONE domain-model.md that every spec
8
+ # inside it references. That is why it is selected BEFORE select-feature — the
9
+ # feature directory cannot be resolved until the context that holds it is known.
10
+ #
11
+ # Contexts and features are told apart by the only reliable evidence on disk: a
12
+ # directory under specs/ carrying its OWN spec.md is a feature, one that does not is
13
+ # a context. No marker file, so nothing can fall out of sync with the tree.
14
+ #
15
+ # BACKWARD COMPATIBLE BY CONSTRUCTION. A repo that predates bounded contexts has its
16
+ # features flat under specs/ and no context.json; every caller falls back to the flat
17
+ # layout when no context is active, so the tree keeps working untouched and adopts
18
+ # contexts when someone runs this command. The flat features are reported as
19
+ # `legacy_features` rather than being hidden, so an un-migrated repo says so.
20
+ #
21
+ # ZERO LLM: scripts + one select + (conditionally) one ask. Same shape as
22
+ # select-feature, one level up.
23
+ #
24
+ # list-contexts → user_select (existing | "+ create new") → [ask name] → set-context
25
+ #
26
+ # Persists .appweaver/context.json {"context":"<Name>","context_directory":"specs/<Name>"}.
27
+ #
28
+ # Launch:
29
+ # /appweaver select-bounded-context # interactive: list + add-new
30
+ # /appweaver select-bounded-context Sales # direct: create-or-select Sales
31
+ # =============================================================================
32
+
33
+ name: aw-spec-select-bounded-context
34
+ description: Pick an existing bounded context or create a new one, and store it in .appweaver/context.json so select-feature nests its features inside it and every spec in the context shares one domain-model.md. Zero LLM — scripts + one selection only.
35
+ version: 1.0.0
36
+ agent_agnostic: true
37
+ dialect: aw-spec.orchestrator/v1
38
+
39
+ inputs:
40
+ user_input:
41
+ description: Optional bounded context name to create or select directly, e.g. Sales.
42
+ required: false
43
+ bind: USER_INPUT
44
+
45
+ scripts:
46
+ actions:
47
+ sh: .appweaver/scripts/bash/aw-spec-actions.sh
48
+ ps1: .appweaver/scripts/powershell/aw-spec-actions.ps1
49
+
50
+ vars:
51
+ new_sentinel: "+ Create a new bounded context" # source of truth is list-contexts $.sentinel; overwritten below
52
+ typed_name: ""
53
+ target_name: ""
54
+
55
+ workflow:
56
+
57
+ # 1. Enumerate the bounded contexts under specs/. `select_options` carries the
58
+ # "+ create new" sentinel and (when USER_INPUT names a context that does not
59
+ # exist yet) that name injected — so a direct argument is one-list select with
60
+ # no extra round trip, matching select-feature's fast path.
61
+ - id: list_contexts
62
+ action: execute_script
63
+ script: { ref: actions }
64
+ subcommand: list-contexts
65
+ args: ["--preselect", "{{USER_INPUT}}"]
66
+ outputs:
67
+ - { name: contexts, from: "$.contexts" }
68
+ - { name: context_names, from: "$.names" }
69
+ - { name: context_count, from: "$.count" }
70
+ - { name: select_options, from: "$.select_options" }
71
+ - { name: new_sentinel, from: "$.sentinel" }
72
+ - { name: legacy_features, from: "$.legacy_features" }
73
+ - { name: legacy_count, from: "$.legacy_count" }
74
+
75
+ # 2. Choose. A matching USER_INPUT auto-selects with no prompt; the lone sentinel
76
+ # on a repo with no contexts yet auto-confirms.
77
+ - id: pick
78
+ action: user_select
79
+ prompt: "Select the bounded context (e.g. Sales, Billing) — or create a new one:"
80
+ options_from_variable: select_options
81
+ preselect_from: USER_INPUT
82
+ single_option_confirm: true
83
+ outputs:
84
+ - { name: chosen, from: selection }
85
+
86
+ # 3. Only on the "create new" branch do we ask for a name.
87
+ - id: ask_new_name
88
+ action: ask
89
+ condition: "{{chosen}} == {{new_sentinel}}"
90
+ prompt: >
91
+ Name the new bounded context — this is a BOUNDARY in the domain (e.g. Sales,
92
+ Billing, Fulfilment), not a feature and not a layer. Aggregates like Order or
93
+ Invoice become features inside it, and every one of them shares this context's
94
+ single domain-model.md:
95
+ answer_var: typed_name
96
+
97
+ # 4. Coalesce the target name. The engine has no compound conditions, so this is
98
+ # two ordered sets: default to the pick, then override with the typed name on
99
+ # the "create new" branch only.
100
+ - id: name_from_pick
101
+ action: set
102
+ vars: { target_name: "{{chosen}}" }
103
+ - id: name_from_new
104
+ action: set
105
+ condition: "{{chosen}} == {{new_sentinel}}"
106
+ vars: { target_name: "{{typed_name}}" }
107
+
108
+ # 5. Create-or-select the context dir + persist it as the active context.
109
+ # domain-model.md is deliberately NOT created here — specify is the model's only
110
+ # writer, and an empty placeholder would read as "the model exists" downstream.
111
+ - id: set_context
112
+ action: execute_script
113
+ script: { ref: actions }
114
+ subcommand: set-context
115
+ args: ["--context", "{{target_name}}"]
116
+ outputs:
117
+ - { name: context, from: "$.context" }
118
+ - { name: context_dir, from: "$.context_dir" }
119
+ - { name: domain_model, from: "$.domain_model" }
120
+ - { name: existed, from: "$.existed" }
121
+ - { name: created, from: "$.created" }
122
+ - { name: domain_model_exists, from: "$.domain_model_exists" }
123
+
124
+ outputs:
125
+ context: "{{context}}"
126
+ context_dir: "{{context_dir}}"
127
+ domain_model: "{{domain_model}}"
128
+ domain_model_exists: "{{domain_model_exists}}"
129
+ created: "{{created}}"
130
+ legacy_features: "{{legacy_features}}"
131
+ note: "Active bounded context set in .appweaver/context.json. select-feature now creates and lists features inside {{context_dir}}/, and every spec there shares {{domain_model}} as its domain model. Features already sitting flat under specs/ are listed as legacy_features — they keep working exactly as before and are not moved; re-run select-feature after selecting a context to place new work inside it."
132
+ next: "/appweaver select-feature (creates {{context_dir}}/<Feature>/, then specify)"
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # feature = AGGREGATE (e.g. Order). This is the FIRST step of the spec-anchored
5
5
  # pipeline: it fixes the aggregate boundary BEFORE any spec/design exists, so the
6
- # feature's single spec.md + its per-lane plans and every command (CreateOrder/UpdateOrder =
6
+ # feature's single spec.md + backend-plan.md and every command (CreateOrder/UpdateOrder =
7
7
  # user stories) hang off one directory. It replaces guessing the aggregate out of prose in
8
8
  # `specify` — that is why finding #1 (feature=aggregate) is enforced here, not hoped for.
9
9
  #
@@ -13,14 +13,20 @@
13
13
  # brownfield repo you pick a real discovered code feature BEFORE anything is under
14
14
  # specs/; on greenfield (no bundle) the list is specs-only, exactly as before.
15
15
  #
16
+ # SCOPED BY THE ACTIVE BOUNDED CONTEXT. When .appweaver/context.json names one, both
17
+ # the listing and the creation happen inside specs/<Context>/ — so a context shows
18
+ # only its own features and new work lands beside the domain-model.md it shares. With
19
+ # no context active the base is specs/ itself and behaviour is identical to before
20
+ # contexts existed, which is what lets a repo adopt them without a migration.
21
+ #
16
22
  # ZERO LLM: only scripts + one select + (conditionally) one ask. No generation,
17
23
  # no standards, no domain-select. Pure, deterministic orchestration/governance.
18
24
  #
19
25
  # list-features (specs/ ∪ OKF aggregates) → user_select (existing | "+ create new") → [ask name] → set-feature
20
26
  #
21
27
  # Persists .appweaver/feature.json {"feature_directory":"specs/<Feature>"} so every
22
- # later command operates on it: the forward loop (specify/plan/analyze-design/implement/
23
- # build/analyze-slice/run/deploy) and the CR loop (analyze-delta-design/implement-delta/
28
+ # later command operates on it: the forward loop (specify/plan/analyze-plan/implement/
29
+ # build/analyze-slice/run/deploy) and the CR loop (analyze-plan-delta/implement/
24
30
  # finalize-delta/close-delta — the standalone `cr` is retired; specify and design author
25
31
  # the delta docs themselves).
26
32
  #
@@ -54,6 +60,31 @@ vars:
54
60
 
55
61
  workflow:
56
62
 
63
+ # 0. Resolve the bounded context this feature will live in, and REFUSE to guess.
64
+ # scope_ok is false in exactly one state: contexts exist and none is selected.
65
+ # Left to run, the feature would be created at specs/<Feature> — outside every
66
+ # context and with no domain model to share — and nothing downstream would say
67
+ # so. A repo with no contexts at all passes (the flat layout is a valid state),
68
+ # which is what keeps a pre-context tree working untouched.
69
+ - id: context_scope
70
+ action: execute_script
71
+ script: { ref: actions }
72
+ subcommand: list-contexts
73
+ outputs:
74
+ - { name: active_context, from: "$.active" }
75
+ - { name: context_dir, from: "$.active_dir" }
76
+ - { name: feature_base, from: "$.feature_base" }
77
+ - { name: context_count, from: "$.count" }
78
+ - { name: scope_ok, from: "$.scope_ok" }
79
+ - { name: scope_message, from: "$.scope_message" }
80
+
81
+ - id: require_context_scope
82
+ action: verify_context
83
+ require_true: scope_ok
84
+ on_false: stop
85
+ on_false_message: >
86
+ {{scope_message}}
87
+
57
88
  # 1. Enumerate selectable aggregates from TWO merged sources (deduped by name):
58
89
  # specs/ sub-folders (already scaffolded/adopted) AND the OKF bundle's
59
90
  # Aggregate concepts — the brownfield feature map onboard reverse-engineers,
@@ -77,7 +108,7 @@ workflow:
77
108
  # no prompt; the lone sentinel at bootstrap (no features yet) auto-confirms.
78
109
  - id: pick
79
110
  action: user_select
80
- prompt: "Select a feature (the aggregate, e.g. Order) — or create a new one:"
111
+ prompt: "Select a feature (the aggregate, e.g. Order) in {{feature_base}}/ — or create a new one:"
81
112
  options_from_variable: select_options
82
113
  preselect_from: USER_INPUT
83
114
  single_option_confirm: true
@@ -118,11 +149,13 @@ workflow:
118
149
  - { name: existed, from: "$.existed" }
119
150
  - { name: created, from: "$.created" }
120
151
  - { name: spec_exists, from: "$.spec_exists" }
152
+ - { name: context, from: "$.context" }
121
153
 
122
154
  outputs:
123
155
  feature: "{{feature}}"
124
156
  feature_dir: "{{feature_dir}}"
157
+ context: "{{context}}"
125
158
  created: "{{created}}"
126
159
  spec_exists: "{{spec_exists}}"
127
- note: "Active feature set in .appweaver/feature.json — the aggregate boundary for its single spec.md and the per-lane plans derived from it (backend-plan.md / frontend-plan.md)."
160
+ note: "Active feature set in .appweaver/feature.json — the aggregate boundary for its single spec.md + backend-plan.md. `context` names the bounded context it was created in, and is empty on a repo with no active context (the flat specs/<Feature> layout, which keeps working unchanged)."
128
161
  next: "/appweaver specify <what this feature does> (authors {{feature_dir}}/spec.md; each operation = a user story)"
@@ -30,7 +30,7 @@
30
30
  # and `lane-verify` to gate, and neither can write.
31
31
  #
32
32
  # WHY THE SOURCE PATH IS ASKED AND NOT INHERITED. A lane's root is the subtree it
33
- # OWNS: the only place its implement phase may write, the directory its build and
33
+ # OWNS: the only place implement's lane for it may write, the directory its build and
34
34
  # run commands act on, and in brownfield the code onboard reads. A profile's default
35
35
  # root describes how a NEW project of that stack is usually laid out, which is a
36
36
  # reasonable proposal and a poor assumption — a repo that already exists did not
@@ -203,7 +203,7 @@ workflow:
203
203
  - id: ask_ui_source_path
204
204
  action: ask
205
205
  condition: "{{ui_selected}}"
206
- prompt: "UI lane SOURCE PATH — the subtree this lane owns, the only place ui-implement may write. It must not overlap the service lane's. Pick one or type your own:"
206
+ prompt: "UI lane SOURCE PATH — the subtree this lane owns, the only place implement's frontend lane may write. It must not overlap the service lane's. Pick one or type your own:"
207
207
  options_from_variable: ui_root_options
208
208
  answer_var: ui_source_path
209
209
 
@@ -292,5 +292,5 @@ outputs:
292
292
  api_detected: "{{api_detected}} at {{api_detected_root}}"
293
293
  ui_detected: "{{ui_detected}} at {{ui_detected_root}}"
294
294
  prototype: "{{prototype}}"
295
- 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 writes only under the service lane's, ui-implement only under the UI lane's, build/test/run act on it, and onboard reverse-engineers out of it — overlapping paths 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 — empty 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, implement-delta, 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."
295
+ 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 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 — overlapping paths 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 — empty 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."
296
296
  next: "GREENFIELD: /appweaver select-domain → constitution → select-feature → specify. BROWNFIELD: /appweaver onboard — it reverse-engineers out of the source paths declared here and generates the project (no select-domain; onboard derives and activates it from the code). Run this workflow FIRST in either sequence: constitution reads the declaration to decide which halves to author, and onboard reads it to decide what to scan."