@camstack/addon-pipeline 1.2.198 → 1.2.200

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 (43) hide show
  1. package/dist/audio-analyzer/index.js +2 -2
  2. package/dist/audio-analyzer/index.mjs +2 -2
  3. package/dist/{default-detection-model-B5v8qEI0.js → default-detection-model-BsmxtyZx.js} +1 -1
  4. package/dist/{default-detection-model-CASpAESz.mjs → default-detection-model-c1LY6thP.mjs} +1 -1
  5. package/dist/detection-pipeline/index.js +5 -5
  6. package/dist/detection-pipeline/index.mjs +4 -4
  7. package/dist/{dist-BxP0SvPz.js → dist-D2YTTkRf.js} +436 -419
  8. package/dist/{dist-oFIHnD88.mjs → dist-mEsrAAc3.mjs} +436 -419
  9. package/dist/{lazy-sharp-VKOG3WW4.js → lazy-sharp-B5bW34uC.js} +1 -1
  10. package/dist/motion-wasm/index.js +2 -2
  11. package/dist/motion-wasm/index.mjs +1 -1
  12. package/dist/{node-B4zETsJ9.mjs → node-CUxep5tc.mjs} +1 -1
  13. package/dist/{node-BIKrNEtS.js → node-x3YokJkD.js} +1 -1
  14. package/dist/pipeline-runner/index.js +8 -7
  15. package/dist/pipeline-runner/index.mjs +7 -6
  16. package/dist/{process-memory-D9xYwm6L.mjs → process-memory-BCqpTruN.mjs} +1 -1
  17. package/dist/{process-memory-CMHOmj75.js → process-memory-CFc4D9A2.js} +1 -1
  18. package/dist/recorder/index.js +169 -334
  19. package/dist/recorder/index.mjs +164 -329
  20. package/dist/restream-intent-B1Difcnq.js +313 -0
  21. package/dist/restream-intent-DWe2kInY.mjs +248 -0
  22. package/dist/{prebuffer-DmBYRQWg.mjs → retire-root-keys-Csb8z2f3.js} +12 -64
  23. package/dist/{prebuffer-DqdeLSsk.js → retire-root-keys-DZvlso2l.mjs} +1 -99
  24. package/dist/{segment-demux-js-DLwL1vmq.js → segment-demux-js-DUZVURQd.js} +1 -1
  25. package/dist/{segment-demux-js-DZysrgdC.mjs → segment-demux-js-xHtB450F.mjs} +1 -1
  26. package/dist/session-decode/decode-worker-child.js +25 -5
  27. package/dist/session-decode/decode-worker-child.mjs +24 -4
  28. package/dist/stream-broker/_stub.js +2 -2
  29. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-C2eTdxvd.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CLXImQ3R.mjs} +3 -3
  30. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-NqqfpkYF.mjs +26 -0
  31. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Dj_qoc1e.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-D62O5bRa.mjs} +1 -1
  32. package/dist/stream-broker/demux-worker-child.js +1 -1
  33. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  34. package/dist/stream-broker/{hostInit-C8tu7NS4.mjs → hostInit-D_cZQhBA.mjs} +3 -3
  35. package/dist/stream-broker/index.js +181 -48
  36. package/dist/stream-broker/index.mjs +172 -39
  37. package/dist/stream-broker/remoteEntry.js +1 -1
  38. package/dist/{worker-protocol-D2mMjUXv.js → worker-protocol-CRDaBlJl.js} +1 -1
  39. package/dist/{worker-protocol-DFlZm_wr.mjs → worker-protocol-oUamwZSn.mjs} +1 -1
  40. package/package.json +1 -1
  41. package/dist/restream-intent-B4BXZra7.mjs +0 -72
  42. package/dist/restream-intent-Cv9x3jmu.js +0 -89
  43. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BcjLrxhF.mjs +0 -26
@@ -2,12 +2,12 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BxP0SvPz.js");
6
- const require_node = require("../node-BIKrNEtS.js");
5
+ const require_dist = require("../dist-D2YTTkRf.js");
6
+ const require_node = require("../node-x3YokJkD.js");
7
7
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
8
- const require_restream_intent = require("../restream-intent-Cv9x3jmu.js");
8
+ const require_restream_intent = require("../restream-intent-B1Difcnq.js");
9
9
  const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DaUBcb13.js");
10
- const require_prebuffer = require("../prebuffer-DqdeLSsk.js");
10
+ const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
11
11
  let node_crypto = require("node:crypto");
12
12
  let node_fs = require("node:fs");
13
13
  let node_path = require("node:path");
@@ -3950,7 +3950,7 @@ var DEFAULT_DEVICE_CONFIG = {
3950
3950
  };
3951
3951
  /** Build the handle. `declare()` MUST run once at boot before any read/write. */
