Repository navigation
v0.11.0 "Early Warning Operations"
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_statussays 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 --webhookposts 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) isNOT_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_PERFORMEDis never cached. - Unchanged: the verdict, the contract and the frozen evaluation. No new dependency.
- Statuses: records use the closed T-F1 vocabulary. A 404 is
- 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
lookupsblock (budget, used, remaining, per-source timeout, cache TTL). - Past the budget: no lookup is made. The answer lists it under
not_establishedwith the reason, andproject_lifecyclereturns a tool error that says so. - Per-file cap:
check_dependency_filenames the projects its 40-project cap skipped (live_lookups_capped). It used to skip them silently. - Cache:
project_lifecyclecaches 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).
- Budget: one server session makes at most 200 live lookups; cached answers are free. Every tool answer carries a
- Signed webhook payloads (#78, v0.11.0 item W3). With
OPENPULSE_WEBHOOK_SECRETor--webhook-secret-file(at least 32 characters), everycheck --webhookpost carriesX-OpenPulse-TimestampandX-OpenPulse-Signature: v1=<hex>, an HMAC-SHA256 over"<timestamp>." + bodyon 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, socriticalincludes OVERDUE. As with every gate, it also exits 1 on FAILED repository coverage. The reason goes to stderr, so--output jsonstays parseable. Without the flag,warningsonly reports, as before.- Persistent alert delivery state (#78, v0.11.0 item W1, C1).
core/delivery.pygives 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.
- Retry: a failed post is retried once in the run, then left