# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.12.1] - 2026-08-12

### Added

- Durable, authenticated, nonterminal `agent-control progress` reports with a
  project cursor, optional unit, summary, iteration, and timestamp. Workspace
  status exposes the current report while the provider is still running and
  labels a retained prior-iteration report explicitly.
- Optional strict workspace-manifest `packet.repo` and repository-relative
  `packet.path` bindings for concise first-run planning, verification, and
  service startup by prepared workspace id.
- Prepared workspace-id and unambiguous current-directory addressing across
  status, watch, stop, control, planning, running, and verification commands.

### Fixed

- Generated agent instructions no longer recommend the information-free
  `repeat --reason "work remains"` handoff. They require live progress reports
  and a terminal reason that names the completed cursor and exact next action.
- Workspace-aware CLI commands no longer interpret a prepared workspace id as
  a filesystem path, eliminating long manifest paths and fragile wrapped shell
  commands from normal operator use.

### Changed

- Workspace status JSON retains `last_action` and `last_reason` and adds the
  structured `agent_control.progress` record. Progress storage is separate
  from the first-wins terminal request, so repeated updates never consume or
  replace the terminal directive.
- Operator help and guides use concise, single-line workspace commands while
  retaining every explicit 0.12.0 manifest/packet form for compatibility.

## [0.12.0] - 2026-08-11

### Added

- Agent-controlled linear execution through iteration-scoped `continue`,
  `repeat`, `finish`, and `blocked` directives. Requests are authenticated to
  one run, prompt, and iteration; the first accepted directive wins.
- Packet-level `agent_control` configuration with a per-prompt iteration cap,
  declared default action, and deterministic `completion_verify` contract.
- Verifier-owned early finish. A premature `finish` request is rejected and
  its unmet completion items are supplied to a fresh prompt iteration; a
  passing request closes the selected linear sequence without duplicate prompt
  slots or a process kill.
- Completion preflight for agent-controlled packets. A non-explicit run whose
  packet-level completion contract already passes exits without launching a
  provider.
- The `agent_control.linear` installed capability and the
  `prompt_runner agent-control ...` CLI surface used inside provider sessions.
- A context-sensitive human workspace-status summary with prompt progress,
  relevant agent-loop or retry state, verifier result, provider activity, and
  service/process health. Iteration and attempt detail stays hidden when it is
  not meaningful.
- Ergonomic status addressing by prepared workspace id or unambiguous related
  current directory. Versioned operator-owned workspace references support both
  automatic and custom roots; explicit manifests and `--json` remain available
  for automation.

### Fixed

- A failed provider launch can no longer become a successful agent-controlled
  iteration merely because the prompt's ordinary repository contract already
  passed. Provider failure is recorded as failure and never reaches the
  directive/default-action decision.
- Structured verifier executables now resolve `@repo:name/path` references as
  documented, matching the existing logical-path resolution for argv entries.
- Agent-control request preparation now carries only the run-scoped values it
  consumes, and synced request-write failures are returned as typed errors.
- Agent directives written before an ordinary verifier failure are discarded
  before repair or retry, so stale control input cannot decide the repaired
  iteration.
- Agent-control configuration now participates in the durable packet
  fingerprint, preventing a resume from silently changing iteration or
  completion semantics.

### Changed

- `agent_session_manager` now requires `~> 0.15.0`.

## [0.11.0] - 2026-08-10

### Added

- Operator workspaces described by a strict versioned manifest. `workspace
  prepare` materializes full independent clones with ordinary operator-owned
  build/dependency trees; `workspace doctor` verifies ownership, remotes,
  toolchain pins, containment, capabilities, and installed contract artifacts
  without invoking Mix or a login shell.
- Installed, operator-owned escript execution and transient `systemd --user`
  service containment. `start`, `stop`, workspace `status`, and workspace
  `watch` use a deterministic service identity and prove the cgroup empty on
  stop instead of matching process names.
- Self-contained escript startup embeds erlexec's native port and materializes
  it into a version-, architecture-, and digest-addressed operator cache before
  OTP application startup; corrupt or non-regular cache entries fail closed.
- Immutable per-run state, packet fingerprints, persisted lower/upper selection
  fences, append-only sequenced journals, torn-tail quarantine, and widening
  rejection on resume.
- Explicit `--new-run` supersession for an intentionally upgraded packet. The
  prior run journal remains immutable and receives a `run_superseded` record;
  completed-prompt progress is preserved while the new run receives a fresh
  identity and content fingerprint.
- Durable control storage outside the packet checkout: atomic synced snapshots
  and requests, append-and-sync event/log streams, per-request outcome receipts,
  and non-destructive request handling when persistence fails.
- Dependency-aware prompt scheduling through `depends_on`. Independent work can
  continue under `continue_independent`; descendants of a failed dependency are
  recorded as blocked.
- Strict structured verifier argv with per-command cwd, environment, timeout,
  stdout assertions, typed fault exit codes, logical `@repo:` argv paths, and
  logical installed `@artifact:` executables.
- Native `doc`, YAML, JSON, glob, forbidden-source, and scoped repository-clean
  contracts. `regenerates` proves a command freshly produced non-empty outputs
  and restores prior artifacts when generation fails.
- `prompt_runner verify`, a read-only, no-provider contract execution command.
- Capability and version discovery, `--from`/`--through` selection bounds, and
  a contract migration task that rejects shell operators instead of translating
  them ambiguously.
- Workspace path rebinding across prompt and system text, preventing an
  operator session from following author-machine absolute paths into another
  user's checkout.
- Explicit one-time legacy completion import into an empty operator runtime,
  with prompt-id validation, active-run/existing-state refusal, and a
  digest-bearing import receipt. Failed or running legacy work is never
  promoted to completed.

### Fixed

- Workspace starts now read tracked packets and choose the default project
  directory from the operator-owned clone. Live control, steering records,
  run-local amendments, and contract inspection address the external workspace
  runtime through `--workspace`; none falls back to the author's checkout.
- Packet fingerprints are content-based and independent of an operator's clone
  path. Moving the same packet into an isolated workspace no longer looks like
  a packet mutation, while real prompt or contract changes still fail closed.
- `--version` and `-v` now use the version surface instead of falling through
  to unknown-command help, and JSON contract inspection serializes diff entries
  as structured objects instead of crashing on tuples.
- Workspace status now serializes containment-manager faults as structured
  JSON instead of crashing when an operator shell cannot reach its systemd user
  bus.
