v0.5.1 — surface-shrink release
Changelog - v0.5.1
Release Date
2026-06-03
Overview
v0.5.1 ships 8 surface-shrinking moves that close every v0.6 item flagged during the v0.5 series and rationalize the surfaces v0.5 introduced. Four parallel sub-agents (ALPHA, BETA, GAMMA, DELTA) landed cleanly via worktree isolation; codex caught 2 P1s in the merged result that fold-in rounds had missed; both fixed. Net: ~10 days of deferred work shipped in one cycle, reviewer-clean.
This release follows the "clean smaller surfaces" coding philosophy: prefer surface-shrink over feature-grow, replace mocks with concrete impls, collapse dual implementations, delete dead paths.
Architectural changes
Recording layer — production-grade replay (ALPHA, items 1+4+6)
- Real
DurableStoreClient. ReplacedMockDurableStorewithDbosDurableStoreClient— the production-facing impl that discovers thedurable_storeplugin viaorchestrator_plugin_host::discover_by_kind, runs the standard initialize handshake withproject_binding, and maps the fence trait todurable/begin_workflow_run/durable/begin_step/durable/commit_step/durable/abandon_step/durable/query_run.MockDurableStorerelegated to#[cfg(test)] mod test_doubles. The load-bearing differentiation is now live: durable workflow retries consume the prior decision instead of re-calling the model. - Out-of-boundary tool side-effect re-assertion (Option B). New
DecisionEvent::ToolSideEffect { name, assertion }variant with typedSideEffectAssertion::{FileExists, FileAbsent, Unknown}(#[serde(other)]onUnknownso v0.6 can add variants). Replay never re-executes side-effecting tools; instead,drive_replayasserts the recorded post-condition. Mismatches raiseReplayDivergenceError, surfaced to the operator asAgentRunEvent::Error. Preserves determinism while catching divergence. - Long-term decision-log compaction. New
sweepermodule: archives older than 24h getzstd-compressed to.jsonl.bak.zst; archives older than 7 days (configurable viaANIMUS_DECISION_LOG_EXPIRY_DAYS) get deleted.ReplaySource::opentransparently decompresses. Wired into the daemon'sstartup_cleanupblock — no new background task.
Reattach completeness — Unix + Windows on one abstraction (BETA, items 2+7)
decisions.jsonlauto-discovery on reattach.AgentSpawnRecordgainsdecisions_jsonl_path: Option<String>andlast_consumed_offset: u64(both#[serde(default)]for backward compatibility). Newreplay_gap_from_spawn_record(record, ...)auto-derives the path. The daemon pre-allocatesagent_session_idvia the newANIMUS_AGENT_RUN_IDenv var; older runners that ignore it still work — gap-replay degrades toOk(None).- Cross-platform reattach via
interprocess. Collapsed the cfg(unix)-only daemon socket code AND the runner's Unix-onlyset_write_timeouthack into ONE cross-platform implementation. Unix uses domain sockets; Windows uses named pipes; both round-trip throughlocal_socket_name_forwhich auto-detects via a path-separator predicate.try_reattachsignature changed from&Pathto&str.ReattachConnectionno longercfg(unix)-gated. - Bounded runner broadcast. Per-reader writer thread fed by a
mpsc::sync_channel(256). A wedged reader tripstry_send → Full → dropinstantly, never blocking the runner's emit path. Cross-platform replacement for the Unix-onlyset_write_timeouthack.
Queue protocol — head-of-line preemption (GAMMA, item 3)
animus-queue-protocolv0.3.0:QueueLeaseRequestgainsexclude_subjects: Option<Vec<SubjectId>>(backward-compatible defaultNone). The queue plugin skips entries whose subject is excluded; they stay inPendingfor a later lease.animus-queue-defaultv0.3.0: implements the filter; 4 new tests including a HoL regression guard.- ao-cli daemon: snapshots
process_manager.active_subject_ids()and passes them asexclude_subjectsat lease time. Deleted therelease_pending-as-HoL-workaround code path (~40 LOC removed).call_queue_release_pendingretained inplugin_clients.rsfor two real use cases: (1) within-batch duplicate subjects in one lease batch (differentworkflow_refs), (2) genuine "leased but cannot handle" scenarios.
Cleanups + setsid (DELTA, items 5+8)
config_contextsparse-override gap closed. Newbuiltin_runtime_config()OnceLockhelper; fallback wired intophase_capabilities,phase_output_contract,phase_decision_contract,phase_output_json_schema. Cross-contamination guards prevent customoutput_contractfrom inheriting builtinoutput_json_schemaand vice versa. The Phase B codex P2 deferral is closed.current_ao_commandsibling-binary lookup removed. Dropped the sibling discovery fromruntime_contract; the canonical surface is thememory_mcp_stdio_commandinit-extension (round-1 fold-in).ProcessManager::spawn_workflow_runnerexportsANIMUS_HOST_CLI_PATH=current_exe()so the daemon's direct-spawn path works without a sibling binary.- Manual-phase ignored test deleted.
approve_manual_phase_continues_non_terminal_workflow— its assertion exercised the deleted in-tree workflow runner; coverage moved to pack conformance. Test count regularized at 1917 (was 1900 v0.5.0, 11 ignored now from 14). - Full setsid hardening. New scoped
#[allow(unsafe_code)] set_session_id_on_spawnhelper inprocess_manager.rs(one function, ~10 LOC, one-line WHY comment). Replacesprocess_group(0)with the strongersetsid()— runner survives terminal hangups too, not just SIGTERM to the daemon pgid.libcdep added. New test asserts new POSIX session.
Codex round 1 P1 fixes (main loop)
daemon_task_dispatch.rs: GAMMA's within-batch dedupe deleted too much. Whenqueue/leasereturns multiple Pending entries for the same subject (differentworkflow_refs) in one batch, the duplicates shouldrelease_pending, notcompletion(CANCELLED). Fixed: within-batch duplicates now release back to Pending; old-plugin fallback tocompletion(CANCELLED)only whenrelease_pendingreturns method-not-found.dbos_client.rs: lock-inversion betweenshutdownandensure_workflow_run. Fixed: both functions now takehostBEFOREinit(consistent order); the init reset happens inside the host critical section so a racing step can't re-init on the old host before shutdown swaps it.
Codex round 3: 0 P1 / 0 P2 / 0 P3 on the merged + fixed v0.5.1 state.
Surface-shrink wins (deletes ranked by impact)
- ALPHA:
MockDurableStoremoved out of production API into#[cfg(test)]. - BETA:
cfg(unix)-only socket code ANDset_write_timeouthack collapsed into oneinterprocess-backed impl. - BETA:
build_recorddeleted (canonical constructor isbuild_record_with_decisions). - BETA:
try_reattachsignature&Path → &str— drops path-vs-name confusion. - GAMMA: ~40 LOC
release_pending-as-HoL-workaround DELETED from daemon dispatch (net −19 LOC in ao-cli for this item). - GAMMA: protocol change is one parameter extension on existing
QueueLeaseRequest, not a new method. - DELTA:
approve_manual_phase_continues_non_terminal_workflow(#[ignore]'d, dead test) DELETED. - DELTA: 2
TODO(codex-p2)markers DELETED (config_context+runtime_contract). - DELTA:
runtime_contract::current_ao_commandsibling-binary discovery DELETED.
Out-of-tree releases
animus-protocol→v0.5.3(commit6b114186) —animus-queue-protocol0.2.0 → 0.3.0 withexclude_subjects.animus-queue-default→v0.3.0(commit5f12e7db) — implementsexclude_subjectsfilter.
📊 Numbers
- Total LOC delta on main: +2978 / −878 across 25 files (most additions are tests + new modules).
- ao-cli tests: 1917 passing (was 1900 at v0.5.0), 6 pre-existing testkit-binary failures (unchanged), 11 ignored (was 14 — net −3 from DELTA's cleanup).
- Codex rounds: 3 rounds on the final merged main, ending 0 P1 / 0 P2 / 0 P3. (Sub-agents could not run codex inside worktrees; main loop ran it.)
- Out-of-tree plugin test totals: +4 in animus-queue-default (now 25), +2 in animus-queue-protocol (now 5).
- Surface-shrinks: 9 explicit deletes (see "Surface-shrink wins" above).
⚠️ Breaking changes
animus-queue-protocol0.2.0 → 0.3.0 — new optional field onQueueLeaseRequest. Backward-compatible at the wire level (old clients omitting the field work identically to today), but the type signature changed. Third-party plugins consuming the protocol must bump their dep.animus-queue-defaultv0.2.0 → v0.3.0 — install withanimus plugin install launchapp-dev/animus-queue-default@v0.3.0or rerunanimus plugin install-defaults. The daemon'sdefault-install.jsonpin is bumped automatically.AgentSpawnRecordgains two fields. Older record files (without the fields) parse cleanly via#[serde(default)]; older daemons reading newer records ignore unknown fields. The wire is forward-compatible.current_ao_commandsibling-binary fallback removed. Standalone plugin deployments that relied on a siblinganimusbinary now must use thememory_mcp_stdio_commandinit-extension or theANIMUS_HOST_CLI_PATHenv var (which the daemon sets automatically).
🚧 v0.6 roadmap (small carry-forwards)
- Live-socket
last_consumed_offsetwrite-back (BETA gap): events between gap-replay and crash get re-emitted on next reattach (duplicates per affected workflow). - Built-in tool classification for side-effect assertions (ALPHA gap): producers currently opt in per-call.
- Side-effect kinds beyond
file_exists/file_absent(process exit codes, network endpoint reachability, env-var equality). - Graceful per-orphan shutdown for
ReattachConnection(drop currently detaches but doesn't kill the reader thread). - DBOS step_query 3-RPC cold path consolidation (perf, not correctness).
- Built-in
archive_decision_logsiblings check for.bak.zstcollisions (low-probability re-archive case).
Upgrade
animus daemon stop
curl -fsSL https://raw.githubusercontent.com/launchapp-dev/animus-cli/main/scripts/install.sh | bash
animus plugin install-defaults --include-subjects --include-transports
animus daemon preflight
animus daemon start --autonomous