ex-workflow 0.2.0-stage → 0.3.0-stage

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 (67) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/README.md +39 -10
  3. package/dist/chunk-23P7C25E.cjs +2 -0
  4. package/dist/chunk-23P7C25E.cjs.map +1 -0
  5. package/dist/chunk-2IKIBB73.cjs +2 -0
  6. package/dist/chunk-2IKIBB73.cjs.map +1 -0
  7. package/dist/{chunk-472WEDPJ.cjs → chunk-4A4TD4MH.cjs} +2 -2
  8. package/dist/{chunk-472WEDPJ.cjs.map → chunk-4A4TD4MH.cjs.map} +1 -1
  9. package/dist/{chunk-D4HNETUL.cjs → chunk-7HZXZF3L.cjs} +2 -2
  10. package/dist/{chunk-D4HNETUL.cjs.map → chunk-7HZXZF3L.cjs.map} +1 -1
  11. package/dist/chunk-AUZIQPFT.cjs +2 -0
  12. package/dist/chunk-AUZIQPFT.cjs.map +1 -0
  13. package/dist/chunk-BJEOTG6P.mjs +2 -0
  14. package/dist/chunk-BJEOTG6P.mjs.map +1 -0
  15. package/dist/chunk-FLCYD2QF.mjs +2 -0
  16. package/dist/chunk-FLCYD2QF.mjs.map +1 -0
  17. package/dist/{chunk-DBR24RTX.cjs → chunk-NN3XJUHP.cjs} +2 -2
  18. package/dist/{chunk-DBR24RTX.cjs.map → chunk-NN3XJUHP.cjs.map} +1 -1
  19. package/dist/chunk-P4P4JTE5.mjs +2 -0
  20. package/dist/chunk-P4P4JTE5.mjs.map +1 -0
  21. package/dist/{chunk-IBRC36PE.mjs → chunk-R27STIER.mjs} +2 -2
  22. package/dist/{chunk-IBRC36PE.mjs.map → chunk-R27STIER.mjs.map} +1 -1
  23. package/dist/chunk-TMKCALFT.cjs +2 -0
  24. package/dist/chunk-TMKCALFT.cjs.map +1 -0
  25. package/dist/{chunk-XSXHEEJX.mjs → chunk-UOGNVWVO.mjs} +2 -2
  26. package/dist/{chunk-XSXHEEJX.mjs.map → chunk-UOGNVWVO.mjs.map} +1 -1
  27. package/dist/{chunk-VN2UYV2O.mjs → chunk-WOKNWJKU.mjs} +2 -2
  28. package/dist/{chunk-VN2UYV2O.mjs.map → chunk-WOKNWJKU.mjs.map} +1 -1
  29. package/dist/chunk-ZX4DAMTG.mjs +2 -0
  30. package/dist/chunk-ZX4DAMTG.mjs.map +1 -0
  31. package/dist/cli/bin.cjs +1 -1
  32. package/dist/cli/bin.cjs.map +1 -1
  33. package/dist/cli/bin.mjs +1 -1
  34. package/dist/cli/bin.mjs.map +1 -1
  35. package/dist/cli/index.cjs +1 -1
  36. package/dist/cli/index.mjs +1 -1
  37. package/dist/index.cjs +1 -1
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.cts +6 -6
  40. package/dist/index.d.ts +6 -6
  41. package/dist/index.mjs +1 -1
  42. package/dist/index.mjs.map +1 -1
  43. package/dist/kanban/index.cjs +1 -1
  44. package/dist/kanban/index.mjs +1 -1
  45. package/dist/scrum/index.cjs +1 -1
  46. package/dist/scrum/index.mjs +1 -1
  47. package/dist/types.d.cts +30 -1
  48. package/dist/types.d.ts +30 -1
  49. package/dist/waterfall/index.cjs +2 -0
  50. package/dist/waterfall/index.cjs.map +1 -0
  51. package/dist/waterfall/index.d.cts +148 -0
  52. package/dist/waterfall/index.d.ts +148 -0
  53. package/dist/waterfall/index.mjs +2 -0
  54. package/dist/waterfall/index.mjs.map +1 -0
  55. package/package.json +14 -1
  56. package/dist/chunk-JDVE7DQR.cjs +0 -2
  57. package/dist/chunk-JDVE7DQR.cjs.map +0 -1
  58. package/dist/chunk-NQU5TH3A.mjs +0 -2
  59. package/dist/chunk-NQU5TH3A.mjs.map +0 -1
  60. package/dist/chunk-NRAPI3GV.cjs +0 -2
  61. package/dist/chunk-NRAPI3GV.cjs.map +0 -1
  62. package/dist/chunk-PD5FBDN7.mjs +0 -2
  63. package/dist/chunk-PD5FBDN7.mjs.map +0 -1
  64. package/dist/chunk-QDT35LXB.cjs +0 -2
  65. package/dist/chunk-QDT35LXB.cjs.map +0 -1
  66. package/dist/chunk-YJNDBKIG.mjs +0 -2
  67. package/dist/chunk-YJNDBKIG.mjs.map +0 -1
package/dist/types.d.cts CHANGED
@@ -26,6 +26,14 @@ interface Transition {
26
26
  at: string;
27
27
  by: string;
28
28
  }
