@gobing-ai/spur 0.3.74 → 0.3.76

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 (78) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.example.yaml +6 -0
  3. package/config/rules/README.md +1 -1
  4. package/config/rules/strict/runtime-boundaries.yaml +1 -0
  5. package/config/rules/typescript/no-eslint-suppressions.yaml +30 -0
  6. package/config/workflows/docs-pipeline.yaml +26 -1
  7. package/config/workflows/feature-dev.yaml +143 -102
  8. package/config/workflows/idea-pipeline.yaml +58 -11
  9. package/config/workflows/task-pipeline.yaml +76 -40
  10. package/config/workflows/wrapup-pipeline.yaml +183 -111
  11. package/package.json +9 -9
  12. package/plugins/sp/agents/expert-spur.md +7 -4
  13. package/plugins/sp/commands/dev-idea.md +1 -1
  14. package/plugins/sp/commands/dev-refineall.md +2 -2
  15. package/plugins/sp/plugin.json +1 -1
  16. package/plugins/sp/skills/dogfood-testing/SKILL.md +7 -4
  17. package/plugins/sp/skills/spur-cli/references/tasks/section-editing.md +9 -3
  18. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +5 -3
  19. package/plugins/sp/skills/spur-cli/references/tasks.md +3 -1
  20. package/plugins/sp/skills/spur-cli/references/workflows.md +3 -3
  21. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +23 -8
  22. package/plugins/sp/skills/spur-dev/references/dev-operations.md +3 -3
  23. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +4 -1
  24. package/schemas/spur-config.schema.json +4 -0
  25. package/spur.js +3675 -838
  26. package/web/_astro/{BoardApp.C60RJZRj.js → BoardApp.CHQ1lycZ.js} +92 -92
  27. package/web/_astro/BoardApp.DV9kx0wo.js +1 -0
  28. package/web/_astro/{TaskDetail.Guk8VuNu.js → TaskDetail.GKfQJ60c.js} +1 -1
  29. package/web/_astro/{arc.CAZtlWJM.js → arc.DWEtA3Tx.js} +1 -1
  30. package/web/_astro/{architectureDiagram-3BPJPVTR.9-XbF_Tz.js → architectureDiagram-3BPJPVTR.DB42oWmP.js} +1 -1
  31. package/web/_astro/{blockDiagram-GPEHLZMM.DC4WLx3L.js → blockDiagram-GPEHLZMM.rhv-zNQV.js} +1 -1
  32. package/web/_astro/{c4Diagram-AAUBKEIU.JgjUQMgG.js → c4Diagram-AAUBKEIU.Ci4-4VvY.js} +1 -1
  33. package/web/_astro/channel.BAI6xLeV.js +1 -0
  34. package/web/_astro/{chunk-2J33WTMH.D_JFiXa-.js → chunk-2J33WTMH.Cc9veUgf.js} +1 -1
  35. package/web/_astro/{chunk-4BX2VUAB.Bk_RxoeT.js → chunk-4BX2VUAB.Bec9c4eI.js} +1 -1
  36. package/web/_astro/{chunk-55IACEB6.a_Rj2VxL.js → chunk-55IACEB6.DoV8S1iB.js} +1 -1
  37. package/web/_astro/{chunk-727SXJPM.C2NoS1U6.js → chunk-727SXJPM.DwR-Qlyj.js} +1 -1
  38. package/web/_astro/{chunk-AQP2D5EJ.D1Xn6CNa.js → chunk-AQP2D5EJ.ND_a81WY.js} +1 -1
  39. package/web/_astro/{chunk-FMBD7UC4.BhJrSBsX.js → chunk-FMBD7UC4.Wv_jwG48.js} +1 -1
  40. package/web/_astro/{chunk-ND2GUHAM.ByVGduYQ.js → chunk-ND2GUHAM.CXKXCMmp.js} +1 -1
  41. package/web/_astro/{chunk-QZHKN3VN.B47Paunq.js → chunk-QZHKN3VN.nkaoNYQq.js} +1 -1
  42. package/web/_astro/{classDiagram-4FO5ZUOK.BERMSD5C.js → classDiagram-4FO5ZUOK.cMQcVlQu.js} +1 -1
  43. package/web/_astro/{classDiagram-v2-Q7XG4LA2.BERMSD5C.js → classDiagram-v2-Q7XG4LA2.cMQcVlQu.js} +1 -1
  44. package/web/_astro/{cose-bilkent-S5V4N54A.O_rHGlhU.js → cose-bilkent-S5V4N54A.OaDJ7Mr2.js} +1 -1
  45. package/web/_astro/{cynefin-OW5HDTMX.qCW2GdNp.js → cynefin-OW5HDTMX.Chi8IphF.js} +1 -1
  46. package/web/_astro/{dagre-BM42HDAG.C5Y4lH_u.js → dagre-BM42HDAG.CzK2t_Fp.js} +1 -1
  47. package/web/_astro/{diagram-2AECGRRQ.DxZmRXxF.js → diagram-2AECGRRQ.DRvxlVS7.js} +1 -1
  48. package/web/_astro/{diagram-5GNKFQAL.qSxZeYSK.js → diagram-5GNKFQAL.CnYvNdwA.js} +1 -1
  49. package/web/_astro/{diagram-KO2AKTUF.-7vl3bXs.js → diagram-KO2AKTUF.CpLpMw5R.js} +1 -1
  50. package/web/_astro/{diagram-LMA3HP47.BHVV-UH3.js → diagram-LMA3HP47.JTb78qUA.js} +1 -1
  51. package/web/_astro/{diagram-OG6HWLK6.DHAuW9sK.js → diagram-OG6HWLK6.Bk-1jDIb.js} +1 -1
  52. package/web/_astro/{erDiagram-TEJ5UH35.DMRLQwPN.js → erDiagram-TEJ5UH35.D8hN9GZq.js} +1 -1
  53. package/web/_astro/{flowDiagram-I6XJVG4X.B3EpPp_8.js → flowDiagram-I6XJVG4X.-6zQr6m5.js} +1 -1
  54. package/web/_astro/{ganttDiagram-6RSMTGT7.BgUrExXM.js → ganttDiagram-6RSMTGT7.DboLQ9ca.js} +1 -1
  55. package/web/_astro/{gitGraphDiagram-PVQCEYII.CU-9yVN4.js → gitGraphDiagram-PVQCEYII.4tYvJKGR.js} +1 -1
  56. package/web/_astro/index.Dcr_8fiK.css +1 -0
  57. package/web/_astro/{infoDiagram-5YYISTIA.CvVTCRLe.js → infoDiagram-5YYISTIA.Bd9rXpsB.js} +1 -1
  58. package/web/_astro/{ishikawaDiagram-YF4QCWOH.BUzOfKPf.js → ishikawaDiagram-YF4QCWOH.CvMoaf67.js} +1 -1
  59. package/web/_astro/{journeyDiagram-JHISSGLW.BBTb7ziz.js → journeyDiagram-JHISSGLW.Ccy1CA7y.js} +1 -1
  60. package/web/_astro/{kanban-definition-UN3LZRKU.CTQQr70i.js → kanban-definition-UN3LZRKU.0MaMqHNS.js} +1 -1
  61. package/web/_astro/{linear.CLKlJPiS.js → linear.CHXgcIbN.js} +1 -1
  62. package/web/_astro/{mermaid.core.BA4wFhLP.js → mermaid.core.Ca-kcelG.js} +4 -4
  63. package/web/_astro/{mindmap-definition-RKZ34NQL.t1vG2l5e.js → mindmap-definition-RKZ34NQL.BUIDlHa0.js} +1 -1
  64. package/web/_astro/{pieDiagram-4H26LBE5.C8zJhyTu.js → pieDiagram-4H26LBE5.2dX3CU1s.js} +1 -1
  65. package/web/_astro/{quadrantDiagram-W4KKPZXB.DqYYvb7T.js → quadrantDiagram-W4KKPZXB.B3LBlRiv.js} +1 -1
  66. package/web/_astro/{requirementDiagram-4Y6WPE33.lzfDZ9nI.js → requirementDiagram-4Y6WPE33.X12I2uNx.js} +1 -1
  67. package/web/_astro/{sankeyDiagram-5OEKKPKP.lm6N5ORM.js → sankeyDiagram-5OEKKPKP.BXohIHqx.js} +1 -1
  68. package/web/_astro/{sequenceDiagram-3UESZ5HK.E6_IV4Dj.js → sequenceDiagram-3UESZ5HK.C37ZIUzg.js} +1 -1
  69. package/web/_astro/{stateDiagram-AJRCARHV.Bnv6Ok2p.js → stateDiagram-AJRCARHV.BRgz317z.js} +1 -1
  70. package/web/_astro/{stateDiagram-v2-BHNVJYJU.mJHH_Ng_.js → stateDiagram-v2-BHNVJYJU.7VYSXN9-.js} +1 -1
  71. package/web/_astro/{timeline-definition-PNZ67QCA.BhRgqSLa.js → timeline-definition-PNZ67QCA.BVNz_HiN.js} +1 -1
  72. package/web/_astro/{vennDiagram-CIIHVFJN.pjb5DMC9.js → vennDiagram-CIIHVFJN.CHVDkPX4.js} +1 -1
  73. package/web/_astro/{wardleyDiagram-YWT4CUSO.CW20KUng.js → wardleyDiagram-YWT4CUSO.EQQ_qT9v.js} +1 -1
  74. package/web/_astro/{xychartDiagram-2RQKCTM6.vX9_yuMl.js → xychartDiagram-2RQKCTM6.DrAT9WoP.js} +1 -1
  75. package/web/index.html +2 -2
  76. package/web/_astro/BoardApp.ymfj1EHA.js +0 -1
  77. package/web/_astro/channel.Bxxe2Byc.js +0 -1
  78. package/web/_astro/index.BhvM3djt.css +0 -1
