aw-spec-cli 0.1.14 → 0.1.16

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 (32) hide show
  1. package/assets/.appweaver/STACK-PROFILES.md +1 -1
  2. package/assets/.appweaver/orchestrator/modules/spec-state-gate.orch.yaml +87 -0
  3. package/assets/.appweaver/orchestrator/orchestrator.py +5 -0
  4. package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +10 -3
  5. package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +38 -11
  6. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +2 -1
  7. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +672 -124
  8. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +700 -72
  9. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +531 -135
  10. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +573 -145
  11. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +78 -14
  12. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +600 -0
  13. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +721 -0
  14. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +3 -3
  15. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +119 -32
  16. package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +1147 -0
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +1121 -0
  18. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +2564 -38
  19. package/assets/.appweaver/scripts/bash/aw-xref.sh +2 -1
  20. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +2458 -22
  21. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +2 -1
  22. package/assets/.appweaver/scripts/python/aw-payload-parity.py +158 -0
  23. package/assets/.appweaver/scripts/python/aw-xref.py +431 -48
  24. package/assets/.appweaver/templates/design-template.md +88 -54
  25. package/assets/.appweaver/templates/spec-template.md +12 -10
  26. package/assets/.appweaver/templates/story-template.md +83 -12
  27. package/assets/.appweaver/templates/ui-design-template.md +71 -39
  28. package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +19 -7
  29. package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +35 -26
  30. package/package.json +1 -1
  31. package/assets/.appweaver/orchestrator/workflows/aw-spec-design-ui.orch.yaml +0 -386
  32. package/assets/.appweaver/orchestrator/workflows/aw-spec-design.orch.yaml +0 -382
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # feature = AGGREGATE (e.g. Order). This is the FIRST step of the spec-anchored
5
5
  # pipeline: it fixes the aggregate boundary BEFORE any spec/design exists, so the
6
- # feature's single spec.md + design.md and every command (CreateOrder/UpdateOrder =
6
+ # feature's single spec.md + its per-lane plans and every command (CreateOrder/UpdateOrder =
7
7
  # user stories) hang off one directory. It replaces guessing the aggregate out of prose in
8
8
  # `specify` — that is why finding #1 (feature=aggregate) is enforced here, not hoped for.
9
9
  #
@@ -19,7 +19,7 @@
19
19
  # list-features (specs/ ∪ OKF aggregates) → user_select (existing | "+ create new") → [ask name] → set-feature
20
20
  #
21
21
  # Persists .appweaver/feature.json {"feature_directory":"specs/<Feature>"} so every
22
- # later command operates on it: the forward loop (specify/design/analyze-design/implement/
22
+ # later command operates on it: the forward loop (specify/plan/analyze-design/implement/
23
23
  # build/analyze-slice/run/deploy) and the CR loop (analyze-delta-design/implement-delta/
24
24
  # finalize-delta/close-delta — the standalone `cr` is retired; specify and design author
25
25
  # the delta docs themselves).
@@ -124,5 +124,5 @@ outputs:
124
124
  feature_dir: "{{feature_dir}}"
125
125
  created: "{{created}}"
126
126
  spec_exists: "{{spec_exists}}"
127
- note: "Active feature set in .appweaver/feature.json — the aggregate boundary for its single spec.md + design.md."
127
+ note: "Active feature set in .appweaver/feature.json — the aggregate boundary for its single spec.md and the per-lane plans derived from it (backend-plan.md / frontend-plan.md)."
128
128
  next: "/appweaver specify <what this feature does> (authors {{feature_dir}}/spec.md; each operation = a user story)"
@@ -4,13 +4,14 @@
4
4
  # Spec-anchored pipeline, spec phase. feature = AGGREGATE (picked/created by
5
5
  # /appweaver select-feature, which sets .appweaver/feature.json). specify does
6
6
  # NOT guess or create the feature — it CONSUMES the active one, and is phase-pure
7
- # (spec side only; the single design.md is the `design` phase).
7
+ # (spec side only; the per-lane plans are the `plan` / `plan-ui` phases).
8
8
  #
9
9
  # specify writes ONE artifact: spec.md, plus the Change History row that records what
