@camstack/types 1.2.169 → 1.2.171

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/addon.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_event_category = require("./event-category-BVo6ta6_.js");
3
- const require_sleep = require("./sleep-BZtO-eFY.js");
3
+ const require_sleep = require("./sleep-Bo9cUOXp.js");
4
4
  const require_err_msg = require("./err-msg-COpsHMw2.js");
5
5
  //#region src/generated/cap-input-defaults.ts
6
6
  /**
package/dist/addon.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as EventCategory } from "./event-category-CnLqLOKs.mjs";
2
- import { D as adminUiCapability, E as DeviceType, G as scopeKey, H as ReadinessTimeoutError, M as expandCapMethods, R as nodePin, S as DEVICE_CHILDREN_BATCH_MAX, V as ReadinessRegistry, _t as DATAPLANE_SECRET_HEADER, a as asJsonObject, b as deviceOpsCapability, bt as BaseAddon, d as BOOT_RECOVERY_BACKOFF_MS, f as DEVICE_SCOPED_CAPS, h as createEventBusSliceSource, m as createDeviceProxy, p as isDeviceScopedCap, s as asString, t as sleep, u as parseJsonUnknown, wt as emitReadiness, x as viewerUiCapability, xt as normalizeAddonInitResult, yt as DisposerChain, z as readNodePin } from "./sleep-Bd-Y4RUt.mjs";
2
+ import { D as adminUiCapability, E as DeviceType, G as scopeKey, H as ReadinessTimeoutError, M as expandCapMethods, R as nodePin, S as DEVICE_CHILDREN_BATCH_MAX, V as ReadinessRegistry, _t as DATAPLANE_SECRET_HEADER, a as asJsonObject, b as deviceOpsCapability, bt as BaseAddon, d as BOOT_RECOVERY_BACKOFF_MS, f as DEVICE_SCOPED_CAPS, h as createEventBusSliceSource, m as createDeviceProxy, p as isDeviceScopedCap, s as asString, t as sleep, u as parseJsonUnknown, wt as emitReadiness, x as viewerUiCapability, xt as normalizeAddonInitResult, yt as DisposerChain, z as readNodePin } from "./sleep-DMghvp8F.mjs";
3
3
  import { t as errMsg } from "./err-msg-IQTHeDzc.mjs";
4
4
  //#region src/generated/cap-input-defaults.ts
5
5
  /**
@@ -2781,6 +2781,95 @@ export declare const pipelineAnalyticsCapability: {
2781
2781
  trained: "trained";
2782
2782
  }>;
2783
2783
  }, z.core.$strip>, "mutation">;
2784
+ /**
2785
+ * The archived debug notes, newest first — the corpus of what operators
2786
+ * have asked to be checked, across tracks that no longer exist (D405).
2787
+ *
2788
+ * `setTrackFlags` writes it: a patch carrying `debug: false` still clears
2789
+ * the note off the track row in the same statement (D353's invariant is
2790
+ * untouched), but the note is APPENDED here first. That is the whole point
2791
+ * — before D405, finishing a review destroyed every note written during it,
2792
+ * and the ones that survived aged out with their tracks. This method is the
2793
+ * only way to read what was kept.
2794
+ *
2795
+ * `auth: 'protected'`, matching `setTrackFlags` next door and for the same
2796
+ * reason: the notes are written from the viewer, by an authenticated
2797
+ * non-admin session, and a corpus only an admin can read is a corpus the
2798
+ * person who wrote it cannot use. It carries operator prose about cameras
2799
+ * and nothing else — no media, no paths, no identities beyond a track's
2800
+ * own display label.
2801
+ *
2802
+ * Newest-first, capped by `limit` (default
2803
+ * {@link ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT}), optionally one camera. The
2804
+ * table itself is bounded by row count, not by age — see
2805
+ * {@link MAX_ARCHIVED_DEBUG_NOTES} for why an age clock would be the wrong
2806
+ * bound for exactly this data.
2807
+ */
2808
+ readonly listArchivedDebugNotes: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
2809
+ deviceId: z.ZodOptional<z.ZodNumber>;
2810
+ limit: z.ZodOptional<z.ZodNumber>;
2811
+ }, z.core.$strip>, z.ZodReadonly<z.ZodArray<z.ZodObject<{
2812
+ id: z.ZodString;
2813
+ note: z.ZodString;
2814
+ deviceId: z.ZodNumber;
2815
+ sourceTrackId: z.ZodString;
2816
+ archivedAt: z.ZodNumber;
2817
+ trackStartedAt: z.ZodNullable<z.ZodNumber>;
2818
+ trackClass: z.ZodNullable<z.ZodString>;
2819
+ trackLabel: z.ZodNullable<z.ZodString>;
2820
+ }, z.core.$strip>>>, "query">;
2821
+ /**
2822
+ * Birth decisions — one row per resolved confirmation-gate verdict (D409).
2823
+ *
2824
+ * The READ half of a measurement, and only that: it gates nothing and no
2825
+ * runtime path consults it. It exists because the two questions the
2826
+ * operator's "traccia partita in ritardo" marks raised are both multi-day
2827
+ * and `logs.query` holds under an hour of fleet traffic —
2828
+ * • how often is a birth DECIDED on a frame the subject was not observed
2829
+ * in (`decidedOnCoastedFrame`), and did D379's carried pixels make it a
2830
+ * real observation anyway (`decidedByBirthEvidence`);
2831
+ * • how long after the camera's motion onset does a track's `firstSeen`
2832
+ * land (`birthLatencyMs` — read its error bars in
2833
+ * `BirthDecisionRecordSchema` before quoting it).
2834
+ *
2835
+ * Newest-first, capped by `limit` (default
2836
+ * {@link BIRTH_DECISION_DEFAULT_LIMIT}), optionally scoped to one camera,
2837
+ * one verdict, or a `since`. The table is bounded by row count, not age —
2838
+ * see {@link MAX_BIRTH_DECISION_RECORDS}.
2839
+ */
2840
+ readonly listBirthDecisions: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
2841
+ deviceId: z.ZodOptional<z.ZodNumber>;
2842
+ since: z.ZodOptional<z.ZodNumber>;
2843
+ verdict: z.ZodOptional<z.ZodEnum<{
2844
+ confirmed: "confirmed";
2845
+ suppressed: "suppressed";
2846
+ "exhausted-fallback": "exhausted-fallback";
2847
+ }>>;
2848
+ limit: z.ZodOptional<z.ZodNumber>;
2849
+ }, z.core.$strip>, z.ZodReadonly<z.ZodArray<z.ZodObject<{
2850
+ id: z.ZodString;
2851
+ at: z.ZodNumber;
2852
+ deviceId: z.ZodNumber;
2853
+ sourceTrackId: z.ZodString;
2854
+ className: z.ZodString;
2855
+ verdict: z.ZodEnum<{
2856
+ confirmed: "confirmed";
2857
+ suppressed: "suppressed";
2858
+ "exhausted-fallback": "exhausted-fallback";
2859
+ }>;
2860
+ reason: z.ZodNullable<z.ZodString>;
2861
+ attempts: z.ZodNumber;
2862
+ deferredForMs: z.ZodNumber;
2863
+ decidedOnCoastedFrame: z.ZodBoolean;
2864
+ birthEvidenceAvailable: z.ZodBoolean;
2865
+ decidedByBirthEvidence: z.ZodBoolean;
2866
+ bestScore: z.ZodNullable<z.ZodNumber>;
2867
+ appliedMinConfidence: z.ZodNullable<z.ZodNumber>;
2868
+ firstSeen: z.ZodNullable<z.ZodNumber>;
2869
+ motionOnsetAt: z.ZodNullable<z.ZodNumber>;
2870
+ birthLatencyMs: z.ZodNullable<z.ZodNumber>;
2871
+ birthIndexInBurst: z.ZodNullable<z.ZodNumber>;
2872
+ }, z.core.$strip>>>, "query">;
2784
2873
  /**
2785
2874
  * Durable event-store footprint for the management UI: event rows
2786
2875
  * (motion + object + audio) counted per camera + total, plus the
@@ -5201,6 +5201,20 @@ export type AppRouter = TrpcCoreRouter<{
5201
5201
  output: z.infer<typeof pipelineAnalyticsCapability.methods.setTrackFlags.output>;
5202
5202
  meta: object;
5203
5203
  }>;
5204
+ listArchivedDebugNotes: TRPCQueryProcedure<{
5205
+ input: {
5206
+ [x: string]: unknown;
5207
+ } & z.input<typeof pipelineAnalyticsCapability.methods.listArchivedDebugNotes.input>;
5208
+ output: z.infer<typeof pipelineAnalyticsCapability.methods.listArchivedDebugNotes.output>;
5209
+ meta: object;
5210
+ }>;
5211
+ listBirthDecisions: TRPCQueryProcedure<{
5212
+ input: {
5213
+ [x: string]: unknown;
5214
+ } & z.input<typeof pipelineAnalyticsCapability.methods.listBirthDecisions.input>;
5215
+ output: z.infer<typeof pipelineAnalyticsCapability.methods.listBirthDecisions.output>;
5216
+ meta: object;
5217
+ }>;
5204
5218
  getEventStoreFootprint: TRPCQueryProcedure<{
5205
5219
  input: {
5206
5220
  [x: string]: unknown;
@@ -6,7 +6,7 @@
6
6
  * scope+access check inside `protectedProcedure` (see
7
7
  * `server/backend/src/api/trpc/trpc.middleware.ts`).
8
8
  *
9
- * Coverage: 1022 method paths across 129 capabilities.
9
+ * Coverage: 1024 method paths across 129 capabilities.
10
10
  */