- Workspace acceptance samples and reports now encode violations as structured
  JSON objects. A required-running watch previously discovered a stopped or
  failed service correctly, then crashed while encoding the tuple
  `{:not_running, state}` instead of persisting the failure evidence. With
  `--json`, samples and the final report are now one compact JSON object per
  line, including the final unhealthy report before exit status 1.
- Run locks now bind PID plus `/proc` start identity and reject zombie holders.
- Runtime updates target only the newest running attempt, JSONL sinks always
  append, and run events never truncate when a run ID is reused.
- A failed prompt can no longer corrupt independent packet progress; verifier
  infrastructure faults remain distinct from failed work and spend no repair
  attempt.
- Document line counts are advisory rather than invented acceptance thresholds,
  while blank files and genuinely unresolved markers still fail.
- Repository push checks use the cached upstream by default and perform network
  access only when the contract explicitly requests it.
- Packet lint no longer rejects relative executables before the prompt that
  owns them has run. A missing relative executable after a session is failed
  work; a missing PATH command, absolute prerequisite, or installed artifact
  remains a verifier infrastructure fault.
- `plan` now resolves and prints the exact selector/dependency-ordered target
  set instead of always listing the whole packet, and `--from`/`--through`
  alone select their inclusive packet range without requiring `--all`.
- Provider sessions are owned by the caller, and terminal completion is no
  longer delayed behind backend teardown.
- Dirty operator clones are reported as resumable work rather than preventing a
  crashed run from restarting. Explicit workspace preparation still refuses to
  overwrite dirty clones.

### Changed

- `agent_session_manager` now requires `~> 0.14.0` and
  `execution_plane_process` requires `~> 0.3.0`. `cli_subprocess_core` now
  requires `~> 0.7.0`, whose embedded model catalogs make the installed
  Prompt Runner escript independent of source-checkout `priv` paths.
- The packaged capability file is now the compile-time source of the runtime
  capability API, eliminating the two lists' drift.

## [0.10.0] - 2026-08-10

### Added

- `--keep-going` for packets whose prompts are independent: prompt-local
  failures are collected and reported after the remaining selected prompts
  have been attempted. Fail-fast remains the default.
- A packaged `priv/capabilities` manifest for launchers to inspect without
  compiling or executing the runner.

- `PromptRunner.Control`: a process-addressable API for watching a live run and
  changing how it renders, without attaching to the session.
  `current_run/1`, `snapshot/1`, `set_view/3`, `subscribe/3`, `unsubscribe/2`,
  and `log/1`. A `run_ref` is `{packet_dir, run_id}` — explicit ids rather than
  an implicit "current run", which costs nothing now and avoids a rewrite if
  the runner ever goes concurrent. Reads never touch the session, so polling
  cannot slow, block, or crash the run being watched.
- The control directory, `packet/.prompt_runner/control/`: `requests/` (one
  file per command, consumed and deleted), `log.jsonl` (append-only: every
  command, who, when, outcome, including refusals), `snapshot.json` (rewritten
  at each event boundary, written-and-renamed so a reader never sees half of
  one), and `events.jsonl` (the canonical stream a subscriber follows). A
  directory rather than a socket: no daemon, no port, works under `tee`,
  `nohup`, and tmux, survives the runner dying, and is per-packet so two
  programs cannot cross wires. Nothing arriving through it is ever fatal to a
  run — a malformed file, an unknown command, or a request naming a run that
  has since ended is logged with its reason, deleted, and stepped over.
  A run that keeps no state on disk writes no control directory at all.
- `mix prompt_runner control status|view|log|events`, written entirely against
  `PromptRunner.Control`.
- Live view settings, changeable mid-run at an event boundary: `log_mode`,
  `tool_output`, `thinking`, and `diff`, as `--thinking` / `--diff` launch
  flags and through `control view`. `log_mode` swaps the renderer outright.
- `c:PromptRunner.Rendering.Renderer.set_view/2`, an optional callback, and a
  `:boundary` hook on `PromptRunner.Rendering.stream/2`. Requests are consumed
  between events and never inside one, because a view that changed halfway
  through rendering an event produces output belonging to neither setting.
- `examples/watch_run.exs`: a consumer that is not the CLI, driving a live run
  through `PromptRunner.Control` alone.
- Steering: `PromptRunner.Control.steer/3` and
  `mix prompt_runner control steer`. Steering changes *how* the agent
  works toward an unchanged definition of done — the verify contract is
  untouched, which is what makes it safe to allow freely.

  Which mechanism a steer uses is the provider profile's own transport fact.
  No current shipped profile declares incremental input after startup, so the
  turn is interrupted and the same provider thread resumes with the steer as
  its next prompt — same thread, full context, nothing re-derived.

  A steer has its own budget, `recovery.max_steers` (default 3), per prompt per
  run. It never consumes and never resets `retry.max_attempts` or
  `repair.max_attempts`: those bound the run's own attempts to satisfy a
  contract, and a steer is not one of those. Exhausting it is a logged refusal,
  not a run failure.

  A steer is never evidence — a contract asserting a document contains X is not
  satisfied by a human having said "put X in the doc" — and is always recorded,
  both on the control log and as an append-only artifact at
  `packet/.prompt_runner/interventions/<prompt>.jsonl` that is committed with
  the work. The prompt's result records `steered` and `steer_count`.

- `c:PromptRunner.LLM.steer/3`, so steering goes through the same seam as
  streaming rather than reaching past it into `PromptRunner.Session`.
- Diff rendering, as the `diff` view setting (`none` | `stat` | `full`,
  default `stat`) and the `--diff` flag. A Claude `Edit` or `Write` carries its
  own patch, so it renders from the event alone — no filesystem read, correct
  even if the file changed again afterwards. A Codex `file_change` or an
  Antigravity `replace_file_content` carries only a path, so `:full` renders a
  `git diff` explicitly labelled as the file's *current state*, never as that
  call's change. Truncation is always marked: a truncated diff that looks
  complete is worse than no diff.
- Amendment: `PromptRunner.Control.amend/3`, `relax/3`, and `contract/2`, with
  `mix prompt_runner control amend|relax|contract`. An amendment changes a
  prompt's verify contract — the one capability here that can make a completed
  prompt mean something other than what the packet says, so it is governed more
  tightly than steering, which cannot.

  Timing is part of the meaning: an amendment before any verify attempt is
  recorded `pre_verify`, the same amendment after a verify failure is recorded
  `post_failure`, and one after a verify pass is `post_success` — also after
  the fact, and named honestly rather than folded into either of the others. Claims here are pre-registered by commit timestamp
  precisely so nobody can decide what they were proving after seeing the
  result, and weakening a contract after a failure is the move that exists to
  prevent. It is not forbidden; it is never quiet.

  Asymmetric by design: adding a requirement is `amend`, removing one is
  `relax` with an explicit `--confirm` — a different verb, never a different
  argument. `--reason` is mandatory on both and refused when absent.

  Run-local by default: the packet file stays authoritative and a re-run from
  clean state uses its contract. `--persist` writes back, as a separate
  explicit act. A prompt completed under an amended contract records `amended`,
  the amendments, and the path to their log, so "completed" stays traceable to
  the contract actually enforced.
