@gobing-ai/spur 0.3.99 → 0.4.1

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 (172) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.global.yaml +17 -0
  3. package/config/decisions/gates.yaml +36 -0
  4. package/config/decisions/history-anatomy.yaml +35 -0
  5. package/config/decisions/idea-pipeline.yaml +38 -0
  6. package/config/decisions/task-pipeline.yaml +55 -0
  7. package/config/plugin-scripts.json +25 -0
  8. package/config/rules/boundary/sp-runtime-path.yaml +2 -0
  9. package/config/rules/docs/sourced-api-claims.yaml +60 -0
  10. package/config/rules/quality/coverage-gate.yaml +5 -3
  11. package/config/rules/recommended-pre-check.yaml +8 -3
  12. package/config/rules/strict/runtime-boundaries.yaml +11 -0
  13. package/config/rules/structure/protected-files.yaml +3 -0
  14. package/config/rules/structure/scratch-evidence-pointer.yaml +35 -0
  15. package/config/script-placement-baseline.json +2 -2
  16. package/config/workflows/feature-lifecycle.yaml +9 -2
  17. package/config/workflows/history-anatomy.yaml +22 -0
  18. package/config/workflows/idea-pipeline.yaml +75 -0
  19. package/config/workflows/task-pipeline.yaml +368 -49
  20. package/config/workflows/wrapup-pipeline.yaml +106 -13
  21. package/package.json +9 -9
  22. package/plugins/sp/README.md +25 -2
  23. package/plugins/sp/agents/super-planner.md +11 -0
  24. package/plugins/sp/agents/super-reviewer.md +39 -11
  25. package/plugins/sp/bunfig.toml +8 -0
  26. package/plugins/sp/commands/dev-brainstorm.md +29 -16
  27. package/plugins/sp/commands/dev-changelog.md +5 -4
  28. package/plugins/sp/commands/dev-find-way.md +80 -0
  29. package/plugins/sp/commands/dev-idea.md +6 -0
  30. package/plugins/sp/commands/dev-parallel.md +8 -2
  31. package/plugins/sp/commands/dev-plan.md +6 -0
  32. package/plugins/sp/commands/dev-review-session.md +14 -7
  33. package/plugins/sp/commands/dev-run.md +17 -4
  34. package/plugins/sp/commands/dev-runall.md +21 -10
  35. package/plugins/sp/hooks/agent-lifecycle.ts +155 -0
  36. package/plugins/sp/hooks/context-session-start.ts +1 -1
  37. package/plugins/sp/hooks/fleet-guest-stop.ts +180 -0
  38. package/plugins/sp/hooks/hooks.json +45 -1
  39. package/plugins/sp/hooks/pi/guard-extension.ts +21 -0
  40. package/plugins/sp/lib/history-anatomy.generated.mjs +5 -0
  41. package/plugins/sp/lib/idea-handoff.generated.mjs +72 -11
  42. package/plugins/sp/lib/inline-run-app.ts +28 -0
  43. package/plugins/sp/lib/inline-run.generated.d.mts +6 -0
  44. package/plugins/sp/lib/inline-run.generated.mjs +364 -46
  45. package/plugins/sp/lib/plan-projection.generated.d.mts +28 -0
  46. package/plugins/sp/lib/plan-projection.generated.mjs +255 -0
  47. package/plugins/sp/lib/quality-gate.generated.d.mts +47 -1
  48. package/plugins/sp/lib/quality-gate.generated.mjs +30489 -82
  49. package/plugins/sp/lib/residual-scan.generated.d.mts +19 -0
  50. package/plugins/sp/lib/residual-scan.generated.mjs +66 -0
  51. package/plugins/sp/lib/run-summary-core.ts +561 -0
  52. package/plugins/sp/lib/transcript.ts +275 -0
  53. package/plugins/sp/plugin.json +1 -1
  54. package/plugins/sp/references/roles.md +1 -1
  55. package/plugins/sp/scripts/batch-plan.mjs +335 -0
  56. package/plugins/sp/scripts/batch-plan.ts +100 -0
  57. package/plugins/sp/scripts/batch-preflight.mjs +51 -3
  58. package/plugins/sp/scripts/batch-preflight.ts +72 -1
  59. package/plugins/sp/scripts/commit-guard.mjs +216 -0
  60. package/plugins/sp/scripts/commit-guard.ts +249 -0
  61. package/plugins/sp/scripts/history-anatomy-cache.mjs +5 -0
  62. package/plugins/sp/scripts/inline-run-dispatch.mjs +130 -0
  63. package/plugins/sp/scripts/inline-run-dispatch.ts +135 -0
  64. package/plugins/sp/scripts/inline-run-setup.mjs +563 -91
  65. package/plugins/sp/scripts/inline-run-setup.ts +65 -97
  66. package/plugins/sp/scripts/persist-out-check.mjs +90 -41
  67. package/plugins/sp/scripts/persist-out-check.ts +112 -57
  68. package/plugins/sp/scripts/quality-gate.mjs +30407 -73
  69. package/plugins/sp/scripts/quality-gate.ts +21 -5
  70. package/plugins/sp/scripts/residual-scan.mjs +88 -38
  71. package/plugins/sp/scripts/residual-scan.ts +35 -38
  72. package/plugins/sp/scripts/run-summary.mjs +693 -0
  73. package/plugins/sp/scripts/run-summary.ts +105 -0
  74. package/plugins/sp/scripts/session-timeline.mjs +331 -0
  75. package/plugins/sp/scripts/session-timeline.ts +204 -0
  76. package/plugins/sp/scripts/wrapup-steps.mjs +150 -2
  77. package/plugins/sp/scripts/wrapup-steps.ts +203 -1
  78. package/plugins/sp/skills/brainstorm/SKILL.md +9 -47
  79. package/plugins/sp/skills/branch-workflow/SKILL.md +1 -0
  80. package/plugins/sp/skills/branch-workflow/references/worktree-patterns.md +1 -0
  81. package/plugins/sp/skills/code-implementation/SKILL.md +48 -1
  82. package/plugins/sp/skills/code-verification/SKILL.md +8 -4
  83. package/plugins/sp/skills/code-verification/references/secu-review.md +3 -0
  84. package/plugins/sp/skills/code-verification/references/verdict-schema.md +68 -0
  85. package/plugins/sp/skills/fleet-join/SKILL.md +63 -0
  86. package/plugins/sp/skills/session-review/SKILL.md +77 -24
  87. package/plugins/sp/skills/source-driven-development/SKILL.md +5 -0
  88. package/plugins/sp/skills/spec-decomposition/references/decomposition.md +5 -3
  89. package/plugins/sp/skills/spur-cli/SKILL.md +6 -2
  90. package/plugins/sp/skills/spur-cli/references/agent.md +124 -17
  91. package/plugins/sp/skills/spur-cli/references/decision.md +98 -0
  92. package/plugins/sp/skills/spur-cli/references/message.md +7 -7
  93. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +41 -3
  94. package/plugins/sp/skills/spur-cli/references/tasks.md +10 -7
  95. package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +7 -0
  96. package/plugins/sp/skills/spur-cli/references/workflows.md +7 -2
  97. package/plugins/sp/skills/spur-composer/SKILL.md +28 -2
  98. package/plugins/sp/skills/spur-dev/SKILL.md +18 -14
  99. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +6 -3
  100. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +49 -14
  101. package/plugins/sp/skills/spur-dev/references/dev-operations.md +51 -18
  102. package/plugins/sp/skills/spur-dev/references/execution-batch-continuation.md +68 -0
  103. package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +194 -0
  104. package/plugins/sp/skills/spur-dev/references/execution-batch.md +113 -963
  105. package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +130 -0
  106. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +20 -0
  107. package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +471 -0
  108. package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +379 -0
  109. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +51 -9
  110. package/plugins/sp/skills/spur-dev/references/glossary.md +2 -2
  111. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +361 -156
  112. package/plugins/sp/skills/spur-dev/references/planning-workflow.md +6 -0
  113. package/plugins/sp/skills/spur-dev/references/structured-trace-emission.md +178 -0
  114. package/plugins/sp/skills/wayfinder/SKILL.md +10 -14
  115. package/schemas/decision-catalog.schema.json +226 -0
  116. package/schemas/spur-config.schema.json +25 -0
  117. package/schemas/state-machine-workflow.schema.json +23 -0
  118. package/spur.js +28165 -19051
  119. package/web/_astro/{BoardApp.i4eh_fHA.js → BoardApp.D66k_s_T.js} +1 -1
  120. package/web/_astro/BoardApp.D6sjAJr_.js +324 -0
  121. package/web/_astro/{TaskDetail.C5Cu8LdB.js → TaskDetail.BynLnMNc.js} +1 -1
  122. package/web/_astro/{arc.Bu5XpBEz.js → arc._BUadrdH.js} +1 -1
  123. package/web/_astro/{architectureDiagram-3BPJPVTR.9vHQTvhF.js → architectureDiagram-3BPJPVTR.Dh7xS1Pp.js} +1 -1
  124. package/web/_astro/{blockDiagram-GPEHLZMM.Cn2Xl7D6.js → blockDiagram-GPEHLZMM.DFDVpfXm.js} +1 -1
  125. package/web/_astro/{c4Diagram-AAUBKEIU.7o0P_QDo.js → c4Diagram-AAUBKEIU.V7_3_glh.js} +1 -1
  126. package/web/_astro/channel.DZVfto-r.js +1 -0
  127. package/web/_astro/{chunk-2J33WTMH.C3zZFsNt.js → chunk-2J33WTMH.De1yx7Jz.js} +1 -1
  128. package/web/_astro/{chunk-4BX2VUAB.CNy_M7fO.js → chunk-4BX2VUAB.BDhHFuxx.js} +1 -1
  129. package/web/_astro/{chunk-55IACEB6.2TiPnLBL.js → chunk-55IACEB6.DDLkWnmC.js} +1 -1
  130. package/web/_astro/{chunk-727SXJPM.5CGSyKAp.js → chunk-727SXJPM.CwfiLnA7.js} +1 -1
  131. package/web/_astro/{chunk-AQP2D5EJ.w-ZIgfsK.js → chunk-AQP2D5EJ.CtGDmUnM.js} +1 -1
  132. package/web/_astro/{chunk-FMBD7UC4.BkcEyhKa.js → chunk-FMBD7UC4.BOLjpw73.js} +1 -1
  133. package/web/_astro/{chunk-ND2GUHAM.mxKKbBG4.js → chunk-ND2GUHAM.BDhQls6O.js} +1 -1
  134. package/web/_astro/{chunk-QZHKN3VN.cyA0z9aT.js → chunk-QZHKN3VN.76qBynDF.js} +1 -1
  135. package/web/_astro/{classDiagram-4FO5ZUOK.CqtUzwhX.js → classDiagram-4FO5ZUOK.N0mpPHT0.js} +1 -1
  136. package/web/_astro/{classDiagram-v2-Q7XG4LA2.CqtUzwhX.js → classDiagram-v2-Q7XG4LA2.N0mpPHT0.js} +1 -1
  137. package/web/_astro/{cose-bilkent-S5V4N54A.BExPTopI.js → cose-bilkent-S5V4N54A.CKknFfjB.js} +1 -1
  138. package/web/_astro/{cynefin-OW5HDTMX.Bq2i10Cn.js → cynefin-OW5HDTMX.Cw3ZWBAh.js} +1 -1
  139. package/web/_astro/{dagre-BM42HDAG.By_HsOuv.js → dagre-BM42HDAG.D3yIKCRD.js} +1 -1
  140. package/web/_astro/{diagram-2AECGRRQ.BtU0b7hz.js → diagram-2AECGRRQ.D51dO83Z.js} +1 -1
  141. package/web/_astro/{diagram-5GNKFQAL.D00ztMdT.js → diagram-5GNKFQAL.Bb-FViDj.js} +1 -1
  142. package/web/_astro/{diagram-KO2AKTUF.DMVJWaUN.js → diagram-KO2AKTUF.D3mN8S6x.js} +1 -1
  143. package/web/_astro/{diagram-LMA3HP47.Cxqrjg5-.js → diagram-LMA3HP47.dMMyPcbJ.js} +1 -1
  144. package/web/_astro/{diagram-OG6HWLK6.2lz_1B5h.js → diagram-OG6HWLK6.AzbvzY4C.js} +1 -1
  145. package/web/_astro/{erDiagram-TEJ5UH35.BVQEuJ3T.js → erDiagram-TEJ5UH35.BR5I4OZb.js} +1 -1
  146. package/web/_astro/{flowDiagram-I6XJVG4X.Qlyo90ky.js → flowDiagram-I6XJVG4X.DFqvhGkn.js} +1 -1
  147. package/web/_astro/{ganttDiagram-6RSMTGT7.DRwb8HHZ.js → ganttDiagram-6RSMTGT7.ByJbhH6J.js} +1 -1
  148. package/web/_astro/{gitGraphDiagram-PVQCEYII.YfoPthfS.js → gitGraphDiagram-PVQCEYII.CInHjQR3.js} +1 -1
  149. package/web/_astro/index.COh_kBxN.css +1 -0
  150. package/web/_astro/{infoDiagram-5YYISTIA.DvFREck1.js → infoDiagram-5YYISTIA.Cm0dGVjJ.js} +1 -1
  151. package/web/_astro/{ishikawaDiagram-YF4QCWOH.BzCj9Q0-.js → ishikawaDiagram-YF4QCWOH.C3nOP6Sj.js} +1 -1
  152. package/web/_astro/{journeyDiagram-JHISSGLW.BDMT_AFb.js → journeyDiagram-JHISSGLW.4IvLa2MW.js} +1 -1
  153. package/web/_astro/{kanban-definition-UN3LZRKU.D6Fh3gj9.js → kanban-definition-UN3LZRKU.DS87Cl-k.js} +1 -1
  154. package/web/_astro/{linear.Dxw00fGQ.js → linear.MzmRrNER.js} +1 -1
  155. package/web/_astro/{mermaid.core.CsoxUWa0.js → mermaid.core.fK-WNswS.js} +4 -4
  156. package/web/_astro/{mindmap-definition-RKZ34NQL.CN8oeHLu.js → mindmap-definition-RKZ34NQL.BqipOHhY.js} +1 -1
  157. package/web/_astro/{pieDiagram-4H26LBE5.C7ADqQ_7.js → pieDiagram-4H26LBE5.0nBiPqFG.js} +1 -1
  158. package/web/_astro/{quadrantDiagram-W4KKPZXB.Cw9lKyHP.js → quadrantDiagram-W4KKPZXB.Szj7Zpx5.js} +1 -1
  159. package/web/_astro/{requirementDiagram-4Y6WPE33.B1ni-z4q.js → requirementDiagram-4Y6WPE33.D4UR6TWo.js} +1 -1
  160. package/web/_astro/{sankeyDiagram-5OEKKPKP.BhexdjVr.js → sankeyDiagram-5OEKKPKP.DAE7BdOs.js} +1 -1
  161. package/web/_astro/{sequenceDiagram-3UESZ5HK.C3Rm7dpQ.js → sequenceDiagram-3UESZ5HK.BP9ZVq25.js} +1 -1
  162. package/web/_astro/{stateDiagram-AJRCARHV.CkuMt27u.js → stateDiagram-AJRCARHV.BQW7mIxk.js} +1 -1
  163. package/web/_astro/{stateDiagram-v2-BHNVJYJU.Dc_aazUA.js → stateDiagram-v2-BHNVJYJU.PbK4nDih.js} +1 -1
  164. package/web/_astro/{timeline-definition-PNZ67QCA.ja7sUFhH.js → timeline-definition-PNZ67QCA.Ci_GvUpo.js} +1 -1
  165. package/web/_astro/{vennDiagram-CIIHVFJN.BxIshX_K.js → vennDiagram-CIIHVFJN.DNw1Y5qx.js} +1 -1
  166. package/web/_astro/{wardleyDiagram-YWT4CUSO.DfHlC0sh.js → wardleyDiagram-YWT4CUSO.CwlkaaNe.js} +1 -1
  167. package/web/_astro/{xychartDiagram-2RQKCTM6.DSnhHwCh.js → xychartDiagram-2RQKCTM6.MI6ZuiH_.js} +1 -1
  168. package/web/board-runtime.json +1 -1
  169. package/web/index.html +2 -2
  170. package/web/_astro/BoardApp.BVp-LYi6.js +0 -322
  171. package/web/_astro/channel.6IFIKBhu.js +0 -1
  172. package/web/_astro/index.CbNz17Sx.css +0 -1
