ultravisor 1.0.24 → 1.0.26

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 (25) hide show
  1. package/docs/_sidebar.md +1 -0
  2. package/docs/features/persistence-via-databeacon.md +1211 -0
  3. package/package.json +6 -6
  4. package/source/cli/Ultravisor-CLIProgram.cjs +80 -0
  5. package/source/config/Ultravisor-Default-Command-Configuration.cjs +9 -1
  6. package/source/datamodel/Ultravisor-Fleet.json +66 -0
  7. package/source/persistence/UltravisorPersistenceSchema.json +240 -0
  8. package/source/services/Ultravisor-AuthBeaconBridge.cjs +271 -0
  9. package/source/services/Ultravisor-Beacon-Coordinator.cjs +339 -151
  10. package/source/services/Ultravisor-Beacon-Scheduler.cjs +65 -29
  11. package/source/services/Ultravisor-DirectoryDistributor.cjs +280 -0
  12. package/source/services/Ultravisor-ExecutionManifest.cjs +99 -4
  13. package/source/services/Ultravisor-FleetManager.cjs +871 -0
  14. package/source/services/Ultravisor-ManifestStoreBridge.cjs +1134 -0
  15. package/source/services/Ultravisor-QueuePersistenceBridge.cjs +1336 -0
  16. package/source/services/persistence/Ultravisor-Beacon-FleetStore.cjs +570 -0
  17. package/source/web_server/Ultravisor-API-Server.cjs +1185 -90
  18. package/test/fleetstore-smoke.js +152 -0
  19. package/webinterface/package.json +1 -0
  20. package/webinterface/source/Pict-Application-Ultravisor.js +59 -2
  21. package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +12 -0
  22. package/webinterface/source/views/PictView-Ultravisor-Fleet.js +489 -0
  23. package/webinterface/source/views/PictView-Ultravisor-Login.js +74 -0
  24. package/webinterface/source/views/PictView-Ultravisor-TopBar.js +26 -0
  25. package/webinterface/source/views/PictView-Ultravisor-UserManagement.js +159 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultravisor",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
4
4
  "description": "Cyclic process execution with ai integration.",
5
5
  "main": "source/Ultravisor.cjs",
