Skip to content

v0.11.0 "Early Warning Operations"

Choose a tag to compare

@github-actions github-actions released this 01 Nov 18:50
· 148 commits to main since this release
835296b

Scheduled alerts an operator can leave running, and a first track T check for AI coding agents.

  • Webhooks: a scheduled webhook posts each alert once. Failed posts are retried and dead-lettered, not lost, and with a secret every post is signed.
  • CI: jobs can gate on upcoming deadlines (warnings --fail-on).
  • MCP: answers state their lookup limits, and dependency_status says whether a PyPI or npm package and version exist.

This release changes webhook output: a scheduled check --webhook posts only new alerts, so cache .openpulse/deliveries in CI. Verdicts, contracts and the frozen evaluation are unchanged. Read the upgrade notes before upgrading scheduled jobs.

Upgrade notes

  • A scheduled check --webhook posts each alert once (#78, v0.11.0 item W1). It used to post the whole digest on every run. It now posts only alerts not delivered before, and nothing when there are none. An alert is one affected cause of a verdict (dependency, event, relationship, impact and the event's content), or a repository whose inspection was not COMPLETE. State is kept in .openpulse/deliveries/alerts.json; in GitHub Actions, carry it in the cache like the ledger (docs/SCHEDULED_CHECKS.md). --delivery-state '' restores the old behaviour. Terminal output, verdicts and exit codes are unchanged.

Added

  • Does this package exist? (track T, T-M1). dependency_status (MCP) now reports whether a PyPI or npm package and version exist in their registry (registry_existence). The common hallucination shapes are covered: an invented package, and a real package with an invented version.
    • Statuses: records use the closed T-F1 vocabulary. A 404 is VERIFIED_FAILURE, an identity fact and never evidence of malice. Anything else (a 5xx, 429, redirect, timeout, offline, a spent budget, an unsupported ecosystem) is NOT_PERFORMED, never "absent".
    • Untrusted text (T-F3): names and versions are validated against the ecosystem's grammar before they go into a URL, and refused otherwise. Requests are HEAD only, so no upstream text is read. Statements are fixed templates.
    • Limits: each check spends one lookup from the session budget (W4). It is cached per session, and NOT_PERFORMED is never cached.
    • Unchanged: the verdict, the contract and the frozen evaluation. No new dependency.
  • MCP: stated lookup limits and a cross-session cache (v0.11.0 item W4, C5).
    • Budget: one server session makes at most 200 live lookups; cached answers are free. Every tool answer carries a lookups block (budget, used, remaining, per-source timeout, cache TTL).
    • Past the budget: no lookup is made. The answer lists it under not_established with the reason, and project_lifecycle returns a tool error that says so.
    • Per-file cap: check_dependency_file names the projects its 40-project cap skipped (live_lookups_capped). It used to skip them silently.
    • Cache: project_lifecycle caches successful lookups on disk across sessions (OPENPULSE_CACHE_DIR; empty disables) with the session's one-hour TTL. Failures are never cached, and an answer served from the cache gives its age (cached_age_seconds).
  • Signed webhook payloads (#78, v0.11.0 item W3). With OPENPULSE_WEBHOOK_SECRET or --webhook-secret-file (at least 32 characters), every check --webhook post carries X-OpenPulse-Timestamp and X-OpenPulse-Signature: v1=<hex>, an HMAC-SHA256 over "<timestamp>." + body on the exact bytes sent. core/webhook_signing.verify_webhook_signature (stdlib only) verifies it over the raw body, in constant time, and rejects a timestamp more than 300 seconds off, which bounds replay. The secret is never printed or stored. Without a secret, posts are byte-for-byte unchanged.
  • warnings --fail-on {overdue,critical,high,medium,low} (#78, v0.11.0 item W2). A CI job can fail on deadlines. The command exits 1 when a warning at or above the severity is in the window; the order is OVERDUE > CRITICAL > HIGH > MEDIUM > LOW, so critical includes OVERDUE. As with every gate, it also exits 1 on FAILED repository coverage. The reason goes to stderr, so --output json stays parseable. Without the flag, warnings only reports, as before.
  • Persistent alert delivery state (#78, v0.11.0 item W1, C1). core/delivery.py gives each alert a stable ID and records its delivery.
    • Retry: a failed post is retried once in the run, then left pending. Later runs retry it after a backoff (5 minutes, doubling, at most 6 hours).
    • Dead letter: after 5 failed runs the alert becomes dead_letter. It is kept with its last (redacted) error and never posted again, and the run says so.
    • One run at a time: the state is written atomically under an OS lock held across load, post and save, so two runs at once can't both send. A corrupt state file is moved aside, never overwritten.
    • Configuration: --delivery-state / OPENPULSE_DELIVERY_STATE.