11
11
  import type { CapabilityMethodAccess } from '../capabilities/capability-definition.js';
12
12
  export interface MethodAccessRecord {
@@ -6,7 +6,7 @@
6
6
  * system-scope cap that takes a deviceId was previously never device-filtered
7
7
  * — see the generator header).
8
8
  *
9
- * Coverage: 367 methods carry a device reference, of which
9
+ * Coverage: 369 methods carry a device reference, of which
10
10
  * 134 are on SYSTEM-scope caps.
11
11
  *
12
12
  * Top-level fields, number arrays, and one-level arrays of objects carrying
package/dist/index.d.ts CHANGED
@@ -40,6 +40,8 @@ export type { HydratedFieldEntry } from './interfaces/config-ui.js';
40
40
  export { collectHydratedFieldEntries, collectHydratedFieldValues, hydrateSchema, resolveHydratedFieldValue, WELL_KNOWN_TAB_MAP, WELL_KNOWN_TABS, } from './interfaces/config-ui.js';
41
41
  export { collectSecretConfigKeys, isSecretConfigField, REDACTED_SECRET, schemaDeclaresAnyField, } from './interfaces/config-ui-secrets.js';
42
42
  export type * from './interfaces/context.js';
43
+ export * from './interfaces/birth-decision-ledger.js';
44
+ export * from './interfaces/debug-note-archive.js';
43
45
  export type * from './interfaces/decoder.js';
