taskops-cli 0.5.8__tar.gz → 0.6.1__tar.gz

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 (325) hide show
  1. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ARCHITECTURE.md +101 -0
  2. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/CHANGELOG.md +66 -0
  3. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/CLAUDE.md +6 -0
  4. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/PKG-INFO +1 -1
  5. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/_version.py +1 -1
  6. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/store/cache.py +13 -2
  7. taskops_cli-0.6.1/src/taskops/ui/app.js +70 -0
  8. taskops_cli-0.6.1/src/taskops/verbs/events.py +89 -0
  9. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_verbs.py +58 -0
  10. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/editor-page.tsx +10 -0
  11. taskops_cli-0.6.1/ui/smoke/sections/stream-rail.tsx +378 -0
  12. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/App.tsx +28 -0
  13. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/card/Thread.tsx +44 -0
  14. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/monitor/EventStream.tsx +7 -30
  15. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/monitor/panels.ts +19 -0
  16. taskops_cli-0.6.1/ui/src/components/stream/Grip.tsx +101 -0
  17. taskops_cli-0.6.1/ui/src/components/stream/Stream.tsx +522 -0
  18. taskops_cli-0.6.1/ui/src/components/stream/model.ts +425 -0
  19. taskops_cli-0.6.1/ui/src/components/stream/useStream.ts +121 -0
  20. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/toasts/model.ts +14 -0
  21. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/pages/Editor.tsx +117 -2
  22. taskops_cli-0.6.1/ui/src/useEvents.ts +349 -0
  23. taskops_cli-0.5.8/src/taskops/ui/app.js +0 -70
  24. taskops_cli-0.5.8/src/taskops/verbs/events.py +0 -60
  25. taskops_cli-0.5.8/ui/src/useEvents.ts +0 -160
  26. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.claude/settings.json +0 -0
  27. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.gitattributes +0 -0
  28. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.github/workflows/ci.yml +0 -0
  29. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.github/workflows/workflow.yml +0 -0
  30. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.gitignore +0 -0
  31. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.mcp.json +0 -0
  32. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.taskops/board/cache.sqlite +0 -0
  33. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.taskops/board/cache.sqlite-shm +0 -0
  34. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.taskops/board/cache.sqlite-wal +0 -0
  35. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.taskops/board/events.jsonl +0 -0
  36. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.taskops/board/live.sqlite +0 -0
  37. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.taskops/board/live.sqlite-shm +0 -0
  38. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.taskops/board/live.sqlite-wal +0 -0
  39. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.taskops/board.json +0 -0
  40. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.taskops/reports/lifecycle-chapter.html +0 -0
  41. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.taskops/reports/reports-chapter.html +0 -0
  42. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/.taskops/ui.lock +0 -0
  43. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/README.md +0 -0
  44. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/pyproject.toml +0 -0
  45. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/smoke.sh +0 -0
  46. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/__init__.py +0 -0
  47. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/_clock.py +0 -0
  48. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/_errors.py +0 -0
  49. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/_ids.py +0 -0
  50. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/_json.py +0 -0
  51. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/_locate.py +0 -0
  52. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/_wire.py +0 -0
  53. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/board.py +0 -0
  54. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/__init__.py +0 -0
  55. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/__main__.py +0 -0
  56. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/admin.py +0 -0
  57. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/claude.py +0 -0
  58. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/commands.py +0 -0
  59. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/enrol.py +0 -0
  60. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/github.py +0 -0
  61. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/gitremote.py +0 -0
  62. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/grants.py +0 -0
  63. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/hooks.py +0 -0
  64. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/main.py +0 -0
  65. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/operate.py +0 -0
  66. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/paging.py +0 -0
  67. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/parser.py +0 -0
  68. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/pull.py +0 -0
  69. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/push.py +0 -0
  70. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/remote.py +0 -0
  71. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/rm.py +0 -0
  72. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/serving.py +0 -0
  73. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/team.py +0 -0
  74. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/watch.py +0 -0
  75. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/window.py +0 -0
  76. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/cli/wording.py +0 -0
  77. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/__init__.py +0 -0
  78. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/actors.py +0 -0
  79. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/challenge.py +0 -0
  80. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/chapters.py +0 -0
  81. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/event.py +0 -0
  82. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/forge.py +0 -0
  83. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/graph.py +0 -0
  84. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/holding.py +0 -0
  85. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/hours.py +0 -0
  86. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/kinds.py +0 -0
  87. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/machine.py +0 -0
  88. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/mentions.py +0 -0
  89. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/replay.py +0 -0
  90. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/reports.py +0 -0
  91. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/review.py +0 -0
  92. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/scope.py +0 -0
  93. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/seams.py +0 -0
  94. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/core/types.py +0 -0
  95. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/__init__.py +0 -0
  96. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/bare.py +0 -0
  97. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/bind.py +0 -0
  98. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/catchup.py +0 -0
  99. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/claudefiles.py +0 -0
  100. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/diff.py +0 -0
  101. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/inhabited.py +0 -0
  102. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/install.py +0 -0
  103. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/landing.py +0 -0
  104. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/onward.py +0 -0
  105. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/patch.py +0 -0
  106. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/reading.py +0 -0
  107. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/remote.py +0 -0
  108. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/run.py +0 -0
  109. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/scan.py +0 -0
  110. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/sig.py +0 -0
  111. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/trailer.py +0 -0
  112. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/gitwork/trees.py +0 -0
  113. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/__init__.py +0 -0
  114. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/admin.py +0 -0
  115. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/auth.py +0 -0
  116. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/editor.py +0 -0
  117. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/feed.py +0 -0
  118. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/gitbody.py +0 -0
  119. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/gitdoor.py +0 -0
  120. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/gitpack.py +0 -0
  121. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/grants.py +0 -0
  122. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/handler.py +0 -0
  123. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/health.py +0 -0
  124. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/ingest.py +0 -0
  125. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/listing.py +0 -0
  126. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/login.py +0 -0
  127. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/members.py +0 -0
  128. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/mounts.py +0 -0
  129. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/page.py +0 -0
  130. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/removal.py +0 -0
  131. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/repos.py +0 -0
  132. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/routes.py +0 -0
  133. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/rpc.py +0 -0
  134. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/scoped.py +0 -0
  135. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/server.py +0 -0
  136. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/stale.py +0 -0
  137. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/static.py +0 -0
  138. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/treewatch.py +0 -0
  139. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/upstream.py +0 -0
  140. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/http/watcher.py +0 -0
  141. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/identity.py +0 -0
  142. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/__init__.py +0 -0
  143. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/__main__.py +0 -0
  144. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/activity.py +0 -0
  145. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/before.py +0 -0
  146. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/boards.py +0 -0
  147. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/boardview.py +0 -0
  148. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/brief.py +0 -0
  149. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/chapter.py +0 -0
  150. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/dossier.py +0 -0
  151. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/fields.py +0 -0
  152. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/gitmoves.py +0 -0
  153. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/hello.py +0 -0
  154. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/integrate.py +0 -0
  155. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/orders.py +0 -0
  156. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/render.py +0 -0
  157. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/schema.py +0 -0
  158. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/server.py +0 -0
  159. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/thread.py +0 -0
  160. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/mcp/tools.py +0 -0
  161. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/py.typed +0 -0
  162. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/session.py +0 -0
  163. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/store/__init__.py +0 -0
  164. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/store/creds.py +0 -0
  165. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/store/handover.py +0 -0
  166. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/store/live.py +0 -0
  167. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/store/log.py +0 -0
  168. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/store/mirroring.py +0 -0
  169. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/store/pubkeys.py +0 -0
  170. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/store/reviews.py +0 -0
  171. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/store/server.py +0 -0
  172. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/store/stores.py +0 -0
  173. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/ui/index.html +0 -0
  174. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/ui/style.css +0 -0
  175. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/__init__.py +0 -0
  176. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/_args.py +0 -0
  177. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/_cards.py +0 -0
  178. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/_chapter.py +0 -0
  179. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/_context.py +0 -0
  180. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/_facts.py +0 -0
  181. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/_mentions.py +0 -0
  182. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/_rows.py +0 -0
  183. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/_stories.py +0 -0
  184. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/_waiting.py +0 -0
  185. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/_windows.py +0 -0
  186. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/activity.py +0 -0
  187. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/assign.py +0 -0
  188. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/card.py +0 -0
  189. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/filed.py +0 -0
  190. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/plan.py +0 -0
  191. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/project.py +0 -0
  192. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/pulse.py +0 -0
  193. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/record.py +0 -0
  194. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/report.py +0 -0
  195. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/review.py +0 -0
  196. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/take.py +0 -0
  197. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/src/taskops/verbs/update.py +0 -0
  198. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/__init__.py +0 -0
  199. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/conftest.py +0 -0
  200. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_architecture.py +0 -0
  201. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_claude.py +0 -0
  202. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_core.py +0 -0
  203. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_editor.py +0 -0
  204. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_git.py +0 -0
  205. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_mcp.py +0 -0
  206. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_mirror.py +0 -0
  207. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_report.py +0 -0
  208. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_store.py +0 -0
  209. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_topology.py +0 -0
  210. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_ui.py +0 -0
  211. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/test_window.py +0 -0
  212. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/tests/ui_harness.js +0 -0
  213. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/build.mjs +0 -0
  214. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/index.html +0 -0
  215. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/package-lock.json +0 -0
  216. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/package.json +0 -0
  217. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/fixture.json +0 -0
  218. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/main.tsx +0 -0
  219. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/run.mjs +0 -0
  220. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/actors-window-filter.tsx +0 -0
  221. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/actors.tsx +0 -0
  222. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/all-chapters-scope.tsx +0 -0
  223. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/board-columns.tsx +0 -0
  224. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/board-flip-abandoned-play.tsx +0 -0
  225. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/board-flow-view.tsx +0 -0
  226. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/board-motion-and-avatars.tsx +0 -0
  227. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/board-root-mount.tsx +0 -0
  228. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/card-diff-range.tsx +0 -0
  229. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/card-progress.tsx +0 -0
  230. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/chapter-completion-derivation.tsx +0 -0
  231. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/chapter-landed.tsx +0 -0
  232. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/chapter-story-grid.tsx +0 -0
  233. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/chapters-open.tsx +0 -0
  234. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/comment-box.tsx +0 -0
  235. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/comment-toasts-model.tsx +0 -0
  236. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/comment-toasts-stack.tsx +0 -0
  237. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/daysheet-midnight-spill.tsx +0 -0
  238. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/diff-page.tsx +0 -0
  239. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/dossier.tsx +0 -0
  240. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/event-stream.tsx +0 -0
  241. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/feed-reconnect.tsx +0 -0
  242. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/forge-links.tsx +0 -0
  243. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/forge-opens-the-board.tsx +0 -0
  244. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/git-diff.tsx +0 -0
  245. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/hosted-window.tsx +0 -0
  246. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/monitor-panes.tsx +0 -0
  247. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/overlay-escape.tsx +0 -0
  248. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/report-markdown.tsx +0 -0
  249. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/report-sandbox.tsx +0 -0
  250. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/reviewing-skew.tsx +0 -0
  251. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/section.ts +0 -0
  252. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/swarm-topology.tsx +0 -0
  253. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/thread-closing-note.tsx +0 -0
  254. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/worktree-page-range.tsx +0 -0
  255. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections/worktrees-index.tsx +0 -0
  256. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/smoke/sections.mjs +0 -0
  257. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/client.ts +0 -0
  258. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/completed.ts +0 -0
  259. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/actors/Daysheet.tsx +0 -0
  260. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/actors/DevPanel.tsx +0 -0
  261. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/board/CardTile.tsx +0 -0
  262. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/board/Column.tsx +0 -0
  263. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/board/ViewToggle.tsx +0 -0
  264. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/board/flip.ts +0 -0
  265. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/board/useFlip.ts +0 -0
  266. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/card/CommentBox.tsx +0 -0
  267. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/card/Drawer.tsx +0 -0
  268. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/card/Patch.tsx +0 -0
  269. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/card/Sections.tsx +0 -0
  270. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/card/split.ts +0 -0
  271. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/card/tokens.ts +0 -0
  272. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/chrome/AvatarStack.tsx +0 -0
  273. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/chrome/Header.tsx +0 -0
  274. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/chrome/KpiRail.tsx +0 -0
  275. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/chrome/MilestonePicker.tsx +0 -0
  276. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/chrome/TabNav.tsx +0 -0
  277. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/editor/CodeView.tsx +0 -0
  278. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/editor/FileTree.tsx +0 -0
  279. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/editor/Icon.tsx +0 -0
  280. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/editor/QuickOpen.tsx +0 -0
  281. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/editor/Tabs.tsx +0 -0
  282. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/editor/fuzzy.ts +0 -0
  283. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/editor/highlight.ts +0 -0
  284. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/editor/icons.ts +0 -0
  285. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/editor/tree.ts +0 -0
  286. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/editor/useWorktree.ts +0 -0
  287. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/flow/FlowView.tsx +0 -0
  288. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/flow/layout.ts +0 -0
  289. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/monitor/Chapter.tsx +0 -0
  290. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/monitor/DependencyChain.tsx +0 -0
  291. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/monitor/EditSurface.tsx +0 -0
  292. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/monitor/LeaseHealth.tsx +0 -0
  293. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/monitor/LiveLeases.tsx +0 -0
  294. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/monitor/Mentions.tsx +0 -0
  295. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/monitor/Pane.tsx +0 -0
  296. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/monitor/Swarm.tsx +0 -0
  297. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/monitor/Throughput.tsx +0 -0
  298. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/reports/ReportFrame.tsx +0 -0
  299. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/shared/Avatar.tsx +0 -0
  300. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/shared/Markdown.tsx +0 -0
  301. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/shared/Overlay.tsx +0 -0
  302. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/shared/Segmented.tsx +0 -0
  303. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/shared/overlayStack.ts +0 -0
  304. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/story/ChapterStory.tsx +0 -0
  305. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/story/stats.ts +0 -0
  306. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/toasts/ToastStack.tsx +0 -0
  307. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/components/toasts/useToasts.ts +0 -0
  308. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/format.ts +0 -0
  309. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/hoursWindow.ts +0 -0
  310. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/links.tsx +0 -0
  311. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/main.tsx +0 -0
  312. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/markdown.ts +0 -0
  313. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/pages/Actors.tsx +0 -0
  314. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/pages/Board.tsx +0 -0
  315. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/pages/Monitor.tsx +0 -0
  316. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/pages/Reports.tsx +0 -0
  317. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/pages/WorktreeDiff.tsx +0 -0
  318. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/pages/Worktrees.tsx +0 -0
  319. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/theme/theme.ts +0 -0
  320. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/theme/tokens.css +0 -0
  321. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/types.ts +0 -0
  322. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/src/useBoard.ts +0 -0
  323. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/styles/index.css +0 -0
  324. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/ui/tsconfig.json +0 -0
  325. {taskops_cli-0.5.8 → taskops_cli-0.6.1}/uv.lock +0 -0
