aw-spec-cli 0.1.18 → 0.1.20

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 (77) hide show
  1. package/assets/.appweaver/DESIGN-FORMAT.md +63 -31
  2. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +62 -14
  3. package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +10 -6
  4. package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +9 -1
  5. package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +21 -0
  6. package/assets/.appweaver/orchestrator/orchestrator.py +23 -1
  7. package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +1 -1
  8. package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +22 -13
  9. package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +1 -1
  10. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +6 -4
  11. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +399 -131
  12. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +1 -1
  13. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +199 -38
  14. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +1 -1
  15. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +159 -25
  16. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +20 -9
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +1 -1
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +183 -40
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-deploy.orch.yaml +1 -1
  20. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +311 -36
  21. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +221 -40
  22. package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +217 -174
  23. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +494 -109
  24. package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +1 -1
  25. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +83 -18
  26. package/assets/.appweaver/orchestrator/workflows/aw-spec-recall-delta.orch.yaml +319 -0
  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-specify.orch.yaml +143 -34
  30. package/assets/.appweaver/scripts/bash/aw-inventory.sh +15 -0
  31. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +878 -51
  32. package/assets/.appweaver/scripts/bash/aw-xref.sh +3 -0
  33. package/assets/.appweaver/scripts/bash/common.sh +28 -1
  34. package/assets/.appweaver/scripts/powershell/aw-inventory.ps1 +15 -0
  35. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +868 -51
  36. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +3 -0
  37. package/assets/.appweaver/scripts/python/aw-inventory.py +1832 -65
  38. package/assets/.appweaver/scripts/python/aw-xref.py +500 -81
  39. package/assets/.appweaver/templates/delta-design-template.md +1 -1
  40. package/assets/.appweaver/templates/delta-domain-model-template.md +1 -1
  41. package/assets/.appweaver/templates/delta-spec-template.md +4 -3
  42. package/assets/.appweaver/templates/design-template.md +56 -40
  43. package/assets/.appweaver/templates/okf-concept-template.md +11 -1
  44. package/assets/.appweaver/templates/proposed-inventory-template.md +127 -0
  45. package/assets/.appweaver/templates/source-map-template.md +5 -3
  46. package/assets/.appweaver/templates/spec-template.md +24 -21
  47. package/assets/.appweaver/templates/ui-delta-design-template.md +1 -1
  48. package/assets/.appweaver/templates/ui-design-template.md +11 -7
  49. package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.pack.txt +4489 -361
  50. package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.pack.txt +2274 -125
  51. package/assets/guardrails/domain/healthcare/auditing.domain.md +3 -3
  52. package/assets/guardrails/domain/pharma/auditing.domain.md +3 -3
  53. package/assets/guardrails/technology/dotnet-webapi/api-versioning.md +1 -1
  54. package/assets/guardrails/technology/dotnet-webapi/auditing.md +1 -1
  55. package/assets/guardrails/technology/dotnet-webapi/authorization.md +1 -1
  56. package/assets/guardrails/technology/dotnet-webapi/clean-architecture.md +1 -1
  57. package/assets/guardrails/technology/dotnet-webapi/cqrs.md +1 -1
  58. package/assets/guardrails/technology/dotnet-webapi/data-classification.md +1 -1
  59. package/assets/guardrails/technology/dotnet-webapi/eventing.md +1 -1
  60. package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +1 -1
  61. package/assets/guardrails/technology/dotnet-webapi/modular-monolith.md +1 -1
  62. package/assets/guardrails/technology/dotnet-webapi/persistence.md +1 -1
  63. package/assets/guardrails/technology/dotnet-webapi/vertical-slices.md +1 -1
  64. package/assets/guardrails/technology/dotnet-webapi/zero-trust.md +1 -1
  65. package/assets/guardrails/technology/nextjs/accessibility-implementation.md +1 -1
  66. package/assets/guardrails/technology/nextjs/api-integration.dotnet-webapi.md +1 -1
  67. package/assets/guardrails/technology/nextjs/api-integration.java-spring.md +1 -1
  68. package/assets/guardrails/technology/nextjs/auth-session.md +1 -1
  69. package/assets/guardrails/technology/nextjs/component-architecture.md +1 -1
  70. package/assets/guardrails/technology/nextjs/design-system.md +1 -1
  71. package/assets/guardrails/technology/nextjs/error-handling.md +1 -1
  72. package/assets/guardrails/technology/nextjs/forms-and-validation.md +1 -1
  73. package/assets/guardrails/technology/nextjs/i18n.md +1 -1
  74. package/assets/guardrails/technology/nextjs/performance-budgets.md +1 -1
  75. package/assets/guardrails/technology/nextjs/routing-navigation.md +1 -1
  76. package/assets/guardrails/technology/nextjs/state-management.md +1 -1
  77. package/package.json +1 -1
@@ -2,27 +2,40 @@
2
2
  # aw-spec-onboard (orchestrator) — the entry step of the Brownfield DEVELOPMENT flow
3
3
  #
4
4
  # Onboard an EXISTING codebase into aw-spec so it can be DEVELOPED (evolved, not
5
- # migrated) governed exactly like greenfield and as lightly as OpenSpec. Runs ONCE
6
- # per repo, over the WHOLE codebase (project-level, not per-slice). It does two
7
- # things, both from a single deterministic whole-repo scan — no slice is adopted yet:
5
+ # migrated) governed exactly like greenfield and as lightly as OpenSpec. Runs ONCE PER
6
+ # BOUNDED CONTEXT, against the boundaries /appweaver inventory proposed and the
7
+ # operator confirmed. No slice is adopted yet; each run does three things:
8
8
  #
9
- # 1. FEATURE MAP — a HIGH-LEVEL OKF discovery index of the features/aggregates the
10
- # code contains (folders/concepts + a bounded-context map + the tech stack). It
11
- # is a shallow map to pick from, NOT a deep reverse — the deep per-feature
12
- # reverse (spec.md + design.md) is /appweaver adopt, done just-in-time when
13
- # you are about to develop that feature.
9
+ # 1. FEATURE MAP — a HIGH-LEVEL OKF discovery index of THIS CONTEXT's aggregates —
10
+ # ONE concept per aggregate, listing the features that act on it — plus the tech
11
+ # stack, added beside whatever earlier contexts already wrote. The bundle is the
12
+ # PROJECT's knowledge: a concept is an aggregate, never a feature. It is a shallow map to pick from, NOT a deep reverse — the deep
13
+ # per-feature reverse (spec.md + the context's domain-model.md + the lane plans)
14
+ # is /appweaver adopt, done just-in-time when you are about to develop that
15
+ # feature.
14
16
  #
15
- # 2. GOVERNANCE SCAFFOLD — the PROJECT-level governing tier, established once from
16
- # the whole-repo vantage so forward development is consistent from the first
17
- # change: the split constitution (technical + domain), the guardrails/projects/
18
- # brf-<domain>/domain/ rule-file set (+ domain-rule-bindings), the domain charter
19
- # that set is headed by, and the activated domain. Because nothing is adopted yet, this tier is
20
- # a PROVISIONAL scaffold evidenced by the SCAN (project identity + bounded-context
21
- # map are firm; principles/rules are marked provisional). adopt then RECONCILES
22
- # each with the slice's own evidence (BR-/CN-/DC-/IC- ids) when that feature is
23
- # developed — the "created here, updated when taking each slice" contract.
17
+ # 2. THE CONTEXT'S SPECS TREE — specs/<Context>/ with an EMPTY directory per feature,
18
+ # and the context made active in .appweaver/context.json. This is what makes the
19
+ # context real to the commands that read the tree rather than the bundle:
20
+ # select-bounded-context lists it, select-feature offers exactly its features, and
21
+ # adopt has somewhere to write. No spec.md and no domain-model.md — a placeholder
22
+ # for either reads as "the document exists" to every gate downstream.
24
23
  #
25
- # ONBOARD → adopt <Feature> (JIT) → specify → plan → analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta ← you are here
24
+ # 3. GOVERNANCE SCAFFOLD — the PROJECT-level governing tier: the split constitution
25
+ # (technical + domain), the guardrails/projects/brf-<domain>/domain/ rule-file set
26
+ # (+ domain-rule-bindings), the domain charter that set is headed by, and the
27
+ # activated domain. AUTHORED BY THE FIRST CONTEXT TO ONBOARD AND BY NO OTHER —
28
+ # re-deriving project principles from one context's evidence is the aggregate-
29
+ # scoping bias this tier exists to avoid, so every later run skips the whole block
30
+ # and contributes its own concepts and specs tree instead. Because nothing is
31
+ # adopted yet, the tier is a PROVISIONAL scaffold evidenced by the SCAN (project
32
+ # identity + bounded-context map are firm; principles/rules are marked
33
+ # provisional); /appweaver constitution is what amends it.
34
+ #
35
+ # inventory → [edit specs/proposed-inventory.md] → inventory apply
36
+ # → ONBOARD <Context> (once per context) ← you are here
37
+ # → adopt <Feature> (JIT) → specify → plan → analyze-plan-delta → implement → build
38
+ # → analyze-slice-delta → finalize-delta → analyze-slice → close-delta
26
39
  #
27
40
  # Establishing the PROJECT-scoped constitution here (whole-repo vantage) — not from a
28
41
  # single adopted slice — is deliberate: it keeps governance project-scoped from day
@@ -112,32 +125,32 @@
112
125
  # pass was re-deriving an answer the run already had. `lane-declare` has one caller again
113
126
  # — select-stack — and this workflow only reads.
114
127
  #
115
- # Never modifies source — every write lands in the OKF bundle, the specs governance dir,
116
- # guardrails/projects/ (domain/ + service/ + frontend/ rule sets + both maps), or the
117
- # .appweaver/templates/overrides/ design templates.
128
+ # Never modifies source — every write lands in the OKF bundle, the context's specs/
129
+ # directories, guardrails/projects/ (domain/ + service/ + frontend/ rule sets + both
130
+ # maps), or the .appweaver/templates/overrides/ design templates.
118
131
  #