@@ -29,7 +29,7 @@ kind: state-machine
29
29
  name: task-pipeline
30
30
  # 0772 R2: explicit identity tag. Set only after the proof failure-path tests
31
31
  # passed; the engine treats unversioned external definitions as supported.
32
- version: "1"
32
+ version: "3"
33
33
  description: "Standard task pipeline: precheck → implement → quality-gate (test/fix/recheck) → review → approve → verify → record → done"
34
34
  # Task entity lifecycle (owned by task-lifecycle.yaml):
35
35
  # normal: backlog → todo → wip → testing → done
@@ -105,6 +105,10 @@ vars:
105
105
  proofDigest: ""
106
106
  proofDigestNow: ""
107
107
  taskSpecPath: ""
108
+ # 0785 R2: linked feature spec path, resolved at `test` entry from the task frontmatter.
109
+ # Empty (orphan task) means the feature spec is legitimately omitted from the digest inputs
110
+ # (empty-string compatibility); a declared feature whose path fails to resolve fails closed.
111
+ featureSpecPath: ""
108
112
  # 0710 R4: task priority tier (P0..P4) extracted from the task frontmatter at the
109
113
  # quality-gate stage. P0/P1 make the review/verify distinct-executor policy apply;
110
114
  # unknown/empty priority means fresh-context-only (executor reuse allowed).