- `mix prompt_runner run PACKET --remaining`, and `remaining: true` on
  `PromptRunner.run/2`.
  Runs every prompt whose recorded status is not `completed`, in order,
  including prompts earlier than the furthest one that finished. `--continue`
  resumes from `last_completed + 1`, so a packet whose 03 failed while 04
  succeeded resumed at 05 and stepped over 03 in silence; both live packets
  carried a `remaining_prompts()` bash function to work around it. A prompt with
  no record is remaining. A missing store is a new run; an existing unreadable
  or malformed store fails closed instead of turning a resume into a full
  rerun. When `--remaining` selects nothing, the run says so.
- Pre-flight verification. Under `--remaining`, a prompt's verify contract is
  evaluated before the provider is invoked; one that already passes is marked
  completed with no session and records `session_ran: false` and
  `source: "preflight_verify"`. A contract with no evaluable clause, and one
  containing `changed_paths_only`, are never pre-flighted — both would pass
  vacuously. `--verify-first` / `--no-verify-first` state it explicitly;
  naming a prompt id is a request to run it, so pre-flight is off for explicit
  ids by default.
- `verify_command_missing_path` in `packet lint`: a `commands:` entry naming a
  script that does not exist under the directory the verifier will run it in.
- `prompts: ["02", "03"]` on `PromptRunner.run/2`. The CLI passes explicit ids
  positionally; an embedded caller has no argv, so running a named subset
  previously required reaching into the runner internals. Integers and unpadded
  ids are accepted.

### Changed

- `PromptRunner.Config` groups its four view settings under a single `:view`
  field. `config.log_mode` becomes `PromptRunner.Config.view(config).log_mode`.
  They are one concept — the control plane sets exactly these and reports
  exactly these back — and the struct was over the field-count limit.
- Claude runs now show thinking, because `cli_subprocess_core` stopped
  swallowing it. `--thinking hide`, or `control view --thinking hide`, turns it
  off. The event still reaches every sink; only the human-facing render drops
  it.
- `--continue` now names the prompts it is stepping over and points at
  `--remaining`. Its selection is unchanged — it is a documented behaviour and
  some callers want it.
- A repair attempt's failure block is rendered as labelled fields and the
  command's own output rather than `inspect/1` of an Elixir map, capped at a
  line budget with an explicit truncation marker.
- A packet run owns an exclusive pidfile lock. A second run cannot overwrite
  the first run's identity, cleanup cannot remove a successor's lock, and Linux
  process start time prevents a recycled PID from reporting a dead run as UP.
- `--keep-going` stops immediately on verifier infrastructure faults while
  continuing past prompt-local outcomes, avoiding the same broken toolchain or
  command being retried across an entire packet.

### Fixed

- Reject unknown run and plan switches instead of silently discarding them.
- Make `--remaining` and `--continue` fail closed when an existing progress
  store is unreadable, empty, or malformed; a missing store still means a new
  run.
- Honor the configured repair attempt budget instead of hard-stopping after
  the first failed repair.
- Run verifier commands in a non-login shell so the explicitly supplied runner
  environment survives into contracts.
- Do not pre-flight-complete a prompt merely because its named evidence exists
  when that evidence is still uncommitted. Unrelated changes elsewhere in a
  shared repository remain outside that decision.
- A failed or crashing committer no longer marks a verified prompt completed;
  it records `commit_failed` and leaves the prompt eligible for recovery.
- Remove the advertised pause command. It interrupted a turn and immediately
  auto-resumed it, so it never established a paused state or an honest resume
  protocol.

- A steer resumes the work rather than ending it. The steer text alone became
  the whole resume prompt, so a thread resumed with "write the numbers as
  digits" heard a complete instruction, said "got it", and finished its turn —
  a live run stopped six numbers into a twelve-number task. The resume now
  carries the steer *and* the instruction to carry on, because steering changes
  how the agent works toward an unchanged definition of done.
- A guardrail record reaches the control log, not only the session log. Phase
  A's acceptance asked for it there; when Phase A landed there was no control
  log to put it in.
- A failed tool call no longer renders as a successful one. The studio renderer
  ignored the event type: a `:tool_call_failed` carries `is_error: true` and
  usually no `status` or `exit_code`, so a failed edit came out as
  `✗ Edited lib/a.ex` — the icon saying failure and the verb saying success —
  and a failed call with no `exit_code` at all came out with a success icon.
  The event type is now the first authority, and `Edit`, `Write`, and `Read`
  render a failure as a failure.
- A verifier that cannot run is no longer read as failed work. Exit 126 and 127
  are classified `verifier_fault` and exit 124 `verifier_timeout`, distinct from
  a contract failure. A fault halts the run naming the command and its cwd,
  does not mark the prompt failed, and does not spend a repair attempt — it is
  not evidence about the work in either direction. A contract kept referencing
  `bin/check_doc.sh` after those scripts moved one directory down; every clause
  exited 127 and a finished attempt was discarded.
- A tool call outside a non-empty `allowed_tools` no longer fails a run on a
  lane that only observes tools after they have executed. `agent_session_manager`
  records the allowlist miss on the event instead, and the runner writes it to
  the session log as a `GUARDRAIL` line. Requires `agent_session_manager` with
  lane-aware `Execution.PolicyPlug`.

## [0.9.1] - 2026-08-10

### Fixed

- Make the published package loadable by any consumer. `mix.exs` required
  `build_support/dependency_sources.exs` and the package shipped that build
  tooling, so a consumer either failed to load the project or received git
  dependencies instead of Hex ones. The tooling is no longer packaged, and
  `mix.exs` detects a source checkout by that file's presence. 0.9.0 is retired.

## [0.9.0] - 2026-08-10

Shaped by a thirty-session unattended program built on 0.8.1. Every addition
here is something that program had to build for itself in shell, and every fix
is something it hit in the field.

**If you are on 0.8.1 or earlier and run packets unattended, read the first
entry under Fixed before anything else.** A prompt that could not satisfy its
verify contract re-invoked the provider without bound, and the cost of that is
paid in provider tokens for as long as nobody is watching.

### Added

