sloop-daemon 0.5.0

Agentic coding scheduler — a daemon that runs background coding agents autonomously in isolated git worktrees
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
# Changelog

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

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

## [Unreleased]

## [0.5.0] - 2026-07-26

### Added

- **A `sloop show` dashboard section for everything waiting on a human.** The
  dashboard envelope gains an `attention` array — every ticket currently in
  `needs_review` or `failed`, newest first, in the same row shape as `recent`
  and never truncated by `-N`. The human rendering prints it between `runs:`
  and `recent:`, with each row's reason, and omits the section entirely when
  the array is empty. A `needs_review` ticket that had aged out of the recent
  window was previously visible only as a digit in the count line.

  This is an additive protocol-v1 field. No existing dashboard field changes
  shape or value.

- **Tty-gated color in the human output.** `FAIL` and `failed` render red;
  `warn`, `needs_review`, and a `(stalled: …)` annotation yellow; `ok` green
  and `merged` dim. Everything else is unstyled. Color is emitted only when
  stdout is a terminal and `NO_COLOR` is unset, and never in `--json`.

### Changed

- **The human `show` and `status` output reads without arithmetic.** `next
  wake` is a countdown (`next wake in 4m12s`, or `next wake imminent` when the
  deadline has passed) rather than the one raw UTC RFC3339 instant in
  otherwise-local output; `--json` still carries `next_wake` as RFC3339. An
  unfinished span says how long it has been running — `19:51-... (9m0s)`  while still refusing to invent an end time. Finished spans are unchanged.

- **The ticket count line shows only what is happening.** A state prints only
  when its count is non-zero, except `ready`, where an empty queue is itself
  the signal, and `merged` moves to the end. Seven states with four zeros
  became `tickets: 0 ready, 1 claimed, 1 needs_review, 77 merged`.

- **Ticket rows drop noise.** The `(project)` column appears only when the
  displayed rows span more than one project, and the reason is suppressed only
  for `merged` — a `needs_review` or `failed` row's reason is the reason to
  read the row.

## [0.4.0] - 2026-07-26

### Changed

- **`sloop brief` is keyed on the stage the caller is executing, not on the
  run.** The reply gains a `stage` block — `{"name", "attempt",
  "result_check"}` — and `definition_of_done` now states what *that* stage
  turns on instead of one run-level sentence handed to everybody.

  The old text was authored in the socket handler and began "Commit your work to
  the run branch" for every caller. Three kinds of worker hold a brief-capable
  token, and only one of them builds anything: Sloop's own panel bootstrap tells
  a reviewer to change nothing and to run `sloop brief`, which replied that its
  job was to commit. The obligation now follows from the stage's `result_check`
  and the caller's role, derived in one place:

  | Caller | Definition of done |
  | --- | --- |
  | a panel reviewer | the stage passes only on its reported verdict; the work under review is not its to change |
  | `result_check: reported` | the stage passes only on the worker's reported verdict |
  | `result_check: { builtin: commits }` | commit the work to the run branch |
  | `result_check: none` | the worker's own exit status is the verdict |
  | any other check | an independent check judges the work after the worker exits |

  A builder's brief is unchanged in substance: an `action: agent` stage defaults
  to the commits check, so it is still told to commit to the run branch.

  `attempt` is the other half of the assignment's identity. A
  `fail_action: { return_to: … }` edge re-enters a stage, each execution is owed
  its own report, and an agent that could not see the attempt could not tell a
  retry from a first run.

  Two judgement calls in the new text, both deliberate:

  - **`flow.test_cmd` no longer earns a line.** It used to append "The
    configured test command passes" whenever the key was set. But `test_cmd` is
    spliced into every flow as its own `test` stage, judged by its own exit —
    so the line told a worker to satisfy a check belonging to a *different*
    stage, which is the same category of error as telling a reviewer to commit.
    A flow's stages name their own tests, and a stage's brief describes that
    stage.
  - **A worker whose check is an independent actor is told that one exists, not
    which.** Naming it would be useful, and it would also hand a worker the
    flow's shape and the judge's argv — an invitation to run or game the check
    rather than do the work. The stage block's `result_check` names the *kind*
    of judge (`exec`, `agent`, `panel`, …), which is a fact about the caller's
    own stage; the command behind it is not.

  For clients: `definition_of_done` keeps its type and was always prose for an
  agent to read. `stage` is additive. `ticket.acceptance` is removed — see
  below.