3952
3952
  function recordingConfigsState(store, logger) {
3953
- return new require_prebuffer.RowMapStore({
3953
+ return new require_retire_root_keys.RowMapStore({
3954
3954
  spec: RECORDING_CONFIGS_SPEC,
3955
3955
  store,
3956
3956
  logger
@@ -5241,7 +5241,7 @@ var RECORDING_EXPORTS_SPEC = {
5241
5241
  };
5242
5242
  /** Build the handle. `declare()` MUST run once at boot before any read/write. */
5243
5243
  function recordingExportsState(store, logger) {
5244
- return new require_prebuffer.RowMapStore({
5244
+ return new require_retire_root_keys.RowMapStore({
5245
5245
  spec: RECORDING_EXPORTS_SPEC,
5246
5246
  store,
5247
5247
  logger
@@ -7733,96 +7733,59 @@ function runFfmpeg(renderDeps, args, ext) {
7733
7733
  });
7734
7734
  });
7735
7735
  }
7736
- //#endregion
7737
- //#region src/recorder/addon/roots.ts
7738
- /**
7739
- * Storage-root resolution + boot hydrate for the recorder addon.
7740
- *
7741
- * Recordings live in the managed `recordings` / `recordingsLow` storage
7742
- * locations (operator-configurable, multi-disk-ready), resolved through the
7743
- * `storage` cap. The v2 `RecordingIndex` is hydrated by walking each location
7744
- * root's `<deviceId>/` subtree on disk — segment paths encode start/dur/bytes,
7745
- * so the in-memory index can never drift from disk. (We walk the filesystem
7746
- * directly instead of `storage.list` because that cap lists only a directory's
7747
- * IMMEDIATE children, not the `<deviceId>/<profile>/Y/M/D/H/*.m4s` tree — using
7748
- * it dropped the entire index to ~empty on every restart/rescan; the recorder
7749
- * already does local fs I/O for stat/move on these same roots.)
7750
- */
7751
- /** The two recordings location types the recorder addon writes/reads. */
7752
- var PLACEMENT_LOCATION_TYPES = ["recordings", "recordingsLow"];
7753
- var RECORDING_LOCATION_TYPES = new Set(PLACEMENT_LOCATION_TYPES);
7736
+ var RECORDING_LOCATION_TYPES = new Set(["recordings", "recordingsLow"]);
7754
7737
  /**
7755
7738
  * The recordings CLASS a profile belongs to. The one place this rule is
7756
- * written: `resolvePlacement`, the planner's demand type and the repoint check
7757
- * must agree on it, and three copies of `profile === 'low' ? … : …` is how they
7758
- * stop agreeing.
7739
+ * written: `resolvePlacement`, the planner's demand type and the placement
7740
+ * census must agree on it, and three copies of `profile === 'low' ? … : …` is
7741
+ * how they stop agreeing.
7759
7742
  */
7760
7743
  function placementTypeForProfile(profile) {
7761
7744
  return profile === "low" ? "recordingsLow" : "recordings";
7762
7745
  }
7763
- /** The profile that routes to each class — the probe `authorityLocations` asks
7764
- * `resolvePlacement` with, so the answer is derived from the same function the
7765
- * unassigned path uses rather than from a second reading of `isDefault`. */
7766
- var CLASS_PROBE_PROFILE = {
7767
- recordings: "high",
7768
- recordingsLow: "low"
7769
- };
7770
- /**
7771
- * Where each recordings class writes when NOTHING has planned it — the location
7772
- * `resolvePlacement` picks, which is the type default unless the default is
7773
- * gone, read-only, or absent from the pool.
7774
- *
7775
- * This is the AUTHORITY a stored assignment is measured against (D298). A
7776
- * repoint (`storage.setDefaultLocations`, an operator making another disk the
7777
- * default, a default deleted) changes exactly this map, and a stored assignment
7778
- * that survives that change is a plan that outlived the pool it was planned
7779
- * for.
7780
- *
7781
- * A class with nothing writable is ABSENT from the map, not guessed at: `place`
7782
- * already throws loudly on that path, and an authority nobody can compute must
7783
- * invalidate nothing.
7784
- */
7785
- function authorityLocations(pool) {
7786
- const out = /* @__PURE__ */ new Map();
7787
- for (const type of PLACEMENT_LOCATION_TYPES) try {
7788
- out.set(type, resolvePlacement(pool, CLASS_PROBE_PROFILE[type]).id);
7789
- } catch {}
7790
- return out;
7791
- }
7792
7746
  /**
7793
7747
  * Narrow a resolved location list to the ones placement may write to: the
7794
- * operator-enabled ones. A location the operator has not opted in
7795
- * (`enabled === false`) is still READ, age-swept and drained — it is simply
7748
+ * enabled ones — ALL of them, together. A location the operator has not opted
7749
+ * in (`enabled === false`) is still READ, age-swept and drained — it is simply
7796
7750
  * never a write target, exactly like a read-only one.
7797
7751
  *
7798
- * When no enabled location is writable the type DEFAULTS serve instead, loudly:
7799
- * the default of a type is guaranteed enabled by the orchestrator, so this can
7800
- * only happen through a hand-edited store — and a recorder that writes nowhere
7801
- * is worse than one that writes to the default and complains.
7752
+ * When no enabled location is writable EVERY location serves instead, loudly.
7753
+ * The orchestrator refuses to leave a recordings class without an enabled
7754
+ * location, so this is reachable only through a hand-edited store — and a
7755
+ * recorder that writes nowhere is worse than one that writes under protest.
7802
7756
  */
7803
7757
  function placementPool(locations) {
7804
7758
  const enabled = locations.filter((l) => l.enabled !== false);
7805
7759
  if (enabled.some((l) => !l.readOnly)) return {
7806
7760
  locations: enabled,
7807
- fellBackToDefault: false
7808
- };
7809
- const defaults = locations.filter((l) => l.isDefault === true);
7810
- if (defaults.some((l) => !l.readOnly)) return {
7811
- locations: defaults,
7812
- fellBackToDefault: true
7761
+ fellBackToDisabled: false
7813
7762
  };
7814
7763
  return {
7815
7764
  locations,
7816
- fellBackToDefault: true
7765
+ fellBackToDisabled: true
7817
7766
  };
7818
7767
  }
7819
7768
  /**
7820
- * Route a recording profile to its storage location by the location's declared
7821
- * `type`: the `low` profile prefers the `recordingsLow` location, every other
7822
- * profile the `recordings` location. READ-ONLY locations are never write
7823
- * targets (the drain model): when the preferred type has only read-only
7824
- * instances, any writable recordings-type location serves. Throws when NO
7825
- * writable location exists — recording cannot start, and that must be loud.
7769
+ * Route a recording profile to a storage location, for a (camera, profile) the
7770
+ * PLANNER has not placed yet — the first attach of a new camera, before the
7771
+ * 30 s pass runs.
7772
+ *
7773
+ * The rule, in order:
7774
+ * 1. the location's declared `type` — `low` prefers `recordingsLow`, every
7775
+ * other profile prefers `recordings`; when the preferred type has only
7776
+ * read-only instances, any writable recordings-type location serves;
7777
+ * 2. among those, the one with the most MEASURED headroom
7778
+ * (`min(free − minFree floor, maxUsedGb − used)`) — the same signal the
7779
+ * planner balances on, so the bootstrap pick and the plan agree instead of
7780
+ * the plan having to undo the pick at the next tick;
7781
+ * 3. ties, and a pool where nothing was measured, fall to the
7782
+ * lexicographically first id — deterministic, never "whichever came back
7783
+ * first from the cap".
7784
+ *
7785
+ * There is no preferred instance any more (D383): every enabled location of a
7786
+ * class is a write target, and which one a camera gets is a capacity question,
7787
+ * not a flag. Throws when NO writable location exists — recording cannot start,
7788
+ * and that must be loud.
7826
7789
  */
7827
7790
  function resolvePlacement(locations, profile) {
7828
7791
  if (locations.length === 0) throw new Error("recorder controller: no recordings storage locations resolved");
@@ -7830,7 +7793,15 @@ function resolvePlacement(locations, profile) {
7830
7793
  if (writable.length === 0) throw new Error("recorder controller: every recordings location is read-only — nowhere to write");
7831
7794
  const preferType = profile === "low" ? "recordingsLow" : "recordings";
7832
7795
  const preferred = writable.filter((l) => l.type === preferType);
7833
- return preferred.find((l) => l.isDefault) ?? preferred[0] ?? writable.find((l) => l.isDefault) ?? writable[0];
7796
+ return emptiestOf(preferred.length > 0 ? preferred : writable);
7797
+ }
7798
+ /** The candidate with the most measured headroom; ties (and an unmeasured
7799
+ * pool, where every headroom is absent) by lexicographic id. */
7800
+ function emptiestOf(candidates) {
7801
+ return candidates.toSorted((a, b) => {
7802
+ const byHeadroom = (b.headroomBytes ?? 0) - (a.headroomBytes ?? 0);
7803
+ return byHeadroom !== 0 ? byHeadroom : a.id.localeCompare(b.id);
7804
+ })[0];
7834
7805
  }
7835
7806
  /**
7836
7807
  * Attribute a discovered segment on an aliased physical root by its profile,
@@ -7880,7 +7851,6 @@ async function resolveRecordingsLocations(api, logger) {
7880
7851
  relativePath: ""
7881
7852
  }),
7882
7853
  readOnly: l.config["readOnly"] === true,
7883
- isDefault: l.isDefault,
7884
7854
  ...l.enabled === false ? { enabled: false } : {},
7885
7855
  ...numericConfig(l.config, "minFreePercent", "minFreePercent"),
7886
7856
  ...numericConfig(l.config, "maxUsedGb", "maxUsedGb")
@@ -10438,47 +10408,17 @@ Object.freeze({
10438
10408
  },
10439
10409
  "setDeviceConfig": { "config": { "__nested__": { "bands": [] } } }
10440
10410
  },
10441
- "storage": { "upsertLocation": {
10442
- "isDefault": false,
10443
- "isSystem": false
10444
- } },
10411
+ "storage": { "upsertLocation": { "isSystem": false } },
10445
10412
  "storage-provider": {
10446
- "beginDownload": { "location": { "__nested__": {
10447
- "isDefault": false,
10448
- "isSystem": false
10449
- } } },
10450
- "beginUpload": { "location": { "__nested__": {
10451
- "isDefault": false,
10452
- "isSystem": false
10453
- } } },
10454
- "delete": { "location": { "__nested__": {
10455
- "isDefault": false,
10456
- "isSystem": false
10457
- } } },
10458
- "exists": { "location": { "__nested__": {
10459
- "isDefault": false,
10460
- "isSystem": false
10461
- } } },
10462
- "getAvailableSpace": { "location": { "__nested__": {
10463
- "isDefault": false,
10464
- "isSystem": false
10465
- } } },
10466
- "list": { "location": { "__nested__": {
10467
- "isDefault": false,
10468
- "isSystem": false
10469
- } } },
10470
- "read": { "location": { "__nested__": {
10471
- "isDefault": false,
10472
- "isSystem": false
10473
- } } },
10474
- "resolve": { "location": { "__nested__": {
10475
- "isDefault": false,
10476
- "isSystem": false
10477
- } } },
10478
- "write": { "location": { "__nested__": {
10479
- "isDefault": false,
10480
- "isSystem": false
10481
- } } }
10413
+ "beginDownload": { "location": { "__nested__": { "isSystem": false } } },
10414
+ "beginUpload": { "location": { "__nested__": { "isSystem": false } } },
10415
+ "delete": { "location": { "__nested__": { "isSystem": false } } },
10416
+ "exists": { "location": { "__nested__": { "isSystem": false } } },
10417
+ "getAvailableSpace": { "location": { "__nested__": { "isSystem": false } } },
10418
+ "list": { "location": { "__nested__": { "isSystem": false } } },
10419
+ "read": { "location": { "__nested__": { "isSystem": false } } },
10420
+ "resolve": { "location": { "__nested__": { "isSystem": false } } },
10421
+ "write": { "location": { "__nested__": { "isSystem": false } } }
10482
10422
  },
10483
10423
  "stream-broker": {
10484
10424
  "produceEventMedia": {
@@ -11084,7 +11024,7 @@ var RECORDING_OPS_LOG_SPEC = {
11084
11024
  };
11085
11025
  /** Build the handle. `declare()` MUST run once at boot before any read/write. */
11086
11026
  function recordingOpsLogState(store, logger) {
11087
- return new require_prebuffer.RowMapStore({
11027
+ return new require_retire_root_keys.RowMapStore({
11088
11028
  spec: RECORDING_OPS_LOG_SPEC,
11089
11029
  store,
11090
11030
  logger
@@ -11496,19 +11436,16 @@ var RecordingPlacementBlobSchema = require_dist.object({
11496
11436
  /** locationId → the volume id last verified at that location's root. */
11497
11437
  volumeIds: require_dist.record(require_dist.string(), require_dist.string()).default({}),
11498
11438
  /**
11499
- * Recordings class (`recordings` / `recordingsLow`) → the location
11500
- * `resolvePlacement` chose for it when these assignments were last planned.
11501
- *
11502
- * ABSENT means "nobody has recorded an authority yet", and that is never read
11503
- * as a repoint: shipping this field must not invalidate assignments that were
11504
- * made under an authority nothing wrote down. The first pass stamps it and
11505
- * invalidates nothing; every later change of it is a repoint.
11439
+ * DEAD KEY (D383) — the class authority a pass planned against, back when a
11440
+ * class had a default distinct from its pool. Declared so an existing blob
11441
+ * round-trips unchanged and so its removal is a decision on the record rather
11442
+ * than a silent strip. Never read, never written.
11506
11443
  */
11507
11444
  defaultLocations: require_dist.record(require_dist.string(), require_dist.string()).default({}),
11508
11445
  /** When the blob was last written (operator-facing debugging only). */
11509
11446
  updatedAt: require_dist.number().optional()
11510
11447
  });
11511
- /** Cold-start value: nothing assigned, nothing claimed, no authority recorded. */
11448
+ /** Cold-start value: nothing assigned, nothing claimed. */
11512
11449
  var EMPTY_PLACEMENT = {
11513
11450
  assignments: {},
11514
11451
  pins: {},
@@ -11557,31 +11494,27 @@ async function replaceAssignments(state, base, assignments) {
11557
11494
  });
11558
11495
  }
11559
11496
  /**
11560
- * The assignment record a pass leaves behind: the stored one, with the repoint
11561
- * applied, then the plan merged over it. **Nothing is ever removed** — a pass
11562
- * that plans a subset cannot delete a key it did not mention.
11497
+ * The assignment record a pass leaves behind: the stored one with the plan
11498
+ * merged over it. **Nothing is ever removed** — a pass that plans a subset
11499
+ * cannot delete a key it did not mention.
11563
11500
  *
11564
- * PURE, and shared by the write and the divergence report — a report computed
11501
+ * PURE, and shared by the write and the placement census — a report computed
11565
11502
  * from a second reading of the same rule is a report that can describe a record
11566
11503
  * the write did not produce.
11567
11504
  */
11568
11505
  function applyPlacementPass(base, pass) {
11569
11506
  const next = new Map(Object.entries(base));
11570
- for (const [key, locationId] of pass.repointed) next.set(key, locationId);
11571
11507
  for (const [key, locationId] of pass.planned) next.set(key, locationId);
11572
11508
  return next;
11573
11509
  }
11574
- /** Persist one work-scoped pass: apply the repoint, merge its plan, and stamp
11575
- * the authority the plan was resolved against. One write. */
11510
+ /** Persist one work-scoped pass: merge its plan over the stored record. One
11511
+ * write. */
11576
11512
  async function writePlacementPass(state, base, pass) {
11577
11513
  const assignments = {};
11578
11514
  for (const [key, locationId] of applyPlacementPass(base.assignments, pass)) assignments[key] = locationId;
11579
- const defaultLocations = {};
11580
- for (const [type, locationId] of pass.defaultLocations) defaultLocations[type] = locationId;
11581
11515
  await state.set({
11582
11516
  ...base,
11583
11517
  assignments,
11584
- defaultLocations,
11585
11518
  updatedAt: Date.now()
11586
11519
  });
11587
11520
  }
@@ -11830,14 +11763,6 @@ var NO_PLAN = {
11830
11763
  assignments: /* @__PURE__ */ new Map(),
11831
11764
  changes: []
11832
11765
  };
11833
- /** Is the authority already stamped, exactly? A pass that would change the
11834
- * stamp always writes, even when it moved nothing: an unstamped authority is
11835
- * an undetectable repoint. */
11836
- function sameStamp(stamped, authority) {
11837
- if (stamped.size !== authority.size) return false;
11838
- for (const [type, locationId] of authority) if (stamped.get(type) !== locationId) return false;
11839
- return true;
11840
- }
11841
11766
  /** Operator pins win over the planner for high/mid of the pinned camera. */
11842
11767
  function overlayPins(planned, pins) {
11843
11768
  const next = new Map(planned);
@@ -11865,14 +11790,11 @@ var PlacementService = class {
11865
11790
  /** The fair tie-break the PURE planner defers to (D37). Advisory state, and
11866
11791
  * consulted only for a tie the capacity signal cannot separate. */
11867
11792
  rotation = new PlacementRotation();
11868
- /** The divergent set last announced, as a stable signature. `null` ⇒ nothing
11869
- * announced yet, which is why an all-aligned fleet says nothing at boot. */
11870
- divergenceSignature = null;
11871
- /** When the standing divergence was last announced. */
11872
- divergenceReportedAt = null;
11873
- /** Repoint episodes already announced (`type:from->to`). Cleared the moment a
11874
- * pass sees no repoint at all — the next one is a NEW episode. */
11875
- repointAnnounced = /* @__PURE__ */ new Set();
11793
+ /** The census last announced, as a stable signature. `null` ⇒ nothing
11794
+ * announced yet. */
11795
+ censusSignature = null;
11796
+ /** When the census was last announced. */
11797
+ censusReportedAt = null;
11876
11798
  constructor(deps) {
11877
11799
  this.deps = deps;
11878
11800
  }
@@ -11907,15 +11829,14 @@ var PlacementService = class {
11907
11829
  async place(deviceId, profile) {
11908
11830
  const pool = this.pool();
11909
11831
  this.reprobeAfterBackoff(pool);
11910
- const authority = authorityLocations(pool);
11911
- const resolved = await this.assignedLocation(deviceId, profile, pool, authority) ?? resolvePlacement(pool, profile);
11832
+ const resolved = await this.assignedLocation(deviceId, profile, pool) ?? resolvePlacement(await this.withHeadroom(pool), profile);
11912
11833
  const usable = await this.ensureUsable(resolved, pool, profile, deviceId);
11913
11834
  if (this.everyLocationUnusable(pool)) {
11914
11835
  this.reportEveryLocationUnusable(deviceId, profile, resolved, pool);
11915
11836
  return resolved;
11916
11837
  }
11917
11838
  this.allUnusableReported.delete(deviceId);
11918
- await this.recordDecision(deviceId, profile, usable, resolved.id === usable.id, authority.get(placementTypeForProfile(profile)) ?? null);
11839
+ await this.recordDecision(deviceId, profile, usable, resolved.id === usable.id);
11919
11840
  return usable;
11920
11841
  }
11921
11842
  /**
@@ -11999,35 +11920,30 @@ var PlacementService = class {
11999
11920
  * to it, so an unknown flag is the only thing that counts as opted in.
12000
11921
  */
12001
11922
  pool() {
12002
- const { locations, fellBackToDefault } = placementPool(this.deps.locations());
12003
- if (fellBackToDefault) this.deps.logger.warn("recorder placement: NO recordings location is enabled for writing — falling back to the type default. Enable a location in Storage, or recordings will keep landing on the default.", { meta: { locationIds: locations.map((l) => l.id) } });
11923
+ const { locations, fellBackToDisabled } = placementPool(this.deps.locations());
11924
+ if (fellBackToDisabled) this.deps.logger.warn("recorder placement: NO recordings location is enabled for writing — writing to a DISABLED location under protest. Enable one in Storage.", { meta: { locationIds: locations.map((l) => l.id) } });
12004
11925
  return locations;
12005
11926
  }
12006
11927
  /**
12007
11928
  * The stored assignment for one (camera, profile), when it is still valid.
12008
11929
  *
12009
- * Three things invalidate it, and the third is the one this used to miss:
12010
- *
12011
- * 1. the location is absent from the current pool (deleted, opted out);
12012
- * 2. the location is read-only (an operator drain);
12013
- * 3. **the class was REPOINTED under it** — the authority stamped in the
12014
- * blob is not the authority now, and the assignment names neither.
11930
+ * Two things invalidate it, and under the operator's model (D383) there is
11931
+ * nothing else that can:
12015
11932
  *
12016
- * (1) and (2) both mean "this location is not a write target any more", so
12017
- * they only ever caught a repoint that took the old location out of service.
12018
- * A repoint of a class the operator is MIGRATING does the opposite: the
12019
- * source must stay enabled and writable, because it holds the history. So the
12020
- * assignment survived every attach, indefinitely, and `resolvePlacement`
12021
- * returning the new default changed nothing — it is the fallback, not the
12022
- * authority. On 2026-08-29 that left two of thirteen cameras writing to the
12023
- * disk the fleet was migrating off, silently (D298).
11933
+ * 1. the location is absent from the current POOL — deleted, or the
11934
+ * operator disabled it. The pool is the single write authority, so an
11935
+ * assignment naming a location outside it is not a divergence to report,
11936
+ * it is unrepresentable;
11937
+ * 2. the location is read-only (an operator drain).
12024
11938
  *
12025
- * The invalidation is a TRANSITION, not a policy: it fires only while the
12026
- * stamped authority disagrees with the live one, which the next placement pass
12027
- * ends by stamping. After that the plan is the authority again and a
12028
- * deliberate spread is honoured — and reported (`reportDivergence`).
11939
+ * There used to be a third — "the class was REPOINTED under it", measured
11940
+ * against the class default stamped in the blob (D301). It existed only
11941
+ * because the default was a SECOND write authority layered over the pool and
11942
+ * the two could disagree. With the default deleted the disagreement cannot be
11943
+ * expressed: a location leaving the pool already invalidates every assignment
11944
+ * naming it, and the planner then moves those keys with `reason: 'gone'`.
12029
11945
  */
12030
- async assignedLocation(deviceId, profile, pool, authority) {
11946
+ async assignedLocation(deviceId, profile, pool) {
12031
11947
  let blob;
12032
11948
  try {
12033
11949
  blob = await readPlacementState(this.deps.state);
@@ -12049,23 +11965,28 @@ var PlacementService = class {
12049
11965
  }
12050
11966
  if (assignedId === void 0) return null;
12051
11967
  const location = pool.find((l) => l.id === assignedId && !l.readOnly) ?? null;
12052
- if (location === null) return null;
12053
- const type = placementTypeForProfile(profile);
12054
- const target = authority.get(type);
12055
- const stamped = blob.defaultLocations[type];
12056
- if (target === void 0 || stamped === void 0) return location;
12057
- if (stamped === target || location.id === target) return location;
12058
- this.deps.logger.warn("recorder placement: stored assignment INVALIDATED — this recordings class was repointed and the assignment still names the location it was repointed away from", {
11968
+ if (location === null) this.deps.logger.info("recorder placement: stored assignment INVALIDATED — its location is not a write target any more (disabled, read-only, or gone). Resolving fresh from the pool.", {
12059
11969
  tags: { deviceId },
12060
11970
  meta: {
12061
11971
  profile,
12062
- assignedLocationId: location.id,
12063
- repointedFrom: stamped,
12064
- repointedTo: target,
12065
- locationType: type
11972
+ assignedLocationId: assignedId,
11973
+ poolLocationIds: pool.filter((l) => !l.readOnly).map((l) => l.id)
12066
11974
  }
12067
11975
  });
12068
- return null;
11976
+ return location;
11977
+ }
11978
+ /** The pool, with each member's measured headroom attached. One capacity read
11979
+ * per location; used only on the bootstrap path, never per segment. */
11980
+ async withHeadroom(pool) {
11981
+ const candidates = await this.candidatesFor(pool);
11982
+ const byId = new Map(candidates.map((c) => [c.locationId, c.headroomBytes]));
11983
+ return pool.map((location) => {
11984
+ const headroomBytes = byId.get(location.id);
11985
+ return headroomBytes === void 0 ? location : {
11986
+ ...location,
11987
+ headroomBytes
11988
+ };
11989
+ });
12069
11990
  }
12070
11991
  /**
12071
11992
  * Re-plan the (camera, profile) pairs in `work` over the current locations.
@@ -12087,7 +12008,6 @@ var PlacementService = class {
12087
12008
  */
12088
12009
  async recompute(work) {
12089
12010
  const pool = this.pool();
12090
- const authority = authorityLocations(pool);
12091
12011
  const candidates = await this.candidatesFor(pool);
12092
12012
  const demands = work.map((item) => ({
12093
12013
  key: assignmentKey(item.deviceId, item.profile),
@@ -12104,17 +12024,7 @@ var PlacementService = class {
12104
12024
  } });
12105
12025
  return NO_PLAN;
12106
12026
  }
12107
- const stamped = new Map(Object.entries(blob.defaultLocations));
12108
- const repoints = this.detectRepoints(blob.assignments, stamped, authority, blob.pins);
12109
- if (repoints.length === 0) this.repointAnnounced.clear();
12110
- for (const repoint of repoints) this.announceRepoint(repoint);
12111
- const repointed = /* @__PURE__ */ new Map();
12112
- for (const repoint of repoints) for (const key of repoint.keys) repointed.set(key, repoint.to);
12113
- const current = applyPlacementPass(blob.assignments, {
12114
- repointed,
12115
- planned: /* @__PURE__ */ new Map(),
12116
- defaultLocations: authority
12117
- });
12027
+ const current = new Map(Object.entries(blob.assignments));
12118
12028
  const evacuate = new Set(candidates.filter((c) => c.headroomBytes <= 0).map((c) => c.locationId));
12119
12029
  for (const location of pool) if (location.readOnly) evacuate.add(location.id);
12120
12030
  const plan = planPlacement({
@@ -12124,13 +12034,9 @@ var PlacementService = class {
12124
12034
  evacuate,
12125
12035
  breakTie: (tied) => this.rotation.next(tied)
12126
12036
  });
12127
- const pass = {
12128
- repointed,
12129
- planned: overlayPins(plan.assignments, blob.pins),
12130
- defaultLocations: authority
12131
- };
12132
- this.reportDivergence(applyPlacementPass(blob.assignments, pass), authority);
12133
- if (plan.changes.length === 0 && repointed.size === 0 && sameStamp(stamped, authority)) return plan;
12037
+ const pass = { planned: overlayPins(plan.assignments, blob.pins) };
12038
+ this.reportCensus(applyPlacementPass(blob.assignments, pass), pool);
12039
+ if (plan.changes.length === 0) return plan;
12134
12040
  for (const change of plan.changes) {
12135
12041
  const deviceId = Number(change.key.split(":")[0]);
12136
12042
  this.deps.logger.info("recorder placement: plan moves a profile to another location", {
@@ -12154,129 +12060,60 @@ var PlacementService = class {
12154
12060
  return plan;
12155
12061
  }
12156
12062
  /**
12157
- * Which recordings classes were REPOINTED under the stored plan, and which of
12158
- * its keys that invalidates.
12159
- *
12160
- * PURE. A class is repointed when the blob stamped an authority for it and
12161
- * the authority now is a different location. A class with NO stamp is not a
12162
- * repoint — it is the first pass after this shipped, and inferring a repoint
12163
- * from an absent stamp would throw away every deliberate assignment on the
12164
- * first tick after a deploy.
12165
- */
12166
- detectRepoints(stored, stamped, authority, pins) {
12167
- const out = [];
12168
- for (const [type, to] of authority) {
12169
- const from = stamped.get(type);
12170
- if (from === void 0 || from === to) continue;
12171
- const keys = [];
12172
- for (const [key, locationId] of Object.entries(stored)) {
12173
- const [devicePart, profilePart] = key.split(":");
12174
- if (placementTypeForProfile(profilePart ?? "") !== type) continue;
12175
- if (locationId === to) continue;
12176
- if (type === "recordings" && devicePart !== void 0 && pins[devicePart] !== void 0) continue;
12177
- keys.push(key);
12178
- }
12179
- out.push({
12180
- type,
12181
- from,
12182
- to,
12183
- keys
12184
- });
12185
- }
12186
- return out;
12187
- }
12188
- /**
12189
- * One line PER CAMERA (a repoint is asked per-camera: "did 3839 move?"), plus
12190
- * one audit row per class. A repoint is a rare operator event, so the volume
12191
- * is one burst of fleet-size, not a stream.
12063
+ * WHERE THE FLEET WRITES — one line per (class, location), always with its
12064
+ * denominator (D277).
12192
12065
  *
12193
- * Announced ONCE per episode: the detection is a comparison against the
12194
- * stamp, so a write that fails leaves the stamp behind and the next tick sees
12195
- * the same repoint again. Without the latch, a settings door that accepts
12196
- * reads and refuses writes would repeat the whole burst every 30 s.
12197
- */
12198
- announceRepoint(repoint) {
12199
- const episode = `${repoint.type}:${repoint.from}->${repoint.to}`;
12200
- if (this.repointAnnounced.has(episode)) return;
12201
- this.repointAnnounced.add(episode);
12202
- for (const key of repoint.keys) {
12203
- const deviceId = Number(key.split(":")[0]);
12204
- this.deps.logger.warn("recorder placement: recordings class REPOINTED — moving this assignment onto the class`s new authority location", {
12205
- ...Number.isFinite(deviceId) ? { tags: { deviceId } } : {},
12206
- meta: {
12207
- key,
12208
- locationType: repoint.type,
12209
- repointedFrom: repoint.from,
12210
- repointedTo: repoint.to
12211
- }
12212
- });
12213
- }
12214
- this.deps.opsLog.append({
12215
- op: "relocate",
12216
- reason: "operator",
12217
- deviceId: null,
12218
- itemsAffected: repoint.keys.length,
12219
- bytesReclaimed: 0,
12220
- actor: "system",
12221
- detail: `placement: ${repoint.type} repointed ${repoint.from} → ${repoint.to}; ${repoint.keys.length.toString()} assignment(s) moved (${repoint.keys.join(", ")})`
12222
- });
12223
- }
12224
- /**
12225
- * Say which (camera, profile) the plan writes somewhere other than its class
12226
- * authority — per camera, WITH its denominator (D277).
12227
- *
12228
- * This is not an error path. The planner spreads a class across its pool on
12229
- * purpose, and a location with headroom is a legitimate target. What was
12230
- * wrong is that a deliberate spread and a leftover from a repoint nobody
12231
- * noticed produced the *same* absence of output: `recordDecision` audits only
12232
- * a CHANGE, so a camera that keeps using its old assignment writes no row and
12233
- * no line, forever. An operator read "migration done" while two cameras were
12234
- * still filling the disk they were migrating off.
12066
+ * This replaced the class-authority divergence report (D301) when the default
12067
+ * was deleted: with every enabled location of a class a legitimate target,
12068
+ * "this camera does not write to THE location" is not a fact any more, while
12069
+ * "recordings: 9 of 13 on recordings:default, 4 on recordings:hgst" is
12070
+ * exactly the question an operator asks after enabling a second disk. A pool
12071
+ * member holding ZERO assignments is listed too — an enabled disk nothing
12072
+ * lands on is the failure this whole model exists to make visible.
12235
12073
  *
12236
- * Fires on a CHANGE of the divergent set, and otherwise at most once per
12237
- * {@link PLACEMENT_DIVERGENCE_REPORT_MS}. An all-aligned fleet says nothing —
12238
- * until it stops being all-aligned, and again when it recovers.
12074
+ * Fires on a CHANGE of the distribution, and otherwise at most once per
12075
+ * {@link PLACEMENT_CENSUS_REPORT_MS}.
12239
12076
  */
12240
- reportDivergence(assignments, authority) {
12241
- const divergent = [];
12077
+ reportCensus(assignments, pool) {
12078
+ const counts = /* @__PURE__ */ new Map();
12079
+ const perClass = /* @__PURE__ */ new Map();
12080
+ for (const location of pool) if (!location.readOnly) counts.set(location.id, 0);
12242
12081
  for (const [key, locationId] of assignments) {
12243
- const profile = key.split(":")[1] ?? "";
12244
- const authorityLocationId = authority.get(placementTypeForProfile(profile));
12245
- if (authorityLocationId === void 0 || authorityLocationId === locationId) continue;
12246
- divergent.push({
12247
- key,
12248
- deviceId: Number(key.split(":")[0]),
12249
- profile,
12250
- locationId,
12251
- authorityLocationId
12252
- });
12082
+ counts.set(locationId, (counts.get(locationId) ?? 0) + 1);
12083
+ const type = placementTypeForProfile(key.split(":")[1] ?? "");
12084
+ perClass.set(type, (perClass.get(type) ?? 0) + 1);
12253
12085
  }
12254
- const signature = divergent.map((d) => `${d.key}=${d.locationId}`).toSorted().join(",");
12255
- const previous = this.divergenceSignature;
12086
+ const typeOf = new Map(pool.map((location) => [location.id, location.type]));
12087
+ const lines = [...counts.entries()].toSorted(([a], [b]) => a.localeCompare(b)).map(([locationId, count]) => {
12088
+ const type = typeOf.get(locationId) ?? locationId.split(":")[0] ?? locationId;
12089
+ return {
12090
+ locationId,
12091
+ type,
12092
+ assignments: count,
12093
+ ofClass: perClass.get(type) ?? 0
12094
+ };
12095
+ });
12096
+ const signature = lines.map((line) => `${line.locationId}=${String(line.assignments)}`).join(",");
12256
12097
  const now = this.now;
12257
- const due = this.divergenceReportedAt === null || now - this.divergenceReportedAt >= 36e5;
12258
- if (signature === previous && !due) return;
12259
- this.divergenceSignature = signature;
12260
- this.divergenceReportedAt = now;
12261
- if (divergent.length === 0) {
12262
- if (previous !== null && previous !== "") this.deps.logger.info("recorder placement: every assignment now writes to its class authority location", { meta: { assignments: assignments.size } });
12263
- return;
12098
+ const due = this.censusReportedAt === null || now - this.censusReportedAt >= 36e5;
12099
+ if (signature === this.censusSignature && !due) return;
12100
+ this.censusSignature = signature;
12101
+ this.censusReportedAt = now;
12102
+ if (lines.length === 0) return;
12103
+ this.deps.logger.info("recorder placement: census — which locations the fleet writes to", { meta: { locations: lines.map((line) => ({
12104
+ locationId: line.locationId,
12105
+ type: line.type,
12106
+ assignments: line.assignments,
12107
+ ofClass: line.ofClass
12108
+ })) } });
12109
+ for (const line of lines) {
12110
+ if (line.assignments > 0) continue;
12111
+ this.deps.logger.warn("recorder placement: this location is ENABLED but holds no assignment — nothing writes to it", { meta: {
12112
+ locationId: line.locationId,
12113
+ type: line.type,
12114
+ ofClass: line.ofClass
12115
+ } });
12264
12116
  }
12265
- this.deps.logger.warn("recorder placement: assignments write somewhere other than their class authority location", { meta: {
12266
- divergent: divergent.length,
12267
- assignments: assignments.size,
12268
- authority: Object.fromEntries(authority)
12269
- } });
12270
- for (const entry of divergent) this.deps.logger.warn("recorder placement: this profile does NOT write to its class authority location", {
12271
- ...Number.isFinite(entry.deviceId) ? { tags: { deviceId: entry.deviceId } } : {},
12272
- meta: {
12273
- profile: entry.profile,
12274
- locationId: entry.locationId,
12275
- authorityLocationId: entry.authorityLocationId,
12276
- divergent: divergent.length,
12277
- assignments: assignments.size
12278
- }
12279
- });
12280
12117
  }
12281
12118
  /** Bound one addon-store round trip. See {@link PLACEMENT_STORE_TIMEOUT_MS}. */
12282
12119
  bounded(promise, label) {
@@ -12457,13 +12294,13 @@ var PlacementService = class {
12457
12294
  * Persist the assignment and audit it — but only when it actually changed.
12458
12295
  * The steady state (every attach after the first) writes nothing.
12459
12296
  *
12460
- * The per-attach line carries `authorityLocationId` and `divergent` so a
12461
- * single camera's line answers "is 3839 writing where the class default
12462
- * says?" on its own. Without it, a camera pinned to a location the class was
12463
- * repointed away from logged `reason: 'resolved'` — byte-identical to a
12464
- * camera correctly on the current default.
12297
+ * The per-attach line carries the camera's location and the one it replaced,
12298
+ * tagged with `deviceId`, so "where does 3839 write, and did it move?" is
12299
+ * answerable per camera. Which locations the FLEET is spread over is the
12300
+ * census's job (D383) — there is no class authority for a single line to be
12301
+ * measured against any more.
12465
12302
  */
12466
- async recordDecision(deviceId, profile, location, verbatim, authorityLocationId) {
12303
+ async recordDecision(deviceId, profile, location, verbatim) {
12467
12304
  const key = assignmentKey(deviceId, profile);
12468
12305
  let previous;
12469
12306
  try {
@@ -12484,8 +12321,6 @@ var PlacementService = class {
12484
12321
  locationId: location.id,
12485
12322
  root: location.root,
12486
12323
  previousLocationId: previous ?? null,
12487
- authorityLocationId,
12488
- divergent: authorityLocationId !== null && authorityLocationId !== location.id,
12489
12324
  reason: verbatim ? "resolved" : "failover"
12490
12325
  }
12491
12326
  });
@@ -12613,7 +12448,7 @@ function effectiveBands(config) {
12613
12448
  */
12614
12449
  function resolveBandBufferMs(band) {
12615
12450
  return {
12616
- preMs: require_prebuffer.PREBUFFER_MAX_MS,
12451
+ preMs: require_restream_intent.PREBUFFER_MAX_MS,
12617
12452
  postMs: (band.postBufferSec ?? require_dist.DEFAULT_EVENTS_BAND_BUFFER_SEC.postBufferSec) * 1e3
12618
12453
  };
12619
12454
  }
@@ -15383,7 +15218,7 @@ var RecordingController = class {
15383
15218
  tag: `recorder:${deviceId}/${profile}`
15384
15219
  }, require_dist.nodePin(this.deps.ownerNodeId)), ATTACH_RPC_TIMEOUT_MS, "getStreamWithCodec"));
15385
15220
  const streamAcquiredMs = this.now();
15386
- const rtspUrl = require_restream_intent.withRecordingIntent(source.url);
15221
+ const rtspUrl = require_restream_intent.withRecordingIntent(source.url, require_restream_intent.RECORDING_PRE_ROLL_REQUEST_MS);
15387
15222
  this.attachSeq++;
15388
15223
  const consumerTag = `recorder/${deviceId}/${profile}/${this.attachSeq}`;
15389
15224
  if (rtspUrl === source.url) this.deps.logger.info("recorder: dial carries no recording intent — no pre-roll", {
@@ -18410,7 +18245,7 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
18410
18245
  */
18411
18246
  async purgeFlattenedSettingsKeys() {
18412
18247
  try {
18413
- await require_prebuffer.retireRootKeys({
18248
+ await require_retire_root_keys.retireRootKeys({
18414
18249
  store: this.ctx.api.settingsStore,
18415
18250
  addonId: RECORDER_ADDON_ID,
18416
18251
  logger: this.ctx.logger,