@codetreker/daynight 0.37.2 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +207 -60
  2. package/dist/api/client.d.ts +8 -3
  3. package/dist/api/client.js +25 -3
  4. package/dist/api/client.js.map +1 -1
  5. package/dist/api/index.d.ts +1 -0
  6. package/dist/api/index.js +1 -0
  7. package/dist/api/index.js.map +1 -1
  8. package/dist/api/parse.d.ts +2 -2
  9. package/dist/api/parse.js +30 -24
  10. package/dist/api/parse.js.map +1 -1
  11. package/dist/api/task-steps.d.ts +6 -0
  12. package/dist/api/task-steps.js +48 -0
  13. package/dist/api/task-steps.js.map +1 -0
  14. package/dist/api/types.d.ts +29 -9
  15. package/dist/cli/application.js +6 -0
  16. package/dist/cli/application.js.map +1 -1
  17. package/dist/commands/cleanup-operation.d.ts +3 -0
  18. package/dist/commands/cleanup-operation.js +12 -0
  19. package/dist/commands/cleanup-operation.js.map +1 -0
  20. package/dist/commands/controller/provider-display.d.ts +2 -0
  21. package/dist/commands/controller/provider-display.js +63 -0
  22. package/dist/commands/controller/provider-display.js.map +1 -0
  23. package/dist/commands/controller/runtime.js +4 -2
  24. package/dist/commands/controller/runtime.js.map +1 -1
  25. package/dist/commands/skill/skills/using-daynight/SKILL.md +1 -1
  26. package/dist/commands/skill/skills/using-daynight/references/authorization.md +1 -1
  27. package/dist/commands/skill/skills/using-daynight/references/autopilot-managed-task.md +4 -3
  28. package/dist/commands/skill/skills/using-daynight/references/handoff.md +25 -15
  29. package/dist/commands/skill/skills/using-daynight/references/task-management.md +1 -1
  30. package/dist/commands/skill/usage.md +43 -5
  31. package/dist/commands/task/command.js +8 -1
  32. package/dist/commands/task/command.js.map +1 -1
  33. package/dist/commands/task/definition.js +23 -1
  34. package/dist/commands/task/definition.js.map +1 -1
  35. package/dist/commands/task/step-diagnostics.d.ts +6 -0
  36. package/dist/commands/task/step-diagnostics.js +63 -0
  37. package/dist/commands/task/step-diagnostics.js.map +1 -0
  38. package/dist/commands/task/step-rendering.d.ts +5 -0
  39. package/dist/commands/task/step-rendering.js +40 -0
  40. package/dist/commands/task/step-rendering.js.map +1 -0
  41. package/dist/commands/task/step.d.ts +8 -0
  42. package/dist/commands/task/step.js +49 -0
  43. package/dist/commands/task/step.js.map +1 -0
  44. package/dist/commands/worker/backends/grant-renewal.js +1 -1
  45. package/dist/commands/worker/backends/grant-renewal.js.map +1 -1
  46. package/dist/commands/worker/backends/session-api.d.ts +1 -1
  47. package/dist/commands/worker/backends/session-api.js +4 -4
  48. package/dist/commands/worker/backends/session-api.js.map +1 -1
  49. package/dist/commands/worker/backends/sqlite/002-resource-observations.sql +6 -0
  50. package/dist/commands/worker/backends/web-process.js +13 -3
  51. package/dist/commands/worker/backends/web-process.js.map +1 -1
  52. package/dist/commands/worker/backends/web-record.js +2 -18
  53. package/dist/commands/worker/backends/web-record.js.map +1 -1
  54. package/dist/commands/worker/backends/web-store.d.ts +4 -13
  55. package/dist/commands/worker/backends/web-store.js +44 -52
  56. package/dist/commands/worker/backends/web-store.js.map +1 -1
  57. package/dist/commands/worker/backends/web-types.d.ts +0 -13
  58. package/dist/commands/worker/backends/web-types.js +0 -1
  59. package/dist/commands/worker/backends/web-types.js.map +1 -1
  60. package/dist/commands/worker/backends/web.d.ts +5 -1
  61. package/dist/commands/worker/backends/web.js +279 -127
  62. package/dist/commands/worker/backends/web.js.map +1 -1
  63. package/dist/commands/worker/cleanup-error.d.ts +5 -0
  64. package/dist/commands/worker/cleanup-error.js +9 -0
  65. package/dist/commands/worker/cleanup-error.js.map +1 -0
  66. package/dist/commands/worker/cleanup-schedule.d.ts +3 -0
  67. package/dist/commands/worker/cleanup-schedule.js +11 -0
  68. package/dist/commands/worker/cleanup-schedule.js.map +1 -0
  69. package/dist/commands/worker/cleanup.d.ts +1 -3
  70. package/dist/commands/worker/cleanup.js +56 -43
  71. package/dist/commands/worker/cleanup.js.map +1 -1
  72. package/dist/commands/worker/coordinator.js +241 -97
  73. package/dist/commands/worker/coordinator.js.map +1 -1
  74. package/dist/commands/worker/execution.d.ts +1 -1
  75. package/dist/commands/worker/execution.js +2 -2
  76. package/dist/commands/worker/execution.js.map +1 -1
  77. package/dist/commands/worker/prompts.js +2 -2
  78. package/dist/commands/worker/prompts.js.map +1 -1
  79. package/dist/commands/worker/runtime.js +1 -1
  80. package/dist/commands/worker/runtime.js.map +1 -1
  81. package/dist/commands/worker/sqlite/006-cleanup-schedules.sql +14 -0
  82. package/dist/commands/worker/sqlite/007-resource-cleanup-intent.sql +2 -0
  83. package/dist/commands/worker/store.d.ts +5 -1
  84. package/dist/commands/worker/store.js +41 -0
  85. package/dist/commands/worker/store.js.map +1 -1
  86. package/dist/commands/worker/types.d.ts +8 -0
  87. package/dist/commands/worker/worktrees.d.ts +1 -0
  88. package/dist/commands/worker/worktrees.js +88 -34
  89. package/dist/commands/worker/worktrees.js.map +1 -1
  90. package/dist/repository/context.js +2 -3
  91. package/dist/repository/context.js.map +1 -1
  92. package/dist/repository/discovery-error.d.ts +5 -0
  93. package/dist/repository/discovery-error.js +18 -0
  94. package/dist/repository/discovery-error.js.map +1 -0
  95. package/dist/repository/index.d.ts +1 -0
  96. package/dist/repository/index.js +1 -0
  97. package/dist/repository/index.js.map +1 -1
  98. package/package.json +2 -2