### Removed

- **`ticket.acceptance` from the `sloop brief` reply.** It was hardcoded to
  `[]` on every reply, so no client could ever have read a value out of it, and
  nothing in Sloop wrote one. It is part of the documented brief shape, so its
  removal is recorded here rather than left to be discovered.
- **The legacy `kind` / `cmd` / `verdict` stage grammar.** `0.3.x` named a
  stage's work with `kind` and graded it with `verdict`; `0.4.0` names the same
  two things with `action` and `result_check`, and the old keys no longer parse.
  Every file under `.agents/sloop/flows/` is parsed eagerly when config loads,
  so a flow left in the old spelling is a startup error naming the stage and the
  key that replaces the one it found, not a surprise the first ticket bound to
  that flow discovers. The rewrite is mechanical, the last row excepted:

  | Removed | Replacement |
  | --- | --- |
  | `kind: agent`, `kind: build` | `action: agent` |
  | `kind: exec` with `cmd: [...]` | `action: { exec: [...] }` |
  | `kind: merge` | `action: { builtin: merge }` |
  | `kind: sync` | `action: { builtin: sync }` |
  | `verdict: exit` | `result_check: none` |
  | `verdict: commits` | `result_check: { builtin: commits }` |
  | `verdict: { check: [...] }` | `result_check: { exec: [...] }` |
  | `verdict: reported` | `result_check: reported` |
  | `on_fail: { agent: ..., attempts: N }` | `fail_action: { return_to: <stage>, attempts: N }` |

  These are breaking changes with no compatibility window: there is no flag that
  restores the old spelling, and a repository whose flows still use it does not
  start. `sloop template flow` prints the current grammar in full, annotated,
  and `docs/configuration.md` documents it.
- **`on_fail` repair blocks**, deprecated in `0.4.0-rc.1`. The last row of the
  table above is the only one that is not a rename. `on_fail` attached a
  *separate* repair agent to one stage and retried it within a single execution;
  `fail_action: { return_to: <stage>, attempts: N }` sends the walk back to an
  earlier stage and re-runs the whole span from there using the ticket's own
  agent, with the failure in its prompt. Converting one is therefore a decision
  about which stage should do the fixing — usually the agent stage that produced
  the work the failing stage was guarding — rather than a rename. Note also that
  the whole span re-runs, so no verdict earned between the target stage and the
  failure survives the loop.

  Two things go with the blocks. Stage rows lose their `attempts` field, the
  count of retries inside one execution: it is gone from `sloop show --json` and
  from the `N attempts` the stage table printed off it, while a `return_to`
  re-entry is what it always was, a row of its own labelled `#2`. A client that
  read the field should read `attempt` and count the rows; the envelope stays at
  version `1`, and `docs/protocol.md` records the removal. And the daemon no
  longer logs `flow_on_fail_deprecated`, having nothing left to warn about.
- The compatibility path that read a `0.3.x` flow snapshot off a `runs` row is
  gone, now that the grammar which produced one no longer parses. The only run
  it could still have served is one claimed by a `0.3.x` binary and recovered
  by a `0.4.0` one. Such a run is now parked for review by id rather than
  resumed: its branch and evidence are untouched, the recovery pass carries on
  to every other run, and `sloop reindex` rebuilds local state from the
  committed files and Git. What an operator sees is one `driver_resume_failed`
  log record and evidence row naming the run, and its ticket settled to
  `needs_review` rather than left claimed by a run nobody is driving. The work
  on the branch is intact, so the remedy is to review and merge it by hand, or
  to start the ticket again with `sloop run` once its flow file is in the
  current grammar.

### Fixed

- A flow file that defines no stages is refused at parse time. Letting an agent
  action sit in any position retired the "the first stage must be an agent
  stage" rule, and with it the only thing that had ruled out an empty `stages:`
  list; such a file loaded, and a ticket posted to it walked straight to
  `complete` having run nothing. It is now a config-load error like any other
  invalid flow.

