Publishing¶
The publishing module turns a run's outcome into the places people actually look: issue trackers, documentation and chat. It's the last layer of the framework, and the one that decides whether automation saves time or just produces more reports nobody reads.
What was built, and what's design
The original framework published to an issue tracker (the tracking ticket and its bugs), object storage and test reports picked up by dashboards. The router, documentation sinks and chat hooks on this page are the design I'd build today on the same interface.
Events, not reports¶
Instead of producing one report at the end, the framework emits events as a run moves through its lifecycle:
| Event | When | Typical use |
|---|---|---|
run.started |
A trigger is accepted and provisioning begins | Open the tracking ticket, post a "running" message |
run.passed |
All checks passed and teardown has started | Update the ticket, propose a documentation change |
run.failed |
Any check failed and the run has paused | Open bugs, alert the owning team |
run.resumed |
Triage is done and teardown is resumed | Close the loop on the ticket and in chat |
run.destroyed |
Infrastructure is gone | Record final cost and duration |
Every event carries the same run result: the triggers, topology, resolved infrastructure details, check results and links to stored artefacts. Sinks pick the fields they need.
One interface per sink¶
Each destination implements a small interface, so adding Slack or swapping Jira for Linear is a new class, not a change to the framework:
class Publisher(Protocol):
name: str
def handles(self, event: RunEvent) -> bool: ...
def publish(self, event: RunEvent) -> PublishOutcome: ...
A publisher that fails (an API is down, a token has expired) returns a failed outcome. It never fails the run. The result is still in run storage, and the publish can be retried from there.
Routing rules¶
Which events go where is configuration, not code:
routes:
- on: [run.started, run.passed, run.failed, run.resumed]
to: tracker # Linear, Jira or GitHub Issues
- on: [run.failed]
to: chat
channel: "#platform-support"
- on: [run.passed]
to: docs # proposes a change; a person approves it
- on: ["*"]
to: storage # everything, always
Teams can subscribe to the topologies or triggers they own, without touching the framework.
The sinks¶
Issue tracker¶
The tracker stays the system of record: one ticket per run, and one linked bug per failing check. Linear, Jira and GitHub Issues all work through their APIs. The publisher updates a single ticket throughout the run, rather than opening a new one for each event.
Documentation¶
On run.passed, the documentation publisher proposes the change. It never publishes it:
- Confluence or Notion: update a draft of the support page, or a "pending listings" table, and notify the page owner.
- Docs as code: open a pull request against the docs repository that adds the new row to the support matrix, with links to the run's evidence.
The pull request route fits best with the matrix as data. If support states live in a structured file, the documentation publisher only changes data, and reviewing it is a normal code review.
Keep a human on the publish button
A support listing is a promise to customers. Automation gathers the evidence and drafts the change, and a person approves it.
Chat: Slack and Microsoft Teams¶
Chat is for attention, not records. Messages are short and link back to the ticket and the stored evidence:
- On failure: the failing checks, the topology and triggers, and links to the ticket and logs, posted to the owning team's channel.
- On pass: an optional one-line summary in a quieter channel.
- Actions: chat apps support interactive buttons, so a failure message can carry "Resume teardown" or "Keep for 24 hours". That turns the paused fail path into a one-click decision, instead of something left in a ticket.
Slack and Teams each start with incoming webhooks for one-way messages, and move to their app platforms when interactive actions are needed.
Run storage and dashboards¶
Everything goes to object storage, whatever the routing rules say. It's the source the other sinks link to, the place failed publishes are retried from, and the input for dashboards that show trends across runs.
What makes publishing worth it¶
| Useful | Noise |
|---|---|
| One ticket per run, updated as it progresses | A new ticket for every event |
| A chat message only when someone needs to act | A message for every passing run in a busy channel |
| A drafted doc change with linked evidence | A doc page edited by automation with no review |
| Links to stored artefacts | Pasted logs |