10
10
  # it wrote. It does not touch stories/. The spec is the CONTRACT; a story is an
11
11
  # execution SLICE over it, and the two directions between them each have their own
12
- # workflow — /appweaver update-stories refreshes the backlog after the spec moves,
13
- # /appweaver update-spec folds a hand-edited story back into the contract. Keeping
12
+ # workflow — /appweaver sync-stories refreshes the backlog after the spec moves,
13
+ # /appweaver sync-spec (⌛ not yet built — .appweaver/SPEC-EDIT-PROTOCOL.md is the hand
14
+ # route meanwhile) folds a hand-edited story back into the contract. Keeping
14
15
  # specify story-blind is what stops the spec->story rule having two implementations.
15
16
  #
16
17
  # Auto-detects first-vs-subsequent from spec.md:
@@ -32,7 +33,7 @@
32
33
  # bootstrap diffs the finished document against nothing, which yields every id it
33
34
  # carries and is exactly the v1.0 row's content. A row the model declared could omit a
34
35
  # requirement the quality loop rewrote on its way past, and an omitted id is silent —
35
- # update-stories would never learn it moved.
36
+ # sync-stories would never learn it moved.
36
37
  # 2. The row and the hash are written by ONE call (ledger-append), so a row can never
37
38
  # exist without the hash that proves the body it describes.
38
39
  # 3. The ledger is checked on ENTRY, not only stamped on exit. A spec hand-edited with
@@ -42,11 +43,11 @@
42
43
  # Launch: /appweaver specify [strategy] <what the feature needs, or what changes>
43
44
  # e.g. /appweaver specify Customers place an order for one or more items
44
45
  # Prereq: /appweaver select-feature (sets the active aggregate)
45
- # Next: /appweaver update-stories (turns the new/changed requirements into stories)
46
+ # Next: /appweaver sync-stories (turns the new/changed requirements into stories)
46
47
  # =============================================================================
47
48
 
48
49
  name: aw-spec-specify
49
- description: Author or extend the specification of the ACTIVE feature (aggregate). The first call writes spec.md from the template; a later call edits spec.md in place, adding the requirements the input describes and amending the ones it changes. Both paths run the checklist/clarify/validate converge loop over spec.md. An extend appends a Change History row whose requirement ids are COMPUTED from a before/after diff, and re-baselines the ledger hash. Writes no stories — /appweaver update-stories owns the backlog. Spec phase only; design is separate.
50
+ description: Author or extend the specification of the ACTIVE feature (aggregate). The first call writes spec.md from the template; a later call edits spec.md in place, adding the requirements the input describes and amending the ones it changes. Both paths run the checklist/clarify/validate converge loop over spec.md. An extend appends a Change History row whose requirement ids are COMPUTED from a before/after diff, and re-baselines the ledger hash. Writes no stories — /appweaver sync-stories owns the backlog. Spec phase only; design is separate.
50
51
  version: 3.4.0
51
52
  agent_agnostic: true
52
53
  dialect: aw-spec.orchestrator/v1
@@ -101,7 +102,7 @@ vars:
101
102
  disturbed_csv: "" # which of them
102
103
  disturbed_ids: "" # the same, as a list — the options of the per-requirement ask
103
104
  disturbed_stories: "" # the open stories citing those ids
104
- disturbed_flying: "" # the subset already past story-spec — a follow-on, not an edit
105
+ disturbed_flying: "" # the subset already past story-plan — a follow-on, not an edit
105
106
  may_change: "" # the requirements the author approved for amendment
106
107
  approved_csv: "" # the same, normalised
107
108
  preserve_csv: "" # disturbed minus approved — restored and re-expressed additively
@@ -239,7 +240,7 @@ workflow:
239
240
  story, never an "As a … I want …" block, never a Given/When/Then acceptance
240
241
  scenario, and never a section for any of those. A story is an execution slice
241
242
  that lives beside this file in stories/ and cites the ids below; it is written
242
- by /appweaver update-stories, not here. Design and code trace to the ids in
243
+ by /appweaver sync-stories, not here. Design and code trace to the ids in
243
244
  this document, which is what lets the backlog churn without disturbing them.
244
245
  - SPECIFY IS THE ONLY PHASE THAT READS THE DOMAIN RULES. Design and implement
