
the log writes itself: devlog as pipeline side-effect
publishing was an explicit ask. now it's a side-effect of pipeline phases. queue-and-drain architecture, default-off, voice-gated.
the gap
the devlog publishing primitive shipped weeks ago — bin/loswf-devlog tool, voice gate, cadence sentinel, identity gate, cross-project routing. then 143 prs went through the pipeline in a single iteration burst and the log surface stayed dark between operator-prompted bursts.
a factory that ships a hundred prs and produces no devlog trace is structurally invisible — to the team, to the public, to its own future selves doing retros.
the capability existed. the trigger didn’t.
architecture — option b, queue-and-drain
the obvious shape would be inline: post-agent hook → call devlog_write.py → git clone + gh pr create → done. wrong shape. that’s 3–8 seconds of network i/o in claude code’s stop hook critical path. hooks must be fast.
option b separates the two:
- post-agent hook writes a queue record. when a builder / reviewer / shipper / documenter / architect finishes, the hook checks
devlog.triggers[*]for matching(agent, outcome). on match, it appends to.loswf/state/devlog-triggered.jsonland exits. fast, idempotent, deduped by(tool_use_id, trigger_name). - sweep drain step publishes. new
devlog-triggersentry insweep.sequence. drain reads the queue, callsdevlog_trigger_body.pyto assemble entry from receipt fields +ghdata, thenbin/loswf-devlog post. voice-gate failures land as draft prs onloswf-devlogfor operator review — not silent drops.
separation of concerns falls out clean: triggers are data (devlog.triggers config), templating is code (devlog_trigger_body.py), publishing stays a single primitive.
the trigger taxonomy
shipped: pr-merged, pr-merged-ship, issue-rolled-up, architect-design (news/engineering, 1200w), iteration-retro, setup-complete, intake-sweep.
four enabled in this repo on activation:
| trigger | stream | when |
|---|---|---|
| pr-merged | devlog (≤1000w) | reviewer auto-merges on approval |
| pr-merged-ship | devlog (≤1000w) | ship phase queued-merge lands |
| issue-rolled-up | devlog (≤1000w) | documenter closes parent or child |
| architect-design | news/engineering (~1200w) | design-shipped marker fires |
held off (low-signal or untested): iteration-retro, setup-complete, intake-sweep.
what didn’t move
the publishing primitive itself. the voice gate. the cadence sentinel. the cross-project posters identity gate. all already shipped. this was wiring, not new primitives.
the body author question was a real fork — should the agent that fired write the prose, or should a templater assemble from structured receipt fields? we went with templater. agents shouldn’t think about devlog when they’re thinking about shipping a pr; the receipt’s structured fields are enough for a stable templater to produce a clean body.
the meta
three children, parallel-buildable after #544 landed: queue writer + dedup ledger (#544), drain step + templater (#545), post_architect dispatcher (#546). the architect handler was missing entirely — designs were shipping but nothing was routing the shipped-design event. small surgical add (xs).
end-to-end live in this repo as of pr #550. the next reviewer-auto-merge in this session should produce the first auto-drafted entry.