@camstack/addon-post-analysis 1.2.178 → 1.2.179

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.
@@ -13236,6 +13236,24 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
13236
13236
  kind: "mutation",
13237
13237
  auth: "admin"
13238
13238
  });
13239
+ /**
13240
+ * Device Manager capability — hub-side singleton that unifies device persistence,
13241
+ * live registry access, and all management operations into a single tRPC surface.
13242
+ *
13243
+ * Replaces:
13244
+ * - `device-persistence` capability (persistence methods absorbed here)
13245
+ * - `device-management.router.ts` (deleted in Phase 2)
13246
+ * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
13247
+ *
13248
+ * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
13249
+ * fork into separate processes but never run on remote cluster agents. Therefore:
13250
+ * - No nodeId routing needed — this is a pure hub singleton.
13251
+ * - The hub's DeviceRegistry is the single source of truth for all live devices.
13252
+ * - No shadow registry or cross-node aggregation required.
13253
+ *
13254
+ * Forked workers register devices back to the hub via `ctx.devices`
13255
+ * (DeviceManagerApi → ctx.api.deviceManager.registerDevice), same as today.
13256
+ */
13239
13257
  /** One child-placement directive on a container's `childLayout`. Structurally
13240
13258
  * identical to `ChildLayoutEntry` in `device-management.ts` — the cap wire
13241
13259
  * shape for the same field. The child is identified by its re-sync-stable
@@ -19626,6 +19644,9 @@ var RetrainStatusSchema = _enum([
19626
19644
  *
19627
19645
  * `debug` does NOT pin; it is attention, not durability.
19628
19646
  *
19647
+ * `debugNote` is the operator's own words about WHAT should be checked on this
19648
+ * track, and it lives and dies with `debug` — see {@link MAX_TRACK_DEBUG_NOTE_LEN}.
19649
+ *
19629
19650
  * `favourited` IS a BOOLEAN pin (durability bit), not a retrain lifecycle.
19630
19651
  * A favourited track is skipped by retention the same way `staging` is, but
19631
19652
  * it does not enter `none|staging|trained` and has no staging budget.
@@ -19636,6 +19657,21 @@ var TrackFlagFields = {
19636
19657
  markForTrain: boolean().optional(),
19637
19658
  /** Operator marked this track for diagnostic attention. */
19638
19659
  debug: boolean().optional(),
19660
+ /**
19661
+ * What the operator wants CHECKED on this track, in their own words.
19662
+ *
19663
+ * The flag alone says "look at this" and nothing about what for; a debug set
19664
+ * reviewed hours later is a list of tracks with no question attached. Bound
19665
+ * to `debug` on the WRITE side — the body clears it when `debug` goes off,
19666
+ * and refuses it on a track whose debug is off — so a note can never outlive
19667
+ * the attention it belongs to.
19668
+ *
19669
+ * `''` is a real value ("marked, nothing to add"); the ABSENT key means
19670
+ * "leave whatever is stored alone", which is what lets a surface turn debug
19671
+ * on without a note (a cancelled prompt) and what lets it edit the note
19672
+ * without touching the flag.
19673
+ */
19674
+ debugNote: string().max(500).optional(),
19639
19675
  /** Operator favourited this track. Pins it against pruning. */
19640
19676
  favourited: boolean().optional()
19641
19677
  };