29
+ /** Change-request: what the change touches and what it costs (waterfall change control). */
30
+ interface ImpactAnalysis {
31
+ summary: string;
32
+ /** phase ids to reopen when the change is approved (each must be baselined or open) */
33
+ reopen: string[];
34
+ /** estimated rework weight (story points / size) */
35
+ weight: number;
36
+ }
29
37
  interface WorkItem {
30
38
  id: string;
31
39
  type: ItemType;
@@ -49,6 +57,10 @@ interface WorkItem {
49
57
  tags?: string[];
50
58
  /** bug / change-request: ids of items whose finished work this invalidates */
51
59
  affects?: string[];
60
+ /** waterfall: Phase.id this item belongs to; questions and follow-ups inherit their parent's */
61
+ phase?: string;
62
+ /** change-request: impact analysis required before the change control board decides */
63
+ impactAnalysis?: ImpactAnalysis;
52
64
  }
53
65
  /** Fields a caller supplies to addItem; the rest are defaulted. */
54
66
  type NewItem = Pick<WorkItem, "id" | "title"> & Partial<Omit<WorkItem, "id" | "title" | "stage" | "status" | "evidence" | "blockers" | "transitions">> & {
@@ -197,12 +209,23 @@ interface Lesson {
197
209
  item?: string;
198
210
  suggestedChange?: LessonChange;
199
211
  }
212
+ /** A deliverable recorded on a phase (gateReview input or engine-generated). */
213
+ interface Produced {
214
+ kind: string;
215
+ ref: string;
216
+ by: string;
217
+ at: string;
218
+ }
200
219
  interface Phase {
201
220
  id: string;
202
221
  name: string;
203
222
  deliverables: string[];
204
223
  gate: Gate;
205
224
  status: "pending" | "open" | "baselined" | "reopened";
225
+ /** planned budget; spent is tracked on the Iteration kickoff opens for the phase */
226
+ budget?: Budget;
227
+ /** phase-level deliverables (item evidence counts too) */
228
+ produced?: Produced[];
206
229
  }
207
230
  interface Baseline {
208
231
  phase: string;
@@ -222,6 +245,8 @@ interface WorkflowState {
222
245
  iterations: Iteration[];
223
246
  phases?: Phase[];
224
247
  baseline?: Baseline;
248
+ /** waterfall: every baseline ever taken, oldest first; `baseline` is the last one */
249
+ baselines?: Baseline[];
225
250
  events: Event[];
226
251
  accounting: Accounting[];
227
252
  pending: PendingHuman[];
@@ -302,6 +327,8 @@ interface PlannerOptions {
302
327
  wipCounts?: ItemStatus[];
303
328
  /** Per-item replacement for the target stage's wipLimit (e.g. an expedite bypass); undefined = no limit. */
304
329
  wipLimitOf?(stage: Stage, item: WorkItem): number | undefined;
330
+ /** Role id to dispatch a todo item to (e.g. the phase's deliverer); undefined = default (item.role, then reviewer/deliverer by capability). */
331
+ roleFor?(item: WorkItem): string | undefined;
305
332
  }
306
333
  interface Methodology<C = unknown> {
307
334
  name: MethodologyName;
@@ -324,7 +351,9 @@ interface Methodology<C = unknown> {
324
351
  afterComplete?(state: WorkflowState, itemId: string, now: string): WorkflowState;
325
352
  /** Methodology-specific status for hosts (e.g. cadences due); printed by `exwf status`. */
326
353
  report?(state: WorkflowState): Record<string, unknown>;
354
+ /** Called by decide() after a pending entry is resolved; lets the methodology act on it (e.g. baseline a phase gate, apply a change-request decision). */
355
+ afterDecide?(state: WorkflowState, entry: PendingHuman, decision: "approve" | "reject", by: string, note: string, now: string): WorkflowState;
327
356
  }
328
357
  type ErrorCode = "unknown-item" | "duplicate-item" | "unknown-role" | "duplicate-role" | "unknown-stage" | "missing-capability" | "exclusive-capability" | "role-has-open-items" | "invalid-transition" | "policy-unmet" | "gate-unmet" | "conflict-of-interest" | "handoff-invalid" | "accounting-invalid" | "rule-violation" | "planner-error" | "no-op" | "unknown-pending" | "unknown-ceremony" | "state-version" | "config-invalid" | "budget-exhausted" | "invalid-input";
329
358
 
330
- export type { Accounting, Action, ApplyResult, Baseline, Blocker, Brief, Budget, Capability, Ceremony, CeremonyInput, ClassOfService, CoreTerm, ErrorCode, Event, Evidence, Gate, GateRequirement, Handoff, ItemStatus, ItemType, Iteration, Lesson, LessonChange, Methodology, MethodologyName, ModelTier, NewItem, PendingHuman, Phase, PlannerOptions, Policy, Role, Rule, Stage, Transition, Verdict, Violation, WorkItem, WorkflowState };
359
+ export type { Accounting, Action, ApplyResult, Baseline, Blocker, Brief, Budget, Capability, Ceremony, CeremonyInput, ClassOfService, CoreTerm, ErrorCode, Event, Evidence, Gate, GateRequirement, Handoff, ImpactAnalysis, ItemStatus, ItemType, Iteration, Lesson, LessonChange, Methodology, MethodologyName, ModelTier, NewItem, PendingHuman, Phase, PlannerOptions, Policy, Produced, Role, Rule, Stage, Transition, Verdict, Violation, WorkItem, WorkflowState };
package/dist/types.d.ts CHANGED
@@ -26,6 +26,14 @@ interface Transition {
26
26
  at: string;
27
27
  by: string;
28
28
  }
29
+ /** Change-request: what the change touches and what it costs (waterfall change control). */
30
+ interface ImpactAnalysis {
31
+ summary: string;
32
+ /** phase ids to reopen when the change is approved (each must be baselined or open) */
33
+ reopen: string[];
34
+ /** estimated rework weight (story points / size) */
35
+ weight: number;
36
+ }
29
37
  interface WorkItem {
30
38
  id: string;
31
39
  type: ItemType;
@@ -49,6 +57,10 @@ interface WorkItem {
49
57
  tags?: string[];
50
58
  /** bug / change-request: ids of items whose finished work this invalidates */
51
59
  affects?: string[];
60
+ /** waterfall: Phase.id this item belongs to; questions and follow-ups inherit their parent's */
61
+ phase?: string;
62
+ /** change-request: impact analysis required before the change control board decides */
63
+ impactAnalysis?: ImpactAnalysis;
52
64
  }
53
65
  /** Fields a caller supplies to addItem; the rest are defaulted. */
54
66
  type NewItem = Pick<WorkItem, "id" | "title"> & Partial<Omit<WorkItem, "id" | "title" | "stage" | "status" | "evidence" | "blockers" | "transitions">> & {
@@ -197,12 +209,23 @@ interface Lesson {
197
209
  item?: string;
198
210
  suggestedChange?: LessonChange;
199
211
  }
212
+ /** A deliverable recorded on a phase (gateReview input or engine-generated). */
213
+ interface Produced {
214
+ kind: string;
215
+ ref: string;
216
+ by: string;
217
+ at: string;
218
+ }
200
219
  interface Phase {
201
220
  id: string;
202
221
  name: string;
203
222
  deliverables: string[];
204
223
  gate: Gate;
205
224
  status: "pending" | "open" | "baselined" | "reopened";
225
+ /** planned budget; spent is tracked on the Iteration kickoff opens for the phase */
226
+ budget?: Budget;
227
+ /** phase-level deliverables (item evidence counts too) */
228
+ produced?: Produced[];
206
229
  }
207
230
  interface Baseline {
208
231
  phase: string;
@@ -222,6 +245,8 @@ interface WorkflowState {
222
245
  iterations: Iteration[];
223
246
  phases?: Phase[];
224
247
  baseline?: Baseline;
248
+ /** waterfall: every baseline ever taken, oldest first; `baseline` is the last one */
249
+ baselines?: Baseline[];
225
250
  events: Event[];
226
251
  accounting: Accounting[];
227
252
  pending: PendingHuman[];
@@ -302,6 +327,8 @@ interface PlannerOptions {
302
327
  wipCounts?: ItemStatus[];
303
328
  /** Per-item replacement for the target stage's wipLimit (e.g. an expedite bypass); undefined = no limit. */
304
329
  wipLimitOf?(stage: Stage, item: WorkItem): number | undefined;
330
+ /** Role id to dispatch a todo item to (e.g. the phase's deliverer); undefined = default (item.role, then reviewer/deliverer by capability). */
331
+ roleFor?(item: WorkItem): string | undefined;
305
332
  }
306
333
  interface Methodology<C = unknown> {
307
334
  name: MethodologyName;
@@ -324,7 +351,9 @@ interface Methodology<C = unknown> {
324
351
  afterComplete?(state: WorkflowState, itemId: string, now: string): WorkflowState;
325
352
  /** Methodology-specific status for hosts (e.g. cadences due); printed by `exwf status`. */
326
353
  report?(state: WorkflowState): Record<string, unknown>;
354
+ /** Called by decide() after a pending entry is resolved; lets the methodology act on it (e.g. baseline a phase gate, apply a change-request decision). */
355
+ afterDecide?(state: WorkflowState, entry: PendingHuman, decision: "approve" | "reject", by: string, note: string, now: string): WorkflowState;
327
356
  }
328
357
  type ErrorCode = "unknown-item" | "duplicate-item" | "unknown-role" | "duplicate-role" | "unknown-stage" | "missing-capability" | "exclusive-capability" | "role-has-open-items" | "invalid-transition" | "policy-unmet" | "gate-unmet" | "conflict-of-interest" | "handoff-invalid" | "accounting-invalid" | "rule-violation" | "planner-error" | "no-op" | "unknown-pending" | "unknown-ceremony" | "state-version" | "config-invalid" | "budget-exhausted" | "invalid-input";
329
358
 
330
- export type { Accounting, Action, ApplyResult, Baseline, Blocker, Brief, Budget, Capability, Ceremony, CeremonyInput, ClassOfService, CoreTerm, ErrorCode, Event, Evidence, Gate, GateRequirement, Handoff, ItemStatus, ItemType, Iteration, Lesson, LessonChange, Methodology, MethodologyName, ModelTier, NewItem, PendingHuman, Phase, PlannerOptions, Policy, Role, Rule, Stage, Transition, Verdict, Violation, WorkItem, WorkflowState };
359
+ export type { Accounting, Action, ApplyResult, Baseline, Blocker, Brief, Budget, Capability, Ceremony, CeremonyInput, ClassOfService, CoreTerm, ErrorCode, Event, Evidence, Gate, GateRequirement, Handoff, ImpactAnalysis, ItemStatus, ItemType, Iteration, Lesson, LessonChange, Methodology, MethodologyName, ModelTier, NewItem, PendingHuman, Phase, PlannerOptions, Policy, Produced, Role, Rule, Stage, Transition, Verdict, Violation, WorkItem, WorkflowState };
@@ -0,0 +1,2 @@
1
+ 'use strict';var chunkTMKCALFT_cjs=require('../chunk-TMKCALFT.cjs');require('../chunk-2IKIBB73.cjs'),require('../chunk-23P7C25E.cjs'),require('../chunk-S3BXVHO7.cjs');Object.defineProperty(exports,"CHANGE_GATE",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.e}});Object.defineProperty(exports,"DEFAULT_DELIVERABLES",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.b}});Object.defineProperty(exports,"DEFAULT_DELIVER_BY",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.c}});Object.defineProperty(exports,"DEFAULT_PHASE_IDS",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.a}});Object.defineProperty(exports,"DONE",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.k}});Object.defineProperty(exports,"GATE_ID",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.d}});Object.defineProperty(exports,"REVIEW",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.j}});Object.defineProperty(exports,"WATERFALL_VOCABULARY",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.K}});Object.defineProperty(exports,"WORK",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.i}});Object.defineProperty(exports,"approveChange",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.z}});Object.defineProperty(exports,"baselinePhase",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.x}});Object.defineProperty(exports,"configOf",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.g}});Object.defineProperty(exports,"currentPhase",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.s}});Object.defineProperty(exports,"defaultPhases",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.f}});Object.defineProperty(exports,"deliverRoleFor",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.L}});Object.defineProperty(exports,"getPhase",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.o}});Object.defineProperty(exports,"isFrozen",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.t}});Object.defineProperty(exports,"itemsInPhase",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.q}});Object.defineProperty(exports,"lastToPhase",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.w}});Object.defineProperty(exports,"missingDeliverables",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.v}});Object.defineProperty(exports,"missingTraces",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.C}});Object.defineProperty(exports,"openPhase",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.r}});Object.defineProperty(exports,"phaseIndex",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.n}});Object.defineProperty(exports,"phaseOf",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.p}});Object.defineProperty(exports,"phaseReport",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.M}});Object.defineProperty(exports,"phasesOf",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.l}});Object.defineProperty(exports,"producedKinds",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.u}});Object.defineProperty(exports,"rejectChange",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.A}});Object.defineProperty(exports,"reopenPhases",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.y}});Object.defineProperty(exports,"traceabilityMatrix",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.B}});Object.defineProperty(exports,"validateWaterfallConfig",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.h}});Object.defineProperty(exports,"wantPhase",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.D}});Object.defineProperty(exports,"waterfall",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.N}});Object.defineProperty(exports,"waterfallAfterDecide",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.F}});Object.defineProperty(exports,"waterfallCeremonies",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.E}});Object.defineProperty(exports,"waterfallMetrics",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.G}});Object.defineProperty(exports,"waterfallRoles",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.H}});Object.defineProperty(exports,"waterfallRules",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.I}});Object.defineProperty(exports,"waterfallStages",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.J}});Object.defineProperty(exports,"withPhases",{enumerable:true,get:function(){return chunkTMKCALFT_cjs.m}});//# sourceMappingURL=index.cjs.map
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
@@ -0,0 +1,148 @@
1
+ import { Gate, Budget, GateRequirement, WorkflowState, Phase, WorkItem, Stage, Role, Rule, CeremonyInput, PendingHuman, Methodology, Ceremony, CoreTerm } from '../types.cjs';
2
+
3
+ interface WaterfallPhaseConfig {
4
+ id: string;
5
+ name: string;
6
+ deliverables: string[];
7
+ gate: Gate;
8
+ budget?: Budget;
9
+ /** role id that delivers this phase's items; default: the first role with "deliver" */
10
+ deliverBy?: string;
11
+ }
12
+ interface WaterfallConfig {
13
+ phases: WaterfallPhaseConfig[];
14
+ changeControl: {
15
+ requiresImpactAnalysis: boolean;
16
+ approvers: GateRequirement[];
17
+ };
18
+ /** phase ids: every item of `from` must trace to an item of each `to` phase before that phase's gate */
19
+ traceability: {
20
+ from: string;
21
+ to: string[];
22
+ };
23
+ humanGates: "all" | string[];
24
+ }
25
+ declare const DEFAULT_PHASE_IDS: readonly string[];
26
+ declare const DEFAULT_DELIVERABLES: Record<string, string[]>;
27
+ declare const DEFAULT_DELIVER_BY: Record<string, string>;
28
+ declare const GATE_ID: (id: string) => string;
29
+ declare const CHANGE_GATE = "change-control";
30
+ declare function defaultPhases(): WaterfallPhaseConfig[];
31
+ declare function configOf(state: WorkflowState): WaterfallConfig;
32
+ declare function validateWaterfallConfig(raw: unknown): WaterfallConfig;
33
+
34
+ declare const WORK = "work";
35
+ declare const REVIEW = "review";
36
+ declare const DONE = "done";
37
+ /** state.phases, or the config phases as pending (before the first ceremony touches them). */
38
+ declare function phasesOf(state: WorkflowState): Phase[];
39
+ declare function withPhases(state: WorkflowState, phases: Phase[]): WorkflowState;
40
+ declare function phaseIndex(state: WorkflowState, id: string): number;
41
+ declare function getPhase(state: WorkflowState, id: string): Phase;
42
+ /** The item's phase, inherited from its parent chain (questions, follow-ups, retro items). */
43
+ declare function phaseOf(state: WorkflowState, item: WorkItem): string | undefined;
44
+ declare function itemsInPhase(state: WorkflowState, phaseId: string): WorkItem[];
45
+ declare function openPhase(state: WorkflowState): Phase | undefined;
46
+ /** First phase in order that is not baselined: the one to kick off (or the open one). */
47
+ declare function currentPhase(state: WorkflowState): Phase | undefined;
48
+ declare function isFrozen(state: WorkflowState, item: WorkItem): boolean;
49
+ declare function producedKinds(state: WorkflowState, phase: Phase): Set<string>;
50
+ /** Deliverables (and gate evidence kinds) still missing, in declaration order. */
51
+ declare function missingDeliverables(state: WorkflowState, phase: Phase, extra?: string[]): string[];
52
+ /** The traceability `to` phase that comes last in phase order (where the matrix must be complete). */
53
+ declare function lastToPhase(state: WorkflowState): string | undefined;
54
+
55
+ /** Every phase's items share the same three steps; the phase is the unit of governance. */
56
+ declare function waterfallStages(_config: WaterfallConfig): Stage[];
57
+
58
+ /** Order matters: the first role with "deliver" is the fallback deliverer for phases without deliverBy. */
59
+ declare function waterfallRoles(config: WaterfallConfig): Role[];
60
+
61
+ declare function waterfallRules(): Rule[];
62
+
63
+ interface WaterfallMetrics {
64
+ phaseCompletion: Record<string, number>;
65
+ budgetPlanned: Record<string, number>;
66
+ budgetSpent: Record<string, number>;
67
+ scheduleVariance: Record<string, number>;
68
+ requirementCoverage: number;
69
+ requirementsTraced: number;
70
+ requirementsTotal: number;
71
+ defectsPerPhase: Record<string, number>;
72
+ changeRequests: number;
73
+ changesApproved: number;
74
+ changesRejected: number;
75
+ changeApprovalRate: number;
76
+ reworkWeight: number;
77
+ phasesReopened: number;
78
+ }
79
+ /** Phase metrics replayed from events alone (membership from item.added, spend from item.completed). */
80
+ declare function waterfallMetrics(state: WorkflowState): WaterfallMetrics;
81
+
82
+ declare function approveChange(state: WorkflowState, changeId: string, by: string, note: string, now: string): WorkflowState;
83
+ declare function rejectChange(state: WorkflowState, changeId: string, by: string, note: string, now: string): WorkflowState;
84
+
85
+ /** input.phase as an existing phase. */
86
+ declare function wantPhase(state: WorkflowState, input: CeremonyInput, ceremony: string): Phase;
87
+
88
+ declare function waterfallCeremonies(methodology: () => Methodology): Record<string, Ceremony>;
89
+ /** decide() hook: a phase gate's human decision baselines the phase or records the failure. */
90
+ declare function waterfallAfterDecide(state: WorkflowState, entry: PendingHuman, decision: "approve" | "reject", by: string, note: string, now: string): WorkflowState;
91
+
92
+ interface TraceRow {
93
+ item: string;
94
+ /** to-phase id → ids of linked items in that phase */
95
+ links: Record<string, string[]>;
96
+ }
97
+ interface TraceabilityMatrix {
98
+ from: string;
99
+ to: string[];
100
+ rows: TraceRow[];
101
+ missing: Array<{
102
+ item: string;
103
+ phases: string[];
104
+ }>;
105
+ coverage: {
106
+ traced: number;
107
+ total: number;
108
+ };
109
+ }
110
+ declare function traceabilityMatrix(state: WorkflowState): TraceabilityMatrix;
111
+ /** Ids of `from` items with no link into `toPhase`. */
112
+ declare function missingTraces(state: WorkflowState, toPhase: string): string[];
113
+
114
+ /** Pass the phase gate: freeze its items, record the baseline, close its iteration. */
115
+ declare function baselinePhase(state: WorkflowState, phaseId: string, approvedBy: string[], now: string): WorkflowState;
116
+ interface ReopenResult {
117
+ state: WorkflowState;
118
+ reopened: string[];
119
+ suspended: string | null;
120
+ }
121
+ /** Change-control approval: reopen the listed baselined phases; a later open phase is suspended until they are re-baselined. */
122
+ declare function reopenPhases(state: WorkflowState, ids: string[], now: string): ReopenResult;
123
+
124
+ declare const WATERFALL_VOCABULARY: Record<CoreTerm, string>;
125
+ /** The role that delivers the item's phase (config deliverBy); undefined = the planner's default deliverer. */
126
+ declare function deliverRoleFor(state: WorkflowState, item: WorkItem): string | undefined;
127
+ interface PhaseReportRow {
128
+ id: string;
129
+ name: string;
130
+ status: string;
131
+ items: number;
132
+ done: number;
133
+ missingDeliverables: string[];
134
+ readyForGate: boolean;
135
+ }
136
+ interface PhaseReport {
137
+ phases: PhaseReportRow[];
138
+ current: string | null;
139
+ open: string | null;
140
+ pendingGates: Array<{
141
+ pending: string;
142
+ gate: string;
143
+ }>;
144
+ }
145
+ declare function phaseReport(state: WorkflowState): PhaseReport;
146
+ declare function waterfall(): Methodology<WaterfallConfig>;
147
+
148
+ export { CHANGE_GATE, DEFAULT_DELIVERABLES, DEFAULT_DELIVER_BY, DEFAULT_PHASE_IDS, DONE, GATE_ID, type PhaseReport, type PhaseReportRow, REVIEW, type ReopenResult, type TraceRow, type TraceabilityMatrix, WATERFALL_VOCABULARY, WORK, type WaterfallConfig, type WaterfallMetrics, type WaterfallPhaseConfig, approveChange, baselinePhase, configOf, currentPhase, defaultPhases, deliverRoleFor, getPhase, isFrozen, itemsInPhase, lastToPhase, missingDeliverables, missingTraces, openPhase, phaseIndex, phaseOf, phaseReport, phasesOf, producedKinds, rejectChange, reopenPhases, traceabilityMatrix, validateWaterfallConfig, wantPhase, waterfall, waterfallAfterDecide, waterfallCeremonies, waterfallMetrics, waterfallRoles, waterfallRules, waterfallStages, withPhases };
@@ -0,0 +1,148 @@
1
+ import { Gate, Budget, GateRequirement, WorkflowState, Phase, WorkItem, Stage, Role, Rule, CeremonyInput, PendingHuman, Methodology, Ceremony, CoreTerm } from '../types.js';
2
+
3
+ interface WaterfallPhaseConfig {
4
+ id: string;
5
+ name: string;
6
+ deliverables: string[];
7
+ gate: Gate;
8
+ budget?: Budget;
9
+ /** role id that delivers this phase's items; default: the first role with "deliver" */
10
+ deliverBy?: string;
11
+ }
12
+ interface WaterfallConfig {
13
+ phases: WaterfallPhaseConfig[];
14
+ changeControl: {
15
+ requiresImpactAnalysis: boolean;
16
+ approvers: GateRequirement[];
17
+ };
18
+ /** phase ids: every item of `from` must trace to an item of each `to` phase before that phase's gate */
19
+ traceability: {
20
+ from: string;
21
+ to: string[];
22
+ };
23
+ humanGates: "all" | string[];
24
+ }
25
+ declare const DEFAULT_PHASE_IDS: readonly string[];
26
+ declare const DEFAULT_DELIVERABLES: Record<string, string[]>;
27
+ declare const DEFAULT_DELIVER_BY: Record<string, string>;
28
+ declare const GATE_ID: (id: string) => string;
29
+ declare const CHANGE_GATE = "change-control";
30
+ declare function defaultPhases(): WaterfallPhaseConfig[];
31
+ declare function configOf(state: WorkflowState): WaterfallConfig;
32
+ declare function validateWaterfallConfig(raw: unknown): WaterfallConfig;
33
+
34
+ declare const WORK = "work";
35
+ declare const REVIEW = "review";
36
+ declare const DONE = "done";
37
+ /** state.phases, or the config phases as pending (before the first ceremony touches them). */
38
+ declare function phasesOf(state: WorkflowState): Phase[];
39
+ declare function withPhases(state: WorkflowState, phases: Phase[]): WorkflowState;
40
+ declare function phaseIndex(state: WorkflowState, id: string): number;
41
+ declare function getPhase(state: WorkflowState, id: string): Phase;
42
+ /** The item's phase, inherited from its parent chain (questions, follow-ups, retro items). */
43
+ declare function phaseOf(state: WorkflowState, item: WorkItem): string | undefined;
44
+ declare function itemsInPhase(state: WorkflowState, phaseId: string): WorkItem[];
45
+ declare function openPhase(state: WorkflowState): Phase | undefined;
46
+ /** First phase in order that is not baselined: the one to kick off (or the open one). */
47
+ declare function currentPhase(state: WorkflowState): Phase | undefined;
48
+ declare function isFrozen(state: WorkflowState, item: WorkItem): boolean;
49
+ declare function producedKinds(state: WorkflowState, phase: Phase): Set<string>;
50
+ /** Deliverables (and gate evidence kinds) still missing, in declaration order. */
51
+ declare function missingDeliverables(state: WorkflowState, phase: Phase, extra?: string[]): string[];
52
+ /** The traceability `to` phase that comes last in phase order (where the matrix must be complete). */
53
+ declare function lastToPhase(state: WorkflowState): string | undefined;
54
+
55
+ /** Every phase's items share the same three steps; the phase is the unit of governance. */
56
+ declare function waterfallStages(_config: WaterfallConfig): Stage[];
57
+
58
+ /** Order matters: the first role with "deliver" is the fallback deliverer for phases without deliverBy. */
59
+ declare function waterfallRoles(config: WaterfallConfig): Role[];
60
+
61
+ declare function waterfallRules(): Rule[];
62
+
63
+ interface WaterfallMetrics {
64
+ phaseCompletion: Record<string, number>;
65
+ budgetPlanned: Record<string, number>;
66
+ budgetSpent: Record<string, number>;
67
+ scheduleVariance: Record<string, number>;
68
+ requirementCoverage: number;
69
+ requirementsTraced: number;
70
+ requirementsTotal: number;
71
+ defectsPerPhase: Record<string, number>;
72
+ changeRequests: number;
73
+ changesApproved: number;
74
+ changesRejected: number;
75
+ changeApprovalRate: number;
76
+ reworkWeight: number;
77
+ phasesReopened: number;
78
+ }
79
+ /** Phase metrics replayed from events alone (membership from item.added, spend from item.completed). */
80
+ declare function waterfallMetrics(state: WorkflowState): WaterfallMetrics;
81
+
82
+ declare function approveChange(state: WorkflowState, changeId: string, by: string, note: string, now: string): WorkflowState;
83
+ declare function rejectChange(state: WorkflowState, changeId: string, by: string, note: string, now: string): WorkflowState;
84
+
85
+ /** input.phase as an existing phase. */
86
+ declare function wantPhase(state: WorkflowState, input: CeremonyInput, ceremony: string): Phase;
87
+
88
+ declare function waterfallCeremonies(methodology: () => Methodology): Record<string, Ceremony>;
89
+ /** decide() hook: a phase gate's human decision baselines the phase or records the failure. */
90
+ declare function waterfallAfterDecide(state: WorkflowState, entry: PendingHuman, decision: "approve" | "reject", by: string, note: string, now: string): WorkflowState;
91
+
92
+ interface TraceRow {
93
+ item: string;
94
+ /** to-phase id → ids of linked items in that phase */
95
+ links: Record<string, string[]>;
96
+ }
97
+ interface TraceabilityMatrix {
98
+ from: string;
99
+ to: string[];
100
+ rows: TraceRow[];
101
+ missing: Array<{
102
+ item: string;
103
+ phases: string[];
104
+ }>;
105
+ coverage: {
106
+ traced: number;
107
+ total: number;
108
+ };
109
+ }
110
+ declare function traceabilityMatrix(state: WorkflowState): TraceabilityMatrix;
111
+ /** Ids of `from` items with no link into `toPhase`. */
112
+ declare function missingTraces(state: WorkflowState, toPhase: string): string[];
113
+
114
+ /** Pass the phase gate: freeze its items, record the baseline, close its iteration. */
115
+ declare function baselinePhase(state: WorkflowState, phaseId: string, approvedBy: string[], now: string): WorkflowState;
116
+ interface ReopenResult {
117
+ state: WorkflowState;
118
+ reopened: string[];
119
+ suspended: string | null;
120
+ }
121
+ /** Change-control approval: reopen the listed baselined phases; a later open phase is suspended until they are re-baselined. */
122
+ declare function reopenPhases(state: WorkflowState, ids: string[], now: string): ReopenResult;
123
+
124
+ declare const WATERFALL_VOCABULARY: Record<CoreTerm, string>;
125
+ /** The role that delivers the item's phase (config deliverBy); undefined = the planner's default deliverer. */
126
+ declare function deliverRoleFor(state: WorkflowState, item: WorkItem): string | undefined;
127
+ interface PhaseReportRow {
128
+ id: string;
129
+ name: string;
130
+ status: string;
131
+ items: number;
132
+ done: number;
133
+ missingDeliverables: string[];
134
+ readyForGate: boolean;
135
+ }
136
+ interface PhaseReport {
137
+ phases: PhaseReportRow[];
138
+ current: string | null;
139
+ open: string | null;
140
+ pendingGates: Array<{
141
+ pending: string;
142
+ gate: string;
143
+ }>;
144
+ }
145
+ declare function phaseReport(state: WorkflowState): PhaseReport;
146
+ declare function waterfall(): Methodology<WaterfallConfig>;
147
+
148
+ export { CHANGE_GATE, DEFAULT_DELIVERABLES, DEFAULT_DELIVER_BY, DEFAULT_PHASE_IDS, DONE, GATE_ID, type PhaseReport, type PhaseReportRow, REVIEW, type ReopenResult, type TraceRow, type TraceabilityMatrix, WATERFALL_VOCABULARY, WORK, type WaterfallConfig, type WaterfallMetrics, type WaterfallPhaseConfig, approveChange, baselinePhase, configOf, currentPhase, defaultPhases, deliverRoleFor, getPhase, isFrozen, itemsInPhase, lastToPhase, missingDeliverables, missingTraces, openPhase, phaseIndex, phaseOf, phaseReport, phasesOf, producedKinds, rejectChange, reopenPhases, traceabilityMatrix, validateWaterfallConfig, wantPhase, waterfall, waterfallAfterDecide, waterfallCeremonies, waterfallMetrics, waterfallRoles, waterfallRules, waterfallStages, withPhases };
@@ -0,0 +1,2 @@
1
+ export{e as CHANGE_GATE,b as DEFAULT_DELIVERABLES,c as DEFAULT_DELIVER_BY,a as DEFAULT_PHASE_IDS,k as DONE,d as GATE_ID,j as REVIEW,K as WATERFALL_VOCABULARY,i as WORK,z as approveChange,x as baselinePhase,g as configOf,s as currentPhase,f as defaultPhases,L as deliverRoleFor,o as getPhase,t as isFrozen,q as itemsInPhase,w as lastToPhase,v as missingDeliverables,C as missingTraces,r as openPhase,n as phaseIndex,p as phaseOf,M as phaseReport,l as phasesOf,u as producedKinds,A as rejectChange,y as reopenPhases,B as traceabilityMatrix,h as validateWaterfallConfig,D as wantPhase,N as waterfall,F as waterfallAfterDecide,E as waterfallCeremonies,G as waterfallMetrics,H as waterfallRoles,I as waterfallRules,J as waterfallStages,m as withPhases}from'../chunk-BJEOTG6P.mjs';import'../chunk-ZX4DAMTG.mjs';import'../chunk-FLCYD2QF.mjs';import'../chunk-2FRRD6A3.mjs';//# sourceMappingURL=index.mjs.map
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.mjs"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ex-workflow",
3
- "version": "0.2.0-stage",
3
+ "version": "0.3.0-stage",
4
4
  "description": "Methodology engine for AI teams: Scrum, Kanban and Waterfall as data-driven state machines on top of ex-flow",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -61,6 +61,16 @@
