diff --git a/activity-definitions/daily-sbom-catchup.md b/activity-definitions/daily-sbom-catchup.md index 7d8a570..c038210 100644 --- a/activity-definitions/daily-sbom-catchup.md +++ b/activity-definitions/daily-sbom-catchup.md @@ -1,10 +1,10 @@ --- id: daily-sbom-catchup name: Daily SBOM Catch-up -enabled: false +enabled: true owner: custodian-agent governance: custodian -status: paused +status: active trigger: type: cron cron_expression: "15 9 * * 1-5" # weekdays 09:15, after the daily triage window @@ -25,9 +25,9 @@ context_sources: # Daily SBOM Catch-up -> **Disabled until production enable evidence is ready.** The `sbom-nexus` -> ranked and terminal ingest/skip APIs are now available. Keep this definition -> off until cluster reachability and a bounded manual fire are proven. +> **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, diff --git a/k8s/railiance/20-runtime.yaml b/k8s/railiance/20-runtime.yaml index 4b756d2..8f2f08a 100644 --- a/k8s/railiance/20-runtime.yaml +++ b/k8s/railiance/20-runtime.yaml @@ -43,6 +43,50 @@ metadata: app.kubernetes.io/name: activity-core app.kubernetes.io/part-of: activity-core data: + daily-sbom-catchup.md: | + --- + 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" + timezone: Europe/Berlin + misfire_policy: skip + context_sources: + - type: sbom-nexus + query: catch_up + required: true + params: + limit: 3 + apply: true + bind_to: context.catchup + --- + + # Daily SBOM Catch-up + + Ranked, bounded SBOM catch-up. Each fire processes at most three + repositories through sbom-nexus and deliberately contains no task rule. + + ```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 + ``` openbao-retention-closeout.md: | --- id: "e274defb-28f2-571e-abcb-c17b57eab473" diff --git a/tests/test_sbom_nexus_context_resolver.py b/tests/test_sbom_nexus_context_resolver.py index 4bc77fc..f6a16e3 100644 --- a/tests/test_sbom_nexus_context_resolver.py +++ b/tests/test_sbom_nexus_context_resolver.py @@ -424,14 +424,14 @@ def test_deterministic_report_carries_t02_ingest_outcomes() -> None: assert "1 updated, 1 skipped" in report["summary"] -def test_daily_definition_is_bounded_and_disabled() -> None: +def test_daily_definition_is_bounded_and_enabled() -> None: from pathlib import Path from activity_core.definition_parser import parse_file definition = parse_file(Path("activity-definitions/daily-sbom-catchup.md")) - assert definition.enabled is False + assert definition.enabled is True # No rule block: the weekly flood came from `for_each` over every stale repo. assert definition.rules == [] source = definition.context_sources[0] @@ -443,3 +443,31 @@ def test_daily_definition_is_bounded_and_disabled() -> None: instruction = definition.instructions[0] assert instruction["model"] == "deterministic" assert instruction["report_sinks"][0]["event_type"] == "sbom_catchup" + + +def test_railiance_projection_enables_the_same_bounded_contract(tmp_path) -> None: + from pathlib import Path + + import yaml + from activity_core.definition_parser import parse_file + + documents = list( + yaml.safe_load_all(Path("k8s/railiance/20-runtime.yaml").read_text()) + ) + config = next( + item + for item in documents + if isinstance(item, dict) + and item.get("kind") == "ConfigMap" + and item.get("metadata", {}).get("name") + == "actcore-external-activity-definitions" + ) + projected = tmp_path / "daily-sbom-catchup.md" + projected.write_text(config["data"]["daily-sbom-catchup.md"]) + + definition = parse_file(projected) + + assert definition.enabled is True + assert definition.rules == [] + source = definition.context_sources[0] + assert source["params"] == {"limit": 3, "apply": True}