@@ -394,6 +398,17 @@ states:
394
398
  options:
395
399
  path: .spur/run/${vars.wbs}-priority.txt
396
400
  var: taskPriority
401
+ # 0785 R2: resolve the task's linked feature spec path so the digest covers it too. An
402
+ # orphan task (no feature_id) resolves to empty — legitimately omitted via empty-string
403
+ # compatibility; a declared feature whose spec path does not resolve fails closed instead
404
+ # of silently folding a stale spec into the proof.
405
+ - kind: shell
406
+ options:
407
+ command: 'mkdir -p .spur/run; FID=$($spurBin task show $wbs --json 2>/dev/null | jq -r ".feature_id // .frontmatter.feature_id // empty" 2>/dev/null); FPATH_OUT=""; if [ -n "$FID" ]; then FPATH_OUT=$($spurBin feature show "$FID" --json | jq -r ".filePath // .path // empty"); if [ -z "$FPATH_OUT" ]; then echo "fail-closed proof chain (0785 R2): feature $FID linked to task $wbs but its spec path did not resolve" >&2; exit 1; fi; fi; printf "%s" "$FPATH_OUT" > ".spur/run/$wbs-featurepath.txt"'
408
+ - kind: file.read.into-var
409
+ options:
410
+ path: .spur/run/${vars.wbs}-featurepath.txt
411
+ var: featureSpecPath
397
412
  # R2 (0703, ADR-071): THE canonical proof capture. Placement is load-bearing: immediately before