245
246
  work from the spec/design, never the domain docs — so this spec is the SOLE
@@ -291,7 +292,7 @@ workflow:
291
292
  read this section — so a bound the SERVICE must meet stays a CN-, cross-referenced.
292
293
  HIGH-LEVEL ONLY, and this is the rule that matters most: every entry is a standard
293
294
  the whole product holds to, checkable on ANY surface. NEVER a screen, page, layout,
294
- navigation model, component or visual-design choice — the prototype and design.ui.md
295
+ navigation model, component or visual-design choice — the prototype and frontend-plan.md
295
296
  settle those, and an entry satisfiable only one way is a design decision smuggled
296
297
  into the spec. A per-feature interaction BEHAVIOUR stays an EARS FR-n; only the
297
298
  standard that governs every such behaviour is a UX-n.
@@ -301,7 +302,7 @@ workflow:
301
302
  "Requirements changed" cell as the template has them. The kit rewrites that row
302
303
  from the finished document, so ids listed here are discarded — the same rule that
303
304
  makes every later version row an observation rather than a claim. Leave its Stories cell as "—";
304
- update-stories fills that in when it raises the backlog.
305
+ sync-stories fills that in when it raises the backlog.
305
306
  - LEAVE THE "## Story Coverage" BLOCK ALONE, placeholder row and all. Everything
306
307
  between the AW:COVERAGE markers is generated from the stories/ directory after
307
308
  this step; anything written there by hand is discarded at the next write.
@@ -328,7 +329,7 @@ workflow:
328
329
  #
329
330
  # unledgered_edit spec.md was hand-edited and no Change History row was added.
330
331
  # Appending this run's row would bump the version past that edit
331
- # without ever naming its ids, so update-stories could not
332
+ # without ever naming its ids, so sync-stories could not
332
333
  # reconcile it and every story citing it stays silently stale.
333
334
  # delta_open a change is open AND its delta-spec has been authored, so a delta
334
335
  # is measured against this master. Temporary — the guard retires with
@@ -392,6 +393,12 @@ workflow:
392
393
  # nothing. Fed to the author as a preference, then checked against the diff below —
393
394
  # the preference is a nudge, the check after the fact is the detector.
394
395
  # Empty until the backlog exists, which degrades to "no preference, no ask".
396
+ # CARRIED, NOT COVERED. `covered_csv` is the DELIVERY relation — the FR-/BR- ids open
397
+ # stories are delivering — and a story also CARRIES the bounds it is built to satisfy.
398
+ # Amending or removing a CN-/DC-/IC-/UX- moves ground somebody is standing on exactly
399
+ # as amending an FR- does, and reading the narrower set here meant it raised no ask at
400
+ # all and printed "nobody has open work" while stories were being built against it.
401
+ # `carried_csv` is exercises: ∪ the `constraints:` frontmatter story-stamp lifts.
395
402
  - id: ext_story_context
396
403
  condition: "{{needs_bootstrap}} == false"
397
404
  action: execute_script
@@ -401,18 +408,21 @@ workflow:
401
408
  args: ["--feature-dir", "{{feature_dir}}"]
402
409
  outputs:
403
410
  - { name: covered_csv, from: "$.covered_csv" }
411
+ - { name: carried_csv, from: "$.carried_csv" }
412
+ - { name: backlog_scanned, from: "$.ok" }
404
413
 
405
414
  # 6b-bis. WORD THE PREFERENCE FOR THE CASE AT HAND. The rule below is an argument for
406
415
  # preferring an ADDITION over an amendment, and it only makes sense while somebody is
407
- # actually building. Interpolating covered_csv straight into it left a spec with no
416
+ # actually building. Interpolating the carried set straight into it left a spec with no
408
417
  # backlog reading an eight-line case for adding requirements under a blank list — the
409
418
  # logic was vacuous ("one of those" names nothing) but the rhetoric was not, and the
410
419
  # first extend of a spec nobody has stories against is exactly where amending in place
