Hide monitored system wire aliases from client contract

This commit is contained in:
rcourtman
2026-03-24 09:19:11 +00:00
parent dc98053112
commit b271fb5b2a
6 changed files with 33 additions and 42 deletions
@@ -256,8 +256,9 @@ That freshest grouped observation is now canonically exposed as the structured
`latest_included_signal` object. Its `at`, `source`, `name`, and `type` fields
identify exactly which included top-level surface reported most recently.
`latest_included_signal_at`, `latest_included_signal_source`, and `last_seen`
remain rollout compatibility fields only; consumers should treat the object as
the primary contract and preserve its meaning in presentation.
remain rollout compatibility fields only on the raw wire shape; normalized
frontend clients should treat the object as the primary contract and must not
re-export those flat aliases in their public response model.
That client contract must also fail closed when older or partial payloads omit
the nested explanation object: the frontend may normalize missing explanation
fields to empty reasons/surfaces plus a safe default summary, but it must not
@@ -225,17 +225,19 @@ That same settings surface must also label the monitored-system signal by its
real meaning. The canonical API shape is now the structured
`latest_included_signal` object, while `latest_included_signal_at`,
`latest_included_signal_source`, and `last_seen` are rollout compatibility
fields only. It represents the freshest included grouped observation, not a
guarantee that every grouped source is healthy, so the UI must not present it
with single-source `Last Seen` wording. When the canonical object is present,
the surface should use its source/name/type attribution instead of showing an
unqualified aggregate timestamp, and that attribution should stay
customer-facing rather than exposing raw monitored-system type/source slugs in
the settings table or included-surface details. When the row expands to show
status reasoning, it should also restate the freshest included surface and
timestamp there so a degraded reason and a fresher grouped signal remain
readable together. When mixed-version payloads omit that canonical freshest
signal entirely, the settings surface should degrade to a safe customer-facing
fields only on the raw response payload. The normalized frontend client
contract should expose only the canonical object. It represents the freshest
included grouped observation, not a guarantee that every grouped source is
healthy, so the UI must not present it with single-source `Last Seen` wording.
When the canonical object is present, the surface should use its
source/name/type attribution instead of showing an unqualified aggregate
timestamp, and that attribution should stay customer-facing rather than
exposing raw monitored-system type/source slugs in the settings table or
included-surface details. When the row expands to show status reasoning, it
should also restate the freshest included surface and timestamp there so a
degraded reason and a fresher grouped signal remain readable together. When
mixed-version payloads omit that canonical freshest signal entirely, the
settings surface should degrade to a safe customer-facing
fallback instead of an unexplained placeholder glyph.
That same billing support boundary now also owns the shared monitored-system
presentation helper. `frontend-modern/src/utils/monitoredSystemPresentation.ts`
@@ -196,14 +196,15 @@ describe('MonitoredSystemLedgerAPI', () => {
const result = await MonitoredSystemLedgerAPI.getLedger();
expect(result.systems[0]?.latest_included_signal_at).toBe('2026-03-23T11:59:50Z');
expect(result.systems[0]?.latest_included_signal_source).toBe('docker');
expect(result.systems[0]?.latest_included_signal).toEqual({
name: 'tower.local',
type: 'docker-host',
source: 'docker',
at: '2026-03-23T11:59:50Z',
});
expect(result.systems[0]).not.toHaveProperty('latest_included_signal_at');
expect(result.systems[0]).not.toHaveProperty('latest_included_signal_source');
expect(result.systems[0]).not.toHaveProperty('last_seen');
});
it('falls back to legacy latest signal fields for older payloads', async () => {
@@ -223,14 +224,15 @@ describe('MonitoredSystemLedgerAPI', () => {
const result = await MonitoredSystemLedgerAPI.getLedger();
expect(result.systems[0]?.latest_included_signal_at).toBe('2026-03-23T11:59:50Z');
expect(result.systems[0]?.latest_included_signal_source).toBeUndefined();
expect(result.systems[0]?.latest_included_signal).toEqual({
name: 'Tower',
type: 'host',
source: undefined,
at: '2026-03-23T11:59:50Z',
});
expect(result.systems[0]).not.toHaveProperty('latest_included_signal_at');
expect(result.systems[0]).not.toHaveProperty('latest_included_signal_source');
expect(result.systems[0]).not.toHaveProperty('last_seen');
});
it('preserves canonical status explanation reasons from the API contract', async () => {
@@ -51,9 +51,6 @@ export interface MonitoredSystemLedgerEntry {
status: MonitoredSystemLedgerStatus;
status_explanation: MonitoredSystemLedgerStatusExplanation;
latest_included_signal: MonitoredSystemLedgerLatestSignal;
latest_included_signal_at: string; // freshest included observation, RFC3339 or empty
latest_included_signal_source?: string;
last_seen?: string; // deprecated compatibility alias
source: string;
explanation: MonitoredSystemLedgerExplanation;
}
@@ -75,6 +72,9 @@ type MonitoredSystemLedgerRawEntry = Omit<
MonitoredSystemLedgerEntry,
'status_explanation' | 'latest_included_signal' | 'explanation'
> & {
latest_included_signal_at?: string; // freshest included observation, RFC3339 or empty
latest_included_signal_source?: string;
last_seen?: string; // deprecated compatibility alias
status_explanation?: MonitoredSystemLedgerStatusExplanation;
latest_included_signal?: MonitoredSystemLedgerLatestSignal;
explanation?: MonitoredSystemLedgerExplanation;
@@ -106,13 +106,11 @@ function normalizeMonitoredSystemLedgerEntry(
entry,
);
return {
...entry,
name: entry.name,
type: entry.type,
status,
source: entry.source,
latest_included_signal: latestIncludedSignal,
latest_included_signal_at: latestIncludedSignal.at,
latest_included_signal_source: normalizeMonitoredSystemLedgerSource(
latestIncludedSignal.source,
),
status_explanation: {
summary: entry.status_explanation?.summary ?? getMonitoredSystemStatusFallbackSummary(status),
reasons: (entry.status_explanation?.reasons ?? []).map(normalizeMonitoredSystemLedgerStatusReason),
@@ -85,9 +85,6 @@ describe('MonitoredSystemLedgerPanel', () => {
source: 'agent',
at: '2026-01-01T00:00:00Z',
},
latest_included_signal_at: '2026-01-01T00:00:00Z',
latest_included_signal_source: 'agent',
last_seen: '2026-01-01T00:00:00Z',
source: 'agent',
explanation: {
summary:
@@ -156,9 +153,6 @@ describe('MonitoredSystemLedgerPanel', () => {
source: 'agent',
at: '2026-01-01T00:00:00Z',
},
latest_included_signal_at: '2026-01-01T00:00:00Z',
latest_included_signal_source: 'agent',
last_seen: '2026-01-01T00:00:00Z',
source: 'agent',
explanation: {
summary:
@@ -199,9 +193,6 @@ describe('MonitoredSystemLedgerPanel', () => {
source: 'pbs',
at: '2026-01-02T00:00:00Z',
},
latest_included_signal_at: '2026-01-02T00:00:00Z',
latest_included_signal_source: 'pbs',
last_seen: '2026-01-02T00:00:00Z',
source: 'pbs',
explanation: {
summary:
@@ -303,9 +294,6 @@ describe('MonitoredSystemLedgerPanel', () => {
source: 'agent',
at: '2026-01-01T00:00:00Z',
},
latest_included_signal_at: '2026-01-01T00:00:00Z',
latest_included_signal_source: 'agent',
last_seen: '2026-01-01T00:00:00Z',
source: 'agent',
},
],
@@ -352,8 +340,6 @@ describe('MonitoredSystemLedgerPanel', () => {
source: 'agent',
at: '',
},
latest_included_signal_at: '',
latest_included_signal_source: 'agent',
source: 'agent',
},
],
@@ -393,9 +379,6 @@ describe('MonitoredSystemLedgerPanel', () => {
source: 'agent',
at: '2026-01-01T00:00:00Z',
},
latest_included_signal_at: '2026-01-01T00:00:00Z',
latest_included_signal_source: 'agent',
last_seen: '2026-01-01T00:00:00Z',
source: 'agent',
},
],
@@ -252,6 +252,11 @@ describe('monitored-system model guardrails', () => {
expect(monitoredSystemLedgerApiSource).toContain('getMonitoredSystemExplanationFallbackSummary');
expect(monitoredSystemLedgerApiSource).toContain('type MonitoredSystemLedgerRawEntry =');
expect(monitoredSystemLedgerApiSource).toContain('systems?: MonitoredSystemLedgerRawEntry[];');
expect(monitoredSystemLedgerApiSource).toContain('latest_included_signal_at?: string;');
expect(monitoredSystemLedgerApiSource).toContain('last_seen?: string;');
expect(monitoredSystemLedgerApiSource).not.toContain(
'latest_included_signal_at: string; // freshest included observation, RFC3339 or empty',
);
expect(monitoredSystemLedgerApiSource).not.toContain(
'All included top-level collection paths currently report online status.',
);