feat(sbom): draft bounded daily SBOM catch-up (ACTIVITY-WP-0030-T01)
Add the sbom-nexus catch_up resolver contract and the daily replacement for weekly-sbom-staleness, both disabled until CUST-WP-0062-T03 lands. The weekly check used `for_each: context.repos.repos` and emitted one task per stale repo — 75 tasks on 2026-08-17 against 111/111 stale repos. The replacement asks sbom-nexus for only the N oldest-stale repos in one ranked call and carries no rule block at all, so tasks_spawned is 0 by construction. - context_resolvers/sbom_nexus.py: source type `sbom-nexus`, query `catch_up`, GET /sbom/catch-up?limit=N. Read-only; ingest is T02. Limit bounded 1..25 and the response truncated to it so an over-long reply cannot widen T02's side-effect. - activity-definitions/daily-sbom-catchup.md: weekdays 09:15 Berlin, enabled: false, deterministic sbom_catchup progress sink. - rules/executor.py: the deterministic report builder only special-cased context.repos, which would have emitted a contentless progress event for this definition. _sbom_catchup_report names the selected repos and reads updated/skipped from context when T02 populates them. - 17 tests against a test double; no live nexus exists yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
a446de1c45
commit
e64af4102d
6 changed files with 667 additions and 2 deletions
100
activity-definitions/daily-sbom-catchup.md
Normal file
100
activity-definitions/daily-sbom-catchup.md
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
id: daily-sbom-catchup
|
||||
name: Daily SBOM Catch-up
|
||||
enabled: false
|
||||
owner: custodian-agent
|
||||
governance: custodian
|
||||
status: paused
|
||||
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
|
||||
required: true
|
||||
params:
|
||||
limit: 3 # catch_up_limit — operator knob, not a nexus constant
|
||||
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
|
||||
|
||||
> **Disabled until CUST-WP-0062-T03 lands.** The `sbom-nexus` catch-up API does
|
||||
> not exist yet; the resolver contract is exercised against a test double in
|
||||
> `tests/test_sbom_nexus_context_resolver.py`. Do not enable this schedule
|
||||
> against the old per-repo `/sbom/{slug}` walk (CUST-WP-0062-T05).
|
||||
|
||||
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` |
|
||||
|
||||
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_required: 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.
|
||||
|
||||
## Not yet implemented (ACTIVITY-WP-0030-T02)
|
||||
|
||||
The bounded ingest side-effect — calling sbom-nexus ingest for each of the N
|
||||
selected repos using the registered checkout — waits on CUST-WP-0062-T03. Until
|
||||
then this definition resolves and reports only. When T02 lands, a recorded skip
|
||||
must advance the queue position so an impossible repo does not permanently head
|
||||
it.
|
||||
|
||||
## Enable checklist
|
||||
|
||||
1. CUST-WP-0062-T02/T03 done: `sbom-nexus` stood up, `GET /sbom/catch-up`
|
||||
returns oldest-N in one call.
|
||||
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).
|
||||
Loading…
Add table
Add a link
Reference in a new issue