- `mix prompt_runner packet lint [DIR]`, a static authoring-hazard gate and the
  sibling of `packet doctor`. Doctor reports authoring *gaps*; lint reports
  authoring *hazards* — constructs that load cleanly, run, and produce a wrong
  answer without ever raising. Errors: a prompt id that does not match its
  filename's numeric prefix (ordering comes from the filename, so a mismatch
  silently reorders the run), a filename with no numeric prefix, duplicate
  prompt ids, an unknown repo in `targets:` or in a verify entry's `repo:`,
  legacy `@group` syntax in `targets:` (repo groups are never expanded for
  packets), and an unrecognized `verify:` clause. Warnings: a `verify.commands`
  entry not wrapped in `timeout`, a prompt with no `verify` contract, a
  contract with neither a `commands:` entry nor any content assertion, any use
  of `changed_paths_only` (it only sees uncommitted work, so it passes
  vacuously wherever the session commits for itself), and the four inert
  front-matter keys. `--strict` promotes warnings to errors and `--json` emits
  a machine-readable report. The known-clause list is read from
  `PromptRunner.Verifier.contract_keys/0` so lint and the verifier cannot
  drift.
- The `doc:` verify clause, an artifact-quality gate. `files_exist` is
  satisfied by a three-line stub, so a prompt whose deliverable is a written
  document had no way to assert the document was written. `doc:` asserts a
  non-blank line floor (`min_lines`), verbatim `requires_sections`, and the
  absence of `forbids_markers` (`TODO`/`TBD`/`FIXME`/`XXX` by default; an
  explicit empty list opts out, a custom list replaces the default).
- The `repos_clean:` verify clause, which asserts that sessions committed their
  own work. Under `--no-commit` the runner's committer never runs and
  `changed_paths_only` passes vacuously, because `git status --porcelain` is
  empty precisely because the session committed. `pushed: true` additionally
  requires an upstream and compares `HEAD` to it — a missing upstream is a
  failure, since the clause was asked to assert publication and cannot.
  `pushed: false` (the default) treats an absent upstream as fine. The upstream
  comparison asks the remote with `git ls-remote` under a bounded timeout
  (`remote_timeout_ms`, default 90s) rather than fetching: `ls-remote` is a
  pure query and does not create or move remote-tracking refs, so the clause
  writes nothing at all into the repository it is judging. When the remote
  cannot be reached, `details:` says so and the comparison falls back to the
  cached remote-tracking ref, which can only be behind the remote and never
  ahead of it — the safe direction for a clause asserting publication.
- `mix prompt_runner watch [DIR]`, supervision for a long unattended run. The
  quiet-time scan skips `.git`, `_build`, `deps`, and `node_modules`, whose
  mtimes are derived output and say nothing about session progress while
  dominating the walk on a large repository. One compact line per interval:
  `WATCH 16:57Z runner=UP prompt=11 quiet=0min repos=3 dirty=0 commits=27`.
  `--interval SECONDS` (default 900), `--once`, and `--json`.
  `PromptRunner.Watch.sample/2` is public for host applications with their own
  monitoring.
- A run pid file. Any run with a file-backed state directory writes
  `.prompt_runner/run.pid` for its duration and removes it on exit, including
  on failure, so liveness can be checked by signalling a pid. `watch` uses it
  rather than a process-name match: such a pattern matches any command line
  containing it, including the supervisor's own shell, and reports a live run
  forever.
- `--dry-run` on `run`. The runner has honoured `opts[:dry_run]` since the
  packet rewrite, but no CLI switch ever set it, so
  `prompt_runner run <packet> --dry-run` dropped the unknown flag and started a
  real provider session.
- `PromptRunner.Verifier.contract_keys/0`, and read-only repository inspection
  in `PromptRunner.Git` (`worktree?/1`, `status_lines/1`, `commit_count/1`,
  `upstream_ref/1`, `fetch/3`) shared by the verifier and `watch`.
- Two guides: [Packet Linting](guides/linting.md) and
  [Supervising A Long Run](guides/supervision.md).

### Fixed

- **The studio renderer showed nothing at all for a provider that does not
  stream.** It discarded `message_received` outright, which is right for a
  provider that already streamed the same text as deltas and would otherwise
  print it twice, and wrong for one that only ever delivers a message whole.
  Codex does the latter, so a Codex run rendered its header and then stayed
  blank for the rest of the session while the provider was working normally.
  The test is now what actually reached the screen rather than which provider
  sent it; reasoning text is displayed but does not count as having shown the
  message. Only the studio renderer was affected — compact and verbose have
  always rendered `message_received`.
- **Every run opened with `unknown session started`.** The model is named on
  the launched argv, but nothing carried it into the `run_started` event:
  provider metadata is populated only once a provider announces its session,
  which is after the header has already printed. The header now falls back to
  the argv, and reports reasoning effort alongside the model where the run
  requested one. `confirmed_model` deliberately does not use that fallback —
  the `cli_confirmation` gate judges what the provider echoed back, and reading
  the argv would let it confirm a request against itself. Where a provider
  genuinely names no model, the header now says so rather than printing
  `unknown` where a model name belongs.
- `run PACKET 01 02 03` now runs **every** listed prompt, in the order given.
  `build_targets/3` returned `[hd(remaining)]`, so it ran only the first, exited
  `0`, and reported success for the work it had discarded — while `guides/cli.md`
  has documented the multi-prompt form since the CLI existed. This is the most
  dangerous shape of defect for an unattended program: a mid-run resume with the
  nineteen remaining prompt ids executed one of them and stopped cleanly, with
  nothing in the log to say the other eighteen had been dropped. A prompt id that
  names no prompt is now an error rather than a silent no-op, on the same
  reasoning — a typo in a resume list should stop the run, not shorten it.
- **A prompt that could not satisfy its verify contract repaired forever.**
  This is the most operationally serious item in the release.

  `RecoveryPolicy.final_action/5` returns `{:verification_failed, ...}` when
  repair is *not* available — repair disabled, its attempts spent, or the
  attempt that just failed was itself a repair. The runner routed that outcome
  through the same function as `{:repair, ...}`, which returns `{:repair, ...}`,
  and the outcome handler reacts to `{:repair, ...}` by starting another repair
  attempt. That attempt ran in `:repair` mode, took the same branch, and
  started another one.

  *Exposure:* every release through 0.8.1, on the default recovery policy, with
  no configuration required to trigger it. Any prompt whose contract simply
  cannot be met — a typo'd path in `files_exist`, a deliverable the model will
  not produce, a verify command that can never exit zero — re-invoked the
  provider without bound. Unattended, that continues until someone notices;
  with a live provider the cost is real tokens per iteration, at the packet's
  own model and permission mode.

  *It did not look like a loop.* Each pass printed an ordinary
  `Verification failed for prompt NN` and started over, so the terminal showed
  a plausible retry cadence rather than a runaway. The clearest signal was
  indirect: the `attempts` list in `.prompt_runner/state.json` grew without
  bound, every entry after the first carrying `"mode": "repair"`, until
  re-encoding that file became the slowest operation in the run.

  *Detection on an affected version:*
  `jq '.prompts["01"].attempts | length' .prompt_runner/state.json` climbing
  past `1 + recovery.repair.max_attempts` means the loop is running.

  `{:verification_failed, ...}` is now terminal: the run stops and reports the
  unmet items. The two outcomes share only the state recording they
  legitimately have in common, and the reported failure names the failing items
  instead of inspecting the whole verifier report:

  ```text
  ERROR: verification failed: file_exists docs/report.md: missing
  ```