119
- # Launch: /appweaver onboard [domain-name / scope hint] [; concerns: a, b, c]
120
- # e.g. /appweaver onboard Pharma
121
- # e.g. /appweaver onboard "Pharma; concerns: auditing, concurrency, authorization"
132
+ # Launch: /appweaver onboard [<BoundedContext>] [; concerns: a, b, c]
133
+ # e.g. /appweaver onboard Clinical
134
+ # e.g. /appweaver onboard # the next context the map has open
135
+ # e.g. /appweaver onboard "Clinical; concerns: auditing, concurrency, authorization"
122
136
  # =============================================================================
123
137
 
124
138
  name: aw-spec-onboard
125
139
  description: Onboard an existing codebase for brownfield DEVELOPMENT — a deterministic whole-repo scan produces a high-level OKF feature map (folders/concepts) PLUS the project-level governance scaffold (split constitution + guardrails/projects/brf-<domain>/ with its DOMAIN rule files at domain/ + domain-rule-bindings + code-derived TECHNICAL rule files at service/ + tech-rule-bindings + activated domain), all provisional-from-scan and refined per slice by adopt. Where the scan finds a frontend lane in the CODE, it also authors that lane's half — constitution.ui.md (UX-*) — and its code-derived rule files at brf-<domain>/frontend/, mapped under `## /aw-spec-ui-constitution`; a backend-only repo gets none of it. Every rule tier is reverse-engineered from the CODE alone (never a greenfield rule file), evidenced-only, and WIRED per-workflow so the whole forward pipeline AND adopt govern against THIS codebase — never a greenfield fallback. It also reverse-engineers the repo's own technical structure into a project-specific design + delta-design template pair, installed as the project template overrides, and — where the lane exists — the frontend lane's own pair from the UI source. Every authoring step is shaped by a NEUTRAL rev-templates skeleton; no greenfield constitution, rule file or document template is ever placed in context. Accepts an optional concerns list to look for (onboard decides which are actually present). Reads any declared lane, never writes one. No migration content; never modifies source.
126
- version: 1.6.0
140
+ version: 2.0.0
127
141
  agent_agnostic: true
128
142
  dialect: aw-spec.orchestrator/v1
129
143
 
130
144
  inputs:
131
145
  user_input:
132
146
  description: >-
133
- Optional free-text hint. May carry (a) a domain name for the governance project
134
- (e.g. Pharma → guardrails/projects/brf-pharma/); (b) a scope/focus hint for the
135
- feature map; and/or (c) a CONCERNS LIST to look for, as a "concerns: a, b, c"
136
- clause (e.g. "Pharma; concerns: auditing, concurrency, authorization"). The
137
- concerns are a SEARCH HINT only — both rule-set authors still DECIDE which
147
+ The BOUNDED CONTEXT to onboard, as named in the feature map that
148
+ /appweaver inventory produced (e.g. Billing). Onboard runs once per context;
149
+ with no argument it takes the first context in the map that is not yet
150
+ onboarded. May also carry a CONCERNS LIST to look for, as a
151
+ "concerns: a, b, c" clause (e.g. "Billing; concerns: auditing, concurrency").
152
+ The concerns are a SEARCH HINT only — the rule-set authors still DECIDE which
138
153
  concerns are actually present from the code, and are never limited to the list.
139
- When omitted, the domain defaults to the PROJECT name from the OKF overview,
140
- then the repo basename; concerns are auto-discovered from the code.
141
154
  required: false
142
155
  bind: USER_INPUT
143
156
 
@@ -164,6 +177,20 @@ vars:
164
177
  bundle_present: "false" # inventory bundle_guard driver
165
178
  conformant: "false" # OKF conformance-gate driver
166
179
  pack_present: "false" # project anti-clobber driver
180
+ # The governance tier — constitution halves, rule packs, document templates —
181
+ # is PROJECT-scoped, authored by the first context to onboard and left alone by
182
+ # every context after it. Re-authoring project principles from one context's
183
+ # evidence is the scoping bias the rev-constitution work removed; this var is
184
+ # what keeps the second run from doing it.
185
+ repo_gov: "false" # true once the project governance exists
186
+ # The governance project is named from the PROJECT, never from the context.
187
+ # Passing the context name here would produce one rule pack per bounded
188
+ # context, which is the same bias by another route.
189
+ gov_name: ""
190
+ context_known: "false"
191
+ is_blocked: "false"
192
+ in_cycle: "false"
193
+ already_onboarded: "false"
167
194
  technical_ok: "false" # technical-half lint guard
168
195
  domain_ok: "false" # domain-half lint guard
169
196
  ui_ok: "false" # UI-half lint guard
@@ -281,14 +308,106 @@ workflow:
281
308
  on_false: stop
282
309
  on_false_message: "Scan found no candidate features in {{root}} (file_count={{file_count}}). Point /appweaver onboard at a codebase with a recognizable domain/vertical-slice layout, or scan a subtree that contains one."
283
310
 
284
- # 1c. Curation guard (OKF rule): a bundle that already has concept files is
285
- # curation — regenerating from scratch would destroy it.
286
- - id: bundle_guard
311
+ # 1c. WHICH BOUNDED CONTEXT, AND IS IT THIS ONE'S TURN. onboard runs once per
312
+ # context, so before anything is written it reads the map inventory
313
+ # produced and asks four questions: is this a context the map knows, what
314
+ # does it hold, has it already been onboarded, and is everything it depends
315
+ # on onboarded first.
316
+ #
317
+ # This replaces the bundle-wide curation guard. That guard stopped whenever
318
+ # the bundle held any concept at all, which is indistinguishable from
319
+ # "context 1 has been onboarded" — so it made onboarding a second context
320
+ # impossible. The curation it protects is still protected, per context:
321
+ # a context whose concepts already exist is not regenerated.
322
+ - id: context_state
323
+ action: execute_script
324
+ script: { ref: inventory }
325
+ subcommand: context-state
326
+ args: ["--context", "{{USER_INPUT}}", "--bundle", "{{bundle_dir}}"]
327
+ on_error: continue
328
+ outputs:
329
+ - { name: map_present, from: "$.map_present" }
330
+ - { name: context_known, from: "$.context_known" }
331
+ - { name: bc, from: "$.context" }
332
+ - { name: bc_slug, from: "$.context_slug" }
333
+ - { name: bc_features, from: "$.features" }
334
+ - { name: bc_feature_count, from: "$.feature_count" }
335
+ - { name: pending_features, from: "$.pending_features" }
336
+ - { name: pending_count, from: "$.pending_count" }
337
+ - { name: bc_aggregates, from: "$.aggregates" }
338
+ - { name: bc_feature_aggregates, from: "$.feature_aggregates" }
339
+ - { name: pending_aggregates, from: "$.pending_aggregates" }
340
+ - { name: already_onboarded, from: "$.already_onboarded" }
341
+ - { name: is_blocked, from: "$.is_blocked" }
342
+ - { name: blocked_by, from: "$.blocked_by" }
343
+ - { name: in_cycle, from: "$.in_cycle" }
344
+ - { name: cycle_with, from: "$.cycle_with" }
345
+ - { name: is_first, from: "$.is_first" }
346
+ - { name: onboarded_contexts, from: "$.onboarded_contexts" }
347
+ - { name: remaining_contexts, from: "$.remaining_contexts" }
348
+ - { name: contexts_ordered, from: "$.contexts_ordered" }
349
+ - { name: feature_map, from: "$.feature_map" }
350
+ - { name: map_error, from: "$.error" }
351
+
352
+ # 1d. No map means the boundaries were never decided. Onboarding without them
353
+ # is what produced a flat aggregate list with no context level at all.
354
+ - id: require_map
355
+ action: verify_context
356
+ require_value: map_present
357
+ equals: "true"
358
+ on_mismatch: stop
359
+ on_mismatch_message: >
360
+ No feature map was found. Onboard works one BOUNDED CONTEXT at a time and
361
+ reads the map /appweaver inventory produces. Run `/appweaver inventory`,
362
+ review and edit `specs/proposed-inventory.md` — it is the boundary map and
363
+ it is yours to change — then run `/appweaver inventory apply`.
364
+
365
+ - id: require_context
287
366
  action: verify_context
288
- require_value: bundle_present
367
+ require_value: context_known
368
+ equals: "true"
369
+ on_mismatch: stop
370
+ on_mismatch_message: "{{map_error}}"
371
+
372
+ # 1e. Per-context curation guard — the narrow form of the old bundle guard.
373
+ - id: context_done_guard
374
+ action: verify_context
375
+ require_value: already_onboarded
289
376
  equals: "false"
290
377
  on_mismatch: stop
