Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a028de-e2c8-7732-8521-46a7fc5db82f
121 lines
4.9 KiB
Markdown
121 lines
4.9 KiB
Markdown
---
|
|
id: daily-sbom-catchup
|
|
name: Daily SBOM Catch-up
|
|
enabled: true
|
|
owner: custodian-agent
|
|
governance: custodian
|
|
status: active
|
|
trigger:
|
|
type: cron
|
|
cron_expression: "15 9 * * 1-5" # weekdays 09:15, after the daily triage window
|
|
timezone: Europe/Berlin
|
|
misfire_policy: skip
|
|
context_sources:
|
|
- type: sbom-nexus
|
|
query: catch_up
|
|
operation: sbom_nexus_ingest
|
|
required: true
|
|
params:
|
|
limit: 3 # catch_up_limit — operator knob, not a nexus constant
|
|
apply: true # declared bounded side-effect; manual runs require confirmation
|
|
bind_to: context.catchup
|
|
# One ranked call returns only the N repos that have lacked a current SBOM the
|
|
# longest (never-scanned first) plus fleet counts. There is deliberately no
|
|
# `for_each` over the stale set: the 2026-08-17 fire emitted 75 tasks that way.
|
|
---
|
|
|
|
# Daily SBOM Catch-up
|
|
|
|
> **Enabled after production proof on 2026-08-22.** Two bounded manual fires
|
|
> processed three distinct repositories each, spawned zero tasks, and persisted
|
|
> terminal `no-checkout` evidence. The superseded weekly schedule remains off.
|
|
|
|
Replaces `weekly-sbom-staleness` / `flag-stale-sbom` (ACTIVITY-WP-0030).
|
|
The weekly check reported the backlog — 111 / 111 repos stale on 2026-08-18,
|
|
93 never scanned — and spawned one task per stale repo, so it could never
|
|
close it. This definition **updates N instead of reporting N**.
|
|
|
|
Runs weekdays at 09:15 Berlin, after the daily triage window.
|
|
|
|
## Contract
|
|
|
|
Context source `sbom-nexus / catch_up` (`context_resolvers/sbom_nexus.py`):
|
|
|
|
| Direction | Shape |
|
|
| --- | --- |
|
|
| input | `{"limit": 3}` |
|
|
| output | `{repos, selected_count, stale_count, never_count, total_count, limit}` |
|
|
| each repo | `repo_slug`, `last_sbom_at`, `sbom_age_days`, `has_sbom`, `checkout_available`, optional immutable `source_ref` |
|
|
|
|
Ranking (never-scanned first, then oldest `last_sbom_at`) belongs to
|
|
sbom-nexus. The adapter truncates to `limit` so an over-long response can never
|
|
widen the bounded side-effect below.
|
|
|
|
## Task emission
|
|
|
|
None. This definition carries **no `rule` block** — that is the point of the
|
|
replacement. `tasks_spawned` must stay 0 on every fire, and no Forgejo issues
|
|
are emitted (ACTIVITY-WP-0022).
|
|
|
|
## Evidence
|
|
|
|
```instruction
|
|
id: daily-sbom-catchup-report
|
|
trusted_fields: []
|
|
model: deterministic
|
|
temperature: 0
|
|
max_tokens: 1
|
|
prompt: |
|
|
Deterministic SBOM catch-up report from context.catchup (no LLM).
|
|
output_schema: ""
|
|
review_advisory: false
|
|
report_sinks:
|
|
- type: state-hub-progress
|
|
event_type: sbom_catchup
|
|
author: activity-core
|
|
topic_id: cee7bedf-2b48-46ef-8601-006474f2ad7a
|
|
```
|
|
|
|
The progress event names the repos selected, updated, and skipped with a
|
|
reason (`no-checkout`, `no-manifest`, `ingest-error`), plus the fleet counters
|
|
so `never_count` can be watched declining day over day.
|
|
|
|
Controlled-source outcomes additionally include `source-unavailable` and
|
|
`source-rejected`. They remain terminal for the selected repository and never
|
|
cause the same fire to select a replacement target.
|
|
|
|
## Bounded side-effect (ACTIVITY-WP-0030-T02 / ACTIVITY-WP-0033)
|
|
|
|
`params.apply: true` declares the write, but context resolution remains
|
|
read-only. The workflow records the already-truncated N targets in Temporal
|
|
history, then a dedicated activity applies exactly that fixed set. Completed
|
|
per-repository outcomes are heartbeated so an ordinary activity retry resumes
|
|
after them instead of selecting or writing a second batch.
|
|
|
|
A target with an explicitly unavailable checkout is recorded through
|
|
`POST /sbom/{slug}/skip` as `no-checkout`; other targets use the terminal ingest
|
|
route, which returns `ingested`, `no-manifest`, or `ingest-error`. Each request
|
|
sends a stable per-run/per-repository `Idempotency-Key`. Transport failures,
|
|
timeouts, and malformed responses fail visibly and are never converted into a
|
|
synthetic skip because the remote write may have committed. Nexus must enforce
|
|
the operation key to close the remaining crash window between a committed POST
|
|
and Activity Core's next heartbeat. The resulting `updated` and `skipped`
|
|
arrays are part of the run context and progress report. There is no task or
|
|
issue emission.
|
|
|
|
When Nexus returns a `forgejo-archive-v1` `source_ref`, the workflow freezes it
|
|
with the original target set and sends that exact object in the ingest body.
|
|
Retries reuse the same full commit SHA even if the repository default branch
|
|
has advanced.
|
|
|
|
## Enable checklist
|
|
|
|
1. CUST-WP-0062-T02/T03 done: `sbom-nexus` stood up, `GET /sbom/catch-up`
|
|
returns oldest-N in one call. **Done 2026-08-22.**
|
|
2. `SBOM_NEXUS_URL` reachable from the railiance01 worker.
|
|
3. ACTIVITY-WP-0030-T02 ingest side-effect implemented and dry-run proven.
|
|
4. `weekly-sbom-staleness` confirmed off in source **and** production
|
|
(ACTIVITY-WP-0030-T03).
|
|
5. Project into `k8s/railiance/20-runtime.yaml`, `enabled: true`, sync
|
|
schedules, capture evidence via `./scripts/prod_automation_status.sh`
|
|
(ACTIVITY-WP-0030-T04).
|