- Call options were silently discarded by packet metadata. `Plan.merged_opts/2`
  normalized option keys once, *after* merging every layer, so
  `%{"provider" => "claude"}` from a packet manifest and
  `%{provider: "simulated"}` from the call site both survived the deep merge as
  distinct keys, and normalization-after-merge let whichever it visited last
  win — by Erlang term order, the string one. Every CLI and API override was
  affected whenever the packet set the same key, and the failure pointed in the
  worst possible direction: a run intended for the simulated provider started
  the packet's live provider, at the packet's model, permission mode, and
  system prompt. Each layer is now normalized before it is merged.
- The ASM run deadline is derived from `timeout`. `ASM.Run.State` defaults
  `:run_deadline_ms` to 600_000 — a total wall-clock budget for the whole run,
  armed independently of the stream and transport timeouts — and Prompt Runner
  never set it. A packet that deliberately left `timeout` unset got seven days
  on the stream and transport bounds and ten minutes on the run, and every
  prompt doing more than a few minutes of work died with a
  `provider_runtime_claim` naming a deadline nothing had configured, after the
  model had already done the work and often after it had committed it. All four
  bounds now derive from `resolve_effective_timeout_ms/1`: an explicit
  `timeout` bounds the run, and an absent one means the seven-day emergency
  bound.
- `prompt_runner plan` reported the packet's provider regardless of overrides.
  `CLI.run_plan/1` parsed no flags and passed none to `PromptRunner.plan/2`, so
  the natural way to check an override before spending a session answered about
  a different plan than `run` would build. `plan` and `run` now share one switch
  list.

### Changed

- The built-in templates and `prompt new` no longer scaffold `references`,
  `required_reading`, `context_files`, or `depends_on`. None of them is read at
  runtime — they are parsed, stored on `PromptRunner.Prompt`, and never sent to
  the provider or used for ordering — so the first prompt anyone scaffolded
  taught them to record required reading in a field the model never sees. The
  `## Required Reading` body section stays, since the body is what reaches the
  model, and the `verify:` skeleton now offers `commands:` in place of
  `changed_paths_only:`. Prompts that still carry the keys keep working; lint
  reports them.
- Full documentation refresh for 0.9.0 across `README.md` and every guide,
  including the run-deadline behaviour, the difference between
  `changed_paths_only` and `repos_clean`, and why the terminal event counters
  are not evidence that a session did work.
- Release preparation now asserts that every documentation extra registered in
  `mix.exs` exists on disk and is grouped, and that every guide on disk is
  registered.

## [0.8.1] - 2026-08-03

### Changed

- Default live-provider models are now `gpt-5.6-luna` for Codex and `haiku`
  (Haiku 4.5) for Claude, across every default and every path that reaches a
  live provider:
  - the `codex-default` profile created by `mix prompt_runner init`
    (`gpt-5.4` -> `gpt-5.6-luna`, `reasoning_effort: xhigh` unchanged)
  - the legacy-interface fallback model (`claude-sonnet-4-6` -> `haiku`)
  - `examples/single_repo_packet` and `examples/multi_repo_packet`
    (`gpt-5.4-mini` -> `gpt-5.6-luna`)
  - `examples/claude_packet` (`sonnet` -> `haiku`)
  - the CLI help text and every guide/README sample
  The simulated packets are unaffected; they never contact a provider.
  Note that `haiku` is the shared model registry's id for Haiku 4.5 —
  `haiku-4.5` is not a registered id or alias and is rejected as an unknown
  model.

### Fixed

- Generated artifacts no longer carry a stale hardcoded version. The
  `run_prompts.exs` scaffold emitted `{:prompt_runner_sdk, "~> 0.7.0"}`, and
  both the CLI help banner and newly created packet manifests announced
  `Prompt Runner 0.7.0`, on a 0.8.0 install. Scaffolding a project from 0.8.0
  therefore produced a dependency entry one minor version behind.

### Changed

- `PromptRunner.version/0` is now the single source of truth for every version
  string Prompt Runner emits. It is baked in at compile time from `mix.exs`,
  and the CLI banner, generated packet manifests, and the scaffolded install
  entry all interpolate it.
- Added a release-preparation regression test that fails when any file under
  `lib/` hardcodes a release version, so this class of drift cannot ship again.

## [0.8.0] - 2026-08-03

### Added

- A live Claude provider example, `examples/claude_packet/`. It runs the same
  two prompts as `examples/single_repo_packet/` (Codex), so the pair is the
  shortest demonstration that a packet is provider-portable. Both were
  verified against their real CLIs for this release.
- The normalized common option surface introduced by
  `agent_session_manager` 0.11/0.12, accepted in every provider option map and
  gated at runtime by the provider's common-feature manifest:
  - `allow_unknown_model`
  - `completion_only`
  - `output_schema`
  - `transport_headless_timeout_ms`
- `reasoning_effort` for Claude, which resolves through the shared model
  registry the same way Codex's does. `include_thinking` remains the separate
  control for thinking output.
- The Codex app-server option surface in `codex_opts`: `app_server`,
  `host_tools`, `dynamic_tools`, and `reviewed_approval`.
- `mix deps.sources`, plus a `publish_preflight` regression test that fails
  when a committed Hex constraint cannot admit the sibling checkout it was
  developed against.

### Changed

- Moved dependency resolution onto the shared
  `build_support/dependency_sources.exs` helper (v7) vendored by the rest of
  this stack, replacing the bespoke `local_dev_or_hex_dep` selection in
  `mix.exs`. Sibling checkouts still win automatically, and a gitignored
  `.dependency_sources.local.exs` can now force Hex resolution — which is what
  makes `mix deps.get` fetch the packages that `mix hex.publish` requires.