411
- # is the honest thing to do. Two ordered sets rather than a compound condition, the
412
- # same idiom as readdit_verdict_na/_real: the no-backlog wording is the default, and
413
- # the preference replaces it only when there is open work to protect.
414
- # Safe on the bootstrap path too — ext_story_context is skipped there, so covered_csv
415
- # is empty and the second step cannot fire.
420
+ # is the honest thing to do. THREE ORDERED SETS rather than a compound condition, the
421
+ # same idiom as readdit_verdict_na/_real: the no-backlog wording is the default, the
422
+ # preference replaces it where there is open work to protect, and the scan-failed
423
+ # wording is last so it wins over both.
424
+ # Safe on the bootstrap path too — ext_story_context is skipped there, so nothing is
425
+ # bound and only the default can fire.
416
426
  - id: ext_covered_none
417
427
  action: set
418
428
  vars:
@@ -424,11 +434,13 @@ workflow:
424
434
  without inventing a companion requirement to avoid touching anything.
425
435
 
426
436
  - id: ext_covered_some
427
- condition: "{{covered_csv}} != ''"
437
+ condition: "{{carried_csv}} != ''"
428
438
  action: set
429
439
  vars:
430
440
  covered_note: >
431
- THESE REQUIREMENTS ARE BEING BUILT RIGHT NOW: {{covered_csv}}. Where the change can
441
+ THESE REQUIREMENTS AND BOUNDS ARE BEING BUILT AGAINST RIGHT NOW: {{carried_csv}}.
442
+ (FR-/BR- are what open stories DELIVER; CN-/DC-/IC-/UX- are what they are built to
443
+ MEET — moving either moves ground someone is standing on.) Where the change can
432
444
  be expressed EITHER as an amendment to one of those or as a new requirement
433
445
  alongside it, prefer the new requirement — an amendment moves ground someone is
434
446
  standing on, and a requirement that gains a companion leaves their work correct as
@@ -438,6 +450,21 @@ workflow:
438
450
  Requirements NOT in that list have no open work against them: amend those in place
439
451
  wherever the change genuinely belongs to them.
440
452
 
453
+ # THE SCAN ITSELF CAN FAIL, and ext_story_context carries `on_error: continue` because a
454
+ # spec must stay editable when its backlog cannot be read. What it must not do is answer
455
+ # as though the scan had run and found nothing: nothing binds, ext_covered_some cannot
456
+ # fire, and the unconditional default above then tells the author NOBODY HAS OPEN WORK —
457
+ # the strongest claim available, on no evidence at all.
458
+ - id: ext_covered_unknown
459
+ condition: "{{backlog_scanned}} != true"
460
+ action: set
461
+ vars:
462
+ covered_note: >
463
+ THE BACKLOG COULD NOT BE READ THIS RUN, so this run does not know who is building
464
+ what. Treat every requirement as possibly under way: prefer adding a requirement to
465
+ amending one, and where you must amend, say in the Change History row exactly what
466
+ moved so the next sync can find every story that carries it.
467
+
441
468
  - id: ext_ctx
442
469
  condition: "{{needs_bootstrap}} == false"
443
470
  action: build_context
@@ -474,7 +501,7 @@ workflow:
474
501
  - {{covered_note}}
475
502
  - THIS DOCUMENT HOLDS NO USER STORIES. Do not add an "As a … I want …" block, a
476
503
  Given/When/Then acceptance scenario, or a section for either. Stories live in
477
- stories/ beside this file and are written by /appweaver update-stories.
504
+ stories/ beside this file and are written by /appweaver sync-stories.
478
505
  - DO NOT TOUCH the "**Version**" header or the "## Change History" table, and leave everything between the AW:COVERAGE markers exactly as it is. Both are
479
506
  written by the kit after this step, from a diff of what you actually changed —
480
507
  editing them here produces a duplicate row and a version nothing agrees on.
@@ -651,7 +678,7 @@ workflow:
651
678
  # Asked, not decided, and RECORD IS FIRST on purpose. spec.md is already written by this
652
679
  # point: a run that refuses the ledger row leaves a document with no row, which is the
653
680
  # `unledgered_edit` state the next run refuses outright. Recording a red spec is honest
654
- # and recoverable — the row names what moved, so update-stories still works, and the
681
+ # and recoverable — the row names what moved, so sync-stories still works, and the
655
682
  # failures are in the checklist file. Stopping is the safer-feeling option that costs
