aw-spec-cli 0.1.15 → 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.
@@ -4,7 +4,8 @@
4
4
  # Spec-anchored pipeline, spec phase. The spec is the CONTRACT and a story is an
5
5
  # execution SLICE over it. The two directions between them each have their own
6
6
  # workflow and neither is shared: sync-stories carries the SPEC forward into the
7
- # backlog; /appweaver update-spec folds a hand-edited STORY back into the contract.
7
+ # backlog; /appweaver sync-spec (⌛ not yet built — .appweaver/SPEC-EDIT-PROTOCOL.md is the
8
+ # hand route meanwhile) folds a hand-edited STORY back into the contract.
8
9
  # Keeping them apart is what stops the spec->story rule having two implementations.
9
10
  #
10
11
  # WHAT THIS READS. The spec's `## Change History` — the version ledger. Each row names
@@ -110,7 +111,7 @@
110
111
  # =============================================================================
111
112
 
112
113
  name: aw-spec-sync-stories
113
- description: "Raise and refresh the story backlog of the ACTIVE feature from its spec. Reads the spec's Change History ledger to find the requirements that have moved since the backlog was last in step, groups them into execution slices, asks before disturbing a story someone already has open, and writes one story per slice — creating, updating in place, or minting a follow-on where a story plan already exists. Slicing is steered by an opening strategy word: `single-run` puts the whole work set into ONE story, splitting it along its dependencies only where that story could not be picked up as one piece, and reporting the split either way; omitted, the number of slices is decided from the requirements. The grouping declares which slices stand behind which, and the kit computes DELIVERY WAVES from those edges — wave N holds the stories whose every dependency sits below it, so stories sharing a wave have no ordering between them and may be picked up in parallel. Writes `depends_on:` and `wave:` into each story's frontmatter, breaks any declared cycle deterministically, and regenerates the spec's Story Coverage block. Writes no requirements — /appweaver specify and /appweaver update-spec own the contract."
114
+ description: "Raise and refresh the story backlog of the ACTIVE feature from its spec. Reads the spec's Change History ledger to find the requirements that have moved since the backlog was last in step, groups them into execution slices, asks before disturbing a story someone already has open, and writes one story per slice — creating, updating in place, or minting a follow-on where a story plan already exists. Slicing is steered by an opening strategy word: `single-run` puts the whole work set into ONE story, splitting it along its dependencies only where that story could not be picked up as one piece, and reporting the split either way; omitted, the number of slices is decided from the requirements. The grouping declares which slices stand behind which, and the kit computes DELIVERY WAVES from those edges — wave N holds the stories whose every dependency sits below it, so stories sharing a wave have no ordering between them and may be picked up in parallel. Writes `depends_on:` and `wave:` into each story's frontmatter, breaks any declared cycle deterministically, and regenerates the spec's Story Coverage block. Writes no requirements — /appweaver specify and /appweaver sync-spec own the contract."
114
115
  version: 1.1.0
115
116
  agent_agnostic: true
116
117
  dialect: aw-spec.orchestrator/v1
@@ -342,6 +343,19 @@ workflow:
342
343
  for what the LAST row named and leave the hand edit unnamed and unreconciled.
343
344
  Resolve the state above, then re-run. (State: {{ledger_state}}.)
344
345
 
346
+ # 3a. THE TWO STATES THAT PASS `ok` AND SHOULD NOT PASS HERE. spec-check's decisive branch
347
+ # asks only whether a version ROW sits above the version the kit last recorded, and
348
+ # never opens the body — so `ledgered_edit` and `header_behind` are ok:true with a
349
+ # document nobody has re-baselined, and every LATER edit lands inside that same window
350
+ # and passes the same test. Syncing from there raises a backlog from rows that are
351
+ # recorded and a body that is not. The module is shared with plan, plan-ui,
352
+ # analyze-design, story-plan, analyze-slice and both implements — the fence is the
353
+ # same wherever spec.md is read and not stamped.
354
+ - id: spec_state_guard
355
+ action: include
356
+ module: .appweaver/orchestrator/modules/spec-state-gate.orch.yaml
357
+ with: { command: "/appweaver sync-stories" }
358
+
345
359
  # 4. THE WORK SET. See the header for why it spans versions rather than reading the
346
360
  # newest row alone, and for the v1.0 case.
347
361
  - id: ledger_read
