aw-spec-cli 0.1.16 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/.appweaver/DESIGN-FORMAT.md +18 -18
- package/assets/.appweaver/ORCH-DIALECT.md +1 -3
- package/assets/.appweaver/RULE-PARTITION.md +29 -13
- package/assets/.appweaver/STACK-PROFILES.md +10 -9
- package/assets/.appweaver/orchestrator/.state/smoke.state.json +32 -0
- package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +4 -1
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +251 -0
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +104 -35
- package/assets/.appweaver/orchestrator/orchestrator.py +5 -4
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +94 -36
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/domain-model-template.skeleton.md +115 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +2 -9
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +16 -40
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +38 -17
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +8 -8
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +10 -11
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +673 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +535 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +643 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +84 -701
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +7 -7
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +16 -14
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +243 -144
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +877 -578
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +32 -91
- package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +836 -514
- package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +132 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +38 -5
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +271 -979
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +1085 -3385
- package/assets/.appweaver/scripts/bash/aw-xref.sh +1 -2
- package/assets/.appweaver/scripts/bash/common.sh +47 -0
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +914 -3220
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +1 -2
- package/assets/.appweaver/scripts/python/aw-payload-parity.py +19 -3
- package/assets/.appweaver/scripts/python/aw-stack.py +6 -2
- package/assets/.appweaver/scripts/python/aw-xref.py +86 -350
- package/assets/.appweaver/templates/constitution-ui-template.md +1 -1
- package/assets/.appweaver/templates/delta-design-template.md +120 -30
- package/assets/.appweaver/templates/delta-domain-model-template.md +81 -0
- package/assets/.appweaver/templates/delta-spec-template.md +43 -17
- package/assets/.appweaver/templates/design-template.md +68 -98
- package/assets/.appweaver/templates/domain-model-template.md +130 -0
- package/assets/.appweaver/templates/okf-concept-template.md +2 -2
- package/assets/.appweaver/templates/source-map-template.md +3 -3
- package/assets/.appweaver/templates/spec-template.md +100 -122
- package/assets/.appweaver/templates/ui-delta-design-template.md +159 -0
- package/assets/.appweaver/templates/ui-design-template.md +46 -75
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +7 -18
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +34 -39
- package/assets/guardrails/requirement-spec.txt +2 -2
- package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
- package/package.json +1 -1
- package/assets/.appweaver/orchestrator/modules/delta-design.orch.yaml +0 -105
- package/assets/.appweaver/orchestrator/modules/spec-state-gate.orch.yaml +0 -87
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-delta-design.orch.yaml +0 -251
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +0 -813
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +0 -253
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +0 -663
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +0 -600
- package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +0 -1147
- package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +0 -1121
- package/assets/.appweaver/templates/story-template.md +0 -178
|
@@ -1,30 +1,45 @@
|
|
|
1
1
|
# =============================================================================
|
|
2
2
|
# aw-spec-finalize-delta (orchestrator) — MERGE the delta into the master docs
|
|
3
3
|
#
|
|
4
|
-
# The fourth of the five CR stages. Fold the open change's delta
|
|
5
|
-
#
|
|
6
|
-
# After this the
|
|
7
|
-
# REMAINS OPEN.
|
|
4
|
+
# The fourth of the five CR stages. Fold the open change's delta into the two documents that
|
|
5
|
+
# DEFINE the slice — the feature's spec.md and the bounded context's domain-model.md — then
|
|
6
|
+
# refresh the OKF knowledge layer. After this the masters describe the shipped-plus-change
|
|
7
|
+
# state, but THE CHANGE REMAINS OPEN.
|
|
8
8
|
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
9
|
+
# TWO MERGE TARGETS, ONE TRANSACTION:
|
|
10
|
+
# • specs/<Feature>/spec.md ← changes/<id>/delta-spec.md
|
|
11
|
+
# • specs/<Context>/domain-model.md ← specs/<Context>/domain-model.<id>.delta.md
|
|
12
|
+
# Behaviour and model land together or neither lands. A requirement merged citing a BR- the
|
|
13
|
+
# model never gained references an invariant that does not exist; an invariant merged that no
|
|
14
|
+
# requirement reached is a rule nothing asks for. ONE agent step writes both, so there is no
|
|
15
|
+
# window in which one is in and the other is not.
|
|
14
16
|
#
|
|
15
|
-
# THE
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# • MASTER, NO DELTA — the change never reached the UI, or ⌛ delta-design-ui has not run.
|
|
21
|
-
# Append the Change History row and touch nothing else. The row is not bookkeeping
|
|
22
|
-
# for its own sake: close-delta stamps design.ui.md to the new version alongside its
|
|
23
|
-
# siblings (version-stamp covers all three), so a header carrying a version that no
|
|
24
|
-
# row explains is indistinguishable from a merge that silently dropped one.
|
|
25
|
-
# A feature with no design.ui.md at all has no UI half and merges two documents, unchanged.
|
|
17
|
+
# THE MODEL MERGES ONE `## Aggregate:` SECTION AT A TIME — the section is the merge unit, and
|
|
18
|
+
# only the sections the delta's "Aggregates touched" header names are opened. Two open deltas
|
|
19
|
+
# on DIFFERENT aggregates therefore never collide; two on the SAME one are a real semantic
|
|
20
|
+
# conflict, which analyze-plan-delta's overlap finding surfaces before the merge rather than
|
|
21
|
+
# leaving it to be discovered here.
|
|
26
22
|
#
|
|
27
|
-
#
|
|
23
|
+
# THE LANE PLANS ARE NOT MERGED HERE. backend-plan.md and frontend-plan.md are DERIVED from
|
|
24
|
+
# the spec and the model — that is what the lane split made them — so they are re-derived from
|
|
25
|
+
# the merged masters, not patched from a delta. Merging a delta plan would fork the
|
|
26
|
+
# derivation: the master would carry an edit no run of `plan` produced, and the next `plan`
|
|
27
|
+
# would silently overwrite it. The delta plans stay in changes/<id>/, where `implement`
|
|
28
|
+
# generated from them, and close-delta archives them as the record of how the change was
|
|
29
|
+
# built. A UX-
|
|
30
|
+
# item merged into the spec is carried by the spec until the frontend plan is re-derived from
|
|
31
|
+
# it — not dropped, re-derived.
|
|
32
|
+
#
|
|
33
|
+
# ⌛ THE RE-DERIVATION IS NOT BUILT YET, and this stage is where that shows. `plan`
|
|
34
|
+
# self-detects on plan-EXISTS, so a run against a merged master authors another DELTA plan
|
|
35
|
+
# rather than refreshing the master ("to regenerate a master, delete it first"). Until a
|
|
36
|
+
# refresh mode lands, the master plans stand at their bootstrap content while spec.md and
|
|
37
|
+
# domain-model.md move ahead of them — visible, because analyze-plan gates the masters against
|
|
38
|
+
# exactly these two documents and reports the drift. What this stage must NOT do is hide that
|
|
39
|
+
# by merging a delta plan: the plan would then agree with the spec without any run of `plan`
|
|
40
|
+
# ever having derived it.
|
|
41
|
+
#
|
|
42
|
+
# specify → plan → analyze-plan-delta → implement → build → analyze-slice-delta
|
|
28
43
|
# → finalize-delta ← you are here
|
|
29
44
|
# → analyze-slice → close-delta
|
|
30
45
|
#
|
|
@@ -39,17 +54,24 @@
|
|
|
39
54
|
# The downstream fence below still guards the MERGE (a change that breaks a dependent
|
|
40
55
|
# must not reach the master docs); close-delta guards nothing — it is the bookkeeping.
|
|
41
56
|
#
|
|
42
|
-
# Prereqs: run /appweaver implement
|
|
43
|
-
#
|
|
44
|
-
#
|
|
57
|
+
# Prereqs: run /appweaver implement (answering it with THIS change), then build, then
|
|
58
|
+
# /appweaver analyze-slice-delta — the delta's code is generated, compiles, and has been
|
|
59
|
+
# VERIFIED to contain what the delta plans said it would, so after the merge the masters
|
|
60
|
+
# and the code agree. That last gate is the one that matters here: this stage writes the
|
|
61
|
+
# change into spec.md and domain-model.md, and merging a change that was never fully
|
|
62
|
+
# implemented is how a master ends up asserting behaviour no code has. Nothing MECHANICALLY
|
|
63
|
+
# proves it ran — the stage boundary is operator-driven, as everywhere else in this
|
|
64
|
+
# pipeline — but it is the difference between a master that describes the system and one
|
|
65
|
+
# that describes an intention.
|
|
66
|
+
# finalize-delta edits ONLY spec.md and domain-model.md; it generates no code.
|
|
45
67
|
#
|
|
46
68
|
# Launch: /appweaver finalize-delta [strategy]
|
|
47
69
|
# Prereq: an OPEN change whose delta was implemented.
|
|
48
70
|
# =============================================================================
|
|
49
71
|
|
|
50
72
|
name: aw-spec-finalize-delta
|
|
51
|
-
description: Merge the open change's delta-spec into spec.md
|
|
52
|
-
version:
|
|
73
|
+
description: Merge the open change's delta-spec into the feature's spec.md and its model delta into the bounded context's domain-model.md — one aggregate section at a time, only the aggregates the delta names — in a single transaction, and append the spec's Change History row. The lane plans are NOT merged — backend-plan.md and frontend-plan.md are derived from these two documents and are re-derived by /appweaver plan main. Gated first by a SCOPED downstream fence over the transitive blast radius (fence_green), which writes a triage report and asks Abort/Override on red. Leaves the change OPEN — analyze-slice runs next against the merged masters, and close-delta archives the change and bumps the version.
|
|
74
|
+
version: 2.0.0
|
|
53
75
|
agent_agnostic: true
|
|
54
76
|
dialect: aw-spec.orchestrator/v1
|
|
55
77
|
|
|
@@ -73,10 +95,14 @@ vars:
|
|
|
73
95
|
downstream_csv: "" # the blast-radius names, fed to the scoped fence's --also and shown to the human
|
|
74
96
|
fence_ok: "true" # default green so the gate is a no-op when nothing downstream
|
|
75
97
|
gate_decision: "clean" # audit: 'clean' unless a RED fence was deliberately overridden
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
98
|
+
domain_model: "" # specs/<Context>/domain-model.md — the second merge target
|
|
99
|
+
domain_model_exists: "false"
|
|
100
|
+
delta_model: "" # specs/<Context>/domain-model.<id>.delta.md — the model half of the change
|
|
101
|
+
delta_model_exists: "false" # false ⇒ a behaviour-only change: the spec merge is the whole merge
|
|
102
|
+
model_orphan: "false" # a model delta whose context has no master to merge it into
|
|
103
|
+
model_master_ok: "true" # lowered by model_orphan_raise; the gate reads only this
|
|
104
|
+
backend_plan: "" # READ, never written here — the OKF edges are parsed out of it
|
|
105
|
+
backend_plan_exists: "false"
|
|
80
106
|
|
|
81
107
|
workflow:
|
|
82
108
|
|
|
@@ -87,11 +113,14 @@ workflow:
|
|
|
87
113
|
script: { ref: actions }
|
|
88
114
|
subcommand: feature-paths
|
|
89
115
|
outputs:
|
|
116
|
+
- { name: feature, from: "$.feature" }
|
|
90
117
|
- { name: feature_dir, from: "$.feature_dir" }
|
|
91
118
|
- { name: spec_file, from: "$.spec_file" }
|
|
92
119
|
- { name: spec_exists, from: "$.spec_exists" }
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
# The backend plan is a READER's input here, not a merge target: the OKF cross-slice
|
|
121
|
+
# edges are parsed out of it, so this stage needs its path and whether it exists.
|
|
122
|
+
- { name: backend_plan, from: "$.backend_plan" }
|
|
123
|
+
- { name: backend_plan_exists, from: "$.backend_plan_exists" }
|
|
95
124
|
|
|
96
125
|
- id: require_feature
|
|
97
126
|
action: verify_context
|
|
@@ -103,7 +132,7 @@ workflow:
|
|
|
103
132
|
action: verify_context
|
|
104
133
|
require_true: spec_exists
|
|
105
134
|
on_false: stop
|
|
106
|
-
on_false_message: "No spec.md for the current feature. Run /appweaver specify
|
|
135
|
+
on_false_message: "No spec.md for the current feature — there is no master to merge into. Run /appweaver specify first."
|
|
107
136
|
|
|
108
137
|
# 1b. Read the OPEN change (opened by cr). finalize-delta needs a change to close.
|
|
109
138
|
- id: read_change
|
|
@@ -121,7 +150,59 @@ workflow:
|
|
|
121
150
|
action: verify_context
|
|
122
151
|
require_true: has_change
|
|
123
152
|
on_false: stop
|
|
124
|
-
on_false_message: "No open change on this feature — nothing to finalize. Run /appweaver specify →
|
|
153
|
+
on_false_message: "No open change on this feature — nothing to finalize. Run /appweaver specify → plan → analyze-plan-delta → implement first."
|
|
154
|
+
|
|
155
|
+
# 1b-ii. The change's own documents, from the ONE resolver that knows the layout: the
|
|
156
|
+
# delta-spec sits at FEATURE level (changes/<id>/) and the model delta at CONTEXT
|
|
157
|
+
# level (specs/<Context>/domain-model.<id>.delta.md), beside the master it merges
|
|
158
|
+
# into. Both merge targets and both sources come from here, so this stage never
|
|
159
|
+
# rebuilds a path by hand.
|
|
160
|
+
- id: cr_paths
|
|
161
|
+
action: execute_script
|
|
162
|
+
script: { ref: actions }
|
|
163
|
+
subcommand: cr-paths
|
|
164
|
+
args: ["--cr-id", "{{active_change}}", "--feature-dir", "{{feature_dir}}"]
|
|
165
|
+
outputs:
|
|
166
|
+
- { name: cr_dir, from: "$.cr_dir" }
|
|
167
|
+
- { name: delta_spec, from: "$.delta_spec" }
|
|
168
|
+
- { name: delta_spec_exists, from: "$.delta_spec_exists" }
|
|
169
|
+
- { name: delta_model, from: "$.delta_model" }
|
|
170
|
+
- { name: delta_model_exists, from: "$.delta_model_exists" }
|
|
171
|
+
- { name: delta_backend_plan, from: "$.delta_backend_plan" }
|
|
172
|
+
- { name: domain_model, from: "$.domain_model" }
|
|
173
|
+
- { name: domain_model_exists, from: "$.domain_model_exists" }
|
|
174
|
+
|
|
175
|
+
# The delta-spec is the merge's source of truth. A missing one would still reach the
|
|
176
|
+
# agent step as an announced-missing context file rather than a stop, and merging from
|
|
177
|
+
# nothing is how a master acquires content no change ever asked for.
|
|
178
|
+
- id: require_delta_spec
|
|
179
|
+
action: verify_context
|
|
180
|
+
require_true: delta_spec_exists
|
|
181
|
+
on_false: stop
|
|
182
|
+
on_false_message: "Change {{active_change}} has no delta-spec.md at {{delta_spec}} — nothing to merge into spec.md. Run /appweaver specify to author the change, or /appweaver recall-delta to withdraw it."
|
|
183
|
+
|
|
184
|
+
# A model delta with no master to merge it into. specify creates domain-model.md the
|
|
185
|
+
# first time it models a context, so this is a context whose master was moved or
|
|
186
|
+
# deleted after the change was opened. Stop rather than merge the spec half alone:
|
|
187
|
+
# close-delta archives the model delta, so a silently skipped model change is a
|
|
188
|
+
# change that is gone. Two ordered assignments, because the engine takes no compound
|
|
189
|
+
# condition: the probe runs only where the master is ABSENT and carries the delta's
|
|
190
|
+
# own presence, so the flag it raises means "a model delta with nothing to merge into".
|
|
191
|
+
# A feature outside any bounded context has neither file and passes both, which is
|
|
192
|
+
# the legacy layout and not an error.
|
|
193
|
+
- id: model_orphan_probe
|
|
194
|
+
condition: "{{domain_model_exists}} == false"
|
|
195
|
+
action: set
|
|
196
|
+
vars: { model_orphan: "{{delta_model_exists}}" }
|
|
197
|
+
- id: model_orphan_raise
|
|
198
|
+
condition: "{{model_orphan}} == true"
|
|
199
|
+
action: set
|
|
200
|
+
vars: { model_master_ok: "false" }
|
|
201
|
+
- id: require_model_master
|
|
202
|
+
action: verify_context
|
|
203
|
+
require_true: model_master_ok
|
|
204
|
+
on_false: stop
|
|
205
|
+
on_false_message: "Change {{active_change}} carries a model delta ({{delta_model}}) but its bounded context has no domain-model.md master at {{domain_model}}. Spec and model merge in one transaction, so this stage will not merge the spec half alone. Restore the context's domain-model.md (it is what specify creates when it first models the context) and re-run /appweaver finalize-delta."
|
|
125
206
|
|
|
126
207
|
# 1c. DOWNSTREAM GATE (hard, with a REVIEWED override). Who depends on this aggregate —
|
|
127
208
|
# its OKF Cross-References, TRANSITIVELY (C→B→A all count) and placeholder-filtered
|
|
@@ -175,7 +256,10 @@ workflow:
|
|
|
175
256
|
fence_log: "{{fence_log}}"
|
|
176
257
|
context:
|
|
177
258
|
- { path: "{{spec_file}}" }
|
|
178
|
-
- { path: "{{
|
|
259
|
+
- { path: "{{delta_spec}}" }
|
|
260
|
+
# The delta plan is how the change was BUILT — the most direct account of what could
|
|
261
|
+
# have broken a dependent. Optional: a frontend-only change never has one.
|
|
262
|
+
- { path: "{{delta_backend_plan}}", optional: true }
|
|
179
263
|
- id: write_broken_downstream
|
|
180
264
|
action: agent_author
|
|
181
265
|
condition: "{{downstream_count}} != 0"
|
|
@@ -251,16 +335,7 @@ workflow:
|
|
|
251
335
|
on_mismatch: stop
|
|
252
336
|
on_mismatch_message: "finalize-delta ABORTED — downstream fence RED after change {{active_change}} and the override was declined. {{downstream_count}} slice(s) depend on this aggregate ({{downstream_csv}}): the change would break a slice that USES this one. Triage: {{feature_dir}}/changes/{{active_change}}/broken-downstream.md. Fix the downstream break; or, if the behaviour change is intended, open a CR on the affected slice to re-baseline its golden-master. Then re-run /appweaver finalize-delta. Last log: {{fence_log}}"
|
|
253
337
|
|
|
254
|
-
# 2.
|
|
255
|
-
- id: design_scaffold
|
|
256
|
-
action: execute_script
|
|
257
|
-
script: { ref: actions }
|
|
258
|
-
subcommand: design-scaffold
|
|
259
|
-
args: ["--feature-dir", "{{feature_dir}}"]
|
|
260
|
-
outputs:
|
|
261
|
-
- { name: design_doc, from: "$.design_doc" }
|
|
262
|
-
|
|
263
|
-
# 3. Pre-execution hooks.
|
|
338
|
+
# 2. Pre-execution hooks.
|
|
264
339
|
- id: resolve_pre_hooks
|
|
265
340
|
action: execute_script
|
|
266
341
|
script: { ref: actions }
|
|
@@ -272,43 +347,53 @@ workflow:
|
|
|
272
347
|
action: dispatch_hooks
|
|
273
348
|
hooks_from_variable: before_hooks
|
|
274
349
|
|
|
275
|
-
#
|
|
350
|
+
# 3. THE MERGE: the change's two halves into the two masters, in place.
|
|
276
351
|
# Context is fed via build_context — agent_generate reads the ENVELOPE, not a
|
|
277
352
|
# step-level context: key (unlike agent_author/agent_query which read both).
|
|
353
|
+
# FOUR documents at most, and NO PLAN AMONG THEM. A plan in context is a plan the
|
|
354
|
+
# merge could quote from, and everything it would quote is a derivation of these
|
|
355
|
+
# two masters rather than a source for them — the delta plan's wording is how the
|
|
356
|
+
# change was BUILT, not what was agreed. The two model documents are independently
|
|
357
|
+
# optional — the gate above has already refused the one combination that would be a
|
|
358
|
+
# mistake — so a behaviour-only change still gets the master model in context, which
|
|
359
|
+
# is what lets the merge check that the BR- ids the spec cites actually exist.
|
|
278
360
|
- id: ctx_merge
|
|
279
361
|
action: build_context
|
|
280
362
|
context:
|
|
281
|
-
- { path: "{{
|
|
282
|
-
- { path: "{{feature_dir}}/changes/{{active_change}}/delta-design.md" }
|
|
363
|
+
- { path: "{{delta_spec}}" }
|
|
283
364
|
- { path: "{{spec_file}}" }
|
|
284
|
-
- { path: "{{
|
|
285
|
-
|
|
286
|
-
# arrived as its instruction. Neither: a backend-only feature, and a two-master
|
|
287
|
-
# merge is the complete outcome. Master only: the change did not reach the UI, or
|
|
288
|
-
# ⌛ delta-design-ui has not run — Change History row only. Both: a full third merge.
|
|
289
|
-
# Optional rather than gated because a missing UI document is an ordinary state
|
|
290
|
-
# here, not a misconfiguration worth stopping the merge over.
|
|
291
|
-
- { path: "{{ui_design_doc}}", optional: true }
|
|
292
|
-
- { path: "{{feature_dir}}/changes/{{active_change}}/delta-design.ui.md", optional: true }
|
|
365
|
+
- { path: "{{delta_model}}", optional: true }
|
|
366
|
+
- { path: "{{domain_model}}", optional: true }
|
|
293
367
|
- id: merge_delta
|
|
294
368
|
action: agent_generate
|
|
295
369
|
tools: "Read,Write,Edit"
|
|
296
370
|
permission_mode: acceptEdits
|
|
297
371
|
task: |
|
|
298
|
-
Merge the open change {{active_change}} into
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
372
|
+
Merge the open change {{active_change}} into the two MASTER documents in context.
|
|
373
|
+
The delta documents are the source of the change; apply them and change nothing else.
|
|
374
|
+
THE MASTERS: spec.md (this feature's behaviour) and, where it is in context,
|
|
375
|
+
domain-model.md (the bounded context's model — delta_model_exists={{delta_model_exists}}).
|
|
376
|
+
A change with no model delta merges the spec alone and that is the COMPLETE outcome,
|
|
377
|
+
not a step you skipped.
|
|
378
|
+
NO LANE PLAN IS A TARGET HERE. backend-plan.md and frontend-plan.md are DERIVED from
|
|
379
|
+
these two documents and are re-derived by /appweaver plan main after this merge; neither is
|
|
380
|
+
in context and neither is to be opened, read or edited. Do not restate a plan's
|
|
381
|
+
wording in a master: the plan is how the change was built, not what was agreed.
|
|
382
|
+
BOTH MASTERS MOVE TOGETHER. Where there is a model delta, edit spec.md and
|
|
383
|
+
domain-model.md in the same pass — a merged requirement citing a BR- the model never
|
|
384
|
+
gained, or a merged invariant no requirement reached, is exactly the split state this
|
|
385
|
+
stage exists to prevent.
|
|
306
386
|
To spec.md:
|
|
307
387
|
- Add each ADDED user story as a US-n tagged `Added: {{active_change}}`, and fold
|
|
308
|
-
the ADDED / MODIFIED / REMOVED FR /
|
|
309
|
-
sections using the master's vocabulary (EARS FR,
|
|
310
|
-
|
|
311
|
-
|
|
388
|
+
the ADDED / MODIFIED / REMOVED FR / CN- / DC- / IC- / UX- items into their
|
|
389
|
+
sections using the master's vocabulary (EARS FR, etc.). Every class the delta
|
|
390
|
+
authored is merged, including UX- into Experience Standards: that section is what
|
|
391
|
+
the frontend plan is re-derived from, so a UX- item left behind reaches no UI.
|
|
392
|
+
- A BR- in the delta-spec is a CITATION, never a definition — the invariant itself
|
|
393
|
+
belongs to the model. Merge the citations (an FR's "Exercises", the
|
|
394
|
+
"Relies on invariants" line, a story's exercised ids) and merge the RULE only into
|
|
395
|
+
domain-model.md. A BR- whose text lands in spec.md is the restatement the model
|
|
396
|
+
exists to prevent, and it drifts the moment the model moves.
|
|
312
397
|
- Where a lift section (Constraints & NFRs, Data Classification, Integration
|
|
313
398
|
Contracts, Experience Standards) still reads "None", its first merged item REPLACES
|
|
314
399
|
that placeholder; a section the delta does not touch keeps its "None" verbatim.
|
|
@@ -316,52 +401,45 @@ workflow:
|
|
|
316
401
|
REMOVED item by removing it. Keep the rest of spec.md VERBATIM.
|
|
317
402
|
- Append a Change History row for {{active_change}} (date + one-line summary).
|
|
318
403
|
Its Version cell is {{next_version}} — the version this change ships as.
|
|
319
|
-
To
|
|
320
|
-
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
-
|
|
357
|
-
|
|
358
|
-
Leave the `**Version**:` HEADER of EVERY document in context exactly as it is — it
|
|
359
|
-
still reads {{version}} and does not move here. close-delta stamps the headers to
|
|
360
|
-
{{next_version}} at the moment it bumps active_version.json, so every carrier of
|
|
361
|
-
the number changes in one step by one mechanism. Do not invent a version anywhere.
|
|
362
|
-
Preserve every unrelated section of EVERY doc byte-for-byte. Do NOT touch the
|
|
363
|
-
delta docs (close-delta archives them later) and do NOT touch any source code.
|
|
364
|
-
Report the files you changed.
|
|
404
|
+
To domain-model.md — ONLY where it and a model delta are both in context:
|
|
405
|
+
- THE MERGE UNIT IS ONE `## Aggregate: <Name>` SECTION, and the ONLY sections you may
|
|
406
|
+
open are the ones the model delta's "Aggregates touched" header names. Every other
|
|
407
|
+
aggregate section stays byte-for-byte, whatever the delta says elsewhere — another
|
|
408
|
+
open change may be merging into one of them, and the section boundary is what keeps
|
|
409
|
+
the two merges from colliding.
|
|
410
|
+
- ADDED: put each new entity, value object, domain event, invariant (BR-n) or
|
|
411
|
+
cross-aggregate invariant (XBR-n) into the sub-section it belongs to (### Entities,
|
|
412
|
+
### Value objects, ### Domain events, ### Invariants) of the aggregate the delta
|
|
413
|
+
names for it. A BR-n is added with its RULE TEXT — this document is where the rule
|
|
414
|
+
lives. An XBR-n goes in `## Cross-Aggregate Invariants`, not inside an aggregate.
|
|
415
|
+
- A wholly NEW aggregate gets a whole new `## Aggregate: <Root>` section in the
|
|
416
|
+
master's own shape (Serving specs, Last changed, ### Boundary, ### Entities,
|
|
417
|
+
### Value objects, ### Domain events, ### Invariants), placed after the existing
|
|
418
|
+
aggregate sections and before `## Cross-Aggregate Invariants`. Its
|
|
419
|
+
**Serving specs** is `{{feature}}`.
|
|
420
|
+
- MODIFIED: the delta states a BEFORE and an AFTER — verify the BEFORE is what the
|
|
421
|
+
master actually says, then replace it with the AFTER in place. If the master does
|
|
422
|
+
not say the BEFORE, do NOT guess: leave it and report the mismatch, because the
|
|
423
|
+
master has moved since the delta was authored and the change needs a rebase.
|
|
424
|
+
- REMOVED: remove the named element. Where the delta says what replaces it, the
|
|
425
|
+
replacement is in ADDED and merges there.
|
|
426
|
+
- Fold new terms into `## Ubiquitous Language` and new context relationships into
|
|
427
|
+
`## Context Map`, both only where the delta introduces them.
|
|
428
|
+
- On each aggregate section you touched, set **Last changed** to
|
|
429
|
+
`<the version token already on that line> · {{active_change}}` — replace the CR half
|
|
430
|
+
only. The version half is a CONTEXT version, stamped by script, and this stage is
|
|
431
|
+
not its author.
|
|
432
|
+
- Do NOT append a Change History row to domain-model.md and do NOT touch its
|
|
433
|
+
`**Context version**:` header. That ledger is close-delta's, written with the
|
|
434
|
+
version it bumps to — the model's row and its version land together or the row
|
|
435
|
+
carries a number nothing else agrees with.
|
|
436
|
+
Leave the `**Version**:` HEADER of spec.md exactly as it is — it still reads
|
|
437
|
+
{{version}} and does not move here. close-delta stamps it to {{next_version}} at the
|
|
438
|
+
moment it bumps active_version.json, so every carrier of the number changes in one
|
|
439
|
+
step by one mechanism. Do not invent a version anywhere.
|
|
440
|
+
Preserve every unrelated section of BOTH masters byte-for-byte. Do NOT touch the
|
|
441
|
+
delta documents (close-delta archives them later), do NOT open or edit any lane plan,
|
|
442
|
+
and do NOT touch any source code. Report the files you changed.
|
|
365
443
|
outputs:
|
|
366
444
|
- { name: merged_docs, from: "$.files" }
|
|
367
445
|
|
|
@@ -369,47 +447,68 @@ workflow:
|
|
|
369
447
|
action: verify_context
|
|
370
448
|
require_non_empty: merged_docs
|
|
371
449
|
on_empty: stop
|
|
372
|
-
on_empty_message: "The merge produced no changes — expected
|
|
450
|
+
on_empty_message: "The merge produced no changes — expected an edit to spec.md, and to domain-model.md where the change carries a model delta. Ensure the delta documents are filled (not the empty template) and re-run."
|
|
373
451
|
|
|
374
|
-
# 4b.
|
|
375
|
-
#
|
|
376
|
-
#
|
|
377
|
-
#
|
|
378
|
-
#
|
|
379
|
-
#
|
|
452
|
+
# 4b. Reconcile the cross-slice EDGES. They are parsed out of the master backend-plan.md
|
|
453
|
+
# (§1's References-by-id + its External integrations table) — the plan is not a merge
|
|
454
|
+
# target here, so this run neither adds nor prunes an edge on its own account; it
|
|
455
|
+
# re-asserts the index against the plan as it stands, which is what keeps a
|
|
456
|
+
# concept stub created by another slice's merge from lingering. The delta's own new
|
|
457
|
+
# dependencies reach the index when /appweaver plan main re-derives the plan from the
|
|
458
|
+
# masters this stage just merged. Skipped where the feature has no plan yet: parsing
|
|
459
|
+
# an absent document yields zero edges, and reconcile-on-write would read that as
|
|
460
|
+
# "every dependency was dropped" and prune the slice's real edges.
|
|
461
|
+
# `--origin auto` keeps the slice's provenance (greenfield vs reverse-engineered)
|
|
462
|
+
# from the plan itself. Best-effort (never blocks the merge).
|
|
380
463
|
- id: xref_record
|
|
464
|
+
condition: "{{backend_plan_exists}} == true"
|
|
381
465
|
action: execute_script
|
|
382
466
|
on_error: continue
|
|
383
467
|
script: { ref: xref }
|
|
384
468
|
subcommand: record
|
|
385
|
-
args: ["--feature-dir", "{{feature_dir}}", "--design", "{{
|
|
469
|
+
args: ["--feature-dir", "{{feature_dir}}", "--design", "{{backend_plan}}", "--origin", "auto"]
|
|
386
470
|
outputs:
|
|
387
471
|
- { name: xref_edge_count, from: "$.edge_count" }
|
|
388
472
|
- { name: xref_origin, from: "$.origin" }
|
|
389
473
|
|
|
390
|
-
# 4b-ii. Re-author the concept's Overview from the NOW-MERGED
|
|
391
|
-
#
|
|
392
|
-
#
|
|
393
|
-
#
|
|
474
|
+
# 4b-ii. Re-author the concept's Overview from the NOW-MERGED domain-model.md. What the
|
|
475
|
+
# Overview states — the root, its invariants, its transaction boundary — is the
|
|
476
|
+
# model's own content, and the model is what this stage just moved, so this is both
|
|
477
|
+
# the authoritative source and the first point the change is in it. Section-scoped
|
|
478
|
+
# splice (the Cross-References rows other slices own are untouched). Skipped where
|
|
479
|
+
# the context has no model: there would be nothing in context to distil, and a
|
|
480
|
+
# query with no source invents one. Best-effort; an empty answer is a no-op.
|
|
481
|
+
# Re-scope the envelope first. build_context REPLACES it and it PERSISTS across the
|
|
482
|
+
# steps that follow, so without this the Overview query would still be holding the
|
|
483
|
+
# merge's payload — the delta-spec and the model delta among it — and would distil the
|
|
484
|
+
# concept's prose from a pending change as readily as from the merged master.
|
|
485
|
+
- id: ctx_overview
|
|
486
|
+
condition: "{{domain_model_exists}} == true"
|
|
487
|
+
action: build_context
|
|
488
|
+
context:
|
|
489
|
+
- { path: "{{domain_model}}" }
|
|
394
490
|
- id: okf_overview
|
|
491
|
+
condition: "{{domain_model_exists}} == true"
|
|
395
492
|
action: agent_query
|
|
396
|
-
context: ["{{design_doc}}"]
|
|
397
493
|
include_standards: false
|
|
398
494
|
schema: { overview: "<2-4 sentences of prose>" }
|
|
399
495
|
mock: { overview: "" }
|
|
400
496
|
task: |
|
|
401
|
-
Write the OKF knowledge-concept Overview for the aggregate
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
497
|
+
Write the OKF knowledge-concept Overview for the aggregate `{{feature}}`, from the
|
|
498
|
+
merged domain-model.md in context. That document models the WHOLE bounded context —
|
|
499
|
+
read ONLY the `## Aggregate: {{feature}}` section (and any `## Cross-Aggregate
|
|
500
|
+
Invariants` naming it); the other aggregates belong to other concepts.
|
|
501
|
+
2-4 sentences of PROSE: name the aggregate ROOT and what the aggregate IS; state the
|
|
502
|
+
INVARIANTS it guards (its BR-n rules); state its TRANSACTION BOUNDARY — what it OWNS
|
|
503
|
+
inside the boundary vs what it only REFERENCES by id. Plain prose only: no headings,
|
|
504
|
+
no bullets, no code fences, no file paths, and no restating of the dependency edges
|
|
505
|
+
(those live in the Depends-On / Cross-References tables). Describe only what the model
|
|
506
|
+
states — invent nothing. If the section is absent, answer with an empty string.
|
|
409
507
|
outputs:
|
|
410
508
|
- { name: okf_overview, from: "$.overview" }
|
|
411
509
|
|
|
412
510
|
- id: okf_overview_write
|
|
511
|
+
condition: "{{domain_model_exists}} == true"
|
|
413
512
|
action: execute_script
|
|
414
513
|
on_error: continue
|
|
415
514
|
script: { ref: xref }
|
|
@@ -424,7 +523,7 @@ workflow:
|
|
|
424
523
|
on_error: continue
|
|
425
524
|
script: { ref: xref }
|
|
426
525
|
subcommand: log
|
|
427
|
-
args: ["--note", "CR {{active_change}} merged (downstream gate: {{gate_decision}}) — OKF cross-references refreshed"]
|
|
526
|
+
args: ["--note", "CR {{active_change}} merged into spec.md + domain-model.md (downstream gate: {{gate_decision}}) — OKF cross-references refreshed"]
|
|
428
527
|
outputs:
|
|
429
528
|
- { name: xref_log, from: "$.log" }
|
|
430
529
|
|
|
@@ -432,8 +531,8 @@ workflow:
|
|
|
432
531
|
# analyze-slice — see the header. The change stays OPEN past this stage on purpose:
|
|
433
532
|
# it is what keeps a red analyze-slice recoverable ("fix and re-run") instead of
|
|
434
533
|
# landing on an already-shipped version. The delta docs therefore remain in
|
|
435
|
-
# changes/{{active_change}}/
|
|
436
|
-
# moves
|
|
534
|
+
# changes/{{active_change}}/ and the model delta beside domain-model.md even though
|
|
535
|
+
# their content is now merged; close-delta moves both to changes/archive/.
|
|
437
536
|
|
|
438
537
|
# 6. Post-execution hooks.
|
|
439
538
|
- id: resolve_post_hooks
|
|
@@ -455,8 +554,8 @@ outputs:
|
|
|
455
554
|
downstream_count: "{{downstream_count}}"
|
|
456
555
|
gate_decision: "{{gate_decision}}"
|
|
457
556
|
xref_edge_count: "{{xref_edge_count}}"
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
#
|
|
461
|
-
#
|
|
462
|
-
next: "Delta MERGED into spec.md
|
|
557
|
+
domain_model: "{{domain_model}}"
|
|
558
|
+
model_merged: "{{delta_model_exists}}" # true ⇒ domain-model.md was the second target of this merge.
|
|
559
|
+
# false = a behaviour-only change: the spec merge was the
|
|
560
|
+
# whole merge, not a half of one.
|
|
561
|
+
next: "Delta MERGED into spec.md, and into the context's domain-model.md one aggregate section at a time where the change carried a model delta (model_merged={{delta_model_exists}} — false means a behaviour-only change, so the spec merge was the whole merge). The lane plans were NOT touched — they are derived from these two masters, and ⌛ re-deriving a MASTER plan is not built yet (plan self-detects on plan-exists and would author another delta), so backend-plan.md / frontend-plan.md stand at their last authored content and analyze-plan will report the drift against the merged spec and model. OKF edges reconciled ({{xref_edge_count}} dep(s)), {{downstream_count}} downstream slice(s) fenced (gate: {{gate_decision}}). Change {{active_change}} is STILL OPEN and the version is still {{version}}. Next: /appweaver analyze-slice — it now reads masters that carry this change, and a red result is still recoverable because nothing has been archived. Then /appweaver close-delta archives the change and bumps the version."
|