package/README.md CHANGED
@@ -161,6 +161,9 @@ daynight task create --title <title> [options]
161
161
  daynight task list [--all | --status <status>] [--ready] [--assigned-to <assignee>] [--json] [--project <project-id>]
162
162
  daynight task ready [--limit <N>] [--json] [--project <project-id>]
163
163
  daynight task show <task-id> [--json] [--project <project-id>]
164
+ daynight task step
165
+ daynight task step list <task-id> [--json] [--project <project-id>]
166
+ daynight task step show <task-id> <step-id> [--json] [--project <project-id>]
164
167
  daynight task update <task-id> [options]
165
168
  daynight task set-status <task-id> <status> [--claim-id <claim-id>] [--if-version <version>] [--project <project-id>]
166
169
  daynight task delete <task-id> [--remove-depends] [--if-version <version>] [--project <project-id>]
@@ -194,7 +197,8 @@ dn --skill
194
197
 
195
198
  `--skill` prints one standalone English project/task usage manual from the
196
199
  current package. It covers project selection, task queries and writes,
197
- dependencies, properties and attachments. Other capabilities are discovered
200
+ dependencies, properties, attachments and ordered Step list/show reads. Other
201
+ capabilities are discovered
198
202
  through `daynight --help`, with exact options in each subcommand's help. The
199
203
  output preserves the manual's original bytes, including YAML
200
204
  frontmatter and the final newline. The document has no package-relative
@@ -773,8 +777,12 @@ case-equivalent duplicate/cross-declared UUIDs fail before configuration or
773
777
  network access. `--clear-dependencies` sends two empty sets and conflicts with
774
778
  either dependency option. An update needs at least one mutable
775
779
  field. Create/update accept `--assigned-to <user-uuid>`; update also accepts
776
- `--clear-assigned-to` to send null. Omission preserves an existing assignee; the
777
- set/clear options conflict, and invalid UUIDs or combinations fail before
780
+ `--clear-assigned-to` to send null. CLI creation without `--assigned-to` reads
781
+ the signed-in account through `/me` and submits its UUID. An explicit UUID skips
782
+ that identity lookup. A failed or cancelled lookup prevents creation; the CLI
783
+ does not substitute an unassigned task. Update omission preserves the existing
784
+ assignee. Direct API callers retain optional assignment and may create unassigned
785
+ tasks by omitting `assignedTo` or sending null. The set/clear options conflict, and invalid UUIDs or combinations fail before
778
786
  configuration, Git or HTTP access. Writes accept UUIDs, not the list tokens
779
787
  me/unassigned. A real assignment change atomically sets allowAgents=false; an
780
788
  equal assignment causes no permission reset; a pure equal update preserves
@@ -918,6 +926,58 @@ allowance. Creation/update expose ordinary metadata; property-specific PR
918
926
  options are not accepted. The native HTTP create/PATCH operations still support
919
927
  atomic fields/dependencies/properties mutations.
920
928
 
