aw-spec-cli 0.1.19 → 0.1.21

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 (75) hide show
  1. package/assets/.appweaver/DESIGN-FORMAT.md +17 -9
  2. package/assets/.appweaver/ORCH-DIALECT.md +8 -3
  3. package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +26 -3
  4. package/assets/.appweaver/orchestrator/orchestrator.py +65 -3
  5. package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +1 -1
  6. package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +4 -2
  7. package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +1 -1
  8. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +1 -1
  9. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +121 -42
  10. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +7 -7
  11. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +145 -52
  12. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +18 -10
  13. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +306 -28
  14. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +1 -1
  15. package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +1 -1
  16. package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +1 -1
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +4 -4
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-deploy.orch.yaml +1 -1
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +114 -30
  20. package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +140 -36
  21. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +385 -61
  22. package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +217 -174
  23. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +407 -107
  24. package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +1 -1
  25. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +49 -11
  26. package/assets/.appweaver/orchestrator/workflows/aw-spec-recall-delta.orch.yaml +1 -1
  27. package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +1 -1
  28. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +29 -23
  29. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +47 -23
  30. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +19 -11
  31. package/assets/.appweaver/scripts/bash/aw-inventory.sh +15 -0
  32. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +683 -92
  33. package/assets/.appweaver/scripts/bash/aw-xref.sh +3 -0
  34. package/assets/.appweaver/scripts/powershell/aw-inventory.ps1 +15 -0
  35. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +625 -64
  36. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +3 -0
  37. package/assets/.appweaver/scripts/python/aw-crstate.py +194 -0
  38. package/assets/.appweaver/scripts/python/aw-foundation.py +151 -0
  39. package/assets/.appweaver/scripts/python/aw-inventory.py +1829 -63
  40. package/assets/.appweaver/scripts/python/aw-payload-parity.py +3 -0
  41. package/assets/.appweaver/scripts/python/aw-plan-coverage.py +423 -0
  42. package/assets/.appweaver/scripts/python/aw-stack.py +251 -28
  43. package/assets/.appweaver/scripts/python/aw-testlog.py +148 -0
  44. package/assets/.appweaver/scripts/python/aw-xref.py +500 -81
  45. package/assets/.appweaver/stack-profiles/dotnet-webapi.json +1 -0
  46. package/assets/.appweaver/stack-profiles/java-spring.json +1 -0
  47. package/assets/.appweaver/stack-profiles/node-express.json +1 -0
  48. package/assets/.appweaver/stack-profiles/python-fastapi.json +1 -0
  49. package/assets/.appweaver/templates/constitution-domain-template.md +17 -2
  50. package/assets/.appweaver/templates/delta-design-template.md +1 -1
  51. package/assets/.appweaver/templates/delta-domain-model-template.md +1 -1
  52. package/assets/.appweaver/templates/delta-spec-template.md +4 -3
  53. package/assets/.appweaver/templates/design-template.md +16 -11
  54. package/assets/.appweaver/templates/okf-concept-template.md +11 -1
  55. package/assets/.appweaver/templates/proposed-inventory-template.md +127 -0
  56. package/assets/.appweaver/templates/source-map-template.md +5 -3
  57. package/assets/.appweaver/templates/spec-template.md +17 -17
  58. package/assets/.appweaver/templates/ui-delta-design-template.md +1 -1
  59. package/assets/.appweaver/templates/ui-design-template.md +4 -4
  60. package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.pack.txt +4489 -361
  61. package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.pack.txt +2274 -125
  62. package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +10 -6
  63. package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +4 -0
  64. package/assets/guardrails/technology/dotnet-webapi/auditing.md +3 -1
  65. package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +5 -3
  66. package/assets/guardrails/technology/dotnet-webapi/cqrs.md +5 -1
  67. package/assets/guardrails/technology/dotnet-webapi/domain-driven-design.md +13 -5
  68. package/assets/guardrails/technology/dotnet-webapi/examples/cqrs.md +2 -0
  69. package/assets/guardrails/technology/dotnet-webapi/examples/domain-driven-design.md +6 -2
  70. package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +70 -0
  71. package/assets/guardrails/technology/dotnet-webapi/technology-constitution.md +3 -0
  72. package/assets/guardrails/technology/dotnet-webapi/testing.md +10 -0
  73. package/assets/guardrails/technology/nextjs/ui-constitution.md +8 -1
  74. package/package.json +1 -1
  75. package/assets/.appweaver/orchestrator/.state/smoke.state.json +0 -32