291
- on_mismatch_message: "The OKF feature-map bundle at {{bundle_dir}} already holds {{bundle_md_count}} concept file(s) — onboard would overwrite that curation. Back up or remove {{bundle_dir}} to re-onboard from scratch."
378
+ on_mismatch_message: >
379
+ Bounded context {{bc}} is already onboarded — every aggregate its
380
+ {{bc_feature_count}} feature(s) act on ({{bc_aggregates}}) already has a concept in
381
+ {{bundle_dir}}, and re-running would overwrite that curation. Contexts still open: {{remaining_contexts}}
382
+
383
+ # 1f. Dependency order. A context whose dependencies are still open would be
384
+ # modelled against aggregates that do not exist yet. A context inside a
385
+ # CYCLE is released rather than blocked — each member is waiting on the
386
+ # other, so blocking both deadlocks the sequence.
387
+ - id: context_order_gate
388
+ action: verify_context
389
+ require_value: is_blocked
390
+ equals: "false"
391
+ on_mismatch: stop
392
+ on_mismatch_message: >
393
+ Bounded context {{bc}} depends on a context that has not been onboarded yet:
394
+ {{blocked_by}}. Its features cite aggregates that will not exist until then, so
395
+ onboard in the order the map gives — {{contexts_ordered}} — or run
396
+ /appweaver onboard with no argument to take the next one automatically.
397
+ To change that order, edit the Depends On column in specs/proposed-inventory.md
398
+ and re-run /appweaver inventory apply.
399
+
400
+ - id: cycle_warning
401
+ action: verify_context
402
+ condition: "{{in_cycle}} == true"
403
+ require_value: in_cycle
404
+ equals: "never"
405
+ on_mismatch: continue
406
+ on_mismatch_message: >
407
+ NOTE: {{bc}} and {{cycle_with}} depend on each other. They cannot be ordered,
408
+ so {{bc}} is released rather than blocked — but the two share a seam, and the
409
+ aggregates each cites from the other will be incomplete until both are
410
+ onboarded. Break the cycle with a seam, or onboard the ring back to back.
292
411
 
293
412
  # 1d. FRONTEND LANE — the DECLARATION decides that it exists, and the CODE decides
294
413
  # what its governance says. Those are two different questions and they used to be
@@ -389,6 +508,7 @@ workflow:
389
508
  action: build_context
390
509
  context:
391
510
  - { path: "{{inventory_json}}" } # the deterministic substrate (aggregate_signals etc.)
511
+ - { path: "{{feature_map}}" } # the CONFIRMED boundaries: which features belong to which bounded context, and in what order
392
512
  - { path: .appweaver/templates/okf-concept-template.md } # OKF v0.1 format authority (frontmatter, index, log, rules) — format only, zero project content
393
513
  - id: author_bundle
394
514
  action: agent_generate
@@ -396,31 +516,55 @@ workflow:
396
516
  permission_mode: acceptEdits
397
517
  task: |
398
518
  Act as the OKF enrichment agent for the Brownfield DEVELOPMENT onboard step.
399
- Produce a CONFORMANT OKF v0.1 knowledge bundle at {{bundle_dir}} that is a
400
- HIGH-LEVEL FEATURE MAP — a discovery index of the features/aggregates this
401
- codebase contains, organized BY AGGREGATE ROOT — from the deterministic
402
- substrate {{inventory_json}} (in context). Follow the OKF concept template in
403
- context for format (frontmatter, index.md/log.md rules, cross-linking, hard rules).
404
- Domain/scope hint (optional): {{USER_INPUT}}
405
-
406
- THIS IS A MAP, NOT A DEEP REVERSE. Keep every feature concept SHALLOW — enough
407
- to recognize and pick the feature to develop next. The deep per-feature reverse
408
- (spec.md + design.md) is /appweaver adopt, run just-in-time when a feature is
409
- about to be developed. Do NOT deep-model invariants/behaviour here.
410
-
411
- Read the substrate first. Its `aggregate_signals` are CANDIDATE feature roots,
412
- each with its files grouped by layer plus detected entities; `ui_attribution`
413
- says which FRONTEND files serve which of those roots (and which serve none);
519
+ Extend the OKF v0.1 knowledge bundle at {{bundle_dir}} with the features of ONE
520
+ BOUNDED CONTEXT — a discovery index of what that context contains — from the
521
+ deterministic substrate {{inventory_json}} and the confirmed boundary map
522
+ {{feature_map}} (both in context). Follow the OKF concept template in context for
523
+ format (frontmatter, index.md/log.md rules, cross-linking, hard rules).
524
+
525
+ THE CONTEXT YOU ARE ONBOARDING IS `{{bc}}`.
526
+ Its features, already confirmed by the operator: {{bc_features}}
527
+ The aggregates those features act on: {{bc_aggregates}}
528
+ Which feature acts on which: {{bc_feature_aggregates}}
529
+ Aggregates still without a concept, to be written this run: {{pending_aggregates}}
530
+
531
+ THE BUNDLE IS THE PROJECT'S KNOWLEDGE, AND A CONCEPT IS AN AGGREGATE — NEVER A
532
+ FEATURE. Features are the specs tree's unit (this run creates their directories);
533
+ they are LISTED on their aggregate's concept and never given concepts of their own.
534
+ Several features can act on one aggregate — the map split it into capabilities —
535
+ and that aggregate still gets exactly one concept.
536
+
537
+ THE BOUNDARIES ARE DECIDED — DO NOT REDRAW THEM. The feature map is the output of
538
+ a scan the operator has already reviewed and edited by hand. Write one concept per
539
+ AGGREGATE the map names for this context, and no others. Do not merge two
540
+ aggregates, do not split one, do not invent an aggregate or a feature the map does
541
+ not list, and do not write a concept for an aggregate belonging to another bounded
542
+ context — those are onboarded by their own runs. Where the substrate's `aggregate_signals` disagree with the map,
543
+ THE MAP WINS; note the disagreement in `open_questions` on the concept.
544
+
545
+ THIS IS ADDITIVE. Other bounded contexts may already have concepts in this bundle.
546
+ Read the bundle before writing. Create the files this context needs, UPDATE the
547
+ shared index/log files in place, and NEVER delete, rewrite or re-scope a concept
548
+ belonging to another context. If a file you would create already exists, it belongs
549
+ to an earlier run — leave it alone.
550
+
551
+ THIS IS A MAP, NOT A DEEP REVERSE. Keep every concept SHALLOW — enough to
552
+ recognize the aggregate and pick a feature over it to develop next. Do NOT deep-model
553
+ invariants/behaviour here.
554
+
555
+ Read the substrate for EVIDENCE about the map's aggregates. Its `aggregate_signals`
556
+ carry each root's files grouped by layer plus detected entities; `ui_attribution`
557
+ says which FRONTEND files serve which root (and which serve none);
414
558
  `git.history.churn` ranks significance; the category lists and `top_level_dirs`
415
- fill in the rest.
416
- Signals are input, not decisions — YOU draw the real feature boundaries:
417
- - Merge a src module and its mirror test module into ONE feature (e.g.
418
- src/.../MedicinalProducts + tests/.../MedicinalProducts → MedicinalProduct).
559
+ fill in the rest. Where several features act on one aggregate (two seams on one
560
+ root), its ONE concept carries the whole module — domain and behaviour files alike
561
+ — and names each feature with the capability it covers. Which files serve which
562
+ capability is recorded per feature later, in the source-map adopt writes.
419
563
  - Treat service-level / shared infrastructure (a shared persistence context,
420
564
  cross-cutting adapters, host / service-defaults projects) as a Platform/Shared
421
565
  concept, NOT a feature.
422
566
  - Fold value objects, entities, domain events, commands and endpoints into the
423
- feature that OWNS them — one feature concept, not one per type.
567
+ aggregate that OWNS them — one aggregate concept, not one per type.
424
568
  NAME EVERY CONCEPT IN THE CODE'S OWN VOCABULARY — read the identifiers the source
425
569
  actually uses. "Aggregate", "entity" and "value object" are how aw-spec ORGANIZES a
426
570
  feature map; they are not a claim that this codebase is modelled that way. Where the
@@ -428,9 +572,19 @@ workflow:
428
572
  grouping around it.
429
573
 
430
574
  Write the bundle (all paths under {{bundle_dir}}; bundle-relative links begin
431
- with "/", per OKF §5.1):
575
+ with "/", per OKF §5.1).
576
+
577
+ IS THIS THE FIRST CONTEXT IN THIS BUNDLE? `{{is_first}}`
578
+ The three `architecture/` notes below describe the WHOLE repository, not one
579
+ context, so they are written ONCE — by the first context to onboard. When
580
+ `{{is_first}}` is false they already exist: do not rewrite them, and add to them
581
+ only where this context introduces a technology or integration point the note
582
+ genuinely lacks. Everything else below is written on every run, for this
583
+ context's features only.
584
+
432
585
  - `index.md` (bundle root) — frontmatter EXACTLY `okf_version: "0.1"` and nothing
433
586
  else; body = sections of bulleted links reusing each concept's `description`.
587
+ On a later context, ADD this context's entries and leave the rest in place.
434
588
  - `architecture/overview.md` (type: Reference) — the system at a glance. Its
435
589
  `## Purpose` section MUST open with the whole solution/product name in bold
436
590
  followed by "is a …" (e.g. "**Medicinal.Healthcare** is a …") — this bold token
@@ -463,11 +617,14 @@ workflow:
463
617
  unattributed` lists the files serving no single aggregate (app shell, layout,
464
618
  design system, global styles). Describe what they provide; they belong here
465
619
  precisely because no slice owns them.
466
- - `aggregates/<feature>.md` (type: Aggregate) — ONE per feature root, SHALLOW:
620
+ - `aggregates/<aggregate>.md` (type: Aggregate) — ONE per aggregate, SHALLOW, its
621
+ `title:` EXACTLY the aggregate's name as the map spells it (`MedicinalProduct`,
622
+ never `Medicinal Product` — later commands find the concept by that name):
467
623
  Responsibilities; the entities / value objects / domain events / commands / API
468
624
  endpoints / persistence it OWNS (named at a glance from the signal's layer
469
- files — a list, not a deep model); and the candidate `specs/<Feature>/` slice
470
- name (feature = aggregate). Set `source_files` to the module's files, grouped
625
+ files — a list, not a deep model); and `features:` in frontmatter — the map's
626
+ features that act on it, each of which is a `specs/{{bc}}/<Feature>/` directory
627
+ this run creates. Set `source_files` to the module's files, grouped
471
628
  with YAML comment headers by each file's OWN folder — the path segment that
472
629
  VARIES across the files after their shared parent (the layer folder in a
473
630
  vertical slice; the tier project in an n-tier app; tests → a single `tests`
@@ -479,18 +636,25 @@ workflow:
479
636
  is and move or drop one it got wrong. Keep the key and leave it EMPTY where the
480
637
  aggregate has no UI (and on a backend-only repo, where every concept has none);
481
638
  never fold these paths into `source_files`, which stays the service surface.
482
- - `aggregates/index.md` — the bounded-context / feature map (no frontmatter beyond
483
- OKF index rules) linking every feature concept.
639
+ - `aggregates/index.md` — the BOUNDED-CONTEXT / feature map (no frontmatter beyond
640
+ OKF index rules). Group the links under a `## <BoundedContext>` heading per
641
+ context, so the file shows which aggregates belong to which context rather than one
642
+ flat list, each link followed by the features that act on that aggregate. Add a
643
+ `## {{bc}}` section with this context's aggregates; leave every
644
+ other section exactly as it is.
484
645
  - An `index.md` in EVERY subdirectory (no frontmatter).