- **`sloop logs` showed the oldest 64 entries and did not say so.** A bare read
  returned one page anchored at the *start* of the capture, so on any run of
  real length an operator saw the agent's first few seconds and nothing else —
  and on a live run the same 64 lines came back every time, which is exactly
  what a hung run looks like. A bare read now tails, like `tail` and
  `journalctl`, and any page that hides output says so:

  ```
  showing the last 64 of 312 entries; --tail N or --follow for more
  ```

  `--tail N` and `--follow` are unchanged, and `--follow` still streams from
  the run's first entry. The default belongs to the CLI alone: on the socket an
  omitted `tail` still reads forward from the cursor. `logs` responses carry a
  new `elided` count for what a tail dropped behind it, which the existing
  `complete` flag cannot report — a tail reads to the end of the file however
  much older output it discards, so it is always `complete`.

## [0.4.0-rc.1] - 2026-07-25

### Added

- `result_check: { panel: {...} }` puts 2 to 5 independent reviewers on a stage
  and derives its verdict from a quorum of their reports: `Pass` iff at least
  `quorum` seats reported `Pass`. Reviewers run one at a time in the run
  worktree with one shared prompt, so a panel never exceeds
  `max_parallel_tasks`, and their spawns count towards the worst-case execution
  budget a flow is admitted against. Each seat gets one-shot credentials bound
  to `(run, stage, attempt, reviewer)`, so which report a `sloop verdict` call
  lands on comes from the credential and never from its arguments. A reviewer
  that exits without reporting counts as a `Fail` with `no verdict reported`.
  The aggregate is never stored — one append-only evidence row per reviewer is,
  and the verdict is recomputed from those rows by a pure function, so a
  restarted daemon reaches the same reading.
- `sloop verdict` takes `--confidence low|medium|high`, defaulting to `medium`.
  It is recorded as evidence and never weighted into a panel's aggregation;
  floats are rejected. `sloop show <run>` lists each panel seat's verdict,
  confidence, and reason under its stage, silent seats included.
- A new `{ builtin: sync }` action merges the default branch into the run
  branch, inside the run worktree. It passes when that merge commits cleanly or
  there was nothing to integrate, and fails on a conflict — aborting the merge
  so a `return_to` target starts from a clean tree, with git's conflict output
  captured in the run log and so in the re-entered agent's prompt. Any number
  of sync stages, anywhere before the merge; the shared default-branch checkout
  is only ever read.
- The merge stage takes `{ builtin: merge, ff_only: true }`, which refuses the
  merge commit: the default branch either fast-forwards to the run branch head,
  or the stage fails having touched nothing. Absent, the merge policy is
  unchanged. `ff_only` is meaningful nowhere else and is a parse error on any
  other action.
- `sloop init` writes a `train` flow beside `default.yaml`, opt-in with
  `flow: train`. It is the merge train: `build → sync → verify → ff_only
  merge`, with `fail_action: { return_to: sync }` on the merge, so a default
  branch that moves between the verification and the merge loops the train
  rather than landing a tree no stage tested. Its `verify` stage uses the
  repository's configured `flow.test_cmd` when it has one.
- Flow stages can now bind the two `fail_action` forms that previously parsed
  but were rejected. `fail_action: continue` makes a stage advisory: its
  failure is recorded and visible in `sloop show`, the walk carries on, and the
  run's outcome is unchanged. `fail_action: { return_to: <stage>, attempts: N }`
  is a bounded backward edge: the walk re-enters an earlier stage and re-runs
  the whole span from there, so no verdict earned before the loop can reach the
  merge. Edges must point backwards, `attempts` is capped at 3, and a flow
  whose budgets could execute more than 32 stages is refused at parse time.
- A re-entered `agent` stage is told why it is running again. Sloop appends a
  delimited `previous attempt failed` block to its prompt — after the ticket
  body and the worker instructions — naming the stage that failed, its resolved
  reason, and the last 100 lines of that execution's captured output. The block
  is rebuilt from the run's persisted evidence, so a daemon that restarts
  mid-loop composes the identical prompt. `exec` actions are handed nothing:
  their command line is fixed by the flow.
- `sloop show` renders each stage execution separately, suffixing re-runs with
  their attempt (`build`, `build#2`), and a non-merged run's derived `reason`
  names the failure the walk actually stopped on rather than one a later
  attempt superseded.