6
6
  "bin": {
@@ -29,20 +29,20 @@
29
29
  "dependencies": {
30
30
  "better-sqlite3": "^11.10.0",
31
31
  "cron": "^4.4.0",
32
- "orator": "^6.0.4",
33
- "orator-authentication": "^1.0.0",
32
+ "orator": "^6.1.0",
33
+ "orator-authentication": "^1.0.1",
34
34
  "orator-serviceserver-restify": "^2.0.10",
35
- "pict": "^1.0.363",
35
+ "pict": "^1.0.365",
36
36
  "pict-service-commandlineutility": "^1.0.19",
37
37
  "pict-serviceproviderbase": "^1.0.4",
38
- "ultravisor-beacon": "^0.0.11",
38
+ "ultravisor-beacon": "^0.0.13",
39
39
  "ultravisor-file-stream": "^0.0.1",
40
40
  "ws": "^8.20.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "pict-docuserve": "^0.1.5",
44
44
  "puppeteer": "^24.40.0",
45
- "quackage": "^1.1.0"
45
+ "quackage": "^1.1.2"
46
46
  },
47
47
  "mocha": {
48
48
  "diff": true,
@@ -18,6 +18,14 @@ const libServiceExecutionManifest = require('../services/Ultravisor-ExecutionMan
18
18
  const libServiceBeaconCoordinator = require('../services/Ultravisor-Beacon-Coordinator.cjs');
19
19
  const libServiceBeaconReachability = require('../services/Ultravisor-Beacon-Reachability.cjs');
20
20
  const libServiceBeaconQueueJournal = require('../services/persistence/Ultravisor-Beacon-QueueJournal.cjs');
21
+ const libServiceBeaconQueueStore = require('../services/persistence/Ultravisor-Beacon-QueueStore.cjs');
22
+ const libServiceBeaconScheduler = require('../services/Ultravisor-Beacon-Scheduler.cjs');
23
+ const libServiceBeaconFleetStore = require('../services/persistence/Ultravisor-Beacon-FleetStore.cjs');
24
+ const libServiceAuthBeaconBridge = require('../services/Ultravisor-AuthBeaconBridge.cjs');
25
+ const libServiceQueuePersistenceBridge = require('../services/Ultravisor-QueuePersistenceBridge.cjs');
26
+ const libServiceManifestStoreBridge = require('../services/Ultravisor-ManifestStoreBridge.cjs');
27
+ const libServiceDirectoryDistributor = require('../services/Ultravisor-DirectoryDistributor.cjs');
28
+ const libServiceFleetManager = require('../services/Ultravisor-FleetManager.cjs');
21
29
 
22
30
  // TODO: Remove this when Restify is fixed.
23
31
  process.removeAllListeners('warning')
@@ -207,6 +215,19 @@ if (tmpQueueJournal)
207
215
  tmpQueueJournal.initialize(_Ultravisor_Pict.fable.settings.UltravisorFileStorePath);
208
216
  }
209
217
 
218
+ // --- Beacon queue store (SQLite-backed history + per-item events) ---
219
+ // Distinct from the journal above: the journal is a write-ahead log for
220
+ // crash recovery of the in-flight queue; the store is a long-lived
221
+ // SQLite database that backs the /queue UI's history list and the
222
+ // /Beacon/Work/:hash/Events endpoint. Both can coexist — they were
223
+ // added in separate generations of the queue work.
224
+ _Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists('UltravisorBeaconQueueStore', libServiceBeaconQueueStore);
225
+ let tmpQueueStore = Object.values(_Ultravisor_Pict.fable.servicesMap['UltravisorBeaconQueueStore'])[0];
226
+ if (tmpQueueStore)
227
+ {
228
+ tmpQueueStore.initialize(_Ultravisor_Pict.fable.settings.UltravisorFileStorePath);
229
+ }
230
+
210
231
  // Restore persisted work queue from journal (if any)
211
232
  let tmpCoordinator = Object.values(_Ultravisor_Pict.fable.servicesMap['UltravisorBeaconCoordinator'])[0];
212
233
  if (tmpCoordinator)
@@ -215,8 +236,67 @@ if (tmpCoordinator)
215
236
  tmpCoordinator.loadActionCatalog();
216
237
  }
217
238
 
239
+ // --- Fleet management (per-(beacon, model) install/enable state) ---
240
+ _Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
241
+ 'UltravisorBeaconFleetStore', libServiceBeaconFleetStore);
242
+ let tmpFleetStore = Object.values(_Ultravisor_Pict.fable.servicesMap['UltravisorBeaconFleetStore'])[0];
243
+ if (tmpFleetStore)
244
+ {
245
+ tmpFleetStore.initialize(_Ultravisor_Pict.fable.settings.UltravisorFileStorePath);
246
+ }
247
+
248
+ _Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
249
+ 'UltravisorDirectoryDistributor', libServiceDirectoryDistributor);
250
+
251
+ _Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
252
+ 'UltravisorFleetManager', libServiceFleetManager);
253
+
254
+ // --- Beacon scheduler (queue.* topic broadcasts + dispatch tick) ---
255
+ // The scheduler must exist before the APIServer wires its broadcast
256
+ // handler — see Ultravisor-API-Server._initializeWebSocket where it
257
+ // calls scheduler.setBroadcastHandler(...). After APIServer is up,
258
+ // we kick off the dispatch/health/summary timers via .start().
259
+ _Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
260
+ 'UltravisorBeaconScheduler', libServiceBeaconScheduler);
261
+
262
+ // --- Auth beacon bridge (consults the optional Authentication-capable
263
+ // beacon for session validation + non-promiscuous mode admission). The
264
+ // bridge is always installed; it just resolves to "not available" when
265
+ // no auth beacon is connected, so the rest of the hub keeps working
266
+ // without it. See source/services/Ultravisor-AuthBeaconBridge.cjs and
267
+ // the matching ultravisor-auth-beacon module under modules/apps/.
268
+ _Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
269
+ 'UltravisorAuthBeaconBridge', libServiceAuthBeaconBridge);
270
+
271
+ // --- Queue persistence bridge (consults the optional QueuePersistence
272
+ // beacon for durable queue + event log storage). Like the auth bridge,
273
+ // it's always installed and falls back to the in-process
274
+ // UltravisorBeaconQueueStore when no beacon is connected. The
275
+ // coordinator + scheduler call into the bridge for every persistence
276
+ // op; switching to a beacon-backed backend is a runtime decision.
277
+ _Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
278
+ 'UltravisorQueuePersistenceBridge', libServiceQueuePersistenceBridge);
279
+
280
+ // --- Manifest store bridge (consults the optional ManifestStore
281
+ // beacon for durable execution-manifest storage). Same shape as the
282
+ // queue bridge: always installed, falls back to the in-process
283
+ // UltravisorExecutionManifest service when no beacon is connected.
284
+ // Persistence calls (finalizeExecution, abandonRun) go through this
285
+ // bridge instead of directly writing JSON files.
286
+ _Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists(
287
+ 'UltravisorManifestStoreBridge', libServiceManifestStoreBridge);
288
+
218
289
  _Ultravisor_Pict.fable.addAndInstantiateServiceTypeIfNotExists('UltravisorAPIServer', libWebServerAPIServer);