485
- - `log.md` (bundle root) — first line under a `## <today's date>` heading must be
486
- `Commit: ` + backtick + `{{head}}` + backtick (validator rule E4), then a
487
- one-line Initialization entry noting the concept count.
646
+ - `log.md` (bundle root) — APPEND under a `## <today's date>` heading whose first
647
+ line is `Commit: ` + backtick + `{{head}}` + backtick (validator rule E4), then a
648
+ one-line entry naming the bounded context onboarded and its concept count. Keep
649
+ every earlier dated entry.
488
650
  Do NOT write a migration-backlog, a seam/clean-torn analysis, or an adopt-order —
489
651
  those are migration artifacts and are out of scope for the development flow.
490
652
 
491
653
  Provenance + safety (OKF hard rules):
492
- - Stamp every concept `origin: reverse-engineered` and `generated_by:
493
- aw-spec-onboard/1.6.0` in frontmatter.
654
+ - Stamp every concept `origin: reverse-engineered`, `generated_by:
655
+ aw-spec-onboard/2.0.0` and `bounded_context: {{bc}}` in frontmatter. The
656
+ context key is what lets later commands filter features by the context they
657
+ belong to instead of listing every aggregate under every one.
494
658
  - The CODE is the source of truth; capture what IS (quirks included), not what
495
659
  SHOULD be. Where intent is unclear from code + git history, add a concrete
496
660
  `open_questions` entry rather than inventing one.
@@ -525,6 +689,76 @@ workflow:
525
689
  on_false: stop
526
690
  on_false_message: "The OKF feature map 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."
527
691
 
692
+ # ═══════════ THE CONTEXT'S SPECS TREE — specs/<Context>/<Feature>/ ═══════════
693
+ # The concepts above say what this context CONTAINS; these two steps make that
694
+ # shape exist where every later command looks for it. Until now a context left
695
+ # no trace under specs/ at all: `list-contexts` reads the tree rather than the
696
+ # bundle, so the context it had just onboarded reported as absent, and
697
+ # select-feature's refusal-to-guess gate — contexts exist, none is active —
698
+ # could not even be reached. select-feature merges the bundle's aggregates into
699
+ # its list to cover the gap, but a feature that exists only as a concept has
700
+ # nowhere for adopt to write, and picking one would create its directory at
701
+ # whatever base happened to be active.
702
+ #
703
+ # DIRECTORIES ONLY. No spec.md, no domain-model.md, no plan: adopt reverse-
704
+ # engineers the first per feature and specify authors the second per context,
705
+ # and a placeholder for either reads as "the document exists" to every gate
706
+ # downstream. The directory says the feature is KNOWN; the documents say it is
707
+ # described.
708
+ - id: activate_context
709
+ action: execute_script
710
+ script: { ref: actions }
711
+ subcommand: set-context
712
+ args: ["--context", "{{bc}}"]
713
+ outputs:
714
+ - { name: bc_dir, from: "$.context_dir" }
715
+ - { name: bc_domain_model, from: "$.domain_model" }
716
+ - { name: bc_dir_created, from: "$.created" }
717
+
718
+ - id: scaffold_features
719
+ action: execute_script
720
+ script: { ref: inventory }
721
+ subcommand: scaffold-context
722
+ args: ["--context", "{{bc}}", "--map", "{{feature_map}}"]
723
+ on_error: continue
724
+ outputs:
725
+ - { name: scaffold_ok, from: "$.ok" }
726
+ - { name: feature_dirs, from: "$.feature_dirs" }
727
+ - { name: dirs_created, from: "$.created" }
728
+ - { name: dirs_created_num, from: "$.created_count" }
729
+ - { name: dirs_rejected, from: "$.rejected" }
730
+ - { name: dirs_rejected_any, from: "$.rejected_present" }
731
+ - { name: scaffold_error, from: "$.error" }
732
+
733
+ - id: scaffold_gate
734
+ action: verify_context
735
+ require_value: scaffold_ok
736
+ equals: "true"
737
+ on_mismatch: stop
738
+ on_mismatch_message: >
739
+ The feature directories for {{bc}} could not be created: {{scaffold_error}}
740
+ The OKF concepts for this context ARE written — only the specs/ tree is
741
+ missing, so fix the name the message reports in specs/proposed-inventory.md,
742
+ run /appweaver inventory apply, and re-run onboard for this context.
743
+
744
+ # A feature name the map carries that is not a usable directory identifier. Named
745
+ # rather than dropped silently: the concept exists, so the feature is pickable from
746
+ # the bundle and would land in a directory nobody chose.
747
+ # The never-satisfiable `equals` is the engine's announce-only idiom: the step is
748
+ # entered only when there IS something to report, and the comparison is written so
749
+ # it always misses, so the message is emitted and the run continues.
750
+ - id: scaffold_name_warning
751
+ action: verify_context
752
+ condition: "{{dirs_rejected_any}} == true"
753
+ require_value: dirs_rejected_any
754
+ equals: "never"
755
+ on_mismatch: continue
756
+ on_mismatch_message: >
757
+ These feature names are not valid directory identifiers and got no directory:
758
+ {{dirs_rejected}}. A feature name has to be a letter-led alphanumeric aggregate
759
+ (Order, MedicinalProduct). Rename them in specs/proposed-inventory.md, re-run
760
+ /appweaver inventory apply, then /appweaver select-feature will offer them.
761
+
528
762
  # ═══════════════ PART 2 — GOVERNANCE SCAFFOLD (project-level) ════════════════
529
763
  # Establish the governing tier ONCE, from the feature map just written (the whole-
530
764
  # repo vantage). No slice is adopted, so list-adopted returns slice_count=0 but STILL
@@ -547,11 +781,16 @@ workflow:
547
781
  # Slugify + `brf-` prefix the domain name and create guardrails/projects/brf-<domain>/.
548
782
  # Name: explicit arg → PROJECT name from the OKF overview → repo basename. Guarded
549
783
  # against clobbering a project that already holds .md files.
784
+ # The governance project is named from the PROJECT — `gov_name` is empty, so
785
+ # project-setup falls back to the project name from the OKF overview and then
786
+ # to the repo basename. Passing the bounded context here would create one rule
787
+ # pack per context and split the project's constitution along context lines,
788
+ # which is the aggregate-scoping bias in a new place.
550
789
  - id: pack_setup
551
790
  action: execute_script
552
791
  script: { ref: revcon }
553
792
  subcommand: project-setup
554
- args: ["--name", "{{USER_INPUT}}", "--fallback", "{{project_name}}"]
793
+ args: ["--name", "{{gov_name}}", "--fallback", "{{project_name}}"]
555
794
  outputs:
556
795
  - { name: domain_slug, from: "$.domain_slug" }
557
796
  - { name: pack_dir, from: "$.pack_dir" }
@@ -559,18 +798,54 @@ workflow:
559
798
  - { name: domain_constitution_source, from: "$.domain_constitution_source" }
560
799
  - { name: pack_present, from: "$.pack_present" }
561
800
 
562
- - id: pack_guard
563
- action: verify_context
564
- require_value: pack_present
565
- equals: "false"
566
- on_mismatch: stop
567
- on_mismatch_message: "The project guardrails/projects/{{domain_slug}}/ already holds curated rule files — onboard would overwrite them. Back up or remove that folder to re-onboard, or amend it via /appweaver constitution."
801
+ # An existing pack is no longer a reason to stop — it is the signal that the
802
+ # project tier has already been authored and this run is onboarding a LATER
803
+ # bounded context. The tier is written once and amended by /appweaver
804
+ # constitution; what each later context still contributes is its own OKF
805
+ # concepts and its own domain model.
806
+ - id: repo_gov_present
807
+ action: set
808
+ condition: "{{pack_present}} == true"
809
+ vars: { repo_gov: "true" }
810
+
811
+ # WHAT THIS RUN AUTHORS. The engine compares one variable against one value and
812
+ # has no compound conditions, so "this lane exists AND the project tier has not
813
+ # been written yet" is computed here as an ordered pair of sets rather than
814
+ # asked at each step: default to the lane declaration, then clear everything
815
+ # once the project tier is present.
816
+ #
817
+ # The second set also SATISFIES the lint guards the skipped block would
818
+ # otherwise leave at their "not yet verified" default — the same move
819
+ # svc_lane_absent makes for a repo with no service lane. Without it a later
820
+ # context would stop at a gate checking work this run correctly did not do.
821
+ #
822
+ # ⌛ OPEN: should a LATER context amend the rule sets? Skipping the whole tier
823
+ # also skips the code-derived rule files, and a second context can evidence a
824
+ # domain or technical concern the first one's source never showed — that
825
+ # evidence reaches nothing today. The two halves of the tier differ here. The
826
+ # constitution halves, principle numbering, domain activation and the document
827
+ # templates are project-scoped, and re-deriving them per context IS the
828
+ # aggregate-scoping bias rev-constitution v1.1.0 removed; /appweaver
829
+ # constitution is their amendment path. The rule SETS
830
+ # (guardrails/projects/<p>/{domain,service,frontend}/) are evidenced-only, so
831
+ # amending them ADDITIVELY from a later context — add a rule for a concern this
832
+ # context exhibits, never rewrite or renumber an existing one — would be more
833
+ # evidence at the same project scope rather than one context's bias. That
834
+ # additive pass is not built; author-once is what runs.
835
+ - id: author_flags_default
836
+ action: set
837
+ vars: { author_repo: "true", author_svc: "{{svc_lane}}", author_ui_flag: "{{ui_lane}}" }
838
+ - id: author_flags_skip
839
+ action: set
840
+ condition: "{{repo_gov}} == true"
841
+ vars: { author_repo: "false", author_svc: "false", author_ui_flag: "false", technical_ok: "true", domain_ok: "true", ui_ok: "true", numbering_ok: "true" }
568
842
 