@@ -0,0 +1,28 @@
1
+ /**
2
+ * GENERATED by scripts/commands/bundle-plugin-lib.ts — do not edit.
3
+ * Source of truth: packages/app/src/workflow/plan-projection.ts (task 1104).
4
+ */
5
+ export declare interface PhasedPlanItem {
6
+ label: string;
7
+ id: string;
8
+ outcome: 'pending' | 'active' | 'completed' | 'skipped' | 'failed' | 'blocked' | 'unattempted';
9
+ note?: string;
10
+ text: string;
11
+ parent?: string;
12
+ title?: string;
13
+ }
14
+ export declare interface BatchTask {
15
+ wbs: string;
16
+ name: string;
17
+ }
18
+ export declare function planLetter(index: number): string;
19
+ export declare function planChild(parent: string, index: number): string;
20
+ export declare function buildPhasedPlan(def: unknown): PhasedPlanItem[] | null;
21
+ export declare function validatePhaseTable(def: unknown): string[];
22
+ export declare function insertOnEntry(items: PhasedPlanItem[], stateId: string, def: unknown): PhasedPlanItem[];
23
+ export declare function buildBatchPlan(tasks: BatchTask[]): PhasedPlanItem[][];
24
+ export declare function taskPhaseChildren(taskLetter: string, plan: PhasedPlanItem[]): PhasedPlanItem[];
25
+ export declare function hostStatus(
26
+ outcome: 'pending' | 'active' | 'completed' | 'skipped' | 'failed' | 'blocked' | 'unattempted',
27
+ ): 'completed' | 'in_progress' | 'pending';
28
+ export declare function hostText(item: PhasedPlanItem): string;
@@ -0,0 +1,255 @@
1
+ // packages/app/src/workflow/plan-projection.ts
2
+ function planLetter(index) {
3
+ if (!Number.isInteger(index) || index < 0 || index > 25) {
4
+ throw new Error(`plan letter out of range: ${index} (expected 0..25)`);
5
+ }
6
+ return String.fromCharCode(65 + index);
7
+ }
8
+ function planChild(parent, index) {
9
+ if (!Number.isInteger(index) || index < 0 || index > 8) {
10
+ throw new Error(`plan child index out of range: ${index} (expected 0..8 under ${parent})`);
11
+ }
12
+ if (parent.length !== 1 || !/[A-Z]/.test(parent)) {
13
+ throw new Error(`plan child parent must be a single letter: ${parent}`);
14
+ }
15
+ return `${parent}${index + 1}`;
16
+ }
17
+ var PREPARE_ID = "prepare";
18
+ var REPORT_ID = "report";
19
+ var PHASE_ID_PREFIX = "phase.";
20
+ var TASK_ID_PREFIX = "task.";
21
+ var WAVE_SIZE = 24;
22
+ var TITLE_CAP = 60;
23
+ function isStateMachine(def) {
24
+ return def.kind !== "transition-flow" && def.kind !== "dag";
25
+ }
26
+ function hasDisplay(s) {
27
+ return s.display !== undefined;
28
+ }
29
+ function resolvePhaseTitle(states, phase) {
30
+ for (const s of states) {
31
+ if (s.display?.phase === phase && s.display.phaseTitle !== undefined)
32
+ return s.display.phaseTitle;
33
+ }
34
+ return phase;
35
+ }
36
+ function childText(label, title, id) {
37
+ return id.startsWith("prepare.") || id.startsWith("task.") || id === PREPARE_ID || id === REPORT_ID ? `${label} ${title}` : `${label} ${title} · ${id}`;
38
+ }
39
+ function buildPhasedPlan(def) {
40
+ if (!isStateMachine(def))
41
+ return null;
42
+ const annotated = def.states.filter(hasDisplay);
43
+ if (annotated.length === 0)
44
+ return null;
45
+ const items = [
46
+ { label: "A", id: PREPARE_ID, outcome: "pending", title: "Prepare", text: "A Prepare" },
47
+ {
48
+ label: "A1",
49
+ id: "prepare.1",
50
+ outcome: "pending",
51
+ parent: "A",
52
+ title: "Quick readiness",
53
+ text: "A1 Quick readiness"
54
+ },
55
+ { label: "A2", id: "prepare.2", outcome: "pending", parent: "A", title: "Prepare Git", text: "A2 Prepare Git" },
56
+ {
57
+ label: "A3",
58
+ id: "prepare.3",
59
+ outcome: "pending",
60
+ parent: "A",
61
+ title: "Publish plan",
62
+ text: "A3 Publish plan"
63
+ }
64
+ ];
65
+ const phases = [];
66
+ for (const s of def.states) {
67
+ if (s.display !== undefined && !phases.includes(s.display.phase))
68
+ phases.push(s.display.phase);
69
+ }
70
+ for (const [phaseIndex, phase] of phases.entries()) {
71
+ const letter = planLetter(phaseIndex + 1);
72
+ const phaseTitle = resolvePhaseTitle(def.states, phase);
73
+ items.push({
74
+ label: letter,
75
+ id: `${PHASE_ID_PREFIX}${phase}`,
76
+ outcome: "pending",
77
+ title: phaseTitle,
78
+ text: `${letter} ${phaseTitle}`
79
+ });
80
+ let childIndex = 0;
81
+ for (const s of def.states) {
82
+ if (s.display?.phase !== phase || s.display.show === "on-entry")
83
+ continue;
84
+ const label = planChild(letter, childIndex++);
85
+ const title = s.display.title ?? s.id;
86
+ items.push({
87
+ label,
88
+ id: s.id,
89
+ outcome: "pending",
90
+ parent: letter,
91
+ title,
92
+ text: childText(label, title, s.id)
93
+ });
94
+ }
95
+ }
96
+ return items;
97
+ }
98
+ function validatePhaseTable(def) {
99
+ if (!isStateMachine(def))
100
+ return [];
101
+ const annotated = def.states.filter(hasDisplay);
102
+ if (annotated.length === 0)
103
+ return [];
104
+ const errors = [];
105
+ const terminals = new Set(def.terminalStates ?? []);
106
+ for (const s of def.states) {
107
+ if (s.display === undefined && !terminals.has(s.id)) {
108
+ errors.push(`state "${s.id}" has no display phase — every non-terminal state must declare display when any state does`);
109
+ }
110
+ if (s.display !== undefined && terminals.has(s.id)) {
111
+ errors.push(`terminal state "${s.id}" must not declare display`);
112
+ }
113
+ }
114
+ const phases = [];
115
+ for (const s of annotated) {
116
+ if (!phases.includes(s.display.phase))
117
+ phases.push(s.display.phase);
118
+ }
119
+ if (phases.length > 25) {
120
+ errors.push(`${phases.length} phases exceed the 25 plan letters (B-Z); split the workflow or drop phase rows`);
121
+ }
122
+ for (const phase of phases) {
123
+ const members = annotated.filter((s) => s.display.phase === phase);
124
+ if (members.length > 9) {
125
+ errors.push(`phase "${phase}" has ${members.length} states — at most 9 digit children fit under one letter`);
126
+ }
127
+ const titles = [...new Set(members.map((s) => s.display.phaseTitle).filter((t) => t !== undefined))];
128
+ if (titles.length > 1) {
129
+ errors.push(`phase "${phase}" has conflicting phaseTitle values: ${titles.map((t) => `"${t}"`).join(", ")}`);
130
+ }
131
+ }
132
+ return errors;
133
+ }
134
+ function insertOnEntry(items, stateId, def) {
135
+ if (!isStateMachine(def))
136
+ return items;
137
+ if (items.some((i) => i.id === stateId))
138
+ return items;
139
+ const display = def.states.find((s) => s.id === stateId)?.display;
140
+ if (display === undefined || (def.terminalStates ?? []).includes(stateId))
141
+ return items;
142
+ const phaseRow = items.find((i) => i.id === `${PHASE_ID_PREFIX}${display.phase}`);
143
+ if (phaseRow === undefined)
144
+ return items;
145
+ const children = items.filter((i) => i.parent === phaseRow.label);
146
+ const label = planChild(phaseRow.label, children.length);
147
+ const title = display.title ?? stateId;
148
+ const item = {
149
+ label,
150
+ id: stateId,
151
+ outcome: "pending",
152
+ parent: phaseRow.label,
153
+ title,
154
+ text: childText(label, title, stateId)
155
+ };
156
+ const last = children.at(-1) ?? phaseRow;
157
+ const at = items.indexOf(last) + 1;
158
+ return [...items.slice(0, at), item, ...items.slice(at)];
159
+ }
160
+ function truncateTitle(name) {
161
+ if (name.length <= TITLE_CAP)
162
+ return name;
163
+ return `${name.slice(0, TITLE_CAP - 1).replace(/[\uD800-\uDBFF]$/, "")}…`;
164
+ }
165
+ function buildBatchPlan(tasks) {
166
+ const waves = [];
167
+ for (let offset = 0;offset < tasks.length; offset += WAVE_SIZE) {
168
+ const items = [
169
+ { label: "A", id: PREPARE_ID, outcome: "pending", title: "Prepare batch", text: "A Prepare batch" },
170
+ {
171
+ label: "A1",
172
+ id: "prepare.1",
173
+ outcome: "pending",
174
+ parent: "A",
175
+ title: "Resolve and freeze task set",
176
+ text: "A1 Resolve and freeze task set"
177
+ },
178
+ {
179
+ label: "A2",
180
+ id: "prepare.2",
181
+ outcome: "pending",
182
+ parent: "A",
183
+ title: "Order by dependencies",
184
+ text: "A2 Order by dependencies"
185
+ },
186
+ {
187
+ label: "A3",
188
+ id: "prepare.3",
189
+ outcome: "pending",
190
+ parent: "A",
191
+ title: "Prepare Git",
192
+ text: "A3 Prepare Git"
193
+ },
194
+ {
195
+ label: "A4",
196
+ id: "prepare.4",
197
+ outcome: "pending",
198
+ parent: "A",
199
+ title: "Publish plan",
200
+ text: "A4 Publish plan"
201
+ }
202
+ ];
203
+ tasks.slice(offset, offset + WAVE_SIZE).forEach((task, i) => {
204
+ const label = planLetter(i + 1);
205
+ const title = `${task.wbs} ${truncateTitle(task.name)}`;
206
+ items.push({
207
+ label,
208
+ id: `${TASK_ID_PREFIX}${task.wbs}`,
209
+ outcome: "pending",
210
+ title,
211
+ text: `${label} ${title}`
212
+ });
213
+ });
214
+ items.push({ label: "Z", id: REPORT_ID, outcome: "pending", title: "Batch report", text: "Z Batch report" });
215
+ waves.push(items);
216
+ }
217
+ return waves;
218
+ }
219
+ function taskPhaseChildren(taskLetter, plan) {
220
+ if (!Array.isArray(plan))
221
+ return [];
222
+ return plan.filter((i) => i.id.startsWith(PHASE_ID_PREFIX) && i.label.length === 1).map((row, i) => {
223
+ const label = planChild(taskLetter, i);
224
+ return {
225
+ label,
226
+ id: row.id,
227
+ outcome: row.outcome,
228
+ parent: taskLetter,
229
+ title: row.title,
230
+ text: `${label} ${row.title}`
231
+ };
232
+ });
233
+ }
234
+ function hostStatus(outcome) {
235
+ if (outcome === "completed")
236
+ return "completed";
237
+ if (outcome === "active")
238
+ return "in_progress";
239
+ return "pending";
240
+ }
241
+ function hostText(item) {
242
+ const hostDecided = item.outcome === "skipped" || item.outcome === "failed" || item.outcome === "unattempted" || item.outcome === "blocked";
243
+ return hostDecided ? `${item.text} [${item.outcome}]` : item.text;
244
+ }
245
+ export {
246
+ validatePhaseTable,
247
+ taskPhaseChildren,
248
+ planLetter,
249
+ planChild,
250
+ insertOnEntry,
251
+ hostText,
252
+ hostStatus,
253
+ buildPhasedPlan,
254
+ buildBatchPlan
255
+ };
@@ -10,6 +10,18 @@ export declare const LOCKED_PATTERN: RegExp;
10
10
  export declare const FINDINGS_PATTERN: RegExp;