- Raised the runtime floor to `agent_session_manager ~> 0.12.1` and
  `cli_subprocess_core ~> 0.4.1`. `cli_subprocess_core` is now a declared
  dependency rather than a local-only override, matching the fact that
  `PromptRunner.Session` consumes `CliSubprocessCore.Payload` directly.
- Refreshed the remaining dependencies: `ex_doc ~> 0.40.3`,
  `yaml_elixir ~> 2.12`, `mox ~> 1.2`, and `credo ~> 1.7.19`. The Credo floor
  is deliberate: 1.7.16 and earlier crash tokenizing Elixir 1.20 sigils.
- `output_schema` is no longer a Codex-local option. It is a normalized option
  gated by the `structured_output` capability, so Claude can request it too.
- Permission-mode validation is documented as capability-derived rather than
  hardcoded per provider, since the supported set moves with ASM.

### Fixed

- `mix hex.publish` could not run from a workspace checkout. `mix.lock` still
  pinned the stale `agent_session_manager 0.10.0` entry — which requires
  `cursor_cli_sdk`, a dependency ASM 0.12 dropped — and nothing in the old
  dependency selection could fetch the Hex packages that packaging needs. The
  lock is regenerated from a real Hex resolution and both modes now coexist.
- Removed an unreachable verifier-override branch from the final-action
  decision in `PromptRunner.RecoveryPolicy`. An earlier clause already handles
  a passing report on the provider-error path, so the branch could only ever
  raise `KeyError` or evaluate to false.
- Removed a dead `preflight_llm_provider/1` fallback clause; `llm_for_prompt/2`
  always populates `:sdk`.
- Dropped two redundant `||` fallbacks in the Codex CLI confirmation audit;
  `confirmation_source/1` already guarantees a default.
- The project again compiles cleanly under `--warnings-as-errors` on Elixir
  1.20, and `mix dialyzer` reports zero errors with no ignore file.

## [0.7.0] - 2026-07-13

### Changed

- Breaking change: redesigned Prompt Runner around packet directories,
  `prompt_runner_packet.md`, and prompt-local YAML front matter instead of the
  older duplicated control-file workflow.
- Added home-scoped profiles under `~/.config/prompt_runner/` and aligned the
  CLI around packet/profile authoring commands.
- Added first-class packet APIs:
  - `PromptRunner.Packet`
  - `PromptRunner.Packets`
  - `PromptRunner.Profile`
  - `PromptRunner.Runtime`
  - `PromptRunner.Verifier`
- Made completion verifier-owned. Prompt runs now record deterministic verifier
  reports in packet-local runtime state and use those reports to drive retry
  and repair decisions.
- Rebuilt the shipped examples as packet-native examples:
  - `examples/single_repo_packet`
  - `examples/multi_repo_packet`
- Rewrote the README, guides, and HexDocs menu around the packet/profile model.
- Standardized the release README badges and Hex/HexDocs package metadata.
- Aligned the public provider surface with `agent_session_manager ~> 0.10.0`:
  Claude, Codex, Amp, Cursor, and Antigravity. Gemini CLI support is retired;
  Antigravity is the Google coding-agent provider, while `gemini_ex` remains a
  distinct model API SDK.
- Added validated `cursor_opts` and `antigravity_opts` packet and prompt
  sections, plus common-runtime coverage for all five ASM providers.
- Raised the Elixir requirement to `~> 1.19` to match the ASM dependency floor.

### Fixed

- Packet, profile, and prompt-local execution options now normalize correctly
  at plan-build time, so packet-defined `provider`, `model`,
  `reasoning_effort`, and `codex_thread_opts` actually reach runtime
  execution.
- `mix prompt_runner run ...` now sets the correct command flag before
  delegating to the runner.
- `changed_paths_only` verification now inherits the prompt's default repo
  scope when entries omit an explicit `repo`.
- Packet runtime state now serializes common result tuples into readable JSON
  maps instead of opaque tuple placeholders.
- Hex packaging now uses an explicit shipped-example allowlist, excluding
  generated repos, workspaces, logs, runtime state, and nested Git data.

## [0.6.1] - 2026-04-09

### Changed

- Bumped the published `agent_session_manager` dependency to `~> 0.9.2`.
- Updated the standalone example runners to install
  `agent_session_manager ~> 0.9.2`.
- Refreshed README and guide version references for `prompt_runner_sdk ~> 0.6.1`.

## [0.6.0] - 2026-04-09

### Changed

- Removed direct provider SDK package requirements from `prompt_runner_sdk`.
  Host projects now install only `prompt_runner_sdk`, while provider CLI
  execution flows through `agent_session_manager` core lane plus
  `cli_subprocess_core`.
- Prompt Runner now starts ASM sessions with `lane: :core` explicitly instead
  of inheriting ASM's default `:auto` lane selection.
- Replaced provider-SDK runtime preflight with provider/core-lane preflight
  based on ASM provider metadata and common CLI discovery facts.
- Updated README, guides, scaffolding, and shipped examples to use provider
  names as the standard config surface and to stop installing provider SDK
  packages.

### Fixed

- Standalone scaffold output and shipped example `run_prompts.exs` files no
  longer declare unnecessary provider SDK dependencies.
- Prompt Runner now behaves consistently whether provider SDK packages happen
  to be installed locally or not, because the runner always stays on ASM core
  lane.

## [0.5.1] - 2026-04-09

### Changed

- Bumped the published Codex dependency guidance and package spec to
  `codex_sdk ~> 0.16.1`.
- Refreshed README, getting-started, provider docs, scaffolded dependency
  output, and example `Mix.install` snippets for the `0.5.1` / `0.16.1`
  release pair.

### Fixed

- Codex CLI confirmation auditing now falls back to the actual launched
  `run_started` command args when hidden confirmation metadata does not include
  model or reasoning details, so `cli_confirmation: :require` no longer fails
  falsely on otherwise-correct Codex runs.
- Hidden Codex confirmation events now merge event metadata with raw
  `thread.started` metadata before Prompt Runner evaluates the confirmation
  payload.

## [0.5.0] - 2026-04-08

### Changed

- Aligned the published dependency matrix with the current Hex releases:
  - `agent_session_manager ~> 0.9.1`
  - `claude_agent_sdk ~> 0.17.0`
  - `codex_sdk ~> 0.16.0`
  - `gemini_cli_sdk ~> 0.2.0`
  - `amp_sdk ~> 0.5.0`
- Updated runtime missing-provider guidance to point at the current SDK ranges.
- `PromptRunner.Scaffold` now derives provider dependencies from the configured
  provider plus any per-prompt provider overrides instead of hardcoding a stale
  provider list into generated `run_prompts.exs` files.