@@ -1,37 +1,52 @@
1
1
  # =============================================================================
2
- # aw-spec-inventory (orchestrator) — B0 of the Brownfield Pipeline
2
+ # aw-spec-inventory (orchestrator) — brownfield discovery
3
3
  #
4
- # The reverse front-end's first phase (see Brownfield-Pipeline.md). Scan
5
- # an EXISTING codebase, propose AGGREGATE-ROOT boundaries + dependency seams,
6
- # and emit an aggregate-organized OKF v0.1 knowledge bundle + a migration
7
- # backlog. Each candidate aggregate is a future specs/<Feature>/ vertical slice —
8
- # this bundle is the BASE the forward brownfield steps read to reverse-engineer
9
- # spec.md + backend-plan.md per slice.
4
+ # The first command of the brownfield sequence after select-stack. It answers
5
+ # one question and writes it down: WHICH FEATURES AND BOUNDED CONTEXTS does this
6
+ # codebase contain, and in what order can they be onboarded?
10
7
  #
11
- # INVENTORY → adopt → (fidelity) → characterize → [ forward pipeline ] ← you are here
8
+ # select-stack → INVENTORY → [edit the proposal] → inventory apply
9
+ # → onboard <BoundedContext> (once per context)
10
+ # ← you are here
12
11
  #
13
- # Two-part shape, mirroring the OKF generate flow:
14
- # 1. a DETERMINISTIC scan (aw-inventory.py scan) → the factual JSON substrate
15
- # (files, languages, churn, entrypoints, data/route/api files, and
16
- # aggregate_signals = candidate roots clustered by vertical-slice layout);
17
- # 2. an ENRICHMENT agent that turns that substrate into the OKF bundle,
18
- # organized BY AGGREGATE ROOT, then a conformance gate (validate_okf.py).
12
+ # Three passes over the code. The generated document explains each of them to the
13
+ # operator reading it; the wording lives in the template
14
+ # .appweaver/templates/proposed-inventory-template.md, overridable per project:
15
+ # A aggregate identification — what must stay consistent together. Three
16
+ # strategies (structure / reference graph / persistence), each offered only
17
+ # where the evidence for it exists, each reporting its own numbers.
18
+ # B the seam overlay — what can be cut over together. Always runs, over
19
+ # application/API/UI only. Emits signals, never decisions.
20
+ # C the proposal — 1:1 aggregate-to-feature by default, splits proposed with
21
+ # their evidence and never applied, grouped into bounded contexts and
22
+ # ordered into dependency waves.
19
23
  #
20
- # It does NOT author spec.md / backend-plan.md (that is /appweaver adopt per slice)
21
- # and does NOT touch source code — all writes stay inside the bundle dir.
24
+ # The deliverable is `specs/proposed-inventory.md` — an EDITABLE document. The
25
+ # operator renames, moves, merges, splits and re-orders in that file, then runs
26
+ # `/appweaver inventory apply`, which validates every edit and regenerates the
27
+ # machine map `.appweaver/inventory/feature-map.json` that onboard reads.
22
28
  #
23
- # Launch: /appweaver inventory [strategy] [focus/scope hints]
29
+ # It does NOT author specs, plans or governance (that is onboard, per context)
30
+ # and never modifies source.
31
+ #
32
+ # Launch: /appweaver inventory [structure|reference|persistence] [focus hints]
33
+ # /appweaver inventory apply
24
34
  # =============================================================================
25
35
 
26
36
  name: aw-spec-inventory
27
- description: Scan a brownfield codebase and produce an aggregate-organized OKF v0.1 knowledge bundle plus a migration backlog — the base for reverse-engineering spec.md + backend-plan.md per aggregate/vertical slice. Deterministic scan + enrichment agent + conformance gate; never modifies source.
28
- version: 1.1.0
37
+ description: Brownfield discovery — scan an existing codebase and propose its Features and Bounded Contexts, in dependency order with their dependencies, as an editable specs/proposed-inventory.md. Runs three passes (aggregate identification by a selectable evidence-gated strategy, a seam overlay over application/API/UI, and a 1:1-by-default feature proposal with evidenced splits offered but never applied). `apply` validates the operator's edits and regenerates the machine map onboard consumes. Never modifies source.
38
+ version: 2.0.0
29
39
  agent_agnostic: true