44
46
  export type * from './interfaces/detection-addon.js';
45
47
  export type { DeviceEvent, DeviceMetadata, DeviceState } from './interfaces/device.js';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_event_category = require("./event-category-BVo6ta6_.js");
3
- const require_sleep = require("./sleep-BZtO-eFY.js");
3
+ const require_sleep = require("./sleep-Bo9cUOXp.js");
4
4
  const require_canonical_hash = require("./canonical-hash-DNV8S5ET.js");
5
5
  const require_enums = require("./enums.js");
6
6
  const require_err_msg = require("./err-msg-COpsHMw2.js");
@@ -1257,6 +1257,313 @@ function walkField(field, out) {
1257
1257
  }
1258
1258
  }
1259
1259
  //#endregion
1260
+ //#region src/interfaces/birth-decision-ledger.ts
1261
+ /**
1262
+ * Birth-decision ledger — one durable row per track-birth VERDICT (D409).
1263
+ *
1264
+ * ── Why this exists ────────────────────────────────────────────────────────
1265
+ * On 2026-09-08 the operator marked eleven tracks for review and five of them
1266
+ * said "traccia partita in ritardo" — the track appears to begin long after the
1267
+ * subject entered the scene. Two causes were proposed and NEITHER was measured:
1268
+ *
1269
+ * - the confirmation gate's deferral (`maxDeferralMs`, 2 s). This is now
1270
+ * DISPROVEN as a cause of a late TIMELINE: a track's `firstSeen` and its
1271
+ * first position are the CANDIDATE's first frame, not the confirmation's.
1272
+ * Verified on `d035f251` (device 3829): first position == `firstSeen`, with
1273
+ * confirmation 2 163 ms later.
1274
+ * - the detector simply not firing until the subject is well into the scene.
1275
+ * Unmeasured, and the reason this table exists.
1276
+ *
1277
+ * A second, real defect is also unquantified. The deferred-birth re-offer in
1278
+ * `index.ts` takes the track from `result.tracked` with NO `matchedThisFrame`
1279
+ * check, so a birth can be CONFIRMED on a frame the subject was never observed
1280
+ * in. D379's carried birth evidence means some of those confirmations rest on a
1281
+ * real observation of the birth instant anyway — and how often is exactly what
1282
+ * nobody knows. {@link BirthDecisionRecord.decidedOnCoastedFrame} and
1283
+ * {@link BirthDecisionRecord.decidedByBirthEvidence} are the two columns that
1284
+ * answer it together.
1285
+ *
1286
+ * ── Why a TABLE and not a log line ─────────────────────────────────────────
1287
+ * `logs.query` is an in-memory ring. Measured on the live hub on 2026-09-08 it
1288
+ * held 40 000 entries covering 38 minutes — the whole ring is under an hour of
1289
+ * fleet traffic. Every question this data is for ("how often, on camera 617,
1290
+ * over the week the operator complained about") outlives that ring by two
1291
+ * orders of magnitude, and a previous investigation in this repo was blocked
1292
+ * for precisely this reason. The gate ALREADY writes a log line per decision
1293
+ * (`confirmation gate: birth confirmed/suppressed/undecided`); adding a
1294
+ * fourteenth field to it would answer nothing a day later.
1295
+ *
1296
+ * This is a MEASUREMENT table. It changes no behaviour, gates nothing, and
1297
+ * nothing reads it on the frame path.
1298
+ */
1299
+ /**
1300
+ * How many birth decisions the cluster keeps, across every camera.
1301
+ *
1302
+ * Sized off a MEASURED rate, not a guess. Fleet-wide on the live hub,
1303
+ * 2026-09-08 20:16 local, over the 38 minutes the log ring covered: 17 `track
1304
+ * started` + 14 `birth suppressed — track record retracted` = 31 decided
1305
+ * births, i.e. ~49/hour, ~1 200/day. Call a busy daytime hour five times that
1306
+ * and the fleet writes ~6 000 rows/day.
1307
+ *
1308
+ * 30 000 rows is therefore ~25 days at the measured rate and ~5 days at five
1309
+ * times it — comfortably past "a few days of traffic", which is the bar the
1310
+ * operator's week-long complaint sets. At ~200 bytes a row the ceiling is
1311
+ * ~6 MB, against a `tracks` table measured at 4.6 MB for a single 501-row page.
1312
+ *
1313
+ * A COUNT and not an age, for the same reason as the debug-note archive: the
1314
+ * denominator of a miss rate is only meaningful over whatever window the table
1315
+ * actually holds, and a quiet fleet should keep more of it, not less. Trimmed
1316
+ * oldest-first on append.
1317
+ */
1318
+ var MAX_BIRTH_DECISION_RECORDS = 3e4;
1319
+ /** Default page size for `listBirthDecisions` when the caller omits one. */
1320
+ var BIRTH_DECISION_DEFAULT_LIMIT = 500;
1321
+ /**
1322
+ * How stale a motion onset may be and still be offered as the birth-latency
1323
+ * proxy. Beyond this the row records `null`, never a large number.
1324
+ *
1325
+ * 60 s is one order of magnitude above the latency being measured (the
1326
+ * complaint is "seconds late") and one below the duration of a busy scene's
1327
+ * continuous motion burst, where the onset is minutes old and says nothing
1328
+ * about THIS subject. See {@link BirthDecisionRecord.birthLatencyMs} for the
1329
+ * full error bars.
1330
+ */
1331
+ var MAX_BIRTH_LATENCY_PROXY_MS = 6e4;
1332
+ /**
1333
+ * What the gate decided about a birth, from the CALLER's point of view.
1334
+ *
1335
+ * Deliberately not `ConfirmationVerdict`: `undecided` is not a birth decision
1336
+ * at all — it is a deferral, and the row is written when the deferral ENDS.
1337
+ * The third member is the one the gate's own type cannot express, because
1338
+ * exhaustion is a property of how long the caller waited.
1339
+ */
1340
+ var BirthDecisionVerdictSchema = zod.z.enum([
1341
+ "confirmed",
1342
+ "suppressed",
1343
+ "exhausted-fallback"
1344
+ ]);
1345
+ /** One birth decision, as it is kept. */
1346
+ var BirthDecisionRecordSchema = zod.z.object({
1347
+ id: zod.z.string(),
1348
+ /** Epoch ms the VERDICT was taken (not the birth instant — see `firstSeen`). */
1349
+ at: zod.z.number().int(),
1350
+ /** The camera. Required: every question here is asked per-camera. */
1351
+ deviceId: zod.z.number().int(),
1352
+ /**
1353
+ * The track the decision was about. PROVENANCE, not ownership — a suppressed
1354
+ * birth has no track at all, and a confirmed one is expected to age out long
1355
+ * before this row does. Named `sourceTrackId` so the retention model's
1356
+ * ownership derivation (`collection-classification.ts`, which keys on a
1357
+ * column literally called `trackId`) cannot reach it.
1358
+ */
1359
+ sourceTrackId: zod.z.string(),
1360
+ /** The candidate's claimed class. The miss rate is asked per class too. */
1361
+ className: zod.z.string(),
1362
+ verdict: BirthDecisionVerdictSchema,
1363
+ /**
1364
+ * The gate's own `ConfirmationReason` (`confirmed`, `suppressed`,
1365
+ * `native-pass`, `no-crop`, `timeout`, `carried-inconclusive`, …), or `null`
1366
+ * when the gate is DISABLED and every birth is waved through. `null` is not
1367
+ * "unknown": it says the gate took no measurement because it was off, which
1368
+ * is a different population and must never be averaged in with the rest.
1369
+ */
1370
+ reason: zod.z.string().nullable(),
1371
+ /** Deferral attempts this birth used. 0 = decided on its first look. */
1372
+ attempts: zod.z.number().int(),
1373
+ /** Ms from the FIRST undecided attempt to this verdict. 0 = never deferred. */
1374
+ deferredForMs: zod.z.number().int(),
1375
+ /**
1376
+ * THE COLUMN THE RE-OFFER DEFECT IS COUNTED IN.
1377
+ *
1378
+ * `true` when the frame the verdict was taken on carried no matched
1379
+ * detection for this track — the tracker was coasting a frozen box and the
1380
+ * subject was not observed. The deferred-birth re-offer pulls the candidate
1381
+ * straight out of `result.tracked` without checking, so this is reachable
1382
+ * today; the question is the RATE, per camera, and whether refusing those
1383
+ * frames would have cost real tracks (cross-read against `verdict` and
1384
+ * `decidedByBirthEvidence`).
1385
+ */
1386
+ decidedOnCoastedFrame: zod.z.boolean(),
1387
+ /** Were D379 birth-instant pixels in hand when this candidate was submitted? */
1388
+ birthEvidenceAvailable: zod.z.boolean(),
1389
+ /**
1390
+ * Did those pixels DECIDE it (`cropSource === 'carried'`)? Available and
1391
+ * deciding are different: the carried crop is confirm-only, so a candidate
1392
+ * can hold evidence, be found inconclusive on it, and be decided by a native
1393
+ * crop of a later instant. A coasted decision backed by carried evidence is
1394
+ * a real observation of the birth instant; one without it is not.
1395
+ */
1396
+ decidedByBirthEvidence: zod.z.boolean(),
1397
+ /** Best class-compatible crop score, or `null` when nothing compatible was
1398
+ * found — deliberately not 0, which would be a measurement that never was. */
1399
+ bestScore: zod.z.number().nullable(),
1400
+ /** The bar this candidate actually faced (the phantom-cell hook may raise it). */
1401
+ appliedMinConfidence: zod.z.number().nullable(),
1402
+ /**
1403
+ * The track's own `firstSeen` — the candidate's first frame, which is where
1404
+ * the timeline starts. `null` for a suppressed birth, which has no track.
1405
+ */
1406
+ firstSeen: zod.z.number().int().nullable(),
1407
+ /**
1408
+ * The device's most recent motion RISING EDGE at the moment of the decision,
1409
+ * or `null` when there is none, it is older than
1410
+ * {@link MAX_BIRTH_LATENCY_PROXY_MS}, or this runner never saw one.
1411
+ */
1412
+ motionOnsetAt: zod.z.number().int().nullable(),
1413
+ /**
1414
+ * `firstSeen − motionOnsetAt` — THE OPERATOR'S COMPLAINT, IN MILLISECONDS,
1415
+ * AND THE WEAKEST NUMBER IN THIS ROW. Read the error bars before quoting it.
1416
+ *
1417
+ * **Why motion onset and not something else.** Two other proxies were
1418
+ * considered and rejected:
1419
+ * - *the recording/pipeline session start*: for a camera on
1420
+ * `detectionMode: 'always'` the session opens at process start, hours
1421
+ * before any subject. It measures nothing.
1422
+ * - *the first detection on the device in this burst*: CIRCULAR. A track's
1423
+ * `firstSeen` IS the first detection of that object, so for the track that
1424
+ * OPENS a burst — the only one the operator is complaining about — the two
1425
+ * are the same instant and the latency is 0 by construction.
1426
+ * Motion onset is the only in-process signal produced by a DIFFERENT
1427
+ * mechanism from the object detector, so it is the only one that can precede
1428
+ * it. It is also already maintained per-device at frame rate on this very
1429
+ * node (`handleMotionAnalysis` / `handleOnboardMotion`), which is what makes
1430
+ * it free — and, decisively, the frame path and the motion path are gated to
1431
+ * the SAME designated post-processing node, so the mirror is never empty for
1432
+ * a camera whose births land here.
1433
+ *
1434
+ * **Error bars, all of them.**
1435
+ * 1. *Motion has no class.* A burst opened by rain, a headlight sweeping a
1436
+ * wall or a branch, and only later joined by the person, OVERSTATES the
1437
+ * latency without bound. Mitigated, never removed, by
1438
+ * {@link BirthDecisionRecord.birthIndexInBurst}: only index 0 is a
1439
+ * candidate for "this burst is this subject", and even then it is a
1440
+ * candidate, not a fact.
1441
+ * 2. *The sign is not guaranteed.* The analyzer needs a pixel-count and
1442
+ * intensity threshold. A subject entering slowly at the far edge of the
1443
+ * frame can clear the detector's confidence floor BEFORE it clears the
1444
+ * motion floor, making this negative. Negatives are stored as-is and
1445
+ * never clamped — clamping would fabricate the distribution's left tail,
1446
+ * which is the half that says the proxy is unreliable.
1447
+ * 3. *Onboard motion carries firmware latency of unknown, per-model offset*
1448
+ * (hundreds of ms), plus camera-vs-hub clock skew on top. Numbers are
1449
+ * therefore comparable WITHIN a camera and not across cameras of
1450
+ * different motion sources. The motion source is deliberately not copied
1451
+ * here — it belongs to the addon that owns the device (D224) and this is
1452
+ * a write on the frame path — so group by `deviceId`, which is how the
1453
+ * question is always asked anyway.
1454
+ * 4. *Continuous motion.* On a busy scene the burst never closes and the
1455
+ * onset is minutes old. Bounded by {@link MAX_BIRTH_LATENCY_PROXY_MS};
1456
+ * past it this is `null`.
1457
+ * 5. *No motion signal at all* — analyzer off, onboard-only camera not
1458
+ * reporting, or nothing since boot: `null`. Which is the truth about a
1459
+ * camera nobody has a reference instant for.
1460
+ *
1461
+ * So this is a per-camera DISTRIBUTION over index-0 births, and it is honest
1462
+ * as such. It is not a per-track fact and must never be shown as one.
1463
+ */
1464
+ birthLatencyMs: zod.z.number().int().nullable(),
1465
+ /**
1466
+ * How many births this device has already decided since that motion onset.
1467
+ * 0 = the first, i.e. the only index at which the burst plausibly belongs to
1468
+ * this subject. `null` when there is no usable onset.
1469
+ */
1470
+ birthIndexInBurst: zod.z.number().int().nullable()
1471
+ });
1472
+ /** Query input for `listBirthDecisions` — newest first, one camera or all. */
1473
+ var BirthDecisionQueryInputSchema = zod.z.object({
1474
+ /** Restrict to a single camera; omit for every row. */
1475
+ deviceId: zod.z.number().int().optional(),
1476
+ /** Only decisions at or after this epoch ms. */
1477
+ since: zod.z.number().int().optional(),
1478
+ /** Restrict to one verdict — the miss rate is read one population at a time. */
1479
+ verdict: BirthDecisionVerdictSchema.optional(),
1480
+ /** Max rows returned, newest-first. */
1481
+ limit: zod.z.number().int().min(1).max(5e3).optional()
1482
+ });
1483
+ //#endregion
1484
+ //#region src/interfaces/debug-note-archive.ts
1485
+ /**
1486
+ * Debug-note archive — the durable corpus of what operators asked to be
1487
+ * checked, and the ONE thing about a debug note that outlives its track.
1488
+ *
1489
+ * D353 bound `debugNote` to the `debug` flag: turning the flag off clears the
1490
+ * note in the same statement, so a track can never present a question without
1491
+ * the flag that explains it. That invariant is right and stays. Its side
1492
+ * effect was not: the act of REVIEWING a debug set destroyed every note in it,
1493
+ * and the notes that survived a sweep aged out with their tracks anyway. On
1494
+ * 2026-09-08 the notes from one morning's review — 25 to 27 of them — were
1495
+ * already unreadable by the afternoon, leaving 11.
1496
+ *
1497
+ * So the clear now ARCHIVES first (D405). A row here is:
1498
+ *
1499
+ * - written ONLY by the hub, from `applyTrackFlags`, immediately before the
1500
+ * note is cleared off the track row;
1501
+ * - keyed by ITS OWN id, derived from `(sourceTrackId, note)` so reviewing
1502
+ * the same track twice cannot double the corpus;
1503
+ * - NOT a track row, NOT subject to track retention, and NOT a durability pin
1504
+ * on anything — the track it names is expected to be gone.
1505
+ *
1506
+ * `sourceTrackId` is deliberately not called `trackId`. The analytics
1507
+ * retention model derives track OWNERSHIP from a declared `trackId` column
1508
+ * (`collection-classification.ts`), and a row that the cascade would delete
1509
+ * with its track is exactly the row this table exists to keep. The name says
1510
+ * PROVENANCE — the same distinction the stationary registry draws for the same
1511
+ * reason.
1512
+ */
1513
+ /**
1514
+ * How many archived notes the cluster keeps, across every camera.
1515
+ *
1516
+ * GLOBAL, not per-device, and a COUNT, not an age. Both halves are deliberate:
1517
+ *
1518
+ * - A per-device bound multiplies by the fleet — 30 cameras at 500 each is
1519
+ * 15 000 rows to protect a corpus that only ever gets read fleet-wide (the
1520
+ * thing being mined is "what keeps going wrong", not "what goes wrong on
1521
+ * 617"). One global ceiling is one number to reason about.
1522
+ * - An AGE bound would delete the corpus for being old, which is the failure
1523
+ * being fixed: a note from six months ago is the most valuable row in the
1524
+ * table precisely because the pattern it describes has had time to repeat.
1525
+ *
1526
+ * The number is sized off the measured rate. The 2026-09-08 sweep produced
1527
+ * 25–27 notes; call a heavy day 100. 5 000 rows is roughly fifty such days,
1528
+ * and at the observed rate (a few sweeps a week) several years. The cost is
1529
+ * bounded by construction: a note is capped at `MAX_TRACK_DEBUG_NOTE_LEN`
1530
+ * (500 chars) and the metadata beside it is under 100 bytes, so the ceiling is
1531
+ * ~3 MB of text and the realistic occupancy is well under 1 MB — against a
1532
+ * `tracks` table measured at 4.6 MB for a single 501-row page.
1533
+ */
1534
+ var MAX_ARCHIVED_DEBUG_NOTES = 5e3;
1535
+ /** Default page size for `listArchivedDebugNotes` when the caller omits one. */
1536
+ var ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT = 200;
1537
+ /** One archived note — the operator's words plus enough context to find what
1538
+ * they were looking at, after the track itself is gone. */
1539
+ var ArchivedDebugNoteSchema = zod.z.object({
1540
+ /** Archive row id. Derived from `(sourceTrackId, note)`, so re-archiving the
1541
+ * same sentence about the same track lands on the row already there. */
1542
+ id: zod.z.string(),
1543
+ /** The operator's own words, verbatim. */
1544
+ note: zod.z.string(),
1545
+ /** The camera. Required — every question here is asked per-camera. */
1546
+ deviceId: zod.z.number().int(),
1547
+ /** The track the note was written on. PROVENANCE, not ownership: the track
1548
+ * is expected to be gone, and this row is not a reference to it. */
1549
+ sourceTrackId: zod.z.string(),
1550
+ /** Epoch ms the note was moved here, i.e. when the operator reviewed it. */
1551
+ archivedAt: zod.z.number().int(),
1552
+ /** The track's `firstSeen`, when the row still knew it; `null` otherwise. */
1553
+ trackStartedAt: zod.z.number().int().nullable(),
1554
+ /** The track's detector class (`person`, `car`, …); `null` when unknown. */
1555
+ trackClass: zod.z.string().nullable(),
1556
+ /** The track's display label at review time; `null` when it had none. */
1557
+ trackLabel: zod.z.string().nullable()
1558
+ });
1559
+ /** Query input for `listArchivedDebugNotes` — newest first, one camera or all. */
1560
+ var ArchivedDebugNoteQueryInputSchema = zod.z.object({
1561
+ /** Restrict to a single camera; omit for every row. */
1562
+ deviceId: zod.z.number().int().optional(),
1563
+ /** Max rows returned, newest-first. */
1564
+ limit: zod.z.number().int().min(1).max(1e3).optional()
1565
+ });
1566
+ //#endregion
1260
1567
  //#region src/interfaces/device-capabilities/camera.ts