- Synced Prompt Runner's config/session surface to the current ASM and SDK
  contracts:
  - normalizes provider-native and legacy permission aliases onto the shared
    runner modes `:default | :auto | :bypass | :plan`
  - rejects stale provider-specific inputs such as Codex `sandbox` and
    `ask_for_approval` at config load instead of failing later during runtime
  - preserves inherited root `timeout` and `permission_mode` values when a
    prompt override switches providers without redefining those fields
- Refreshed README, provider docs, getting-started docs, and example docs to
  reflect the 0.5.0 provider matrix and current install instructions.
- Expanded the shipped example packs and standalone `run_prompts.exs` scripts
  to exercise all four providers: Claude, Codex, Amp, and Gemini.
- Example setup scripts now reset their workspaces before seeding, so repeated
  example runs start from a deterministic clean repo state.
- Local sibling-repo development now requires explicit opt-in via
  `PROMPT_RUNNER_USE_LOCAL_DEPS=1`, and Hex packaging tasks ignore that opt-in
  so release builds never emit `path:` dependencies.
- Hex package builds now exclude generated example runtime artifacts such as
  seeded repos, workspaces, logs, and progress files.

### Fixed

- Removed stale Prompt Runner Claude model remapping so current short aliases
  such as `sonnet` resolve through `claude_agent_sdk` instead of an outdated
  hardcoded model id.
- Aligned recovery-related prompt-control behavior with the actual current
  runtime support in the local ASM/SDK stack:
  - Claude no longer defaults `max_turns` to `1` when the runner does not set it
  - Amp rejects unsupported prompt controls such as `system_prompt` and
    `max_turns` instead of silently accepting dead config
  - Gemini SDK startup now uses `approval_mode: :yolo` without duplicating the
    deprecated `yolo: true` flag
- Corrected the live example provider contracts:
  - Codex now stays on the supported ASM shared permission modes
    (`:default | :bypass | :plan`) instead of the invalid shared `:auto` path
  - Amp examples now use the current `amp-1` model instead of a Claude model id
  - Gemini examples allow the current provider-native shell tool name
    `run_shell_command`
  - multi-repo prompts now describe the real working-directory and sibling-repo
    layout used at runtime

## [0.4.0] - 2026-02-11

### Added

- **Studio rendering mode** (`log_mode: :studio`) — CLI-grade interactive output using AgentSessionManager's new `StudioRenderer`
  - Human-readable tool summaries instead of raw JSON token streams
  - Status symbols: `◐` (running), `✓` (success), `✗` (failure), `●` (info)
  - Three tool output verbosity levels via `tool_output:` config: `:summary` (default), `:preview`, `:full`
  - Automatic non-TTY fallback for piped/redirected output
- New `--tool-output` CLI flag for runtime verbosity override (`summary`, `preview`, `full`)
- New `tool_output` config key in runner configuration
- Redesigned prompt header with box-drawing characters and aligned layout in studio mode
- New guide: `guides/rendering.md` documenting all three rendering modes
- **Codex CLI confirmation and model auditing** — verify that the Codex CLI is actually using the model and reasoning effort you configured
  - New `cli_confirmation` config key (`:off`, `:warn`, `:require`) — controls response to confirmation mismatches
  - New `--cli-confirmation MODE` CLI flag for runtime override
  - New `--require-cli-confirmation` CLI flag (shortcut for `--cli-confirmation require`)
  - Machine-readable audit lines written to session logs (`LLM_AUDIT`, `LLM_AUDIT_CONFIRMED`, `LLM_AUDIT_RESULT`)
  - Mismatch and missing-confirmation warnings printed to console when `cli_confirmation: :warn` (default for Codex)
  - Hard failure when `cli_confirmation: :require` and CLI does not confirm reasoning effort
  - Per-prompt `cli_confirmation` override via `prompt_overrides`
- **Unbounded and infinite timeout support** — `timeout` now accepts `:unbounded`, `:infinity`, `"unbounded"`, `"infinity"`, and `"infinite"` in addition to positive integers
  - Sentinel values resolve to a 7-day emergency cap in the session layer
  - Works in both top-level config and per-prompt `prompt_overrides`
- **LLM SDK preflight checks** — Runner verifies that the required SDK module is loaded before starting a prompt (currently Codex only), with clear error messages including the missing package name
- **Stream error handling** — `Rendering.stream/2` failures (exceptions, throws) are now caught and returned as `{:error, {:stream_failed, message}}` instead of crashing the runner
- Codex `reasoning_effort` displayed in prompt plan output when configured via `codex_thread_opts`

### Changed

- Default `log_mode` remains `:compact` (studio is opt-in for this release)
- Default `cli_confirmation` is `:warn` for Codex prompts, effectively a no-op for other providers
- Modularized `Runner` internals — extracted helper functions for prompt header printing, permission mode display, adapter option display, and codex thread options
- Stream error tracking now preserves structured `provider_error` payloads from `:error_occurred`/`:run_failed` events instead of flattening to generic strings
- `return_error` now renders concise summaries by default and prints provider stderr detail only when `log_meta: :full`
- Updated `guides/configuration.md` with new rendering, timeout, and CLI confirmation options
- Updated `guides/providers.md` with Codex reasoning effort and CLI confirmation details
- Updated `guides/getting-started.md` with studio mode and new CLI flags
- Updated README with rendering modes section, new CLI options, and error detail behavior

### Dependencies

- Requires `agent_session_manager ~> 0.8.0` (StudioRenderer module)
- `claude_agent_sdk` updated to `~> 0.12.0`
- `codex_sdk` updated to `~> 0.8.0`

## [0.3.0] - 2026-02-09

### Changed

