@generacy-ai/cockpit 0.6.0 → 0.7.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 (58) hide show
  1. package/README.md +148 -0
  2. package/dist/gates/clarification-hash.d.ts +33 -0
  3. package/dist/gates/clarification-hash.d.ts.map +1 -0
  4. package/dist/gates/clarification-hash.js +27 -0
  5. package/dist/gates/clarification-hash.js.map +1 -0
  6. package/dist/gates/fixtures.d.ts +15 -0
  7. package/dist/gates/fixtures.d.ts.map +1 -0
  8. package/dist/gates/fixtures.js +227 -0
  9. package/dist/gates/fixtures.js.map +1 -0
  10. package/dist/gates/gate-id.d.ts +11 -0
  11. package/dist/gates/gate-id.d.ts.map +1 -0
  12. package/dist/gates/gate-id.js +11 -0
  13. package/dist/gates/gate-id.js.map +1 -0
  14. package/dist/gates/generation.d.ts +39 -0
  15. package/dist/gates/generation.d.ts.map +1 -0
  16. package/dist/gates/generation.js +31 -0
  17. package/dist/gates/generation.js.map +1 -0
  18. package/dist/gates/index.d.ts +10 -0
  19. package/dist/gates/index.d.ts.map +1 -0
  20. package/dist/gates/index.js +19 -0
  21. package/dist/gates/index.js.map +1 -0
  22. package/dist/gates/schema.d.ts +192 -0
  23. package/dist/gates/schema.d.ts.map +1 -0
  24. package/dist/gates/schema.js +136 -0
  25. package/dist/gates/schema.js.map +1 -0
  26. package/dist/gates/schemas.d.ts +28 -0
  27. package/dist/gates/schemas.d.ts.map +1 -0
  28. package/dist/gates/schemas.js +21 -0
  29. package/dist/gates/schemas.js.map +1 -0
  30. package/dist/gates/types.d.ts +15 -0
  31. package/dist/gates/types.d.ts.map +1 -0
  32. package/dist/gates/types.js +19 -0
  33. package/dist/gates/types.js.map +1 -0
  34. package/dist/gates/wire-fixtures.d.ts +84 -0
  35. package/dist/gates/wire-fixtures.d.ts.map +1 -0
  36. package/dist/gates/wire-fixtures.js +107 -0
  37. package/dist/gates/wire-fixtures.js.map +1 -0
  38. package/dist/gh/wrapper.d.ts +22 -0
  39. package/dist/gh/wrapper.d.ts.map +1 -1
  40. package/dist/gh/wrapper.js +47 -6
  41. package/dist/gh/wrapper.js.map +1 -1
  42. package/dist/index.d.ts +2 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +22 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/resolver/parse-epic-body.d.ts +9 -3
  47. package/dist/resolver/parse-epic-body.d.ts.map +1 -1
  48. package/dist/resolver/parse-epic-body.js +59 -5
  49. package/dist/resolver/parse-epic-body.js.map +1 -1
  50. package/dist/resolver/resolve.d.ts.map +1 -1
  51. package/dist/resolver/resolve.js +3 -1
  52. package/dist/resolver/resolve.js.map +1 -1
  53. package/dist/resolver/types.d.ts +19 -0
  54. package/dist/resolver/types.d.ts.map +1 -1
  55. package/dist/state/precedence.d.ts.map +1 -1
  56. package/dist/state/precedence.js +16 -0
  57. package/dist/state/precedence.js.map +1 -1
  58. package/package.json +2 -2
package/README.md CHANGED
@@ -78,6 +78,154 @@ cockpit:
78
78
  owner: alice # optional; defaults to `gh auth status` login