219
290
 
291
+ // Kick the scheduler timers AFTER the API server has wired
292
+ // setBroadcastHandler — otherwise the first summary tick has nowhere
293
+ // to fan out to and is silently dropped.
294
+ let tmpScheduler = Object.values(_Ultravisor_Pict.fable.servicesMap['UltravisorBeaconScheduler'])[0];
295
+ if (tmpScheduler && typeof tmpScheduler.start === 'function')
296
+ {
297
+ tmpScheduler.start();
298
+ }
299
+
220
300
  // ── Service name aliases ────────────────────────────────
221
301
  // Some CLI commands access services by hyphenated names via this.fable['Name'].
222
302
  // Bridge the camelCase registration to hyphenated access.
@@ -16,5 +16,13 @@ module.exports = (
16
16
  "UltravisorBeaconWorkItemTimeoutMs": 300000,
17
17
  "UltravisorBeaconAffinityTTLMs": 3600000,
18
18
  "UltravisorBeaconPollIntervalMs": 5000,
19
- "UltravisorBeaconJournalCompactThreshold": 500
19
+ "UltravisorBeaconJournalCompactThreshold": 500,
20
+
21
+ // Optional non-promiscuous mode. When true, every BeaconRegister
22
+ // must present a JoinSecret that either (a) matches the bootstrap
23
+ // secret below, for the auth beacon's own admission, or (b) is
24
+ // validated by the auth beacon's AUTH_ValidateBeaconJoin action.
25
+ // Default false → behavior identical to pre-auth-beacon ultravisor.
26
+ "UltravisorNonPromiscuous": false,
27
+ "UltravisorBootstrapAuthSecret": ""
20
28
  });