398
413
  # the final evidence chain, after every implement mutation (including the post-implement format).
399
414
  # Capture-only here; `record` compares. A remediation pass re-captures at `test-recheck` (R4).
@@ -401,6 +416,7 @@ states:
401
416
  options:
402
417
  var: proofDigest
403
418
  taskFile: ${vars.taskSpecPath}
419
+ featureFile: ${vars.featureSpecPath}
404
420
  - kind: shell
405
421
  options:
406
422
  command: >-
@@ -503,6 +519,7 @@ states:
503
519
  options:
504
520
  var: proofDigest
505
521
  taskFile: ${vars.taskSpecPath}
522
+ featureFile: ${vars.featureSpecPath}
506
523
  # 0587 R3: probe-then-full recheck. A red gateProbeCmd records FAIL and skips the full
507
524
  # gate (the measured waste is re-running a 110–140s gate to learn the tree is still red);
508
525
  # a green probe (or empty gateProbeCmd) falls through to the full-gate loop unchanged.
@@ -573,6 +590,14 @@ states:
573
590
  priority: ${vars.taskPriority}
574
591
  compareExecutorWith: implement
575
592
  timeoutMs: ${vars.stepTimeoutMs}
593
+ # 0785 R4: the reviewer's completion evidence for THIS run — the current proof digest is
594
+ # written to a run-scoped marker by the review stage itself, not caller-stamped. The
595
+ # verify stamp marks review `completed` only when this marker equals the current digest;
596
+ # otherwise `skipped` (an honest downgrade, never a fake completed), and the bound
597
+ # `record` registration refuses a skipped review.
598
+ - kind: shell
599
+ options:
600
+ command: 'mkdir -p .spur/run; printf "%s" "$proofDigest" > ".spur/run/$__runId-review-proof.digest"; exit 0'
576
601
 
577
602
  - id: approve
578
603
  description: >
@@ -604,11 +629,12 @@ states:
604
629
  onEnter:
605
630
  # R2 (0703): midpoint bracket compare — refuse to spend verification on a state that no
606
631
  # longer matches the digest the quality/review evidence names. Reuses `proofDigestNow`:
607
- # set here and re-set by the final compare at `record` entry.
632
+ # set here and re-set by the bound `run.artifact` registration at `record` entry (0785 R3).
608
633
  - kind: proof.fingerprint
609
634
  options:
610
635
  var: proofDigestNow
611
636
  taskFile: ${vars.taskSpecPath}
637
+ featureFile: ${vars.featureSpecPath}
612
638
  expect: ${vars.proofDigest}
613
639
  - kind: agent.run
614
640
  options:
@@ -654,21 +680,32 @@ states:
654
680
  # workflow definition it executed — a stale-definition resume or a definition edited