61
61
  "default": "./dist/kanban/index.cjs"
62
62
  }
63
63
  },
64
+ "./waterfall": {
65
+ "import": {
66
+ "types": "./dist/waterfall/index.d.ts",
67
+ "default": "./dist/waterfall/index.mjs"
68
+ },
69
+ "require": {
70
+ "types": "./dist/waterfall/index.d.cts",
71
+ "default": "./dist/waterfall/index.cjs"
72
+ }
73
+ },
64
74
  "./adapters": {
65
75
  "import": {
66
76
  "types": "./dist/adapters/index.d.ts",
@@ -100,6 +110,9 @@
100
110
  "kanban": [
101
111
  "dist/kanban/index.d.ts"
102
112
  ],
113
+ "waterfall": [
114
+ "dist/waterfall/index.d.ts"
115
+ ],
103
116
  "adapters": [
104
117
  "dist/adapters/index.d.ts"
105
118
  ],
@@ -1,2 +0,0 @@
1
- 'use strict';var chunkS3BXVHO7_cjs=require('./chunk-S3BXVHO7.cjs');function k(t){return t??new Date().toISOString()}function C(t){let e=new Set;for(let n of t.stages)e.has(n.id)&&chunkS3BXVHO7_cjs.b("config-invalid",`duplicate stage id "${n.id}"`,"give every stage a unique id"),e.add(n.id);let o={};for(let n of t.roles)chunkS3BXVHO7_cjs.c(n.id,"role id"),Object.hasOwn(o,n.id)&&chunkS3BXVHO7_cjs.b("duplicate-role",`duplicate role id "${n.id}"`,"give every role a unique id"),o[n.id]=n;let i=k(t.now);return {version:1,methodology:t.methodology,config:t.config,items:{},stages:t.stages,roles:o,iterations:[],events:[{seq:1,at:i,type:"state.created",by:"system",payload:{methodology:t.methodology}}],accounting:[],pending:[],lessons:[],agreements:[]}}function g(t,e,o,i,n){let r={seq:(t.events[t.events.length-1]?.seq??0)+1,at:k(n),type:e,by:o,payload:i};return {...t,events:[...t.events,r]}}function te(t,e){for(let o=t.events.length-1;o>=0;o--){let i=t.events[o];if(e===void 0||i.type===e)return i}}function j(t,e){return Object.hasOwn(t.roles,e)?t.roles[e]:void 0}var H=new Set(["in_progress","review","blocked"]);function re(t,e,o,i){chunkS3BXVHO7_cjs.c(e.id,"role id"),j(t,e.id)&&chunkS3BXVHO7_cjs.b("duplicate-role",`role "${e.id}" already exists`,`use a different id or retireRole("${e.id}") first`);let n={...t,roles:{...t.roles,[e.id]:e}};return g(n,"role.registered",o,{role:e.id,capabilities:e.capabilities},i)}function se(t,e,o,i){j(t,e)||chunkS3BXVHO7_cjs.b("unknown-role",`role "${e}" does not exist`,`registered roles: ${Object.keys(t.roles).join(", ")||"(none)"}`);let n=Object.values(t.items).filter(r=>r.role===e&&H.has(r.status)).map(r=>r.id);n.length&&chunkS3BXVHO7_cjs.b("role-has-open-items",`role "${e}" still holds ${n.length} open item(s)`,`complete or reassign ${n.join(", ")} before retiring the role`,{items:n});let s={...t.roles};return delete s[e],g({...t,roles:s},"role.retired",o,{role:e},i)}function x(t,e){return t.capabilities.includes(e)}function O(t,e){return Object.values(t.roles).filter(o=>x(o,e))}function w(t,e,o,i){return Object.values(t.roles).filter(n=>n.id!==i&&n.canApprove.includes(e)&&(o===void 0||n.capabilities.includes(o))).map(n=>n.id)}function ae(t,e){return O(t,"approve-gate").filter(o=>o.canApprove.includes(e))}function q(t,e,o){for(let i of e)O(t,i).length===0&&chunkS3BXVHO7_cjs.b("missing-capability",`no registered role has capability "${i}"`,`register a role whose capabilities include "${i}" (registerRole) before init`);for(let i of o){let n=O(t,i);n.length>1&&chunkS3BXVHO7_cjs.b("exclusive-capability",`capability "${i}" must belong to exactly one role but is held by ${n.map(s=>s.id).join(", ")}`,`remove "${i}" from all but one role`);}}function P(t,e){return e.length===0?[]:Object.values(t.roles).filter(o=>e.every(i=>x(o,i)))}function G(t,e,...o){if(e==="human")return true;let i=j(t,e);return !!i&&o.some(n=>x(i,n))}function le(t,e,...o){if(!G(t,e,...o)){let i=Object.values(t.roles).filter(n=>o.some(s=>x(n,s))).map(n=>n.id);chunkS3BXVHO7_cjs.b("missing-capability",`"${e}" lacks capability ${o.map(n=>`"${n}"`).join(" or ")}`,`run this as one of: human${i.map(n=>`, ${n}`).join("")}`);}}function I(t,e){let o=t.stages.find(i=>i.id===e);return o||chunkS3BXVHO7_cjs.b("unknown-stage",`stage "${e}" does not exist`,`stages are: ${t.stages.map(i=>i.id).join(" \u2192 ")}`),o}function D(t,e){let o=t.stages.findIndex(i=>i.id===e);return o<0&&I(t,e),t.stages[o+1]}function K(t,e){return t.stages[t.stages.length-1]?.id===e}function v(t,e){return {...t,items:{...t.items,[e.id]:e}}}function J(t,e,o){return v(t,o(h(t,e)))}function L(t,e,o){return J(t,e,i=>{let n={...i};for(let s of o)delete n[s];return n})}function h(t,e){let o=Object.hasOwn(t.items,e)?t.items[e]:void 0;return o||chunkS3BXVHO7_cjs.b("unknown-item",`item "${e}" does not exist`,`add it with addItem first; known items: ${Object.keys(t.items).join(", ")||"(none)"}`,{items:[e]}),o}function $(t,e,o){let i=e.filter(n=>!Object.hasOwn(t.items,n));i.length&&chunkS3BXVHO7_cjs.b("unknown-item",`${o} refers to unknown item(s) ${i.join(", ")}`,"add those items first or remove the reference",{items:i});}function ue(t,e,o,i){chunkS3BXVHO7_cjs.c(e.id,"item id"),Object.hasOwn(t.items,e.id)&&chunkS3BXVHO7_cjs.b("duplicate-item",`item "${e.id}" already exists`,"use editItem to change it, or pick a new id",{items:[e.id]});let n=t.stages[0];n||chunkS3BXVHO7_cjs.b("config-invalid","state has no stages","create the state with at least one stage");let s=e.stage??n.id;I(t,s),$(t,e.dependsOn??[],`item "${e.id}".dependsOn`),e.affects&&$(t,e.affects,`item "${e.id}".affects`),e.parent!==void 0&&$(t,[e.parent],`item "${e.id}".parent`),e.role!==void 0&&!Object.hasOwn(t.roles,e.role)&&chunkS3BXVHO7_cjs.b("unknown-role",`role "${e.role}" does not exist`,`registered roles: ${Object.keys(t.roles).join(", ")}`);let{stage:r,...a}=e,l={type:"task",acceptance:[],dependsOn:[],...a,id:e.id,title:e.title,stage:s,status:"todo",evidence:[],blockers:[],transitions:[]},c=v(t,l);return g(c,"item.added",o,{id:l.id,type:l.type,stage:l.stage,weight:l.weight??null,priority:l.priority??null},i)}function ge(t,e,o,i,n){let s=h(t,e);o.dependsOn&&$(t,o.dependsOn,`item "${e}".dependsOn`),o.affects&&$(t,o.affects,`item "${e}".affects`),o.parent!==void 0&&$(t,[o.parent],`item "${e}".parent`),o.role!==void 0&&!Object.hasOwn(t.roles,o.role)&&chunkS3BXVHO7_cjs.b("unknown-role",`role "${o.role}" does not exist`,`registered roles: ${Object.keys(t.roles).join(", ")}`);let r=Object.keys(o).filter(c=>JSON.stringify(o[c])!==JSON.stringify(s[c]));r.length===0&&chunkS3BXVHO7_cjs.b("no-op",`editItem("${e}") changes nothing`,"pass at least one field with a different value",{items:[e]});let a={...s};for(let c of r)a[c]=o[c];let l=v(t,a);return g(l,"item.edited",i,{id:e,changed:r},n)}function S(t,e,o,i,n){let s=h(t,e),r=o.stage??s.stage;I(t,r),(s.status==="done"||s.status==="dropped")&&chunkS3BXVHO7_cjs.b("invalid-transition",`item "${e}" is ${s.status} and cannot move`,"create a new item (or a change-request) instead of reopening a finished one",{items:[e]}),r===s.stage&&o.status===s.status&&chunkS3BXVHO7_cjs.b("no-op",`item "${e}" is already ${s.status} in "${r}"`,"choose a different stage or status",{items:[e]});let a=k(n),l={...s,stage:r,status:o.status,transitions:[...s.transitions,{from:{stage:s.stage,status:s.status},to:{stage:r,status:o.status},at:a,by:i}]},c=v(t,l);return g(c,"item.transition",i,{id:e,from:{stage:s.stage,status:s.status},to:{stage:r,status:o.status}},a)}function pe(t,e,o,i){let n=h(t,e),s=k(i),r={...n,evidence:[...n.evidence,{...o,at:s}]},a=v(t,r);return g(a,"item.evidence",o.by,{id:e,kind:o.kind,ref:o.ref},s)}function V(t,e,o,i,n,s){let r=h(t,e),a=k(s),l=`${e}-b${r.blockers.length+1}`,c=r.status==="blocked"?null:{from:{stage:r.stage,status:r.status},to:{stage:r.stage,status:"blocked"},at:a,by:n},f={...r,status:"blocked",blockers:[...r.blockers,{id:l,reason:o,owner:i,raisedAt:a}],transitions:c?[...r.transitions,c]:r.transitions},u=v(t,f);return g(u,"blocker.raised",n,{id:e,blocker:l,reason:o,owner:i,transition:c&&{from:c.from,to:c.to}},a)}function E(t,e){for(let o=t.transitions.length-1;o>=0;o--){let i=t.transitions[o];if(i.to.status===e)return i}}function me(t,e,o,i,n){let s=h(t,e),r=s.blockers.findIndex(m=>m.id===o&&!m.clearedAt);r<0&&chunkS3BXVHO7_cjs.b("invalid-input",`item "${e}" has no open blocker "${o}"`,`open blockers: ${s.blockers.filter(m=>!m.clearedAt).map(m=>m.id).join(", ")||"(none)"}`,{items:[e]});let a=k(n),l=s.blockers.map((m,F)=>F===r?{...m,clearedAt:a}:m),c=l.some(m=>!m.clearedAt),f=E(s,"blocked")?.from.status??"todo",u=s.stage,p=f,W=false;f==="in_progress"?(p="todo",u=E(s,"in_progress")?.from.stage??s.stage):f==="awaiting_human"&&(p=I(t,s.stage).review?"review":"todo",W=true);let{role:Z,...T}=s,b=c?null:{from:{stage:s.stage,status:"blocked"},to:{stage:u,status:p},at:a,by:i},N={...W&&!c?T:s,blockers:l,stage:c?s.stage:u,status:c?"blocked":p,transitions:b?[...s.transitions,b]:s.transitions},B=v(t,N);return g(B,"blocker.cleared",i,{id:e,blocker:o,transition:b&&{from:b.from,to:b.to}},a)}function R(t,e){return L(t,e,["role"])}function M(t,e,o,i,n){let s=h(t,e),r=D(t,s.stage);if(!r)return S(t,e,{status:"done"},o,n);if(K(t,r.id))return S(t,e,{stage:r.id,status:"done"},o,n);let a=i||r.review===true?"review":"todo";return R(S(t,e,{stage:r.id,status:a},o,n),e)}function z(t,e,o){let i=[],n=(s,r,a)=>i.push({rule:s,severity:"error",item:e.id,message:r,fix:a});for(let s of o)switch(s.kind){case "evidence":e.evidence.some(r=>r.kind===s.evidenceKind)||n("policy:evidence",`item "${e.id}" has no evidence of kind "${s.evidenceKind}"`,`attach evidence { kind: "${s.evidenceKind}" } via addEvidence or in the handoff.produced`);break;case "acceptance-written":e.acceptance.length===0&&n("policy:acceptance-written",`item "${e.id}" has no acceptance criteria`,"editItem with a non-empty acceptance[] (needs define-acceptance)");break;case "estimate-present":e.weight===void 0&&n("policy:estimate-present",`item "${e.id}" has no weight`,"editItem with a weight (story points)");break;case "no-open-blockers":{let r=e.blockers.filter(a=>!a.clearedAt);r.length&&n("policy:no-open-blockers",`item "${e.id}" has ${r.length} open blocker(s)`,`clearBlocker ${r.map(a=>a.id).join(", ")}`);break}case "dependencies-done":{let r=e.dependsOn.filter(a=>t.items[a]?.status!=="done");r.length&&n("policy:dependencies-done",`item "${e.id}" depends on unfinished ${r.join(", ")}`,`finish ${r.join(", ")} first`);break}case "custom":e.evidence.some(r=>r.kind===`policy:${s.id}`)||n(`policy:custom:${s.id}`,`item "${e.id}" has not satisfied "${s.description}"`,`attach evidence { kind: "policy:${s.id}" } when it is satisfied`);break}return i}function we(t,e,o,i){let n=z(t,e,o);n.length&&chunkS3BXVHO7_cjs.b("policy-unmet",`item "${e.id}" cannot ${i}: ${n.length} polic${n.length===1?"y":"ies"} unmet`,n.map(s=>s.fix).join("; "),{items:[e.id]});}function Q(t,e){let o=new Map(t.stages.map((n,s)=>[n.id,s])),i=o.get(e.stage);if(i!==void 0)for(let n=e.transitions.length-1;n>=0;n--){let s=e.transitions[n],r=o.get(s.from.stage);if(s.to.status==="in_progress"&&r!==void 0&&r<i)return s}}function A(t,e){let o=Q(t,e);if(o)return o.by;for(let i=e.transitions.length-1;i>=0;i--){let n=e.transitions[i];if(n.to.status==="in_progress")return n.by}}function U(t,e,o,i){let n=[];for(let r of o.evidence)e.evidence.some(a=>a.kind===r)||n.push({rule:"gate:evidence",severity:"error",item:e.id,message:`gate "${o.id}" needs evidence "${r}" on item "${e.id}"`,fix:`attach evidence { kind: "${r}" } before approving`});if(n.length)return {outcome:"fail",violations:n};let s=false;for(let r of o.requires){if(r==="human"){i!=="human"&&(s=true);continue}if(!w(t,o.id,r.capability).includes(i)){n.push({rule:"gate:capability",severity:"error",item:e.id,message:`"${i}" may not approve gate "${o.id}" (needs capability "${r.capability}" and canApprove "${o.id}")`,fix:`approve as one of: ${w(t,o.id,r.capability).join(", ")||"(no role qualifies; register one)"}`});continue}let l=A(t,e);if(l!==void 0&&l===i){let c=w(t,o.id,r.capability,i);n.push({rule:"conflict-of-interest",severity:"error",item:e.id,message:`role "${i}" did the work on "${e.id}" and may not approve it`,fix:`approve as a different role: ${c.join(", ")||"(register another approver)"}`});}}return n.length?{outcome:"fail",violations:n}:s?{outcome:"human"}:{outcome:"pass"}}function be(t,e,o){let i=k(o),n=`p${t.events.filter(a=>a.type==="pending.raised").length+1}`,s={...e,id:n,raisedAt:i},r={...t,pending:[...t.pending,s]};return e.item&&h(r,e.item).status!=="awaiting_human"&&(r=S(r,e.item,{status:"awaiting_human"},"system",i)),g(r,"pending.raised","system",{id:n,kind:e.kind,item:e.item??null,gate:e.gate??null},i)}function xe(t,e,o,i,n,s){o!=="approve"&&o!=="reject"&&chunkS3BXVHO7_cjs.b("invalid-input",`decision must be "approve" or "reject" (got ${JSON.stringify(o)})`,"pass approve or reject");let r=t.pending.find(u=>u.id===e);r||chunkS3BXVHO7_cjs.b("unknown-pending",`no pending decision "${e}"`,`open decisions: ${t.pending.map(u=>u.id).join(", ")||"(none)"}`);let a=r.kind==="gate"&&r.item?h(t,r.item):void 0,l=a&&(t.stages.find(u=>r.gate!==void 0&&u.gate?.id===r.gate)?.gate??t.stages.find(u=>u.id===a.stage)?.gate);a&&l&&i!=="human"&&(l.requires.includes("human")?void 0:U(t,a,l,i))?.outcome!=="pass"&&chunkS3BXVHO7_cjs.b("gate-unmet",`"${i}" may not decide gate "${l.id}" on "${a.id}"`,`decide as one of: human${l.requires.flatMap(p=>p==="human"?[]:w(t,l.id,p.capability)).map(p=>`, ${p}`).join("")}`,{items:[a.id],rule:"gate"});let c=k(s),f={...t,pending:t.pending.filter(u=>u.id!==e)};if(f=g(f,"pending.decided",i,{id:e,decision:o,note:n,item:r.item??null,gate:r.gate??null},c),a){let u=a.id,p=r.gate??l?.id??null;if(o==="approve")f=M(f,u,i,false,c),f=g(f,"gate.passed",i,{item:u,gate:p},c);else {let W=l?.onFail??{action:"block"};W.action==="return-to"?f=R(S(f,u,{stage:W.stage,status:"todo"},i,c),u):f=V(f,u,`gate "${p??"?"}" rejected: ${n||"no reason given"}`,i,i,c),f=g(f,"gate.failed",i,{item:u,gate:p,note:n},c);}}return f}function _(t){return [{id:"separation-lost",severity:"warning",check(i,n){return n.type!=="init"&&n.type!=="role.register"?[]:P(i,t.requiredCapabilities).map(s=>({rule:"separation-lost",severity:"warning",message:`role "${s.id}" holds every required capability (${t.requiredCapabilities.join(", ")}); no separation of duties`,fix:"fine for a solo operator; otherwise split capabilities across roles"}))}},{id:"conflict-of-interest",severity:"error",check(i,n){if(n.type!=="complete")return [];let s=n.payload?.handoff;if(!s)return [];let r=s.verdict??"done";if(r!=="done"&&r!=="needs-review")return [];let a=i.items[s.item];if(!a)return [];let l=i.stages.find(f=>f.id===a.stage)?.gate;if(!l||!l.requires.some(f=>f!=="human"))return [];if(A(i,a)!==s.role)return [];let c=w(i,l.id,void 0,s.role);return [{rule:"conflict-of-interest",severity:"error",item:a.id,message:`role "${s.role}" authored "${a.id}" and may not pass gate "${l.id}" on it`,fix:`hand the gate to another approver: ${c.join(", ")||"(register one)"}`}]}}]}function X(t,e,o){let i=[];for(let n of [..._(e),...e.rules])i.push(...n.check(t,o));return i}function Y(t,e,o,i){let n=X(t,e,o),s=n.filter(r=>r.severity==="error");return s.length&&chunkS3BXVHO7_cjs.b("rule-violation",`${o.type} violates ${s.map(r=>r.rule).join(", ")}: ${s.map(r=>r.message).join("; ")}`,s.map(r=>r.fix).join("; "),{rule:s[0].rule,items:s.map(r=>r.item).filter(r=>!!r)}),{state:i(t),warnings:n.filter(r=>r.severity==="warning")}}function Ee(t){let e=t.methodology,o=e.validateConfig(t.config),i=t.stages??e.defaultStages(o),n=t.roles??e.defaultRoles(o),s=t.now!==void 0?{methodology:e.name,config:o,stages:i,roles:n,now:t.now}:{methodology:e.name,config:o,stages:i,roles:n},r=C(s);return q(r,e.requiredCapabilities,e.exclusiveCapabilities),Y(r,e,{type:"init",by:t.by??"system"},a=>a)}exports.A=me;exports.B=R;exports.C=M;exports.D=z;exports.E=we;exports.F=Q;exports.G=A;exports.H=U;exports.I=be;exports.J=xe;exports.K=_;exports.L=X;exports.M=Y;exports.N=Ee;exports.a=k;exports.b=C;exports.c=g;exports.d=te;exports.e=j;exports.f=re;exports.g=se;exports.h=x;exports.i=O;exports.j=w;exports.k=ae;exports.l=q;exports.m=P;exports.n=G;exports.o=le;exports.p=I;exports.q=D;exports.r=K;exports.s=J;exports.t=L;exports.u=h;exports.v=ue;exports.w=ge;exports.x=S;exports.y=pe;exports.z=V;//# sourceMappingURL=chunk-JDVE7DQR.cjs.map
2
- //# sourceMappingURL=chunk-JDVE7DQR.cjs.map