929
+ ## Task Steps
930
+
931
+ ```bash
932
+ daynight task step list <task-id>
933
+ daynight task step list <task-id> --json --project <project-id>
934
+ daynight task step show <task-id> <step-id> --json --project <project-id>
935
+ ```
936
+
937
+ Steps are an independent ordered resource owned by a Task. The
938
+ [resource contract](../../docs/spec/daynight-task-steps.md#cli-读取) owns their
939
+ fields and read behavior. The CLI provides `list` and `show`; the parent Task's
940
+ ordinary show/list/graph does not contain the full Steps collection. Step status
941
+ is `todo`, `in_progress`, `done` or `skipped`, independent from Task status.
942
+
943
+ Project and Task arguments are complete UUIDs. A Step argument is exactly eight
944
+ hex characters, normalized to lowercase on input with leading zeros preserved;
945
+ full UUIDs, prefixes and display numbers are invalid. Explicit `--project`
946
+ works outside Git and sends the selected Steps GET directly, without hidden
947
+ project or parent reads. Without it, normal repository project discovery runs.
948
+ Group and operation help complete before configuration, Git or network work.
949
+
950
+ List reads the complete collection once, retains saved order and prints the
951
+ Task, exact parent Version and Steps count, followed by one-based display numbers,
952
+ full short IDs, statuses and untruncated titles. Empty output reports zero Steps.
953
+ Show prints the complete target title, description, status and created/updated
954
+ timestamps with its Task/project scope and parent version. Both use visible
955
+ terminal-control and bidi-control escapes and preserve all text. `--json` emits one complete
956
+ StepsResource or StepResource with raw strings, every required field and exact
957
+ int64 JSON numbers, followed by LF; it does not use a Task-list projection or
958
+ output a bare Steps array.
959
+
960
+ Typed reads validate complete fields, requested scope, canonical response IDs,
961
+ collection uniqueness, four-state values, original text and UTC microsecond
962
+ timestamp strings. They preserve array order and time spelling without sorting,
963
+ rounding or filling missing values. Malformed or mismatched resources, duplicate
964
+ IDs and read failures remain errors rather than empty collections. Reads retain
965
+ the shared request/operation deadlines, cancellation and response-byte budgets.
966
+ The endpoint body cap is 64 MiB, inclusive, and is combined with the configured
967
+ response budget by taking the smaller bound. Stricter configured limits still
968
+ win, total-operation bytes remain charged, and shared defaults do not change;
969
+ no partial resource reaches stdout before full validation and rendering. An
970
+ output failure can still leave partial stdout and is reported as a failure.
971
+ Errors go to stderr with exit status 1; argument errors use 2 and success uses 0.
972
+ A 401 includes `daynight login` guidance without automatic login or retry.
973
+ Help, Bash/zsh completion and the standalone manual expose only these delivered
974
+ read commands. The [server acceptance owner](../daynight-server/README.md#built-process-acceptance-and-ci-evidence)
975
+ maps the real-service installed Node-entry read case, separate maximum-resource
976
+ check and their normal fixture prerequisites. The packed CLI acceptance runner
977
+ checks literal `daynight`/`dn` launchers through cross-spawn on native platforms.
978
+ Manual copy/byte checks establish package resource fidelity separately from
979
+ these complementary runtime and launcher checks.
980
+
921
981
  ## Task deletion
922
982
 
923
983
  Read the task and use its observed version when deleting it:
@@ -1317,13 +1377,25 @@ line separators and bidirectional display controls are escaped as visible text;
1317
1377
  Unicode titles and emoji remain readable. Full titles and owner IDs are retained,
1318
1378
  with Herdr responsible for fitting the label into its current terminal width.
1319
1379
 
1320
- The local database has exactly these business fields:
1380
+ The local claim index has these business fields:
1321
1381
 
1322
1382
  ```text
1323
1383
  projects(project_id, repo_url, local_path)
1324
1384
  tasks(project_id, task_id, claim_id, status, agent, session_id, agent_control_mode)
1325
1385
  ```
1326
1386
 
1387
+ A separate `cleanup_schedules` table binds terminal cleanup responsibility to
1388
+ the original Project/task/claim/worktree. It retains the first deadline, failure
1389
+ count, next retry time, stage and error. It contains no notification-progress or
1390
+ resource-completion flags.
1391
+ These records preserve responsibility; they do not prove that resources stopped
1392
+ or disappeared. A cleanup target without a known worktree ID keeps its existing
1393
+ claim-index row and uses process-local backoff while rereading authoritative
1394
+ resources. It never invents an ID or another ledger. An ended row with no globally
1395
+ observed resources still enters a serialized cleanup job, which verifies actual
1396
+ absence and removes only an empty unregistered reservation. A recognized resource
1397
+ for another claim of that task remains untouched.
1398
+
1327
1399
  Status is pending, active or done. Pending is an actual acquired claim waiting
1328
1400
  for startup; active owns startup, running, recovery or cleanup responsibility;
1329
1401
  done means the ended claim's resources have been cleaned. Completed rows remain,
@@ -1335,7 +1407,9 @@ The state directory belongs to the configured single service, Daynight account
1335
1407
  and workerId. Finish old executions before changing those identities and
1336
1408
  rebuilding local state. The running process pins its startup authenticated user;
1337
1409
  the claim-index database does not retain prior-service or prior-account recovery
1338
- scopes. The claim index has its own `user_version=5`. All five business databases
1410
+ scopes. The claim index and cleanup schedule share `user_version=7`; upgrading
1411
+ version 5 preserves its Project and task rows, while version 6 to 7 removes
1412
+ notification submission flags and preserves the remaining cleanup schedule. All five business databases
1339
1413
  reset `user_version=0` after their ownership checks: the original schema and data
1340
1414
  are discarded, then the baseline and numbered upgrades rebuild the current
1341
1415
  format. This includes existing empty files and query access. Known valid nonzero
@@ -1393,8 +1467,10 @@ pane's retained reference is not saved as a newly observed session ID. A live
1393
1467
  agent keeps its current connection.
1394
1468
 
1395
1469
  Startup, recovery and continuation prompts require autonomous decisions within
1396
- the task scope and existing authorization. When human intervention is necessary,
1397
- the managed agent sets its own task's `paused` property to true with an observed
1470
+ the task scope and existing authorization. Human intervention requires specific
1471
+ information, a decision, authorization or an action that the agent cannot supply
1472
+ within that scope and authority. Before waiting for that intervention, the
1473
+ managed agent sets its own task's `paused` property to true with an observed
1398
1474
  exact version and confirms the same assignment and pause before waiting. It
1399
1475
  reports the issue and retains session context; the user chooses how to respond.
1400
1476
  After verifying resolution, the original assignment and permission to resume,
@@ -1402,10 +1478,13 @@ it clears the pause and confirms false or absence before continuing. Independent
1402
1478
  user restrictions still apply. The [managed workflow](src/commands/skill/skills/using-daynight/references/autopilot-managed-task.md#request-user-interaction)
1403
1479
  owns readback, failure and conflict handling.
1404
1480
 
1405
- Ordinary review and CI waits remain ongoing execution: the agent waits, checks
1406
- results and continues delivery without ending its turn or setting a pause just
1407
- because a poll times out. Fixed native full access and folder trust apply to
1408
- every fresh/resumed process; task delivery remains within its business authorization.
1481
+ Pending PR review, required reviewer approval and CI remain ongoing execution,
1482
+ including reviews performed by a person. Pending results and polling timeouts
1483
+ do not justify setting `properties.paused=true`, requesting user intervention
1484
+ or ending the turn. The agent monitors results and continues delivery while
1485
+ honoring independent user pauses, restrictions and revocations. Fixed native
1486
+ full access and folder trust apply to every fresh/resumed process; task delivery
1487
+ remains within its business authorization.
1409
1488
 
1410
1489
  For a still-owned active claim, each patrol observes the live agent. An
1411
1490
  interactive-ready agent that is not launching and has no first input pending
@@ -1437,10 +1516,12 @@ agent's ID may fill a NULL record; inactive pane references remain excluded.
1437
1516
 
1438
1517
  The NULL rule also covers interruption before a newly started native ID was saved.
1439
1518
  It retains the task, claim and worktree and requires no history scan or approval
1440
- gate. A failed resume of a known ID remains an error. Launch/input receipts,
1441
- process observations, retries and cleanup progress exist only in the current
1442
- call or process. Recovery counts reset on restart; reaching recoveryLimit keeps
1443
- the task active with a visible error until explicit retry or another run.
1519
+ gate. A failed resume of a known ID remains an error. Business launch/input
1520
+ receipts, process observations and execution retries remain local to the current
1521
+ call or process in Herdr mode. Terminal cleanup retains its separate persistent
1522
+ schedule. Herdr execution recovery counts reset on restart; reaching
1523
+ recoveryLimit keeps the task active with a visible error until explicit retry or
1524
+ another run. Web recovery follows the persistent rules below.
1444
1525
 
1445
1526
  For human input, use another interactive terminal:
1446
1527
 
@@ -1485,41 +1566,72 @@ lost across a process interruption.
1485
1566
 
1486
1567
  During normal reconciliation, when the original claim is absent from a
1487
1568
  successful complete inventory, the worker asks its agent to release attributable
1488
- temporary files, services and processes outside the worktree. It waits at most
1489
- ten minutes for this cleanup attempt, stops the agent, closes its workspace, removes the matching worktree
1490
- and marks the local row done. Done, release and force withdrawal have the same terminal
1491
- resource rule. Failed stop or deletion retains responsibility and the error.
1492
-
1493
- Known-target cleanup and shutdown observe that Herdr execution directly;
1494
- worktree deletion validates only its target Git registration and directory.
1495
- Unrelated repository or sibling-worktree registration errors are reported
1496
- separately and do not prevent a healthy agent from stopping.
1497
-
1498
- After the cleanup message is successfully submitted, a subsequent patrol that
1499
- observes idle completes the cleanup turn and proceeds to stop the agent and
1500
- close its workspace. Sequence counters and interactive readiness are not
1501
- required. A pre-notification observation or the submission receipt itself
1502
- cannot finish cleanup. Failed or unknown notification results retain their error
1503
- and the current bounded wait, without automatic replay. Reaching the deadline
1504
- still proceeds through actual stop confirmation. Idle in this terminal cleanup
1505
- phase does not complete the business task.
1506
-
1507
- Cleanup timing and progress are in memory. Polls do not extend the current
1508
- attempt's deadline; after a crash, the next successful reconciliation observes
1509
- remaining resources and starts a new cleanup attempt and timer. Cleanup prompts
1510
- may repeat. Separate tasks have independent bounded jobs, so one held RPC does
1511
- not postpone another task's cleanup. A cleanup already selected in the current
1512
- process can continue during later service-read failures.
1569
+ temporary files, services and processes outside the worktree. It gives the
1570
+ original agent an advisory cleanup opportunity until fresh observations permit
1571
+ stopping or the original ten-minute deadline expires, then stops the agent,
1572
+ closes its workspace and reconciles the matching worktree. Actual resource
1573
+ absence permits marking the local row done. Done, release and force withdrawal have the same terminal
1574
+ resource rule. Cleanup observations, notification, stop/close, and worktree
1575
+ verification and deletion each have a five-minute operation budget, including
1576
+ their RPC, cleanup provenance HTTP verification, Controller cleanup requests
1577
+ and Git commands. Notification and
1578
+ Controller cleanup-response waits are also capped by the remaining ten-minute
1579
+ agent deadline. Ordinary execution keeps its existing operation budgets. Failed
1580
+ stop or deletion retains responsibility and the error.
1581
+
1582
+ Known-target cleanup observes that Herdr execution directly; worktree deletion
1583
+ validates only its target Git registration and directory. Shutdown proves the
1584
+ original processes stopped without depending on Git discovery or cleanup retry
1585
+ backoff. Unrelated repository or sibling-worktree registration errors are
1586
+ reported separately and do not prevent a healthy agent from stopping.
1587
+
1588
+ Each bounded Herdr cleanup call sends one advisory message to the still-live
1589
+ original agent, then reads fresh snapshots until idle permits stopping, resources
1590
+ disappear, or the original deadline expires. Sequence counters and interactive
1591
+ readiness are not required. Failed or unknown notification results retain their
1592
+ error and the same deadline without replay inside that call. Herdr provides no
1593
+ queryable per-input operation ID, so interruption or restart can notify the
1594
+ still-live agent again. The Worker keeps no notification progress ledger. Idle
1595
+ selects when to stop; only current process and workspace absence establishes
1596
+ resource cleanup. It does not complete the business task.
1597
+
1598
+ Terminal cleanup responsibility is recorded before notification or resource
1599
+ removal. The original resource identity, deadline and retry policy survive
1600
+ failure, cancellation and restart. Resource completion is always observed from
1601
+ Herdr, the Controller journal, OS process instances, Git and the filesystem.
1602
+ A missing worktree directory is already removed; its owned Git administration
1603
+ is a separate remaining resource. Empty reservations and final-unlink residues
1604
+ can be removed nonrecursively, while unattributable nonempty directories remain
1605
+ protected. Worktree preparation can likewise remove an empty unregistered
1606
+ reservation left before Git add and then create the intended checkout. Such stray
1607
+ directories do not invalidate repository-wide discovery.
1608
+ Restart resumes the same claim's remaining resources without starting business
1609
+ execution. Previously confirmed cleanup responsibility can continue during later
1610
+ service-read failures.
1611
+
1612
+ Failed claims retry independently after 1, 2, 4, 8, 16, 32 and then at most 60
1613
+ seconds, when normal reconciliation next observes eligibility. Backoff does not
1614
+ spend execution recovery budget or release a capacity slot. Other eligible
1615
+ cleanup and healthy execution continue under the existing capacity limit. Errors
1616
+ identify the failed stage and next retry time, distinguishing unconfirmed native
1617
+ stop from a stopped agent awaiting backend closure, archive upload or worktree
1618
+ removal. If a retry-state write fails, the current process retains bounded
1619
+ backoff and both errors without claiming that the latest decision is durable.
1513
1620
 
1514
1621
  Ctrl+C stops acquisition and scheduling, cancels all running jobs including
1515
1622
  terminal cleanup, and joins them before directly stopping agents and closing
1516
- their workspaces. Process exit sends no cleanup prompt and does not wait for a
1517
- cleanup turn. It preserves claims, worktrees, native session IDs and all
1518
- unfinished active/pending rows, including those whose ownership has ended. The
1519
- next run uses normal inventory reconciliation to re-enter terminal cleanup for
1520
- ended claims. External temporary resources may remain after direct interruption.
1521
- Normal completion, release, withdrawal and manual-control cleanup retain their
1522
- notification and removal sequence. Pending stop confirmations must settle before the private
1623
+ their workspaces. Process exit sends no cleanup prompt, does not wait for a
1624
+ cleanup turn or retry backoff, and does not repeat archive upload or worktree
1625
+ removal. It preserves claims, worktrees, native session IDs, unfinished
1626
+ active/pending rows and existing terminal cleanup schedules, including ended
1627
+ claims. The next run restores each saved responsibility and original deadline, then
1628
+ verifies remaining resources. A retained Herdr cleanup
1629
+ still requires its original backend even if current configuration uses Web;
1630
+ backend initialization and retries remain local to that cleanup job and cannot
1631
+ block unrelated Web execution. External temporary resources may remain after
1632
+ direct interruption.
1633
+ Normal completion, release, withdrawal and manual-control cleanup use the same
1634
+ advisory notification and observed resource-removal targets. Pending stop confirmations must settle before the private
1523
1635
  Herdr instance stops; stop errors remain visible, and uncertain stopping retains
1524
1636
  that instance and the affected resources. Before closing a verified orphan
1525
1637
  workspace with no local task row, cleanup and exit save its observed native
@@ -1575,22 +1687,46 @@ required separately from protocol tests.
1575
1687
 
1576
1688
  The primary worker database retains its claim and native-session index. Web
1577
1689
  execution state lives separately in `~/.daynight/worker/web-executions.sqlite`
1578
- with its own format version 1. Version 0 resets its schema and data through the
1579
- common SQLite policy; current-version CRUD retains the existing JSON parser and
1580
- default-field behavior. Private launch tickets and Controller
1690
+ with its own format version 2. The version 1 upgrade removes redundant cleanup
1691
+ input/results and stopped/closed/deleted flags, preserving cleanup intent,
1692
+ identity and deadline. Version 0 resets its schema and data through the common
1693
+ SQLite policy. Private launch tickets and Controller
1581
1694
  journals live under `~/.daynight/controller/`; journals retain their independent
1582
- format version 5. Both Worker stores must open successfully before scheduling.
1695
+ format version 6. Both Worker stores must open successfully before scheduling.
1583
1696
  Web restart reconnects to a surviving Controller. Starting a new generation
1584
1697
  requires proof that the original native processes stopped and the exact native
1585
1698
  session ID. A missing journal or ID cannot authorize a replacement conversation.
1586
1699
  Unknown initial inputs and operations are reconciled without replay.
1587
1700
 
1588
- Normal Web terminal cleanup keeps its original cleanup operation and deadline,
1589
- then proves native stop before deleting the task worktree. Worker shutdown stops
1701
+ Web recovery counts and diagnostic pause reasons persist across worker restarts.
1702
+ Exhausted automatic recovery or the execution failure threshold pauses the
1703
+ original execution before worktree preparation, Controller initialization,
1704
+ launch or native input. An older exhausted stopped record without a pause reason
1705
+ or orderly-shutdown provenance is reconciled from its journal and complete
1706
+ process-stop proof before any launch;
1707
+ uncertain stop evidence retains a diagnostic wait. A healthy final recovery
1708
+ and its orderly shutdown restart are not paused merely because the count has
1709
+ reached its limit.
1710
+
1711
+ A paused execution keeps its active claim, native session ID, worktree and
1712
+ capacity slot. The worker continues publishing `waiting` with `herdr:null` even
1713
+ when execution is blocked. A failed summary update is retried using a newly read
1714
+ task version without consuming native recovery budget; an unchanged confirmed
1715
+ summary is not written again. Ordinary restart and shutdown cannot clear this
1716
+ pause. It is separate from `task.properties.paused`, which controls Herdr
1717
+ continuation pings.
1718
+
1719
+ Normal Web terminal cleanup keeps its original cleanup operation identity and
1720
+ deadline. Every attempt reads its operation receipt from the original Controller
1721
+ journal, checks original process instances against the OS, reconciles the IPC
1722
+ endpoint and remaining Git resources, and uses the resulting current observations
1723
+ to determine completion. A recorded done status never suppresses these checks.
1724
+ Native stop and the service's ready archive receipt precede worktree removal. Worker shutdown stops
1590
1725
  owned processes while preserving unfinished claims, worktrees and native history.
1591
1726
  The [session design](../../docs/design/task-session-protocol.md) owns stopping,
1592
- recovery and relay details; the Herdr lifecycle above retains its process-local
1593
- recovery and cleanup semantics.
1727
+ recovery and relay details. Herdr business recovery remains process-local; both
1728
+ modes retain terminal cleanup responsibility, its original deadline and retry
1729
+ policy in the independent schedule, without resource-completion flags.
1594
1730
 
1595
1731
  The worker starts `daynight controller codex|claude|copilot --launch-id <uuid>`
1596
1732
  from a private immutable launch ticket. Its cwd, profile, claim, generation,
@@ -1684,11 +1820,22 @@ Retry performs one attempt for an active claim still owned by this worker:
1684
1820
  daynight worker retry <task-id> --claim-id <claim-id> --project <project-id>
1685
1821
  ```
1686
1822
 
1687
- It connects the live agent or resumes its recorded native session. A NULL ID
1688
- starts a fresh conversation in the same task worktree with an `info` event. The
1689
- claim and agent type remain; pending rows are started through normal run
1690
- scheduling. After the attempt, run connects to the actual surviving resource. Retry does not
1691
- persist a permit or a cumulative cross-restart budget.
1823
+ In Herdr mode, retry connects the live agent or resumes its recorded native
1824
+ session. A NULL ID starts a fresh conversation in the same task worktree with an
1825
+ `info` event. Its extra attempt and recovery count remain process-local.
1826
+
1827
+ In Web mode, retry persists one authorization to recover the original native
1828
+ session. It still requires the original active claim, matching generation and
1829
+ complete proof that the old processes stopped. Missing native identity or stop
1830
+ evidence cannot authorize a replacement conversation or a repeated initial
1831
+ assignment. Authorization does not report successful execution: the summary
1832
+ remains waiting through initialization until a ready or working observation, and failure
1833
+ pauses again. The attempt remains bound to that generation across ticks and
1834
+ worker restarts; initialization cannot authorize another generation.
1835
+
1836
+ Both modes retain the claim and agent type; pending rows are started through
1837
+ normal run scheduling. After the attempt, run reconciles the actual surviving
1838
+ resource and any persistent Web pause.
1692
1839
 
1693
1840
  Release validates current Project access, source owner/admin, original claim
1694
1841
  and exact supplied version. Its ordering is:
@@ -1,10 +1,11 @@
1
- import type { ThreadReadQuery, PostThreadMessageInput, ThreadMessage, ThreadPage } from "./types.js";
1
+ import type { StepsResource, StepResource, ThreadReadQuery, PostThreadMessageInput, ThreadMessage, ThreadPage } from "./types.js";
2
2
  import type { Operation } from "./operation.js";
3
- import type { AttachmentConsumer, CreateProjectInput, CreateTaskInput, DeleteTaskInput, ErrorDetails, Project, ProjectAccess, ProjectMember, PutTaskAttachmentInput, ReadyTaskFilters, ServerError, Task, SetTaskStatusInput, TaskAttachment, TaskFilters, TaskGraph, UpdateTaskInput, ClaimWorkerTaskInput, CompleteWorkerTaskInput, ReleaseWorkerClaimInput, ResumeWorkerTaskInput, WithdrawWorkerClaimInput, UpdateWorkerClaimInput, User, Page, TaskPageQuery, WorkerReport, WorkerReportReceipt, TaskPropertyResource, TaskPropertySchema, SetTaskPropertyInput, RemoveTaskPropertyInput } from "./types.js";
3
+ import type { AttachmentConsumer, CreateProjectInput, CreateTaskInput, DeleteTaskInput, ErrorDetails, Project, ProjectAccess, ProjectParticipant, PutTaskAttachmentInput, ReadyTaskFilters, ServerError, Task, SetTaskStatusInput, TaskAttachment, TaskFilters, TaskGraph, UpdateTaskInput, ClaimWorkerTaskInput, CompleteWorkerTaskInput, ReleaseWorkerClaimInput, ResumeWorkerTaskInput, WithdrawWorkerClaimInput, UpdateWorkerClaimInput, User, Page, TaskPageQuery, WorkerReport, WorkerReportReceipt, TaskPropertyResource, TaskPropertySchema, SetTaskPropertyInput, RemoveTaskPropertyInput } from "./types.js";
4
4
  type Context = {
5
5
  readonly operation: string;
6
6
  readonly projectId?: string;
7
7
  readonly taskId?: string;
8
+ readonly stepId?: string;
8
9
  };
9
10
  export type ApiPhase = "prepare" | "request" | "response" | "protocol" | "pagination";
10
11
  export declare class ApiError extends Error {
@@ -12,6 +13,8 @@ export declare class ApiError extends Error {
12
13
  readonly phase: ApiPhase;
13
14
  readonly projectId?: string;
14
15
  readonly taskId?: string;
16
+ readonly stepId?: string;
17
+ readonly serverMessage?: string;
15
18
  readonly status?: number;
16
19
  readonly code?: string;
17
20
  readonly requestId?: string;
@@ -29,6 +32,8 @@ export declare class ApiError extends Error {
29
32
  });
30
33
  }
31
34
  export type ApiClient = {
35
+ listTaskSteps(projectId: string, taskId: string): Promise<StepsResource>;
36
+ getTaskStep(projectId: string, taskId: string, stepId: string): Promise<StepResource>;
32
37
  readTaskThread(projectId: string, taskId: string, query: ThreadReadQuery): Promise<ThreadPage>;
33
38
  postTaskThreadMessage(projectId: string, taskId: string, input: PostThreadMessageInput): Promise<ThreadMessage>;
34
39
  getCurrentUser(): Promise<User>;
@@ -37,7 +42,7 @@ export type ApiClient = {
37
42
  getProject(projectId: string): Promise<Project>;
38
43
  getProjectAccess(projectId: string): Promise<ProjectAccess>;
39
44
  listProjects(): Promise<readonly Project[]>;
40
- listProjectMembers(projectId: string): Promise<readonly ProjectMember[]>;
45
+ listProjectMembers(projectId: string): Promise<readonly ProjectParticipant[]>;
41
46
  createTask(projectId: string, input: CreateTaskInput): Promise<Task>;
42
47
  getTask(projectId: string, taskId: string): Promise<Task>;
43
48
  getTaskPropertySchema(): Promise<TaskPropertySchema>;
@@ -1,17 +1,20 @@
1
1
  import { parseTaskPropertyResource, parseTaskPropertySchema, readWorkerClaim } from "./task-properties.js";
2
2
  import { resourcePath } from "./resource-path.js";
3
+ import { canonicalStepId, decodeStepsJsonSource, parseStepResource, parseStepsResource, TASK_STEPS_RESPONSE_BYTES } from "./task-steps.js";
3
4
  import { decodeThreadJsonSource, parseThreadMessage, parseThreadPage, THREAD_MESSAGE_BYTES, THREAD_PAGE_BYTES } from "./thread.js";
4
5
  import { isNonemptyText } from "../platform/text.js";
5
6
  import { performance } from "node:perf_hooks";
6
7
  import { parseApiJson, stringifyApiJson } from "./json.js";
7
8
  import { OperationError } from "./operation.js";
8
9
  import { validateServerToken, validateTokenTransport } from "../config/server-token.js";
9
- import { canonicalId, parsePage, parseProject, parseProjectAccess, parseProjectMembers, parseServerError, parseTask, parseTaskGraph, parseTaskAttachment, parseUser, parseWorkerReportReceipt, ProtocolError, validateAttachmentPath } from "./parse.js";
10
+ import { canonicalId, parsePage, parseProject, parseProjectAccess, parseProjectParticipants, parseServerError, parseTask, parseTaskGraph, parseTaskAttachment, parseUser, parseWorkerReportReceipt, ProtocolError, validateAttachmentPath } from "./parse.js";
10
11
  export class ApiError extends Error {
11
12
  operation;
12
13
  phase;
13
14
  projectId;
14
15
  taskId;
16
+ stepId;
17
+ serverMessage;
15
18
  status;
16
19
  code;
17
20
  requestId;
@@ -20,7 +23,7 @@ export class ApiError extends Error {
20
23
  confirmedRefusal;
21
24
  confirmedMessageId;
22
25
  constructor(context, phase, options) {
23
- const target = [context.projectId === undefined ? "" : ` project=${context.projectId}`, context.taskId === undefined ? "" : ` task=${context.taskId}`].join("");
26
+ const target = [context.projectId === undefined ? "" : ` project=${context.projectId}`, context.taskId === undefined ? "" : ` task=${context.taskId}`, context.stepId === undefined ? "" : ` step=${context.stepId}`].join("");
24
27
  const server = options.serverError;
25
28
  const reason = server === undefined
26
29
  ? options.cause instanceof OperationError ? options.cause.message : phase === "protocol" || phase === "pagination" ? "Invalid server response." : "Local HTTP request failed."
@@ -35,11 +38,14 @@ export class ApiError extends Error {
35
38
  this.projectId = context.projectId;
36
39
  if (context.taskId !== undefined)
37
40
  this.taskId = context.taskId;
41
+ if (context.stepId !== undefined)
42
+ this.stepId = context.stepId;
38
43
  if (options.status !== undefined)
39
44
  this.status = options.status;
40
45
  if (server !== undefined) {
41
46
  this.code = server.code;
42
47
  this.requestId = server.requestId;
48
+ this.serverMessage = server.message;
43
49
  }
44
50
  if (details !== undefined)
45
51
  this.details = details;
@@ -413,6 +419,22 @@ export const createApiClient = (options) => {
413
419
  } }, [200], { kind: "json", value: input }, true);
414
420
  };
415
421
  return {
422
+ listTaskSteps: (projectId, taskId) => {
423
+ const project = resourceId(projectId), task = resourceId(taskId);
424
+ return exchange({ operation: "task.step.list", projectId: project, taskId: task }, resourcePath("projects", project, "tasks", task, "steps"), "GET", {
425
+ kind: "json", requirePrincipal: true, responseBytes: TASK_STEPS_RESPONSE_BYTES, decodeJsonSource: decodeStepsJsonSource,
426
+ parse: (value) => parseStepsResource(value, project, task),
427
+ }, [200], undefined, true);
428
+ },
429
+ getTaskStep: (projectId, taskId, stepId) => {
430
+ const project = resourceId(projectId), task = resourceId(taskId), step = canonicalStepId(stepId);
431
+ if (step === null)
432
+ throw new Error("Step ID must contain exactly eight hexadecimal characters.");
433
+ return exchange({ operation: "task.step.show", projectId: project, taskId: task, stepId: step }, resourcePath("projects", project, "tasks", task, "steps", step), "GET", {
434
+ kind: "json", requirePrincipal: true, responseBytes: TASK_STEPS_RESPONSE_BYTES, decodeJsonSource: decodeStepsJsonSource,
435
+ parse: (value) => parseStepResource(value, project, task, step),
436
+ }, [200], undefined, true);
437
+ },
416
438
  readTaskThread: (projectId, taskId, query) => {
417
439
  const project = resourceId(projectId), task = resourceId(taskId);
418
440
  if (!Number.isInteger(query.count) || query.count < 1 || query.count > 100 || typeof query.details !== "boolean" || query.beforeId !== undefined && query.afterId !== undefined)
@@ -460,7 +482,7 @@ export const createApiClient = (options) => {
460
482
  },
461
483
  listProjectMembers: (projectId) => {
462
484
  const project = resourceId(projectId);
463
- return exchange({ operation: "project.members", projectId: project }, `/projects/${encodeURIComponent(project)}/members`, "GET", { kind: "json", parse: (value) => parseProjectMembers(value, project) }, [200], undefined, true);
485
+ return exchange({ operation: "project.members", projectId: project }, `/projects/${encodeURIComponent(project)}/members`, "GET", { kind: "json", parse: (value) => parseProjectParticipants(value, project) }, [200], undefined, true);
464
486
  },
465
487
  createTask: (projectId, input) => {
466
488
  const project = resourceId(projectId);