1261
1568
  /** Friendly display labels for stream quality IDs. */
1262
1569
  var STREAM_QUALITY_LABELS = {
@@ -20186,6 +20493,51 @@ var pipelineAnalyticsCapability = {
20186
20493
  flags: TrackFlagsPatchSchema
20187
20494
  }), TrackFlagsSchema, { kind: "mutation" }),
20188
20495
  /**
20496
+ * The archived debug notes, newest first — the corpus of what operators
20497
+ * have asked to be checked, across tracks that no longer exist (D405).
20498
+ *
20499
+ * `setTrackFlags` writes it: a patch carrying `debug: false` still clears
20500
+ * the note off the track row in the same statement (D353's invariant is
20501
+ * untouched), but the note is APPENDED here first. That is the whole point
20502
+ * — before D405, finishing a review destroyed every note written during it,
20503
+ * and the ones that survived aged out with their tracks. This method is the
20504
+ * only way to read what was kept.
20505
+ *
20506
+ * `auth: 'protected'`, matching `setTrackFlags` next door and for the same
20507
+ * reason: the notes are written from the viewer, by an authenticated
20508
+ * non-admin session, and a corpus only an admin can read is a corpus the
20509
+ * person who wrote it cannot use. It carries operator prose about cameras
20510
+ * and nothing else — no media, no paths, no identities beyond a track's
20511
+ * own display label.
20512
+ *
20513
+ * Newest-first, capped by `limit` (default
20514
+ * {@link ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT}), optionally one camera. The
20515
+ * table itself is bounded by row count, not by age — see
20516
+ * {@link MAX_ARCHIVED_DEBUG_NOTES} for why an age clock would be the wrong
20517
+ * bound for exactly this data.
20518
+ */
20519
+ listArchivedDebugNotes: require_sleep.method(ArchivedDebugNoteQueryInputSchema, zod.z.array(ArchivedDebugNoteSchema).readonly(), { kind: "query" }),
20520
+ /**
20521
+ * Birth decisions — one row per resolved confirmation-gate verdict (D409).
20522
+ *
20523
+ * The READ half of a measurement, and only that: it gates nothing and no
20524
+ * runtime path consults it. It exists because the two questions the
20525
+ * operator's "traccia partita in ritardo" marks raised are both multi-day
20526
+ * and `logs.query` holds under an hour of fleet traffic —
20527
+ * • how often is a birth DECIDED on a frame the subject was not observed
20528
+ * in (`decidedOnCoastedFrame`), and did D379's carried pixels make it a
20529
+ * real observation anyway (`decidedByBirthEvidence`);
20530
+ * • how long after the camera's motion onset does a track's `firstSeen`
20531
+ * land (`birthLatencyMs` — read its error bars in
20532
+ * `BirthDecisionRecordSchema` before quoting it).
20533
+ *
20534
+ * Newest-first, capped by `limit` (default
20535
+ * {@link BIRTH_DECISION_DEFAULT_LIMIT}), optionally scoped to one camera,
20536
+ * one verdict, or a `since`. The table is bounded by row count, not age —
20537
+ * see {@link MAX_BIRTH_DECISION_RECORDS}.
20538
+ */
20539
+ listBirthDecisions: require_sleep.method(BirthDecisionQueryInputSchema, zod.z.array(BirthDecisionRecordSchema).readonly(), { kind: "query" }),
20540
+ /**
20189
20541
  * Durable event-store footprint for the management UI: event rows
20190
20542
  * (motion + object + audio) counted per camera + total, plus the
20191
20543
  * event-owned media bytes on disk per camera + total. Stat/count-based,
@@ -44572,6 +44924,18 @@ var METHOD_ACCESS_MAP = Object.freeze({
44572
44924
  addonId: null,
44573
44925
  access: "view"
44574
44926
  },
44927
+ "pipelineAnalytics.listArchivedDebugNotes": {
44928
+ capName: "pipeline-analytics",
44929
+ capScope: "device",
44930
+ addonId: null,
44931
+ access: "view"
44932
+ },
44933
+ "pipelineAnalytics.listBirthDecisions": {
44934
+ capName: "pipeline-analytics",
44935
+ capScope: "device",
44936
+ addonId: null,
44937
+ access: "view"
44938
+ },
44575
44939
  "pipelineAnalytics.listEventKinds": {
44576
44940
  capName: "pipeline-analytics",
44577
44941
  capScope: "device",
@@ -48628,6 +48992,16 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
48628
48992
  form: "array",
48629
48993
  optional: true
48630
48994
  }],
48995
+ "pipelineAnalytics.listArchivedDebugNotes": [{
48996
+ name: "deviceId",
48997
+ form: "single",
48998
+ optional: true
48999
+ }],
49000
+ "pipelineAnalytics.listBirthDecisions": [{
49001
+ name: "deviceId",
49002
+ form: "single",
49003
+ optional: true
49004
+ }],
48631
49005
  "pipelineAnalytics.listEventKinds": [{
48632
49006
  name: "deviceId",
48633
49007
  form: "single",
@@ -54624,6 +54998,7 @@ exports.ACCESS_ROLES = ACCESS_ROLES;
54624
54998
  exports.ALEXA_EGRESS_PROFILE = ALEXA_EGRESS_PROFILE;
54625
54999
  exports.ALL_CAPABILITY_DEFINITIONS = ALL_CAPABILITY_DEFINITIONS;
54626
55000
  exports.APPLE_SA_TO_MACRO = APPLE_SA_TO_MACRO;
55001
+ exports.ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT = ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT;
54627
55002
  exports.AUDIO_ANALYSIS_CAP_NAME = AUDIO_ANALYSIS_CAP_NAME;
54628
55003
  exports.AUDIO_BACKEND_CHOICES = AUDIO_BACKEND_CHOICES;
54629
55004
  exports.AUDIO_MACRO_LABELS = AUDIO_MACRO_LABELS;
@@ -54661,6 +55036,8 @@ exports.ApiKeyRecordSchema = ApiKeyRecordSchema;
54661
55036
  exports.ApiKeySummarySchema = ApiKeySummarySchema;
54662
55037
  exports.ArchiveEntrySchema = ArchiveEntrySchema;
54663
55038
  exports.ArchiveManifestSchema = ArchiveManifestSchema;
55039
+ exports.ArchivedDebugNoteQueryInputSchema = ArchivedDebugNoteQueryInputSchema;
55040
+ exports.ArchivedDebugNoteSchema = ArchivedDebugNoteSchema;
54664
55041
  exports.AttachmentMediaTypeSchema = AttachmentMediaTypeSchema;
54665
55042
  exports.AttachmentSchema = AttachmentSchema;
54666
55043
  exports.AudioAnalysisResultSchema = AudioAnalysisResultSchema;
@@ -54693,6 +55070,7 @@ exports.BACKEND_TO_FORMAT = BACKEND_TO_FORMAT;
54693
55070
  exports.BASE_LIVE_EGRESS_PROFILE = BASE_LIVE_EGRESS_PROFILE;
54694
55071
  exports.BATTERY_DEVICE_PROFILE = BATTERY_DEVICE_PROFILE;
54695
55072
  exports.BATTERY_UNREACHABLE_AFTER_MS = BATTERY_UNREACHABLE_AFTER_MS;
55073
+ exports.BIRTH_DECISION_DEFAULT_LIMIT = BIRTH_DECISION_DEFAULT_LIMIT;
54696
55074
  exports.BOOT_RECOVERY_BACKOFF_MS = require_sleep.BOOT_RECOVERY_BACKOFF_MS;
54697
55075
  exports.BacklightModeSchema = BacklightModeSchema;
54698
55076
  exports.BackupDestinationInfoSchema = BackupDestinationInfoSchema;
@@ -54706,6 +55084,9 @@ exports.BaseDevice = BaseDevice;
54706
55084
  exports.BaseDeviceProvider = BaseDeviceProvider;
54707
55085
  exports.BatteryStatusSchema = BatteryStatusSchema;
54708
55086
  exports.BinaryStatusSchema = BinaryStatusSchema;
55087
+ exports.BirthDecisionQueryInputSchema = BirthDecisionQueryInputSchema;
55088
+ exports.BirthDecisionRecordSchema = BirthDecisionRecordSchema;
55089
+ exports.BirthDecisionVerdictSchema = BirthDecisionVerdictSchema;
54709
55090
  exports.BoundingBoxSchema = BoundingBoxSchema;
54710
55091
  exports.BrightnessStatusSchema = BrightnessStatusSchema;
54711
55092
  exports.BrokerAddInputSchema = AddInputSchema;
@@ -55081,6 +55462,9 @@ exports.LoggingSettingsStateSchema = LoggingSettingsStateSchema;
55081
55462
  exports.LoginMethodContributionSchema = LoginMethodContributionSchema;
55082
55463
  exports.LoginStageEnum = LoginStageEnum;
55083
55464
  exports.MACRO_LABELS = MACRO_LABELS;
55465
+ exports.MAX_ARCHIVED_DEBUG_NOTES = MAX_ARCHIVED_DEBUG_NOTES;
55466
+ exports.MAX_BIRTH_DECISION_RECORDS = MAX_BIRTH_DECISION_RECORDS;
55467
+ exports.MAX_BIRTH_LATENCY_PROXY_MS = MAX_BIRTH_LATENCY_PROXY_MS;
55084
55468
  exports.MAX_CLIP_EVENT_IDS = MAX_CLIP_EVENT_IDS;
55085
55469
  exports.MAX_CLIP_LABELS = MAX_CLIP_LABELS;
55086
55470
  exports.MAX_CONDITION_DEPTH = MAX_CONDITION_DEPTH;