@@ -3112,3 +3112,104 @@ or highlight.js with the eleven grammars): keyword, string, comment, number,
3112
3112
  tag, attribute — enough to read by, in six `--code-*` tokens that live in
3113
3113
  `theme/tokens.css` beside the palette rather than borrowing its status colours.
3114
3114
  The bundle grew 288 307 → 313 294 bytes for the whole feature.
3115
+
3116
+ ## 23. The Stream — the log beside the code, live (2026-09-08)
3117
+
3118
+ Berna asked for "un streaming de comentarios en tiempo real … cada progress,
3119
+ cada comment, se muestra, se highlitean los nuevos, se puede ver el card tipo
3120
+ popup … al lado del editor … con una excelente organización y agrupación". It
3121
+ is a third column inside the Editor (`ui/src/components/stream/`), shut by
3122
+ default, opened from the one bar that page has.
3123
+
3124
+ **The socket did not change, and that is the whole design.** `http/feed.py`
3125
+ publishes `{"type":"change","verb","seq"}` and nothing else, because that is
3126
+ what lets a PUBLIC board's feed open with no credential: a watcher learns only
3127
+ THAT the board moved and re-reads through `/rpc`, where the anonymous gate
3128
+ applies again in full. A live comment column is exactly the feature that would
3129
+ have tempted a payload onto that frame, and the day a message carries a card
3130
+ that door has to close with it. So: the socket pokes, the page reads.
3131
+
3132
+ **What DID change is the direction the log can be read in.** `events` used to
3133
+ answer backwards only — newest-first pages on a `before=` cursor — so "what
3134
+ arrived since I looked" was a thing the client had to DERIVE: `head - lastHead`
3135
+ rows off page one, plus a set of already-seen ids as the safety net for a burst
3136
+ larger than a page (`ui/src/components/toasts/model.ts` is the post-mortem of
3137
+ that arithmetic, and it is exact only until it is not).
3138
+
3139
+ ```
3140
+ events → the newest page next = the older cursor
3141
+ events before=<seq> → further back next = the older cursor
3142
+ events after=<seq> → what arrived since, next = carry on from here
3143
+ OLDEST first (a full page means more)
3144
+ ```
3145
+
3146
+ `next` means the same thing in both directions — the cursor for the page you
3147
+ have not read yet, in the direction you are reading — and the two cannot be
3148
+ asked at once, which is two questions and is refused as one. The SQL is
3149
+ `store/cache.py::since`, which replay already used, given a `limit`: a
3150
+ catch-up is capped on the OLDEST end, which is the one thing a descending
3151
+ `page()` cannot do.
3152
+
3153
+ **The cursor a live reader carries is `head`, not a seq per row.** The rowid is
3154
+ still dropped on the way out (`verbs/events.py:53` was and remains the shape).
3155
+ A reader keeps the `head` of the answer it read and asks `after=<that head>`;
3156
+ what comes back IS the news, by construction — no arithmetic, no dedupe, and no
3157
+ way to be wrong when six hundred events landed while the tab was asleep, because
3158
+ then it pages forward on `next` and, past `CATCH_UP_PAGES`, starts over on page
3159
+ one rather than drawing a list with a hole in the middle.
3160
+
3161
+ **Still ONE read of the log.** `useEvents` is called in `App`, once, and three
3162
+ surfaces are handed the result — the Monitor's Event stream, the comment toasts,
3163
+ and this rail. The change is that it CATCHES UP instead of starting over: new
3164
+ rows are prepended, the reader's scrollback survives, and a quiet board answers
3165
+ with an empty list instead of fifty rows on every poke.
3166
+
3167
+ **What arrived is stamped in the CLIENT's clock**, never in `Event.ts`. A
3168
+ comment written three minutes ago that reaches this tab now is new HERE, and a
3169
+ rail that highlighted by `ts` would show it already faded — `toasts/model.ts::
3170
+ Toast.shown` decided the same thing first, for the same reason. The stamps are
3171
+ pruned to a minute, so the map is bounded by what arrived, not by how long the
3172
+ tab has been open. Nothing is stored: there are no read-receipts on this board
3173
+ and there is no mark-as-read verb, by design (§11), so "new" is arithmetic that
3174
+ dies with the tab.
3175
+
3176
+ **The organisation is a MOMENT, not a row.** The Monitor's pane draws one row
3177
+ per event and is right to: it answers "what happened, in order, ever". A 320px
3178
+ column beside the code answers "what are the workers doing right now", and at
3179
+ that question one row per event is noise — a `taskops_plan` of nine cards is
3180
+ nine identical rows. So consecutive events by the same actor on the same card
3181
+ within `MOMENT_GAP_S` (120s) fold into one entry, counted, sized and ended with
3182
+ the worker's own number: `2 commits · 2 files · +41 −7 · progress → 68`. The
3183
+ fold is on (actor, task) because a worker moving between cards is the change a
3184
+ reader wants to see, and it is CONSECUTIVE-only, so an entry can never be
3185
+ hoisted above events that happened after it. Files are counted per PATH across
3186
+ the run — one file touched in three commits is one file — through
3187
+ `Thread.tsx::changed`, which is the Event stream's own numstat fold given a
3188
+ list; a second definition of "how much changed" is the drift `format.ts` is the
3189
+ post-mortem of.
3190
+
3191
+ **Three ways to narrow, none of them a fetch:** five families (`talk` `work`
3192
+ `code` `review` `chapter`, every kind in `core/kinds.py` in exactly one), one
3193
+ card followed by clicking its id, and `older` for history. `progress` needs no
3194
+ branch anywhere — it is an `edited` event (`verbs/update.py::_progress` argues
3195
+ why it is not a kind of its own) and the roll-up reads it off the body.
3196
+
3197
+ **The reader's place is never taken.** New entries land on top; a reader who has
3198
+ scrolled down into history does not have the list move under them, and a pill
3199
+ says how many are waiting above. The card popup is `openCard` — App's one door
3200
+ into the Drawer, which is mounted once over whichever page is on — so the rail
3201
+ draws no dossier of its own and the same card opens identically from the Board,
3202
+ the Monitor and here.
3203
+
3204
+ **What the headless harness does and does not pin**
3205
+ (`ui/smoke/sections/stream-rail.tsx`): the fold, the roll-up, the numstat, the
3206
+ freshness tiers, the merge, the arrival stamps, the narrowing, the entry markup,
3207
+ the three empty states and the Editor's third column — all pure functions or
3208
+ `Stream`, which renders under `react-dom/server` with no client, no socket and
3209
+ no timer. Not pinned, and said rather than faked with a jsdom: the scroll
3210
+ position, the one interval, and the catch-up walk. The `after=` half is pinned
3211
+ on the server, where it is decided (`tests/test_verbs.py`), including the
3212
+ burst-larger-than-a-page case the client arithmetic could not survive.
3213
+
3214
+ The bundle grew 330 231 → 341 411 bytes for the whole feature: the model, the
3215
+ rail and the Editor's third column, no dependency added.
@@ -3,6 +3,72 @@
3
3
  The source of truth for release notes — GitHub Releases are extracted from
