v0.14.0
Ask the pipeline how it's doing ππ
Apiary has always recorded how its agents perform β timings, tokens, cost,
turns, failure kinds, a transcript of every session β and then only ever used
that to answer what happened to this task. This release adds the cross-cutting
question: across the last N runs, which steps waste money, which agent
instructions keep producing rework, and what should change.
apiary improve mines the history, has an agent reason over it, and emits an
evidence-backed report plus a validated diff over your config, workflows, souls
and skills β or applies it. Alongside it, apiary profile answers where a
step's minutes actually went, and three monitoring-source features that had been
sitting unreleased finally ship.
apiary improve
apiary improve # analyse; print findings and a diff
apiary improve --effort deep --since 30d
apiary improve --apply # write the accepted changes
apiary improve --dump-evidence # just the metrics, as JSON, no modelThe evidence pack is computed entirely in Go β no model is involved in
producing any number, so the same database and window always give the same
result, and --dump-evidence is a free diagnostic. It covers per-step rates,
percentiles, cost, cache reuse, prompt weight and failover; per-workflow terminal
states, cost per completed instance and rework loops (the same step running
twice in one instance β the signature of an on_fail/goto cycle, priced);
max_turns saturation; wait polling; clustered failures; and dead config that
never ran.
Every proposal passes a five-stage gate before you see it: path β applies
cleanly β the patched config parses and validates β expressions still lint β new
warnings surfaced. Patch application does no fuzzy matching: a hunk whose
context does not match is rejected, because a patch landing in the wrong place is
worse than one that does not land. Rejected proposals are shown with the reason,
since the observation behind them is often still worth acting on by hand.
Instruction files are labelled honestly. Souls and skills clear "the patch
applies" and nothing else β nothing in a markdown file can be validated
mechanically β and the diff says so per file. That is the common case, not the
edge case, which is why apiary improve effect exists: it recomputes the metrics
that justified an applied change and reports the delta. For a prose edit, those
numbers are the first evidence either way.
Applying leaves undo to git. No backups, no snapshots, no revert command β
git diff and git checkout do that better. What apply owes you is an accurate
account of what it touched, including which files nothing could check and whether
a daemon restart is needed.
Full documentation: Self-Improvement.
Where the minutes went
- Per-step wall-clock attribution (#399) β thinking, writing, tool waits and
background tasks recorded alongside the token columns, plus the slowest
individual calls. Newapiary profile <instance-id> [--json]. - A step that is 80% tool waits has a different problem from one that is 80%
thinking, and until now nothing distinguished them.
Monitoring sources
ack_via_silence(#362) β silence a Prometheus alert while an agent
investigates, so a firing alert does not page twice.interrupt_on_resolve(#362) β stop a still-running instance when the
alert that triggered it stops firing. Off by default: an investigation's
findings usually outlive the alert.dispatch_by: group(#362) β one task per Alertmanager group instead of
per alert.
Timeouts
settings.task_timeout was documented as defaulting to 30m in two places while
the code has always used 2h (#411). The docs are now correct. The default did
not change: implementation steps legitimately run for an hour, so a shorter
default would kill working pipelines rather than runaway ones.
That makes a total timeout a poor hang detector, so there is now a second bound:
settings:
task_timeout: 2h # total β the backstop
stall_timeout: 20m # no output at all for this long β the hang detectorstall_timeout measures silence rather than duration. A step streaming tool
calls for ninety minutes survives; one silent for twenty does not. It is
off by default β it only makes sense for runners that stream as they work,
and a runner buffering until exit would look permanently stalled.
Both bounds are now logged at step start, and a bound that fires is recorded as
a timeout naming the bound and the elapsed time, instead of a bare
signal: killed that was indistinguishable from a crash.
Fixes
- Transcript sampling read the wrong file β it picked each task's most
recently modified transcript rather than the one for the step being analysed,
so every hotspot got the final step's session. Excerpt budgets raised
accordingly (24 KB standard, 40 KB deep). - The thinking/writing split double-counted backgrounded calls (#399).
- Daemon test teardown is deterministic β dispatch goroutines are tracked, so
a prior test's workers can no longer race the next one. - The shipped example config validates again β
spec-decomposeused
materialize: sub_issueon a source that cannot host sub-issues.
Upgrading
No breaking changes. Two new optional settings β settings.improve and
settings.stall_timeout β both absent by default.
To use apiary improve you need an agent for it to run as: define one with id
improver, set settings.improve.agent, or pass --advisor / --runner +
--model on the command line. It errors rather than guessing, because model is
required per agent and there is no global default. --dump-evidence needs none
of that.
A note on maturity: the deterministic half of improve β the evidence pack, the
validation gate, the ledger β is well covered by tests. The advisor's judgement
has had little real-world exposure so far. Diff is the default and applying is
opt-in for that reason; read what it proposes before you take it.
Changelog
- 939d71d: docs(improve): document the self-improvement advisor (#407) (#418) (@orlandoburli)
- 8f7d284: feat(improve): --apply writes accepted changes to disk (#405) (#416) (@orlandoburli)
- f793008: feat(improve): advisor agent β reason over the evidence and report (#403) (#412) (@orlandoburli)
- a85902b: feat(improve): evidence pack β mine execution history into metrics (#402) (#408) (@orlandoburli)
- cb3ab4b: feat(improve): improvement ledger and effect measurement (#406) (#417) (@orlandoburli)
- 5d83174: feat(improve): patch parsing and the five-stage validation gate (#404) (#414) (@orlandoburli)
- dc53d02: feat(prometheus): ack_via_silence β silence an alert while an agent investigates (#362) (#397) (@orlandoburli)
- aa6b9ea: feat(prometheus): dispatch_by group β one task per Alertmanager group (#362) (#400) (@orlandoburli)
- a9da613: feat(sources): interrupt_on_resolve β stop a run whose alert resolved (#362) (#398) (@orlandoburli)
- 09aa1d7: feat: record and surface per-step wall-clock attribution (#399) (#409) (@orlandoburli)
- 66b872c: fix(daemon): track dispatch goroutines so test teardown is deterministic (#415) (@orlandoburli)
- a14b170: fix(examples): pin spec-decompose to a source that can host sub-issues (#420) (@orlandoburli)
- 5cf4f1a: fix(improve): sample the transcript of the step being analysed (#403) (#413) (@orlandoburli)
- fdd7d9b: fix(runner): correct the documented task_timeout and add a stall bound (#411) (#419) (@orlandoburli)
- 6d0c3c3: fix(runner): correct thinking/writing split and de-duplicate backgrounded calls (#399) (#410) (@orlandoburli)