- **Migrated rendering to `AgentSessionManager.Rendering`** — `Runner` now builds a renderer/sink pipeline from config instead of calling `StreamRenderer.stream/4`. Uses `CompactRenderer` or `VerboseRenderer` with `TTYSink`, `FileSink`, `JSONLSink`, and `CallbackSink`.
- **Migrated session lifecycle to `AgentSessionManager.StreamSession`** — `Session` now delegates stream creation, task management, and cleanup to `StreamSession.start/1` instead of hand-rolling ~200 lines of `Stream.resource`, receive loop, error event constructors, task shutdown, and child cleanup.
- **Canonical event format** — `Session` no longer normalizes ASM events. Canonical events (`:run_started`, `:message_streamed`, `:tool_call_started`, `:tool_call_completed`, `:run_completed`, etc.) pass through directly to the rendering pipeline.
- `Session.start_stream/2` signature and return type unchanged — existing callers work without modification.
- `start_adapter` replaced with `build_adapter_spec` returning `{Module, opts}` tuples instead of starting processes directly.
- `PromptRunner.Application` simplified — removed `PromptRunner.TaskSupervisor` and `PromptRunner.SessionSupervisor` (StreamSession manages its own lifecycle).
- Error tracking changed from `StreamRenderer` return value to `CallbackSink` with process dictionary (`Process.put/:prompt_runner_stream_result`).
- Session header now written directly to log file IO device via `IO.binwrite` instead of through `StreamRenderer.emit_line`.
- Tests updated to emit canonical ASM events instead of previously-normalized types.
- **Examples now use isolated workspace directories** — each example has `setup.sh` / `cleanup.sh` scripts and a standalone `run_prompts.exs` using `Mix.install`, so examples no longer operate within the SDK repository itself.
- Updated documentation (providers.md, getting-started.md, configuration.md, README) to reflect canonical event format, removed supervisors, new rendering pipeline, and example isolation.

### Removed

- **Deleted `PromptRunner.StreamRenderer`** (935 lines) — all rendering now handled by `AgentSessionManager.Rendering`.
- Removed `normalize_event/1` and all event normalization functions from `Session` (`:message_start`, `:text_delta`, `:tool_use_start`, `:tool_complete`, `:message_stop`, etc. mappings).
- Removed `build_stream_session`, `build_event_stream`, `next_stream_events`, `done_error_events`, `run_once`, `start_store`, `stop_task`, `await_task_exit`, `cleanup_children`, `terminate_child`, `start_supervised_child`, `ensure_runtime_started` from `Session` (all replaced by StreamSession).
- Removed `PromptRunner.TaskSupervisor` and `PromptRunner.SessionSupervisor` process tree entries.
- Removed `examples/simple/claude-output.txt` (examples now write to workspace directories).

### Added

- Standalone `run_prompts.exs` scripts for simple and multi-repo-dummy examples (use `Mix.install` for self-contained execution).
- `setup.sh` and `cleanup.sh` for the simple example to manage an isolated git workspace.
- `workspace/` added to `.gitignore` for example directories.

### Dependencies

- Requires `agent_session_manager ~> 0.7.0` (StreamSession and Rendering modules).

## [0.2.0] - 2026-02-08

### Added

- Added `PromptRunner.Application` OTP supervision tree with:
  - `PromptRunner.TaskSupervisor` for run execution tasks.
  - `PromptRunner.SessionSupervisor` for adapter/store process lifecycle.
- Added `PromptRunner.Session` as the AgentSessionManager bridge layer.
- Added support for provider alias `amp` (`amp_sdk`) in LLM normalization.
- Added `adapter_opts` config support at both root and `llm` scopes.
- **Normalized adapter options passthrough** — Session now forwards these config keys to all adapters:
  - `permission_mode` — `:default`, `:accept_edits`, `:plan`, `:full_auto`, or `:dangerously_skip_permissions`
  - `max_turns` — integer turn limit (Claude: unlimited by default, Codex: SDK default 10, Amp: no-op)
  - `system_prompt` — system-level instructions (Claude: `system_prompt`, Codex: `base_instructions`, Amp: stored only)
  - `sdk_opts` — keyword list of arbitrary provider-specific SDK options (normalized options take precedence)
- **Claude `cwd` passthrough** — Session passes `project_dir` as `cwd` to the Claude adapter, so the Claude CLI runs in the correct working directory.

### Changed

- Migrated runtime execution from direct SDK integration to `agent_session_manager`.
- Reworked `PromptRunner.LLMFacade` into a thin delegator to `PromptRunner.Session`.
- Updated config normalization to accept both `provider` and legacy `sdk` keys.
- Updated examples and CLI help text to use `provider` in config snippets.
- Updated README guidance from dual-SDK to multi-provider terminology.

### Removed

- Removed direct `PromptRunner.LLM.CodexNormalizer` integration and tests.
- Removed direct `claude_agent_sdk` and `codex_sdk` dependency declarations.

### Dependencies

- Added `agent_session_manager ~> 0.6.0` (now `~> 0.7.0` as of 0.3.0).

## [0.1.2] - 2026-01-26

### Added

- New `RepoTargets` module for expanding repo group references in target_repos.
  Groups are defined in config as `repo_groups: %{"pipeline" => ["command", "flowstone"]}`
  and referenced in prompts.txt as `@pipeline`.
- Support for nested group references (e.g., `@portfolio` containing `@pipeline`).
- Cycle detection for repo group definitions with clear error messages.
- Validator now checks repo-specific commit messages for default repo when prompt
  has no explicit target_repos.
- Test suites for `RepoTargets` and `Validator` modules.

### Changed

- `Runner` now expands repo group references before resolving target repositories.
- `Validator` expands repo groups when checking commit messages and repo references.
- Improved error handling when target repos cannot be resolved.

## [0.1.1] - 2026-01-26

### Fixed

- Fixed single-repo commit path bug where `commit_single_repo` always committed to `config.project_dir` instead of the resolved target repository path. Now correctly passes repo name and path from `runner.ex` to `git.ex`.

### Changed

- Added `:inets` to extra_applications for OTP HTTP client support.
- `commit_single_repo/2` now accepts optional `repo_name` and `repo_path` parameters for explicit targeting.

### Dependencies

- Updated `ex_doc` from 0.39.3 to 0.40.0.
- Updated `finch` from 0.20.0 to 0.21.0.

## [0.1.0] - 2026-01-18

- Initial release.
- Prompt runner CLI with streaming output.
- Claude Agent SDK and Codex SDK support via a unified facade.
- Multi-repo prompt execution with per-repo commit messages.
- Example prompt sets for single-repo and multi-repo workflows.

[Unreleased]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.12.1...HEAD
[0.12.1]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.12.0...v0.12.1
[0.12.0]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.11.0...v0.12.0
[0.11.0]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.10.0...v0.11.0
[0.10.0]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.9.1...v0.10.0
[0.9.1]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.9.0...v0.9.1
[0.9.0]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.8.1...v0.9.0
[0.8.1]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.8.0...v0.8.1
[0.8.0]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.7.0...v0.8.0
[0.7.0]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.6.1...v0.7.0
[0.6.1]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.6.0...v0.6.1
[0.6.0]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.5.1...v0.6.0
[0.5.1]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.1.2...v0.2.0
[0.1.2]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.1.1...v0.1.2
[0.1.1]: https://github.com/nshkrdotcom/prompt_runner_sdk/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/nshkrdotcom/prompt_runner_sdk/releases/tag/v0.1.0