655
681
  # between run and record cannot certify silently. `__definitionDigest` is injected at run
656
682
  # start (workflow-service.ts) and equals the digest stamped on the run row (task 0603).
683
+ # 0785 R4: review is stamped `completed` only when the run-scoped marker written by the
684
+ # review stage equals the current digest; otherwise `skipped` — an unexecuted review is
685
+ # never reported completed, and the verify→record guard plus the bound record registration
686
+ # refuse a skipped review (the D9 fast route stays dormant, honestly).
657
687
  # Also keeps the flat
658
688
  # `proof-input-digest` check row for consumers that read `checks[]`. Soft action + hard
659
689
  # guard: a missing/malformed stamp fails the `verify → record` guard below, not this step.
660
690
  - kind: shell
661
691
  options:
692
+ # One folded line: every content line sits at the block's base indentation so
693
+ # `>-` joins them with spaces. A more-indented continuation would preserve a
694
+ # literal newline and split the jq invocation from its program argument.
662
695
  command: >-
663
696
  V=".spur/run/$wbs-verdict.json";
697
+ RV="skipped";
698
+ if [ -f ".spur/run/$__runId-review-proof.digest" ] && [ "$(cat ".spur/run/$__runId-review-proof.digest")" = "$proofDigest" ]; then
699
+ RV="completed";
700
+ fi;
664
701
  if [ -f "$V" ] && [ -n "$proofDigest" ]; then
665
- jq --arg d "$proofDigest" --arg r "$__runId" --arg dd "$__definitionDigest" --arg g "$(cat .spur/run/$wbs-test-gate.status 2>/dev/null || echo UNKNOWN)"
666
- '. + {proof: {digest: $d, runId: $r, definitionDigest: $dd, capturePoint: "quality-gate-entry", stages: {
667
- qualityGate: {status: $g, digest: $d},
668
- review: {status: "completed", digest: $d},
669
- verification: {status: .verdict, digest: $d}}}}
670
- | .checks += [{name: "proof-input-digest", status: "pass", evidence: $d}]'
671
- "$V" > "$V.tmp" && mv "$V.tmp" "$V";
702
+ jq --arg d "$proofDigest" --arg r "$__runId" --arg dd "$__definitionDigest" --arg rv "$RV" --arg g "$(cat .spur/run/$wbs-test-gate.status 2>/dev/null || echo UNKNOWN)"
703
+ '. + {proof: {digest: $d, runId: $r, definitionDigest: $dd, capturePoint: "quality-gate-entry", stages: {
704
+ qualityGate: {status: $g, digest: $d},
705
+ review: {status: $rv, digest: $d},
706
+ verification: {status: .verdict, digest: $d}}}}
707
+ | .checks += [{name: "proof-input-digest", status: "pass", evidence: $d}]'
708
+ "$V" > "$V.tmp" && mv "$V.tmp" "$V";
672
709
  fi;
673
710
  exit 0
674
711
 
@@ -676,25 +713,33 @@ states:
676
713
  description: >
677
714
  Record pipeline results into the task file via `spur task record` —
678
715
  Testing/Review from the verdict, Solution backfilled from git diff as a
679
- safety net, optional transition to testing. Post-record step conditionally syncs
680
- feature status via the bounded `feature-sync-bounded` wrapper (task 0411
681
- retry-suppression) if `feature_id` is present, or appends an orphan link proposal
682
- to the run report if absent (task 0328 / ADR-0322).
716
+ safety net, optional transition to testing. Entry is gated by the bound
717
+ `run.artifact` registration (0785 R3): the verify verdict is ledger-registered
718
+ against a freshly captured proof digest including review-completion marker
719
+ evidence (0785 R4) before any task record or status mutation. Post-record step
720
+ conditionally syncs feature status via the bounded `feature-sync-bounded` wrapper
721
+ (task 0411 retry-suppression) if `feature_id` is present, or appends an orphan
722
+ link proposal to the run report if absent (task 0328 / ADR-0322).
683
723
  onEnter:
684
- # Proof-state compare (task 0612, ADR-071; bracket closed against the pre-chain
685
- # capture per task 0703) FIRST action in the state, before any record write.
686
- # Re-captures the digest and asserts it equals the value the evidence chain started
687
- # from. A mismatch means a proof input changed after evidence was established; the
688
- # default `fail` policy halts the sequence and routes the run to `failed` rather
689
- # than crossing into `record`. Task-spec evidence writes (Testing/Review/Solution,
690
- # R6) happen only in the actions AFTER this comparison, and the fingerprint scopes
691
- # task content to the proof-input sections, so they cannot invalidate the certified
692
- # input set.
693
- - kind: proof.fingerprint
724
+ # 0785 R3: BOUND verify-verdict registration FIRST action, before any task record or
725
+ # status mutation, replacing the redundant proof.fingerprint compare (the action itself
726
+ # freshly re-captures the proof inputs via its processExecutor seam). The action refuses
727
+ # before the ledger write AND before `task record` unless: the run's declared digest
728
+ # equals a fresh capture over the canonical task spec (+ linked feature when linked);
729
+ # the verdict artifact is canonical-valid PASS for this wbs; its raw proof block names
730
+ # the fresh digest, the certifying run id, and the run row's resume/definition digest;
731
+ # qualityGate and verification stages are PASS with that digest; review is completed and
732
+ # independently evidenced by the run-scoped marker `<runId>-review-proof.digest`
733
+ # (0785 R4). Task-spec evidence writes (Testing/Review/Solution, R6) happen only in the
734
+ # actions AFTER this registration, and the fingerprint scopes task content to the
735
+ # proof-input sections, so they cannot invalidate the certified input set.
736
+ - kind: run.artifact
694
737
  options:
695
- var: proofDigestNow
738
+ path: .spur/run/${vars.wbs}-verdict.json
739
+ artifactKind: verify-verdict
740
+ proofBinding: current
696
741
  taskFile: ${vars.taskSpecPath}
697
- expect: ${vars.proofDigest}
742
+ featureFile: ${vars.featureSpecPath}
698
743
  - kind: shell
699
744
  options:
700
745
  command: >-
@@ -759,23 +804,13 @@ states:
759
804
  return "$RETRY_RC";
760
805
  };
761
806
  retry_transient $spurBin task update "$wbs" done --no-lifecycle
762
- # D5-L: the verify verdict is this pipeline's proof of completion, so its ownership
763
- # belongs to a deterministic primitive rather than an untracked file on disk.
764
- # `run.artifact` records path + kind + runId only — never the body (ADR-069).
765
- - kind: run.artifact
766
- options:
767
- path: .spur/run/${vars.wbs}-verdict.json
768
- artifactKind: verify-verdict
769
- # 0751 R4: bind the verdict to the run's captured proof digest. `record`
770
- # re-captures `proofDigestNow` with expect=proofDigest, so the binding
771
- # holds by construction here — making the option non-decorative.
772
- proofBinding: current
773
807
  - kind: note
774
808
  options:
775
809
  message: "Pipeline complete for task ${vars.wbs} (done gate cleared)."
776
- # Checkpoint write: record session state for resume (0711 R1–R3) —
777
- # canonical frontmatter contract; mirrors the Session Checkpoint
778
- # Convention in plugins/sp/skills/spur-dev/references/cross-cutting.md.
810
+ # Checkpoint write: record session state for resume (0711 R1–R3;
811
+ # run-id/artifact binding corrected by 0784 R4) — canonical frontmatter
812
+ # contract; mirrors the Session Checkpoint Convention in
813
+ # plugins/sp/skills/spur-dev/references/cross-cutting.md.
779
814
  # Advisory only: the task file and the persisted run row stay authoritative.
