@camstack/addon-matter-broker 0.2.37 → 0.2.38

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
@@ -13547,6 +13547,114 @@ method(LogEntrySchema, _void(), { kind: "mutation" }), method(object({
13547
13547
  limit: number().optional(),
13548
13548
  tags: record(string$2(), string$2()).optional()
13549
13549
  }), array(LogEntrySchema).readonly());
13550
+ /**
13551
+ * `failure-contribution` — the capability an addon reports its OWN losses
13552
+ * through, per camera, with the denominator attached. It stores nothing.
13553
+ *
13554
+ * ## The twin of `load-contribution`, and why it is a twin and not a field
13555
+ *
13556
+ * `load-contribution` answers *what did this camera COST*. This answers *what
13557
+ * did this camera LOSE*. The reporting discipline is identical and deliberately
13558
+ * copied: the contributor reports what it already knows, hub-main adds only
13559
+ * `addonId`, nothing needs global knowledge, and there is no central list for
13560
+ * somebody to forget to edit.
13561
+ *
13562
+ * They are not merged, because their invariants are opposites:
13563
+ *
13564
+ * - a `load-contribution` measurement is **absent, never zero** — a zero would
13565
+ * claim a camera cost nothing, which is a measurement nobody made;
13566
+ * - a `failure-contribution` zero is the **most valuable value on the
13567
+ * surface** — `attempts: 400, succeeded: 400` is the proof a fix landed,
13568
+ * and it is exactly what an absent entry cannot say.
13569
+ *
13570
+ * Putting a loss counter on a cost entry would also break the reconciliation
13571
+ * that gives `load-contribution` its point: contributions are subtracted from
13572
+ * `metrics.node-processes-snapshot` to find processes nobody claims. A failure
13573
+ * has no process.
13574
+ *
13575
+ * ## Why not a log line, since the counters already exist
13576
+ *
13577
+ * Several of these paths already counted themselves — `CaptureScheduler`'s
13578
+ * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
13579
+ * ends in a log line, and a log line is the thing the operator asked to stop
13580
+ * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
13581
+ * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
13582
+ * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
13583
+ * media blackout were both diagnosed. The counters stay; this is where they can
13584
+ * be READ.
13585
+ *
13586
+ * ## The rate is served with its denominator or not at all
13587
+ *
13588
+ * Every entry carries `attempts` and `succeeded`. A miss count alone is
13589
+ * unreadable: on 2026-08-28 the enrichment-crop miss count read as "35x worse
13590
+ * than yesterday" and was **flat across twelve hours** once divided by the
13591
+ * successes on the same path. A surface that publishes only the numerator
13592
+ * reproduces that mistake on every read.
13593
+ *
13594
+ * ## Shape
13595
+ *
13596
+ * Copied from `load-contribution.cap.ts` (`mode: 'collection'`,
13597
+ * `internal: true`, `mount: { kind: 'skip' }`): no tRPC route of its own and no
13598
+ * generated hooks, while `addons.listCapabilityProviders` still enumerates it
13599
+ * and the hub's `CapabilityRegistry` still holds an RPC proxy per provider — so
13600
+ * a forked runner's entries reach hub-main over transport that already exists.
13601
+ * No new UDS message, no second registry (D3). The operator reads the assembled
13602
+ * result through `system.getFailureContributions`.
13603
+ */
13604
+ var FailureReasonCountSchema = object({
13605
+ /**
13606
+ * Why the attempt did not land, in the contributor's own vocabulary —
13607
+ * `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
13608
+ * strings that already appear in this repo's logs and, where one exists, the
13609
+ * same string the per-track `previewMissReason` records (D276): a second
13610
+ * vocabulary for the same loss would make the row and the counter
13611
+ * un-joinable.
13612
+ */
13613
+ reason: string$2(),
13614
+ count: number().int().nonnegative()
13615
+ });
13616
+ var FailureContributionSchema = object({
13617
+ /**
13618
+ * The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
13619
+ * `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
13620
+ * `unit` free: the families are owned by different addons and a shared enum
13621
+ * is a central list that rots invisibly.
13622
+ */
13623
+ family: string$2(),
13624
+ /**
13625
+ * The NUMERIC device id — the same value every log line carries as
13626
+ * `tags.deviceId`. Never nullable and never absent: a contributor that
13627
+ * cannot name the camera must not emit the entry, because a fleet total
13628
+ * cannot answer the only question anybody asks of this surface.
13629
+ */
13630
+ deviceId: number().int().positive(),
13631
+ /**
13632
+ * A second dimension inside the family: the model / step id for an inference
13633
+ * timeout, so "which camera AND which model" is one read. Absent when the
13634
+ * family has a single variant.
13635
+ */
13636
+ variant: string$2().optional(),
13637
+ /**
13638
+ * Epoch ms this counter started — the INCARNATION MARKER. A consumer
13639
+ * differencing two reads must drop the interval when it changes, because the
13640
+ * counter restarted from zero in a respawned runner. Same discipline as
13641
+ * `LoadContribution.startedAtMs`.
13642
+ */
13643
+ sinceMs: number(),
13644
+ /** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
13645
+ atMs: number(),
13646
+ /**
13647
+ * THE DENOMINATOR — every attempt on this path for this camera in the
13648
+ * window. A failure count published without it is the mistake this schema
13649
+ * exists to make impossible.
13650
+ */
13651
+ attempts: number().int().nonnegative(),
13652
+ /** Attempts that landed. `attempts - succeeded` is the loss. */
13653
+ succeeded: number().int().nonnegative(),
13654
+ /** The loss, partitioned. Sums to `attempts - succeeded`. */
13655
+ reasons: array(FailureReasonCountSchema).readonly()
13656
+ });
13657
+ method(_void(), array(FailureContributionSchema).readonly());
13550
13658
  var LoadContributionSchema = object({
13551
13659
  role: _enum([
13552
13660
  "decode",
@@ -18137,6 +18245,20 @@ var TrackSchema = object({
18137
18245
  * `=== true` and render nothing otherwise — never infer "no rider".
18138
18246
  */
18139
18247
  hasRider: boolean().optional(),
18248
+ /**
18249
+ * WHY this track ended without a NATIVE best-shot tile
18250
+ * ([D276](../decisions/adr-0276-a-stand-in-tile-is-provisional-and-a-close-says-why.md)) —
18251
+ * a composed token line (`no-key-frame capture=keyframe:native-missx4`,
18252
+ * `derive-returned-null tile=standin`, …) written at close and CLEARED by
18253
+ * the late-keyFrame upgrade when a native tile lands after all. The
18254
+ * operator-facing answer to "perché manca l'immagine?" on a track whose
18255
+ * tile is a face/plate stand-in, a raster crop, or an icon.
18256
+ *
18257
+ * **Absent ≠ "missed silently"**: a row written before the column, a hub
18258
+ * that predates the field, and every track whose tile landed native all
18259
+ * omit it. Render nothing when absent.
18260
+ */
18261
+ previewMissReason: string$2().optional(),
18140
18262
  ...TrackFlagFields,
18141
18263
  ...TrackRetrainFields
18142
18264
  });
@@ -29329,6 +29451,13 @@ var LoggingSettingsPatchSchema = object({
29329
29451
  * anyone but its owner.
29330
29452
  */
29331
29453
  var ReportedLoadContributionSchema = LoadContributionSchema.extend({ addonId: string$2() });
29454
+ /**
29455
+ * One per-camera failure counter, plus WHO reported it.
29456
+ *
29457
+ * Same rule as {@link ReportedLoadContributionSchema}: `addonId` is stamped by
29458
+ * the hub as it enumerates providers, never by the contributor.
29459
+ */
29460
+ var ReportedFailureContributionSchema = FailureContributionSchema.extend({ addonId: string$2() });
29332
29461
  var GetLoggingSettingsInputSchema = object({
29333
29462
  scopeNodeId: string$2().optional(),
29334
29463
  /**
@@ -29387,7 +29516,7 @@ method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), met
29387
29516
  }), method(_void(), SiteLocationStatusSchema, {
29388
29517
  kind: "mutation",
29389
29518
  auth: "admin"
29390
- }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
29519
+ }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(_void(), array(ReportedFailureContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
29391
29520
  kind: "mutation",
29392
29521
  auth: "admin"
29393
29522
  });
@@ -33377,6 +33506,12 @@ Object.freeze({
33377
33506
  addonId: null,
33378
33507
  access: "create"
33379
33508
  },
33509
+ "failureContribution.list": {
33510
+ capName: "failure-contribution",
33511
+ capScope: "system",
33512
+ addonId: null,
33513
+ access: "view"
33514
+ },
33380
33515
  "fanControl.setDirection": {
33381
33516
  capName: "fan-control",
33382
33517
  capScope: "device",
@@ -36683,6 +36818,12 @@ Object.freeze({
36683
36818
  addonId: null,
36684
36819
  access: "create"
36685
36820
  },
36821
+ "system.getFailureContributions": {
36822
+ capName: "system",
36823
+ capScope: "system",
36824
+ addonId: null,
36825
+ access: "view"
36826
+ },
36686
36827
  "system.getLoadContributions": {
36687
36828
  capName: "system",
36688
36829
  capScope: "system",
package/dist/addon.mjs CHANGED
@@ -13545,6 +13545,114 @@ method(LogEntrySchema, _void(), { kind: "mutation" }), method(object({
13545
13545
  limit: number().optional(),
13546
13546
  tags: record(string$2(), string$2()).optional()
13547
13547
  }), array(LogEntrySchema).readonly());
13548
+ /**
13549
+ * `failure-contribution` — the capability an addon reports its OWN losses
13550
+ * through, per camera, with the denominator attached. It stores nothing.
13551
+ *
13552
+ * ## The twin of `load-contribution`, and why it is a twin and not a field
13553
+ *
13554
+ * `load-contribution` answers *what did this camera COST*. This answers *what
13555
+ * did this camera LOSE*. The reporting discipline is identical and deliberately
13556
+ * copied: the contributor reports what it already knows, hub-main adds only
13557
+ * `addonId`, nothing needs global knowledge, and there is no central list for
13558
+ * somebody to forget to edit.
13559
+ *
13560
+ * They are not merged, because their invariants are opposites:
13561
+ *
13562
+ * - a `load-contribution` measurement is **absent, never zero** — a zero would
13563
+ * claim a camera cost nothing, which is a measurement nobody made;
13564
+ * - a `failure-contribution` zero is the **most valuable value on the
13565
+ * surface** — `attempts: 400, succeeded: 400` is the proof a fix landed,
13566
+ * and it is exactly what an absent entry cannot say.
13567
+ *
13568
+ * Putting a loss counter on a cost entry would also break the reconciliation
13569
+ * that gives `load-contribution` its point: contributions are subtracted from
13570
+ * `metrics.node-processes-snapshot` to find processes nobody claims. A failure
13571
+ * has no process.
13572
+ *
13573
+ * ## Why not a log line, since the counters already exist
13574
+ *
13575
+ * Several of these paths already counted themselves — `CaptureScheduler`'s
13576
+ * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
13577
+ * ends in a log line, and a log line is the thing the operator asked to stop
13578
+ * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
13579
+ * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
13580
+ * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
13581
+ * media blackout were both diagnosed. The counters stay; this is where they can
13582
+ * be READ.
13583
+ *
13584
+ * ## The rate is served with its denominator or not at all
13585
+ *
13586
+ * Every entry carries `attempts` and `succeeded`. A miss count alone is
13587
+ * unreadable: on 2026-08-28 the enrichment-crop miss count read as "35x worse
13588
+ * than yesterday" and was **flat across twelve hours** once divided by the
13589
+ * successes on the same path. A surface that publishes only the numerator
13590
+ * reproduces that mistake on every read.
13591
+ *
13592
+ * ## Shape
13593
+ *
13594
+ * Copied from `load-contribution.cap.ts` (`mode: 'collection'`,
13595
+ * `internal: true`, `mount: { kind: 'skip' }`): no tRPC route of its own and no
13596
+ * generated hooks, while `addons.listCapabilityProviders` still enumerates it
13597
+ * and the hub's `CapabilityRegistry` still holds an RPC proxy per provider — so
13598
+ * a forked runner's entries reach hub-main over transport that already exists.
13599
+ * No new UDS message, no second registry (D3). The operator reads the assembled
13600
+ * result through `system.getFailureContributions`.
13601
+ */
13602
+ var FailureReasonCountSchema = object({
13603
+ /**
13604
+ * Why the attempt did not land, in the contributor's own vocabulary —
13605
+ * `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
13606
+ * strings that already appear in this repo's logs and, where one exists, the
13607
+ * same string the per-track `previewMissReason` records (D276): a second
13608
+ * vocabulary for the same loss would make the row and the counter
13609
+ * un-joinable.
13610
+ */
13611
+ reason: string$2(),
13612
+ count: number().int().nonnegative()
13613
+ });
13614
+ var FailureContributionSchema = object({
13615
+ /**
13616
+ * The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
13617
+ * `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
13618
+ * `unit` free: the families are owned by different addons and a shared enum
13619
+ * is a central list that rots invisibly.
13620
+ */
13621
+ family: string$2(),
13622
+ /**
13623
+ * The NUMERIC device id — the same value every log line carries as
13624
+ * `tags.deviceId`. Never nullable and never absent: a contributor that
13625
+ * cannot name the camera must not emit the entry, because a fleet total
13626
+ * cannot answer the only question anybody asks of this surface.
13627
+ */
13628
+ deviceId: number().int().positive(),
13629
+ /**
13630
+ * A second dimension inside the family: the model / step id for an inference
13631
+ * timeout, so "which camera AND which model" is one read. Absent when the
13632
+ * family has a single variant.
13633
+ */
13634
+ variant: string$2().optional(),
13635
+ /**
13636
+ * Epoch ms this counter started — the INCARNATION MARKER. A consumer
13637
+ * differencing two reads must drop the interval when it changes, because the
13638
+ * counter restarted from zero in a respawned runner. Same discipline as
13639
+ * `LoadContribution.startedAtMs`.
13640
+ */
13641
+ sinceMs: number(),
13642
+ /** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
13643
+ atMs: number(),
13644
+ /**
13645
+ * THE DENOMINATOR — every attempt on this path for this camera in the
13646
+ * window. A failure count published without it is the mistake this schema
13647
+ * exists to make impossible.
13648
+ */
13649
+ attempts: number().int().nonnegative(),
13650
+ /** Attempts that landed. `attempts - succeeded` is the loss. */
13651
+ succeeded: number().int().nonnegative(),
13652
+ /** The loss, partitioned. Sums to `attempts - succeeded`. */
13653
+ reasons: array(FailureReasonCountSchema).readonly()
13654
+ });
13655
+ method(_void(), array(FailureContributionSchema).readonly());
13548
13656
  var LoadContributionSchema = object({
13549
13657
  role: _enum([
13550
13658
  "decode",
@@ -18135,6 +18243,20 @@ var TrackSchema = object({
18135
18243
  * `=== true` and render nothing otherwise — never infer "no rider".
18136
18244
  */
18137
18245
  hasRider: boolean().optional(),
18246
+ /**
18247
+ * WHY this track ended without a NATIVE best-shot tile
18248
+ * ([D276](../decisions/adr-0276-a-stand-in-tile-is-provisional-and-a-close-says-why.md)) —
18249
+ * a composed token line (`no-key-frame capture=keyframe:native-missx4`,
18250
+ * `derive-returned-null tile=standin`, …) written at close and CLEARED by
18251
+ * the late-keyFrame upgrade when a native tile lands after all. The
18252
+ * operator-facing answer to "perché manca l'immagine?" on a track whose
18253
+ * tile is a face/plate stand-in, a raster crop, or an icon.
18254
+ *
18255
+ * **Absent ≠ "missed silently"**: a row written before the column, a hub
18256
+ * that predates the field, and every track whose tile landed native all
18257
+ * omit it. Render nothing when absent.
18258
+ */
18259
+ previewMissReason: string$2().optional(),
18138
18260
  ...TrackFlagFields,
18139
18261
  ...TrackRetrainFields
18140
18262
  });
@@ -29327,6 +29449,13 @@ var LoggingSettingsPatchSchema = object({
29327
29449
  * anyone but its owner.
29328
29450
  */
29329
29451
  var ReportedLoadContributionSchema = LoadContributionSchema.extend({ addonId: string$2() });
29452
+ /**
29453
+ * One per-camera failure counter, plus WHO reported it.
29454
+ *
29455
+ * Same rule as {@link ReportedLoadContributionSchema}: `addonId` is stamped by
29456
+ * the hub as it enumerates providers, never by the contributor.
29457
+ */
29458
+ var ReportedFailureContributionSchema = FailureContributionSchema.extend({ addonId: string$2() });
29330
29459
  var GetLoggingSettingsInputSchema = object({
29331
29460
  scopeNodeId: string$2().optional(),
29332
29461
  /**
@@ -29385,7 +29514,7 @@ method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), met
29385
29514
  }), method(_void(), SiteLocationStatusSchema, {
29386
29515
  kind: "mutation",
29387
29516
  auth: "admin"
29388
- }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
29517
+ }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(_void(), array(ReportedFailureContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
29389
29518
  kind: "mutation",
29390
29519
  auth: "admin"
29391
29520
  });
@@ -33375,6 +33504,12 @@ Object.freeze({
33375
33504
  addonId: null,
33376
33505
  access: "create"
33377
33506
  },
33507
+ "failureContribution.list": {
33508
+ capName: "failure-contribution",
33509
+ capScope: "system",
33510
+ addonId: null,
33511
+ access: "view"
33512
+ },
33378
33513
  "fanControl.setDirection": {
33379
33514
  capName: "fan-control",
33380
33515
  capScope: "device",
@@ -36681,6 +36816,12 @@ Object.freeze({
36681
36816
  addonId: null,
36682
36817
  access: "create"
36683
36818
  },
36819
+ "system.getFailureContributions": {
36820
+ capName: "system",
36821
+ capScope: "system",
36822
+ addonId: null,
36823
+ access: "view"
36824
+ },
36684
36825
  "system.getLoadContributions": {
36685
36826
  capName: "system",
36686
36827
  capScope: "system",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-matter-broker",
3
- "version": "0.2.37",
3
+ "version": "0.2.38",
4
4
  "description": "Matter broker addon for CamStack — owns a Matter fabric (commissioning + the long-lived controller) via the matter.js controller and brokers commissioned Matter nodes into CamStack",
5
5
  "keywords": [
6
6
  "camstack",