- `sloop show` tells an advisory failure apart from the one that ended the run:
  the stage table marks it `advisory` and the scan strip marks it `warn` rather
  than `FAIL`. A run's derived `reason` never names an advisory stage, since
  the walk provably continued past it, and a terminal run whose only failures
  were advisory says so. Stage rows carry a new `advisory` field.
- A run whose walk stopped short says which halt it was. `sloop show <run>`
  appends `; return_to budget spent` or `; the stage log does not replay
  against this flow` to the derived reason where the failed stage alone does
  not explain the ending, and `value.halt` carries the same distinction as a
  stable token (`fail_action`, `return_budget_exhausted`, `corrupt_log`). It is
  re-derived by replaying the walk's own fold, so it cannot disagree with the
  driver.
- `sloop logs --stage` accepts `<stage>#<attempt>`, selecting one execution of a
  stage a `return_to` edge re-entered. Parsing happens in the daemon, so every
  socket client gets it; a bare stage name still selects every execution. Log
  lines label re-runs the same way (`[agent:build#2]`), so the label `sloop
  show` prints is the selector to type next.

### Changed

- **What Sloop called an *activation* is now a *trigger*.** The scheduling model
  has three nouns — a ticket is what to do, a run is one attempt at it, and the
  third is the durable record that demand exists, the thing that makes the
  dispatcher pick a ticket up. "Activation" named the act of activating rather
  than the row waiting in a queue, so every mention of it had to be glossed
  first; "the trigger fired" and "the trigger is pinned to TICK-63" read without
  one. Scheduling, selection, claiming, and rearm behaviour are unchanged.

  Operator-visible consequences: `sloop run` prints `trigger TR91 queued (now,
  ticket TICK-63)`; trigger ids widen from `A<ordinal>` to `TR<ordinal>`,
  because `T91` reads as a ticket id beside `TICK-91`; and the dashboard field
  `queued_activations` becomes `queued_triggers`. On the wire, `post` and `run`
  take a `trigger` argument where they took `activation`, and `post` answers
  with `trigger` and `trigger_suppressed`. See "The activation → trigger
  rename" in `docs/protocol.md` for the full field-by-field table.

  Upgrading rewrites the database in place, ids and all. Queued triggers are
  pending work recorded in no committed file and no commit, so `sloop reindex`
  cannot rebuild one; the migration carries every row across instead.
- `sloop verdict --confidence` on a `reported` stage is now recorded rather
  than discarded, so the flag means the same thing from a stage worker as from
  a panel reviewer. `sloop show <run>` prints it beside that stage's verdict
  source, and the stage row carries a new `confidence` field.
- `sloop template flow` prints the current grammar as its canonical example:
  `action`, `result_check`, and `fail_action` written out on every stage, with
  one `return_to` edge, one advisory stage, and a commented panel block. The
  pre-`action` `kind:`/`verdict:` spelling and the deprecated `on_fail` are
  demoted to a short "legacy spellings" note, and `sloop template ticket` names
  the two flows that ship with the binary. `sloop verdict --help` and `sloop
  logs --help` now document who may call them and what their selectors accept.
- Denials and parse errors use one vocabulary for one concept: a stage that
  cannot take a report says it does not use `result_check: reported` rather
  than naming a "verdict policy".
- The ticket-state table in `sloop --help --all` states the trigger
  precondition first: `ready` said it was "eligible for dispatch once a run is
  queued", which reads as a caveat on a state that sounds like a promise. It
  now leads with the fact that a `ready` ticket runs only once a trigger is
  queued for it, and `failed` names both halves of the recovery — `sloop retry`
  returns it to ready, `sloop run` starts it again.

- Captured output records, stage-process checkpoints, agent-exit checkpoints,
  and reported verdicts are all scoped to `(stage, attempt)` rather than stage
  alone, so a re-entered stage gets its own output, its own worker report, and
  its own exit checkpoint instead of inheriting an earlier attempt's.

### Deprecated

- The `queued_activations` field on `status` and on the `show` dashboard, in
  favour of `queued_triggers`. Both are emitted carrying the identical value,
  following the precedent set by the `list``show` aliases;
  `queued_activations` is **removed in 0.5.0**. The other envelope fields the
  rename touched are swaps rather than aliases — see `docs/protocol.md`.