569
843
  # ── DOMAIN RULE SET (provisional, evidenced by the SCAN) ────────────────────
570
844
  # Feed NEUTRAL shape skeletons (rev-templates/, zero content to leak) + the rule-
571
845
  # partition authority + the project context (the feature map). NO adopted specs
572
846
  # exist — the evidence is the OKF overview + aggregates/index + platform.
573
847
  - id: ctx_pack
848
+ condition: "{{author_repo}} == true"
574
849
  action: build_context
575
850
  context:
576
851
  - { path: "{{inventory_json}}" } # the COMPLETE deterministic scan substrate — read representative SOURCE from its file lists (the OKF map only SUMMARISES this)
@@ -580,6 +855,7 @@ workflow:
580
855
  - { path: .appweaver/RULE-PARTITION.md } # the rule-partition authority (which command reads what)
581
856
  context_from: project_context
582
857
  - id: author_pack
858
+ condition: "{{author_repo}} == true"
583
859
  action: agent_generate
584
860
  tools: "Read,Write,Edit"
585
861
  permission_mode: acceptEdits
@@ -648,8 +924,8 @@ workflow:
648
924
  charter; a later step in this same run authors it, so wire the bullet now), then the
649
925
  domain identity/regulatory/interop files → domain principles.
650
926
  - `## /aw-spec-specify` — the FULL domain set (specify models the domain, lifts CN-/DC-/IC-).
651
- - `## /aw-spec-plan` — `_None — design loads no domain file. Domain reaches design only through the spec._`
652
- - `## /aw-spec-implement` — `_None — implement loads no domain file. Domain reaches implement only through design.md._`
927
+ - `## /aw-spec-plan` — `_None — plan loads no domain file. Domain reaches plan through the spec and the context's domain-model.md._`
928
+ - `## /aw-spec-implement` — `_None — implement loads no domain file. Domain reaches implement through the lane plans and the context's domain-model.md._`
653
929
  - `## /aw-spec-gate` — the conformance-checklist set (the *.domain.md files).
654
930
  Each bullet is a workspace-relative path `- guardrails/projects/{{domain_slug}}/domain/<file>`.
655
931
  Write ONLY inside guardrails/projects/{{domain_slug}}/ (domain/ + the map). Report the
@@ -658,11 +934,13 @@ workflow:
658
934
  - { name: pack_files, from: "$.files" }
659
935
 
660
936
  - id: verify_pack
937
+ condition: "{{author_repo}} == true"
661
938
  action: verify_context
662
939
  require_non_empty: pack_files
663
940
  on_empty: stop
664
941
  on_empty_message: "No domain rule files were written to guardrails/projects/{{domain_slug}}/domain/. Ensure the feature map carries a modellable domain (a feature map + ubiquitous language) and re-run."
665
942
  - id: require_domain_rule_bindings
943
+ condition: "{{author_repo}} == true"
666
944
  action: verify_context
667
945
  required_files: ["{{domain_rule_bindings}}"]
668
946
 
@@ -670,12 +948,14 @@ workflow:
670
948
  # Named and placed exactly as a greenfield industry corpus names and places its charter,
671
949
  # and already wired as the first `## /aw-spec-constitution` bullet by the step above.
672
950
  - id: ctx_domain_source
951
+ condition: "{{author_repo}} == true"
673
952
  action: build_context
674
953
  context:
675
954
  - { path: "{{domain_rule_bindings}}" }
676
955
  - { path: .appweaver/orchestrator/rev-templates/domain-constitution.skeleton.md, optional: true }
677
956
  context_from: project_context
678
957
  - id: author_domain_source
958
+ condition: "{{author_repo}} == true"
679
959
  action: agent_author
680
960
  include_standards: false
681
961
  output_target: { type: file, path: "{{domain_constitution_source}}" }
@@ -718,6 +998,7 @@ workflow:
718
998
  # do, so a later call without the flag silently recreates the half an earlier call
719
999
  # was told to skip.
720
1000
  - id: setup_constitution
1001
+ condition: "{{author_repo}} == true"
721
1002
  action: execute_script
722
1003
  script: { ref: actions }
723
1004
  subcommand: setup-constitution
@@ -731,7 +1012,7 @@ workflow:
731
1012
  # set, which is empty), so nothing leaks into the Part-1 feature-map agent.
732
1013
  - id: ctx_technical
733
1014
  action: build_context
734
- condition: "{{svc_lane}} == true"
1015
+ condition: "{{author_svc}} == true"
735
1016
  context:
736
1017
  - { path: "{{constitution_technical_file}}" }
737
1018
  - { path: .appweaver/orchestrator/rev-templates/technical-constitution.skeleton.md, optional: true }
@@ -739,7 +1020,7 @@ workflow:
739
1020
  context_from: design_context
740
1021
  - id: author_technical
741
1022
  action: agent_author
742
- condition: "{{svc_lane}} == true"
1023
+ condition: "{{author_svc}} == true"
743
1024
  include_standards: false
744
1025
  output_target: { type: file, path: "{{constitution_technical_file}}" }
745
1026
  task: |
@@ -782,7 +1063,7 @@ workflow:
782
1063
 
783
1064
  - id: technical_validation_loop
784
1065
  action: loop
785
- condition: "{{svc_lane}} == true"
1066
+ condition: "{{author_svc}} == true"
786
1067
  while: "{{technical_ok}} != true"
787
1068
  max_iterations: 3
788
1069
  steps:
@@ -827,7 +1108,7 @@ workflow:
827
1108
  # ABSENCE is meaningful: a repo with no UI never gets the file at all.
828
1109
  - id: setup_ui_constitution
829
1110
  action: execute_script
830
- condition: "{{ui_lane}} == true"
1111
+ condition: "{{author_ui_flag}} == true"
831
1112
  script: { ref: actions }
832
1113
  subcommand: setup-constitution
833
1114
  args: ["--ui", "{{setup_tech_flag}}"]
@@ -837,7 +1118,7 @@ workflow:
837
1118
 
838
1119
  - id: ctx_ui
839
1120
  action: build_context
840
- condition: "{{ui_lane}} == true"
1121
+ condition: "{{author_ui_flag}} == true"
841
1122
  context:
842
1123
  - { path: "{{constitution_ui_file}}" } # the target half being filled
843
1124
  - { path: "{{inventory_json}}" } # PRIMARY: the COMPLETE deterministic substrate — frontend_signals + the UI file lists to read source from
@@ -845,7 +1126,7 @@ workflow:
845
1126
  - { path: "{{bundle_dir}}/architecture/frontend.md", optional: true } # SECONDARY (shallow): the OKF as-built frontend note — corroborate, don't rely on alone
846
1127
  - id: author_ui
847
1128
  action: agent_author
848
- condition: "{{ui_lane}} == true"
1129
+ condition: "{{author_ui_flag}} == true"
849
1130
  include_standards: false
850
1131
  tools: Read # EVIDENCE = THE UI CODE ITSELF; the inventory inlines the file LISTS, not the files
851
1132
  output_target: { type: file, path: "{{constitution_ui_file}}" }
@@ -914,7 +1195,7 @@ workflow:
914
1195
 
915
1196
  - id: ui_validation_loop
916
1197
  action: loop
917
- condition: "{{ui_lane}} == true"
1198
+ condition: "{{author_ui_flag}} == true"
918
1199
  while: "{{ui_ok}} != true"
919
1200
  max_iterations: 3
920
1201
  steps:
@@ -947,15 +1228,17 @@ workflow:
947
1228
  # repo no half was created, so the run reports that rather than a bare false a reader
948
1229
  # would take for a half that needs a manual touch.
949
1230
  - id: ui_validation_state_absent
1231
+ condition: "{{author_repo}} == true"
950
1232
  action: set
951
1233
  vars: { ui_validation_state: "n/a — no frontend lane, no UI half authored" }
952
1234
  - id: ui_validation_state_present
953
1235
  action: set
954
- condition: "{{ui_lane}} == true"
1236
+ condition: "{{author_ui_flag}} == true"
955
1237
  vars: { ui_validation_state: "{{ui_ok}}" }
956
1238
 
957
1239
  # DOMAIN HALF — from the project + charter, principles continue the technical numbering.
958
1240
  - id: ctx_domain
1241
+ condition: "{{author_repo}} == true"
959
1242
  action: build_context
960
1243
  context:
961
1244
  - { path: "{{constitution_domain_file}}" }
@@ -964,6 +1247,7 @@ workflow:
964
1247
  - { path: "{{domain_rule_bindings}}" }
965
1248
  context_from: project_context
966
1249
  - id: author_domain
1250
+ condition: "{{author_repo}} == true"
967
1251
  action: agent_author
968
1252
  include_standards: false
969
1253
  output_target: { type: file, path: "{{constitution_domain_file}}" }