30
40
  dialect: aw-spec.orchestrator/v1
31
41
 
32
42
  inputs:
33
43
  user_input:
34
- description: Optional focus / scope hints (e.g. an area to prioritize, or a granularity note).
44
+ description: >-
45
+ Either the literal word `apply` — validate the edited specs/proposed-inventory.md
46
+ and regenerate the machine map — or an optional Pass A strategy name
47
+ (structure | reference | persistence) to force, plus free-text focus hints.
48
+ With no argument, the highest-coverage available strategy is proposed and the
49
+ operator confirms it from a menu carrying each strategy's measured numbers.
35
50
  required: false
36
51
  bind: USER_INPUT
37
52
 
@@ -43,181 +58,190 @@ scripts:
43
58
  sh: .appweaver/scripts/bash/aw-spec-actions.sh
44
59
  ps1: .appweaver/scripts/powershell/aw-spec-actions.ps1
45
60
 
46
- # No modelling rule file is loaded. author_bundle states the vocabulary it needs
47
- # inline — entities, value objects, domain events, the aggregate that OWNS them,
48
- # and the clean/torn seam test — in terms no stack owns, which is what lets this
49
- # scan run against an arbitrary codebase. A rule file here would reach every step
50
- # (this is the sticky set) and would hand the agent one project's house style as
51
- # the shape to look for.
61
+ # Nothing project-shaped in the sticky set: every pass here is deterministic and
62
+ # reads the code, so no rule file or exemplar has any part to play.
52
63
  standards:
53
64
  - { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
54
65
 
55
66
  vars:
56
- bundle_present: "false"
57
- conformant: "false"
67
+ mode: "propose" # apply-mode driver
68
+ strategy: "" # forced Pass A strategy; empty = probe default
69
+ feature_count: "0"
70
+ proposal_ok: "false"
58
71
 
59
72
  workflow:
60
73
 
61
- # 1. DETERMINISTIC scan of the current codebase → JSON substrate. Emits the
62
- # factual base (files, churn, categories, aggregate_signals) and reports
63
- # whether the target bundle already holds curation.
64
- - id: scan
74
+ # 0. MODE. The engine compares variables as text and has no compound
75
+ # conditions, so the two entry points are separated by one ordered set.
76
+ - id: mode_apply
77
+ action: set
78
+ condition: "{{USER_INPUT}} == apply"
79
+ vars: { mode: "apply" }
80
+
81
+ # ═══════════════════ APPLY — read the operator's edits back ═══════════════
82
+ # Runs first and stops the workflow, so the propose half never re-derives a
83
+ # map over the top of an edited one.
84
+
85
+ - id: apply_edits
65
86
  action: execute_script
87
+ condition: "{{mode}} == apply"
66
88
  script: { ref: inventory }
67
- subcommand: scan
89
+ subcommand: apply
90
+ on_error: continue
68
91
  outputs:
69
- - { name: inventory_json, from: "$.inventory_json" }
70
- - { name: root, from: "$.root" }
71
- - { name: head, from: "$.head" }
72
- - { name: file_count, from: "$.file_count" }
73
- - { name: aggregate_count, from: "$.aggregate_count" }
74
- - { name: bundle_dir, from: "$.bundle_dir" }
75
- - { name: bundle_present, from: "$.bundle_present" }
76
- - { name: bundle_md_count, from: "$.bundle_md_count" }
77
-
78
- # 1b. Substance gate: a scan that found no aggregate candidates has nothing to
79
- # organize a bundle around (empty repo, or no recognizable domain layout).
80
- - id: require_aggregates
81
- action: verify_context
82
- require_true: aggregate_count
83
- on_false: stop
84
- on_false_message: "Scan found no candidate aggregates in {{root}} (file_count={{file_count}}). Point /appweaver inventory at a codebase with a recognizable domain/vertical-slice layout, or scan a subtree that contains one."
92
+ - { name: proposal_ok, from: "$.ok" }
93
+ - { name: feature_map, from: "$.feature_map" }
94
+ - { name: feature_count, from: "$.feature_count" }
95
+ - { name: context_count, from: "$.context_count" }
96
+ - { name: wave_count, from: "$.wave_count" }
97
+ - { name: cycle_count, from: "$.cycle_count" }
98
+ - { name: contexts_ordered, from: "$.contexts_ordered" }
99
+ - { name: apply_problems, from: "$.problems" }
85
100
 
86
- # 1c. Curation guard (OKF rule): a bundle that already has concept files is
87
- # hand/agent curation — regenerating from scratch would destroy it. Stop
88
- # and let the operator clear or back up {{bundle_dir}} before re-running.
89
- - id: bundle_guard
101
+ # A rejected edit names the row it could not resolve. Stopping here is the
102
+ # point: a map built from a half-understood file would be onboarded silently.
103
+ - id: apply_gate
90
104
  action: verify_context
91
- require_value: bundle_present
92
- equals: "false"
105
+ condition: "{{mode}} == apply"
106
+ require_value: proposal_ok
107
+ equals: "true"
93
108
  on_mismatch: stop
94
- on_mismatch_message: "The OKF bundle at {{bundle_dir}} already holds {{bundle_md_count}} concept file(s) — inventory would overwrite that curation. Back up or remove {{bundle_dir}} to regenerate from scratch (a future /appweaver inventory-update will refresh incrementally)."
109
+ on_mismatch_message: >
110
+ specs/proposed-inventory.md could not be applied: {{apply_problems}}
111
+ Fix those rows and re-run /appweaver inventory apply. The Aggregate column
112
+ must name an aggregate the scan found; every Depends On value must name a
113
+ Feature that exists in the same table.
95
114
 
96
- # 2. Pre-execution hooks.
97
- - id: resolve_pre_hooks
115
+ - id: apply_report
116
+ action: verify_context
117
+ condition: "{{mode}} == apply"
118
+ require_non_empty: feature_map
119
+ on_empty: stop
120
+ on_empty_message: "apply reported success but wrote no map — re-run /appweaver inventory."
121
+
122
+ - id: applied
123
+ action: set
124
+ condition: "{{mode}} == apply"
125
+ vars: { next_hint: "Onboard the contexts in order: {{contexts_ordered}}" }
126
+
127
+ # ═══════════════════ PROPOSE — discover and write the map ═════════════════
128
+
129
+ # 1. WHICH LANES, AND WHERE IS THEIR SOURCE. Same declaration onboard reads:
130
+ # the scan is scoped to the declared subtrees so backend candidates are not
131
+ # drawn from UI source and vice versa.
132
+ - id: lane_model
98
133
  action: execute_script
134
+ condition: "{{mode}} == propose"
99
135
  script: { ref: actions }
100
- subcommand: resolve-hooks
101
- args: ["--phase", "before_inventory"]
102
- outputs:
103
- - { name: before_hooks, from: "$.hooks" }
104
- - id: dispatch_pre_hooks
105
- action: dispatch_hooks
106
- hooks_from_variable: before_hooks
107
-
108
- # 3. ENRICHMENT — turn the substrate into an aggregate-organized OKF bundle.
109
- # Context is fed via build_context (agent_generate reads the envelope, NOT a
110
- # step-level context: key): the factual substrate, the OKF format authority
111
- # (generate.md), and the brownfield design (seam/backlog framing).
112
- - id: ctx_author
113
- action: build_context
114
- context:
115
- - { path: "{{inventory_json}}" } # the deterministic substrate (aggregate_signals etc.)
116
- - { path: .appweaver/templates/okf-concept-template.md } # OKF v0.1 format authority (frontmatter, index, log, rules)
117
- - { path: Brownfield-Pipeline.md } # seam-as-unit-of-work, migration backlog, source anchor
118
- - id: author_bundle
119
- action: agent_generate
120
- tools: "Read,Write,Edit"
121
- permission_mode: acceptEdits
122
- task: |
123
- Act as the OKF enrichment agent for the Brownfield-Pipeline INVENTORY (B0)
124
- phase. Produce a CONFORMANT OKF v0.1 knowledge bundle at {{bundle_dir}},
125
- organized BY AGGREGATE ROOT, from the deterministic substrate
126
- {{inventory_json}} (in context). Follow the OKF concept template in context for
127
- format (frontmatter, index.md/log.md rules, cross-linking, hard rules) —
128
- but ORGANIZE around aggregates, not services/modules. Scope/focus hints:
129
- {{USER_INPUT}}
130
-
131
- Read the substrate first. Its `aggregate_signals` are CANDIDATE roots,
132
- each with its files grouped by layer (domain / feature / infra / api) plus
133
- detected entities; `git.history.churn` ranks significance; the category
134
- lists (data_layer_files, route_like_files, api_definitions, ops_files,
135
- docs, adr_docs) and `top_level_dirs` fill in the rest. Signals are input,
136
- NOT decisions — YOU draw the real boundaries.
137
-
138
- Consolidate the candidates into REAL aggregates:
139
- - Merge a src module and its mirror test module into ONE aggregate (e.g.
140
- src/.../MedicinalProducts + tests/.../MedicinalProducts → MedicinalProduct).
141
- - Treat service-level / shared infrastructure (a shared DbContext, cross-
142
- cutting adapters) as a Platform/Shared concept, NOT an aggregate.
143
- - Fold value objects, entities, domain events and CQRS commands/endpoints
144
- into the aggregate that OWNS them — one aggregate concept, not one per type.
145
-
146
- Write the bundle (all paths under {{bundle_dir}}, bundle-relative links
147
- begin with "/", per OKF §5.1):
148
- - `index.md` (bundle root) — frontmatter EXACTLY `okf_version: "0.1"` and
149
- nothing else; body = sections of bulleted links reusing each concept's
150
- `description`.
151
- - `architecture/overview.md` (type: Reference) — system purpose, the
152
- AGGREGATE MAP (each aggregate + its seam), tech stack, and links to every
153
- other concept.
154
- - `aggregates/<aggregate>.md` (type: Aggregate) — ONE per aggregate root.
155
- Cover: Responsibilities; the entities / value objects / domain events /
156
- commands / API endpoints / persistence it owns (from the signal's layer
157
- files); invariants (INFERRED from code — mark confidence, park anything
158
- unclear in `open_questions`, never guess); the SEAM (the boundary you
159
- could route across — repository, endpoint, table owner, queue — and
160
- whether it is CLEAN or TORN, i.e. shares a transaction/table with another
161
- slice); and the candidate `specs/<Feature>/` slice name (feature =
162
- aggregate). Set `source_files` to the module's files, grouped with YAML
163
- comment headers by each file's OWN folder — the path segment that VARIES
164
- across the files after their shared parent directory (the layer folder in
165
- a vertical slice; the tier project in an n-tier / layered app; tests → a
166
- single `tests` group). Do NOT impose a fixed Domain/Application/UI
167
- taxonomy — the CODE's folders ARE the grouping, and the toolchain
168
- re-derives them the same way on every later write. Set `timestamp` to the
169
- latest commit time across them.
170
- - `data/` and `api/` concepts only where a data model or API surface needs
171
- its own home beyond the owning aggregate (medium granularity: prefer
172
- folding into the aggregate).
173
- - `migration-backlog.md` (type: Reference, at bundle root) — the B0
174
- deliverable: a table `| aggregate | candidate slice | seam (clean/torn) |
175
- depends on | risks | adopt order |`, sequenced so clean seams go first
176
- and TORN seams are flagged as remediation items (introduce the seam
177
- before adopting). This is the strangler rollout plan.
178
- - An `index.md` in EVERY subdirectory (no frontmatter).
179
- - `log.md` (bundle root) — first line under a `## <today's date>` heading
180
- must be `Commit: ` + backtick + `{{head}}` + backtick (validator rule E4),
181
- then a one-line Initialization entry noting the concept count.
182
-
183
- Provenance + safety (OKF hard rules):
184
- - Stamp every concept `origin: reverse-engineered` and `generated_by:
185
- aw-spec-inventory/1.1.0` in frontmatter.
186
- - The CODE is the source of truth; capture what IS (bugs/quirks included),
187
- not what SHOULD be. Where intent/invariants are unclear from code + git
188
- history, add a concrete `open_questions` entry rather than inventing one.
189
- - NEVER fabricate facts, and NEVER emit secrets/tokens/credentials/internal
190
- hostnames found in the code — describe shape, never values.
191
- - Write ONLY inside {{bundle_dir}}; do NOT modify any source file.
192
- Report the bundle tree and the concept count by type.
136
+ subcommand: lane-verify
137
+ args: ["--command", "/appweaver inventory"]
193
138
  outputs:
194
- - { name: bundle_files, from: "$.files" }
139
+ - { name: lane_model_ok, from: "$.ok" }
140
+ - { name: lane_model_issues, from: "$.message" }
141
+ - { name: be_root, from: "$.backend_source_path" }
142
+ - { name: fe_root, from: "$.frontend_source_path" }
195
143
 
196
- - id: verify_authored
144
+ - id: lane_model_gate
197
145
  action: verify_context
198
- require_non_empty: bundle_files
199
- on_empty: stop
200
- on_empty_message: "No bundle files were written to {{bundle_dir}}. Ensure the substrate {{inventory_json}} has aggregate_signals and re-run."
146
+ condition: "{{mode}} == propose"
147
+ require_true: lane_model_ok
148
+ on_false: stop
149
+ on_false_message: >
150
+ /appweaver inventory cannot run: {{lane_model_issues}}
151
+ The scan is scoped to the declared lanes, so it has to be told which subtree
152
+ holds which lane before it starts. Run /appweaver select-stack first — it
153
+ detects what this repo already is and lets you correct the proposal.
201
154
 
202
- # 4. CONFORMANCE gate — OKF v0.1 validator (errors block; warnings are noted).
203
- - id: validate_bundle
155
+ # 2. PASS A + B + C. One deterministic call: every AVAILABLE strategy's
156
+ # clustering is run (cheap — the expensive half is authoring, which does not
157
+ # happen here), the seam overlay is laid over the chosen one, and the
158
+ # proposal is written with its dependency waves.
159
+ - id: propose
204
160
  action: execute_script
161
+ condition: "{{mode}} == propose"
205
162
  script: { ref: inventory }
206
- subcommand: validate
207
- args: ["--bundle", "{{bundle_dir}}", "--repo-root", "{{root}}"]
163
+ subcommand: propose
164
+ args: ["--service-root", "{{be_root}}", "--ui-root", "{{fe_root}}", "--strategy", "{{strategy}}"]
208
165
  outputs:
209
- - { name: conformant, from: "$.conformant" }
210
- - { name: okf_errors, from: "$.errors" }
211
- - { name: okf_warnings, from: "$.warnings" }
212
- - { name: concepts, from: "$.concepts" }
166
+ - { name: proposal_ok, from: "$.ok" }
167
+ - { name: proposal_md, from: "$.proposal_md" }
168
+ - { name: feature_map, from: "$.feature_map" }
169
+ - { name: root, from: "$.root" }
170
+ - { name: chosen_strategy, from: "$.strategy" }
171
+ - { name: feature_count, from: "$.feature_count" }
172
+ - { name: context_count, from: "$.context_count" }
173
+ - { name: wave_count, from: "$.wave_count" }
174
+ - { name: cycle_count, from: "$.cycle_count" }
175
+ - { name: coverage_pct, from: "$.coverage_pct" }
176
+ - { name: split_candidates, from: "$.split_candidates" }
177
+ - { name: contexts_ordered, from: "$.contexts_ordered" }
178
+ - { name: strategy_options, from: "$.strategy_options" }
213
179
 
214
- - id: conformance_gate
180
+ # 2b. Substance gate. Zero features is the one outcome that leaves onboard with
181
+ # nothing to do, and it is reported as what it is rather than as a crash.
182
+ - id: require_features
215
183
  action: verify_context
216
- require_true: conformant
184
+ condition: "{{mode}} == propose"
185
+ require_true: feature_count
217
186
  on_false: stop
218
- on_false_message: "The OKF bundle at {{bundle_dir}} is NON-CONFORMANT (errors: {{okf_errors}}). Fix the reported ERRORs (unparseable frontmatter, missing `type`, malformed index.md/log.md) and re-run — or edit the bundle by hand, then re-validate."
187
+ on_false_message: >
188
+ No candidate features were found in {{root}}. Every Pass A strategy either
189
+ returned nothing or was unavailable for want of evidence. Point
190
+ /appweaver inventory at a subtree that holds application source, or check
191
+ that the lane declaration names the right path.
192
+
193
+ # 3. THE STRATEGY PICK. Offered from the probe, each option carrying its own
194
+ # measured numbers — features found, share of source covered, how many
195
+ # one-file candidates it produced. A strategy that would return nothing is
196
+ # not in the list, because choosing it would be the zero-candidate stop
197
+ # arrived at deliberately. One option auto-confirms.
198
+ - id: pick_strategy
199
+ action: user_select
200
+ condition: "{{mode}} == propose"
201
+ prompt: >
202
+ Pass A found these candidate boundaries. Which evidence should the feature
203
+ map be built from? (`{{chosen_strategy}}` is proposed: highest coverage,
204
+ ties broken by fewest single-file candidates.)
205
+ options_from_variable: strategy_options
206
+ preselect_from: chosen_strategy
207
+ single_option_confirm: true
208
+ outputs:
209
+ - { name: picked, from: selection }
210
+
211
+ # 4. Re-derive on the operator's pick. Deterministic and about a second, so it
212
+ # is re-run unconditionally rather than branched on whether the pick moved.
213
+ - id: repropose
214
+ action: execute_script
215
+ condition: "{{mode}} == propose"
216
+ script: { ref: inventory }
217
+ subcommand: propose
218
+ args: ["--service-root", "{{be_root}}", "--ui-root", "{{fe_root}}", "--strategy", "{{picked}}"]
219
+ outputs:
220
+ - { name: proposal_md, from: "$.proposal_md" }
221
+ - { name: feature_map, from: "$.feature_map" }
222
+ - { name: chosen_strategy, from: "$.strategy" }
223
+ - { name: feature_count, from: "$.feature_count" }
224
+ - { name: context_count, from: "$.context_count" }
225
+ - { name: wave_count, from: "$.wave_count" }
226
+ - { name: cycle_count, from: "$.cycle_count" }
227
+ - { name: coverage_pct, from: "$.coverage_pct" }
228
+ - { name: split_candidates, from: "$.split_candidates" }
229
+ - { name: contexts_ordered, from: "$.contexts_ordered" }
230
+
231
+ # 5. Hooks, after the map exists and before the operator is handed it.
232
+ - id: resolve_pre_hooks
233
+ action: execute_script
234
+ condition: "{{mode}} == propose"
235
+ script: { ref: actions }
236
+ subcommand: resolve-hooks
237
+ args: ["--phase", "before_inventory"]
238
+ outputs:
239
+ - { name: before_hooks, from: "$.hooks" }
240
+ - id: dispatch_pre_hooks
241
+ action: dispatch_hooks
242
+ condition: "{{mode}} == propose"
243
+ hooks_from_variable: before_hooks
219
244
 
220
- # 5. Post-execution hooks.
221
245
  - id: resolve_post_hooks
222
246
  action: execute_script
223
247
  script: { ref: actions }
@@ -230,9 +254,28 @@ workflow:
230
254
  hooks_from_variable: after_hooks
231
255
 
232
256
  outputs:
233
- inventory_json: "{{inventory_json}}"
234
- bundle_dir: "{{bundle_dir}}"
235
- aggregate_count: "{{aggregate_count}}"
236
- concepts: "{{concepts}}"
237
- warnings: "{{okf_warnings}}"
238
- next: "Review {{bundle_dir}} (start with architecture/overview.md and migration-backlog.md). Then adopt a slice: /appweaver adopt <Aggregate> reverse-engineers its spec.md + backend-plan.md from the bundle, and the forward pipeline carries it from there."
257
+ proposal: "{{proposal_md}}"
258
+ feature_map: "{{feature_map}}"
259
+ strategy: "{{chosen_strategy}}"
260
+ features: "{{feature_count}}"
261
+ contexts: "{{context_count}}"
262
+ waves: "{{wave_count}}"
263
+ cycles: "{{cycle_count}}"
264
+ coverage_pct: "{{coverage_pct}}"
265
+ split_candidates: "{{split_candidates}}"
266
+ contexts_ordered: "{{contexts_ordered}}"
267
+
268
+ next: >
269
+ {{feature_count}} feature(s) across {{context_count}} bounded context(s) were
270
+ proposed from `{{chosen_strategy}}` evidence ({{coverage_pct}}% of source
271
+ covered), ordered into {{wave_count}} dependency wave(s).
272
+
273
+ READ AND EDIT specs/proposed-inventory.md — it is the boundary map, and it is
274
+ yours. Rename features and contexts, move a feature to another context, merge
275
+ two by giving them one name, split one by adding a row with the same Aggregate,
276
+ and set Depends On to change the order. {{split_candidates}} split(s) were
277
+ PROPOSED with their evidence and none were applied.
278
+
279
+ Then run `/appweaver inventory apply` to validate those edits and regenerate the
280
+ machine map, and onboard the contexts one at a time in the order it reports:
281
+ {{contexts_ordered}}