656
683
  # more, so the prompt says what it costs and how to undo it.
657
684
  - id: quality_ask
@@ -691,7 +718,7 @@ workflow:
691
718
  # diff for the reason this file opens with: a row the model DECLARED can omit an id it
692
719
  # moved, and an omitted id is silent. v1.0 was the one exception — written by the
693
720
  # author from a template whose placeholder reads "FR-001…FR-00n", so nothing
694
- # downstream could trust it and update-stories had to be told to enumerate from the
721
+ # downstream could trust it and sync-stories had to be told to enumerate from the
695
722
  # document instead. Diffing against no snapshot yields every id the finished spec
696
723
  # carries, which is exactly the v1.0 row's content, so the first row is now derived
697
724
  # the same way as the rest and the special case downstream goes away.
@@ -755,6 +782,26 @@ workflow:
755
782
  - { name: disturbing_csv, from: "$.disturbing_csv" }
756
783
  - { name: reissued_csv, from: "$.reissued_csv" }
757
784
  - { name: change_count, from: "$.change_count" }
785
+ - { name: diff_warnings, from: "$.warnings_note" }
786
+
787
+ # 10a-bis. IDS THE LEDGER CANNOT KEY TO A DEFINITION. The diff keys an id to the line that
788
+ # DEFINES it and hashes the block under it; three authoring forms fall outside that,
789
+ # and each one is silent — the requirement moves, change_count stays 0, no row is
790
+ # appended, and the stamp below re-baselines the new body as clean. A standing
791
+ # property of the document rather than a verdict on this run, so it is reported every
792
+ # time until the requirement is rewritten in the shape the template uses.
793
+ - id: diff_warning_notice
794
+ condition: "{{diff_warnings}} != ''"
795
+ action: verify_context
796
+ require_value: diff_warnings
797
+ equals: ""
798
+ on_mismatch: continue
799
+ on_mismatch_message: >
800
+ THE LEDGER CANNOT SEE PART OF THIS SPEC. {{spec_file}} states requirements in a form
801
+ the version diff cannot key to a definition, so an edit to one of them raises no
802
+ Change History row and no story citing it is ever told: {{diff_warnings}}. Rewrite them
803
+ in the template's shape — `- **FR-001**: …`, defined ABOVE the Change History table,
804
+ with any continuation on the lines directly under the id and no blank line between.
758
805
 
759
806
  # 10b. DID THIS MOVE GROUND SOMEONE IS STANDING ON? The diff is fact, so this is the
760
807
  # detector the pre-authoring preference lacked: of the requirements this run
@@ -784,7 +831,7 @@ workflow:
784
831
  # two outcomes differ sharply for the people downstream —
785
832
  #
786
833
  # amend the contract moves. Every open story citing it goes stale, and
787
- # update-stories reconciles them: edited in place if no story-spec
834
+ # sync-stories reconciles them: edited in place if no story plan
788
835
  # exists yet, carried by a follow-on if one does (§5.5).
789
836
  # add the amended requirements go back to how they read, and the change is
790
837
  # expressed as new requirements alongside them. Nobody's work goes
@@ -803,10 +850,10 @@ workflow:
803
850
  This change MODIFIED or REMOVED requirements that people have open stories against.
804
851
  Requirements: {{disturbed_csv}}
805
852
  Open stories: {{disturbed_stories}}
806
- Already in flight (story-spec exists): {{disturbed_flying}}
853
+ Already in flight (story plan exists): {{disturbed_flying}}
807
854
  Pick the ones this change MAY alter. Each one you pick moves the contract: its open
808
- stories go stale, and update-stories reconciles them — editing a story in place, or
809
- minting a follow-on where a story-spec already exists. Each one you LEAVE OUT is put
855
+ stories go stale, and sync-stories reconciles them — editing a story in place, or
856
+ minting a follow-on where a story plan already exists. Each one you LEAVE OUT is put
810
857
  back exactly as it read, and the change it was carrying is expressed as a new
811
858
  requirement alongside it, so the work already under way stays correct.
812
859
  Which may be changed?
@@ -1024,7 +1071,7 @@ workflow:
1024
1071
  # It announces rather than stops. spec.md is already written by this point, so a stop