@@ -1004,6 +1288,7 @@ workflow:
1004
1288
  Return the complete updated domain addenda content only.
1005
1289
 
1006
1290
  - id: domain_validation_loop
1291
+ condition: "{{author_repo}} == true"
1007
1292
  action: loop
1008
1293
  while: "{{domain_ok}} != true"
1009
1294
  max_iterations: 3
@@ -1035,6 +1320,7 @@ workflow:
1035
1320
 
1036
1321
  # ── COMPOSE + ACTIVATE ───────────────────────────────────────────────────────
1037
1322
  - id: compose_constitution
1323
+ condition: "{{author_repo}} == true"
1038
1324
  action: execute_script
1039
1325
  script: { ref: actions }
1040
1326
  subcommand: compose-constitution
@@ -1057,6 +1343,7 @@ workflow:
1057
1343
  # Ordered override — the var holds "ok" unless the check reported a problem, because
1058
1344
  # require_non_empty reports on EMPTY.
1059
1345
  - id: numbering_default
1346
+ condition: "{{author_repo}} == true"
1060
1347
  action: set
1061
1348
  vars: { numbering_gate: "ok" }
1062
1349
  - id: numbering_detect
@@ -1064,6 +1351,7 @@ workflow:
1064
1351
  condition: "{{numbering_ok}} == false"
1065
1352
  vars: { numbering_gate: "" }
1066
1353
  - id: numbering_warning
1354
+ condition: "{{author_repo}} == true"
1067
1355
  action: verify_context
1068
1356
  require_non_empty: numbering_gate
1069
1357
  on_empty: continue
@@ -1079,6 +1367,7 @@ workflow:
1079
1367
  # Auto-activate: point .appweaver/domain.json at the scaffolded project so the forward CR
1080
1368
  # loop governs development with it from the first change.
1081
1369
  - id: activate_domain
1370
+ condition: "{{author_repo}} == true"
1082
1371
  action: execute_script
1083
1372
  script: { ref: actions }
1084
1373
  subcommand: set-domain
@@ -1110,7 +1399,7 @@ workflow:
1110
1399
  # lives inside pack_dir, so pack_guard (above) already protects it from clobber.
1111
1400
  - id: ctx_tech_pack
1112
1401
  action: build_context
1113
- condition: "{{svc_lane}} == true"
1402
+ condition: "{{author_svc}} == true"
1114
1403
  context:
1115
1404
  - { path: "{{inventory_json}}" } # PRIMARY: the COMPLETE deterministic substrate — read representative SOURCE from its file lists
1116
1405
  - { path: "{{constitution_technical_file}}" } # PRIMARY: the VALIDATED as-built technology base — the ratified principles these files detail
@@ -1121,7 +1410,7 @@ workflow:
1121
1410
  context_from: design_context
1122
1411
  - id: author_tech_pack
1123
1412
  action: agent_generate
1124
- condition: "{{svc_lane}} == true"
1413
+ condition: "{{author_svc}} == true"
1125
1414
  tools: "Read,Write,Edit"
1126
1415
  permission_mode: acceptEdits
1127
1416
  task: |
@@ -1209,11 +1498,12 @@ workflow:
1209
1498
 
1210
1499
  - id: verify_tech_pack
1211
1500
  action: verify_context
1212
- condition: "{{svc_lane}} == true"
1501
+ condition: "{{author_svc}} == true"
1213
1502
  require_non_empty: tech_pack_files
1214
1503
  on_empty: stop
1215
1504
  on_empty_message: "No technical rule files were written to guardrails/projects/{{domain_slug}}/service/. Ensure the scan substrate (inventory_json) + the validated technical constitution carry an as-built technology base and re-run."
1216
1505
  - id: require_tech_rule_bindings
1506
+ condition: "{{author_repo}} == true"
1217
1507
  action: verify_context
1218
1508
  required_files: ["{{pack_dir}}/tech-rule-bindings.md"]
1219
1509
 
@@ -1226,7 +1516,7 @@ workflow:
1226
1516
  # file, and the run's remaining work is worth keeping either way.
1227
1517
  - id: probe_specify_wiring
1228
1518
  action: execute_script
1229
- condition: "{{svc_lane}} == true"
1519
+ condition: "{{author_svc}} == true"
1230
1520
  on_error: continue
1231
1521
  script: { ref: actions }
1232
1522
  subcommand: resolve-tech
@@ -1235,7 +1525,7 @@ workflow:
1235
1525
  - { name: specify_rules, from: "$.candidates" }
1236
1526
  - id: warn_specify_wiring
1237
1527
  action: verify_context
1238
- condition: "{{svc_lane}} == true"
1528
+ condition: "{{author_svc}} == true"
1239
1529
  require_non_empty: specify_rules
1240
1530
  on_empty: continue
1241
1531
  on_empty_message: >
@@ -1259,7 +1549,7 @@ workflow:
1259
1549
  # find the lane on every future run without stacks.json or a named stack.
1260
1550
  - id: ctx_ui_pack
1261
1551
  action: build_context
1262
- condition: "{{ui_lane}} == true"
1552
+ condition: "{{author_ui_flag}} == true"
1263
1553
  context:
1264
1554
  - { path: "{{inventory_json}}" } # PRIMARY: the COMPLETE deterministic substrate — frontend_signals + the UI file lists to read source from
1265
1555
  - { path: "{{constitution_ui_file}}" } # PRIMARY: the VALIDATED as-built UX-* base — the ratified principles these files detail
@@ -1270,7 +1560,7 @@ workflow:
1270
1560
  - { path: "{{bundle_dir}}/architecture/frontend.md", optional: true } # SECONDARY (shallow): the OKF as-built frontend note
1271
1561
  - id: author_ui_pack
1272
1562
  action: agent_generate
1273
- condition: "{{ui_lane}} == true"
1563
+ condition: "{{author_ui_flag}} == true"
1274
1564
  tools: "Read,Write,Edit"
1275
1565
  permission_mode: acceptEdits
1276
1566
  task: |
@@ -1342,7 +1632,7 @@ workflow:
1342
1632
 
1343
1633
  - id: verify_ui_pack
1344
1634
  action: verify_context
1345
- condition: "{{ui_lane}} == true"
1635
+ condition: "{{author_ui_flag}} == true"
1346
1636
  require_non_empty: ui_pack_files
1347
1637
  on_empty: stop
1348
1638
  on_empty_message: "A frontend lane was detected ({{ui_evidence}}) but no lane rule files were written to guardrails/projects/{{domain_slug}}/frontend/. Check that the scan substrate's frontend_signals carry readable UI source, then re-run."
@@ -1354,7 +1644,7 @@ workflow:
1354
1644
  # it. Announce, don't stop — the rule files are on disk and the heading is one edit away.
1355
1645
  - id: probe_ui_wiring
1356
1646
  action: execute_script
1357
- condition: "{{ui_lane}} == true"
1647
+ condition: "{{author_ui_flag}} == true"
1358
1648
  on_error: continue
1359
1649
  script: { ref: actions }
1360
1650
  subcommand: resolve-tech
@@ -1363,7 +1653,7 @@ workflow:
1363
1653
  - { name: ui_map_rules, from: "$.candidates" }
1364
1654
  - id: warn_ui_wiring
1365
1655
  action: verify_context
1366
- condition: "{{ui_lane}} == true"
1656
+ condition: "{{author_ui_flag}} == true"
1367
1657
  require_non_empty: ui_map_rules
1368
1658
  on_empty: continue
1369
1659
  on_empty_message: >
@@ -1391,6 +1681,7 @@ workflow:
1391
1681
  # the run reports the lanes it actually scanned under rather than a second opinion
1392
1682
  # about them.
1393
1683
  - id: lane_readback
1684
+ condition: "{{author_repo}} == true"
1394
1685
  action: execute_script
1395
1686
  on_error: continue
1396
1687
  script: { ref: actions }
@@ -1415,7 +1706,13 @@ workflow:
1415
1706
  # override_active ⇒ design_mode == bootstrap_bf) then authors EVERY new feature against the
1416
1707
  # as-built architecture, and a CHANGE authors its delta-design against the SAME code-derived
1417
1708
  # sections (cr-setup scaffolds the delta from resolve_template("delta-design-template") ⇒
1418
- # the override) — no adopt required, consistent with the master design.md adopt emits.
1709
+ # the override) — no adopt required, consistent with the backend-plan.md adopt emits.
1710
+ #
1711
+ # THE TEMPLATE IDENTIFIERS KEEP THE `design-template` SPELLING DELIBERATELY. The DOCUMENTS
1712
+ # are backend-plan.md / frontend-plan.md now; the template NAMES are not, because a repo
1713
+ # that already ran onboard carries its override at overrides/design-template.md and
1714
+ # renaming the identifier would make that file stop resolving — silently falling back to
1715
+ # the kit's greenfield template and imposing a layering the codebase never chose.
1419
1716
  #
1420
1717
  # GROUNDED IN CODE + REVERSE-ENGINEERED FILES ONLY. The Part-1 OKF feature map is a SHALLOW
1421
1718
  # discovery index (by design — NOT a deep reverse), so it is NOT the authority: the section
@@ -1444,7 +1741,7 @@ workflow:
1444
1741
  # plus a NEUTRAL skeleton for the bones. No greenfield template is in context.
1445
1742
  - id: ctx_design_template
1446
1743
  action: build_context
1447
- condition: "{{svc_lane}} == true"
1744
+ condition: "{{author_svc}} == true"
1448
1745
  context:
1449
1746
  - { path: "{{inventory_json}}" } # PRIMARY: the COMPLETE deterministic substrate (top_level_dirs, aggregate_signals' per-layer file groupings) — the OKF markdown only SUMMARISES this
1450
1747
  - { path: "{{constitution_technical_file}}" } # PRIMARY: the VALIDATED as-built technology base (framework/persistence/concurrency/hosting/testing/cross-cutting)