79
79
  ```
80
80
 
81
+ ## Gates protocol (Cockpit Remote Gates epic, P2 mirror-doc)
82
+
83
+ **Audience**: engineers implementing the P2 cloud-side fake-cluster tests in
84
+ `generacy-ai/generacy-cloud`. This section pins the wire shapes exchanged
85
+ between the cluster-side integration harness (`packages/orchestrator/src/__tests__/cockpit-gates-integration.integration.test.ts`)
86
+ and the fake relay peer, so the cloud side can emit and expect the exact
87
+ same bytes without cross-repo copy-paste.
88
+
89
+ **Single-source the shapes**: build every wire body through the fixture
90
+ builders exported from `@generacy-ai/cockpit` so cluster and cloud stay
91
+ byte-identical — never hand-inline a literal:
92
+
93
+ ```ts
94
+ import {
95
+ gateOpenFixture, // POST /cockpit/gates body → GateOpenSchema
96
+ gateAckFixture, // POST /cockpit/gates/:id/ack body → GateAckSchema
97
+ answerLineFixture, // POST /cockpit/answers body + tail → GateAnswerEnvelopeSchema (+ scope)
98
+ DEFAULT_WIRE_SCOPE, // { owner, repo, number }
99
+ DEFAULT_WIRE_EPIC_REF // "owner/repo#number"
100
+ } from '@generacy-ai/cockpit';
101
+ ```
102
+
103
+ ### Wire framing
104
+
105
+ All messages are JSON-encoded WebSocket text frames matching
106
+ `RelayMessageSchema` from `@generacy-ai/cluster-relay`. No custom framing,
107
+ no compression, no TLS in tests (fake peer is `ws://127.0.0.1:<port>`).
108
+
109
+ ### Cluster → Cloud
110
+
111
+ **Gate-open event** (emitted when a `POST /cockpit/gates` succeeds). The
112
+ route echoes the validated `GateOpen` envelope as `data` verbatim — the
113
+ `kind` discriminator lives at the top of `data`, not nested under a `gate`
114
+ key:
115
+
116
+ ```jsonc
117
+ {
118
+ "type": "event",
119
+ "event": "cluster.cockpit",
120
+ "timestamp": "2026-07-21T12:34:56.789Z",
121
+ "data": {
122
+ "kind": "gate-open",
123
+ "gateId": "g_…",
124
+ "generation": 0,
125
+ "scope": { "owner": "generacy-ai", "repo": "generacy", "number": 1024 },
126
+ "openedAt": "2026-07-21T12:00:00.000Z"
127
+ // …plus any passthrough keys (e.g. `payload`) — GateOpenSchema is .passthrough()
128
+ }
129
+ }
130
+ ```
131
+
132
+ **Gate-ack event** (emitted when a `POST /cockpit/gates/:id/ack` succeeds).
133
+ `data` is the validated `GateAck` envelope; the route injects the path `:id`
134
+ as `gateId`:
135
+
136
+ ```jsonc
137
+ {
138
+ "type": "event",
139
+ "event": "cluster.cockpit",
140
+ "timestamp": "…",
141
+ "data": {
142
+ "kind": "gate-ack",
143
+ "gateId": "g_…",
144
+ "generation": 0,
145
+ "outcome": "answered",
146
+ "ackedAt": "2026-07-21T12:05:01.000Z"
147
+ // …plus any passthrough keys (e.g. `answer`)
148
+ }
149
+ }
150
+ ```
151
+
152
+ Both events use `data.kind` (`"gate-open"` | `"gate-ack"`) as the
153
+ discriminator on the `cluster.cockpit` channel.
154
+
155
+ ### Cloud → Cluster
156
+
157
+ **Answer down-path** — the cloud injects operator-authored answers into the
158
+ cluster via an `api_request` frame that the orchestrator proxies to its
159
+ `POST /cockpit/answers` route:
160
+
161
+ ```jsonc
162
+ {
163
+ "type": "api_request",
164
+ "correlationId": "<uuid v4>",
165
+ "method": "POST",
166
+ "path": "/cockpit/answers",
167
+ "headers": { "content-type": "application/json" },
168
+ "body": {
169
+ "kind": "gate-answer",
170
+ "deliveryId": "dlv_…", // dedup key — the writer keeps one file line per deliveryId
171
+ "gateId": "g_…",
172
+ "generation": 0,
173
+ "answeredAt": "2026-07-21T12:05:00.000Z",
174
+ "answer": { /* operator's choice */ },
175
+ "scope": { "owner": "generacy-ai", "repo": "generacy", "number": 1024 }
176
+ // `answeredBy` optional
177
+ }
178
+ }
179
+ ```
180
+
181
+ > **Seam pinned by the harness**: the answers route validates
182
+ > `GateAnswerEnvelopeSchema` (which does not require `scope`), but the doorbell
183
+ > tailer validates `GateAnswerLineSchema` (which does). A body **without**
184
+ > `scope` is written to the answers file yet silently dropped by the doorbell.
185
+ > `answerLineFixture()` carries `scope` so the single wire shape satisfies both
186
+ > ends — mirror it exactly.
187
+
188
+ Response frame (200 on happy path, `{ accepted, deduped }`; 4xx on validation
189
+ error):
190
+
191
+ ```jsonc
192
+ {
193
+ "type": "api_response",
194
+ "correlationId": "<same as request>",
195
+ "status": 200,
196
+ "body": { "accepted": true, "deduped": false }
197
+ }
198
+ ```
199
+
200
+ ### Connection lifecycle
201
+
202
+ 1. Orchestrator dials `wss://…/relay` (or `ws://127.0.0.1:<port>` in tests).
203
+ 2. Orchestrator sends a `handshake` frame carrying its cluster metadata.
204
+ 3. Peer responds with a `heartbeat` frame — this transitions the client from
205
+ `authenticating → connected`. (Fake peer mirrors this pattern; see
206
+ `packages/cluster-relay/tests/relay.test.ts:93-100`.)
207
+ 4. Steady state: `event` / `api_request` / `api_response` frames in both
208
+ directions, `heartbeat` at the configured interval.
209
+
210
+ ### Retain-and-replay across disconnect
211
+
212
+ When the peer disconnects while a `cluster.cockpit` event is pending, the
213
+ orchestrator retains the event and replays it on the next successful
214
+ connection. Mirror of the `cluster.vscode-tunnel` pattern in
215
+ `packages/orchestrator/src/routes/retained-tunnel-event.ts`.
216
+
217
+ Assertion pattern (harness scenario S1b): `disconnect → POST /cockpit/gates
218
+ → reconnect → assert peer sees the event exactly once on the new socket`.
219
+
220
+ ### Further reading
221
+
222
+ - Harness invocation and troubleshooting:
223
+ [`specs/1024-part-cockpit-remote-gates/quickstart.md`](../../specs/1024-part-cockpit-remote-gates/quickstart.md).
224
+ - Fake-peer wire contract in full:
225
+ [`specs/1024-part-cockpit-remote-gates/contracts/fake-peer-protocol.md`](../../specs/1024-part-cockpit-remote-gates/contracts/fake-peer-protocol.md).
226
+ - 8-scenario catalog:
227
+ [`specs/1024-part-cockpit-remote-gates/contracts/scenario-catalog.md`](../../specs/1024-part-cockpit-remote-gates/contracts/scenario-catalog.md).
228
+
81
229
  ## References