4
4
  here, never written twice.
5
5
 
6
+ ## 0.6.1 — the Stream earns its column
7
+
8
+ - **Open the files the agents name.** Every entry now wears the paths it
9
+ named, and a click opens that file in the editor beside it — switching to
10
+ the card's own worktree when it has one on this disk. Three sources, in
11
+ descending order of trust: a commit's `numstat` keys (git's own answer), a
12
+ created card's declared `files`, and what the prose names.
13
+ - **The prose half is a guess and is kept one.** Agents write paths to each
14
+ other constantly — "the fix site is `verbs/_facts.py::pending_mentions`" —
15
+ and no structured field carries them. So they are extracted, narrowly:
16
+ backticked or bare, a trailing `::symbol` / `:line` / `:line:col` stripped
17
+ because `events.py:53` names a file and not a file with that name, never a
18
+ URL, never a bare directory, capped. A false negative costs a click; a false
19
+ positive is the rail lying about the disk.
20
+ - **Markdown**, the drawer's own renderer over the same parser, so a fence, a
21
+ list and a `path` read here exactly as they read there. The 96-character cut
22
+ is gone: it landed inside a fence as often as not, and what the reader saw
23
+ was not a shortened comment but a broken one. The clamp is now a HEIGHT,
24
+ undone by expanding the entry — and expansion is a set, not a flag, because
25
+ a reader opens two comments to compare them as often as one.
26
+ - **The rail resizes by hand**: a grip on its edge, 240–760px, double-click to
27
+ reset, and the width is remembered across leaving the tab. The listeners are
28
+ on the document and not the handle — bound to a 6px strip they are lost the
29
+ first time the pointer outruns the render.
30
+ - `useStream` is the container half, extracted so a second mount of the same
31
+ picture cannot come to disagree about what is new or which family is on.
32
+ - Four mutations, one site at a time. Two assertions were born FALSE and found
33
+ by running them: a path cleaner that did not strip backticks, and a "draws
34
+ no chips" claim asserted against a mount that HAD an editor.
35
+ - The bundle grew 341 411 → 345 368 bytes.
36
+
37
+ ## 0.6.0 — the Stream, beside the code
38
+
39
+ - **A live rail inside the Editor**: every comment, every commit, every
40
+ `progress` a worker reports, as it happens, in a third column you open from
41
+ the page's one bar. Shut by default — the Editor's whole decision is that the
42
+ code gets the window, and a live column appearing unasked would be that
43
+ decision taken back on the reader's behalf.
44
+ - **The socket did not change.** A frame is still `{"type":"change","verb","seq"}`
45
+ and nothing else, which is what lets a public board's feed open with no
46
+ credential. The socket pokes; the page reads.
47
+ - **`events` can be read forwards**: `after=<seq>` answers exactly the rows
48
+ written since the cursor a reader already holds, oldest first, and pages
49
+ forward on `next` when the catch-up is bigger than one page. The cursor is
50
+ `head` — the rowid is still not sent per row. What that replaces is the
51
+ arithmetic the client had to do over page one (`head - lastHead` rows plus a
52
+ seen-set), which was exact only until a burst was larger than a page.
53
+ - **The one log read catches up instead of starting over.** New rows are
54
+ prepended, so a reader's scrollback survives a board that is moving, and a
55
+ quiet board answers with an empty list instead of fifty rows on every poke.
56
+ Three surfaces still share one read: the Monitor's pane, the toasts, the rail.
57
+ - **A run of work is one entry.** Consecutive events by one worker on one card
58
+ within two minutes fold together, counted and sized:
59
+ `2 commits · 2 files · +41 −7 · progress → 68`. Nine rows from one
60
+ `taskops_plan` were the shape this exists to stop drawing.
61
+ - **What arrived is marked and fades by arithmetic** — stamped in the reader's
62
+ own clock, never the event's, so a comment that reaches the tab late is new
63
+ HERE. Nothing is stored: no badge, no counter, no read-receipt.
64
+ - **The reader's place is never taken**: entries land on top, a reader scrolled
65
+ into history is not moved, and a pill says how many are waiting above.
66
+ - Filter by family (talk · work · code · review · chapter), follow one card by
67
+ clicking its id, open its dossier in the popup every other view uses.
68
+ - Five mutations, one site at a time, and one of them found a fixture that was
69
+ not exercising the rule it claimed to pin.
70
+ - The bundle grew 330 231 → 341 411 bytes. No dependency added.
71
+
6
72
  ## 0.5.8 — the Editor is the whole window