@@ -363,6 +377,9 @@ workflow:
363
377
  - { name: synced_floor, from: "$.synced_floor" }
364
378
  - { name: floor_source, from: "$.floor_source" }
365
379
  - { name: latest_version, from: "$.latest_version" }
380
+ - { name: non_story_csv, from: "$.non_story_csv" }
381
+ - { name: non_story_any, from: "$.non_story_any" }
382
+ - { name: work_kind, from: "$.work_kind" }
366
383
 
367
384
  # 4a. AN EMPTY LEDGER STOPS THE RUN. The table is there and carries no version row, which
368
385
  # means the spec has never been versioned. There is no "since" to sync from, and
@@ -382,17 +399,48 @@ workflow:
382
399
  /appweaver sync-stories. (If the spec was written by hand, add a row naming the
383
400
  requirement ids it introduced and re-run spec-stamp.)
384
401
 
402
+ # 4a-bis. WHAT MOVED THAT NO STORY CAN BE RAISED FOR. Constraints and success criteria
403
+ # travel in the same version rows as requirements, and this workflow can act on
404
+ # neither: a story's `exercises:` is FR-/BR- only, because those are DELIVERED, while a
405
+ # CN-/DC-/IC-/UX- is MET and an SC- is SCORED. Until now that meant a row naming only
406
+ # those produced an empty work set and a run that stopped with "the backlog is already
407
+ # in step" — about the exact ids story-plan-check had just reported as stale.
408
+ #
409
+ # BEFORE the work gate and INDEPENDENT of it, on purpose. A row naming FR-001 AND
410
+ # CN-001 has real story work, so the gate below passes and the run continues — and
411
+ # branching this notice on "no work" would then have said nothing at all about the
412
+ # constraint, which is the same silence one case over. It fires on the ids, never on
413
+ # the shape of the run.
414
+ - id: non_story_notice
415
+ condition: "{{non_story_csv}} != ''"
416
+ action: verify_context
417
+ require_value: non_story_csv
418
+ equals: ""
419
+ on_mismatch: continue
420
+ on_mismatch_message: >
421
+ ALSO MOVED, AND NOT SLICEABLE: {{non_story_csv}}. These are carried by stories, not
422
+ delivered by them, so this run raises nothing for them and none of the writes below
423
+ touches them.
424
+ • CN-/DC-/IC-/UX- are BOUNDS. Every open story is built to satisfy the feature's whole
425
+ constraint set, so a moved bound touches all of them: re-run /appweaver story-plan on
426
+ each open story to design against the new wording, and see .appweaver/SPEC-EDIT-PROTOCOL.md
427
+ §4.2 for the by-hand reconciliation until /appweaver sync-spec ships.
428
+ • SC- are feature-level OUTCOMES, scored at /appweaver analyze-slice against the whole
429
+ feature — no story claims one, and none should.
430
+
385
431
  # 4b. NOTHING TO DO IS NOT AN ERROR. Every version row is at or below the oldest open
386
432
  # story's stamp: the backlog is already in step. Stopping here rather than running an
387
- # empty grouping keeps the no-op cheap and the message honest.
433
+ # empty grouping keeps the no-op cheap and the message honest. Left as `work_any`
434
+ # (FR-/BR- only) deliberately — the non-story notice above has already fired by the
435
+ # time this is reached, so a constraint-only row now stops with its reason SAID.
388
436
  - id: work_gate
389
437
  action: verify_context
390
438
  require_true: work_any
391
439
  on_false: stop
392
440
  on_false_message: >
393
- The backlog is already in step with {{spec_file}} at {{latest_version}} — every
394
- requirement a version row names has either been delivered by a closed story or is
395
- already carried by an open one synced past it. Nothing was written. ({{work_note}}.)
441
+ No story work: every requirement a version row names has either been delivered by a
442
+ closed story or is already carried by an open one synced past it, so the backlog is in
443
+ step with {{spec_file}} at {{latest_version}}. Nothing was written. ({{work_note}}.)
396
444
 
397
445
  # 4c. A REMOVED REQUIREMENT IS A DECISION, NOT A DEFAULT (E1). A row names what a version
398
446
  # TOUCHED, and that includes ids it deleted. Those leave the work set — there is no
@@ -618,7 +666,8 @@ workflow:
618
666
  on_mismatch_message: >
619
667
  The grouping left these requirements out of every slice: {{unplaced_csv}}. No story
