@forgecart/flight-observability 0.202608220925.0 → 0.202608300703.0

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.
@@ -25,6 +25,42 @@ pg_idle_in_tx:
25
25
  usage: GAUGE
26
26
  description: Number of idle-in-transaction backends
27
27
 
28
+ # Attribution for the FcIdleInTxAge class (run 32930565055 forensics: a 9.7min
29
+ # idle-in-tx squatter blocked a scope boot and nothing named the owner). Top-1
30
+ # oldest idle-in-tx session WITH identity labels. Cardinality is safe by
31
+ # construction: zero rows — and therefore ZERO series — whenever no squatter
32
+ # exists; during an incident exactly one label tuple lives for the session's
33
+ # lifetime. query_head is whitespace-collapsed and truncated so a label value
34
+ # can never carry a full statement.
35
+ pg_idle_in_tx_top:
36
+ query: >-
37
+ SELECT
38
+ COALESCE(datname, '') AS datname,
39
+ COALESCE(usename, '') AS usename,
40
+ COALESCE(application_name, '') AS application_name,
41
+ COALESCE(left(regexp_replace(query, '\s+', ' ', 'g'), 60), '') AS query_head,
42
+ EXTRACT(EPOCH FROM (now() - xact_start))::float AS age_seconds
43
+ FROM pg_stat_activity
44
+ WHERE backend_type = 'client backend' AND state = 'idle in transaction'
45
+ ORDER BY xact_start ASC
46
+ LIMIT 1
47
+ metrics:
48
+ - datname:
49
+ usage: LABEL
50
+ description: Database of the oldest idle-in-transaction session
51
+ - usename:
52
+ usage: LABEL
53
+ description: Role of the oldest idle-in-transaction session
54
+ - application_name:
55
+ usage: LABEL
56
+ description: application_name of the oldest idle-in-transaction session
57
+ - query_head:
58
+ usage: LABEL
59
+ description: First 60 chars of the session's last statement (whitespace-collapsed)
60
+ - age_seconds:
61
+ usage: GAUGE
62
+ description: Age of the oldest idle-in-transaction session (seconds)
63
+
28
64
  pg_citus_backends:
29
65
  query: >-
30
66
  SELECT
@@ -62,3 +98,25 @@ pg_outbox:
62
98
  - oldest_unsent_seconds:
63
99
  usage: GAUGE
64
100
  description: Age of the oldest due-but-unsent outbox row (seconds)
101
+
102
+ # Marketing-event flush health (#1074) — the sensor run 32584944772 lacked: a
103
+ # batch consumed with zero dispatch was invisible. WHERE-form ON PURPOSE, not
104
+ # pg_outbox's FILTER shape: FILTER predicates are evaluated at the aggregate
105
+ # node over an unqualified scan, so on the unreaped firehose table every
106
+ # scrape would seq-scan ALL rows; the WHERE form is served by the partial
107
+ # index IDX_marketing_event_outbox_unflushed and stays O(backlog) — the 097
108
+ # shop_outbox_metrics() precedent. Column names quoted (TypeORM camelCase).
109
+ pg_marketing_outbox:
110
+ query: >-
111
+ SELECT
112
+ count(*)::float AS backlog,
113
+ COALESCE(EXTRACT(EPOCH FROM (now() - min("ingestedAt"))), 0)::float AS oldest_pending_seconds
114
+ FROM marketing_event_outbox
115
+ WHERE "flushedAt" IS NULL
116
+ metrics:
117
+ - backlog:
118
+ usage: GAUGE
119
+ description: Pending (unflushed) marketing-event outbox rows
120
+ - oldest_pending_seconds:
121
+ usage: GAUGE
122
+ description: Age of the oldest pending marketing-event outbox row (seconds)
@@ -54,7 +54,7 @@ groups:
54
54
  severity: warning
55
55
  annotations:
56
56
  summary: 'Idle-in-transaction backend older than 120s'
57
- description: 'Oldest idle-in-tx = {{ $value }}s — a held-open transaction pins a real coordinator connection through the pooler (tech-debt #10).'
57
+ description: 'Oldest idle-in-tx = {{ $value }}s — a held-open transaction pins a real coordinator connection through the pooler (tech-debt #10). Owner: db={{ with query "topk(1, pg_idle_in_tx_top_age_seconds)" }}{{ . | first | label "datname" }} user={{ . | first | label "usename" }} app={{ . | first | label "application_name" }} q="{{ . | first | label "query_head" }}"{{ end }}'
58
58
 
59
59
  - name: fc-alerts-pooler
60
60
  rules:
@@ -123,6 +123,20 @@ groups:
123
123
  summary: 'Transaction p99 above 80% of TRANSACTION_TIME_BUDGET_MS'
124
124
  description: 'p99 = {{ $value }}s vs the budget gauge — client-paced spans are drifting toward the kill window.'
125
125
 
126
+ - name: fc-alerts-model
127
+ rules:
128
+ # Run 32571110796's invisible class: account starvation fails live-model
129
+ # turns FAST behind generic fallbacks while every dashboard reads
130
+ # healthy — this is the loud surface. Fires on the FIRST rejection
131
+ # (usage limits persist for a billing window; there is no flap to damp).
132
+ - alert: FcModelUsageLimit
133
+ expr: sum(increase(fc_model_rate_limited_total{reason="usage_limit"}[10m])) > 0
134
+ labels:
135
+ severity: warning
136
+ annotations:
137
+ summary: 'Model account usage limit — live-model turns are being rejected'
138
+ description: 'Agent turns are failing fast on provider usage limits; resetsAt rides the agent log "Claude Code cannot serve". Live-model specs will red until the window resets or the limit is raised.'
139
+
126
140
  - name: fc-alerts-probes
127
141
  rules:
128
142
  - alert: FcDnsProbeFailing
@@ -142,6 +156,14 @@ groups:
142
156
  annotations:
143
157
  summary: 'Oldest due-but-unsent outbox row over 120s'
144
158
  description: 'oldest = {{ $value }}s, backlog = {{ with query "fc:outbox:backlog" }}{{ . | first | value }}{{ end }} — the relay is stalled or the broker is refusing publishes.'
159
+ - alert: FcMarketingFlushLag
160
+ expr: fc:marketing:flush_lag_seconds > 60
161
+ for: 5m
162
+ labels:
163
+ severity: warning
164
+ annotations:
165
+ summary: 'Oldest pending marketing-event outbox row over 60s'
166
+ description: 'oldest = {{ $value }}s, backlog = {{ with query "fc:marketing:backlog" }}{{ . | first | value }}{{ end }} — flush workers stalled, claim starvation, or every claimer erroring (#1074).'
145
167
 
146
168
  - name: fc-alerts-doctrine
147
169
  rules:
@@ -242,6 +242,10 @@ groups:
242
242
  expr: sum by (service, target) (fc_grpc_inflight)
243
243
  - record: fc:grpc:transient_failure_rate
244
244
  expr: sum by (service, target) (rate(fc_grpc_transient_failure_seconds_total[5m]))
245
+ # Model-provider limit rejections (run 32571110796's invisible class):
246
+ # reasons are a bounded set by construction (deriveModelLimitReason).
247
+ - record: fc:model:rate_limited_rate
248
+ expr: sum by (reason) (rate(fc_model_rate_limited_total[5m]))
245
249
 
246
250
  - name: fc-pgbouncer
247
251
  interval: 15s
@@ -384,6 +388,23 @@ groups:
384
388
  expr: max(pg_outbox_oldest_unsent_seconds)
385
389
  - record: fc:outbox:backlog
386
390
  expr: max(pg_outbox_backlog)
391
+ - name: fc-marketing
392
+ interval: 15s
393
+ rules:
394
+ # Marketing-event flush health (#1074) — pg_marketing_outbox is the
395
+ # WHERE-form probe (partial-index-served; never the FILTER full-scan).
396
+ - record: fc:marketing:backlog
397
+ expr: max(pg_marketing_outbox_backlog)
398
+ - record: fc:marketing:flush_lag_seconds
399
+ expr: max(pg_marketing_outbox_oldest_pending_seconds)
400
+ # Planned-vs-fired imbalance panel — both counters at-least-once, so
401
+ # panels read rate trends, never exact equality (#1074).
402
+ - record: fc:automation:planned_match_rate
403
+ expr: sum(rate(fc_automation_dispatch_matches_total[5m]))
404
+ - record: fc:automation:fire_rate
405
+ expr: sum by (outcome) (rate(fc_automation_fire_total[5m]))
406
+ - record: fc:automation:rule_cache_rate
407
+ expr: sum by (outcome) (rate(fc_automation_rule_cache_total[5m]))
387
408
  - name: fc-workspace
388
409
  interval: 15s
389
410
  rules:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgecart/flight-observability",
3
- "version": "0.202608220925.0",
3
+ "version": "0.202608300703.0",
4
4
  "private": false,
5
5
  "description": "Flight Deck dashboards, fc: recording/alert rules, and the pg-custom-queries contract — the version-pinned sync artifact between shop-beta and forgecart/cloud (launch#46 P4).",
6
6
  "files": [