- `on_fail` repair blocks. `fail_action: { return_to: <stage> }` covers the
  same ground with the flow's own stages — a failing check stage returning to
  the stage it guards — and the daemon now logs a `flow_on_fail_deprecated`
  note when it admits a run whose flow uses `on_fail`. Nothing is removed:
  existing flows keep parsing, repairing, and settling exactly as before.

### Fixed

- A daemon spawned by a client command now calls `setsid`, so it leads its own
  session and process group instead of inheriting them from that client. The
  client exits as soon as its request is answered, so a daemon that stayed in
  its session died to any signal aimed at that short-lived process — a terminal
  hangup, or a supervisor reaping a finished command's process group — and the
  next client command silently started a replacement. The symptom was a daemon
  that vanished with no `daemon_stopped` record, and, if a run was in flight at
  the time, lost work.
- `sloop post` accepts an absolute path to a ticket that reaches the repository
  through a symlink. The repository root is always canonicalized, but the path
  the operator typed was compared to it unresolved, so on macOS — where `/tmp`
  and `/var/folders` are both links into `/private` — posting a ticket by
  absolute path was rejected as "outside the repository". Both sides now
  resolve before the comparison. A ticket file that does not exist yet is
  unaffected: the longest existing ancestor resolves and the rest is appended,
  so a missing file inside the ticket directory still reports `not found`.

  This tightens one case that used to pass: a ticket path that is itself a
  symlink out of the repository is now rejected, because containment is
  decided on the file whose bytes get read rather than on the name used to
  reach it.
- A Claude session limit now classifies as `rate_limited` and cools the target
  down instead of failing the ticket. The rule required stderr, but the
  `claude` target runs with `--output-format stream-json` and reports vendor
  rejections as synthetic assistant messages on stdout; its signature also only
  covered `You've hit your limit`, not the `You've hit your session limit`
  wording a real run hit. The rule now matches either stream on the wording
  every limit window shares. Without this, one vendor limit could burn several
  tickets' retry attempts and strand them in `failed`.

## [0.3.0] - 2026-07-21

### Added

- The read surface is now one verb. Bare `sloop` or `sloop show` prints a
  dashboard: daemon status, scheduler state, and recent activity.
  `sloop show <REF>` resolves ticket IDs, ticket names, run IDs and aliases,
  unique run-id prefixes, and project IDs; an exact reference always wins.
  Anything else becomes a case-insensitive ticket pattern over IDs and
  names — a substring by default, an unanchored regular expression when the
  text contains regex metacharacters — rendered as a ticket list.
  `-n`/`--limit N` or the shorthand `-N` caps list rows. `--follow` streams
  the shown scope's events; ticket and run followers exit when the subject
  settles, and `--follow --quiet` returns only the outcome for scripting.
  Exit codes are stable: `0` for success or a merged subject, `1` for
  another terminal outcome or a daemon error, `2` for usage errors.
  `sloop show --help` documents the whole resolution ladder. Pattern
  resolution happens in the daemon: the `show` verb accepts patterns, the
  `events` verb gained an optional `scope`, and the `list` verb gained an
  optional `limit`, all additive within protocol version 1.
- `sloop template <kind>` prints fully commented canonical templates for
  the config, flow, project, and ticket files, so a working example is
  always one command away.
- `sloop show <TICKET>` now lists the ticket's runs, newest attempt first:
  alias, outcome, wall-clock span, and a strip of the run's flow stages
  marked `ok`, `FAIL`, `..` (running), or `-` (not reached). A ticket that
  has never run prints `runs: none`.
- `sloop show <RUN>` now shows the run's timeline, a per-stage table (state,
  attempts including `on_fail` retries, duration, exit code, and verdict
  source), and a derived `reason` for any non-merged terminal run — for
  example ``stage `test` failed (exit 1) after agent completed with
  commits``. The reason comes from the stored stage and evidence rows, never
  from an agent's own claim about its work. Stage names come from the run's
  admitted flow snapshot, so a run reports the stages it actually had even
  after the flow file changes.
- The `show` response gained `value.runs` on tickets and `value.stages`,
  `value.attempt`, `value.agent_exit_code`, and the timeline fields on runs,
  all additive within protocol version 1.