@@ -0,0 +1,66 @@
1
+ {
2
+ "Tables":
3
+ {
4
+ "BeaconModelInstallation":
5
+ {
6
+ "TableName": "BeaconModelInstallation",
7
+ "Domain": "Default",
8
+ "Description": "Per-(beacon, model) installation state. Authoritative source of truth for which beacons may be dispatched a given model. Status tracks install lifecycle; EnabledForDispatch is a separate operator toggle.",
9
+ "Columns":
10
+ [
11
+ { "Column": "IDBeaconModelInstallation", "DataType": "ID" },
12
+ { "Column": "GUIDBeaconModelInstallation", "DataType": "GUID", "Size": "36" },
13
+ { "Column": "CreateDate", "DataType": "DateTime" },
14
+ { "Column": "CreatingIDUser", "DataType": "Numeric", "Size": "int" },
15
+ { "Column": "UpdateDate", "DataType": "DateTime" },
16
+ { "Column": "UpdatingIDUser", "DataType": "Numeric", "Size": "int" },
17
+ { "Column": "Deleted", "DataType": "Boolean" },
18
+ { "Column": "DeleteDate", "DataType": "DateTime" },
19
+ { "Column": "DeletingIDUser", "DataType": "Numeric", "Size": "int" },
20
+ { "Column": "BeaconID", "DataType": "String", "Size": "128" },
21
+ { "Column": "BeaconName", "DataType": "String", "Size": "256" },
22
+ { "Column": "ModelKey", "DataType": "String", "Size": "256" },
23
+ { "Column": "ModelName", "DataType": "String", "Size": "256" },
24
+ { "Column": "ModelSourceDir", "DataType": "String", "Size": "1024" },
25
+ { "Column": "ExpectedTreeHash", "DataType": "String", "Size": "128" },
26
+ { "Column": "InstalledTreeHash", "DataType": "String", "Size": "128" },
27
+ { "Column": "InstalledBytes", "DataType": "Numeric", "Size": "bigint" },
28
+ { "Column": "Status", "DataType": "String", "Size": "32" },
29
+ { "Column": "EnabledForDispatch", "DataType": "Boolean" },
30
+ { "Column": "PushProgressBytes", "DataType": "Numeric", "Size": "bigint" },
31
+ { "Column": "PushTotalBytes", "DataType": "Numeric", "Size": "bigint" },
32
+ { "Column": "LastError", "DataType": "String", "Size": "4096" },
33
+ { "Column": "InstalledAt", "DataType": "DateTime" },
34
+ { "Column": "LastUpdatedAt", "DataType": "DateTime" },
35
+ { "Column": "Source", "DataType": "String", "Size": "32" }
36
+ ]
37
+ },
38
+ "BeaconRuntimeInstallation":
39
+ {
40
+ "TableName": "BeaconRuntimeInstallation",
41
+ "Domain": "Default",
42
+ "Description": "Per-(beacon, runtime) state. A runtime is a named directory of pipeline-worker code (e.g. retold-labs' examples/pipeline-workers). Auto-pushed on beacon connect. Tracks last-known runtime hash so the hub can short-circuit pushes when the worker is already up to date.",
43
+ "Columns":
44
+ [
45
+ { "Column": "IDBeaconRuntimeInstallation", "DataType": "ID" },
46
+ { "Column": "GUIDBeaconRuntimeInstallation", "DataType": "GUID", "Size": "36" },
47
+ { "Column": "CreateDate", "DataType": "DateTime" },
48
+ { "Column": "CreatingIDUser", "DataType": "Numeric", "Size": "int" },
49
+ { "Column": "UpdateDate", "DataType": "DateTime" },
50
+ { "Column": "UpdatingIDUser", "DataType": "Numeric", "Size": "int" },
51
+ { "Column": "Deleted", "DataType": "Boolean" },
52
+ { "Column": "DeleteDate", "DataType": "DateTime" },
53
+ { "Column": "DeletingIDUser", "DataType": "Numeric", "Size": "int" },
54
+ { "Column": "BeaconID", "DataType": "String", "Size": "128" },
55
+ { "Column": "BeaconName", "DataType": "String", "Size": "256" },
56
+ { "Column": "RuntimeName", "DataType": "String", "Size": "128" },
57
+ { "Column": "ExpectedRuntimeHash", "DataType": "String", "Size": "128" },
58
+ { "Column": "InstalledRuntimeHash", "DataType": "String", "Size": "128" },
59
+ { "Column": "Status", "DataType": "String", "Size": "32" },
60
+ { "Column": "LastError", "DataType": "String", "Size": "4096" },
61
+ { "Column": "InstalledAt", "DataType": "DateTime" },
62
+ { "Column": "LastUpdatedAt", "DataType": "DateTime" }
63
+ ]
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,240 @@
1
+ {
2
+ "_comment": "Ultravisor persistence schema — source of truth for the four tables used when ultravisor's QueuePersistenceBridge / ManifestStoreBridge route through retold-databeacon's MeadowProxy. See modules/apps/ultravisor/docs/features/persistence-via-databeacon.md for the architectural context.",
3
+
4
+ "SchemaName": "ultravisor",
5
+ "Version": 1,
6
+
7
+ "Tables":
8
+ [
9
+ {
10
+ "Scope": "UVQueueWorkItem",
11
+ "DefaultIdentifier": "IDUVQueueWorkItem",
12
+ "Domain": "Ultravisor",
13
+ "Schema":
14
+ [
15
+ { "Column": "IDUVQueueWorkItem", "Type": "AutoIdentity", "Size": "Default" },
16
+ { "Column": "WorkItemHash", "Type": "String", "Size": "128" },
17
+ { "Column": "RunID", "Type": "String", "Size": "128" },
18
+ { "Column": "RunHash", "Type": "String", "Size": "128" },
19
+ { "Column": "NodeHash", "Type": "String", "Size": "128" },
20
+ { "Column": "OperationHash", "Type": "String", "Size": "128" },
21
+ { "Column": "Capability", "Type": "String", "Size": "128" },
22
+ { "Column": "Action", "Type": "String", "Size": "128" },
23
+ { "Column": "Settings", "Type": "String", "Size": "Default" },
24
+ { "Column": "AffinityKey", "Type": "String", "Size": "128" },
25
+ { "Column": "AssignedBeaconID", "Type": "String", "Size": "128" },
26
+ { "Column": "Status", "Type": "String", "Size": "32" },
27
+ { "Column": "Priority", "Type": "Integer", "Size": "int" },
28
+ { "Column": "EnqueuedAt", "Type": "String", "Size": "50" },
29
+ { "Column": "DispatchedAt", "Type": "String", "Size": "50" },
30
+ { "Column": "StartedAt", "Type": "String", "Size": "50" },
31
+ { "Column": "ClaimedAt", "Type": "String", "Size": "50" },
32
+ { "Column": "CompletedAt", "Type": "String", "Size": "50" },
33
+ { "Column": "CanceledAt", "Type": "String", "Size": "50" },
34
+ { "Column": "AssignedAt", "Type": "String", "Size": "50" },
35
+ { "Column": "LastEventAt", "Type": "String", "Size": "50" },
36
+ { "Column": "QueueWaitMs", "Type": "Integer", "Size": "int" },
37
+ { "Column": "TimeoutMs", "Type": "Integer", "Size": "int" },
38
+ { "Column": "Health", "Type": "Float", "Size": "Default" },
39
+ { "Column": "HealthLabel", "Type": "String", "Size": "32" },
40
+ { "Column": "HealthReason", "Type": "String", "Size": "512" },
41
+ { "Column": "HealthComputedAt", "Type": "String", "Size": "50" },
42
+ { "Column": "AttemptNumber", "Type": "Integer", "Size": "int" },
43
+ { "Column": "MaxAttempts", "Type": "Integer", "Size": "int" },
44
+ { "Column": "RetryBackoffMs", "Type": "Integer", "Size": "int" },
45
+ { "Column": "RetryAfter", "Type": "String", "Size": "50" },
46
+ { "Column": "LastError", "Type": "String", "Size": "Default" },
47
+ { "Column": "Result", "Type": "String", "Size": "Default" },
48
+ { "Column": "CancelRequested", "Type": "Boolean", "Size": "Default" },
49
+ { "Column": "CancelReason", "Type": "String", "Size": "512" },
50
+ { "Column": "CreateDate", "Type": "CreateDate", "Size": "Default" },
51
+ { "Column": "UpdateDate", "Type": "UpdateDate", "Size": "Default" },
52
+ { "Column": "Deleted", "Type": "Deleted", "Size": "Default" }
53
+ ],
54
+ "DefaultObject":
55
+ {
56
+ "IDUVQueueWorkItem": 0,
57
+ "WorkItemHash": "",
58
+ "RunID": "",
59
+ "RunHash": "",
60
+ "NodeHash": "",
61
+ "OperationHash": "",
62
+ "Capability": "",
63
+ "Action": "",
64
+ "Settings": "{}",
65
+ "AffinityKey": "",
66
+ "AssignedBeaconID": "",
67
+ "Status": "Pending",
68
+ "Priority": 0,
69
+ "EnqueuedAt": "",
70
+ "DispatchedAt": "",
71
+ "StartedAt": "",
72
+ "ClaimedAt": "",
73
+ "CompletedAt": "",
74
+ "CanceledAt": "",
75
+ "AssignedAt": "",
76
+ "LastEventAt": "",
77
+ "QueueWaitMs": 0,
78
+ "TimeoutMs": 0,
79
+ "Health": 0,
80
+ "HealthLabel": "Unknown",
81
+ "HealthReason": "",
82
+ "HealthComputedAt": "",
83
+ "AttemptNumber": 0,
84
+ "MaxAttempts": 1,
85
+ "RetryBackoffMs": 0,
86
+ "RetryAfter": "",
87
+ "LastError": "",
88
+ "Result": "",
89
+ "CancelRequested": false,
90
+ "CancelReason": "",
91
+ "Deleted": false
92
+ },
93
+ "Indexes":
94
+ [
95
+ { "Name": "IX_UVQueueWorkItem_Hash", "Columns": ["WorkItemHash"], "Unique": true },
96
+ { "Name": "IX_UVQueueWorkItem_Status", "Columns": ["Status"] },
97
+ { "Name": "IX_UVQueueWorkItem_RunID", "Columns": ["RunID"] },
98
+ { "Name": "IX_UVQueueWorkItem_Assigned", "Columns": ["AssignedBeaconID", "Status"] },
99
+ { "Name": "IX_UVQueueWorkItem_Dispatch", "Columns": ["Status", "Priority", "EnqueuedAt"] }
100
+ ]
101
+ },
102
+
103
+ {
104
+ "Scope": "UVQueueWorkItemEvent",
105
+ "DefaultIdentifier": "IDUVQueueWorkItemEvent",
106
+ "Domain": "Ultravisor",
107
+ "_comment": "Append-only event log per work item. EventGUID is unique for idempotency on bootstrap-flush replay.",
108
+ "Schema":
109
+ [
110
+ { "Column": "IDUVQueueWorkItemEvent", "Type": "AutoIdentity", "Size": "Default" },
111
+ { "Column": "EventGUID", "Type": "String", "Size": "36" },
112
+ { "Column": "WorkItemHash", "Type": "String", "Size": "128" },
113
+ { "Column": "EventType", "Type": "String", "Size": "64" },
114
+ { "Column": "Payload", "Type": "String", "Size": "Default" },
115
+ { "Column": "EmittedAt", "Type": "String", "Size": "50" },
116
+ { "Column": "Seq", "Type": "Integer", "Size": "int" },
117
+ { "Column": "FromStatus", "Type": "String", "Size": "32" },
118
+ { "Column": "ToStatus", "Type": "String", "Size": "32" },
119
+ { "Column": "BeaconID", "Type": "String", "Size": "128" },
120
+ { "Column": "CreateDate", "Type": "CreateDate", "Size": "Default" },
121
+ { "Column": "Deleted", "Type": "Deleted", "Size": "Default" }
122
+ ],
123
+ "DefaultObject":
124
+ {
125
+ "IDUVQueueWorkItemEvent": 0,
126
+ "EventGUID": "",
127
+ "WorkItemHash": "",
128
+ "EventType": "",
129
+ "Payload": "{}",
130
+ "EmittedAt": "",
131
+ "Seq": 0,
132
+ "FromStatus": "",
133
+ "ToStatus": "",
134
+ "BeaconID": "",
135
+ "Deleted": false
136
+ },
137
+ "Indexes":
138
+ [
139
+ { "Name": "IX_UVQueueEvent_GUID", "Columns": ["EventGUID"], "Unique": true },
140
+ { "Name": "IX_UVQueueEvent_Hash", "Columns": ["WorkItemHash"] }
141
+ ]
142
+ },
143
+
144
+ {
145
+ "Scope": "UVQueueWorkItemAttempt",
146
+ "DefaultIdentifier": "IDUVQueueWorkItemAttempt",
147
+ "Domain": "Ultravisor",
148
+ "_comment": "One row per dispatch attempt. (WorkItemHash, AttemptNumber) is unique.",
149
+ "Schema":
150
+ [
151
+ { "Column": "IDUVQueueWorkItemAttempt", "Type": "AutoIdentity", "Size": "Default" },
152
+ { "Column": "WorkItemHash", "Type": "String", "Size": "128" },
153
+ { "Column": "AttemptNumber", "Type": "Integer", "Size": "int" },
154
+ { "Column": "BeaconID", "Type": "String", "Size": "128" },
155
+ { "Column": "StartedAt", "Type": "String", "Size": "50" },
156
+ { "Column": "EndedAt", "Type": "String", "Size": "50" },
157
+ { "Column": "Outcome", "Type": "String", "Size": "32" },
158
+ { "Column": "Error", "Type": "String", "Size": "Default" },
159
+ { "Column": "CreateDate", "Type": "CreateDate", "Size": "Default" },
160
+ { "Column": "Deleted", "Type": "Deleted", "Size": "Default" }
161
+ ],
162
+ "DefaultObject":
163
+ {
164
+ "IDUVQueueWorkItemAttempt": 0,
165
+ "WorkItemHash": "",
166
+ "AttemptNumber": 0,
167
+ "BeaconID": "",
168
+ "StartedAt": "",
169
+ "EndedAt": "",
170
+ "Outcome": "",
171
+ "Error": "",
172
+ "Deleted": false
173
+ },
174
+ "Indexes":
175
+ [
176
+ { "Name": "IX_UVQueueAttempt_HashNum", "Columns": ["WorkItemHash", "AttemptNumber"], "Unique": true }
177
+ ]
178
+ },
179
+
180
+ {
181
+ "Scope": "UVManifest",
182
+ "DefaultIdentifier": "IDUVManifest",
183
+ "Domain": "Ultravisor",
184
+ "_comment": "Execution manifests, one row per run. ManifestJSON is the full wire-safe blob produced by _cleanManifestForWire.",
185
+ "Schema":
186
+ [
187
+ { "Column": "IDUVManifest", "Type": "AutoIdentity", "Size": "Default" },
188
+ { "Column": "Hash", "Type": "String", "Size": "128" },
189
+ { "Column": "OperationHash", "Type": "String", "Size": "128" },
190
+ { "Column": "OperationName", "Type": "String", "Size": "500" },
191
+ { "Column": "Status", "Type": "String", "Size": "32" },
192
+ { "Column": "RunMode", "Type": "String", "Size": "32" },
193
+ { "Column": "Live", "Type": "Boolean", "Size": "Default" },
194
+ { "Column": "StartTime", "Type": "String", "Size": "50" },
195
+ { "Column": "StopTime", "Type": "String", "Size": "50" },
196
+ { "Column": "ElapsedMs", "Type": "Integer", "Size": "int" },
197
+ { "Column": "ManifestJSON", "Type": "String", "Size": "Default" },
198
+ { "Column": "StagingPath", "Type": "String", "Size": "1024" },
199
+ { "Column": "CreateDate", "Type": "CreateDate", "Size": "Default" },
200
+ { "Column": "UpdateDate", "Type": "UpdateDate", "Size": "Default" },
201
+ { "Column": "Deleted", "Type": "Deleted", "Size": "Default" }
202
+ ],
203
+ "DefaultObject":
204
+ {
205
+ "IDUVManifest": 0,
206
+ "Hash": "",
207
+ "OperationHash": "",
208
+ "OperationName": "",
209
+ "Status": "",
210
+ "RunMode": "",
211
+ "Live": false,
212
+ "StartTime": "",
213
+ "StopTime": "",
214
+ "ElapsedMs": 0,
215
+ "ManifestJSON": "{}",
216
+ "StagingPath": "",
217
+ "Deleted": false
218
+ },
219
+ "Indexes":
220
+ [
221
+ { "Name": "IX_UVManifest_Hash", "Columns": ["Hash"], "Unique": true },
222
+ { "Name": "IX_UVManifest_StatusStop","Columns": ["Status", "StopTime"] },
223
+ { "Name": "IX_UVManifest_OpHash", "Columns": ["OperationHash"] }
224
+ ]
225
+ }
226
+ ],
227
+
228
+ "_notes":
229
+ {
230
+ "meadowConventions": "Type values follow meadow column conventions. AutoIdentity → engine PK; CreateDate / UpdateDate / Deleted → meadow's auto-managed audit columns. The per-engine Meadow-Schema-<engine>.js files translate these to engine-specific DDL.",
231
+
232
+ "indexes": "Indexes here are NOT part of meadow's standard table descriptor. retold-databeacon's EnsureSchema action will issue them as CREATE INDEX IF NOT EXISTS statements after the meadow table create. Per-engine syntax differences (e.g. SQL Server's CREATE INDEX vs Postgres's CREATE INDEX IF NOT EXISTS) handled in the engine-specific path.",
233
+
234
+ "jsonColumns": "Settings (work item), Payload (event), Result, LastError, ManifestJSON are stored as opaque strings (JSON-encoded by callers). We don't use a native JSON column type because not all meadow connectors support it uniformly (mssql JSON support varies; sqlite has none). Callers are responsible for parse/stringify on the boundary.",
235
+
236
+ "floatColumns": "Health is stored as Float (0..1 score). Connector translation: REAL on sqlite, DOUBLE on mysql/postgres, FLOAT on mssql.",
237
+
238
+ "softDelete": "Deleted column matches meadow's standard soft-delete convention. The bridges' `removeManifest` etc. flip Deleted=1 rather than DROP. Hard-delete deferred to a separate retention sweep."
239
+ }
240
+ }