@cleocode/core 2026.6.10 → 2026.6.12
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.
- package/dist/docs/export-document.js +110 -22
- package/dist/docs/export-document.js.map +2 -2
- package/dist/llm/plugin-facade.js +74 -7
- package/dist/llm/plugin-facade.js.map +2 -2
- package/dist/release/plan.d.ts +4 -6
- package/dist/release/plan.d.ts.map +1 -1
- package/dist/release/plan.js +10 -23
- package/dist/release/plan.js.map +1 -1
- package/dist/release/reconcile.d.ts +0 -2
- package/dist/release/reconcile.d.ts.map +1 -1
- package/dist/release/reconcile.js +43 -50
- package/dist/release/reconcile.js.map +1 -1
- package/dist/release/release-manifest.d.ts.map +1 -1
- package/dist/release/release-manifest.js +4 -1
- package/dist/release/release-manifest.js.map +1 -1
- package/dist/release/verify-provenance.js +20 -20
- package/dist/release/verify-provenance.js.map +1 -1
- package/dist/store/agent-registry-store.d.ts.map +1 -1
- package/dist/store/agent-registry-store.js +4 -2
- package/dist/store/agent-registry-store.js.map +1 -1
- package/dist/store/conduit-sqlite.d.ts.map +1 -1
- package/dist/store/conduit-sqlite.js +4 -2
- package/dist/store/conduit-sqlite.js.map +1 -1
- package/dist/store/dual-scope-db.d.ts.map +1 -1
- package/dist/store/dual-scope-db.js +56 -6
- package/dist/store/dual-scope-db.js.map +1 -1
- package/dist/store/migration-manager.d.ts +26 -1
- package/dist/store/migration-manager.d.ts.map +1 -1
- package/dist/store/migration-manager.js +104 -4
- package/dist/store/migration-manager.js.map +1 -1
- package/dist/store/nexus-sqlite.d.ts.map +1 -1
- package/dist/store/nexus-sqlite.js +4 -2
- package/dist/store/nexus-sqlite.js.map +1 -1
- package/dist/store/resolve-migrations-folder.d.ts +41 -0
- package/dist/store/resolve-migrations-folder.d.ts.map +1 -1
- package/dist/store/resolve-migrations-folder.js +53 -0
- package/dist/store/resolve-migrations-folder.js.map +1 -1
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +5 -3
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/tasks-schema.d.ts +4 -0
- package/dist/store/tasks-schema.d.ts.map +1 -1
- package/dist/store/tasks-schema.js +14 -0
- package/dist/store/tasks-schema.js.map +1 -1
- package/dist/store/validation-schemas.d.ts +62 -62
- package/dist/task-tools/describe-schema.d.ts +4 -2
- package/dist/task-tools/describe-schema.d.ts.map +1 -1
- package/dist/task-tools/describe-schema.js +9 -3
- package/dist/task-tools/describe-schema.js.map +1 -1
- package/migrations/drizzle-cleo-project/20260606000000_t11884-restore-prefixed-task-triggers/migration.sql +272 -0
- package/migrations/drizzle-cleo-project/20260606000000_t11884-restore-prefixed-task-triggers/revert.sql +29 -0
- package/migrations/drizzle-cleo-project/20260606000100_t11883-tasks-releases-version-unique/migration.sql +21 -0
- package/migrations/drizzle-cleo-project/20260606000100_t11883-tasks-releases-version-unique/revert.sql +4 -0
- package/package.json +12 -12
- package/dist/release/provenance-fk.d.ts +0 -74
- package/dist/release/provenance-fk.d.ts.map +0 -1
- package/dist/release/provenance-fk.js +0 -122
- package/dist/release/provenance-fk.js.map +0 -1
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
-- T11884 — Restore task-domain invariant + handoff triggers on the PREFIXED tables.
|
|
2
|
+
--
|
|
3
|
+
-- ==========================================================================
|
|
4
|
+
-- Problem (latent bug exposed by the SG-DB-SUBSTRATE-V2 cutover)
|
|
5
|
+
-- ==========================================================================
|
|
6
|
+
-- The T11578 dual-scope cutover repointed the runtime drizzle symbols
|
|
7
|
+
-- (`tasks`, `task_relations`, `task_acceptance_criteria`, session handoff, …)
|
|
8
|
+
-- from the BARE un-prefixed tables onto the domain-prefixed tables
|
|
9
|
+
-- (`tasks_tasks`, `tasks_task_relations`, …). But the 12 SQLite invariant /
|
|
10
|
+
-- handoff triggers were authored ONLY on the bare tables (T877, T10572,
|
|
11
|
+
-- T10638, T1609) and were never recreated on the prefixed tables — exodus
|
|
12
|
+
-- copies rows with triggers absent on the target. Result: every task invariant
|
|
13
|
+
-- has been SILENTLY UNENFORCED on the live write path (`tasks_tasks` has zero
|
|
14
|
+
-- triggers), and the session-handoff `handoff_json` mirror is dead.
|
|
15
|
+
--
|
|
16
|
+
-- This migration recreates all 12 guards on the prefixed tables, with bodies
|
|
17
|
+
-- rewritten to reference `tasks_tasks` / `tasks_sessions`. New trigger NAMES
|
|
18
|
+
-- are used (prefixed) so they do NOT collide with the still-present legacy
|
|
19
|
+
-- triggers; when the legacy bare tables are dropped (E5 of the cutover) their
|
|
20
|
+
-- triggers drop with them and these remain the SSoT.
|
|
21
|
+
--
|
|
22
|
+
-- Invariants restored:
|
|
23
|
+
-- - tasks_tasks.parent_id cannot introduce a containment cycle
|
|
24
|
+
-- - tasks_tasks.parent_id obeys the saga->epic, epic->task|subtask,
|
|
25
|
+
-- task->subtask type matrix (sagas/tasks are roots)
|
|
26
|
+
-- - tasks_tasks status/pipeline_stage T877 invariant
|
|
27
|
+
-- - tasks_task_relations is non-containment-only
|
|
28
|
+
-- - tasks_task_acceptance_criteria child_task targets must be direct children
|
|
29
|
+
-- - tasks_session_handoff_entries is write-once + mirrors handoff_json into
|
|
30
|
+
-- tasks_sessions
|
|
31
|
+
--
|
|
32
|
+
-- Trigger messages keep the same stable error codes so CLI callers surface
|
|
33
|
+
-- identical remediation regardless of which physical table fired.
|
|
34
|
+
--
|
|
35
|
+
-- @task T11884
|
|
36
|
+
-- @epic T11883
|
|
37
|
+
-- @saga T11242
|
|
38
|
+
|
|
39
|
+
DROP TRIGGER IF EXISTS `tasks_tasks_parent_cycle_guard_insert`;
|
|
40
|
+
--> statement-breakpoint
|
|
41
|
+
DROP TRIGGER IF EXISTS `tasks_tasks_parent_cycle_guard_update`;
|
|
42
|
+
--> statement-breakpoint
|
|
43
|
+
DROP TRIGGER IF EXISTS `tasks_tasks_parent_type_matrix_insert`;
|
|
44
|
+
--> statement-breakpoint
|
|
45
|
+
DROP TRIGGER IF EXISTS `tasks_tasks_parent_type_matrix_update`;
|
|
46
|
+
--> statement-breakpoint
|
|
47
|
+
DROP TRIGGER IF EXISTS `trg_tasks_tasks_status_pipeline_insert`;
|
|
48
|
+
--> statement-breakpoint
|
|
49
|
+
DROP TRIGGER IF EXISTS `trg_tasks_tasks_status_pipeline_update`;
|
|
50
|
+
--> statement-breakpoint
|
|
51
|
+
DROP TRIGGER IF EXISTS `tasks_task_relations_non_containment_insert`;
|
|
52
|
+
--> statement-breakpoint
|
|
53
|
+
DROP TRIGGER IF EXISTS `tasks_task_relations_non_containment_update`;
|
|
54
|
+
--> statement-breakpoint
|
|
55
|
+
DROP TRIGGER IF EXISTS `tasks_task_acceptance_child_target_insert`;
|
|
56
|
+
--> statement-breakpoint
|
|
57
|
+
DROP TRIGGER IF EXISTS `tasks_task_acceptance_child_target_update`;
|
|
58
|
+
--> statement-breakpoint
|
|
59
|
+
DROP TRIGGER IF EXISTS `trg_tasks_session_handoff_mirror`;
|
|
60
|
+
--> statement-breakpoint
|
|
61
|
+
DROP TRIGGER IF EXISTS `trg_tasks_session_handoff_no_update`;
|
|
62
|
+
--> statement-breakpoint
|
|
63
|
+
|
|
64
|
+
-- ---------------------------------------------------------------------------
|
|
65
|
+
-- Parent-cycle guard (T10572) — tasks_tasks
|
|
66
|
+
-- ---------------------------------------------------------------------------
|
|
67
|
+
CREATE TRIGGER `tasks_tasks_parent_cycle_guard_insert`
|
|
68
|
+
BEFORE INSERT ON `tasks_tasks`
|
|
69
|
+
WHEN NEW.`parent_id` IS NOT NULL
|
|
70
|
+
BEGIN
|
|
71
|
+
SELECT RAISE(ABORT, 'E_TASK_PARENT_CYCLE: tasks.parent_id cannot create a containment cycle')
|
|
72
|
+
WHERE EXISTS (
|
|
73
|
+
WITH RECURSIVE ancestors(`id`, `parent_id`) AS (
|
|
74
|
+
SELECT parent.`id`, parent.`parent_id`
|
|
75
|
+
FROM `tasks_tasks` parent
|
|
76
|
+
WHERE parent.`id` = NEW.`parent_id`
|
|
77
|
+
UNION ALL
|
|
78
|
+
SELECT next_parent.`id`, next_parent.`parent_id`
|
|
79
|
+
FROM `tasks_tasks` next_parent
|
|
80
|
+
JOIN ancestors ON next_parent.`id` = ancestors.`parent_id`
|
|
81
|
+
WHERE ancestors.`parent_id` IS NOT NULL
|
|
82
|
+
)
|
|
83
|
+
SELECT 1 FROM ancestors WHERE ancestors.`id` = NEW.`id`
|
|
84
|
+
);
|
|
85
|
+
END;
|
|
86
|
+
--> statement-breakpoint
|
|
87
|
+
|
|
88
|
+
CREATE TRIGGER `tasks_tasks_parent_cycle_guard_update`
|
|
89
|
+
BEFORE UPDATE OF `parent_id` ON `tasks_tasks`
|
|
90
|
+
WHEN NEW.`parent_id` IS NOT NULL
|
|
91
|
+
BEGIN
|
|
92
|
+
SELECT RAISE(ABORT, 'E_TASK_PARENT_CYCLE: tasks.parent_id cannot create a containment cycle')
|
|
93
|
+
WHERE EXISTS (
|
|
94
|
+
WITH RECURSIVE ancestors(`id`, `parent_id`) AS (
|
|
95
|
+
SELECT parent.`id`, parent.`parent_id`
|
|
96
|
+
FROM `tasks_tasks` parent
|
|
97
|
+
WHERE parent.`id` = NEW.`parent_id`
|
|
98
|
+
UNION ALL
|
|
99
|
+
SELECT next_parent.`id`, next_parent.`parent_id`
|
|
100
|
+
FROM `tasks_tasks` next_parent
|
|
101
|
+
JOIN ancestors ON next_parent.`id` = ancestors.`parent_id`
|
|
102
|
+
WHERE ancestors.`parent_id` IS NOT NULL
|
|
103
|
+
)
|
|
104
|
+
SELECT 1 FROM ancestors WHERE ancestors.`id` = NEW.`id`
|
|
105
|
+
);
|
|
106
|
+
END;
|
|
107
|
+
--> statement-breakpoint
|
|
108
|
+
|
|
109
|
+
-- ---------------------------------------------------------------------------
|
|
110
|
+
-- Parent type-matrix guard (T10638) — tasks_tasks
|
|
111
|
+
-- ---------------------------------------------------------------------------
|
|
112
|
+
CREATE TRIGGER `tasks_tasks_parent_type_matrix_insert`
|
|
113
|
+
BEFORE INSERT ON `tasks_tasks`
|
|
114
|
+
WHEN NEW.`parent_id` IS NOT NULL
|
|
115
|
+
AND NEW.`type` IS NOT NULL
|
|
116
|
+
AND EXISTS (SELECT 1 FROM `tasks_tasks` parent WHERE parent.`id` = NEW.`parent_id` AND parent.`type` IS NOT NULL)
|
|
117
|
+
AND NOT EXISTS (
|
|
118
|
+
SELECT 1
|
|
119
|
+
FROM `tasks_tasks` parent
|
|
120
|
+
WHERE parent.`id` = NEW.`parent_id`
|
|
121
|
+
AND (
|
|
122
|
+
(NEW.`type` = 'epic' AND parent.`type` = 'saga')
|
|
123
|
+
OR (NEW.`type` = 'task' AND parent.`type` = 'epic')
|
|
124
|
+
OR (NEW.`type` = 'subtask' AND parent.`type` IN ('epic', 'task'))
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
BEGIN
|
|
128
|
+
SELECT RAISE(ABORT, 'E_TASK_PARENT_TYPE_MATRIX: tasks.parent_id must follow saga->epic, epic->task|subtask, and task->subtask; sagas/tasks must be roots');
|
|
129
|
+
END;
|
|
130
|
+
--> statement-breakpoint
|
|
131
|
+
|
|
132
|
+
CREATE TRIGGER `tasks_tasks_parent_type_matrix_update`
|
|
133
|
+
BEFORE UPDATE OF `parent_id`, `type` ON `tasks_tasks`
|
|
134
|
+
WHEN NEW.`parent_id` IS NOT NULL
|
|
135
|
+
AND NEW.`type` IS NOT NULL
|
|
136
|
+
AND EXISTS (SELECT 1 FROM `tasks_tasks` parent WHERE parent.`id` = NEW.`parent_id` AND parent.`type` IS NOT NULL)
|
|
137
|
+
AND NOT EXISTS (
|
|
138
|
+
SELECT 1
|
|
139
|
+
FROM `tasks_tasks` parent
|
|
140
|
+
WHERE parent.`id` = NEW.`parent_id`
|
|
141
|
+
AND (
|
|
142
|
+
(NEW.`type` = 'epic' AND parent.`type` = 'saga')
|
|
143
|
+
OR (NEW.`type` = 'task' AND parent.`type` = 'epic')
|
|
144
|
+
OR (NEW.`type` = 'subtask' AND parent.`type` IN ('epic', 'task'))
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
BEGIN
|
|
148
|
+
SELECT RAISE(ABORT, 'E_TASK_PARENT_TYPE_MATRIX: tasks.parent_id must follow saga->epic, epic->task|subtask, and task->subtask; sagas/tasks must be roots');
|
|
149
|
+
END;
|
|
150
|
+
--> statement-breakpoint
|
|
151
|
+
|
|
152
|
+
-- ---------------------------------------------------------------------------
|
|
153
|
+
-- Status / pipeline_stage invariant (T877) — tasks_tasks
|
|
154
|
+
-- ---------------------------------------------------------------------------
|
|
155
|
+
CREATE TRIGGER `trg_tasks_tasks_status_pipeline_insert`
|
|
156
|
+
BEFORE INSERT ON `tasks_tasks`
|
|
157
|
+
FOR EACH ROW
|
|
158
|
+
WHEN (NEW.`status` = 'done' AND (NEW.`pipeline_stage` IS NULL OR NEW.`pipeline_stage` NOT IN ('contribution','cancelled')))
|
|
159
|
+
OR (NEW.`status` = 'cancelled' AND (NEW.`pipeline_stage` IS NULL OR NEW.`pipeline_stage` != 'cancelled'))
|
|
160
|
+
BEGIN
|
|
161
|
+
SELECT RAISE(ABORT, 'T877_INVARIANT_VIOLATION: status/pipeline_stage mismatch. status=done requires pipeline_stage IN (contribution,cancelled); status=cancelled requires pipeline_stage=cancelled.');
|
|
162
|
+
END;
|
|
163
|
+
--> statement-breakpoint
|
|
164
|
+
|
|
165
|
+
CREATE TRIGGER `trg_tasks_tasks_status_pipeline_update`
|
|
166
|
+
BEFORE UPDATE OF `status`, `pipeline_stage` ON `tasks_tasks`
|
|
167
|
+
FOR EACH ROW
|
|
168
|
+
WHEN (NEW.`status` = 'done' AND (NEW.`pipeline_stage` IS NULL OR NEW.`pipeline_stage` NOT IN ('contribution','cancelled')))
|
|
169
|
+
OR (NEW.`status` = 'cancelled' AND (NEW.`pipeline_stage` IS NULL OR NEW.`pipeline_stage` != 'cancelled'))
|
|
170
|
+
BEGIN
|
|
171
|
+
SELECT RAISE(ABORT, 'T877_INVARIANT_VIOLATION: status/pipeline_stage mismatch. status=done requires pipeline_stage IN (contribution,cancelled); status=cancelled requires pipeline_stage=cancelled.');
|
|
172
|
+
END;
|
|
173
|
+
--> statement-breakpoint
|
|
174
|
+
|
|
175
|
+
-- ---------------------------------------------------------------------------
|
|
176
|
+
-- Non-containment guard (T10572) — tasks_task_relations
|
|
177
|
+
-- ---------------------------------------------------------------------------
|
|
178
|
+
CREATE TRIGGER `tasks_task_relations_non_containment_insert`
|
|
179
|
+
BEFORE INSERT ON `tasks_task_relations`
|
|
180
|
+
WHEN EXISTS (
|
|
181
|
+
SELECT 1
|
|
182
|
+
FROM `tasks_tasks` child
|
|
183
|
+
WHERE child.`id` = NEW.`task_id`
|
|
184
|
+
AND child.`parent_id` = NEW.`related_to`
|
|
185
|
+
) OR EXISTS (
|
|
186
|
+
SELECT 1
|
|
187
|
+
FROM `tasks_tasks` child
|
|
188
|
+
WHERE child.`id` = NEW.`related_to`
|
|
189
|
+
AND child.`parent_id` = NEW.`task_id`
|
|
190
|
+
)
|
|
191
|
+
BEGIN
|
|
192
|
+
SELECT RAISE(ABORT, 'E_TASK_RELATION_CONTAINMENT: task_relations is non-containment-only; use tasks.parent_id for parent/child edges');
|
|
193
|
+
END;
|
|
194
|
+
--> statement-breakpoint
|
|
195
|
+
|
|
196
|
+
CREATE TRIGGER `tasks_task_relations_non_containment_update`
|
|
197
|
+
BEFORE UPDATE OF `task_id`, `related_to` ON `tasks_task_relations`
|
|
198
|
+
WHEN EXISTS (
|
|
199
|
+
SELECT 1
|
|
200
|
+
FROM `tasks_tasks` child
|
|
201
|
+
WHERE child.`id` = NEW.`task_id`
|
|
202
|
+
AND child.`parent_id` = NEW.`related_to`
|
|
203
|
+
) OR EXISTS (
|
|
204
|
+
SELECT 1
|
|
205
|
+
FROM `tasks_tasks` child
|
|
206
|
+
WHERE child.`id` = NEW.`related_to`
|
|
207
|
+
AND child.`parent_id` = NEW.`task_id`
|
|
208
|
+
)
|
|
209
|
+
BEGIN
|
|
210
|
+
SELECT RAISE(ABORT, 'E_TASK_RELATION_CONTAINMENT: task_relations is non-containment-only; use tasks.parent_id for parent/child edges');
|
|
211
|
+
END;
|
|
212
|
+
--> statement-breakpoint
|
|
213
|
+
|
|
214
|
+
-- ---------------------------------------------------------------------------
|
|
215
|
+
-- Acceptance child_target containment guard (T10572) — tasks_task_acceptance_criteria
|
|
216
|
+
-- ---------------------------------------------------------------------------
|
|
217
|
+
CREATE TRIGGER `tasks_task_acceptance_child_target_insert`
|
|
218
|
+
BEFORE INSERT ON `tasks_task_acceptance_criteria`
|
|
219
|
+
WHEN NEW.`target_task_id` IS NOT NULL
|
|
220
|
+
AND (
|
|
221
|
+
NEW.`kind` <> 'child_task'
|
|
222
|
+
OR NOT EXISTS (
|
|
223
|
+
SELECT 1
|
|
224
|
+
FROM `tasks_tasks` child
|
|
225
|
+
WHERE child.`id` = NEW.`target_task_id`
|
|
226
|
+
AND child.`parent_id` = NEW.`task_id`
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
BEGIN
|
|
230
|
+
SELECT RAISE(ABORT, 'E_CHILD_TASK_TARGET_CONTAINMENT: child_task acceptance target_task_id must be a direct child of task_id; non-child_task criteria must not set target_task_id');
|
|
231
|
+
END;
|
|
232
|
+
--> statement-breakpoint
|
|
233
|
+
|
|
234
|
+
CREATE TRIGGER `tasks_task_acceptance_child_target_update`
|
|
235
|
+
BEFORE UPDATE OF `task_id`, `kind`, `target_task_id` ON `tasks_task_acceptance_criteria`
|
|
236
|
+
WHEN NEW.`target_task_id` IS NOT NULL
|
|
237
|
+
AND (
|
|
238
|
+
NEW.`kind` <> 'child_task'
|
|
239
|
+
OR NOT EXISTS (
|
|
240
|
+
SELECT 1
|
|
241
|
+
FROM `tasks_tasks` child
|
|
242
|
+
WHERE child.`id` = NEW.`target_task_id`
|
|
243
|
+
AND child.`parent_id` = NEW.`task_id`
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
BEGIN
|
|
247
|
+
SELECT RAISE(ABORT, 'E_CHILD_TASK_TARGET_CONTAINMENT: child_task acceptance target_task_id must be a direct child of task_id; non-child_task criteria must not set target_task_id');
|
|
248
|
+
END;
|
|
249
|
+
--> statement-breakpoint
|
|
250
|
+
|
|
251
|
+
-- ---------------------------------------------------------------------------
|
|
252
|
+
-- Session-handoff write-once + mirror (T1609) — tasks_session_handoff_entries
|
|
253
|
+
-- ---------------------------------------------------------------------------
|
|
254
|
+
CREATE TRIGGER `trg_tasks_session_handoff_mirror`
|
|
255
|
+
AFTER INSERT ON `tasks_session_handoff_entries`
|
|
256
|
+
FOR EACH ROW
|
|
257
|
+
BEGIN
|
|
258
|
+
UPDATE `tasks_sessions`
|
|
259
|
+
SET `handoff_json` = NEW.handoff_json
|
|
260
|
+
WHERE `id` = NEW.session_id;
|
|
261
|
+
END;
|
|
262
|
+
--> statement-breakpoint
|
|
263
|
+
|
|
264
|
+
CREATE TRIGGER `trg_tasks_session_handoff_no_update`
|
|
265
|
+
BEFORE UPDATE ON `tasks_session_handoff_entries`
|
|
266
|
+
FOR EACH ROW
|
|
267
|
+
BEGIN
|
|
268
|
+
SELECT RAISE(
|
|
269
|
+
ABORT,
|
|
270
|
+
'T1609_HANDOFF_IMMUTABLE: session_handoff_entries rows are write-once. Use persistHandoff() exactly once per session.'
|
|
271
|
+
);
|
|
272
|
+
END;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- Revert T11884 — drop the prefixed-table invariant + handoff triggers.
|
|
2
|
+
-- The legacy bare-table triggers are untouched (they remain the active guards
|
|
3
|
+
-- until the E5 cutover drops the bare tables).
|
|
4
|
+
--
|
|
5
|
+
-- @task T11884
|
|
6
|
+
|
|
7
|
+
DROP TRIGGER IF EXISTS `tasks_tasks_parent_cycle_guard_insert`;
|
|
8
|
+
--> statement-breakpoint
|
|
9
|
+
DROP TRIGGER IF EXISTS `tasks_tasks_parent_cycle_guard_update`;
|
|
10
|
+
--> statement-breakpoint
|
|
11
|
+
DROP TRIGGER IF EXISTS `tasks_tasks_parent_type_matrix_insert`;
|
|
12
|
+
--> statement-breakpoint
|
|
13
|
+
DROP TRIGGER IF EXISTS `tasks_tasks_parent_type_matrix_update`;
|
|
14
|
+
--> statement-breakpoint
|
|
15
|
+
DROP TRIGGER IF EXISTS `trg_tasks_tasks_status_pipeline_insert`;
|
|
16
|
+
--> statement-breakpoint
|
|
17
|
+
DROP TRIGGER IF EXISTS `trg_tasks_tasks_status_pipeline_update`;
|
|
18
|
+
--> statement-breakpoint
|
|
19
|
+
DROP TRIGGER IF EXISTS `tasks_task_relations_non_containment_insert`;
|
|
20
|
+
--> statement-breakpoint
|
|
21
|
+
DROP TRIGGER IF EXISTS `tasks_task_relations_non_containment_update`;
|
|
22
|
+
--> statement-breakpoint
|
|
23
|
+
DROP TRIGGER IF EXISTS `tasks_task_acceptance_child_target_insert`;
|
|
24
|
+
--> statement-breakpoint
|
|
25
|
+
DROP TRIGGER IF EXISTS `tasks_task_acceptance_child_target_update`;
|
|
26
|
+
--> statement-breakpoint
|
|
27
|
+
DROP TRIGGER IF EXISTS `trg_tasks_session_handoff_mirror`;
|
|
28
|
+
--> statement-breakpoint
|
|
29
|
+
DROP TRIGGER IF EXISTS `trg_tasks_session_handoff_no_update`;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- T11883 (E2) — Repair the missing UNIQUE index on tasks_releases.version.
|
|
2
|
+
--
|
|
3
|
+
-- The drizzle schema declares `version: text('version').notNull().unique()`
|
|
4
|
+
-- (packages/core/src/store/schema/cleo-project/provenance-rest.ts:201), but the
|
|
5
|
+
-- T11363 consolidation migration that built `tasks_releases` emitted only a
|
|
6
|
+
-- NON-unique `idx_tasks_releases_version` — the UNIQUE was dropped during the
|
|
7
|
+
-- exodus build. `cleo release plan` upserts with
|
|
8
|
+
-- `onConflictDoUpdate({ target: releases.version })`, which REQUIRES a physical
|
|
9
|
+
-- unique index on `version`; once the provenance `releases` symbol is rebound to
|
|
10
|
+
-- `tasks_releases` (E3) the upsert would otherwise throw "ON CONFLICT clause
|
|
11
|
+
-- does not match any PRIMARY KEY or UNIQUE constraint". This is a pure
|
|
12
|
+
-- drift-repair (schema↔physical), so it ships as a custom migration rather than
|
|
13
|
+
-- a generated one (generate has no committed snapshot to diff an index against).
|
|
14
|
+
--
|
|
15
|
+
-- Safe: the live corpus has 0 duplicate versions (74 rows), and fresh/test DBs
|
|
16
|
+
-- are empty. IF NOT EXISTS keeps it idempotent under the reconciler's re-probe.
|
|
17
|
+
--
|
|
18
|
+
-- @task T11883
|
|
19
|
+
-- @saga T11242
|
|
20
|
+
|
|
21
|
+
CREATE UNIQUE INDEX IF NOT EXISTS `uq_tasks_releases_version` ON `tasks_releases` (`version`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleocode/core",
|
|
3
|
-
"version": "2026.6.
|
|
3
|
+
"version": "2026.6.12",
|
|
4
4
|
"description": "CLEO core business logic kernel — tasks, sessions, memory, orchestration, lifecycle, with bundled SQLite store",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -429,16 +429,16 @@
|
|
|
429
429
|
"write-file-atomic": "^7.0.1",
|
|
430
430
|
"yaml": "^2.8.3",
|
|
431
431
|
"zod": "^4.3.6",
|
|
432
|
-
"@cleocode/
|
|
433
|
-
"@cleocode/agents": "2026.6.
|
|
434
|
-
"@cleocode/contracts": "2026.6.
|
|
435
|
-
"@cleocode/
|
|
436
|
-
"@cleocode/
|
|
437
|
-
"@cleocode/lafs": "2026.6.
|
|
438
|
-
"@cleocode/nexus": "2026.6.
|
|
439
|
-
"@cleocode/
|
|
440
|
-
"@cleocode/
|
|
441
|
-
"@cleocode/
|
|
432
|
+
"@cleocode/caamp": "2026.6.12",
|
|
433
|
+
"@cleocode/agents": "2026.6.12",
|
|
434
|
+
"@cleocode/contracts": "2026.6.12",
|
|
435
|
+
"@cleocode/adapters": "2026.6.12",
|
|
436
|
+
"@cleocode/git-shim": "2026.6.12",
|
|
437
|
+
"@cleocode/lafs": "2026.6.12",
|
|
438
|
+
"@cleocode/nexus": "2026.6.12",
|
|
439
|
+
"@cleocode/worktree": "2026.6.12",
|
|
440
|
+
"@cleocode/paths": "2026.6.12",
|
|
441
|
+
"@cleocode/skills": "2026.6.12"
|
|
442
442
|
},
|
|
443
443
|
"engines": {
|
|
444
444
|
"node": ">=24.16.0"
|
|
@@ -472,7 +472,7 @@
|
|
|
472
472
|
},
|
|
473
473
|
"optionalDependencies": {
|
|
474
474
|
"llmtxt": "^2026.5.15",
|
|
475
|
-
"@cleocode/studio": "^2026.6.
|
|
475
|
+
"@cleocode/studio": "^2026.6.12"
|
|
476
476
|
},
|
|
477
477
|
"scripts": {
|
|
478
478
|
"build": "tsc",
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Post-cutover provenance `task_id` / `epic_id` FK reconciliation (DHQ-051).
|
|
3
|
-
*
|
|
4
|
-
* Shared between `cleo release reconcile` ({@link releaseReconcileV2}) and
|
|
5
|
-
* `cleo release plan` ({@link releasePlan} → `upsertReleasesRow`). Both verbs
|
|
6
|
-
* write provenance rows (`task_commits`, `pr_tasks`, `releases`,
|
|
7
|
-
* `release_changes`) whose `task_id` / `epic_id` foreign keys reference the
|
|
8
|
-
* BARE `tasks` table — which is empty on a consolidated dual-scope `cleo.db`
|
|
9
|
-
* after the T11578 cutover (runtime task data moved to the prefixed
|
|
10
|
-
* `tasks_tasks` table). A row inserted with an `epicId` / `taskId` that exists
|
|
11
|
-
* only in `tasks_tasks` violates the FK at INSERT time, aborting the verb.
|
|
12
|
-
*
|
|
13
|
-
* This module is the single source of truth for the FK-ordered parent backfill
|
|
14
|
-
* that makes those FKs satisfiable. `reconcile.ts` re-exports both symbols so
|
|
15
|
-
* its public surface is unchanged.
|
|
16
|
-
*
|
|
17
|
-
* @task T11659 — reconcile slice (original)
|
|
18
|
-
* @task T11818 — plan/open slice (DRY hoist)
|
|
19
|
-
* @epic T11466
|
|
20
|
-
*/
|
|
21
|
-
import type { DatabaseSync } from '../store/sqlite.js';
|
|
22
|
-
/** Result of resolving + reconciling the provenance `task_id` FK parent table. */
|
|
23
|
-
export interface FkParentTaskResolution {
|
|
24
|
-
/** Physical table the provenance `task_id` FK references, or null if absent. */
|
|
25
|
-
parentTable: string | null;
|
|
26
|
-
/**
|
|
27
|
-
* Task IDs the FK can now resolve (FK parent rows present after any
|
|
28
|
-
* FK-ordered shim backfill). A `task_id` reference to an id in this set is
|
|
29
|
-
* safe to INSERT under strict FK enforcement.
|
|
30
|
-
*/
|
|
31
|
-
resolvableIds: Set<string>;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Make the provenance `task_id` foreign keys satisfiable on a consolidated
|
|
35
|
-
* `cleo.db` by inserting any missing FK-parent rows in FK order (parent-before
|
|
36
|
-
* -child), then returning the set of IDs the FK can resolve.
|
|
37
|
-
*
|
|
38
|
-
* ## Background (DHQ-051 · T11659 / T11818 — same class as DHQ-045)
|
|
39
|
-
*
|
|
40
|
-
* After the dual-scope `cleo.db` cutover (T11578) the runtime task store moved
|
|
41
|
-
* from the bare `tasks` table to the prefixed `tasks_tasks` table. The drizzle
|
|
42
|
-
* `schema.tasks` symbol is shadowed at the barrel (`tasks-schema.ts`) onto
|
|
43
|
-
* `tasks_tasks`, so token-validity probes read the populated prefixed table.
|
|
44
|
-
* BUT the provenance tables (`task_commits`, `pr_tasks`, `releases`,
|
|
45
|
-
* `release_changes`) predate the cutover — their `task_id` / `epic_id` FKs
|
|
46
|
-
* still reference the BARE `tasks` table, which is empty on a consolidated
|
|
47
|
-
* `cleo.db`. A token validated against `tasks_tasks` therefore passes the
|
|
48
|
-
* legacy gate yet violates the FK at INSERT time, aborting the whole verb with
|
|
49
|
-
* `E_PROVENANCE_FAILED` (reconcile) or `E_INTERNAL` (plan).
|
|
50
|
-
*
|
|
51
|
-
* ## Strategy — FK-ordered parent backfill (Strategy 1)
|
|
52
|
-
*
|
|
53
|
-
* For every task referenced by this release we ensure a row exists in the FK
|
|
54
|
-
* parent table BEFORE the child provenance rows are written. The parent table
|
|
55
|
-
* is discovered dynamically via `PRAGMA foreign_key_list('task_commits')` (so
|
|
56
|
-
* the logic tracks whatever the live schema enforces — including a future
|
|
57
|
-
* schema that repoints the FK at `tasks_tasks`, where this becomes a no-op).
|
|
58
|
-
* Missing parent rows are copied from the runtime `tasks_tasks` store via
|
|
59
|
-
* `INSERT OR IGNORE … SELECT` of only the NOT NULL columns, so the copied rows
|
|
60
|
-
* satisfy the parent table's own CHECK/NOT NULL constraints. Tasks that exist
|
|
61
|
-
* in neither table remain unresolvable and are skipped-with-warn / NULLed by
|
|
62
|
-
* the callers (the row, never the whole verb).
|
|
63
|
-
*
|
|
64
|
-
* Idempotent: `INSERT OR IGNORE` never duplicates and never overwrites an
|
|
65
|
-
* existing parent row. Callers run it inside their write transaction so a later
|
|
66
|
-
* failure rolls the shim rows back too.
|
|
67
|
-
*
|
|
68
|
-
* @param nativeDb - The consolidated `cleo.db` native handle.
|
|
69
|
-
* @param referencedIds - Every task id this verb may reference (commit tokens,
|
|
70
|
-
* PR tokens, plan tasks, epic).
|
|
71
|
-
* @returns The FK parent table name + the IDs it can now resolve.
|
|
72
|
-
*/
|
|
73
|
-
export declare function ensureProvenanceTaskFkParents(nativeDb: DatabaseSync, referencedIds: ReadonlySet<string>): FkParentTaskResolution;
|
|
74
|
-
//# sourceMappingURL=provenance-fk.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"provenance-fk.d.ts","sourceRoot":"","sources":["../../src/release/provenance-fk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,kFAAkF;AAClF,MAAM,WAAW,sBAAsB;IACrC,gFAAgF;IAChF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;OAIG;IACH,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,YAAY,EACtB,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,GACjC,sBAAsB,CAmExB"}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Post-cutover provenance `task_id` / `epic_id` FK reconciliation (DHQ-051).
|
|
3
|
-
*
|
|
4
|
-
* Shared between `cleo release reconcile` ({@link releaseReconcileV2}) and
|
|
5
|
-
* `cleo release plan` ({@link releasePlan} → `upsertReleasesRow`). Both verbs
|
|
6
|
-
* write provenance rows (`task_commits`, `pr_tasks`, `releases`,
|
|
7
|
-
* `release_changes`) whose `task_id` / `epic_id` foreign keys reference the
|
|
8
|
-
* BARE `tasks` table — which is empty on a consolidated dual-scope `cleo.db`
|
|
9
|
-
* after the T11578 cutover (runtime task data moved to the prefixed
|
|
10
|
-
* `tasks_tasks` table). A row inserted with an `epicId` / `taskId` that exists
|
|
11
|
-
* only in `tasks_tasks` violates the FK at INSERT time, aborting the verb.
|
|
12
|
-
*
|
|
13
|
-
* This module is the single source of truth for the FK-ordered parent backfill
|
|
14
|
-
* that makes those FKs satisfiable. `reconcile.ts` re-exports both symbols so
|
|
15
|
-
* its public surface is unchanged.
|
|
16
|
-
*
|
|
17
|
-
* @task T11659 — reconcile slice (original)
|
|
18
|
-
* @task T11818 — plan/open slice (DRY hoist)
|
|
19
|
-
* @epic T11466
|
|
20
|
-
*/
|
|
21
|
-
import { getLogger } from '../logger.js';
|
|
22
|
-
const log = getLogger('release:provenance-fk');
|
|
23
|
-
/**
|
|
24
|
-
* Make the provenance `task_id` foreign keys satisfiable on a consolidated
|
|
25
|
-
* `cleo.db` by inserting any missing FK-parent rows in FK order (parent-before
|
|
26
|
-
* -child), then returning the set of IDs the FK can resolve.
|
|
27
|
-
*
|
|
28
|
-
* ## Background (DHQ-051 · T11659 / T11818 — same class as DHQ-045)
|
|
29
|
-
*
|
|
30
|
-
* After the dual-scope `cleo.db` cutover (T11578) the runtime task store moved
|
|
31
|
-
* from the bare `tasks` table to the prefixed `tasks_tasks` table. The drizzle
|
|
32
|
-
* `schema.tasks` symbol is shadowed at the barrel (`tasks-schema.ts`) onto
|
|
33
|
-
* `tasks_tasks`, so token-validity probes read the populated prefixed table.
|
|
34
|
-
* BUT the provenance tables (`task_commits`, `pr_tasks`, `releases`,
|
|
35
|
-
* `release_changes`) predate the cutover — their `task_id` / `epic_id` FKs
|
|
36
|
-
* still reference the BARE `tasks` table, which is empty on a consolidated
|
|
37
|
-
* `cleo.db`. A token validated against `tasks_tasks` therefore passes the
|
|
38
|
-
* legacy gate yet violates the FK at INSERT time, aborting the whole verb with
|
|
39
|
-
* `E_PROVENANCE_FAILED` (reconcile) or `E_INTERNAL` (plan).
|
|
40
|
-
*
|
|
41
|
-
* ## Strategy — FK-ordered parent backfill (Strategy 1)
|
|
42
|
-
*
|
|
43
|
-
* For every task referenced by this release we ensure a row exists in the FK
|
|
44
|
-
* parent table BEFORE the child provenance rows are written. The parent table
|
|
45
|
-
* is discovered dynamically via `PRAGMA foreign_key_list('task_commits')` (so
|
|
46
|
-
* the logic tracks whatever the live schema enforces — including a future
|
|
47
|
-
* schema that repoints the FK at `tasks_tasks`, where this becomes a no-op).
|
|
48
|
-
* Missing parent rows are copied from the runtime `tasks_tasks` store via
|
|
49
|
-
* `INSERT OR IGNORE … SELECT` of only the NOT NULL columns, so the copied rows
|
|
50
|
-
* satisfy the parent table's own CHECK/NOT NULL constraints. Tasks that exist
|
|
51
|
-
* in neither table remain unresolvable and are skipped-with-warn / NULLed by
|
|
52
|
-
* the callers (the row, never the whole verb).
|
|
53
|
-
*
|
|
54
|
-
* Idempotent: `INSERT OR IGNORE` never duplicates and never overwrites an
|
|
55
|
-
* existing parent row. Callers run it inside their write transaction so a later
|
|
56
|
-
* failure rolls the shim rows back too.
|
|
57
|
-
*
|
|
58
|
-
* @param nativeDb - The consolidated `cleo.db` native handle.
|
|
59
|
-
* @param referencedIds - Every task id this verb may reference (commit tokens,
|
|
60
|
-
* PR tokens, plan tasks, epic).
|
|
61
|
-
* @returns The FK parent table name + the IDs it can now resolve.
|
|
62
|
-
*/
|
|
63
|
-
export function ensureProvenanceTaskFkParents(nativeDb, referencedIds) {
|
|
64
|
-
// Discover the physical parent table the `task_commits.task_id` FK targets.
|
|
65
|
-
let parentTable = 'tasks';
|
|
66
|
-
try {
|
|
67
|
-
const fks = nativeDb.prepare(`PRAGMA foreign_key_list('task_commits')`).all();
|
|
68
|
-
const taskFk = fks.find((fk) => fk.from === 'task_id');
|
|
69
|
-
if (taskFk?.table)
|
|
70
|
-
parentTable = taskFk.table;
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
// PRAGMA unavailable (table absent / older sqlite) — keep the bare default.
|
|
74
|
-
}
|
|
75
|
-
const tableExists = (name) => nativeDb.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name=?`).get(name) !=
|
|
76
|
-
null;
|
|
77
|
-
// Parent table absent entirely → nothing resolvable, nothing to backfill.
|
|
78
|
-
if (!tableExists(parentTable)) {
|
|
79
|
-
return { parentTable: null, resolvableIds: new Set() };
|
|
80
|
-
}
|
|
81
|
-
// FK-ordered shim backfill: copy any referenced task that lives in the
|
|
82
|
-
// runtime `tasks_tasks` store but is missing from the FK parent. Only when
|
|
83
|
-
// the parent is the bare legacy table AND the prefixed store exists (i.e. the
|
|
84
|
-
// post-cutover split-brain) — when the FK already points at `tasks_tasks`
|
|
85
|
-
// this is skipped and the parent is read as-is.
|
|
86
|
-
// Identifiers come from sqlite_master / the FK pragma (verified existing
|
|
87
|
-
// table names), never user input — safe to interpolate (node:sqlite exposes
|
|
88
|
-
// no bound-identifier form). All VALUES are bound parameters.
|
|
89
|
-
if (parentTable !== 'tasks_tasks' && tableExists('tasks_tasks') && referencedIds.size > 0) {
|
|
90
|
-
// `pipeline_stage` is copied alongside the NOT NULL columns so the bare
|
|
91
|
-
// `tasks` table's T877 invariant trigger (status='done' ⇒ pipeline_stage IN
|
|
92
|
-
// ('contribution','cancelled'); status='cancelled' ⇒ pipeline_stage=
|
|
93
|
-
// 'cancelled') is satisfied. A released epic is typically status='done', so
|
|
94
|
-
// omitting `pipeline_stage` would RAISE(ABORT) the shim copy and leave the
|
|
95
|
-
// epic unresolvable (DHQ-051 · T11818).
|
|
96
|
-
const insertShim = nativeDb.prepare(`INSERT OR IGNORE INTO "${parentTable}" (id, title, status, priority, role, scope, pipeline_stage)
|
|
97
|
-
SELECT id, title, status, priority, role, scope, pipeline_stage FROM tasks_tasks WHERE id = ?`);
|
|
98
|
-
for (const id of referencedIds) {
|
|
99
|
-
try {
|
|
100
|
-
insertShim.run(id);
|
|
101
|
-
}
|
|
102
|
-
catch (err) {
|
|
103
|
-
// A single shim copy that violates the parent's own constraints (e.g. a
|
|
104
|
-
// NOT NULL / CHECK the prefixed row does not satisfy) must NOT abort the
|
|
105
|
-
// whole verb. Leave the id unresolvable — the writers below
|
|
106
|
-
// skip-with-warn that one link (Strategy 3). SAVEPOINT-free: the failed
|
|
107
|
-
// statement is rolled back individually by SQLite, leaving the
|
|
108
|
-
// surrounding transaction intact.
|
|
109
|
-
log.warn({ taskId: id, parentTable, err: err instanceof Error ? err.message : String(err) }, 'provenance: FK-parent shim copy failed — task_id stays unresolvable, link will be skipped');
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// Read back what the FK can now resolve.
|
|
114
|
-
const resolvableIds = new Set();
|
|
115
|
-
const rows = nativeDb.prepare(`SELECT id FROM "${parentTable}"`).all();
|
|
116
|
-
for (const r of rows) {
|
|
117
|
-
if (typeof r.id === 'string')
|
|
118
|
-
resolvableIds.add(r.id);
|
|
119
|
-
}
|
|
120
|
-
return { parentTable, resolvableIds };
|
|
121
|
-
}
|
|
122
|
-
//# sourceMappingURL=provenance-fk.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"provenance-fk.js","sourceRoot":"","sources":["../../src/release/provenance-fk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,MAAM,GAAG,GAAG,SAAS,CAAC,uBAAuB,CAAC,CAAC;AAc/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,UAAU,6BAA6B,CAC3C,QAAsB,EACtB,aAAkC;IAElC,4EAA4E;IAC5E,IAAI,WAAW,GAAG,OAAO,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAC,GAAG,EAGzE,CAAC;QACH,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;QACvD,IAAI,MAAM,EAAE,KAAK;YAAE,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,4EAA4E;IAC9E,CAAC;IAED,MAAM,WAAW,GAAG,CAAC,IAAY,EAAW,EAAE,CAC5C,QAAQ,CAAC,OAAO,CAAC,8DAA8D,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;QAC1F,IAAI,CAAC;IAEP,0EAA0E;IAC1E,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,GAAG,EAAU,EAAE,CAAC;IACjE,CAAC;IAED,uEAAuE;IACvE,2EAA2E;IAC3E,8EAA8E;IAC9E,0EAA0E;IAC1E,gDAAgD;IAChD,yEAAyE;IACzE,4EAA4E;IAC5E,8DAA8D;IAC9D,IAAI,WAAW,KAAK,aAAa,IAAI,WAAW,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QAC1F,wEAAwE;QACxE,4EAA4E;QAC5E,qEAAqE;QACrE,4EAA4E;QAC5E,2EAA2E;QAC3E,wCAAwC;QACxC,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CACjC,0BAA0B,WAAW;qGAC0D,CAChG,CAAC;QACF,KAAK,MAAM,EAAE,IAAI,aAAa,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACH,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACrB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,wEAAwE;gBACxE,yEAAyE;gBACzE,4DAA4D;gBAC5D,wEAAwE;gBACxE,+DAA+D;gBAC/D,kCAAkC;gBAClC,GAAG,CAAC,IAAI,CACN,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAClF,2FAA2F,CAC5F,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,yCAAyC;IACzC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,mBAAmB,WAAW,GAAG,CAAC,CAAC,GAAG,EAA4B,CAAC;IACjG,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ;YAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC;AACxC,CAAC"}
|