620
668
  will be written for them by this run. Re-run /appweaver sync-stories to place them,
621
- or raise them by hand with /appweaver user-story.
669
+ or raise them by hand from .appweaver/templates/story-template.md (/appweaver add-story,
670
+ which mints the id and stamps the frontmatter, is ⌛ not yet built).
622
671
 
623
672
  - id: repeated_notice
624
673
  condition: "{{repeated_csv}} != ''"
@@ -708,7 +757,8 @@ workflow:
708
757
  the grouping produced no slice for any of them ({{work_count}} requirement(s) went
709
758
  in, {{group_count}} group(s) came out). Nothing was declined and nothing was
710
759
  written — re-run /appweaver sync-stories, and if it comes back empty again, raise
711
- the stories by hand with /appweaver user-story
760
+ the stories by hand from .appweaver/templates/story-template.md (/appweaver add-story
761
+ is ⌛ not yet built)
712
762
 
713
763
  - id: nothing_declined
714
764
  condition: "{{skipped_csv}} != ''"
@@ -828,7 +878,8 @@ workflow:
828
878
  - THE SPEC IS THE CONTRACT AND THIS IS A SLICE OF WORK OVER IT. Nothing here
829
879
  changes it. If writing this story exposes something the spec gets wrong or
830
880
  leaves out, say so in Notes and leave the requirement alone — the contract
831
- moves through /appweaver update-spec, never through a story.
881
+ moves through /appweaver sync-spec (⌛ not yet built — until it is, the hand
882
+ route is .appweaver/SPEC-EDIT-PROTOCOL.md), never through a story.
832
883
  - Frontmatter: set `id:` to {{p.story}}, `exercises:` to exactly
833
884
  [{{p.requirements_csv}}], `status: open`, and `spec_version:` to
834
885
  {{latest_version}}. Give it a `title:` that names the OUTCOME a reviewer would
@@ -845,18 +896,19 @@ workflow:
845
896
  - "## Requirements delivered": one line per id, saying what THIS story delivers
846
897
  OF that requirement. A story rarely delivers a whole requirement; name the
847
898
  part. Quote no requirement in full — the spec is the wording.
848
- - "## Constraints" IS NOT OPTIONAL AND IS THE HALF MOST OFTEN LOST. Read the
849
- spec's Constraints & NFRs (CN-), Data Classification (DC-), Integration
850
- Contracts (IC-) and Experience Standards (UX-) sections, and its Experience
851
- Scope, and carry BY ID every entry that binds this slice — with one line each
852
- saying what this slice has to do about it. Where the slice has a user-facing
853
- surface, the UX- standards and the device / browser / assistive-technology
854
- floors apply and MUST be listed: prototype, design-ui and implement-ui author
855
- from the spec and the story, so a standard not carried here is a standard this
856
- slice will be built without. Do NOT add these ids to `exercises:` — that field
857
- is FR-/BR- only, because it is the delivery relation, and a constraint is not
858
- delivered, it is met. Write "None" only for a class the spec genuinely leaves
859
- empty or that genuinely does not touch this slice.
899
+ - "## Constraints" — THE IDS ARE STAMPED FOR YOU; WRITE THE LINE EACH. The kit
900
+ lifts every CN-, DC-, IC- and UX- the spec declares into this story's
901
+ `constraints:` frontmatter, because they are declared aggregate-wide and so the
902
+ feature's whole set binds every slice of it. That field is the gate surface;
903
+ this section is what makes it READABLE. Write one line per id saying what THIS
904
+ slice has to do about it, and where one genuinely does not apply here, say so
905
+ plainly in its line — do not omit it. An omission used to be indistinguishable
906
+ from a bound nobody noticed, which is exactly how a slice gets built without one.
907
+ Carry the Experience Scope floors (device / browser / assistive technology) in
908
+ the same way wherever this slice has a UI surface: implement-ui authors from the
909
+ spec and the story, so a floor not stated here is one it will be built without.
910
+ Do NOT add these ids to `exercises:` — that field is FR-/BR- only, because it is
911
+ the delivery relation, and a constraint is not delivered, it is met.
860
912
  - "## Acceptance": checkable statements in the spec's vocabulary, covering every
861
913
  requirement in `exercises:` and every constraint listed above that this slice
862
914
  can be observed to meet. These are what analyze-slice pairs tests against, so