82
230
 
83
231
  - Spec: [`specs/806-epic-generacy-ai-tetrad/spec.md`](../../specs/806-epic-generacy-ai-tetrad/spec.md)
@@ -0,0 +1,33 @@
1
+ /**
2
+ * A single question in a clarification-gate answer set.
3
+ *
4
+ * The canonical hash projects to only `questionNumber` + `questionText` —
5
+ * extra fields (e.g. drafted answers, timestamps) are stripped and MUST NOT
6
+ * influence gate identity. Same round of asks → same generation.
7
+ */
8
+ export interface ClarificationQuestion {
9
+ questionNumber: number;
10
+ questionText: string;
11
+ }
12
+ export interface ComputeClarificationAnswerSetHashInput {
13
+ questions: readonly ClarificationQuestion[];
14
+ }
15
+ /**
16
+ * Canonical answer-set hash for a `clarification`-gate `generation` (#1038).
17
+ *
18
+ * Algorithm (locked by SC-002 — MUST match agency sweep byte-for-byte):
19
+ * 1. Sort ascending by `questionNumber`.
20
+ * 2. Project to exactly `{ questionNumber, questionText }` (drop any extra
21
+ * fields — this is the mechanical enforcement of "question identity
22
+ * only; drafted/pending answers excluded" per spec Q1 → A).
23
+ * 3. `JSON.stringify` the projected array.
24
+ * 4. `sha256` the canonical string.
25
+ * 5. Return the first 12 hex characters (48 bits — collision-safe for the
26
+ * population; keeps `gateKey` short in logs).
27
+ *
28
+ * Consumers pass the returned string as the `batchId` argument to
29
+ * `deriveClarificationGeneration({ batchId })`. Both the agency-side sweep
30
+ * and the cluster-side live path MUST go through this helper.
31
+ */
32
+ export declare function computeClarificationAnswerSetHash(input: ComputeClarificationAnswerSetHashInput): string;
33
+ //# sourceMappingURL=clarification-hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clarification-hash.d.ts","sourceRoot":"","sources":["../../src/gates/clarification-hash.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,sCAAsC;IACrD,SAAS,EAAE,SAAS,qBAAqB,EAAE,CAAC;CAC7C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,sCAAsC,GAC5C,MAAM,CASR"}
@@ -0,0 +1,27 @@
1
+ import { createHash } from 'node:crypto';
2
+ /**
3
+ * Canonical answer-set hash for a `clarification`-gate `generation` (#1038).
4
+ *
5
+ * Algorithm (locked by SC-002 — MUST match agency sweep byte-for-byte):
6
+ * 1. Sort ascending by `questionNumber`.
7
+ * 2. Project to exactly `{ questionNumber, questionText }` (drop any extra
8
+ * fields — this is the mechanical enforcement of "question identity
9
+ * only; drafted/pending answers excluded" per spec Q1 → A).
10
+ * 3. `JSON.stringify` the projected array.
11
+ * 4. `sha256` the canonical string.
12
+ * 5. Return the first 12 hex characters (48 bits — collision-safe for the
13
+ * population; keeps `gateKey` short in logs).
14
+ *
15
+ * Consumers pass the returned string as the `batchId` argument to
16
+ * `deriveClarificationGeneration({ batchId })`. Both the agency-side sweep
17
+ * and the cluster-side live path MUST go through this helper.
18
+ */
19
+ export function computeClarificationAnswerSetHash(input) {
20
+ const sorted = [...input.questions].sort((a, b) => a.questionNumber - b.questionNumber);
21
+ const canonical = JSON.stringify(sorted.map((q) => ({
22
+ questionNumber: q.questionNumber,
23
+ questionText: q.questionText,
24
+ })));
25
+ return createHash('sha256').update(canonical, 'utf8').digest('hex').slice(0, 12);
26
+ }
27
+ //# sourceMappingURL=clarification-hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clarification-hash.js","sourceRoot":"","sources":["../../src/gates/clarification-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAkBzC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,iCAAiC,CAC/C,KAA6C;IAE7C,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC;IACxF,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAC9B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACjB,cAAc,EAAE,CAAC,CAAC,cAAc;QAChC,YAAY,EAAE,CAAC,CAAC,YAAY;KAC7B,CAAC,CAAC,CACJ,CAAC;IACF,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACnF,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { type GateAnswer, type GateOpen, type GateOutcome, type GateType } from './schema.js';
2
+ import type { ClarificationQuestion } from './clarification-hash.js';
3
+ export declare const VALID_FIXTURES: Record<GateType, GateOpen>;
4
+ export declare const VALID_ANSWER_FIXTURES: Record<GateType, GateAnswer>;
5
+ export declare const VALID_ACK_FIXTURES: Record<'applied' | 'superseded' | 'failed', GateOutcome>;
6
+ export declare const MALFORMED_FIXTURES: Record<string, unknown>;
7
+ export interface ClarificationAnswerSetFixture {
8
+ readonly questions: readonly ClarificationQuestion[];
9
+ }
10
+ export declare const CLARIFICATION_ANSWER_SET_FIXTURES: Readonly<{
11
+ readonly singleQuestion: ClarificationAnswerSetFixture;
12
+ readonly threeQuestions: ClarificationAnswerSetFixture;
13
+ readonly unicode: ClarificationAnswerSetFixture;
14
+ }>;
15
+ //# sourceMappingURL=fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../src/gates/fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAYrB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAmErE,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,QAAQ,EAAE,QAAQ,CASrD,CAAC;AAyCF,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,QAAQ,EAAE,UAAU,CAS9D,CAAC;AAOF,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,SAAS,GAAG,YAAY,GAAG,QAAQ,EAAE,WAAW,CAqBvF,CAAC;AAqBF,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAgDtD,CAAC;AAOF,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,SAAS,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACtD;AAED,eAAO,MAAM,iCAAiC;6BAKtC,6BAA6B;6BAQ7B,6BAA6B;sBAa7B,6BAA6B;EAC1B,CAAC"}
@@ -0,0 +1,227 @@
1
+ import { GateAnswerSchema, GateOpenSchema, GateOutcomeSchema, deriveGateId, deriveGateKey, } from './schema.js';
2
+ import { issueRefToString } from './schemas.js';
3
+ import { deriveArtifactReviewGeneration, deriveClarificationGeneration, deriveEscalationGeneration, deriveFilingGeneration, deriveImplementationReviewGeneration, deriveManualValidationGeneration, derivePhaseQueueGeneration, deriveScopeDrainedGeneration, } from './generation.js';
4
+ const EPIC_REF = { owner: 'generacy-ai', repo: 'generacy', number: 1000 };
5
+ const ISSUE_REF = { owner: 'generacy-ai', repo: 'generacy', number: 1020 };
6
+ const TRACKING_REF = { owner: 'generacy-ai', repo: 'generacy', number: 900 };
7
+ const EPIC_REF_STR = issueRefToString(EPIC_REF);
8
+ const ISSUE_REF_STR = issueRefToString(ISSUE_REF);
9
+ const SESSION_ID = 'sess-abc123def456';
10
+ const ASKED_AT = '2026-07-21T12:00:00.000Z';
11
+ const ANSWERED_AT = '2026-07-21T12:05:00.000Z';
12
+ const OUTCOME_AT = '2026-07-21T12:05:01.000Z';
13
+ const ACTOR = {
14
+ userId: 'user-1',
15
+ email: 'operator@example.com',
16
+ displayName: 'Operator One',
17
+ };
18
+ const DEFAULT_OPTIONS = [
19
+ { id: 'opt-a', label: 'Approve', description: 'Approve and proceed' },
20
+ { id: 'opt-b', label: 'Reject', description: 'Reject and halt', recommended: true },
21
+ ];
22
+ const GENERATIONS = {
23
+ clarification: deriveClarificationGeneration({ batchId: 'batch-abc123' }),
24
+ 'artifact-review': deriveArtifactReviewGeneration({ kind: 'spec-review', headSha: 'abc1234' }),
25
+ 'implementation-review': deriveImplementationReviewGeneration({ headSha: 'def5678' }),
26
+ 'manual-validation': deriveManualValidationGeneration({ phaseNumber: 2 }),
27
+ escalation: deriveEscalationGeneration({
28
+ subtype: 'stalled',
29
+ labelOrState: 'agent:error',
30
+ counter: 1,
31
+ }),
32
+ 'phase-queue': derivePhaseQueueGeneration({ phaseNumber: 3 }),
33
+ filing: deriveFilingGeneration({ draftHash: 'feedbeef1234' }),
34
+ 'scope-drained': deriveScopeDrainedGeneration({ trackingIssueRef: TRACKING_REF, counter: 1 }),
35
+ };
36
+ // phase-queue is the sole per-issue exception: its wire `issueRef` slot carries
37
+ // the EPIC ref, not a child issue (see the auto.md UI-mode gate-mapping table).
38
+ function issueRefFor(gateType) {
39
+ return gateType === 'phase-queue' ? EPIC_REF_STR : ISSUE_REF_STR;
40
+ }
41
+ function buildRecord(gateType) {
42
+ const generation = GENERATIONS[gateType];
43
+ const issueRef = issueRefFor(gateType);
44
+ const gateKey = deriveGateKey(issueRef, gateType, generation);
45
+ const gateId = deriveGateId(gateKey);
46
+ return {
47
+ type: 'gate-open',
48
+ gateId,
49
+ gateKey,
50
+ gateType,
51
+ epicRef: EPIC_REF_STR,
52
+ issueRef,
53
+ issueTitle: `Issue #${ISSUE_REF.number}: cockpit remote gates`,
54
+ issueUrl: `https://github.com/${ISSUE_REF.owner}/${ISSUE_REF.repo}/issues/${ISSUE_REF.number}`,
55
+ title: `${gateType} gate`,
56
+ body: `Please review the ${gateType} gate.`,
57
+ options: DEFAULT_OPTIONS.map((o) => ({ ...o })),
58
+ allowFreeText: true,
59
+ sessionId: SESSION_ID,
60
+ askedAt: ASKED_AT,
61
+ };
62
+ }
63
+ export const VALID_FIXTURES = {
64
+ clarification: buildRecord('clarification'),
65
+ 'artifact-review': buildRecord('artifact-review'),
66
+ 'implementation-review': buildRecord('implementation-review'),
67
+ 'manual-validation': buildRecord('manual-validation'),
68
+ escalation: buildRecord('escalation'),
69
+ 'phase-queue': buildRecord('phase-queue'),
70
+ filing: buildRecord('filing'),
71
+ 'scope-drained': buildRecord('scope-drained'),
72
+ };
73
+ // Assert every valid fixture parses at module load — fixture drift fails the build,
74
+ // not a test run.
75
+ for (const gateType of Object.keys(VALID_FIXTURES)) {
76
+ GateOpenSchema.parse(VALID_FIXTURES[gateType]);
77
+ }
78
+ // optionId XOR free-text is NOT enforced on the wire (the cloud sends the unused
79
+ // side as an explicit null); every fixture therefore carries both fields.
80
+ const ANSWER_SPECS = {
81
+ clarification: { optionId: 'opt-a', freeText: null },
82
+ 'artifact-review': { optionId: null, freeText: 'approve with concerns' },
83
+ 'implementation-review': { optionId: 'opt-a', freeText: null },
84
+ 'manual-validation': { optionId: null, freeText: 'verified manually on 2026-07-21' },
85
+ escalation: { optionId: 'opt-a', freeText: null },
86
+ 'phase-queue': { optionId: null, freeText: 'proceed with next phase' },
87
+ filing: { optionId: 'opt-a', freeText: null },
88
+ 'scope-drained': { optionId: null, freeText: 'confirmed scope drained' },
89
+ };
90
+ function buildAnswer(gateType) {
91
+ const record = VALID_FIXTURES[gateType];
92
+ const spec = ANSWER_SPECS[gateType];
93
+ return {
94
+ type: 'gate-answer',
95
+ gateId: record.gateId,
96
+ gateKey: record.gateKey,
97
+ optionId: spec.optionId,
98
+ freeText: spec.freeText,
99
+ actor: { ...ACTOR },
100
+ answeredAt: ANSWERED_AT,
101
+ deliveryId: `delivery-${gateType}-1`,
102
+ };
103
+ }
104
+ export const VALID_ANSWER_FIXTURES = {
105
+ clarification: buildAnswer('clarification'),
106
+ 'artifact-review': buildAnswer('artifact-review'),
107
+ 'implementation-review': buildAnswer('implementation-review'),
108
+ 'manual-validation': buildAnswer('manual-validation'),
109
+ escalation: buildAnswer('escalation'),
110
+ 'phase-queue': buildAnswer('phase-queue'),
111
+ filing: buildAnswer('filing'),
112
+ 'scope-drained': buildAnswer('scope-drained'),
113
+ };
114
+ for (const gateType of Object.keys(VALID_ANSWER_FIXTURES)) {
115
+ GateAnswerSchema.parse(VALID_ANSWER_FIXTURES[gateType]);
116
+ }
117
+ // gate-outcome (the ACK) fixtures — one per closed outcome enum value.
118
+ export const VALID_ACK_FIXTURES = {
119
+ applied: {
120
+ type: 'gate-outcome',
121
+ gateId: VALID_FIXTURES.clarification.gateId,
122
+ outcome: 'applied',
123
+ at: OUTCOME_AT,
124
+ },
125
+ superseded: {
126
+ type: 'gate-outcome',
127
+ gateId: VALID_FIXTURES['artifact-review'].gateId,
128
+ outcome: 'superseded',
129
+ detail: 'A newer answer arrived first',
130
+ at: OUTCOME_AT,
131
+ },
132
+ failed: {
133
+ type: 'gate-outcome',
134
+ gateId: VALID_FIXTURES.escalation.gateId,
135
+ outcome: 'failed',
136
+ detail: 'Label mutation returned 422',
137
+ at: OUTCOME_AT,
138
+ },
139
+ };
140
+ for (const key of Object.keys(VALID_ACK_FIXTURES)) {
141
+ GateOutcomeSchema.parse(VALID_ACK_FIXTURES[key]);
142
+ }
143
+ const BASE_RECORD_UNKNOWN = VALID_FIXTURES.clarification;
144
+ const BASE_ANSWER_UNKNOWN = VALID_ANSWER_FIXTURES.clarification;
145
+ function withoutKey(obj, key) {
146
+ const clone = { ...obj };
147
+ delete clone[key];
148
+ return clone;
149
+ }
150
+ export const MALFORMED_FIXTURES = {
151
+ // Wrong up-path discriminator (must be the 'gate-open' literal).
152
+ 'wrong-type-literal': {
153
+ ...BASE_RECORD_UNKNOWN,
154
+ type: 'gate-ack',
155
+ },
156
+ // Option missing its required `id` (description is OPTIONAL in the frozen shape).
157
+ 'option-missing-id': {
158
+ ...BASE_RECORD_UNKNOWN,
159
+ options: [{ label: 'Approve' }],
160
+ },
161
+ // allowFreeText is a REQUIRED boolean — a non-boolean must reject.
162
+ 'allow-free-text-non-boolean': {
163
+ ...BASE_RECORD_UNKNOWN,
164
+ allowFreeText: 'yes',
165
+ },
166
+ // gateId is pinned to exactly 24 chars.
167
+ 'empty-gate-id': {
168
+ ...BASE_RECORD_UNKNOWN,
169
+ gateId: '',
170
+ },
171
+ 'wrong-length-gate-id': {
172
+ ...BASE_RECORD_UNKNOWN,
173
+ gateId: '0123456789abcdef0123', // 20 chars
174
+ },
175
+ 'unknown-gate-type': {
176
+ ...BASE_RECORD_UNKNOWN,
177
+ gateType: 'not-a-real-gate-type',
178
+ },
179
+ 'invalid-issue-url': {
180
+ ...BASE_RECORD_UNKNOWN,
181
+ issueUrl: 'not-a-url',
182
+ },
183
+ 'naive-timestamp': {
184
+ ...BASE_RECORD_UNKNOWN,
185
+ askedAt: 'Tue Jul 21 2026 12:00:00 GMT+0000',
186
+ },
187
+ 'record-missing-title': withoutKey(BASE_RECORD_UNKNOWN, 'title'),
188
+ // Down-path: actor.email must be a valid email OR null; a malformed string rejects.
189
+ 'answer-invalid-email': {
190
+ ...BASE_ANSWER_UNKNOWN,
191
+ actor: { ...ACTOR, email: 'not-an-email' },
192
+ },
193
+ // Down-path: wrong discriminator (must be the 'gate-answer' literal).
194
+ 'answer-wrong-type-literal': {
195
+ ...BASE_ANSWER_UNKNOWN,
196
+ type: 'gate-open',
197
+ },
198
+ };
199
+ export const CLARIFICATION_ANSWER_SET_FIXTURES = Object.freeze({
200
+ singleQuestion: Object.freeze({
201
+ questions: Object.freeze([
202
+ Object.freeze({ questionNumber: 1, questionText: 'Which auth method?' }),
203
+ ]),
204
+ }),
205
+ // Deliberately out-of-order to prove sort determinism (SC-002).
206
+ threeQuestions: Object.freeze({
207
+ questions: Object.freeze([
208
+ Object.freeze({ questionNumber: 3, questionText: 'Timezone?' }),
209
+ Object.freeze({ questionNumber: 1, questionText: 'Which auth method?' }),
210
+ Object.freeze({ questionNumber: 2, questionText: 'Which DB?' }),
211
+ ]),
212
+ }),
213
+ // Non-ASCII bytes to exercise UTF-8 canonicalization end to end.
214
+ unicode: Object.freeze({
215
+ questions: Object.freeze([
216
+ Object.freeze({
217
+ questionNumber: 1,
218
+ questionText: 'Préférence pour l’heure locale? 🌍',
219
+ }),
220
+ Object.freeze({
221
+ questionNumber: 2,
222
+ questionText: '中文文本 — 数据库偏好?',
223
+ }),
224
+ ]),
225
+ }),
226
+ });
227
+ //# sourceMappingURL=fixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.js","sourceRoot":"","sources":["../../src/gates/fixtures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Back-compat shim. Canonical gateKey/gateId derivation now lives in
3
+ * `./schema.ts` — the single source. Prefer importing `deriveGateKey` /
4
+ * `deriveGateId` from `./schema.js` (or the package index).
5
+ *
6
+ * NOTE the signature moved: `deriveGateKey(issueRef: string, gateType, generation)`
7
+ * now takes the FLAT `owner/repo#N` ref string, not an object. Use
8
+ * `issueRefToString` (schemas.ts) to convert an object ref first.
9
+ */
10
+ export { deriveGateKey, deriveGateId } from './schema.js';
11
+ //# sourceMappingURL=gate-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate-id.d.ts","sourceRoot":"","sources":["../../src/gates/gate-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Back-compat shim. Canonical gateKey/gateId derivation now lives in
3
+ * `./schema.ts` — the single source. Prefer importing `deriveGateKey` /
4
+ * `deriveGateId` from `./schema.js` (or the package index).
5
+ *
6
+ * NOTE the signature moved: `deriveGateKey(issueRef: string, gateType, generation)`
7
+ * now takes the FLAT `owner/repo#N` ref string, not an object. Use
8
+ * `issueRefToString` (schemas.ts) to convert an object ref first.
9
+ */
10
+ export { deriveGateKey, deriveGateId } from './schema.js';
11
+ //# sourceMappingURL=gate-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate-id.js","sourceRoot":"","sources":["../../src/gates/gate-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,39 @@
1
+ import type { IssueRef } from './schemas.js';
2
+ import type { ArtifactReviewKind } from './types.js';
3
+ export interface ClarificationGenerationInput {
4
+ batchId: string;
5
+ }
6
+ export declare function deriveClarificationGeneration(input: ClarificationGenerationInput): string;
7
+ export interface ArtifactReviewGenerationInput {
8
+ kind: ArtifactReviewKind;
9
+ headSha: string;
10
+ }
11
+ export declare function deriveArtifactReviewGeneration(input: ArtifactReviewGenerationInput): string;
12
+ export interface ImplementationReviewGenerationInput {
13
+ headSha: string;
14
+ }
15
+ export declare function deriveImplementationReviewGeneration(input: ImplementationReviewGenerationInput): string;
16
+ export interface ManualValidationGenerationInput {
17
+ phaseNumber: number;
18
+ }
19
+ export declare function deriveManualValidationGeneration(input: ManualValidationGenerationInput): string;
20
+ export interface EscalationGenerationInput {
21
+ subtype: string;
22
+ labelOrState: string;
23
+ counter: number;
24
+ }
25
+ export declare function deriveEscalationGeneration(input: EscalationGenerationInput): string;
26
+ export interface PhaseQueueGenerationInput {
27
+ phaseNumber: number;
28
+ }
29
+ export declare function derivePhaseQueueGeneration(input: PhaseQueueGenerationInput): string;
30
+ export interface FilingGenerationInput {
31
+ draftHash: string;
32
+ }
33
+ export declare function deriveFilingGeneration(input: FilingGenerationInput): string;
34
+ export interface ScopeDrainedGenerationInput {
35
+ trackingIssueRef: IssueRef;
36
+ counter: number;
37
+ }
38
+ export declare function deriveScopeDrainedGeneration(input: ScopeDrainedGenerationInput): string;
39
+ //# sourceMappingURL=generation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generation.d.ts","sourceRoot":"","sources":["../../src/gates/generation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIrD,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,4BAA4B,GAAG,MAAM,CAEzF;AAED,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AACD,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,6BAA6B,GAAG,MAAM,CAE3F;AAED,MAAM,WAAW,mCAAmC;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB;AACD,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,mCAAmC,GACzC,MAAM,CAER;AAED,MAAM,WAAW,+BAA+B;IAC9C,WAAW,EAAE,MAAM,CAAC;CACrB;AACD,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,+BAA+B,GACrC,MAAM,CAER;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AACD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,yBAAyB,GAAG,MAAM,CAEnF;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAC;CACrB;AACD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,yBAAyB,GAAG,MAAM,CAEnF;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAE3E;AAED,MAAM,WAAW,2BAA2B;IAC1C,gBAAgB,EAAE,QAAQ,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AACD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,2BAA2B,GAAG,MAAM,CAGvF"}
@@ -0,0 +1,31 @@
1
+ // #1038 — the canonical `batchId` construction path is
2
+ // `computeClarificationAnswerSetHash` in ./clarification-hash.ts. This helper's
3
+ // signature is intentionally unchanged (additive, not breaking); consumers on
4
+ // the sweep and live paths call the hash helper first and pass its 12-hex
5
+ // output as `batchId` to keep sweep-derived and live-derived gateIds coalesced.
6
+ export function deriveClarificationGeneration(input) {
7
+ return input.batchId;
8
+ }
9
+ export function deriveArtifactReviewGeneration(input) {
10
+ return `${input.kind}:${input.headSha}`;
11
+ }
12
+ export function deriveImplementationReviewGeneration(input) {
13
+ return input.headSha;
14
+ }
15
+ export function deriveManualValidationGeneration(input) {
16
+ return String(input.phaseNumber);
17
+ }
18
+ export function deriveEscalationGeneration(input) {
19
+ return `${input.subtype}:${input.labelOrState}:${input.counter}`;
20
+ }
21
+ export function derivePhaseQueueGeneration(input) {
22
+ return String(input.phaseNumber);
23
+ }
24
+ export function deriveFilingGeneration(input) {
25
+ return input.draftHash;
26
+ }
27
+ export function deriveScopeDrainedGeneration(input) {
28
+ const { trackingIssueRef, counter } = input;
29
+ return `${trackingIssueRef.owner}/${trackingIssueRef.repo}#${trackingIssueRef.number}:${counter}`;
30
+ }
31
+ //# sourceMappingURL=generation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generation.js","sourceRoot":"","sources":["../../src/gates/generation.ts"],"names":[],"mappings":"AAQA,uDAAuD;AACvD,gFAAgF;AAChF,8EAA8E;AAC9E,0EAA0E;AAC1E,gFAAgF;AAChF,MAAM,UAAU,6BAA6B,CAAC,KAAmC;IAC/E,OAAO,KAAK,CAAC,OAAO,CAAC;AACvB,CAAC;AAMD,MAAM,UAAU,8BAA8B,CAAC,KAAoC;IACjF,OAAO,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;AAC1C,CAAC;AAKD,MAAM,UAAU,oCAAoC,CAClD,KAA0C;IAE1C,OAAO,KAAK,CAAC,OAAO,CAAC;AACvB,CAAC;AAKD,MAAM,UAAU,gCAAgC,CAC9C,KAAsC;IAEtC,OAAO,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AACnC,CAAC;AAOD,MAAM,UAAU,0BAA0B,CAAC,KAAgC;IACzE,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;AACnE,CAAC;AAKD,MAAM,UAAU,0BAA0B,CAAC,KAAgC;IACzE,OAAO,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AACnC,CAAC;AAKD,MAAM,UAAU,sBAAsB,CAAC,KAA4B;IACjE,OAAO,KAAK,CAAC,SAAS,CAAC;AACzB,CAAC;AAMD,MAAM,UAAU,4BAA4B,CAAC,KAAkC;IAC7E,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC5C,OAAO,GAAG,gBAAgB,CAAC,KAAK,IAAI,gBAAgB,CAAC,IAAI,IAAI,gBAAgB,CAAC,MAAM,IAAI,OAAO,EAAE,CAAC;AACpG,CAAC"}
@@ -0,0 +1,10 @@
1
+ export { GateOpenSchema, GateOutcomeSchema, GateAnswerSchema, type GateOpen, type GateOutcome, type GateAnswer, GateTypeSchema, GateOptionSchema, type GateType, type GateOption, deriveGateKey, deriveGateId, } from './schema.js';
2
+ /** The 8 gate-type values as a readonly tuple (single source: GateTypeSchema). */
3
+ export declare const GATE_TYPES: ["clarification", "artifact-review", "implementation-review", "manual-validation", "escalation", "phase-queue", "filing", "scope-drained"];
4
+ export { IssueRefSchema, issueRefToString, type IssueRef, } from './schemas.js';
5
+ export { ArtifactReviewKindSchema, ARTIFACT_REVIEW_KINDS, type ArtifactReviewKind, } from './types.js';
6
+ export { deriveClarificationGeneration, deriveArtifactReviewGeneration, deriveImplementationReviewGeneration, deriveManualValidationGeneration, deriveEscalationGeneration, derivePhaseQueueGeneration, deriveFilingGeneration, deriveScopeDrainedGeneration, type ClarificationGenerationInput, type ArtifactReviewGenerationInput, type ImplementationReviewGenerationInput, type ManualValidationGenerationInput, type EscalationGenerationInput, type PhaseQueueGenerationInput, type FilingGenerationInput, type ScopeDrainedGenerationInput, } from './generation.js';
7
+ export { computeClarificationAnswerSetHash, type ClarificationQuestion, type ComputeClarificationAnswerSetHashInput, } from './clarification-hash.js';
8
+ export { VALID_FIXTURES, MALFORMED_FIXTURES, VALID_ANSWER_FIXTURES, VALID_ACK_FIXTURES, CLARIFICATION_ANSWER_SET_FIXTURES, type ClarificationAnswerSetFixture, } from './fixtures.js';
9
+ export { gateOpenFixture, gateOutcomeFixture, answerLineFixture, DEFAULT_WIRE_SCOPE, DEFAULT_WIRE_EPIC_REF, type WireScope, type GateOpenFixtureOverrides, type GateOutcomeFixtureOverrides, type AnswerLineFixtureOverrides, } from './wire-fixtures.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gates/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,UAAU,EAEf,cAAc,EACd,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,UAAU,EAEf,aAAa,EACb,YAAY,GACb,MAAM,aAAa,CAAC;AAIrB,kFAAkF;AAClF,eAAO,MAAM,UAAU,4IAAyB,CAAC;AAEjD,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,KAAK,QAAQ,GACd,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,6BAA6B,EAC7B,8BAA8B,EAC9B,oCAAoC,EACpC,gCAAgC,EAChC,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,EACtB,4BAA4B,EAC5B,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAClC,KAAK,mCAAmC,EACxC,KAAK,+BAA+B,EACpC,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,GACjC,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,iCAAiC,EACjC,KAAK,qBAAqB,EAC1B,KAAK,sCAAsC,GAC5C,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,iCAAiC,EACjC,KAAK,6BAA6B,GACnC,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,KAAK,SAAS,EACd,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,GAChC,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,19 @@
1
+ // Canonical cockpit gate wire contract — single source is ./schema.ts.
2
+ // See tetrad-development/docs/cockpit-remote-gates-plan.md § "Wire contracts".
3
+ export {
4
+ // Wire shapes (the frozen contract, Shapes 1/2/3)
5
+ GateOpenSchema, GateOutcomeSchema, GateAnswerSchema,
6
+ // Enum + option
7
+ GateTypeSchema, GateOptionSchema,
8
+ // gateKey/gateId derivation
9
+ deriveGateKey, deriveGateId, } from './schema.js';
10
+ import { GateTypeSchema } from './schema.js';
11
+ /** The 8 gate-type values as a readonly tuple (single source: GateTypeSchema). */
12
+ export const GATE_TYPES = GateTypeSchema.options;
13
+ export { IssueRefSchema, issueRefToString, } from './schemas.js';
14
+ export { ArtifactReviewKindSchema, ARTIFACT_REVIEW_KINDS, } from './types.js';
15
+ export { deriveClarificationGeneration, deriveArtifactReviewGeneration, deriveImplementationReviewGeneration, deriveManualValidationGeneration, deriveEscalationGeneration, derivePhaseQueueGeneration, deriveFilingGeneration, deriveScopeDrainedGeneration, } from './generation.js';
16
+ export { computeClarificationAnswerSetHash, } from './clarification-hash.js';
17
+ export { VALID_FIXTURES, MALFORMED_FIXTURES, VALID_ANSWER_FIXTURES, VALID_ACK_FIXTURES, CLARIFICATION_ANSWER_SET_FIXTURES, } from './fixtures.js';
18
+ export { gateOpenFixture, gateOutcomeFixture, answerLineFixture, DEFAULT_WIRE_SCOPE, DEFAULT_WIRE_EPIC_REF, } from './wire-fixtures.js';
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/gates/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,+EAA+E;AAE/E,OAAO;AACL,kDAAkD;AAClD,cAAc,EACd,iBAAiB,EACjB,gBAAgB;AAIhB,gBAAgB;AAChB,cAAc,EACd,gBAAgB;AAGhB,4BAA4B;AAC5B,aAAa,EACb,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,kFAAkF;AAClF,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC;AAEjD,OAAO,EACL,cAAc,EACd,gBAAgB,GAEjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,wBAAwB,EACxB,qBAAqB,GAEtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,6BAA6B,EAC7B,8BAA8B,EAC9B,oCAAoC,EACpC,gCAAgC,EAChC,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,EACtB,4BAA4B,GAS7B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,iCAAiC,GAGlC,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,iCAAiC,GAElC,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,GAKtB,MAAM,oBAAoB,CAAC"}