11
11
  export declare const COVERAGE_ROW_PATTERN: RegExp;
12
12
  export declare const RECEIPT_SCHEMA_VERSION: 'check-receipt/v1';
13
+ export declare const GATE_LOCK_DIR_ENV: string;
14
+ export declare const GATE_LOCK_POLL_MS_ENV: string;
15
+ export declare const GATE_LOCK_OFF_ENV: string;
16
+ export declare const GATE_LOCK_TOKEN_ENV: string;
17
+ export declare const GATE_LOCK_POLL_MS_DEFAULT: number;
18
+ export declare const GATE_LOCK_MKDIR_HOLD_MS_ENV: string;
19
+ export declare interface GateLock {
20
+ release(): void;
21
+ queueWaitMs: number;
22
+ token: string;
23
+ }
24
+ export declare function acquireGateLock(env: QualityGateEnv, logLine?: (line: string) => void): GateLock;
13
25
  export declare interface QualityGateEnv {
14
26
  wbs: string;
15
27
  qualityGateCmd?: string;
@@ -20,6 +32,10 @@ export declare interface QualityGateEnv {
20
32
  }
21
33
  export declare interface QualityGateOptions {
22
34
  cwd?: string;
35
+ recomputeFingerprint?: (
36
+ cwd: string,
37
+ env: QualityGateEnv,
38
+ ) => { ok: true; digest: string } | { ok: false; error: string };
23
39
  }
24
40
  export declare interface QualityGateResult {
25
41
  status: 'PASS' | 'FAIL';
@@ -75,12 +91,32 @@ export declare interface BuildReceiptInput {
75
91
  export declare function buildReceipt(input: BuildReceiptInput): CheckReceipt;
76
92
  export declare function receiptFailsAtDigest(receipt: CheckReceipt | null, currentDigest: string): boolean;
77
93
  export declare function readReceiptStatus(receiptPath: string, currentDigest: string): ReceiptReadStatus;
94
+ export declare interface ReceiptReuseDecision {
95
+ reuse: boolean;
96
+ receiptStatus: ReceiptReadStatus;
97
+ refusal?: string;
98
+ recomputedDigest?: string;
99
+ }
100
+ export declare function resolveReceiptReuse(
101
+ receiptPath: string,
102
+ env: QualityGateEnv,
103
+ cwd: string,
104
+ recomputeFn?: (
105
+ cwd: string,
106
+ env: QualityGateEnv,
107
+ ) => { ok: true; digest: string } | { ok: false; error: string },
108
+ ): ReceiptReuseDecision;
109
+ export declare function recomputeGateProofFingerprint(
110
+ cwd: string,
111
+ env: QualityGateEnv,
112
+ ): { ok: true; digest: string } | { ok: false; error: string };
78
113
  export declare interface LightScope {
79
114
  files: string[];
80
115
  workspaces: string[];
81
116
  tests: string[];
82
117
  }
83
118
  export declare function lightScope(changedFiles: string[], exists?: (p: string) => boolean): LightScope;
119
+ export declare const scopeFromFiles: typeof lightScope;
84
120
  export declare function shQuote(arg: string): string;
85
121
  export declare interface LightCheckPlan {
86
122
  id: string;
@@ -96,7 +132,17 @@ export declare interface LightGateResult {
96
132
  receiptFile: string;
97
133
  }
98
134
  export declare function runLightGate(env: QualityGateEnv, options?: QualityGateOptions): LightGateResult;
99
- export declare function runShellCommand(cmd: string, cwd: string | undefined): { output: string; code: number };
135
+ export declare interface DeferredGateResult {
136
+ status: 'DEFERRED' | 'FAIL';
137
+ light: LightGateResult;
138
+ statusFile: string;
139
+ }
140
+ export declare function runDeferredGate(env: QualityGateEnv, options?: QualityGateOptions): DeferredGateResult;
141
+ export declare function runShellCommand(
142
+ cmd: string,
143
+ cwd: string | undefined,
144
+ extraEnv?: Record<string, string>,
145
+ ): { output: string; code: number };
100
146
  export declare function runQualityGate(
101
147
  mode: 'run' | 'recheck',
102
148
  env: QualityGateEnv,