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>
Add phase5_stabilization_check State Hub resolver with progress evidence
sinks, schedule projections for daily and closeout checks, custodian:// and
activity-core:// runtime path resolution, and Railiance mounts under /var/custodian.
Add ADR-004 documenting the producer trust boundary: untrusted producers (LLM,
agent, human; erroneous and malicious), the trust-but-handle vs verify-and-mitigate
postures, error-locality and quarantine-with-provenance principles, and the concrete
activity-core mechanisms.
Implement producer-agnostic guardrails in executor.py, applied uniformly on the
happy path and the recovery path via _partition_items: structural-type -> schema ->
structural caps (_MAX_DEPTH, _MAX_STRING_LEN) -> reference allow-list -> count cap.
Each quarantine carries a reason. Closes the happy-path maxItems count cap deferred
from T03 (valid 9-item report keeps 7, quarantines 2). Reference allow-list reads
context["known_candidates"] via _allow_list_from_context; inert until a resolver
populates it. SCOPE.md updated (executor bullet + ADR list); no INTENT drift.
New tests: happy-path count cap, oversized-string guardrail, allow-list rejection.
Full suite: 218 passed, 1 skipped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When the whole-document parse + one retry still fail, report instructions now run
_resilient_report before the total-loss path. A brace/quote-aware scanner
(_extract_object_spans) recovers each recommendation object whether pretty-printed
across many lines or NDJSON one-per-line; a truncated tail gets a best-effort
_try_repair; _partition_items validates each recovered object against the T02 item
schema. Valid items survive (output_validated=True, partial=True), malformed/
over-maxItems items are quarantined with provenance (index, error, raw, reason),
capped at 20. Error locality now matches the unit of work: one bad item costs one
item, not the whole report.
Verified against the real 06-26 shape: 7 valid recommendations + a truncated tail
now recovers all 7 and quarantines the broken tail (previously the whole run was
discarded). Happy-path maxItems top-N enforcement is deferred to T04 (count caps).
Full suite: 215 passed, 1 skipped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add safe action interpolation and for_each binding for rule fan-out, update the weekly SBOM definition, cover the new evaluation path, and reconcile activity-core scope/workplans for the State Hub sync.
The CUST-WP-0045 canary failed validation twice without leaving any
record of what the model actually returned. The warning logged only the
error message ($: missing required property 'summary'), not the JSON
shape that triggered it — so diagnosing required modifying code and
re-running. Log a 2KB preview of the offending raw output alongside the
error so the next failure of this shape is one grep away from diagnosis.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>