780
815
  - kind: shell
781
816
  options:
@@ -783,7 +818,7 @@ states:
783
818
  # command whose continuation lines look like argument lists (heredocs with
784
819
  # `- item` entries trip it), so the checkpoint body is one printf with \n escapes.
785
820
  command: >-
786
- mkdir -p .spur/memory/sessions; CP_TS="$(date -u +%Y-%m-%dT%H:%M:%SZ)"; CP_COMMIT="$(git rev-parse HEAD 2>/dev/null || echo unknown)"; CP_DIGEST="$(cut -d= -f2 .spur/run/$wbs-proofdigest.txt 2>/dev/null || echo '')"; CP_RUN="$SPUR_RUN_ID"; [ -z "$CP_RUN" ] && CP_RUN="$RUN_ID"; printf '%s\n' '---' 'schema_version: 1' "session_id: $(date -u +%Y-%m-%d)-$wbs" 'workflow: task-pipeline' "run_id: $CP_RUN" "task_wbs: $wbs" 'feature_id: ""' 'phase: done' 'status: done' 'last_gate: record' "source_commit: $CP_COMMIT" "digest: $CP_DIGEST" "generated_at: $CP_TS" "updated_at: $CP_TS" "next_action: none - task $wbs complete (terminal; advisory only)" 'artifacts:' ' - .spur/run/$wbs-verdict.json' ' - .spur/run/$wbs-test-gate.log' '---' '' '## Session Notes' '' "Terminal checkpoint for task $wbs (task-pipeline done)." 'Advisory only; the task file is authoritative.' > .spur/memory/sessions/$wbs-checkpoint.md; exit 0
821
+ mkdir -p .spur/memory/sessions; CP_TS="$(date -u +%Y-%m-%dT%H:%M:%SZ)"; CP_COMMIT="$(git rev-parse HEAD 2>/dev/null || echo unknown)"; CP_DIGEST="$proofDigest"; CP_RUN="$__runId"; printf '%s\n' '---' 'schema_version: 1' "session_id: $(date -u +%Y-%m-%d)-$wbs" 'workflow: task-pipeline' "run_id: $CP_RUN" "task_wbs: $wbs" 'feature_id: ""' 'phase: done' 'status: done' 'last_gate: record' "source_commit: $CP_COMMIT" "digest: $CP_DIGEST" "generated_at: $CP_TS" "updated_at: $CP_TS" "next_action: none - task $wbs complete (terminal; advisory only)" 'artifacts:' " - .spur/run/$wbs-verdict.json" " - .spur/run/$wbs-test-gate.log" '---' '' '## Session Notes' '' "Terminal checkpoint for task $wbs (task-pipeline done)." 'Advisory only; the task file is authoritative.' > .spur/memory/sessions/$wbs-checkpoint.md; exit 0
787
822
 
788
823
  - id: failed
789
824
  description: >
@@ -946,6 +981,7 @@ transitions:
946
981
  test "$(jq -r '.proof.digest // ""' "$V" 2>/dev/null)" = "$proofDigest" &&
947
982
  test "$(jq -r '.proof.stages.qualityGate.digest // ""' "$V" 2>/dev/null)" = "$proofDigest" &&
948
983
  test "$(jq -r '.proof.stages.review.digest // ""' "$V" 2>/dev/null)" = "$proofDigest" &&
984
+ test "$(jq -r '.proof.stages.review.status // ""' "$V" 2>/dev/null)" = "completed" &&
949
985
  test "$(jq -r '.proof.stages.verification.digest // ""' "$V" 2>/dev/null)" = "$proofDigest" &&
950
986
  test "$(jq -r '.proof.runId // ""' "$V" 2>/dev/null)" = "$__runId" &&
951
987
  test "$(jq -r '.proof.definitionDigest // ""' "$V" 2>/dev/null)" = "$__definitionDigest"