diff --git a/scripts/gc-remote-growth-contract-test.ts b/scripts/gc-remote-growth-contract-test.ts index db82d67d..bd86c76b 100644 --- a/scripts/gc-remote-growth-contract-test.ts +++ b/scripts/gc-remote-growth-contract-test.ts @@ -85,6 +85,17 @@ assertCondition( trendBody, ); +const compactPointBody = functionBody("compact_growth_point"); +assertCondition( + compactPointBody.includes('"sourceCount"') + && compactPointBody.includes('"registry"') + && compactPointBody.includes('"ciStorage"') + && compactPointBody.includes('"containerd"') + && sourceText.includes("[compact_growth_point(item) for item in history[-min(len(history), 3):]]"), + "growth trend must default to compact history points unless --full is requested", + compactPointBody, +); + console.log(JSON.stringify({ ok: true, checks: [ @@ -93,5 +104,6 @@ console.log(JSON.stringify({ "snapshot includes source sizes, owner-aware CI PVCs, registry cadence and containerd observation", "HWLAB and AgentRun retention handoff commands are explicit", "trend includes latest/window deltas and registry counters", + "trend returns compact history points by default", ], })); diff --git a/scripts/src/gc-remote.ts b/scripts/src/gc-remote.ts index b68f7333..0f4d8751 100644 --- a/scripts/src/gc-remote.ts +++ b/scripts/src/gc-remote.ts @@ -865,6 +865,37 @@ def growth_trend_payload(points): }, } +def compact_growth_point(item): + registry = item.get("registry") or {} + retention = registry.get("retentionPlan") or {} + ci_storage = item.get("ciStorage") or {} + containerd = item.get("containerd") or {} + return { + "observedAt": item.get("observedAt"), + "rootDisk": item.get("rootDisk"), + "sourceCount": len(item.get("sources") or []), + "registry": { + "sizeBytes": registry.get("sizeBytes"), + "sizeHuman": registry.get("sizeHuman"), + "totalTags": retention.get("totalTags"), + "totalRevisions": retention.get("totalRevisions"), + "deleteTags": retention.get("deleteTags"), + "deleteRevisions": retention.get("deleteRevisions"), + "estimatedReclaimBytes": retention.get("estimatedReclaimBytes"), + "estimatedReclaimHuman": retention.get("estimatedReclaimHuman"), + }, + "ciStorage": { + "pvcCount": ci_storage.get("pvcCount"), + "estimatedBytes": ci_storage.get("estimatedBytes"), + "estimatedHuman": ci_storage.get("estimatedHuman"), + "byOwnerGroup": ci_storage.get("byOwnerGroup"), + }, + "containerd": { + "state": containerd.get("state"), + "cleanupSupported": containerd.get("cleanupSupported"), + }, + } + def collect_growth_snapshot(observed_at, preflight): root_disk = df_snapshot() sources = disk_source_snapshot() @@ -2126,7 +2157,7 @@ def main(): "statePath": growth_snapshot_path(), "historyLimit": history_limit, "trend": growth_trend_payload(history), - "points": history if bool(OPTIONS.get("full")) else history[-min(len(history), 3):], + "points": history if bool(OPTIONS.get("full")) else [compact_growth_point(item) for item in history[-min(len(history), 3):]], "returnedPointCount": min(len(history), 3) if not bool(OPTIONS.get("full")) else len(history), "totalPointCount": len(history), "next": { @@ -2151,14 +2182,7 @@ def main(): "history": { "totalPointCount": len(read_growth_snapshots(1000000)) if bool(OPTIONS.get("saveSnapshot", True)) else len(history), "returnedPointCount": len(history[-min(len(history), 3):]), - "recentPoints": history[-min(len(history), 3):] if bool(OPTIONS.get("full")) else [ - { - "observedAt": item.get("observedAt"), - "rootDisk": item.get("rootDisk"), - "sourceCount": len(item.get("sources") or []), - } - for item in history[-min(len(history), 3):] - ], + "recentPoints": history[-min(len(history), 3):] if bool(OPTIONS.get("full")) else [compact_growth_point(item) for item in history[-min(len(history), 3):]], }, }) emit_json(snapshot, persist_large=True)