1025
1072
  # here leaves a document on disk with no ledger row, which is the state the next run's
1026
1073
  # tripwire refuses. Announcing keeps the ledger row honest — the violated ids ARE in
1027
- # it, so update-stories still reconciles their stories — and moves them out of the
1074
+ # it, so sync-stories still reconciles their stories — and moves them out of the
1028
1075
  # "preserved" column of the report into a column that says what actually happened.
1029
1076
  - id: ext_restore_check
1030
1077
  condition: "{{preserve_count}} != 0"
@@ -1045,7 +1092,7 @@ workflow:
1045
1092
  on_false_message: >
1046
1093
  The re-authoring did NOT restore {{violated_csv}} — you declined to change these and
1047
1094
  they moved anyway. They are recorded in this version's Change History row, so
1048
- /appweaver update-stories will reconcile the stories citing them rather than leaving
1095
+ /appweaver sync-stories will reconcile the stories citing them rather than leaving
1049
1096
  them silently stale; check that the wording it landed on is one you can live with.
1050
1097
 
1051
1098
  # 10h. Re-read the affected stories from the FINAL document. After a restore, the
@@ -1065,6 +1112,23 @@ workflow:
1065
1112
  args: ["--feature-dir", "{{feature_dir}}", "--ids", "{{disturbing_csv}}"]
1066
1113
  outputs:
1067
1114
  - { name: disturbed_stories, from: "$.story_csv" }
1115
+ - { name: final_refs_ok, from: "$.ok" }
1116
+
1117
+ # 10h-bis. THE SAME HONESTY THE PRE-AUTHORING SCAN NOW HAS. This step carries
1118
+ # `on_error: continue` so a failed re-scan cannot strand a written document, and on
1119
+ # failure disturbed_stories binds to nothing — which the post-restore report (law 45)
1120
+ # then reads as "nobody was disturbed", the one claim it must not make on no evidence.
1121
+ - id: final_refs_unknown_notice
1122
+ condition: "{{final_refs_ok}} != true"
1123
+ action: verify_context
1124
+ require_value: final_refs_ok
1125
+ equals: "true"
1126
+ on_mismatch: continue
1127
+ on_mismatch_message: >
1128
+ The final backlog re-scan did not complete, so the disturbed-stories list below is
1129
+ NOT a list of nobody — it is unknown. The requirements this run moved are
1130
+ {{disturbing_csv}}; run /appweaver sync-stories to find and reconcile the stories
1131
+ carrying them.
1068
1132
 
1069
1133
  # 10i. AN ID THE LEDGER HAS ALREADY SPENT. Ids are permanent: re-issuing a retired one
1070
1134
  # silently re-points stories, designs and code that have already shipped, which is
@@ -1087,7 +1151,7 @@ workflow:
1087
1151
  # 11. One call writes the row, bumps the version, stamps the header and re-hashes, so
1088
1152
  # a row cannot exist without the hash that proves the body it describes. Skipped
1089
1153
  # when the run changed no requirement — a version that names no ids is a bump
1090
- # update-stories can do nothing with.
1154
+ # sync-stories can do nothing with.
1091
1155
  - id: ext_ledger_append
1092
1156
  condition: "{{change_count}} != 0"
1093
1157
  action: execute_script
@@ -1097,6 +1161,29 @@ workflow:
1097
1161
  outputs:
1098
1162
  - { name: ledger_version, from: "$.version" }
1099
1163
 