@@ -966,11 +1018,11 @@ workflow:
966
1018
  - WHERE AN ID IT ALREADY CITED HAS CHANGED WORDING, restate what this story
967
1019
  delivers of it and re-check every acceptance line that depended on the old
968
1020
  reading. That is the whole reason this story is being touched.
969
- - "## Constraints" is re-derived, not preserved: read the spec's CN-/DC-/IC-/UX-
970
- sections and its Experience Scope again and carry by id what binds the slice as
971
- it now stands. A constraint that has left the spec leaves this section; one that
972
- has arrived is added, with the acceptance line that shows it is met. Never add
973
- these ids to `exercises:`.
1021
+ - "## Constraints" — the ids are re-stamped into `constraints:` for you from the
1022
+ spec as it now stands; this section's LINES are yours. A bound that has left the
1023
+ spec leaves this section; one that has arrived gets its line, with the acceptance
1024
+ line that shows it is met. Say plainly where one does not apply to this slice
1025
+ rather than dropping it. Never add these ids to `exercises:`.
974
1026
  - NO IMPLEMENTATION DETAIL, and nothing here changes the spec. If the change
975
1027
  reveals a problem with the contract, note it and leave the requirement alone.
976
1028
 
@@ -988,7 +1040,36 @@ workflow:
988
1040
  # the sync sees one feature's spec, and an edge added by hand may name work outside
989
1041
  # it. `--wave` is set outright — it is derived from the union, so re-deriving it is
990
1042
  # the only way it stays true.
991
- args: ["--feature-dir", "{{feature_dir}}", "--story", "{{p.story}}", "--ids", "{{p.requirements_csv}}", "--version", "{{latest_version}}", "--follows", "{{p.follows}}", "--title", "{{p.group_title}}", "--depends-on", "{{p.depends_on_csv}}", "--wave", "{{p.wave}}"]
1043
+ # `--constraints` is a BARE FLAG: the op has the feature dir, so it lifts the ids
1044
+ # from spec.md itself. Nothing here enumerates them, because there is nothing to
1045
+ # decide — spec constraints are declared aggregate-wide ("must ALWAYS hold for the
1046
+ # aggregate, independent of any single operation"), so the feature's whole set binds
1047
+ # every slice of it. This is what makes `constraints:` a gate surface the KIT
1048
+ # writes; the body's "## Constraints" section keeps the per-slice prose and is
1049
+ # reconciled against the field, never overwritten by it.
1050
+ args: ["--feature-dir", "{{feature_dir}}", "--story", "{{p.story}}", "--ids", "{{p.requirements_csv}}", "--version", "{{latest_version}}", "--follows", "{{p.follows}}", "--title", "{{p.group_title}}", "--depends-on", "{{p.depends_on_csv}}", "--wave", "{{p.wave}}", "--constraints"]
1051
+ outputs:
1052
+ - { name: stamped_constraints_csv, from: "$.constraints_csv" }
1053
+ - { name: constraints_unlined_csv, from: "$.unlined_csv" }
1054
+
1055
+ # 10e. THE RECONCILE REPORT. The stamp owns the ID SET and the model owns the LINE
1056
+ # each id gets — "what this slice has to do about it" is the reason the section
1057
+ # is worth reading, and a script cannot write it. So a bound that is now in the
1058
+ # frontmatter with no line in the body is announced rather than silently added:
1059
+ # the gate surface is already closed by the field, and this is only so the
1060
+ # missing sentence is loud.
1061
+ - id: constraints_reconcile_notice
1062
+ condition: "{{constraints_unlined_csv}} != ''"
1063
+ action: verify_context
1064
+ require_value: constraints_unlined_csv
1065
+ equals: ""
1066
+ on_mismatch: continue
1067
+ on_mismatch_message: >
1068
+ {{p.story}} is bound by {{stamped_constraints_csv}} — the ids are stamped into its
1069
+ `constraints:` frontmatter, which is what implement, implement-ui and analyze-slice
1070
+ read. These have no line under "## Constraints" saying what this slice does about
1071
+ them: {{constraints_unlined_csv}}. Add one each, or say plainly that it does not
1072
+ apply here. The bound holds either way; what is missing is the reader's half.
992
1073
 
993
1074
  # 11. THE RELATION, READ THE OTHER WAY ROUND. Every story declares what it delivers in
994
1075
  # its own `exercises:`; the spec's Story Coverage block is that relation viewed from