@@ -1455,7 +1752,7 @@ workflow:
1455
1752
  - { path: "{{bundle_dir}}/aggregates/index.md", optional: true } # SECONDARY (shallow): the bounded-context / feature map
1456
1753
  - id: gen_design_template
1457
1754
  action: agent_generate
1458
- condition: "{{svc_lane}} == true"
1755
+ condition: "{{author_svc}} == true"
1459
1756
  tools: "Read,Write,Edit"
1460
1757
  permission_mode: acceptEdits
1461
1758
  task: |
@@ -1545,7 +1842,7 @@ workflow:
1545
1842
 
1546
1843
  - id: verify_design_template
1547
1844
  action: verify_context
1548
- condition: "{{svc_lane}} == true"
1845
+ condition: "{{author_svc}} == true"
1549
1846
  require_non_empty: design_template_files
1550
1847
  on_empty: stop
1551
1848
  on_empty_message: "No design template was written to .appweaver/templates/overrides/design-template.md. Ensure the scan substrate (inventory_json) + the technical constitution are present and re-run."
@@ -1556,14 +1853,14 @@ workflow:
1556
1853
  # master's OWN section structure").
1557
1854
  - id: ctx_delta_design_template
1558
1855
  action: build_context
1559
- condition: "{{svc_lane}} == true"
1856
+ condition: "{{author_svc}} == true"
1560
1857
  context:
1561
1858
  - { path: .appweaver/templates/overrides/design-template.md, optional: true } # PRIMARY: the JUST-generated code-derived master — mirror its ACTUAL sections
1562
1859
  - { path: .appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md } # SHAPE ONLY: the neutral change-record bones (zero layering, zero technology)
1563
1860
  - { path: "{{constitution_technical_file}}" } # the validated as-built technology base (keep the delta consistent with the master)
1564
1861
  - id: gen_delta_design_template
1565
1862
  action: agent_generate
1566
- condition: "{{svc_lane}} == true"
1863
+ condition: "{{author_svc}} == true"
1567
1864
  tools: "Read,Write,Edit"
1568
1865
  permission_mode: acceptEdits
1569
1866
  task: |
@@ -1592,11 +1889,91 @@ workflow:
1592
1889
 
1593
1890
  - id: verify_delta_design_template
1594
1891
  action: verify_context
1595
- condition: "{{svc_lane}} == true"
1892
+ condition: "{{author_svc}} == true"
1596
1893
  require_non_empty: delta_design_template_files
1597
1894
  on_empty: stop
1598
1895
  on_empty_message: "No delta-design template was written to .appweaver/templates/overrides/delta-design-template.md. Re-run onboard after confirming the design template override was generated."
1599
1896
 
1897
+ # ═══════ CODE-DERIVED DOMAIN-MODEL TEMPLATE (the context's structure) ════════
1898
+ # The same argument as the design pair, for the document that carries the DOMAIN. A
1899
+ # bounded context's domain-model.md is where the aggregate root, its boundary, its
1900
+ # entities and the TEXT of every BR-nnn live; `specify` fills it on the forward path and
1901
+ # `adopt` fills it per reversed slice. Without a code-derived override both author
1902
+ # against the kit's GREENFIELD model template, whose shape assumes a domain that was
1903
+ # designed before it was built — the one assumption a brownfield repo is guaranteed to
1904
+ # break.
1905
+ #
1906
+ # NOT GATED ON A LANE. The domain model is ubiquitous language and invariants, which a
1907
+ # frontend-only project has as much as any other; WHERE an invariant is enforced is the
1908
+ # backend plan's concern and is gated with the service block above.
1909
+ #
1910
+ # A TEMPLATE OF A TEMPLATE. What this writes is not a filled model — it is the project's
1911
+ # domain-model TEMPLATE, keeping every [PLACEHOLDER] and authoring comment. What onboard
1912
+ # replaces is the STRUCTURE, never the content, and it must name NO aggregate and NO
1913
+ # entity: naming one would hand the downstream author the answer to the single question
1914
+ # the reverse exists to ask the code.
1915
+ - id: ctx_model_template
1916
+ condition: "{{author_repo}} == true"
1917
+ action: build_context
1918
+ context:
1919
+ - { path: "{{inventory_json}}" } # PRIMARY: the COMPLETE deterministic substrate
1920
+ - { path: .appweaver/orchestrator/rev-templates/domain-model-template.skeleton.md } # SHAPE ONLY: the neutral bones + the pinned anchors
1921
+ - { path: "{{constitution_domain_file}}", optional: true } # the reversed DOMAIN frame — vocabulary, not structure
1922
+ - { path: "{{bundle_dir}}/aggregates/index.md", optional: true } # SECONDARY (shallow): the bounded-context / feature map
1923
+ - id: gen_model_template
1924
+ condition: "{{author_repo}} == true"
1925
+ action: agent_generate
1926
+ tools: "Read,Write,Edit"
1927
+ permission_mode: acceptEdits
1928
+ task: |
1929
+ REVERSE-ENGINEER this repo's DOMAIN MODEL convention into a PROJECT-SPECIFIC
1930
+ domain-model TEMPLATE and Write it to
1931
+ .appweaver/templates/overrides/domain-model-template.md (create the overrides/ dir if
1932
+ absent). It is a TEMPLATE — section headers with [PLACEHOLDER] tokens + authoring
1933
+ HTML comments that `specify` (forward) and `adopt` (per reversed slice) fill per
1934
+ context — NOT a filled model.
1935
+
1936
+ GROUND EVERYTHING IN THE CODE. Read REPRESENTATIVE source files; the OKF map in
1937
+ context is a SHALLOW discovery index and corroborates, it does not decide.
1938
+
1939
+ KEEP THE PINNED ANCHORS from the skeleton in context — each is machinery something
1940
+ keys on, not taste:
1941
+ - `## Aggregate: <Name>` is the MERGE UNIT. finalize-delta merges a model delta one
1942
+ such section at a time, so the heading FORM cannot be reshaped per project. Keep it
1943
+ as ONE repeatable block.
1944
+ - `### Invariants` carries stable BR-nnn ids — specs cite them and the conformance
1945
+ gates trace them, so the id class cannot be renamed.
1946
+ - `**Serving specs**:` is what makes a cross-spec aggregate visible from the model
1947
+ side; keep it.
1948
+ - The Change History table is the model's version ledger. close-delta appends to it
1949
+ and stamps the context version; the model never writes its own.
1950
+
1951
+ WHAT YOU MAY ADAPT is everything else: which subsections an aggregate section carries
1952
+ and what they are CALLED, in this codebase's own vocabulary. If the code expresses
1953
+ structure through something it names differently, template THAT and say in a comment
1954
+ which code paths evidence it.
1955
+
1956
+ NAME NO AGGREGATE, NO ENTITY AND NO TECHNOLOGY. Every [BRACKET] token stays a token.
1957
+ Write the authoring comments so the downstream author derives from the code the way
1958
+ the skeleton's BROWNFIELD DERIVATION note describes — aggregate roots from the types
1959
+ referenced by id but never navigated into and the transaction boundaries the
1960
+ persistence layer commits; entity vs value object by IDENTITY; invariants from the
1961
+ guards and the persistence constraints, recording that the CODE is their current
1962
+ authority; the ubiquitous language from the names the code actually uses, recording
1963
+ BOTH where code and business disagree rather than silently picking one.
1964
+
1965
+ Write ONLY the override file; modify no source. Report the file written and the
1966
+ structural choices you derived, with the code paths that evidence them.
1967
+ outputs:
1968
+ - { name: model_template_files, from: "$.files" }
1969
+
1970
+ - id: verify_model_template
1971
+ condition: "{{author_repo}} == true"
1972
+ action: verify_context
1973
+ require_non_empty: model_template_files
1974
+ on_empty: stop
1975
+ on_empty_message: "No domain-model template was written to .appweaver/templates/overrides/domain-model-template.md. Without it every reversed slice authors its aggregate against the kit's GREENFIELD model template, whose shape assumes a domain designed before it was built. Ensure the scan substrate (inventory_json) is present and re-run."
1976
+
1600
1977
  # ═══════ UI DESIGN TEMPLATES (frontend lane only) — the LANE's own pair ═══════
1601
1978
  # The lane's twin of the two steps above, on the same terms: reverse-engineered from
1602
1979
  # the UI SOURCE, shaped by a NEUTRAL skeleton, installed in the project override slot
@@ -1625,7 +2002,7 @@ workflow:
1625
2002
  # nothing breaks for their absence in a repo that never onboarded.
1626
2003
  - id: ctx_ui_design_template
1627
2004
  action: build_context
1628
- condition: "{{ui_lane}} == true"
2005
+ condition: "{{author_ui_flag}} == true"
1629
2006
  context:
1630
2007
  - { path: "{{inventory_json}}" } # PRIMARY: the COMPLETE deterministic substrate — frontend_signals + the component/route/store/style file lists to read source from
1631
2008
  - { path: "{{constitution_ui_file}}" } # PRIMARY: the VALIDATED as-built UX-* base — the ratified principles this structure must serve
@@ -1634,7 +2011,7 @@ workflow:
1634
2011
  - { path: "{{bundle_dir}}/architecture/frontend.md", optional: true } # SECONDARY (shallow): the OKF as-built frontend note
1635
2012
  - id: gen_ui_design_template
1636
2013
  action: agent_generate
1637
- condition: "{{ui_lane}} == true"
2014
+ condition: "{{author_ui_flag}} == true"
1638
2015
  tools: "Read,Write,Edit"
1639
2016
  permission_mode: acceptEdits
1640
2017
  task: |
@@ -1712,7 +2089,7 @@ workflow:
1712
2089
 
1713
2090
  - id: verify_ui_design_template