1164
+ # 11a-bis. A PROSE-ONLY EDIT RE-BASELINES WITH NO ROW, AND SAYS SO. ledger-append is
1165
+ # conditional on change_count, because law 5 requires a version row to NAME at least
1166
+ # one id and an edit that touched no requirement has none to give. The stamp below is
1167
+ # not conditional, so this run rewrites the baseline and the document's own history
1168
+ # records nothing — which is right, and was silent. Aggregate Boundary, Experience
1169
+ # Scope and the rest of the load-bearing prose are read by every later phase and have
1170
+ # no staleness signal of their own; the least this can do is say that the baseline
1171
+ # moved and nothing recorded why.
1172
+ - id: prose_only_notice
1173
+ condition: "{{change_count}} == 0"
1174
+ action: verify_context
1175
+ require_value: needs_bootstrap
1176
+ equals: "true"
1177
+ on_mismatch: continue
1178
+ on_mismatch_message: >
1179
+ This run changed {{spec_file}} but moved no requirement: the diff found no id added,
1180
+ modified or removed, so no Change History row was appended — a row must name at least
1181
+ one id, and there is none to name. The document is still RE-BASELINED below, so the
1182
+ next run reads it as clean. Nothing else is: if the edit touched load-bearing prose
1183
+ (Aggregate Boundary, Experience Scope, Scope & Assumptions), no story is stale, no
1184
+ sync will notice, and the only record is this message and your commit. Say what moved
1185
+ in the commit message.
1186
+
1100
1187
  # ═══════════════ STORY COVERAGE — both paths, before the baseline ══════════
1101
1188
  # 11b. Rewrite the spec's AW:COVERAGE block: every FR-/BR- in the document and the
1102
1189
  # stories delivering it, read out of the stories/ frontmatter. The relation is
@@ -1105,7 +1192,7 @@ workflow:
1105
1192
  #
1106
1193
  # Deliberately OUTSIDE the ledger. The hash guards the CONTRACT, and coverage is a
1107
1194
  # projection of the backlog — spec-check, spec-diff and ledger-append all cut this
1108
- # block out before they look. That is what lets user-story and update-stories write
1195
+ # block out before they look. That is what lets add-story and sync-stories write
1109
1196
  # it too without a story creation reading as an unledgered spec edit, or a story
1110
1197
  # raised against FR-003 reading as FR-003 having changed.
1111
1198
  #
@@ -1172,7 +1259,7 @@ outputs:
1172
1259
  spec_file: "{{spec_file}}"
1173
1260
  version: "{{version}}"
1174
1261
  requirements_changed: "{{changed_csv}}" # every class — what the ledger row records
1175
- requirements_for_stories: "{{story_ids_csv}}" # the FR-/BR- subset — what update-stories acts on
1262
+ requirements_for_stories: "{{story_ids_csv}}" # the FR-/BR- subset — what sync-stories acts on
1176
1263
  requirements_removed: "{{removed_csv}}"
1177
1264
  requirements_amended: "{{modified_csv}}" # what finally stands amended, ask or no ask
1178
1265
  requirements_preserved: "{{held_csv}}" # the ones the restore actually put back
@@ -1183,4 +1270,4 @@ outputs:
1183
1270
  stories_disturbed: "{{disturbed_stories}}"
1184
1271
  checklist_file: "{{checklist_file}}"
1185
1272
  checklist_quality: "{{checklist_passed}}/{{checklist_total}} items passing"
1186
- next: "spec.md is at {{version}}. Run /appweaver update-stories — it raises or refreshes the backlog for the requirements this run touched that a story can cite ({{story_ids_csv}}), and nothing else keeps the contract and its stories in step. That is the FR-/BR- subset of requirements_changed on purpose: the ledger row records every class this run moved, but only these are slices of work. Then /appweaver design authors design.md from the spec. READ THE FIELDS ABOVE BEFORE YOU DO: requirements_removed orphans every story citing them and needs deciding by hand; requirements_not_restored moved despite you declining them; requirements_reissued point at something other than what cited them before; open_change will be merged into this document by finalize-delta; and checklist_quality is what the spec actually scored."
1273
+ next: "spec.md is at {{version}}. Run /appweaver sync-stories — it raises or refreshes the backlog for the requirements this run touched that a story can cite ({{story_ids_csv}}), and nothing else keeps the contract and its stories in step. That is the FR-/BR- subset of requirements_changed on purpose: the ledger row records every class this run moved, but only these are slices of work. Then /appweaver plan authors or conforms backend-plan.md to this spec version, and names the /appweaver plan-ui follow-up when a frontend lane is declared. READ THE FIELDS ABOVE BEFORE YOU DO: requirements_removed orphans every story citing them and needs deciding by hand; requirements_not_restored moved despite you declining them; requirements_reissued point at something other than what cited them before; open_change will be merged into this document by finalize-delta; and checklist_quality is what the spec actually scored."