@aperant/framework 0.17.0 → 0.19.0

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 (152) hide show
  1. package/CHANGELOG.md +152 -0
  2. package/dist/cli/commands/ci-watch.mjs +49 -2
  3. package/dist/cli/commands/ci-watch.mjs.map +1 -1
  4. package/dist/cli/commands/commit.mjs +3 -3
  5. package/dist/cli/commands/commit.mjs.map +1 -1
  6. package/dist/cli/commands/event.mjs +16 -16
  7. package/dist/cli/commands/event.mjs.map +1 -1
  8. package/dist/cli/commands/merge-integrate.mjs +3 -3
  9. package/dist/cli/commands/merge-integrate.mjs.map +1 -1
  10. package/dist/cli/commands/produce.d.mts +9 -0
  11. package/dist/cli/commands/produce.d.mts.map +1 -0
  12. package/dist/cli/commands/produce.mjs +1345 -0
  13. package/dist/cli/commands/produce.mjs.map +1 -0
  14. package/dist/cli/commands/task.d.mts +16 -1
  15. package/dist/cli/commands/task.d.mts.map +1 -1
  16. package/dist/cli/commands/task.mjs +434 -266
  17. package/dist/cli/commands/task.mjs.map +1 -1
  18. package/dist/cli/coordination/auto-emit-artifact-ready.mjs +5 -5
  19. package/dist/cli/coordination/auto-emit-artifact-ready.mjs.map +1 -1
  20. package/dist/cli/coordination/event-schema.d.mts +5 -3
  21. package/dist/cli/coordination/event-schema.d.mts.map +1 -1
  22. package/dist/cli/coordination/event-schema.mjs +245 -21
  23. package/dist/cli/coordination/event-schema.mjs.map +1 -1
  24. package/dist/cli/coordination/store.d.mts +2 -2
  25. package/dist/cli/coordination/store.mjs +4 -4
  26. package/dist/cli/coordination/store.mjs.map +1 -1
  27. package/dist/cli/dispatch.d.mts.map +1 -1
  28. package/dist/cli/dispatch.mjs +2 -0
  29. package/dist/cli/dispatch.mjs.map +1 -1
  30. package/dist/cli/help.d.mts.map +1 -1
  31. package/dist/cli/help.mjs +30 -0
  32. package/dist/cli/help.mjs.map +1 -1
  33. package/dist/cli/install/legacy-paths.d.mts.map +1 -1
  34. package/dist/cli/install/legacy-paths.mjs +2 -0
  35. package/dist/cli/install/legacy-paths.mjs.map +1 -1
  36. package/dist/cli/produce/blind-probe.d.mts +85 -0
  37. package/dist/cli/produce/blind-probe.d.mts.map +1 -0
  38. package/dist/cli/produce/blind-probe.mjs +217 -0
  39. package/dist/cli/produce/blind-probe.mjs.map +1 -0
  40. package/dist/cli/produce/claim.d.mts +188 -0
  41. package/dist/cli/produce/claim.d.mts.map +1 -0
  42. package/dist/cli/produce/claim.mjs +518 -0
  43. package/dist/cli/produce/claim.mjs.map +1 -0
  44. package/dist/cli/produce/done-gate.d.mts +87 -0
  45. package/dist/cli/produce/done-gate.d.mts.map +1 -0
  46. package/dist/cli/produce/done-gate.mjs +200 -0
  47. package/dist/cli/produce/done-gate.mjs.map +1 -0
  48. package/dist/cli/produce/events.d.mts +77 -0
  49. package/dist/cli/produce/events.d.mts.map +1 -0
  50. package/dist/cli/produce/events.mjs +126 -0
  51. package/dist/cli/produce/events.mjs.map +1 -0
  52. package/dist/cli/produce/evidence-oracle.d.mts +63 -0
  53. package/dist/cli/produce/evidence-oracle.d.mts.map +1 -0
  54. package/dist/cli/produce/evidence-oracle.mjs +122 -0
  55. package/dist/cli/produce/evidence-oracle.mjs.map +1 -0
  56. package/dist/cli/produce/ledger.d.mts +140 -0
  57. package/dist/cli/produce/ledger.d.mts.map +1 -0
  58. package/dist/cli/produce/ledger.mjs +272 -0
  59. package/dist/cli/produce/ledger.mjs.map +1 -0
  60. package/dist/cli/produce/probe-family.d.mts +53 -0
  61. package/dist/cli/produce/probe-family.d.mts.map +1 -0
  62. package/dist/cli/produce/probe-family.mjs +160 -0
  63. package/dist/cli/produce/probe-family.mjs.map +1 -0
  64. package/dist/cli/produce/projection.d.mts +55 -0
  65. package/dist/cli/produce/projection.d.mts.map +1 -0
  66. package/dist/cli/produce/projection.mjs +97 -0
  67. package/dist/cli/produce/projection.mjs.map +1 -0
  68. package/dist/cli/produce/run-id.d.mts +42 -0
  69. package/dist/cli/produce/run-id.d.mts.map +1 -0
  70. package/dist/cli/produce/run-id.mjs +79 -0
  71. package/dist/cli/produce/run-id.mjs.map +1 -0
  72. package/dist/cli/produce/saga.d.mts +180 -0
  73. package/dist/cli/produce/saga.d.mts.map +1 -0
  74. package/dist/cli/produce/saga.mjs +290 -0
  75. package/dist/cli/produce/saga.mjs.map +1 -0
  76. package/dist/cli/produce/scheduler.d.mts +165 -0
  77. package/dist/cli/produce/scheduler.d.mts.map +1 -0
  78. package/dist/cli/produce/scheduler.mjs +399 -0
  79. package/dist/cli/produce/scheduler.mjs.map +1 -0
  80. package/dist/cli/produce/setpoint.d.mts +52 -0
  81. package/dist/cli/produce/setpoint.d.mts.map +1 -0
  82. package/dist/cli/produce/setpoint.mjs +113 -0
  83. package/dist/cli/produce/setpoint.mjs.map +1 -0
  84. package/dist/cli/produce/verification-ttl.d.mts +75 -0
  85. package/dist/cli/produce/verification-ttl.d.mts.map +1 -0
  86. package/dist/cli/produce/verification-ttl.mjs +169 -0
  87. package/dist/cli/produce/verification-ttl.mjs.map +1 -0
  88. package/dist/cli/roadmap/{conductor-view.d.mts → showrunner-view.d.mts} +3 -3
  89. package/dist/cli/roadmap/showrunner-view.d.mts.map +1 -0
  90. package/dist/cli/roadmap/{conductor-view.mjs → showrunner-view.mjs} +7 -7
  91. package/dist/cli/roadmap/showrunner-view.mjs.map +1 -0
  92. package/dist/plugin/.claude-plugin/plugin.json +2 -1
  93. package/dist/plugin/skills/apt/SKILL.md +112 -38
  94. package/dist/plugin/skills/apt-debug/SKILL.md +14 -24
  95. package/dist/plugin/skills/apt-fan-out/SKILL.md +4 -4
  96. package/dist/plugin/skills/apt-handoff/SKILL.md +1 -1
  97. package/dist/plugin/skills/apt-plan/SKILL.md +5 -5
  98. package/dist/plugin/skills/apt-plan/adapters/{conductor.md → showrunner.md} +16 -16
  99. package/dist/plugin/skills/apt-produce/SKILL.md +606 -0
  100. package/dist/plugin/skills/apt-quick/SKILL.md +14 -22
  101. package/dist/plugin/skills/apt-run/SKILL.md +126 -3
  102. package/dist/plugin/skills/apt-ship/SKILL.md +2 -0
  103. package/dist/plugin/skills/apt-spar/SKILL.md +5 -3
  104. package/dist/plugin/skills/apt-watch-ci/SKILL.md +4 -4
  105. package/package.json +4 -4
  106. package/prompts/{conductor-framework-context.md → showrunner-framework-context.md} +1 -1
  107. package/prompts/{conductor-status-check.md → showrunner-status-check.md} +1 -1
  108. package/prompts/{conductor-sub-agent.md → showrunner-sub-agent.md} +6 -6
  109. package/prompts/{conductor-system.md → showrunner-system.md} +8 -8
  110. package/skills/apt/SKILL.md +112 -38
  111. package/skills/apt-debug/SKILL.md +14 -24
  112. package/skills/apt-fan-out/SKILL.md +4 -4
  113. package/skills/apt-handoff/SKILL.md +1 -1
  114. package/skills/apt-plan/SKILL.md +5 -5
  115. package/skills/apt-plan/adapters/{conductor.md → showrunner.md} +16 -16
  116. package/skills/apt-produce/SKILL.md +606 -0
  117. package/skills/apt-quick/SKILL.md +14 -22
  118. package/skills/apt-run/SKILL.md +126 -3
  119. package/skills/apt-ship/SKILL.md +2 -0
  120. package/skills/apt-spar/SKILL.md +5 -3
  121. package/skills/apt-watch-ci/SKILL.md +4 -4
  122. package/src/cli/commands/ci-watch.mjs +51 -2
  123. package/src/cli/commands/commit.mjs +3 -3
  124. package/src/cli/commands/event.mjs +16 -16
  125. package/src/cli/commands/merge-integrate.mjs +3 -3
  126. package/src/cli/commands/produce.mjs +1466 -0
  127. package/src/cli/commands/task.mjs +482 -285
  128. package/src/cli/coordination/auto-emit-artifact-ready.mjs +5 -5
  129. package/src/cli/coordination/event-schema.d.ts +4 -2
  130. package/src/cli/coordination/event-schema.mjs +276 -21
  131. package/src/cli/coordination/store.mjs +4 -4
  132. package/src/cli/dispatch.mjs +2 -0
  133. package/src/cli/help.mjs +30 -0
  134. package/src/cli/install/legacy-paths.mjs +2 -0
  135. package/src/cli/produce/blind-probe.mjs +245 -0
  136. package/src/cli/produce/claim.mjs +543 -0
  137. package/src/cli/produce/done-gate.mjs +238 -0
  138. package/src/cli/produce/events.mjs +131 -0
  139. package/src/cli/produce/evidence-oracle.mjs +133 -0
  140. package/src/cli/produce/ledger.mjs +284 -0
  141. package/src/cli/produce/probe-family.mjs +168 -0
  142. package/src/cli/produce/projection.mjs +105 -0
  143. package/src/cli/produce/run-id.mjs +84 -0
  144. package/src/cli/produce/saga.mjs +303 -0
  145. package/src/cli/produce/scheduler.mjs +423 -0
  146. package/src/cli/produce/setpoint.mjs +122 -0
  147. package/src/cli/produce/verification-ttl.mjs +191 -0
  148. package/src/cli/roadmap/showrunner-view.d.ts +10 -0
  149. package/src/cli/roadmap/{conductor-view.mjs → showrunner-view.mjs} +6 -6
  150. package/dist/cli/roadmap/conductor-view.d.mts.map +0 -1
  151. package/dist/cli/roadmap/conductor-view.mjs.map +0 -1
  152. package/src/cli/roadmap/conductor-view.d.ts +0 -10
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Fold the run's acceptance-criteria ledger from the append-only log. A
3
+ * `produce.criteria.declared` seeds an OPEN criterion (in first-seen order); a
4
+ * `produce.criteria.closed` advances it to `closed`; `produce.scope.changed` is
5
+ * recorded on the scope-change trail. Deterministic — two folds of the same log
6
+ * yield the identical ledger; `updated_at` is the last folded event's timestamp.
7
+ *
8
+ * @param {string} projectDir
9
+ * @param {string} runId
10
+ * @returns {{
11
+ * run_id: string,
12
+ * criteria: Array<{ criterion_id: string, milestone_id: string | null, text: string, status: 'open' | 'closed' }>,
13
+ * open_count: number,
14
+ * closed_count: number,
15
+ * scope_changes: Array<{ reason: string, milestone_id: string | null, t: string | null }>,
16
+ * updated_at: string | null,
17
+ * }}
18
+ */
19
+ export function projectLedger(projectDir: string, runId: string): {
20
+ run_id: string;
21
+ criteria: Array<{
22
+ criterion_id: string;
23
+ milestone_id: string | null;
24
+ text: string;
25
+ status: "open" | "closed";
26
+ }>;
27
+ open_count: number;
28
+ closed_count: number;
29
+ scope_changes: Array<{
30
+ reason: string;
31
+ milestone_id: string | null;
32
+ t: string | null;
33
+ }>;
34
+ updated_at: string | null;
35
+ };
36
+ /**
37
+ * V = open acceptance criteria = declared − closed, folded from the log.
38
+ *
39
+ * @param {string} projectDir
40
+ * @param {string} runId
41
+ * @returns {number}
42
+ */
43
+ export function computeOpenCriteria(projectDir: string, runId: string): number;
44
+ /**
45
+ * The write-time anti-windup guard (ID-M3-03). `declare-criteria` raises V; it is
46
+ * REJECTED when the run is IN DESCENT (≥1 `produce.criteria.closed` already
47
+ * logged) AND no scope-change is supplied. Before descent, declaration is FREE
48
+ * (the baseline scope-definition window). This is the ONLY hard Lyapunov
49
+ * enforcement and it is fail-closed.
50
+ *
51
+ * @param {string} projectDir
52
+ * @param {string} runId
53
+ * @param {{ hasScopeChange?: boolean }} [opts]
54
+ * @returns {{ allowed: boolean, reason: string, in_descent: boolean }}
55
+ */
56
+ export function evaluateDeclareGuard(projectDir: string, runId: string, { hasScopeChange }?: {
57
+ hasScopeChange?: boolean;
58
+ }): {
59
+ allowed: boolean;
60
+ reason: string;
61
+ in_descent: boolean;
62
+ };
63
+ /**
64
+ * Compute the monotone-progress report (ID-M3-02 / AC6). Folds ALL of
65
+ * `produce.criteria.*` + `produce.scope.changed` +
66
+ * `produce.roadmap.milestone_status_changed` in append order, maintaining the
67
+ * running V. Every `milestone_status_changed{status:'done'}` is a checkpoint: it
68
+ * captures `v_before`, then consumes the contiguous run of `criteria.closed`
69
+ * events that ship triggered (appended immediately after the done event) to
70
+ * capture `v_after`. `delta = v_after − v_before` (≤ 0). The series is STRICTLY
71
+ * DECREASING iff every ship checkpoint had `delta < 0`; a flat ship (delta === 0)
72
+ * is a soft STALL, not a hard violation.
73
+ *
74
+ * Read-time defense (ID-M3-03): a `produce.criteria.declared` that raised V
75
+ * AFTER descent WITHOUT an accompanying (immediately-preceding)
76
+ * `produce.scope.changed` is a HARD `lyapunov_violation`. `progress` FLAGS — it
77
+ * never rejects (the rejection is the write-time guard).
78
+ *
79
+ * @param {string} projectDir
80
+ * @param {string} runId
81
+ * @returns {{
82
+ * run_id: string,
83
+ * v_open_now: number,
84
+ * v_series: Array<{ milestone_id: string | null, v_before: number, v_after: number, delta: number, decreased: boolean }>,
85
+ * strictly_decreasing: boolean,
86
+ * lyapunov_ok: boolean,
87
+ * lyapunov_violations: Array<{ criterion_id: string | null, milestone_id: string | null, reason: string, t: string | null }>,
88
+ * scope_changes: Array<{ reason: string, milestone_id: string | null, t: string | null }>,
89
+ * }}
90
+ */
91
+ export function computeProgress(projectDir: string, runId: string): {
92
+ run_id: string;
93
+ v_open_now: number;
94
+ v_series: Array<{
95
+ milestone_id: string | null;
96
+ v_before: number;
97
+ v_after: number;
98
+ delta: number;
99
+ decreased: boolean;
100
+ }>;
101
+ strictly_decreasing: boolean;
102
+ lyapunov_ok: boolean;
103
+ lyapunov_violations: Array<{
104
+ criterion_id: string | null;
105
+ milestone_id: string | null;
106
+ reason: string;
107
+ t: string | null;
108
+ }>;
109
+ scope_changes: Array<{
110
+ reason: string;
111
+ milestone_id: string | null;
112
+ t: string | null;
113
+ }>;
114
+ };
115
+ /**
116
+ * Close a shipped milestone's still-open acceptance criteria — the CONSEQUENCE
117
+ * of a milestone flipping to `done` (ID-M3-05, wired additively in
118
+ * `cmdSetMilestoneDone`). Folds the ledger, finds the milestone's OPEN criteria,
119
+ * and appends one `produce.criteria.closed` per criterion so V strictly
120
+ * decreases. Idempotent — an already-closed (or criteria-less) milestone appends
121
+ * nothing (`closed_count: 0`), so a criteria-less ship is byte-identical to M2.
122
+ *
123
+ * @param {string} projectDir
124
+ * @param {string} runId
125
+ * @param {string} milestoneId
126
+ * @returns {{ closed_count: number, criterion_ids: string[] }}
127
+ */
128
+ export function closeCriteriaForShippedMilestone(projectDir: string, runId: string, milestoneId: string): {
129
+ closed_count: number;
130
+ criterion_ids: string[];
131
+ };
132
+ /**
133
+ * Mint a criterion id: `<slug-of-text>-<rand>`. Reuses `slugFromOutcome` and a
134
+ * short random suffix so two criteria minted from the same text never collide.
135
+ *
136
+ * @param {string} text
137
+ * @returns {string}
138
+ */
139
+ export function mintCriterionId(text: string): string;
140
+ //# sourceMappingURL=ledger.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.d.mts","sourceRoot":"","sources":["../../../src/cli/produce/ledger.mjs"],"names":[],"mappings":"AAyBA;;;;;;;;;;;;;;;;;GAiBG;AACH,0CAXW,MAAM,SACN,MAAM,GACJ;IACR,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,KAAK,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAA;KAAE,CAAC,CAAC;IAChH,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IACxF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CA4CH;AAED;;;;;;GAMG;AACH,gDAJW,MAAM,SACN,MAAM,GACJ,MAAM,CAIlB;AAED;;;;;;;;;;;GAWG;AACH,iDALW,MAAM,SACN,MAAM,uBACN;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,GAC1B;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAerE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,4CAZW,MAAM,SACN,MAAM,GACJ;IACR,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,KAAK,CAAC;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACvH,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,mBAAmB,EAAE,KAAK,CAAC;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC3H,aAAa,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CACzF,CA6FH;AAED;;;;;;;;;;;;GAYG;AACH,6DALW,MAAM,SACN,MAAM,eACN,MAAM,GACJ;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAE,CAe7D;AAED;;;;;;GAMG;AACH,sCAHW,MAAM,GACJ,MAAM,CAIlB"}
@@ -0,0 +1,272 @@
1
+ /**
2
+ * @internal
3
+ * produce/ledger.mjs — the acceptance-criteria ledger + Lyapunov math (M3).
4
+ *
5
+ * V (the Lyapunov function) = open acceptance criteria = declared − closed,
6
+ * folded from the append-only log on every read (ID-M3-01 / ID-M3-02). Each
7
+ * criterion is a `produce.criteria.declared` event bound to a milestone; it
8
+ * closes via `produce.criteria.closed`. `produce.scope.changed` is the ONLY
9
+ * sanctioned way V may rise after the run has begun closing criteria (ID-M3-04).
10
+ *
11
+ * This module is a SEPARATE fold parallel to `projection.mjs` — the roadmap
12
+ * projection still folds `produce.roadmap.*` ONLY, so criteria/scope events
13
+ * never enter the roadmap the Showrunner re-reads (the M1/M2 blindness invariant
14
+ * is preserved). It reads ONLY the event log through `events.mjs` (no `node:`
15
+ * beyond what events.mjs already uses) and holds NO model id (it is pure math).
16
+ */
17
+ import { appendProduceEvent, readProduceRunEventsMatching } from './events.mjs';
18
+ import { slugFromOutcome } from './run-id.mjs';
19
+ /** The op-match for the criteria + scope ledger fold (never `produce.roadmap.*`). */
20
+ function isLedgerOp(op) {
21
+ return op.startsWith('produce.criteria.') || op === 'produce.scope.changed';
22
+ }
23
+ /**
24
+ * Fold the run's acceptance-criteria ledger from the append-only log. A
25
+ * `produce.criteria.declared` seeds an OPEN criterion (in first-seen order); a
26
+ * `produce.criteria.closed` advances it to `closed`; `produce.scope.changed` is
27
+ * recorded on the scope-change trail. Deterministic — two folds of the same log
28
+ * yield the identical ledger; `updated_at` is the last folded event's timestamp.
29
+ *
30
+ * @param {string} projectDir
31
+ * @param {string} runId
32
+ * @returns {{
33
+ * run_id: string,
34
+ * criteria: Array<{ criterion_id: string, milestone_id: string | null, text: string, status: 'open' | 'closed' }>,
35
+ * open_count: number,
36
+ * closed_count: number,
37
+ * scope_changes: Array<{ reason: string, milestone_id: string | null, t: string | null }>,
38
+ * updated_at: string | null,
39
+ * }}
40
+ */
41
+ export function projectLedger(projectDir, runId) {
42
+ const events = readProduceRunEventsMatching(projectDir, runId, isLedgerOp);
43
+ const byId = new Map();
44
+ const order = [];
45
+ const scopeChanges = [];
46
+ let updatedAt = null;
47
+ for (const e of events) {
48
+ const d = e.data || {};
49
+ if (e.op === 'produce.criteria.declared') {
50
+ const cid = typeof d.criterion_id === 'string' ? d.criterion_id : null;
51
+ if (cid && !byId.has(cid)) {
52
+ byId.set(cid, {
53
+ criterion_id: cid,
54
+ milestone_id: typeof d.milestone_id === 'string' ? d.milestone_id : null,
55
+ text: typeof d.text === 'string' ? d.text : '',
56
+ status: 'open',
57
+ });
58
+ order.push(cid);
59
+ }
60
+ }
61
+ else if (e.op === 'produce.criteria.closed') {
62
+ const cid = typeof d.criterion_id === 'string' ? d.criterion_id : null;
63
+ if (cid && byId.has(cid))
64
+ byId.get(cid).status = 'closed';
65
+ }
66
+ else if (e.op === 'produce.scope.changed') {
67
+ scopeChanges.push({
68
+ reason: typeof d.reason === 'string' ? d.reason : '',
69
+ milestone_id: typeof d.milestone_id === 'string' ? d.milestone_id : null,
70
+ t: typeof e.t === 'string' ? e.t : null,
71
+ });
72
+ }
73
+ if (typeof e.t === 'string')
74
+ updatedAt = e.t;
75
+ }
76
+ const criteria = order.map((cid) => byId.get(cid));
77
+ return {
78
+ run_id: runId,
79
+ criteria,
80
+ open_count: criteria.filter((c) => c.status === 'open').length,
81
+ closed_count: criteria.filter((c) => c.status === 'closed').length,
82
+ scope_changes: scopeChanges,
83
+ updated_at: updatedAt,
84
+ };
85
+ }
86
+ /**
87
+ * V = open acceptance criteria = declared − closed, folded from the log.
88
+ *
89
+ * @param {string} projectDir
90
+ * @param {string} runId
91
+ * @returns {number}
92
+ */
93
+ export function computeOpenCriteria(projectDir, runId) {
94
+ return projectLedger(projectDir, runId).open_count;
95
+ }
96
+ /**
97
+ * The write-time anti-windup guard (ID-M3-03). `declare-criteria` raises V; it is
98
+ * REJECTED when the run is IN DESCENT (≥1 `produce.criteria.closed` already
99
+ * logged) AND no scope-change is supplied. Before descent, declaration is FREE
100
+ * (the baseline scope-definition window). This is the ONLY hard Lyapunov
101
+ * enforcement and it is fail-closed.
102
+ *
103
+ * @param {string} projectDir
104
+ * @param {string} runId
105
+ * @param {{ hasScopeChange?: boolean }} [opts]
106
+ * @returns {{ allowed: boolean, reason: string, in_descent: boolean }}
107
+ */
108
+ export function evaluateDeclareGuard(projectDir, runId, { hasScopeChange = false } = {}) {
109
+ const closes = readProduceRunEventsMatching(projectDir, runId, (op) => op === 'produce.criteria.closed');
110
+ const inDescent = closes.length > 0;
111
+ const allowed = !inDescent || !!hasScopeChange;
112
+ let reason;
113
+ if (!allowed)
114
+ reason = 'in_descent_no_scope_change';
115
+ else if (!inDescent)
116
+ reason = 'baseline_window';
117
+ else
118
+ reason = 'scope_change';
119
+ return { allowed, reason, in_descent: inDescent };
120
+ }
121
+ /**
122
+ * Compute the monotone-progress report (ID-M3-02 / AC6). Folds ALL of
123
+ * `produce.criteria.*` + `produce.scope.changed` +
124
+ * `produce.roadmap.milestone_status_changed` in append order, maintaining the
125
+ * running V. Every `milestone_status_changed{status:'done'}` is a checkpoint: it
126
+ * captures `v_before`, then consumes the contiguous run of `criteria.closed`
127
+ * events that ship triggered (appended immediately after the done event) to
128
+ * capture `v_after`. `delta = v_after − v_before` (≤ 0). The series is STRICTLY
129
+ * DECREASING iff every ship checkpoint had `delta < 0`; a flat ship (delta === 0)
130
+ * is a soft STALL, not a hard violation.
131
+ *
132
+ * Read-time defense (ID-M3-03): a `produce.criteria.declared` that raised V
133
+ * AFTER descent WITHOUT an accompanying (immediately-preceding)
134
+ * `produce.scope.changed` is a HARD `lyapunov_violation`. `progress` FLAGS — it
135
+ * never rejects (the rejection is the write-time guard).
136
+ *
137
+ * @param {string} projectDir
138
+ * @param {string} runId
139
+ * @returns {{
140
+ * run_id: string,
141
+ * v_open_now: number,
142
+ * v_series: Array<{ milestone_id: string | null, v_before: number, v_after: number, delta: number, decreased: boolean }>,
143
+ * strictly_decreasing: boolean,
144
+ * lyapunov_ok: boolean,
145
+ * lyapunov_violations: Array<{ criterion_id: string | null, milestone_id: string | null, reason: string, t: string | null }>,
146
+ * scope_changes: Array<{ reason: string, milestone_id: string | null, t: string | null }>,
147
+ * }}
148
+ */
149
+ export function computeProgress(projectDir, runId) {
150
+ const events = readProduceRunEventsMatching(projectDir, runId, (op) => op.startsWith('produce.criteria.') ||
151
+ op === 'produce.scope.changed' ||
152
+ op === 'produce.roadmap.milestone_status_changed');
153
+ const status = new Map();
154
+ let v = 0;
155
+ let inDescent = false;
156
+ const vSeries = [];
157
+ const violations = [];
158
+ const scopeChanges = [];
159
+ /** Apply a `criteria.closed` payload to V (idempotent; sets inDescent). */
160
+ const applyClose = (d) => {
161
+ const cid = typeof d.criterion_id === 'string' ? d.criterion_id : null;
162
+ if (cid && status.get(cid) === 'open') {
163
+ status.set(cid, 'closed');
164
+ v -= 1;
165
+ }
166
+ else if (cid && !status.has(cid)) {
167
+ // Close of a never-declared criterion — record the state, no V change.
168
+ status.set(cid, 'closed');
169
+ }
170
+ inDescent = true;
171
+ };
172
+ for (let i = 0; i < events.length; i++) {
173
+ const e = events[i];
174
+ const d = e.data || {};
175
+ if (e.op === 'produce.criteria.declared') {
176
+ if (inDescent) {
177
+ const prev = i > 0 ? events[i - 1] : null;
178
+ const accompanied = !!prev && prev.op === 'produce.scope.changed';
179
+ if (!accompanied) {
180
+ violations.push({
181
+ criterion_id: typeof d.criterion_id === 'string' ? d.criterion_id : null,
182
+ milestone_id: typeof d.milestone_id === 'string' ? d.milestone_id : null,
183
+ reason: 'declared_after_descent_without_scope_change',
184
+ t: typeof e.t === 'string' ? e.t : null,
185
+ });
186
+ }
187
+ }
188
+ const cid = typeof d.criterion_id === 'string' ? d.criterion_id : null;
189
+ if (cid && !status.has(cid)) {
190
+ status.set(cid, 'open');
191
+ v += 1;
192
+ }
193
+ }
194
+ else if (e.op === 'produce.criteria.closed') {
195
+ applyClose(d);
196
+ }
197
+ else if (e.op === 'produce.scope.changed') {
198
+ scopeChanges.push({
199
+ reason: typeof d.reason === 'string' ? d.reason : '',
200
+ milestone_id: typeof d.milestone_id === 'string' ? d.milestone_id : null,
201
+ t: typeof e.t === 'string' ? e.t : null,
202
+ });
203
+ }
204
+ else if (e.op === 'produce.roadmap.milestone_status_changed' && d.status === 'done') {
205
+ const vBefore = v;
206
+ // Consume the contiguous run of criteria.closed this ship triggered
207
+ // (the real flow appends them immediately after the done event).
208
+ let j = i + 1;
209
+ while (j < events.length && events[j].op === 'produce.criteria.closed') {
210
+ applyClose(events[j].data || {});
211
+ j++;
212
+ }
213
+ i = j - 1;
214
+ const vAfter = v;
215
+ const delta = vAfter - vBefore;
216
+ vSeries.push({
217
+ milestone_id: typeof d.milestone_id === 'string' ? d.milestone_id : null,
218
+ v_before: vBefore,
219
+ v_after: vAfter,
220
+ delta,
221
+ decreased: delta < 0,
222
+ });
223
+ }
224
+ }
225
+ return {
226
+ run_id: runId,
227
+ v_open_now: v,
228
+ v_series: vSeries,
229
+ strictly_decreasing: vSeries.every((s) => s.delta < 0),
230
+ lyapunov_ok: violations.length === 0,
231
+ lyapunov_violations: violations,
232
+ scope_changes: scopeChanges,
233
+ };
234
+ }
235
+ /**
236
+ * Close a shipped milestone's still-open acceptance criteria — the CONSEQUENCE
237
+ * of a milestone flipping to `done` (ID-M3-05, wired additively in
238
+ * `cmdSetMilestoneDone`). Folds the ledger, finds the milestone's OPEN criteria,
239
+ * and appends one `produce.criteria.closed` per criterion so V strictly
240
+ * decreases. Idempotent — an already-closed (or criteria-less) milestone appends
241
+ * nothing (`closed_count: 0`), so a criteria-less ship is byte-identical to M2.
242
+ *
243
+ * @param {string} projectDir
244
+ * @param {string} runId
245
+ * @param {string} milestoneId
246
+ * @returns {{ closed_count: number, criterion_ids: string[] }}
247
+ */
248
+ export function closeCriteriaForShippedMilestone(projectDir, runId, milestoneId) {
249
+ const ledger = projectLedger(projectDir, runId);
250
+ const open = ledger.criteria.filter((c) => c.milestone_id === milestoneId && c.status === 'open');
251
+ const criterionIds = [];
252
+ for (const c of open) {
253
+ appendProduceEvent(projectDir, 'produce.criteria.closed', {
254
+ run_id: runId,
255
+ milestone_id: milestoneId,
256
+ criterion_id: c.criterion_id,
257
+ });
258
+ criterionIds.push(c.criterion_id);
259
+ }
260
+ return { closed_count: criterionIds.length, criterion_ids: criterionIds };
261
+ }
262
+ /**
263
+ * Mint a criterion id: `<slug-of-text>-<rand>`. Reuses `slugFromOutcome` and a
264
+ * short random suffix so two criteria minted from the same text never collide.
265
+ *
266
+ * @param {string} text
267
+ * @returns {string}
268
+ */
269
+ export function mintCriterionId(text) {
270
+ return `${slugFromOutcome(text)}-${Math.random().toString(36).slice(2, 8)}`;
271
+ }
272
+ //# sourceMappingURL=ledger.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.mjs","sourceRoot":"","sources":["../../../src/cli/produce/ledger.mjs"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Normalize a provider/CLI family alias to one of KNOWN_FAMILIES. Returns null
3
+ * for an unrecognized family so it is excluded from selection (fail-closed).
4
+ *
5
+ * @param {unknown} family
6
+ * @returns {string | null}
7
+ */
8
+ export function normalizeFamily(family: unknown): string | null;
9
+ /**
10
+ * Resolve the probe/oracle family + LIVE model for a produce run.
11
+ *
12
+ * @param {{
13
+ * implementerFamily: string,
14
+ * hostFamily?: string | null,
15
+ * availableFamilies?: string[],
16
+ * codexDeps?: { home: string, existsSync: (p: string) => boolean, readFileSync: (p: string, enc: string) => string },
17
+ * }} args
18
+ * @returns {{
19
+ * available: boolean,
20
+ * probe_family: string | null,
21
+ * transport_hint: { prefer_self_resolving: boolean, recommended_walk: string[] } | null,
22
+ * model: string | null,
23
+ * source: string | null,
24
+ * reason: string | null,
25
+ * }}
26
+ */
27
+ export function resolveProbeFamily({ implementerFamily, hostFamily, availableFamilies, codexDeps, }?: {
28
+ implementerFamily: string;
29
+ hostFamily?: string | null;
30
+ availableFamilies?: string[];
31
+ codexDeps?: {
32
+ home: string;
33
+ existsSync: (p: string) => boolean;
34
+ readFileSync: (p: string, enc: string) => string;
35
+ };
36
+ }): {
37
+ available: boolean;
38
+ probe_family: string | null;
39
+ transport_hint: {
40
+ prefer_self_resolving: boolean;
41
+ recommended_walk: string[];
42
+ } | null;
43
+ model: string | null;
44
+ source: string | null;
45
+ reason: string | null;
46
+ };
47
+ /**
48
+ * The provider families M2 selects across — the same three apt:spar supports.
49
+ * Ordered for a deterministic tiebreak when the partner-map default is not in
50
+ * the available pool.
51
+ */
52
+ export const KNOWN_FAMILIES: string[];
53
+ //# sourceMappingURL=probe-family.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe-family.d.mts","sourceRoot":"","sources":["../../../src/cli/produce/probe-family.mjs"],"names":[],"mappings":"AA6CA;;;;;;GAMG;AACH,wCAHW,OAAO,GACL,MAAM,GAAG,IAAI,CASzB;AA8BD;;;;;;;;;;;;;;;;;GAiBG;AACH,sGAfW;IACN,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;QAAC,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;KAAE,CAAC;CACpH,GACS;IACR,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE;QAAE,qBAAqB,EAAE,OAAO,CAAC;QAAC,gBAAgB,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IACtF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CA8DH;AA5ID;;;;GAIG;AACH,sCAA2D"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * @internal
3
+ * produce/probe-family.mjs — resolve a probe/oracle provider family DISJOINT
4
+ * from the implementer's, reusing the apt-spar cross-family mechanism (M2,
5
+ * ID-M2-01).
6
+ *
7
+ * The framework already selects + dispatches a different provider family in
8
+ * `apt:spar`: `host-detect` returns the current family, a partner-map picks a
9
+ * different one, and `spar-resolve-partner` resolves the partner's model LIVE
10
+ * from the user's own CLI config with ZERO baked default. M2 does NOT invent a
11
+ * second cross-family path — this module consumes the SAME `resolveCodexSparModel`
12
+ * primitive + the same partner-map shape and adds exactly ONE new rule: the
13
+ * resolved probe/oracle family must be DISJOINT from the *implementer* family
14
+ * (not merely "≠ host").
15
+ *
16
+ * Provider is an OUTPUT of selection; Aperant NEVER holds a model id (CLAUDE.md
17
+ * resolver rule / ID-M2-07). For codex the live model comes from
18
+ * `~/.codex/config.toml`; claude / gemini self-resolve (`model: null`). A
19
+ * setup where no disjoint family exists (host = implementer = only CLI) returns
20
+ * `available: false` fail-closed — a single-family setup cannot satisfy the
21
+ * cross-family rule by design.
22
+ */
23
+ import { existsSync, readFileSync } from 'node:fs';
24
+ import { homedir } from 'node:os';
25
+ import { resolveCodexSparModel } from '../host/codex-config.mjs';
26
+ /**
27
+ * The provider families M2 selects across — the same three apt:spar supports.
28
+ * Ordered for a deterministic tiebreak when the partner-map default is not in
29
+ * the available pool.
30
+ */
31
+ export const KNOWN_FAMILIES = ['claude', 'codex', 'gemini'];
32
+ /**
33
+ * The apt-spar §3 partner-map shape: each family's natural cross-family partner.
34
+ * Consulted as a PREFERENCE only — the disjoint + available + non-host rules
35
+ * decide the final pick; this just orders ties the way spar does.
36
+ */
37
+ const PARTNER_MAP = {
38
+ claude: 'codex',
39
+ codex: 'claude',
40
+ gemini: 'codex',
41
+ };
42
+ /**
43
+ * Normalize a provider/CLI family alias to one of KNOWN_FAMILIES. Returns null
44
+ * for an unrecognized family so it is excluded from selection (fail-closed).
45
+ *
46
+ * @param {unknown} family
47
+ * @returns {string | null}
48
+ */
49
+ export function normalizeFamily(family) {
50
+ if (typeof family !== 'string')
51
+ return null;
52
+ const f = family.trim().toLowerCase();
53
+ if (f === 'claude' || f === 'claude-code' || f === 'anthropic')
54
+ return 'claude';
55
+ if (f === 'codex' || f === 'openai' || f === 'gpt')
56
+ return 'codex';
57
+ if (f === 'gemini' || f === 'google')
58
+ return 'gemini';
59
+ return null;
60
+ }
61
+ /**
62
+ * Pick the probe/oracle family: disjoint from the implementer (the ONE new
63
+ * rule), preferring a family that is ALSO not the host (a genuine third party),
64
+ * then honoring the apt-spar partner-map default, then a deterministic order.
65
+ *
66
+ * @param {string} implementerFamily — normalized implementer family.
67
+ * @param {string | null} hostFamily — normalized host family (or null).
68
+ * @param {string[]} normalizedAvailable — normalized available families.
69
+ * @returns {{ family: string | null, reason: string | null }}
70
+ */
71
+ function pickProbeFamily(implementerFamily, hostFamily, normalizedAvailable) {
72
+ // The ONE new constraint over apt-spar: DISJOINT from the implementer.
73
+ const disjoint = normalizedAvailable.filter((f) => f !== implementerFamily);
74
+ if (disjoint.length === 0)
75
+ return { family: null, reason: 'no_disjoint_family' };
76
+ // Prefer a disjoint family that is ALSO not the host — a genuine third-party
77
+ // reviewer. Fall back to a disjoint-but-host family only if that is all
78
+ // that's available (still satisfies the cross-family rule, which is about the
79
+ // implementer, not the host).
80
+ const nonHost = disjoint.filter((f) => f !== hostFamily);
81
+ const pool = nonHost.length > 0 ? nonHost : disjoint;
82
+ // Honor the partner-map default when it is in the pool (apt-spar §3 shape).
83
+ const preferred = PARTNER_MAP[implementerFamily];
84
+ if (preferred && pool.includes(preferred))
85
+ return { family: preferred, reason: null };
86
+ // Deterministic tiebreak: first in KNOWN_FAMILIES order present in the pool.
87
+ const ordered = KNOWN_FAMILIES.filter((f) => pool.includes(f));
88
+ return { family: ordered[0] ?? pool[0], reason: null };
89
+ }
90
+ /**
91
+ * Resolve the probe/oracle family + LIVE model for a produce run.
92
+ *
93
+ * @param {{
94
+ * implementerFamily: string,
95
+ * hostFamily?: string | null,
96
+ * availableFamilies?: string[],
97
+ * codexDeps?: { home: string, existsSync: (p: string) => boolean, readFileSync: (p: string, enc: string) => string },
98
+ * }} args
99
+ * @returns {{
100
+ * available: boolean,
101
+ * probe_family: string | null,
102
+ * transport_hint: { prefer_self_resolving: boolean, recommended_walk: string[] } | null,
103
+ * model: string | null,
104
+ * source: string | null,
105
+ * reason: string | null,
106
+ * }}
107
+ */
108
+ export function resolveProbeFamily({ implementerFamily, hostFamily = null, availableFamilies = KNOWN_FAMILIES, codexDeps, } = {}) {
109
+ const implementer = normalizeFamily(implementerFamily);
110
+ if (!implementer) {
111
+ return {
112
+ available: false,
113
+ probe_family: null,
114
+ transport_hint: null,
115
+ model: null,
116
+ source: null,
117
+ reason: 'unknown_implementer_family',
118
+ };
119
+ }
120
+ const host = normalizeFamily(hostFamily);
121
+ const normalizedAvailable = [...new Set(availableFamilies.map(normalizeFamily).filter(Boolean))];
122
+ const { family, reason } = pickProbeFamily(implementer, host, normalizedAvailable);
123
+ if (!family) {
124
+ return {
125
+ available: false,
126
+ probe_family: null,
127
+ transport_hint: null,
128
+ model: null,
129
+ source: null,
130
+ reason: reason ?? 'no_disjoint_family',
131
+ };
132
+ }
133
+ // Resolve the model LIVE via the reused spar primitive (provider-is-output,
134
+ // zero baked id). codex reads ~/.codex/config.toml; claude/gemini self-resolve.
135
+ if (family === 'codex') {
136
+ const deps = codexDeps ?? { home: homedir(), existsSync, readFileSync };
137
+ const res = resolveCodexSparModel(deps);
138
+ return {
139
+ available: true,
140
+ probe_family: 'codex',
141
+ transport_hint: { prefer_self_resolving: true, recommended_walk: ['plugin', 'cli', 'mcp'] },
142
+ model: res.model,
143
+ source: res.source,
144
+ // A codex model that fails to resolve from config does NOT make the
145
+ // probe family unavailable — the self-resolving plugin/CLI rungs still
146
+ // run without a named model. Surface the model-resolution reason.
147
+ reason: res.reason,
148
+ };
149
+ }
150
+ // claude / gemini MCP rungs take only {prompt} — no model, no staleness vector.
151
+ return {
152
+ available: true,
153
+ probe_family: family,
154
+ transport_hint: { prefer_self_resolving: false, recommended_walk: ['mcp', 'plugin', 'cli'] },
155
+ model: null,
156
+ source: null,
157
+ reason: 'self_resolving',
158
+ };
159
+ }
160
+ //# sourceMappingURL=probe-family.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe-family.mjs","sourceRoot":"","sources":["../../../src/cli/produce/probe-family.mjs"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAA;AAEhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAA;AAE3D;;;;GAIG;AACH,MAAM,WAAW,GAAG;IACnB,MAAM,EAAE,OAAO;IACf,KAAK,EAAE,QAAQ;IACf,MAAM,EAAE,OAAO;CACf,CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,MAAM;IACrC,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC3C,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IACrC,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,aAAa,IAAI,CAAC,KAAK,WAAW;QAAE,OAAO,QAAQ,CAAA;IAC/E,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,KAAK;QAAE,OAAO,OAAO,CAAA;IAClE,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAA;IACrD,OAAO,IAAI,CAAA;AACZ,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,eAAe,CAAC,iBAAiB,EAAE,UAAU,EAAE,mBAAmB;IAC1E,uEAAuE;IACvE,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,iBAAiB,CAAC,CAAA;IAC3E,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAA;IAChF,6EAA6E;IAC7E,wEAAwE;IACxE,8EAA8E;IAC9E,8BAA8B;IAC9B,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,UAAU,CAAC,CAAA;IACxD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAA;IACpD,4EAA4E;IAC5E,MAAM,SAAS,GAAG,WAAW,CAAC,iBAAiB,CAAC,CAAA;IAChD,IAAI,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IACrF,6EAA6E;IAC7E,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9D,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;AACvD,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAClC,iBAAiB,EACjB,UAAU,GAAG,IAAI,EACjB,iBAAiB,GAAG,cAAc,EAClC,SAAS,GACT,GAAG,EAAE;IACL,MAAM,WAAW,GAAG,eAAe,CAAC,iBAAiB,CAAC,CAAA;IACtD,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,OAAO;YACN,SAAS,EAAE,KAAK;YAChB,YAAY,EAAE,IAAI;YAClB,cAAc,EAAE,IAAI;YACpB,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,4BAA4B;SACpC,CAAA;IACF,CAAC;IAED,MAAM,IAAI,GAAG,eAAe,CAAC,UAAU,CAAC,CAAA;IACxC,MAAM,mBAAmB,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAEhG,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,eAAe,CAAC,WAAW,EAAE,IAAI,EAAE,mBAAmB,CAAC,CAAA;IAClF,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,OAAO;YACN,SAAS,EAAE,KAAK;YAChB,YAAY,EAAE,IAAI;YAClB,cAAc,EAAE,IAAI;YACpB,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,MAAM,IAAI,oBAAoB;SACtC,CAAA;IACF,CAAC;IAED,4EAA4E;IAC5E,gFAAgF;IAChF,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,SAAS,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,CAAA;QACvE,MAAM,GAAG,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAA;QACvC,OAAO;YACN,SAAS,EAAE,IAAI;YACf,YAAY,EAAE,OAAO;YACrB,cAAc,EAAE,EAAE,qBAAqB,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE;YAC3F,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,oEAAoE;YACpE,uEAAuE;YACvE,kEAAkE;YAClE,MAAM,EAAE,GAAG,CAAC,MAAM;SAClB,CAAA;IACF,CAAC;IAED,gFAAgF;IAChF,OAAO;QACN,SAAS,EAAE,IAAI;QACf,YAAY,EAAE,MAAM;QACpB,cAAc,EAAE,EAAE,qBAAqB,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE;QAC5F,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,gBAAgB;KACxB,CAAA;AACF,CAAC"}