Context
CannObserv/archiver vendors a generated watcher_client SDK from Watcher's OpenAPI (clients/watcher-python/). When Watcher changes a response shape, the committed client silently goes stale until it fails at runtime — that was incident archiver#66 (default_content_type dropped → KeyError on every parse).
Archiver now has three defensive layers (archiver#67):
Why this issue (the preferred long-term replacement)
Polling is the fallback. The cleaner design is Watcher pushing on change: when Watcher's OpenAPI changes, fire a repository_dispatch to archiver, which opens the regen PR. Event-driven, no scheduled flake, no archiver↔Watcher uptime coupling. Archiver could then retire the Layer C timer.
That wasn't buildable from archiver's side because this repo currently has no CI workflows and commits no OpenAPI snapshot — so there's nothing to detect "the OpenAPI changed" against, and nothing to fire the dispatch.
Proposed work (in this repo)
- Commit an OpenAPI snapshot + a CI gate. Add a script that dumps Watcher's own
/openapi.json in-process (no server/DB, like archiver's scripts/dump_openapi.py), commit the snapshot, and add a CI job that fails if the regenerated spec differs from the committed one. This makes "the OpenAPI changed" a reviewable, detectable event.
- Dispatch on change. When the committed OpenAPI snapshot changes on
main, fire a repository_dispatch (e.g. event_type: watcher-openapi-changed) to CannObserv/archiver. Archiver adds a workflow that, on receipt, regenerates watcher_client from the new spec and opens a bump PR.
- Retire archiver's poll. Once the push path is proven, archiver removes the Layer C on-VM timer (
scripts/watcher_live_drift_pr.sh, ff_deploy_clone.sh, deploy/watcher-live-drift.*).
Notes
- Needs a token with
repository_dispatch permission on CannObserv/archiver, stored in this repo's Actions secrets.
- The OpenAPI dump in (1) is also independently useful as Watcher's own contract-of-record.
Refs: archiver#70 (Layer C poll this replaces) · archiver#68 (Layer B gate) · archiver#67 (parent) · archiver#66 (incident).
Context
CannObserv/archivervendors a generatedwatcher_clientSDK from Watcher's OpenAPI (clients/watcher-python/). When Watcher changes a response shape, the committed client silently goes stale until it fails at runtime — that was incident archiver#66 (default_content_typedropped →KeyErroron every parse).Archiver now has three defensive layers (archiver#67):
generated/== regen-from-committed-snapshot. Consistency-only — can't see the snapshot itself go stale vs live Watcher./openapi.jsondaily, and on drift regenerates + opens a PR on archiver. This closes the Notification system v2: Apprise integration, credential encryption, health-status transitions #66 gap but is a poll.Why this issue (the preferred long-term replacement)
Polling is the fallback. The cleaner design is Watcher pushing on change: when Watcher's OpenAPI changes, fire a
repository_dispatchto archiver, which opens the regen PR. Event-driven, no scheduled flake, no archiver↔Watcher uptime coupling. Archiver could then retire the Layer C timer.That wasn't buildable from archiver's side because this repo currently has no CI workflows and commits no OpenAPI snapshot — so there's nothing to detect "the OpenAPI changed" against, and nothing to fire the dispatch.
Proposed work (in this repo)
/openapi.jsonin-process (no server/DB, like archiver'sscripts/dump_openapi.py), commit the snapshot, and add a CI job that fails if the regenerated spec differs from the committed one. This makes "the OpenAPI changed" a reviewable, detectable event.main, fire arepository_dispatch(e.g.event_type: watcher-openapi-changed) toCannObserv/archiver. Archiver adds a workflow that, on receipt, regenerateswatcher_clientfrom the new spec and opens a bump PR.scripts/watcher_live_drift_pr.sh,ff_deploy_clone.sh,deploy/watcher-live-drift.*).Notes
repository_dispatchpermission onCannObserv/archiver, stored in this repo's Actions secrets.Refs: archiver#70 (Layer C poll this replaces) · archiver#68 (Layer B gate) · archiver#67 (parent) · archiver#66 (incident).