1714
2091
  action: verify_context
1715
- condition: "{{ui_lane}} == true"
2092
+ condition: "{{author_ui_flag}} == true"
1716
2093
  require_non_empty: ui_design_template_files
1717
2094
  on_empty: stop
1718
2095
  on_empty_message: "A frontend lane was detected ({{ui_evidence}}) but no UI design template was written to .appweaver/templates/overrides/ui-design-template.md. Check that the scan substrate's frontend_signals carry readable UI source, then re-run."
@@ -1721,14 +2098,14 @@ workflow:
1721
2098
  # as the service delta mirrors the service master.
1722
2099
  - id: ctx_ui_delta_design_template
1723
2100
  action: build_context
1724
- condition: "{{ui_lane}} == true"
2101
+ condition: "{{author_ui_flag}} == true"
1725
2102
  context:
1726
2103
  - { path: .appweaver/templates/overrides/ui-design-template.md, optional: true } # PRIMARY: the JUST-generated lane master — mirror its ACTUAL sections
1727
2104
  - { path: .appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md } # SHAPE ONLY: the neutral change-record bones (zero framework)
1728
2105
  - { path: "{{constitution_ui_file}}" } # the validated UX-* base (keep the delta consistent with the master)
1729
2106
  - id: gen_ui_delta_design_template
1730
2107
  action: agent_generate
1731
- condition: "{{ui_lane}} == true"
2108
+ condition: "{{author_ui_flag}} == true"
1732
2109
  tools: "Read,Write,Edit"
1733
2110
  permission_mode: acceptEdits
1734
2111
  task: |
@@ -1762,7 +2139,7 @@ workflow:
1762
2139
 
1763
2140
  - id: verify_ui_delta_design_template
1764
2141
  action: verify_context
1765
- condition: "{{ui_lane}} == true"
2142
+ condition: "{{author_ui_flag}} == true"
1766
2143
  require_non_empty: ui_delta_design_template_files
1767
2144
  on_empty: stop
1768
2145
  on_empty_message: "No UI delta-design template was written to .appweaver/templates/overrides/ui-delta-design-template.md. Re-run onboard after confirming the UI design template override was generated."
@@ -1781,6 +2158,13 @@ workflow:
1781
2158
 
1782
2159
  outputs:
1783
2160
  bundle_dir: "{{bundle_dir}}"
2161
+ # The context's specs tree — created here, empty by design. It is what makes the
2162
+ # context visible to list-contexts and its features pickable by select-feature.
2163
+ bounded_context: "{{bc}}"
2164
+ context_dir: "{{bc_dir}}"
2165
+ context_domain_model: "{{bc_domain_model}}"
2166
+ feature_dirs: "{{feature_dirs}}"
2167
+ feature_dirs_created: "{{dirs_created}}"
1784
2168
  aggregate_count: "{{aggregate_count}}"
1785
2169
  concepts: "{{concepts}}"
1786
2170
  warnings: "{{okf_warnings}}"
@@ -1820,9 +2204,10 @@ outputs:
1820
2204
  delta_design_template_override: ".appweaver/templates/overrides/delta-design-template.md"
1821
2205
  design_template_files: "{{design_template_files}}"
1822
2206
  delta_design_template_files: "{{delta_design_template_files}}"
2207
+ model_template_files: "{{model_template_files}}" # the context's domain-model template — not lane-gated
1823
2208
  # The lane's own template pair. Empty on a backend-only repo — no lane, no files.
1824
2209
  ui_design_template_files: "{{ui_design_template_files}}"
1825
2210
  ui_delta_design_template_files: "{{ui_delta_design_template_files}}"
1826
2211
  technical_validation_ok: "{{technical_ok}}"
1827
2212
  domain_validation_ok: "{{domain_ok}}"
1828
- next: "Onboarded {{project_name}}: a high-level feature map at {{bundle_dir}} + a PROVISIONAL project governance scaffold, domain {{active_domain}} ACTIVATED, and — reverse-engineered from THIS code alone — the rule tiers wired per-workflow: the DOMAIN set (guardrails/projects/{{domain_slug}}/domain/*.domain.md + domain-rule-bindings.md) always, and each LANE's set where that lane is declared. ONE HALF AND ONE RULE SET PER LANE: service_lane / ui_lane report which lanes .appweaver/stacks.json declared, and the declaration is what decided. A declared service lane gets constitution.technical.md, guardrails/projects/{{domain_slug}}/service/*.md + tech-rule-bindings.md, and the service design/delta-design template overrides; a declared UI lane gets constitution.ui.md, guardrails/projects/{{domain_slug}}/frontend/*.md mapped under ## /aw-spec-ui-constitution, and its own template pair. A lane declared (none) in select-stack gets none of it, which is the correct outcome and not an omission. LANE DECLARATION: read, never written — select-stack is its sole writer, and this run scanned the source paths it named (scanned_service_root / scanned_ui_root). The scan was SCOPED by them, so the backend substrate excludes the UI subtree and the frontend substrate is that subtree: scanned_backend_files / scanned_ui_files report each. ui_source_evidence_found false means the UI lane's declared path held nothing a scan recognises as UI, so its governance is thin — move the source path with /appweaver select-stack and re-run. The build/test/run commands on each lane are that PROFILE's defaults and may not match a legacy layout — review them, and re-run /appweaver select-stack to correct any lane. Because the domain is activated, resolve-domain + resolve-tech now route the WHOLE forward pipeline AND adopt to these code-derived rules — never another project's rules as a fallback. Code-derived design + delta-design templates are installed as the project template overrides (.appweaver/templates/overrides/) so a NEW feature is developed against the as-built architecture with NO adopt — every section derived from a NEUTRAL skeleton plus this repo's own structure, with no greenfield template in context. Where the lane exists, the SAME pair is generated for it (ui-design-template.md + ui-delta-design-template.md) from the UI source; ui-design-template.md is consumed by /appweaver adopt, which reverses a slice's design.ui.md from it wherever that slice owns part of the lane's surface; ui-delta-design-template.md is consumed by the frontend lane of /appweaver plan on its DELTA path, and the plan it produces is generated by the frontend lane of /appweaver implement. Develop an EXISTING feature just-in-time: /appweaver adopt <Feature> reverse-engineers its spec.md + design.md + source-map.md and completes that feature's OKF concept; its governance reconcile is currently HELD, so the scaffold this run wrote stays as it is. Develop a NEW feature straight through the forward pipeline (/appweaver specify → plan → implement). Then drive every change through the CR delta loop (/appweaver specify → plan → analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta). If a validation_ok is false, its constitution half needs a manual touch (see its Sync Impact Report)."
2213
+ next: "Onboarded bounded context {{bc}} ({{bc_feature_count}} feature(s): {{bc_features}}). Its aggregate concepts ({{bc_aggregates}}) are in {{bundle_dir}}, each keyed `bounded_context: {{bc}}` and listing the features that act on it — the bundle is the project's knowledge, one concept per aggregate, never one per feature — and its specs tree now exists at {{bc_dir}}/ with a directory per feature ({{feature_dirs}}) — EMPTY on purpose: the directory says the feature is known, and the documents that describe it are written per feature by /appweaver adopt and per context by /appweaver specify. {{bc}} is now the ACTIVE bounded context (.appweaver/context.json), so /appweaver select-feature lists exactly these features and nothing from a neighbouring context; switch with /appweaver select-bounded-context. ONBOARD RUNS ONCE PER CONTEXT: contexts already done — {{onboarded_contexts}}; still open — {{remaining_contexts}}; the order the map gives is {{contexts_ordered}}. Run /appweaver onboard <NextContext> for each remaining one, or with no argument to take the next one automatically. THE PROJECT TIER IS WRITTEN ONCE, BY THE FIRST CONTEXT (this run was first: {{is_first}}). That tier is the PROVISIONAL governance scaffold — the split constitution, the domain rule set at guardrails/projects/{{domain_slug}}/domain/*.domain.md + domain-rule-bindings.md, each declared LANE\'s rule set and constitution half, the activated domain {{active_domain}}, and the code-derived design / delta-design / domain-model template overrides in .appweaver/templates/overrides/. A later context does NOT re-author any of it: re-deriving project principles from one context\'s evidence is the scoping bias the governance tier exists to avoid. Amend it with /appweaver constitution. ONE HALF AND ONE RULE SET PER LANE: service_lane / ui_lane report which lanes .appweaver/stacks.json declared, and the declaration is what decided. A lane declared (none) in select-stack gets none of it, which is the correct outcome and not an omission. LANE DECLARATION: read, never written — select-stack is its sole writer, and this run scanned the source paths it named (scanned_service_root / scanned_ui_root); scanned_backend_files / scanned_ui_files report each. ui_source_evidence_found false means the UI lane\'s declared path held nothing a scan recognises as UI, so its governance is thin — move the source path with /appweaver select-stack and re-run. The build/test/run commands on each lane are that PROFILE\'s defaults and may not match a legacy layout — review them. Because the domain is activated, resolve-domain + resolve-tech route the WHOLE forward pipeline to these code-derived rules — never another project\'s rules as a fallback. THE BOUNDARIES CAME FROM THE MAP, not from this run: /appweaver inventory proposed them and you confirmed them in specs/proposed-inventory.md. To change a boundary, edit that file and run /appweaver inventory apply — a context whose concepts already exist is not re-onboarded. Develop an EXISTING feature just-in-time: /appweaver adopt <Feature> reverse-engineers its spec.md + design.md + source-map.md and deepens the OKF concept of the aggregate it acts on. Develop a NEW feature straight through the forward pipeline (/appweaver specify → plan → implement). Then drive every change through the CR delta loop (/appweaver specify → plan → analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta). If a validation_ok is false, its constitution half needs a manual touch (see its Sync Impact Report)."