Releases: denniyahh/devflow
Release list
v2.12.0
Unattended auto-mode hardening (Phase 45). Makes --mode auto launchable and safe out of the box:
a configurable git-flow trunk so a phase worktree can fork from a personal .planning/-tracking
branch instead of hardcoded develop, a self-dogfood staleness check scoped to actual Cargo
workspace members, and a shared unattended-decision policy for the Code stage.
Added
- configurable base branch (
base_branchindevflow.toml, orDEVFLOW_BASE_BRANCH), resolved
once and shared by the worktree fork point, the merge target, and every later process
(advance/resume/the monitor/ship evidence) that needs to agree on the same trunk affects_compiled_binaryscoped to Cargo workspace members plus root build files (including
.cargo/config.toml) for DevFlow's own self-dogfood check, so.planning/spikes/no longer
trips a false stale-build block; every other project DevFlow drives keeps its prior repo-wide rule- a shared
CODE_STAGE_POLICYinstructing the agent to evaluate unattendeddecisioncheckpoints
on merit and record its reasoning, delivered to both agent-family Code-stage renderers on the
first pass
Fixed
- the base-branch substitution's blast radius:
cleanup_mergedcould delete the built-indevelop/
maintrunks under a configured base, and the resolved base was not persisted, so a later
resumein a fresh shell silently fell back todevelopfor the worktree base, the idle-timeout
commit evidence, and the Ship merge-evidence check — each fixed to consult the run's own recorded
base - a
devflow.tomlthat fails to parse no longer silently falls back to the default trunk; the
base-branch resolver's fail-hard contract now covers a bad file, not only a bad value
Known gap
- the unattended-decision policy does not yet reach the Claude/OpenCode loop-back Code prompt
(fix_prompt'sFullExecutearm), so a run that fails Validate and retries under that path gets
no policy while the Codex/Pi equivalent does. Confirmed in shipped source by an internal review
and two independent external reviews; accepted as this release's terminal state for that one path
rather than reopening the plan — tracked as backlog 999.115 and 999.116.
v2.11.0
Codex end-to-end verification (Phase 44). Proves devflow resume --phase N --agent codex
end-to-end through a real phase run and closes every gap the run and its reviews surfaced.
Added
devflow resume --phase N --agent <AGENT>handoff: preflights the candidate driver against the
full generic preflight bundle before persisting the new agent- per-phase cron-instruction lifecycle, with TOCTOU-safe deletion under concurrent consumers
- Hermes resume schedules as runnable UTC instants via a real
hermes cron createinvocation
Fixed
- quoting bug in the composite Hermes cron command string that broke (and, for a path containing
an apostrophe, made exploitable) project paths containing shell metacharacters consume_cron_instructions_preserves_unreadable_legacy_recordasserted achmod 0o000guarantee
that root — the container pre-push gate's default user — bypasses; now skips under root instead
of failing
v2.10.0
OpenCode driver completion (Phase 43). Completes the under-built AgentDriver for OpenCode: a
real headless launch, JSONL event-stream completion/verdict parsing, and fail-closed health and
subagent-dispatch capability probes.
Added
- OpenCode headless launch (
opencode run --auto --format json) and Layer-1 JSONL marker parsing - OpenCode error-event and torn-tail precedence in completion/verdict detection
- fail-closed credential health check for OpenCode
- fail-closed subagent-dispatch capability probe for OpenCode
Fixed
- close 4 fail-closed gaps in OpenCode health/capability probes found in Ship-stage review
- correct stale
cargo installOpenCode doctor hint
Changed
- workspace isolation, conventional commits hook, and release tooling
- automate sync branch and PR workflow for sync-main-to-develop
v2.9.0 - Hermes driver
2.9.0 — 2026-08-21
Added
- unlock unattended auto mode for Antigravity after dogfood validation
- implement Hermes driver, doctor probe, and transport tests
Fixed
- address review findings for Hermes driver, cadence docs, and delegation parser
Changed
- ship phase 42 — PR #137
- cargo fmt formatting adjustments
- add security threat model audit
- update code review and verification artifacts
- update generated docs
- record raw adversarial review transcripts from Claude, Codex, and DeepSeek
- document multi-model adversarial review findings and action items
- add verification and UAT evidence for Phase 42
- create phase plan
- record phase 42 context session
- capture phase context
v2.8.0 — Antigravity driver + test hygiene and dogfood validation
2.8.0 — 2026-08-21
Antigravity driver + test hygiene and dogfood validation (milestone v2.8.0). DevFlow adds the
AntigravityDriver for headless CLI automation (agy), closes the test monitor-leak hygiene
defect, and fixes container git worktree verification.
What's new
- Antigravity CLI Driver (Phase 41). Operator can select
--agent antigravity. Spawnsagy
headless via--input-format stream-json --output-format stream-json --print-timeout 60m.
Includes agent-aware transport handling (event-key first turn, top-level result marker close
predicate), agent-aware canary trust, and explicit ERROR-envelope failure parsing. - Unattended Safety Policy (Phase 41).
--mode autois refused for Antigravity in preflight
(C2 gate) until dogfooded through a supervised run. - Test Monitor Hygiene (HYG-01 / Phase 41). Integration tests in
phase7_clibind
MonitorReapGuardto settled state, with a suite registry and audit ensuring zero orphaned
monitor processes across test runs. - Container Parity for Git Worktrees (HYG-02 / Phase 41).
scripts/check-in-container.sh
bind-mounts worktree.gitfiles and commondir paths to pass cleanly under container root. - Pi Supervised Dogfood Run (Phase 40). Full supervised Define→Validate run completed
through--agent pi(closing 999.85 stale comments).
v2.7.0 — Pi end-to-end + driver contract completion
2.7.0 — 2026-08-18
Pi end-to-end (milestone v2.7.0). DevFlow finishes the AgentDriver migration and drives Pi
through the full pipeline. Phase 37.1 (research) returned a VIABLE verdict for the
@bacnh85/pi-subagent extension; Phase 38 removed the legacy AgentAdapter surface and wired
InteractivityMode; Phase 39 landed the Pi pipeline (provider-aware health, Legacy launch,
subagent dispatch).
What's new
- The legacy
AgentAdaptersurface is gone (999.106 / Phase 38).AgentAdapter,DriverShim,
adapter_for, and the four legacy*Agentstructs are removed; every call site resolves through
driver_for(kind) -> Box<dyn AgentDriver>. Claude's launch argv stays byte-identical (zero
regression). InteractivityModeis consumed (Phase 38). A driver-driven Define/Plan gate replaces the
hardcodedAgentKind::CodexDefine check.- Two Codex-parser defects fixed (999.107 / Phase 38). Terminal
turn.failednow takes
precedence over an earlier success marker; non-UTF-8 / hostile writable-root paths are refused
rather than lossily converted to U+FFFD. - Pi health probes the active provider (Phase 39).
PiDriver::healthreadssettings.json's
defaultProvider(falling back to Pi's--providerdefault) instead of a hardcodedgoogleor
"any readymodels.jsonprovider" — fixing both a false-reject of standard installs and a
false-green of a provider the launch never uses. - Pi is pinned to
MonitorLaunch::Legacy(Phase 39). A regression test asserts the
claude_stream_launch_enabled(Pi)precondition, so Pi can never land on thePipeOwningpath
whose stdin wire protocol deadlocks it. - Pi subagent dispatch is detected, not faked (Phase 39). Capability detection matches the
vetted@bacnh85/pi-subagentpackage specifically (not*subagent*), and a captured session
transcript proves the live dispatch (parenttoolCall: subagent→ nested subagentbash→
DEVFLOW_RESULT) completes underLegacywith no drain gate.
Deferred (recorded, not shipped here)
999.94— unattendeddecisioncheckpoint takes the first option blindly.- Isolated-context (process-spawning) Pi dispatch — needs a
--no-approvechild-argv patch.
v2.6.0 — multi-agent adapter migration
The multi-agent adapter migration (milestone v2.6.0). DevFlow stops rendering one shared
/gsd-* prompt for every agent and moves to a modular AgentDriver contract, where each agent
owns its prompt rendering, command building, completion parsing, and health. This fixes Codex
(which received GSD slash commands as literal shell strings) and registers Pi as a fourth,
selectable agent.
What's new
- Pi is a fourth, selectable agent (Phase 36).
AgentKind::Pi+PiAgentwith a
pi auth check-backed health check that distinguishes "installed" from "headless-capable"
(never env-var sniffing). Pi launches with--no-approve—--approvewould trust
project-local extensions that execute unsandboxed. - The release path signs deterministically (999.104 / Phase 36).
scripts/cut-release.sh
fails loudly beforetag -swhendevflow.releaseSigningKeyis unset or unreadable, instead of
silently signing with whateveruser.signingkeydefaults to. The capability-only
signing-viability probe is removed; the pre-push fingerprint hook — the only check that
distinguishes the maintainer key from the agent key — is retained. release --checkcatches a forgotten version bump (999.96 / Phase 36). A new row compares
CHANGELOG.md's top heading to the workspace version and reports NOT viable with direction.- A modular
AgentDrivercontract replacesAgentAdapter(Phase 37). Each driver owns its
prompt rendering, command building, completion parsing, health, and interactivity declaration,
instead of that logic being scattered acrossprompt.rs,agents/*.rs,agent_result.rs, and
preflight.rs. - Codex is fixed (999.31 / Phase 37). The
StageIntentde-Claude-ification removes the
literal/gsd-*slash commands from every stage prompt; Codex now receives a workflow-file
reference and the verified non-interactive approval flag (-a never, the global form). - Pi is the second native driver (Phase 37). Pi runs on
-pprint mode with the same
de-Claude-ified prompt; its end-to-end run (JSON unwrapper + monitorCloseRule) is a
follow-on (37.1/38). - A shared conformance suite (
test_contract()) (Phase 37). Every driver must pass it; a
deliberately-broken driver must fail it.InteractivityModeandDriverHealthare declared at
the driver layer.
Deferred (recorded, not shipped here)
AgentAdapterremoval +InteractivityModeconsumption (999.106).- Codex parser success-before-failure + writable-root serialization (999.107, pre-existing).
- Pi end-to-end (JSON unwrapper +
CloseRule) — 37.1/38.
v2.4.0 — Resume Unattended Dogfooding
Phase 33 (loop-back correctness for multi-wave Validate↔Code cycles) and phase 34 (stream-json
coverage, the Validate trust boundary, and Layer 0 in worktree mode). Together they close the
structural defects blocking unattended, multi-wave devflow start runs found during the phase
29 dogfood run and phase 31 planning, so unattended dogfooding can safely resume.
The headline is the Validate trust boundary (999.74): an agent that self-reports status: failed
alongside verdict: pass used to have that verdict grafted onto an otherwise-derived Success
result and advance to Ship unattended. reconcile_layer0_verdict now consults Layer 1's own status
before transplanting its verdict, and classify_validate_outcome was rewritten as an exhaustive
match naming all seven AgentStatus variants — an eighth is now a compile error, not a silent join.
The exploit was reproduced against the real cascade before the fix, with a matched positive control
proving the fix isn't indiscriminate.
What's new
- The Code↔Validate loop no longer false-gates on healthy work (999.66).
consecutive_failuresis measured from a persisted commit-count baseline instead of an unreset
counter, so a healthy 3+ wave phase no longer false-gates at wave 3 while a genuinely stuck loop
still reachesMAX_CONSECUTIVE_FAILURES. - Loop-back fix selection reads the worktree (999.65).
select_loop_back_fixreads
{N}-VERIFICATION.mdfrom the phase's worktree instead of the main checkout, making
FixType::GapsOnlyreachable on the Validate path in worktree mode for the first time. - All five stream-json stages joined the launch path on real evidence (999.73). Widened beyond
Stage::Codeagainst committed, PII-scrubbed production captures with per-stage drain analysis,
not a flag flip. The capture campaign refuted its own premise — zerobackground_tasks_changed
events across 1063 events despite 8 concurrent sub-agent dispatches — filed as a known gap rather
than absorbed; see Known Issues. - Layer 0 external verification works in worktree mode (999.76). Declaration discovery now
reads the execution root, so a correctly-declaredexternal_verifyprobe set no longer silently
never executes — the plan-28-03 checkpoint auto-decide path is fixed at the same call site.
Fixed
- Self-reported
status: failedpaired withverdict: passcould reach Ship unattended via the
Layer 0 verdict graft (999.74). Closed by gating the graft on Layer 1's own status. - The Validate classifier's status-position wildcard could discard a non-
Successstatus silently;
now everyAgentStatusvariant is named explicitly.
Known Issues
- The drain gate has not been observed to see sub-agent concurrency on Claude CLI 2.1.222 — the
safety mechanism the widened stream-json stages' unattended behavior depends on. Tracked as 999.83. - One call site in the worktree-mode checkpoint fix has no regression test. The fix is correct
by direct source read and by two root-sensitivity tests on the function it calls, but no test
drives the call site itself: reverting its argument leaves the full test suite green. Tracked as
999.84. - Two in-source comments (
idle_timeout_resultand a test-module comment) describe a mechanism
this release's own fixes replaced. Their conclusions are still correct; their stated reasoning
is not. Tracked as 999.85.
v2.3.0 — closes the 999.64 arc
Phase 30 (the stream-json parser and the feasibility gate) and phase 31 (the
launch path itself). Together they close the 999.64 arc: a DevFlow-driven
phase containing a multi-plan wave now completes that wave without orphaning
delegated work — the failure that had blocked every attempt at an unattended
run through this project's history. Phase 29 remains unmerged and is not in
this release.
The headline is phase 31: the Claude adapter's detached sh monitor is
replaced by a pipe-owning Rust monitor, and the adapter always launches with
--input-format stream-json --output-format stream-json, prompt delivered on
stdin rather than argv. Holding stdin open past the first turn is what lets a
background helper agent's completion actually reach the parent session — the
notification had nowhere to arrive before this. Verified with a live two-plan
acceptance run, not just integration tests: both plans produced a SUMMARY.md
and both merged, crossing the exact point where a prior attempt (phase 29)
orphaned both executors of a two-plan wave.
What's new
- Idle timeout. A stage that goes quiet for longer than a configurable,
floor-clamped window now fails loudly with a distinctAgentStatus::IdleTimeout,
naming the agent's commits and rolling nothing back — instead of hanging
indefinitely or having a later signal misread as an unrelated failure.
DEVFLOW_CLAUDE_IDLE_TIMEOUT_SECSraises the window above its floor; it can
never be set lower. - Startup delivery canary. Before a stream-json launch, DevFlow now
confirms the background-notification path actually works with one throwaway
task, and refuses to run rather than silently degrading if it doesn't. --legacy-claude-launch(andDEVFLOW_CLAUDE_LEGACY_LAUNCH) — an
explicit, off-by-default escape hatch back to the pre-31 launch path, loud on
every use, with no automatic fallback.- Exit-code arbitration: a stream-derived
Successcan no longer stand against
a contradicting non-zero process exit code.
Fixed
Two defects from this release's own peer code review, neither reachable by the
acceptance run because it only exercised healthy paths: an idle timeout could
fire and overwrite a stage that had already completed successfully, and a
single non-UTF-8 byte in agent output could silently truncate the capture.
Both are covered by mutation-tested regression tests.
v2.2.0 — Checkpoint Answer Return Path, Hermetic Git Invocation
Phase 27 (hermetic git invocation) and phase 28 (the checkpoint answer return
path). Phase 26 remains unmerged and is not in this release.
The headline is phase 28: a gate="blocking-human" checkpoint used to be a dead
end for any DevFlow-driven run. The agent stopped, asked a question, and no path
existed by which an answer could reach it — every retry spawned a fresh process
that asked the identical question again. A plan that correctly gated an
irreversible decision became a plan that could never finish unattended.
⚠ Behavior change: human-blocking checkpoints are now resolved by the agent
This is unconditional and there is no flag or config toggle to disable it.
When a stage halts at a task declared gate="blocking-human", DevFlow now
relaunches the exact exited Claude session with an instruction to decide the
checkpoint itself, and records what it decided.
This deliberately overrides checkpoints.md rule 6 (blocking-human is never
auto-approved, in any mode). It was adopted because DevFlow has no usable
notification or response channel — the only push mechanism is an
operator-supplied DEVFLOW_GATE_NOTIFY_CMD that is a silent no-op when unset,
and the only pull mechanism is running devflow status by hand. A "wait for a
human" default would not degrade gracefully; it would hang. Given that, a flag
implying a working "off" state would have been misleading.
What bounds it: the path is reachable only for a checkpoint that the
operator's own approved plan declared — the authorizing check is a static scan
of the phase's PLAN.md files, which an agent cannot influence at runtime, and
it is evaluated strictly before any agent-controlled signal. Resumes are capped
(MAX_CHECKPOINT_RESUMES), and exhaustion falls through to the existing
never-silent gate. Every auto-decision writes a checkpoint_auto_decided event
to .devflow/events.jsonl naming the session, the instruction, and the policy —
with no human in the loop beforehand, that record is the only way anyone learns
after the fact what the agent decided.
Fixed
- A
blocking-humancheckpoint no longer strands a headless run (999.57). DevFlow statically scans the stage's plans for a declared human-blocking task before launching, confirms from captured stdout that one actually fired, and resolves it by resuming the exact exited session rather than spawning a fresh one — no CONTEXT/RESEARCH re-read, no re-running completed tasks - The checkpoint confirmation reader now matches what a real run actually emits. The reader was built against a rendering predicted by reading the emitting source, and shipped with that caveat recorded rather than hidden. A live end-to-end run then showed the executor renders the value as a markdown code span —
**Gate:** `blocking-human`— and the matcher, which trimmed only*and whitespace, terminated on the backtick and produced an empty token. Genuine checkpoints fell through to the generic gate. The unit suite could not catch it: every fixture was built from the same prediction. Regression tests are now transcribed from the live capture. The reader's documented safe-direction property held throughout — a false negative degrades to the never-silent gate, so nothing was ever silently authorized devflow resumeno longer discards an unfired--untilcap (999.60). The stop-marker clear was unconditional, so a cap the operator set that had not yet fired was silently dropped and the pipeline ran past the stage they named — observed live during phase 27's own dogfood run, which advanced into Ship unguarded. The clear is now gated on the pipeline having actually stopped- The Define stage no longer invokes an interactive interview headlessly (999.59). When
CONTEXT.mdwas absent, Define issued/gsd-discuss-phase, which hangs onAskUserQuestionunderclaude -pwith no operator present. The branch is deleted rather than flag-gated: whether to run an interview is decided beforedevflow startis ever invoked, so there is no runtime accommodation to make - All 41 production
gitinvocations are hermetic (phase 27). EveryCommand::new("git")in production code now routes throughgit_command/hermetic_command, including two indirectsh → cargo → gitedges andmonitor.rs's spawn of the coding agent itself — the highest-consequence site, and one that was not in the original grep scope. Under a hostileGIT_DIRthe suite went from 37 deliberately-failing tests to 0
Added
yes_shipis settable indevflow.toml(and viaDEVFLOW_YES_SHIP), in addition to the existing--yes-shipflag. The flag ORs with the config value rather than replacing it, so passing it always wins. A run whose authorization came from config prints a line namingdevflow.tomlas the source — a standing default, but never a silent onecheckpoint_auto_decidedevents in.devflow/events.jsonl, emitted before the relaunch spawns so a crash mid-relaunch still leaves the decision recordeddevflow-corepublic API:verify::phase_plan_files,verify::phase_has_blocking_human_checkpoint,agent_result::{claude_session_id, session_id_from_capture, blocking_human_checkpoint_reported, checkpoint_reported_in_capture},config::yes_ship, andState::{session_id, checkpoint_resumes}
Changed
Stateis now#[non_exhaustive]. Downstream crates must construct it throughState::newand assign fields afterward, rather than by struct literal. Deserialization is unaffected — every#[serde(default)]field still loads state written by older binaries.Stategains a field roughly every phase that introduces a run-scoped concept; without this, each addition is a semver-breaking change for any consumer using a literal. Paying that cost once here makes every future field additive--yes-shipmay now come from configuration, deliberately reversing the phase 23 decision that made it per-run only "so a standing unattended auto-merge can never become the silent default." That decision's own stated cost applies: relaxing this is easy, re-tightening it after operators depend on the persisted setting is not. The never-silent notice above is the compensating control. What did not change: the Ship gate still fires and still records an explicit, attributed approval rather than being bypassedsession_idis read only from the result envelope's top-level key, never from the agent-authoredDEVFLOW_RESULTmarker, and is deliberately not a deserialized field onAgentResult— otherwise an agent could nominate which session DevFlow resumes into