7
73
 
8
74
  - **Nothing is drawn above the Editor any more** — no KPI rail, no header, no
@@ -270,6 +270,12 @@ what it exists to report, not a fault.
270
270
  cannot conflict.
271
271
  - **Do not run browser/UI demos unless asked.** The UI is tested headlessly
272
272
  through `react-dom/server`, no browser and no jsdom.
273
+ - **The Editor carries the Stream rail** — the log, live, in a third column,
274
+ shut by default (ARCHITECTURE §23). It fetches NOTHING: `useEvents` is read
275
+ once in `App` and three surfaces share it, and the socket frame is still a
276
+ signal, never a payload. A run of events by one worker on one card inside two
277
+ minutes is ONE entry; "new" is stamped in the reader's clock and fades by
278
+ arithmetic, because there is no mark-as-read verb and never will be.
273
279
  - One card rebuilds the bundle at the end of a wave. N cards rebuilding it is
274
280
  N-1 conflicts by construction.
275
281
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: taskops-cli
3
- Version: 0.5.8
3
+ Version: 0.6.1
4
4
  Summary: A shared work board for teams of coding agents, with a human who decides.
5
5
  Project-URL: Repository, https://github.com/bernatch22/taskops
6
6
  License-Expression: MIT
@@ -1,4 +1,4 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  __title__ = "taskops"
4
- __version__ = "0.5.8" # single source of truth; pyproject reads it dynamically
4
+ __version__ = "0.6.1" # single source of truth; pyproject reads it dynamically
@@ -93,8 +93,19 @@ class Cache:
93
93
  rows = self._query("SELECT kind, COUNT(*) FROM events GROUP BY kind")
94
94
  return {str(kind): int(n) for kind, n in rows}
95
95
 
96
- def since(self, seq: int) -> list[tuple[int, Event]]:
97
- return self._events("WHERE seq > ? ORDER BY seq", (seq,))
96
+ def since(self, seq: int, limit: int | None = None) -> list[tuple[int, Event]]:
97
+ """Everything after `seq`, OLDEST first — replay's order, and the order
98
+ a live reader catches up in.
99
+
100
+ `limit` is for the second caller only (`verbs/events.py`'s `after=`): a
101
+ page of a CATCH-UP is read forwards, so the cap has to fall on the
102
+ oldest end and not on the newest, which is the one thing a descending
103
+ `page()` cannot do. Replay passes nothing and reads the tail whole —
104
+ that is what makes a cache rebuildable.
105
+ """
106
+ if limit is None:
107
+ return self._events("WHERE seq > ? ORDER BY seq", (seq,))
108
+ return self._events("WHERE seq > ? ORDER BY seq LIMIT ?", (seq, limit))
98
109
 
99
110
  def page(self, before: int | None, limit: int) -> list[tuple[int, Event]]:
100
111
  """One page of the whole log, NEWEST FIRST, by keyset on `seq`.