@@ -19662,6 +19698,12 @@ var TrackFlagsSchema = object({
19662
19698
  trackId: string(),
19663
19699
  markForTrain: boolean(),
19664
19700
  debug: boolean(),
19701
+ /** The note as it STANDS after the write — never absent here (a track with no
19702
+ * note reports `''`), for the same reason the booleans are required: a
19703
+ * surface that has just written must be able to render the note it now owns
19704
+ * without a re-fetch, and an absent key would read as "unchanged" on a
19705
+ * screen that has no previous value to keep. */
19706
+ debugNote: string(),
19665
19707
  favourited: boolean(),
19666
19708
  /** The lifecycle state the boolean was derived from. Required here (unlike on
19667
19709
  * a track row) because this shape is only ever produced by the write body,
@@ -20954,11 +20996,20 @@ var pipelineAnalyticsCapability = {
20954
20996
  * staging is a deliberate action of the retrain page, which is also the only
20955
20997
  * thing that produces `trained` in the first place.
20956
20998
  *
20957
- * Returns the RESOLVED state of both flags (absent → `false`) plus the
20958
- * `retrainStatus` they were derived from, so a caller can drive its toggle
20959
- * — and render a `trained` badge — without a re-fetch. Rejects an unknown
20960
- * track, a new staging mark on a device already holding its full budget, and
20961
- * any `markForTrain` write against a trained track.
20999
+ * `debugNote` is the operator's sentence about WHAT to check on this track,
21000
+ * and it is bound to `debug`: writing `debug: false` clears it in the same
21001
+ * statement, and a note sent for a track whose debug is off (and is not
21002
+ * being turned on) is DROPPED with a warning rather than stored where
21003
+ * nothing will ever read it. An omitted `debugNote` leaves the stored one
21004
+ * alone — which is what lets a surface turn debug on when the operator
21005
+ * declined to type anything, without that refusal costing them the flag.
21006
+ *
21007
+ * Returns the RESOLVED state of all three flags (absent → `false`) plus the
21008
+ * note (absent → `''`) and the `retrainStatus` they were derived from, so a
21009
+ * caller can drive its toggle — and render a `trained` badge — without a
21010
+ * re-fetch. Rejects an unknown track, a new staging mark on a device already
21011
+ * holding its full budget, and any `markForTrain` write against a trained
21012
+ * track.
20962
21013
  */
20963
21014
  setTrackFlags: method(object({
20964
21015
  /** Log/audit scope only — the trackId is globally unique on its own. */
@@ -13267,6 +13267,24 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
13267
13267
  kind: "mutation",
13268
13268
  auth: "admin"
13269
13269
  });
13270
+ /**
13271
+ * Device Manager capability — hub-side singleton that unifies device persistence,
13272
+ * live registry access, and all management operations into a single tRPC surface.
13273
+ *
13274
+ * Replaces:
13275
+ * - `device-persistence` capability (persistence methods absorbed here)
13276
+ * - `device-management.router.ts` (deleted in Phase 2)
13277
+ * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
13278
+ *
13279
+ * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
13280
+ * fork into separate processes but never run on remote cluster agents. Therefore:
13281
+ * - No nodeId routing needed — this is a pure hub singleton.
13282
+ * - The hub's DeviceRegistry is the single source of truth for all live devices.
13283
+ * - No shadow registry or cross-node aggregation required.
13284
+ *
13285
+ * Forked workers register devices back to the hub via `ctx.devices`
13286
+ * (DeviceManagerApi → ctx.api.deviceManager.registerDevice), same as today.
13287
+ */
13270
13288
  /** One child-placement directive on a container's `childLayout`. Structurally
13271
13289
  * identical to `ChildLayoutEntry` in `device-management.ts` — the cap wire
13272
13290
  * shape for the same field. The child is identified by its re-sync-stable
@@ -19657,6 +19675,9 @@ var RetrainStatusSchema = _enum([
19657
19675
  *
19658
19676
  * `debug` does NOT pin; it is attention, not durability.
19659
19677
  *
19678
+ * `debugNote` is the operator's own words about WHAT should be checked on this
19679
+ * track, and it lives and dies with `debug` — see {@link MAX_TRACK_DEBUG_NOTE_LEN}.
19680
+ *
19660
19681
  * `favourited` IS a BOOLEAN pin (durability bit), not a retrain lifecycle.
19661
19682
  * A favourited track is skipped by retention the same way `staging` is, but
19662
19683
  * it does not enter `none|staging|trained` and has no staging budget.
@@ -19667,6 +19688,21 @@ var TrackFlagFields = {
19667
19688
  markForTrain: boolean().optional(),
19668
19689
  /** Operator marked this track for diagnostic attention. */
19669
19690
  debug: boolean().optional(),
19691
+ /**
19692
+ * What the operator wants CHECKED on this track, in their own words.
19693
+ *
19694
+ * The flag alone says "look at this" and nothing about what for; a debug set
19695
+ * reviewed hours later is a list of tracks with no question attached. Bound
19696
+ * to `debug` on the WRITE side — the body clears it when `debug` goes off,
19697
+ * and refuses it on a track whose debug is off — so a note can never outlive
19698
+ * the attention it belongs to.
19699
+ *
19700
+ * `''` is a real value ("marked, nothing to add"); the ABSENT key means
19701
+ * "leave whatever is stored alone", which is what lets a surface turn debug
19702
+ * on without a note (a cancelled prompt) and what lets it edit the note
19703
+ * without touching the flag.
19704
+ */
19705
+ debugNote: string().max(500).optional(),
19670
19706
  /** Operator favourited this track. Pins it against pruning. */
19671
19707
  favourited: boolean().optional()
19672
19708
  };
@@ -19693,6 +19729,12 @@ var TrackFlagsSchema = object({
19693
19729
  trackId: string(),
19694
19730
  markForTrain: boolean(),
19695
19731
  debug: boolean(),
19732
+ /** The note as it STANDS after the write — never absent here (a track with no
19733
+ * note reports `''`), for the same reason the booleans are required: a
19734
+ * surface that has just written must be able to render the note it now owns
19735
+ * without a re-fetch, and an absent key would read as "unchanged" on a
19736
+ * screen that has no previous value to keep. */
19737
+ debugNote: string(),
19696
19738
  favourited: boolean(),
19697
19739
  /** The lifecycle state the boolean was derived from. Required here (unlike on
19698
19740
  * a track row) because this shape is only ever produced by the write body,
@@ -20985,11 +21027,20 @@ var pipelineAnalyticsCapability = {
20985
21027
  * staging is a deliberate action of the retrain page, which is also the only
20986
21028
  * thing that produces `trained` in the first place.
20987
21029
  *
20988
- * Returns the RESOLVED state of both flags (absent → `false`) plus the
20989
- * `retrainStatus` they were derived from, so a caller can drive its toggle
20990
- * — and render a `trained` badge — without a re-fetch. Rejects an unknown
20991
- * track, a new staging mark on a device already holding its full budget, and
20992
- * any `markForTrain` write against a trained track.
21030
+ * `debugNote` is the operator's sentence about WHAT to check on this track,
21031
+ * and it is bound to `debug`: writing `debug: false` clears it in the same
21032
+ * statement, and a note sent for a track whose debug is off (and is not
21033
+ * being turned on) is DROPPED with a warning rather than stored where
21034
+ * nothing will ever read it. An omitted `debugNote` leaves the stored one
21035
+ * alone — which is what lets a surface turn debug on when the operator
21036
+ * declined to type anything, without that refusal costing them the flag.
21037
+ *
21038
+ * Returns the RESOLVED state of all three flags (absent → `false`) plus the
21039
+ * note (absent → `''`) and the `retrainStatus` they were derived from, so a
21040
+ * caller can drive its toggle — and render a `trained` badge — without a
21041
+ * re-fetch. Rejects an unknown track, a new staging mark on a device already
21042
+ * holding its full budget, and any `markForTrain` write against a trained
21043
+ * track.
20993
21044
  */
20994
21045
  setTrackFlags: method(object({
20995
21046
  /** Log/audit scope only — the trackId is globally unique on its own. */
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BfdKgU7-.js");
5
+ const require_dist = require("../dist-QJ8XSy0F.js");
6
6
  let node_fs = require("node:fs");
7
7
  node_fs = require_dist.__toESM(node_fs);
8
8
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { At as BaseAddon, F as PoolMemoryWatchdog, St as resolvePoolMemoryPolicy, at as embeddingEncoderCapability, gt as parseProcStatus, ut as hfModelUrl } from "../dist-B1cFmGC0.mjs";
1
+ import { At as BaseAddon, F as PoolMemoryWatchdog, St as resolvePoolMemoryPolicy, at as embeddingEncoderCapability, gt as parseProcStatus, ut as hfModelUrl } from "../dist-NBkMwEZ0.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import * as fs from "node:fs";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
3
3
  var e = {
4
4
  "@camstack/sdk": {
5
5
  name: "@camstack/sdk",
6
- version: "1.2.66",
6
+ version: "1.2.67",
7
7
  scope: ["default"],
8
8
  loaded: !1,
9
9
  from: "addon_pipeline_analytics_widgets",
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.2.146",
21
+ version: "1.2.147",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_pipeline_analytics_widgets",
@@ -33,7 +33,7 @@ var e = {
33
33
  },
34
34
  "@camstack/ui-library": {
35
35
  name: "@camstack/ui-library",
36
- version: "1.2.112",
36
+ version: "1.2.113",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_pipeline_analytics_widgets",
@@ -36,7 +36,7 @@ async function r() {
36
36
  }
37
37
  },
38
38
  "@camstack/types": {
39
- version: "1.2.146",
39
+ version: "1.2.147",
40
40
  scope: "default",
41
41
  shareConfig: {
42
42
  singleton: !0,
@@ -45,7 +45,7 @@ async function r() {
45
45
  }
46
46
  },
47
47
  "@camstack/sdk": {
48
- version: "1.2.66",
48
+ version: "1.2.67",
49
49
  scope: "default",
50
50
  shareConfig: {
51
51
  singleton: !0,
@@ -81,7 +81,7 @@ async function r() {
81
81
  }
82
82
  },
83
83
  "@camstack/ui-library": {
84
- version: "1.2.112",
84
+ version: "1.2.113",
85
85
  scope: "default",
86
86
  shareConfig: {
87
87
  singleton: !0,
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BfdKgU7-.js");
5
+ const require_dist = require("../dist-QJ8XSy0F.js");
6
6
  let node_fs = require("node:fs");
7
7
  node_fs = require_dist.__toESM(node_fs, 1);
8
8
  let node_path = require("node:path");
@@ -39528,6 +39528,10 @@ var TRACKS_COLUMNS = [
39528
39528
  name: "debug",
39529
39529
  type: "BOOLEAN"
39530
39530
  },
39531
+ {
39532
+ name: "debugNote",
39533
+ type: "TEXT"
39534
+ },
39531
39535
  {
39532
39536
  name: "favourited",
39533
39537
  type: "BOOLEAN"
@@ -39723,6 +39727,7 @@ function cloneTrack(t) {
39723
39727
  markForTrain: t.retrainStatus === "staging"
39724
39728
  } : {},
39725
39729
  ...t.debug !== void 0 ? { debug: t.debug } : {},
39730
+ ...t.debugNote !== void 0 ? { debugNote: t.debugNote } : {},
39726
39731
  ...t.favourited !== void 0 ? { favourited: t.favourited } : {},
39727
39732
  ...t.hasFace !== void 0 ? { hasFace: t.hasFace } : {},
39728
39733
  ...t.hasEmbeddedFace !== void 0 ? { hasEmbeddedFace: t.hasEmbeddedFace } : {},
@@ -40113,12 +40118,14 @@ var TrackStore = class {
40113
40118
  const nextStatus = patch.markForTrain === void 0 ? void 0 : patch.markForTrain ? "staging" : "none";
40114
40119
  if (nextStatus !== void 0) data["retrainStatus"] = nextStatus;
40115
40120
  if (patch.debug !== void 0) data["debug"] = patch.debug;
40121
+ if (patch.debugNote !== void 0) data["debugNote"] = patch.debugNote;
40116
40122
  if (patch.favourited !== void 0) data["favourited"] = patch.favourited;
40117
40123
  if (Object.keys(data).length === 0) return;
40118
40124
  const active = this.active.get(trackId);
40119
40125
  if (active) {
40120
40126
  if (nextStatus !== void 0) active.retrainStatus = nextStatus;
40121
40127
  if (patch.debug !== void 0) active.debug = patch.debug;
40128
+ if (patch.debugNote !== void 0) active.debugNote = patch.debugNote;
40122
40129
  if (patch.favourited !== void 0) active.favourited = patch.favourited;
40123
40130
  }
40124
40131
  try {
@@ -40321,6 +40328,7 @@ var TrackStore = class {
40321
40328
  if (active) return {
40322
40329
  retrainStatus: active.retrainStatus ?? "none",
40323
40330
  debug: active.debug === true,
40331
+ debugNote: active.debugNote ?? "",
40324
40332
  favourited: active.favourited === true
40325
40333
  };
40326
40334
  const persisted = await this.getPersistedByTrackId(trackId);
@@ -40328,6 +40336,7 @@ var TrackStore = class {
40328
40336
  return {
40329
40337
  retrainStatus: persisted.retrainStatus ?? "none",
40330
40338
  debug: persisted.debug === true,
40339
+ debugNote: persisted.debugNote ?? "",
40331
40340
  favourited: persisted.favourited === true
40332
40341
  };
40333
40342
  }
@@ -41227,6 +41236,7 @@ var TrackStore = class {
41227
41236
  ...t.audioLabels !== void 0 ? { audioLabels: [...t.audioLabels] } : {},
41228
41237
  ...t.retrainStatus !== void 0 ? { retrainStatus: t.retrainStatus } : {},
41229
41238
  ...t.debug !== void 0 ? { debug: t.debug } : {},
41239
+ ...t.debugNote !== void 0 ? { debugNote: t.debugNote } : {},
41230
41240
  ...t.favourited !== void 0 ? { favourited: t.favourited } : {},
41231
41241
  ...t.hasFace !== void 0 ? { hasFace: t.hasFace } : {},
41232
41242
  ...t.hasEmbeddedFace !== void 0 ? { hasEmbeddedFace: t.hasEmbeddedFace } : {},
@@ -41285,6 +41295,7 @@ var TrackStore = class {
41285
41295
  const audioLabels = readAudioLabels(data["audioLabels"]);
41286
41296
  const retrainParsed = require_dist.RetrainStatusSchema.safeParse(data["retrainStatus"]);
41287
41297
  const debug = data["debug"];
41298
+ const debugNote = data["debugNote"];
41288
41299
  const favourited = data["favourited"];
41289
41300
  const hasFace = data["hasFace"];
41290
41301
  /** Same read as `hasFace` — NULL / missing stays ABSENT, never `false`.
@@ -41333,6 +41344,7 @@ var TrackStore = class {
41333
41344
  markForTrain: retrainParsed.data === "staging"
41334
41345
  } : {},
41335
41346
  ...typeof debug === "boolean" ? { debug } : {},
41347
+ ...typeof debugNote === "string" ? { debugNote } : {},
41336
41348
  ...typeof favourited === "boolean" ? { favourited } : {},
41337
41349
  ...typeof hasFace === "boolean" ? { hasFace } : {},
41338
41350
  ...typeof hasEmbeddedFace === "boolean" ? { hasEmbeddedFace } : {},
@@ -54904,6 +54916,47 @@ var TrackAlreadyTrainedError = class extends Error {
54904
54916
  }
54905
54917
  };
54906
54918
  /**
54919
+ * The `debugNote` half of the patch — `{}` when nothing should be written.
54920
+ *
54921
+ * The note is not an independent column: it is the operator's sentence about
54922
+ * why `debug` is on, so it exists exactly as long as the flag does. Three
54923
+ * cases, and the order matters:
54924
+ *
54925
+ * 1. **`debug: false` in the patch → the note is CLEARED**, whatever the
54926
+ * caller sent with it. A note surviving the flag is a leftover that reads
54927
+ * as a live request, and the invariant belongs here rather than in the
54928
+ * three surfaces that write this patch — one of which is a browser grid
54929
+ * that has no note UI at all and would never think to clear it.
54930
+ * 2. **No note in the patch → nothing is written.** This is what makes a
54931
+ * cancelled prompt free: the app turns debug on, the operator declines to
54932
+ * type, and the flag still lands. Refusing to write a note must never cost
54933
+ * the capture that was asked for.
54934
+ * 3. **A note, on a track whose debug is off and is not being turned on →
54935
+ * DROPPED.** Storing it would put the operator's words somewhere no reader
54936
+ * of a debug set will ever look. Dropped work is logged, per-camera: a
54937
+ * branch that silently discards a sentence is indistinguishable from one
54938
+ * that stored it.
54939
+ *
54940
+ * `''` is a WRITE, not an absence — clearing a note the operator no longer
54941
+ * stands behind is a real edit.
54942
+ */
54943
+ function resolveNotePatch(deps, input, current) {
54944
+ if (input.flags.debug === false) return { debugNote: "" };
54945
+ if (input.flags.debugNote === void 0) return {};
54946
+ if (!(input.flags.debug === true || current.debug)) {
54947
+ deps.logger.warn("setTrackFlags dropped a debug note — the track is not flagged for debug", {
54948
+ tags: { deviceId: input.deviceId },
54949
+ meta: {
54950
+ deviceId: input.deviceId,
54951
+ trackId: input.trackId,
54952
+ noteLength: input.flags.debugNote.length
54953
+ }
54954
+ });
54955
+ return {};
54956
+ }
54957
+ return { debugNote: input.flags.debugNote };
54958
+ }
54959
+ /**
54907
54960
  * Apply a partial flag patch and report the RESOLVED state of both flags.
54908
54961
  *
54909
54962
  * Existence is checked BEFORE the write: a patch against a track that no longer
@@ -54964,7 +55017,8 @@ async function applyTrackFlags(deps, input) {
54964
55017
  const patch = {
54965
55018
  ...input.flags.markForTrain !== void 0 ? { markForTrain: input.flags.markForTrain } : {},
54966
55019
  ...input.flags.debug !== void 0 ? { debug: input.flags.debug } : {},
54967
- ...input.flags.favourited !== void 0 ? { favourited: input.flags.favourited } : {}
55020
+ ...input.flags.favourited !== void 0 ? { favourited: input.flags.favourited } : {},
55021
+ ...resolveNotePatch(deps, input, current)
54968
55022
  };
54969
55023
  if (Object.keys(patch).length > 0) await store.setFlags(input.trackId, patch);
54970
55024
  const retrainStatus = patch.markForTrain === void 0 ? current.retrainStatus : patch.markForTrain ? "staging" : "none";
@@ -54972,6 +55026,7 @@ async function applyTrackFlags(deps, input) {
54972
55026
  trackId: input.trackId,
54973
55027
  markForTrain: retrainStatus === "staging",
54974
55028
  debug: patch.debug ?? current.debug,
55029
+ debugNote: patch.debugNote ?? current.debugNote,
54975
55030
  favourited: patch.favourited ?? current.favourited,
54976
55031
  retrainStatus
54977
55032
  };
@@ -58701,6 +58756,13 @@ var ANALYTICS_COLLECTIONS = [
58701
58756
  kind: "never-orphaned",
58702
58757
  why: "declares `sourceTrackId`, which is a REFERENCE and not ownership — a parked object outliving the track that first saw it is the entire purpose of the registry. It has its own TTL sweep keyed on OBSERVED frame time, not wall clock. Classified by hand precisely because the derivation must not see it."
58703
58758
  }
58759
+ },
58760
+ {
58761
+ collection: STATIONARY_EVIDENCE_COLLECTION,
58762
+ classification: {
58763
+ kind: "never-orphaned",
58764
+ why: "the candidate half of the registry above, and it inherits that entry's argument for the same reason: `sourceTrackId` is the PROVENANCE of whichever track happened to be looking, never ownership. It cannot be `track-owned` on the behaviour, not merely on the name — a candidate is fed by a SUCCESSION of tracks (`StationaryEvidenceLedger.observe` re-binds under D280, and `retainTracks` drops a binding the moment the tracker forgets the track) while the row itself survives, because agreement accumulating ACROSS tracks is the whole measurement. Cascading it on the first contributor's eviction would delete evidence still being gathered by tracks alive right now. Its bounds are its own and stay inside D61's one-clock rule: a candidate untouched for EVIDENCE_WINDOW_MS (6h) is dropped by `expire`, on the next OBSERVATION of that device and against the frame timestamp — the same observed-time clock the registry uses, never wall clock and never a sweep; a device is capped at EVIDENCE_MAX_CANDIDATES_PER_DEVICE (512, least-recently-touched evicted); a promotion deletes the candidate the instant the registry entry is born, so the two tables cannot both hold the object; and `clearDevice` takes the rest when a camera is removed. Deliberately NOT `owner-indirection`: that kind enrols a collection into `auditableCollections()`, and the audit has a resolver only for `pipeline-analytics:media`."
58765
+ }
58704
58766
  }
58705
58767
  ];
58706
58768
  /** The collections the orphan audit actually scans — everything with an owner. */
@@ -1,4 +1,4 @@
1
- import { $ as audioModeOf, A as NcSnoozeSchema, At as BaseAddon, B as SCENE_DEFAULT_UNCOVERED_POLICY, Bt as boolean, C as NcConditionDescriptorSchema, Ct as sceneMonitorCapability, D as NcRuleTargetSchema, Dt as videoclipsCapability, E as NcRuleSchema, Et as vectorDimFromBase64, Ft as isDeviceScopedCap, G as TimelapseRulePatchSchema, Gt as partialRecord, H as SceneMonitorSchema, Ht as literal, I as RECORDING_EXPORT_MAX_READ_BYTES, It as nodePin, J as VISIT_MERGE_GAP_MS, Jt as unknown, K as TimelapseRuleSchema, Kt as record, L as RetrainStatusSchema, Lt as sleep$1, M as NcSystemEventKindSchema, Mt as DeviceType, N as NcTaxonomySchema, Nt as createEvent, O as NcScheduleSchema, Ot as zoneAnalyticsCapability, P as OpsLogEntrySchema, Pt as hydrateSchema, Q as audioMetricsCapability, Rt as _enum, S as NC_TAXONOMY, T as NcRulePatchSchema, Tt as systemEventFilterApplies, U as TIMELAPSE_DENSE_FLOOR_SEC, Ut as number, V as SCENE_DIVERGED, Vt as discriminatedUnion, W as TimelapseRuleInputSchema, Wt as object, X as alarmPanelCapability, Y as addonWidgetsSourceCapability, Yt as EventCategory, Z as assertTimelapseCadences, _ as MediaFileKindEnum, _t as pickClusterStepModels, a as DEFAULT_EVENT_COLOR, b as NC_DEFAULT_SNOOZE_MINUTES, bt as readDeviceStateFrom, c as DETECTION_MACRO_CLASSES, ct as faceGalleryCapability, d as EVENT_KIND_BY_CAP, dt as isDetectionMacroClass, et as buildEventKindDescriptor, f as EVENT_PAD_MS, ft as isScheduleActive, g as MACRO_LABELS, h as LabelAttributionSchema, ht as notificationRulesCapability, i as COCO_TO_MACRO, it as deriveRecordingMode, j as NcSnoozeSuppressedSchema, jt as CamProfileSchema, k as NcSnoozeInputSchema, kt as errMsg, lt as failureContributionCapability, m as FailureCounters, mt as kebabToCamel, n as BaseDevice, nt as customAction, ot as encodeVectorBase64, p as FULL_IMAGE_BBOX, pt as isSourceCap, q as TrackSourceSchema, qt as string, r as CLUSTER_MODEL_SCOPED_STEPS, rt as defineCustomActions, st as evaluateSensorEdge, t as AUDIO_MACRO_LABELS, tt as cosineSimilarity$1, u as DeclaredDevices, v as NC_ALARM_SYSTEM_EVENT_KINDS, vt as pipelineAnalyticsCapability, w as NcRuleInputSchema, wt as subKindsOf, xt as readTimelapseGeneratedAt, y as NC_CONDITION_CATALOG, yt as plateGalleryCapability, z as SCENE_DEFAULT_ANCHOR_THRESHOLD, zt as array } from "../dist-B1cFmGC0.mjs";
1
+ import { $ as audioModeOf, A as NcSnoozeSchema, At as BaseAddon, B as SCENE_DEFAULT_UNCOVERED_POLICY, Bt as boolean, C as NcConditionDescriptorSchema, Ct as sceneMonitorCapability, D as NcRuleTargetSchema, Dt as videoclipsCapability, E as NcRuleSchema, Et as vectorDimFromBase64, Ft as isDeviceScopedCap, G as TimelapseRulePatchSchema, Gt as partialRecord, H as SceneMonitorSchema, Ht as literal, I as RECORDING_EXPORT_MAX_READ_BYTES, It as nodePin, J as VISIT_MERGE_GAP_MS, Jt as unknown, K as TimelapseRuleSchema, Kt as record, L as RetrainStatusSchema, Lt as sleep$1, M as NcSystemEventKindSchema, Mt as DeviceType, N as NcTaxonomySchema, Nt as createEvent, O as NcScheduleSchema, Ot as zoneAnalyticsCapability, P as OpsLogEntrySchema, Pt as hydrateSchema, Q as audioMetricsCapability, Rt as _enum, S as NC_TAXONOMY, T as NcRulePatchSchema, Tt as systemEventFilterApplies, U as TIMELAPSE_DENSE_FLOOR_SEC, Ut as number, V as SCENE_DIVERGED, Vt as discriminatedUnion, W as TimelapseRuleInputSchema, Wt as object, X as alarmPanelCapability, Y as addonWidgetsSourceCapability, Yt as EventCategory, Z as assertTimelapseCadences, _ as MediaFileKindEnum, _t as pickClusterStepModels, a as DEFAULT_EVENT_COLOR, b as NC_DEFAULT_SNOOZE_MINUTES, bt as readDeviceStateFrom, c as DETECTION_MACRO_CLASSES, ct as faceGalleryCapability, d as EVENT_KIND_BY_CAP, dt as isDetectionMacroClass, et as buildEventKindDescriptor, f as EVENT_PAD_MS, ft as isScheduleActive, g as MACRO_LABELS, h as LabelAttributionSchema, ht as notificationRulesCapability, i as COCO_TO_MACRO, it as deriveRecordingMode, j as NcSnoozeSuppressedSchema, jt as CamProfileSchema, k as NcSnoozeInputSchema, kt as errMsg, lt as failureContributionCapability, m as FailureCounters, mt as kebabToCamel, n as BaseDevice, nt as customAction, ot as encodeVectorBase64, p as FULL_IMAGE_BBOX, pt as isSourceCap, q as TrackSourceSchema, qt as string, r as CLUSTER_MODEL_SCOPED_STEPS, rt as defineCustomActions, st as evaluateSensorEdge, t as AUDIO_MACRO_LABELS, tt as cosineSimilarity$1, u as DeclaredDevices, v as NC_ALARM_SYSTEM_EVENT_KINDS, vt as pipelineAnalyticsCapability, w as NcRuleInputSchema, wt as subKindsOf, xt as readTimelapseGeneratedAt, y as NC_CONDITION_CATALOG, yt as plateGalleryCapability, z as SCENE_DEFAULT_ANCHOR_THRESHOLD, zt as array } from "../dist-NBkMwEZ0.mjs";
2
2
  import { t as __exportAll } from "../embedding-encoder/index.mjs";
3
3
  import * as fs from "node:fs";
4
4
  import { promises } from "node:fs";
@@ -39503,6 +39503,10 @@ var TRACKS_COLUMNS = [
39503
39503
  name: "debug",
39504
39504
  type: "BOOLEAN"
39505
39505
  },
39506
+ {
39507
+ name: "debugNote",
39508
+ type: "TEXT"
39509
+ },
39506
39510
  {
39507
39511
  name: "favourited",
39508
39512
  type: "BOOLEAN"
@@ -39698,6 +39702,7 @@ function cloneTrack(t) {
39698
39702
  markForTrain: t.retrainStatus === "staging"
39699
39703
  } : {},
39700
39704
  ...t.debug !== void 0 ? { debug: t.debug } : {},
39705
+ ...t.debugNote !== void 0 ? { debugNote: t.debugNote } : {},
39701
39706
  ...t.favourited !== void 0 ? { favourited: t.favourited } : {},
39702
39707
  ...t.hasFace !== void 0 ? { hasFace: t.hasFace } : {},
39703
39708
  ...t.hasEmbeddedFace !== void 0 ? { hasEmbeddedFace: t.hasEmbeddedFace } : {},
@@ -40088,12 +40093,14 @@ var TrackStore = class {
40088
40093
  const nextStatus = patch.markForTrain === void 0 ? void 0 : patch.markForTrain ? "staging" : "none";
40089
40094
  if (nextStatus !== void 0) data["retrainStatus"] = nextStatus;
40090
40095
  if (patch.debug !== void 0) data["debug"] = patch.debug;
40096
+ if (patch.debugNote !== void 0) data["debugNote"] = patch.debugNote;
40091
40097
  if (patch.favourited !== void 0) data["favourited"] = patch.favourited;
40092
40098
  if (Object.keys(data).length === 0) return;
40093
40099
  const active = this.active.get(trackId);
40094
40100
  if (active) {
40095
40101
  if (nextStatus !== void 0) active.retrainStatus = nextStatus;
40096
40102
  if (patch.debug !== void 0) active.debug = patch.debug;
40103
+ if (patch.debugNote !== void 0) active.debugNote = patch.debugNote;
40097
40104
  if (patch.favourited !== void 0) active.favourited = patch.favourited;
40098
40105
  }
40099
40106
  try {
@@ -40296,6 +40303,7 @@ var TrackStore = class {
40296
40303
  if (active) return {
40297
40304
  retrainStatus: active.retrainStatus ?? "none",
40298
40305
  debug: active.debug === true,
40306
+ debugNote: active.debugNote ?? "",
40299
40307
  favourited: active.favourited === true
40300
40308
  };
40301
40309
  const persisted = await this.getPersistedByTrackId(trackId);
@@ -40303,6 +40311,7 @@ var TrackStore = class {
40303
40311
  return {
40304
40312
  retrainStatus: persisted.retrainStatus ?? "none",
40305
40313
  debug: persisted.debug === true,
40314
+ debugNote: persisted.debugNote ?? "",
40306
40315
  favourited: persisted.favourited === true
40307
40316
  };
40308
40317
  }
@@ -41202,6 +41211,7 @@ var TrackStore = class {
41202
41211
  ...t.audioLabels !== void 0 ? { audioLabels: [...t.audioLabels] } : {},
41203
41212
  ...t.retrainStatus !== void 0 ? { retrainStatus: t.retrainStatus } : {},
41204
41213
  ...t.debug !== void 0 ? { debug: t.debug } : {},
41214
+ ...t.debugNote !== void 0 ? { debugNote: t.debugNote } : {},
41205
41215
  ...t.favourited !== void 0 ? { favourited: t.favourited } : {},
41206
41216
  ...t.hasFace !== void 0 ? { hasFace: t.hasFace } : {},
41207
41217
  ...t.hasEmbeddedFace !== void 0 ? { hasEmbeddedFace: t.hasEmbeddedFace } : {},
@@ -41260,6 +41270,7 @@ var TrackStore = class {
41260
41270
  const audioLabels = readAudioLabels(data["audioLabels"]);
41261
41271
  const retrainParsed = RetrainStatusSchema.safeParse(data["retrainStatus"]);
41262
41272
  const debug = data["debug"];
41273
+ const debugNote = data["debugNote"];
41263
41274
  const favourited = data["favourited"];
41264
41275
  const hasFace = data["hasFace"];
41265
41276
  /** Same read as `hasFace` — NULL / missing stays ABSENT, never `false`.
@@ -41308,6 +41319,7 @@ var TrackStore = class {
41308
41319
  markForTrain: retrainParsed.data === "staging"
41309
41320
  } : {},
41310
41321
  ...typeof debug === "boolean" ? { debug } : {},
41322
+ ...typeof debugNote === "string" ? { debugNote } : {},
41311
41323
  ...typeof favourited === "boolean" ? { favourited } : {},
41312
41324
  ...typeof hasFace === "boolean" ? { hasFace } : {},
41313
41325
  ...typeof hasEmbeddedFace === "boolean" ? { hasEmbeddedFace } : {},
@@ -54830,6 +54842,47 @@ var TrackAlreadyTrainedError = class extends Error {
54830
54842
  }
54831
54843
  };
54832
54844
  /**
54845
+ * The `debugNote` half of the patch — `{}` when nothing should be written.
54846
+ *
54847
+ * The note is not an independent column: it is the operator's sentence about
54848
+ * why `debug` is on, so it exists exactly as long as the flag does. Three
54849
+ * cases, and the order matters:
54850
+ *
54851
+ * 1. **`debug: false` in the patch → the note is CLEARED**, whatever the
54852
+ * caller sent with it. A note surviving the flag is a leftover that reads
54853
+ * as a live request, and the invariant belongs here rather than in the
54854
+ * three surfaces that write this patch — one of which is a browser grid
54855
+ * that has no note UI at all and would never think to clear it.
54856
+ * 2. **No note in the patch → nothing is written.** This is what makes a
54857
+ * cancelled prompt free: the app turns debug on, the operator declines to
54858
+ * type, and the flag still lands. Refusing to write a note must never cost
54859
+ * the capture that was asked for.
54860
+ * 3. **A note, on a track whose debug is off and is not being turned on →
54861
+ * DROPPED.** Storing it would put the operator's words somewhere no reader
54862
+ * of a debug set will ever look. Dropped work is logged, per-camera: a
54863
+ * branch that silently discards a sentence is indistinguishable from one
54864
+ * that stored it.
54865
+ *
54866
+ * `''` is a WRITE, not an absence — clearing a note the operator no longer
54867
+ * stands behind is a real edit.
54868
+ */
54869
+ function resolveNotePatch(deps, input, current) {
54870
+ if (input.flags.debug === false) return { debugNote: "" };
54871
+ if (input.flags.debugNote === void 0) return {};
54872
+ if (!(input.flags.debug === true || current.debug)) {
54873
+ deps.logger.warn("setTrackFlags dropped a debug note — the track is not flagged for debug", {
54874
+ tags: { deviceId: input.deviceId },
54875
+ meta: {
54876
+ deviceId: input.deviceId,
54877
+ trackId: input.trackId,
54878
+ noteLength: input.flags.debugNote.length
54879
+ }
54880
+ });
54881
+ return {};
54882
+ }
54883
+ return { debugNote: input.flags.debugNote };
54884
+ }
54885
+ /**
54833
54886
  * Apply a partial flag patch and report the RESOLVED state of both flags.
54834
54887
  *
54835
54888
  * Existence is checked BEFORE the write: a patch against a track that no longer
@@ -54890,7 +54943,8 @@ async function applyTrackFlags(deps, input) {
54890
54943
  const patch = {
54891
54944
  ...input.flags.markForTrain !== void 0 ? { markForTrain: input.flags.markForTrain } : {},
54892
54945
  ...input.flags.debug !== void 0 ? { debug: input.flags.debug } : {},
54893
- ...input.flags.favourited !== void 0 ? { favourited: input.flags.favourited } : {}
54946
+ ...input.flags.favourited !== void 0 ? { favourited: input.flags.favourited } : {},
54947
+ ...resolveNotePatch(deps, input, current)
54894
54948
  };
54895
54949
  if (Object.keys(patch).length > 0) await store.setFlags(input.trackId, patch);
54896
54950
  const retrainStatus = patch.markForTrain === void 0 ? current.retrainStatus : patch.markForTrain ? "staging" : "none";
@@ -54898,6 +54952,7 @@ async function applyTrackFlags(deps, input) {
54898
54952
  trackId: input.trackId,
54899
54953
  markForTrain: retrainStatus === "staging",
54900
54954
  debug: patch.debug ?? current.debug,
54955
+ debugNote: patch.debugNote ?? current.debugNote,
54901
54956
  favourited: patch.favourited ?? current.favourited,
54902
54957
  retrainStatus
54903
54958
  };
@@ -58627,6 +58682,13 @@ var ANALYTICS_COLLECTIONS = [
58627
58682
  kind: "never-orphaned",
58628
58683
  why: "declares `sourceTrackId`, which is a REFERENCE and not ownership — a parked object outliving the track that first saw it is the entire purpose of the registry. It has its own TTL sweep keyed on OBSERVED frame time, not wall clock. Classified by hand precisely because the derivation must not see it."
58629
58684
  }
58685
+ },
58686
+ {
58687
+ collection: STATIONARY_EVIDENCE_COLLECTION,
58688
+ classification: {
58689
+ kind: "never-orphaned",
58690
+ why: "the candidate half of the registry above, and it inherits that entry's argument for the same reason: `sourceTrackId` is the PROVENANCE of whichever track happened to be looking, never ownership. It cannot be `track-owned` on the behaviour, not merely on the name — a candidate is fed by a SUCCESSION of tracks (`StationaryEvidenceLedger.observe` re-binds under D280, and `retainTracks` drops a binding the moment the tracker forgets the track) while the row itself survives, because agreement accumulating ACROSS tracks is the whole measurement. Cascading it on the first contributor's eviction would delete evidence still being gathered by tracks alive right now. Its bounds are its own and stay inside D61's one-clock rule: a candidate untouched for EVIDENCE_WINDOW_MS (6h) is dropped by `expire`, on the next OBSERVATION of that device and against the frame timestamp — the same observed-time clock the registry uses, never wall clock and never a sweep; a device is capped at EVIDENCE_MAX_CANDIDATES_PER_DEVICE (512, least-recently-touched evicted); a promotion deletes the candidate the instant the registry entry is born, so the two tables cannot both hold the object; and `clearDevice` takes the rest when a camera is removed. Deliberately NOT `owner-indirection`: that kind enrols a collection into `auditableCollections()`, and the audit has a resolver only for `pipeline-analytics:media`."
58691
+ }
58630
58692
  }
58631
58693
  ];
58632
58694
  /** The collections the orphan audit actually scans — everything with an owner. */
@@ -30,7 +30,7 @@ async function d(e) {
30
30
  }
31
31
  }
32
32
  async function f() {
33
- return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-MvTzBID6.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BL6jNwq1.mjs")).catch((e) => {
34
34
  throw l = void 0, e;
35
35
  }), l;
36
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-post-analysis",
3
- "version": "1.2.178",
3
+ "version": "1.2.179",
4
4
  "description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
5
5
  "keywords": [
6
6
  "camstack",