- `sloop logs` gained `--stage <NAME>`, `--tail <N>`, and `--follow`.
  `--stage` selects one flow stage by the name the flow gives it, including
  the agent stage, and rejects a name the run's flow does not define instead
  of returning an empty page. `--tail` keeps the last N entries; `--follow`
  streams new entries until the run settles. The three combine, so
  `sloop logs <RUN> --stage test --tail 50` answers "why did the test stage
  fail" in one command. Filtering happens in the daemon: the `logs` verb
  gained `stage`, `tail`, and `after` arguments and a `terminal` response
  field, all additive within protocol version 1.
### Changed

- The default flow's review stage is now a real gate. It previously
  inherited `verdict: exit` and passed whenever the reviewer process exited
  zero, so every fresh `sloop init` deployment silently approved all work.
  The shipped stage now uses `verdict: reported`, and the review prompt
  requires the reviewer to call `sloop verdict pass|fail --reason` exactly
  once; the command, not the prose, decides the stage.
- Compact `sloop --help` now lists only the everyday operator commands
  (`init`, `template`, `daemon`, `post`, `show`, `logs`). Everything else,
  including the worker verb `brief`, remains available and documented under
  `sloop --help --all`.
- `sloop show <RUN>` labels the agent stage's exit as `agent exit:` rather
  than a bare `exit:`, which could read as "the whole run passed" on a run
  whose later stage failed. The JSON `exit_code` field is unchanged.
- Ticket lists — the dashboard, pattern results, and the deprecated `list`
  alias — order tickets by registration time, newest first, instead of
  oldest first, so recently posted and currently running work leads the
  output. State does not affect the order. Tickets registered in the same
  millisecond fall back to their id's numeric ordinal, newest first.
- `sloop post` now reports every problem with a ticket file in a single
  `invalid_arguments` error, one per line under the file path, instead of
  stopping at the first one. A file whose frontmatter cannot be parsed at
  all still fails fast with the parse error, and a file with exactly one
  problem reads as it always has.

### Deprecated

- `status`, `list`, `watch`, and `wait` remain accepted as hidden
  deprecated aliases of `sloop show`. They no longer appear in normal help,
  and each invocation writes a note to stderr naming its replacement:
  `status` and `list` point to `sloop show`, `watch` to `sloop show
  --follow` (its optional scope and tail still work), and `wait` to
  `sloop show --follow --quiet` (its run and timeout still work). The
  aliases will be removed in a future release.

### Fixed

- `sloop reindex` now recognizes patch-equivalent merges. A run branch
  whose commits were squashed or rebased onto the default branch is
  detected with `git cherry` and indexed as merged; previously only true
  ancestor merges counted, and reindex flipped squash-merged tickets to
  `needs_review`.

## [0.2.1] - 2026-07-20

### Fixed

- Keep worker socket paths within the 104-byte macOS socket path limit by
  placing them directly in the runtime directory under the short run id.
  On macOS the previous layout exceeded the limit, every agent spawn failed
  at socket bind, and no run could start.

## [0.2.0] - 2026-07-20

### Fixed

- Draw run-id entropy from `getrandom` on Linux, which musl libc exports,
  instead of `getentropy`, which it does not, so musl builds compile.
- Wait out a stopping predecessor's lock for up to two seconds when the
  daemon starts, so a stop followed by an immediate start cannot lose the
  race against the old process releasing its lock.

### Added

- Initial command-line surface and local daemon implementation.
- Release automation for Homebrew formulae, crates.io publication, and signed
  GitHub artifact provenance.
- Recurring and overnight activations, with `post --at` activations dispatched
  at their scheduled time.
- Blocked ticket dependencies enforced during selection.
- Optional `on_fail` repair agents for exec and merge stages, including merge
  conflict repair before retry.
- Activity events feed and a `sloop watch` command.
- Operator `show` resolves tickets with bodies and runs.
- Draining daemon restart.
- Settled worktree retention and default worktree branches derived from ticket
  file stems.
- Per-target default model and effort, defaulting to `claude`.
- Vendor error classification from agent output.

### Changed

- Store daemon state, logs, sockets, and locks in platform-native per-user
  locations instead of the repository's `.sloop` directory.
- Run start and exit moved behind coordination verbs, with typed run states
  and lease renewal.
- Runs get random internal ids with ticket-derived aliases.
- Commit counts removed from run verdicts; outcomes derive from evidence only.
- Externally merged run branches reconcile out of `needs_review`, and stale
  runs reconcile periodically.