What's changed
This release contains no functional changes — behaviour is identical to v0.3. The focus is internal architecture: making the update path easier to test, reason about, and extend.
Update strategy cascade (#4, #5)
update_batch previously embedded three strategies (App Manager → Scripted REST → background script fallback) in a single function, returning a polymorphic dict whose keys varied depending on which path succeeded. Callers had to inspect the method field to know what they received.
The strategies are now split into three named functions — _try_app_manager, _try_scripted_rest, _try_script_fallback — each owning its own logic and failure mode. update_batch is a five-line orchestrator.
Results are now typed via an UpdateResult dataclass with a fixed set of fields. Every path returns the same shape.
As part of this work, _parse_tracker (which normalises the many shapes a tracker ID can take across ServiceNow API responses) is now applied consistently to both API paths. Previously the plugin_overseer path did its own narrower inline extraction.
The dead _batch_payload function (payload was always built inline in the old update_batch) has been removed.
Application factory and testability seam (#6)
app.py is now structured as an application factory (create_app). Routes receive a ServiceNowClient via an injected factory rather than constructing one from environment variables inline. This means routes can be exercised without a live ServiceNow instance:
app = create_app(client_factory=lambda: FakeClient())Environment variables (SN_INSTANCE, SN_USERNAME, SN_PASSWORD) are now validated at startup. A missing variable raises immediately rather than surfacing as a 500 on the first request.