--- id: ACTIVITY-WP-0030 type: workplan title: "Daily bounded SBOM catch-up via sbom-nexus" domain: infotech repo: activity-core status: active owner: grok topic_slug: infotech created: "2026-08-18" updated: "2026-08-18" parent_workplan: CUST-WP-0062 related: - CUST-WP-0062 - ACTIVITY-WP-0022 - ADHOC-2026-06-01 state_hub_workstream_id: "bfe2945e-38df-40d1-94d4-6e4a003ef66d" --- # Daily bounded SBOM catch-up via sbom-nexus ## Goal Replace `weekly-sbom-staleness` / `flag-stale-sbom` with a **daily** ActivityDefinition that: 1. Evaluates fleet SBOM freshness through **sbom-nexus** (one ranked catch-up call, not a per-repo walk and not a `for_each` over every stale repo). 2. **Updates N** SBOM snapshots for the repos that have lacked a current SBOM the longest. **N defaults to 3** (`catch_up_limit`). Parent coordination: **CUST-WP-0062**. Parked 2026-08-18 — do not start until that parent is unblocked. Do not enable this schedule until the catch-up API exists. ## Current failure mode Monday 2026-08-17 prod fire spawned **75** `Run SBOM rescan for {slug}` tasks. The rule is `for_each: context.repos.repos` where `context.repo.sbom_age_days > 30`. On 2026-08-18 the hub listed **111 / 111** repos stale or never scanned. The weekly check reports the backlog; it does not close it. `repo_sbom_status` bulk mode already uses `GET /repos/` (age from `last_sbom_at`) rather than N `/sbom/{slug}` calls. That is still the wrong shape for catch-up: the worker materialises every repo, then emits one task each. The replacement must ask sbom-nexus for **only N targets** and then **ingest** them. ## Tasks ### Draft definition and resolver contract ```task id: ACTIVITY-WP-0030-T01 status: wait priority: high state_hub_task_id: "b216976e-5402-4e40-b282-78c5d021df72" ``` Write `activity-definitions/daily-sbom-catchup.md` (`enabled: false` until cutover): weekday or daily cron, Berlin morning after triage is fine. Context source: sbom-nexus `catch_up` with `limit` default 3. Deterministic progress (`event_type: sbom_catchup`) listing selected / updated / skipped. No `for_each` over the full stale set. Resolver contract (implement against a test double until CUST-WP-0062-T03 lands): - input: `{limit: 3}` - output: `{repos: [...], stale_count, never_count, total_count, limit}` - each repo: `repo_slug`, `last_sbom_at`, `sbom_age_days`, `has_sbom`, `checkout_available` if known ### Implement ingest side-effect for N targets ```task id: ACTIVITY-WP-0030-T02 status: wait priority: high state_hub_task_id: "0d09c0d3-74bd-4f4d-8b27-5006ba010fa3" ``` Wait on CUST-WP-0062-T03. Bounded side-effect (SCOPE: declared purpose only): for each of the N repos, call sbom-nexus ingest using the registered checkout when present. Record skip reasons (`no-checkout`, `no-manifest`, `ingest-error`) in the run artefact and progress event. Do not spawn Forgejo issues (ACTIVITY-WP-0022). Do not retry a recorded skip as if it were still the oldest never-scanned repo. ### Retire weekly task flood ```task id: ACTIVITY-WP-0030-T03 status: wait priority: high state_hub_task_id: "18bcfc21-616a-48a2-aac3-cdcef7579913" ``` After the daily job is proven on railiance01: set `weekly-sbom-staleness` `enabled: false` (or remove `flag-stale-sbom`), keep or drop the weekly summary report only if it no longer emits one task per repo. Update runbook / playbook. Prove one Monday window with zero SBOM task spawns and a successful daily catch-up the same week. ### Prod enable and evidence ```task id: ACTIVITY-WP-0030-T04 status: wait priority: medium state_hub_task_id: "f09144f6-c7f9-41d4-b466-d8a6288e6a21" ``` Project the definition into `k8s/railiance/20-runtime.yaml`, sync schedules, and capture prod evidence via `./scripts/prod_automation_status.sh` plus the `sbom_catchup` progress event. Confirm `tasks_spawned` is 0 and N updates (or documented skips) appear per fire. ## Acceptance - [ ] Daily definition exists; weekly flood is off - [ ] Evaluation is one sbom-nexus catch-up call - [ ] Each fire updates at most N repos (default 3) - [ ] Progress names the repos and skip reasons - [ ] Railiance01 evidence for at least two successful daily fires