@shivaedev/work-board 0.16.0 → 0.17.1

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 (331) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +7 -7
  3. package/assets/react.css +1 -1
  4. package/assets/react.js +53 -39
  5. package/dist/browser/responses/cardContext.d.ts +8 -1
  6. package/dist/browser/responses/cardContext.d.ts.map +1 -1
  7. package/dist/browser/responses/cardContext.js +14 -6
  8. package/dist/browser/responses/cardContext.js.map +1 -1
  9. package/dist/browser/views/remaining.d.ts +0 -240
  10. package/dist/browser/views/remaining.d.ts.map +1 -1
  11. package/dist/browser/views/remaining.js +0 -2
  12. package/dist/browser/views/remaining.js.map +1 -1
  13. package/dist/browser/views/schema.d.ts +11 -0
  14. package/dist/browser/views/schema.d.ts.map +1 -1
  15. package/dist/browser/views/schema.js +1 -0
  16. package/dist/browser/views/schema.js.map +1 -1
  17. package/dist/client/formStatus.d.ts +1 -1
  18. package/dist/client/formStatus.d.ts.map +1 -1
  19. package/dist/client/formStatus.js +2 -2
  20. package/dist/client/formStatus.js.map +1 -1
  21. package/dist/client/react/AnswerContext.d.ts.map +1 -1
  22. package/dist/client/react/AnswerContext.js +5 -2
  23. package/dist/client/react/AnswerContext.js.map +1 -1
  24. package/dist/client/react/AnswerForm.d.ts.map +1 -1
  25. package/dist/client/react/AnswerForm.js +6 -2
  26. package/dist/client/react/AnswerForm.js.map +1 -1
  27. package/dist/client/react/AnswerMarkdown.d.ts +6 -0
  28. package/dist/client/react/AnswerMarkdown.d.ts.map +1 -0
  29. package/dist/client/react/AnswerMarkdown.js +7 -0
  30. package/dist/client/react/AnswerMarkdown.js.map +1 -0
  31. package/dist/client/react/AnswerMore.d.ts.map +1 -1
  32. package/dist/client/react/AnswerMore.js +2 -1
  33. package/dist/client/react/AnswerMore.js.map +1 -1
  34. package/dist/client/react/AnswerPanel.d.ts.map +1 -1
  35. package/dist/client/react/AnswerPanel.js +3 -2
  36. package/dist/client/react/AnswerPanel.js.map +1 -1
  37. package/dist/client/react/AnswerPrompts.d.ts.map +1 -1
  38. package/dist/client/react/AnswerPrompts.js +16 -10
  39. package/dist/client/react/AnswerPrompts.js.map +1 -1
  40. package/dist/client/react/BoardShell.js +1 -1
  41. package/dist/client/react/BoardShell.js.map +1 -1
  42. package/dist/client/react/DocumentAttention.d.ts.map +1 -1
  43. package/dist/client/react/DocumentAttention.js +2 -3
  44. package/dist/client/react/DocumentAttention.js.map +1 -1
  45. package/dist/client/react/DocumentMain.d.ts.map +1 -1
  46. package/dist/client/react/DocumentMain.js +1 -1
  47. package/dist/client/react/DocumentMain.js.map +1 -1
  48. package/dist/client/react/GoalPage.js +1 -1
  49. package/dist/client/react/GoalPage.js.map +1 -1
  50. package/dist/client/react/GoalProgressHeader.js +1 -1
  51. package/dist/client/react/GoalProgressHeader.js.map +1 -1
  52. package/dist/client/react/LibraryMain.d.ts.map +1 -1
  53. package/dist/client/react/LibraryMain.js +1 -1
  54. package/dist/client/react/LibraryMain.js.map +1 -1
  55. package/dist/client/react/LibraryPage.js +1 -1
  56. package/dist/client/react/LibraryPage.js.map +1 -1
  57. package/dist/client/react/ProjectionFrame.js +1 -1
  58. package/dist/client/react/ProjectionFrame.js.map +1 -1
  59. package/dist/client/react/ProjectionMain.d.ts.map +1 -1
  60. package/dist/client/react/ProjectionMain.js +1 -1
  61. package/dist/client/react/ProjectionMain.js.map +1 -1
  62. package/dist/client/react/PromptOption.d.ts.map +1 -1
  63. package/dist/client/react/PromptOption.js +7 -5
  64. package/dist/client/react/PromptOption.js.map +1 -1
  65. package/dist/client/react/RemainingMain.d.ts.map +1 -1
  66. package/dist/client/react/RemainingMain.js +1 -4
  67. package/dist/client/react/RemainingMain.js.map +1 -1
  68. package/dist/client/react/ResponseHistory.d.ts.map +1 -1
  69. package/dist/client/react/ResponseHistory.js +11 -5
  70. package/dist/client/react/ResponseHistory.js.map +1 -1
  71. package/dist/client/react/ResultPage.d.ts.map +1 -1
  72. package/dist/client/react/ResultPage.js +2 -1
  73. package/dist/client/react/ResultPage.js.map +1 -1
  74. package/dist/client/react/ShellApi.d.ts +0 -1
  75. package/dist/client/react/ShellApi.d.ts.map +1 -1
  76. package/dist/client/react/SystemMain.d.ts.map +1 -1
  77. package/dist/client/react/SystemMain.js +1 -1
  78. package/dist/client/react/SystemMain.js.map +1 -1
  79. package/dist/client/react/TodayNeeds.d.ts +5 -0
  80. package/dist/client/react/TodayNeeds.d.ts.map +1 -1
  81. package/dist/client/react/TodayNeeds.js +6 -3
  82. package/dist/client/react/TodayNeeds.js.map +1 -1
  83. package/dist/client/react/TodayOptionButton.d.ts +15 -0
  84. package/dist/client/react/TodayOptionButton.d.ts.map +1 -0
  85. package/dist/client/react/TodayOptionButton.js +10 -0
  86. package/dist/client/react/TodayOptionButton.js.map +1 -0
  87. package/dist/client/react/TodayOptions.d.ts.map +1 -1
  88. package/dist/client/react/TodayOptions.js +13 -11
  89. package/dist/client/react/TodayOptions.js.map +1 -1
  90. package/dist/client/react/TodayPage.d.ts.map +1 -1
  91. package/dist/client/react/TodayPage.js +7 -4
  92. package/dist/client/react/TodayPage.js.map +1 -1
  93. package/dist/client/react/activeRead.js +1 -1
  94. package/dist/client/react/activeRead.js.map +1 -1
  95. package/dist/client/react/entry.js +1 -1
  96. package/dist/client/react/entry.js.map +1 -1
  97. package/dist/client/react/loadBoardPage.d.ts.map +1 -1
  98. package/dist/client/react/loadBoardPage.js +6 -14
  99. package/dist/client/react/loadBoardPage.js.map +1 -1
  100. package/dist/client/react/loadProjection.d.ts.map +1 -1
  101. package/dist/client/react/loadProjection.js +1 -1
  102. package/dist/client/react/loadProjection.js.map +1 -1
  103. package/dist/client/react/loadRemaining.d.ts +17 -257
  104. package/dist/client/react/loadRemaining.d.ts.map +1 -1
  105. package/dist/client/react/loadRemaining.js +1 -3
  106. package/dist/client/react/loadRemaining.js.map +1 -1
  107. package/dist/client/react/pageQueries.d.ts.map +1 -1
  108. package/dist/client/react/pageQueries.js +1 -4
  109. package/dist/client/react/pageQueries.js.map +1 -1
  110. package/dist/client/react/readerLink.js +1 -1
  111. package/dist/client/react/readerLink.js.map +1 -1
  112. package/dist/client/react/remainingRoute.d.ts +1 -1
  113. package/dist/client/react/remainingRoute.d.ts.map +1 -1
  114. package/dist/client/react/remainingRoute.js +0 -2
  115. package/dist/client/react/remainingRoute.js.map +1 -1
  116. package/dist/client/react/resolvedDocumentFailure.d.ts.map +1 -0
  117. package/dist/client/react/resolvedDocumentFailure.js.map +1 -0
  118. package/dist/client/react/routeQuery.d.ts.map +1 -0
  119. package/dist/client/react/routeQuery.js.map +1 -0
  120. package/dist/client/react/useResponseForm.d.ts.map +1 -1
  121. package/dist/client/react/useResponseForm.js +2 -2
  122. package/dist/client/react/useResponseForm.js.map +1 -1
  123. package/dist/client/responses/summary.d.ts +1 -0
  124. package/dist/client/responses/summary.d.ts.map +1 -1
  125. package/dist/client/responses/summary.js +12 -1
  126. package/dist/client/responses/summary.js.map +1 -1
  127. package/dist/responses/url.js +1 -1
  128. package/dist/responses/url.js.map +1 -1
  129. package/dist/rpc/PageUrl.d.ts.map +1 -1
  130. package/dist/rpc/PageUrl.js +0 -1
  131. package/dist/rpc/PageUrl.js.map +1 -1
  132. package/dist/rpc/contract.d.ts +2195 -2885
  133. package/dist/rpc/contract.d.ts.map +1 -1
  134. package/dist/rpc/contract.js +6 -9
  135. package/dist/rpc/contract.js.map +1 -1
  136. package/dist/rpc/responseContract.d.ts +56 -56
  137. package/dist/server/{responses/service.d.ts → Responses.d.ts} +9 -21
  138. package/dist/server/Responses.d.ts.map +1 -0
  139. package/dist/server/{responses/service.js → Responses.js} +22 -22
  140. package/dist/server/Responses.js.map +1 -0
  141. package/dist/server/attention/inbox.d.ts +15 -2
  142. package/dist/server/attention/inbox.d.ts.map +1 -1
  143. package/dist/server/attention/inbox.js +44 -26
  144. package/dist/server/attention/inbox.js.map +1 -1
  145. package/dist/server/attention/managedRequestReceipt.d.ts +10 -0
  146. package/dist/server/attention/managedRequestReceipt.d.ts.map +1 -1
  147. package/dist/server/attention/managedRequestReceipt.js +29 -10
  148. package/dist/server/attention/managedRequestReceipt.js.map +1 -1
  149. package/dist/server/attention/projection.d.ts.map +1 -1
  150. package/dist/server/attention/projection.js +3 -2
  151. package/dist/server/attention/projection.js.map +1 -1
  152. package/dist/server/attention/requestInbox.d.ts +6 -3
  153. package/dist/server/attention/requestInbox.d.ts.map +1 -1
  154. package/dist/server/attention/requestInbox.js +3 -2
  155. package/dist/server/attention/requestInbox.js.map +1 -1
  156. package/dist/server/attention/requests.d.ts +1 -0
  157. package/dist/server/attention/requests.d.ts.map +1 -1
  158. package/dist/server/attention/requests.js +7 -3
  159. package/dist/server/attention/requests.js.map +1 -1
  160. package/dist/server/boardRuntime.d.ts +2 -2
  161. package/dist/server/boardRuntime.d.ts.map +1 -1
  162. package/dist/server/boardRuntime.js +12 -16
  163. package/dist/server/boardRuntime.js.map +1 -1
  164. package/dist/server/boardServices.d.ts +3 -4
  165. package/dist/server/boardServices.d.ts.map +1 -1
  166. package/dist/server/boardServices.js +5 -4
  167. package/dist/server/boardServices.js.map +1 -1
  168. package/dist/server/boardSocket.d.ts +1 -1
  169. package/dist/server/boardSocket.d.ts.map +1 -1
  170. package/dist/server/document/read.d.ts +4 -10
  171. package/dist/server/document/read.d.ts.map +1 -1
  172. package/dist/server/document/read.js +5 -5
  173. package/dist/server/document/read.js.map +1 -1
  174. package/dist/server/document/requests.d.ts.map +1 -1
  175. package/dist/server/document/requests.js +1 -2
  176. package/dist/server/document/requests.js.map +1 -1
  177. package/dist/server/handoffs/Service.d.ts +1 -2
  178. package/dist/server/handoffs/Service.d.ts.map +1 -1
  179. package/dist/server/handoffs/Service.js +14 -13
  180. package/dist/server/handoffs/Service.js.map +1 -1
  181. package/dist/server/http/assets.d.ts +0 -3
  182. package/dist/server/http/assets.d.ts.map +1 -1
  183. package/dist/server/http/assets.js +1 -7
  184. package/dist/server/http/assets.js.map +1 -1
  185. package/dist/server/http/boardRoutes.d.ts.map +1 -1
  186. package/dist/server/http/boardRoutes.js +3 -3
  187. package/dist/server/http/boardRoutes.js.map +1 -1
  188. package/dist/server/integration/responses.d.ts +207 -4
  189. package/dist/server/integration/responses.d.ts.map +1 -1
  190. package/dist/server/integration/responses.js +4 -2
  191. package/dist/server/integration/responses.js.map +1 -1
  192. package/dist/server/mcp/client.d.ts +869 -1098
  193. package/dist/server/mcp/client.d.ts.map +1 -1
  194. package/dist/server/mcp/queries.d.ts +47 -734
  195. package/dist/server/mcp/queries.d.ts.map +1 -1
  196. package/dist/server/mcp/queries.js +3 -3
  197. package/dist/server/mcp/queries.js.map +1 -1
  198. package/dist/server/packageInfo.d.ts +1 -1
  199. package/dist/server/packageInfo.js +1 -1
  200. package/dist/server/responses/readingActions.d.ts +10 -1
  201. package/dist/server/responses/readingActions.d.ts.map +1 -1
  202. package/dist/server/responses/readingActions.js +2 -4
  203. package/dist/server/responses/readingActions.js.map +1 -1
  204. package/dist/server/responses/registerQuestion.d.ts +2 -2
  205. package/dist/server/responses/registerQuestion.d.ts.map +1 -1
  206. package/dist/server/responses/registerQuestion.js +3 -3
  207. package/dist/server/responses/registerQuestion.js.map +1 -1
  208. package/dist/server/responses/retry.d.ts +2 -2
  209. package/dist/server/responses/retry.d.ts.map +1 -1
  210. package/dist/server/responses/retry.js +1 -1
  211. package/dist/server/responses/retry.js.map +1 -1
  212. package/dist/server/rpc/HandlerOptions.d.ts +0 -8
  213. package/dist/server/rpc/HandlerOptions.d.ts.map +1 -1
  214. package/dist/server/rpc/handlers.d.ts +1 -1
  215. package/dist/server/rpc/handlers.d.ts.map +1 -1
  216. package/dist/server/rpc/handlers.js +14 -13
  217. package/dist/server/rpc/handlers.js.map +1 -1
  218. package/dist/server/rpc/hints.d.ts +3 -5
  219. package/dist/server/rpc/hints.d.ts.map +1 -1
  220. package/dist/server/rpc/hints.js +5 -1
  221. package/dist/server/rpc/hints.js.map +1 -1
  222. package/dist/server/views/fleetItems.d.ts +2 -2
  223. package/dist/server/views/fleetItems.d.ts.map +1 -1
  224. package/dist/server/views/fleetItems.js +8 -3
  225. package/dist/server/views/fleetItems.js.map +1 -1
  226. package/dist/server/views/projectionQueries.d.ts +16 -9
  227. package/dist/server/views/projectionQueries.d.ts.map +1 -1
  228. package/dist/server/views/projectionQueries.js +6 -6
  229. package/dist/server/views/projectionQueries.js.map +1 -1
  230. package/dist/server/views/remainingQueries.d.ts +4 -252
  231. package/dist/server/views/remainingQueries.d.ts.map +1 -1
  232. package/dist/server/views/remainingQueries.js +11 -19
  233. package/dist/server/views/remainingQueries.js.map +1 -1
  234. package/dist/server/workspace.d.ts +222 -0
  235. package/dist/server/workspace.d.ts.map +1 -0
  236. package/dist/server/workspace.js +25 -0
  237. package/dist/server/workspace.js.map +1 -0
  238. package/docs/delivery/README.md +2 -2
  239. package/package.json +5 -5
  240. package/src/browser/responses/cardContext.ts +15 -6
  241. package/src/browser/views/remaining.ts +0 -3
  242. package/src/browser/views/schema.ts +1 -0
  243. package/src/client/formStatus.ts +2 -2
  244. package/src/client/react/AnswerContext.tsx +9 -4
  245. package/src/client/react/AnswerForm.tsx +25 -6
  246. package/src/client/react/AnswerMarkdown.tsx +15 -0
  247. package/src/client/react/AnswerMore.tsx +58 -46
  248. package/src/client/react/AnswerPanel.tsx +14 -8
  249. package/src/client/react/AnswerPrompts.tsx +33 -18
  250. package/src/client/react/BoardShell.tsx +1 -1
  251. package/src/client/react/DocumentAttention.tsx +2 -3
  252. package/src/client/react/DocumentMain.tsx +1 -1
  253. package/src/client/react/GoalPage.tsx +1 -1
  254. package/src/client/react/GoalProgressHeader.tsx +1 -1
  255. package/src/client/react/LibraryMain.tsx +1 -1
  256. package/src/client/react/LibraryPage.tsx +1 -1
  257. package/src/client/react/ProjectionFrame.tsx +1 -1
  258. package/src/client/react/ProjectionMain.tsx +1 -1
  259. package/src/client/react/PromptOption.tsx +39 -24
  260. package/src/client/react/RemainingMain.tsx +1 -11
  261. package/src/client/react/ResponseHistory.tsx +53 -14
  262. package/src/client/react/ResultPage.tsx +2 -2
  263. package/src/client/react/ShellApi.ts +0 -1
  264. package/src/client/react/SystemMain.tsx +1 -1
  265. package/src/client/react/TodayNeeds.tsx +21 -6
  266. package/src/client/react/TodayOptionButton.tsx +37 -0
  267. package/src/client/react/TodayOptions.tsx +38 -30
  268. package/src/client/react/TodayPage.tsx +18 -3
  269. package/src/client/react/activeRead.ts +1 -1
  270. package/src/client/react/entry.css +0 -6
  271. package/src/client/react/entry.tsx +1 -1
  272. package/src/client/react/loadBoardPage.ts +6 -15
  273. package/src/client/react/loadProjection.ts +1 -1
  274. package/src/client/react/loadRemaining.ts +1 -3
  275. package/src/client/react/pageQueries.ts +1 -4
  276. package/src/client/react/readerLink.ts +1 -1
  277. package/src/client/react/remainingRoute.ts +0 -2
  278. package/src/client/react/useResponseForm.ts +4 -2
  279. package/src/client/responses/summary.ts +13 -1
  280. package/src/responses/url.ts +1 -1
  281. package/src/rpc/PageUrl.ts +0 -1
  282. package/src/rpc/contract.ts +6 -9
  283. package/src/server/{responses/service.ts → Responses.ts} +21 -29
  284. package/src/server/attention/inbox.ts +51 -26
  285. package/src/server/attention/managedRequestReceipt.ts +41 -10
  286. package/src/server/attention/projection.ts +5 -2
  287. package/src/server/attention/requestInbox.ts +3 -2
  288. package/src/server/attention/requests.ts +7 -2
  289. package/src/server/boardRuntime.ts +20 -25
  290. package/src/server/boardServices.ts +7 -7
  291. package/src/server/document/read.ts +6 -14
  292. package/src/server/document/requests.ts +1 -2
  293. package/src/server/handoffs/Service.ts +16 -19
  294. package/src/server/http/assets.ts +1 -7
  295. package/src/server/http/boardRoutes.ts +3 -3
  296. package/src/server/integration/responses.ts +5 -3
  297. package/src/server/mcp/queries.ts +3 -3
  298. package/src/server/packageInfo.ts +1 -1
  299. package/src/server/responses/readingActions.ts +3 -3
  300. package/src/server/responses/registerQuestion.ts +5 -5
  301. package/src/server/responses/retry.ts +3 -3
  302. package/src/server/rpc/HandlerOptions.ts +0 -8
  303. package/src/server/rpc/handlers.ts +17 -15
  304. package/src/server/rpc/hints.ts +6 -3
  305. package/src/server/views/fleetItems.ts +8 -4
  306. package/src/server/views/projectionQueries.ts +6 -7
  307. package/src/server/views/remainingQueries.ts +11 -31
  308. package/src/server/workspace.ts +41 -0
  309. package/assets/native.js +0 -37
  310. package/dist/client/entry.d.ts +0 -3
  311. package/dist/client/entry.d.ts.map +0 -1
  312. package/dist/client/entry.js +0 -3
  313. package/dist/client/entry.js.map +0 -1
  314. package/dist/client/page/resolvedDocumentFailure.d.ts.map +0 -1
  315. package/dist/client/page/resolvedDocumentFailure.js.map +0 -1
  316. package/dist/client/page/routeQuery.d.ts.map +0 -1
  317. package/dist/client/page/routeQuery.js.map +0 -1
  318. package/dist/client/react/RespondPage.d.ts +0 -5
  319. package/dist/client/react/RespondPage.d.ts.map +0 -1
  320. package/dist/client/react/RespondPage.js +0 -5
  321. package/dist/client/react/RespondPage.js.map +0 -1
  322. package/dist/server/responses/service.d.ts.map +0 -1
  323. package/dist/server/responses/service.js.map +0 -1
  324. package/src/client/entry.ts +0 -3
  325. package/src/client/react/RespondPage.tsx +0 -8
  326. /package/dist/client/{page → react}/resolvedDocumentFailure.d.ts +0 -0
  327. /package/dist/client/{page → react}/resolvedDocumentFailure.js +0 -0
  328. /package/dist/client/{page → react}/routeQuery.d.ts +0 -0
  329. /package/dist/client/{page → react}/routeQuery.js +0 -0
  330. /package/src/client/{page → react}/resolvedDocumentFailure.ts +0 -0
  331. /package/src/client/{page → react}/routeQuery.ts +0 -0
@@ -2,7 +2,7 @@ export function readerLink(target: URL) {
2
2
  return (
3
3
  target.origin === location.origin
4
4
  && (target.pathname === "/"
5
- || /^\/_board\/(?:library|goals|system|diagnostics|handoff|result|respond|start|changes|inbox|overview|work)$/u.test(target.pathname)
5
+ || /^\/_board\/(?:library|goals|system|diagnostics|handoff|result|start|changes|inbox|overview|work)$/u.test(target.pathname)
6
6
  || target.pathname.startsWith("/_board/item/")
7
7
  || /\.md$/iu.test(target.pathname))
8
8
  );
@@ -8,8 +8,6 @@ export function remainingRoute(pathname: string) {
8
8
  return "system";
9
9
  case "/_board/start":
10
10
  return "start";
11
- case "/_board/respond":
12
- return "respond";
13
11
  case "/_board/handoff":
14
12
  return "handoff";
15
13
  case "/_board/result":
@@ -11,7 +11,7 @@ import { authorLabel } from "#client/responses/authorLabel.ts";
11
11
  import { type ResponseInput, responseFormSchema } from "#client/responses/formSchema.ts";
12
12
  import { responsePreview } from "#client/responses/preview.ts";
13
13
  import { retainContext } from "#client/responses/retainContext.ts";
14
- import { responseFeedback, responseSummary } from "#client/responses/summary.ts";
14
+ import { chosenLabels, responseFeedback, responseSummary } from "#client/responses/summary.ts";
15
15
  import { sourceChanged } from "#client/sourceChanged.ts";
16
16
  import { createResponse } from "./createResponse.ts";
17
17
  import { useFormClient } from "./FormContext.ts";
@@ -116,7 +116,9 @@ export function useResponseForm(source: QuestionPreview, prompts: readonly Promp
116
116
  setInteraction("cleared");
117
117
  }
118
118
  const previewed = preview !== undefined && same && Equal.equals(preview, values);
119
- const { outcome, text: status } = formStatus(created, interaction, "response", (saved) => responseSaved(saved.id, created.dirty));
119
+ const { outcome, text: status } = formStatus(created, interaction, "response", (saved) =>
120
+ responseSaved(saved.id, created.dirty, chosenLabels(prompts, saved.answers)),
121
+ );
120
122
  const summary = responseSummary(values, prompts, answers(values), attempted);
121
123
  return {
122
124
  answers: answers(values),
@@ -1,3 +1,4 @@
1
+ import { optionText, promptHeading } from "#browser/responses/cardContext.ts";
1
2
  import type { Prompt } from "#browser/responses/Prompt.ts";
2
3
  import type { PromptAnswer } from "#browser/responses/schema.ts";
3
4
  import type { ResponseInput } from "./formSchema.ts";
@@ -14,7 +15,9 @@ export function responseSummary(values: ResponseInput, prompts: readonly Prompt[
14
15
  : [];
15
16
  return [
16
17
  values.author.trim() ? "" : "Add your local author label.",
17
- missing.length > 0 ? `Unanswered prompts: ${missing.map((prompt) => `Question ${prompts.indexOf(prompt) + 1} · ${prompt.id}`).join("; ")}.` : "",
18
+ missing.length > 0
19
+ ? `Unanswered: ${missing.map((prompt) => `“${promptHeading(prompt.source).title ?? `Question ${prompts.indexOf(prompt) + 1}`}”`).join(", ")}.`
20
+ : "",
18
21
  ]
19
22
  .filter(Boolean)
20
23
  .join(" ");
@@ -23,3 +26,12 @@ export function responseSummary(values: ResponseInput, prompts: readonly Prompt[
23
26
  export function responseFeedback(status: string, outcome: string | undefined, summary: string) {
24
27
  return outcome || status.startsWith("Draft cleared") ? status : summary || status;
25
28
  }
29
+
30
+ export function chosenLabels(prompts: readonly Prompt[], answers: readonly PromptAnswer[] | undefined) {
31
+ return prompts
32
+ .flatMap((prompt) => {
33
+ const selected = answers?.find((answer) => answer.prompt === prompt.id)?.selected ?? [];
34
+ return prompt.options.filter((option) => selected.includes(option.id)).map((option) => optionText(option.id, option.source, "").label);
35
+ })
36
+ .join(", ");
37
+ }
@@ -1,3 +1,3 @@
1
1
  export function responseUrl(item: string, request: string): string {
2
- return `/_board/respond?item=${encodeURIComponent(item)}&request=${encodeURIComponent(request)}`;
2
+ return `/_board/item/${encodeURIComponent(item)}/#${encodeURIComponent(`answer-card-${request}`)}`;
3
3
  }
@@ -17,7 +17,6 @@ export const PageUrl = Schema.String.check(
17
17
  "/_board/overview",
18
18
  "/_board/changes",
19
19
  "/_board/start",
20
- "/_board/respond",
21
20
  "/_board/handoff",
22
21
  "/_board/result",
23
22
  ].includes(path)
@@ -10,7 +10,7 @@ import { Library, LibraryInput } from "#browser/navigation/library.ts";
10
10
  import { FleetItemsProjection } from "#browser/views/FleetItemsProjection.ts";
11
11
  import { GoalsProjection } from "#browser/views/GoalsProjection.ts";
12
12
  import { ResultProjection } from "#browser/views/ResultProjection.ts";
13
- import { DiagnosticsProjection, HandoffProjection, RespondProjection, StartProjection } from "#browser/views/remaining.ts";
13
+ import { DiagnosticsProjection, HandoffProjection, StartProjection } from "#browser/views/remaining.ts";
14
14
  import { BoardProjection, InboxProjection, WorkProjection } from "#browser/views/schema.ts";
15
15
  import { handoffContract } from "#rpc/handoffContract.ts";
16
16
  import { responseContract } from "#rpc/responseContract.ts";
@@ -53,15 +53,13 @@ const ReadWork = query("work", {
53
53
  const ReadInbox = query("inbox", { reads: () => [workspace.list, index.list], rejections: { InternalServerError }, success: InboxProjection });
54
54
  const ReadServer = query("server", { reads: () => [], rejections: { ServiceUnavailable }, success: Server });
55
55
  const ReadDiagnostics = query("diagnostics", { reads: () => [workspace.list], rejections: { InternalServerError }, success: DiagnosticsProjection });
56
- const ReadFleetItems = query("fleetItems", { reads: () => [workspace.list, index.list], success: FleetItemsProjection });
56
+ const ReadFleetItems = query("fleetItems", {
57
+ reads: () => [workspace.list, index.list],
58
+ rejections: { InternalServerError },
59
+ success: FleetItemsProjection,
60
+ });
57
61
  const ReadGoals = query("goals", { reads: () => [workspace.list, index.list], rejections: { InternalServerError }, success: GoalsProjection });
58
62
  const ReadStart = query("start", { reads: () => [workspace.list, index.list], rejections: { InternalServerError }, success: StartProjection });
59
- const ReadRespond = query("respond", {
60
- payload: { url: PageUrl },
61
- reads: ({ url }) => [...pageKeys(url), index.list],
62
- rejections: documentRejections,
63
- success: RespondProjection,
64
- });
65
63
  const ReadHandoff = query("handoff", {
66
64
  payload: { url: PageUrl },
67
65
  reads: () => [workspace.list, index.list],
@@ -109,7 +107,6 @@ export const workContract = contract("work-board", {
109
107
  ReadFleetItems,
110
108
  ReadGoals,
111
109
  ReadStart,
112
- ReadRespond,
113
110
  ReadHandoff,
114
111
  ReadResult,
115
112
  ReadNavigation,
@@ -1,29 +1,20 @@
1
- import { Clock, Context, Effect, FileSystem, Path, Schema, Semaphore } from "effect";
1
+ import { Clock, Effect, FileSystem, Path, Schema, Semaphore } from "effect";
2
2
  import { defineService } from "@shivaedev/effect-service/define-service.ts";
3
3
  import { Conflict, Forbidden, PreconditionFailed, ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
4
4
  import { rpcBoundary } from "@shivaedev/platform/rpc-server/boundary.ts";
5
5
  import type { DraftInput } from "#browser/responses/schema.ts";
6
- import type { Changes } from "#server/files/changes.ts";
7
- import type { Snapshot } from "#server/search/snapshot.ts";
8
- import { awaitResponse } from "./await.ts";
9
- import { responseHistory } from "./history.ts";
10
- import { locate as locateQuestion } from "./locate.ts";
11
- import { makeResponse } from "./make.ts";
12
- import { publish } from "./publish.ts";
13
- import { readResponses } from "./read.ts";
14
- import { responseMarkdown } from "./records.ts";
15
- import { registerQuestion } from "./registerQuestion.ts";
16
- import { retryResponse } from "./retry.ts";
17
- import { validatedInput } from "./validatedInput.ts";
18
- import { validateSupersedes } from "./validateSupersedes.ts";
19
-
20
- export interface ResponseOptions {
21
- readonly changes: Changes;
22
- readonly enabled: boolean;
23
- readonly index: Effect.Effect<Snapshot, unknown>;
24
- readonly root: string;
25
- }
26
- export class ResponseConfig extends Context.Service<ResponseConfig, ResponseOptions>()("WorkBoard/ResponseConfig") {}
6
+ import { awaitResponse } from "#server/responses/await.ts";
7
+ import { responseHistory } from "#server/responses/history.ts";
8
+ import { locate as locateQuestion } from "#server/responses/locate.ts";
9
+ import { makeResponse } from "#server/responses/make.ts";
10
+ import { publish } from "#server/responses/publish.ts";
11
+ import { readResponses } from "#server/responses/read.ts";
12
+ import { responseMarkdown } from "#server/responses/records.ts";
13
+ import { registerQuestion } from "#server/responses/registerQuestion.ts";
14
+ import { retryResponse } from "#server/responses/retry.ts";
15
+ import { validatedInput } from "#server/responses/validatedInput.ts";
16
+ import { validateSupersedes } from "#server/responses/validateSupersedes.ts";
17
+ import { Workspace } from "#server/workspace.ts";
27
18
 
28
19
  function invalidResponse(cause: unknown): Conflict {
29
20
  return Schema.is(Conflict)(cause)
@@ -34,19 +25,20 @@ function invalidResponse(cause: unknown): Conflict {
34
25
  export const Responses = defineService({
35
26
  id: "WorkBoard/Responses",
36
27
  initialize: Effect.gen(function* () {
37
- return { fs: yield* FileSystem.FileSystem, lock: yield* Semaphore.make(1), options: yield* ResponseConfig };
28
+ const workspace = yield* Workspace;
29
+ return { fs: yield* FileSystem.FileSystem, lock: yield* Semaphore.make(1), options: yield* workspace.settings(), workspace };
38
30
  }),
39
- methods: ({ options, fs, lock }) => {
40
- const snapshot = options.index.pipe(
31
+ methods: ({ options, fs, lock, workspace }) => {
32
+ const snapshot = workspace.index().pipe(
41
33
  rpcBoundary(ServiceUnavailable, {
42
34
  message: "The workspace could not be indexed.",
43
35
  unexpected: (message) => new ServiceUnavailable({ message }),
44
36
  }),
45
37
  );
46
- const enabled = options.enabled
38
+ const enabled = options.writable
47
39
  ? Effect.void
48
40
  : Effect.fail(new Forbidden({ message: "Start with --responses to explicitly enable local question/response writes." }));
49
- const locate = locateQuestion(options.root, options.changes.realRoot, snapshot, fs);
41
+ const locate = locateQuestion(options.root, options.realRoot, snapshot, fs);
50
42
  const read = readResponses(snapshot);
51
43
  const register = registerQuestion(options, snapshot, locate, read);
52
44
  function record(draft: DraftInput) {
@@ -80,7 +72,7 @@ export const Responses = defineService({
80
72
  question,
81
73
  result.responses.reduce((time, saved) => Math.max(time, saved.response.recordedAt + 1), yield* Clock.currentTimeMillis),
82
74
  );
83
- yield* publish(options.root, options.changes.realRoot, response.id, responseMarkdown(response)).pipe(
75
+ yield* publish(options.root, options.realRoot, response.id, responseMarkdown(response)).pipe(
84
76
  Effect.ensuring(options.changes.refresh ?? Effect.void),
85
77
  );
86
78
  return response;
@@ -107,6 +99,6 @@ export const Responses = defineService({
107
99
  register: (input: Parameters<typeof register>[0]) => lock.withPermit(register(input)),
108
100
  };
109
101
  },
110
- requires: [ResponseConfig, FileSystem.FileSystem, Path.Path],
102
+ requires: [Workspace, FileSystem.FileSystem, Path.Path],
111
103
  });
112
104
  export type Responses = typeof Responses.Identifier;
@@ -6,7 +6,7 @@ import type { Snapshot } from "#server/search/snapshot.ts";
6
6
  import { answeredQuestion } from "./questionState.ts";
7
7
  import { requestInbox } from "./requestInbox.ts";
8
8
  import { isMaintainerRequest } from "./requests.ts";
9
- import { closedItem } from "./status.ts";
9
+ import { closedItem, deferredItem } from "./status.ts";
10
10
 
11
11
  const automation = /^(?:codex|claude|fleet|orchestrator|agent|automation|bot|copilot)(?:$|[-_. /])/iu;
12
12
 
@@ -24,6 +24,10 @@ function addressedElsewhere(document: MetadataDocument, maintainer: string): boo
24
24
  }
25
25
 
26
26
  export function waitingReason(document: MetadataDocument, model: MetadataModel, maintainer = "maintainer"): string | undefined {
27
+ return deferredItem(document) ? undefined : attentionReason(document, model, maintainer);
28
+ }
29
+
30
+ function attentionReason(document: MetadataDocument, model: MetadataModel, maintainer: string): string | undefined {
27
31
  const fields = document.parsed.fields;
28
32
  if (!openItem(document) || fields.kind === "response" || fields.kind === "handoff" || fields.question) {
29
33
  return undefined;
@@ -64,38 +68,59 @@ function inboxTarget(document: MetadataDocument, problem: string | undefined): s
64
68
  return answer ? responseUrl(item, answer) : identityUrl(item);
65
69
  }
66
70
 
67
- export function inboxItems(snapshot: Snapshot) {
68
- if (snapshot.unavailable.length > 0) {
69
- return [];
70
- }
71
+ function directItems(snapshot: Snapshot, skipped: ReadonlySet<string>, reasonOf: (document: MetadataDocument) => string | undefined) {
71
72
  const titles = new Map(snapshot.entries.filter((entry) => entry.kind === "document").map((entry) => [entry.file, entry.title]));
72
- const requests = requestInbox(snapshot);
73
- const you = requests.items.filter((item) => item.responseFrom.includes(snapshot.maintainer ?? "maintainer"));
74
- const direct = snapshot.documents.flatMap((document) => {
75
- if (requests.files.has(document.file)) {
73
+ return snapshot.documents.flatMap((document) => {
74
+ const reason = skipped.has(document.file) ? undefined : reasonOf(document);
75
+ if (!reason) {
76
76
  return [];
77
77
  }
78
78
  const id = document.parsed.fields.id;
79
79
  const problem = identityProblem(document, snapshot.model);
80
- const reason = waitingReason(document, snapshot.model, snapshot.maintainer);
81
- return reason
82
- ? [
83
- {
84
- document,
85
- href: inboxTarget(document, problem),
86
- id: id ?? document.file,
87
- problem,
88
- reason,
89
- title: titles.get(document.file) ?? document.file,
90
- unblocks: [id ?? ""],
91
- },
92
- ]
93
- : [];
80
+ return [
81
+ {
82
+ document,
83
+ href: inboxTarget(document, problem),
84
+ id: id ?? document.file,
85
+ modified: snapshot.modified.get(document.file),
86
+ problem,
87
+ reason,
88
+ request: undefined,
89
+ title: titles.get(document.file) ?? document.file,
90
+ unblocks: [id ?? ""],
91
+ },
92
+ ];
94
93
  });
95
- const ordinary = direct
94
+ }
95
+
96
+ function byAge(
97
+ left: { readonly id: string; readonly modified: number | undefined },
98
+ right: { readonly id: string; readonly modified: number | undefined },
99
+ ) {
100
+ return (left.modified ?? 0) - (right.modified ?? 0) || left.id.localeCompare(right.id);
101
+ }
102
+
103
+ export function inboxItems(snapshot: Snapshot) {
104
+ if (snapshot.unavailable.length > 0) {
105
+ return [];
106
+ }
107
+ const requests = requestInbox(snapshot);
108
+ const you = requests.items.filter((item) => item.responseFrom.includes(snapshot.maintainer ?? "maintainer"));
109
+ const ordinary = directItems(snapshot, requests.files, (document) => waitingReason(document, snapshot.model, snapshot.maintainer))
96
110
  .filter((item) => item.problem || !you.some((ask) => ask.id === item.id))
97
- .map((item) => ({ ...item, earlierAsks: 0, modified: snapshot.modified.get(item.document.file) }));
98
- return [...ordinary, ...you].sort((left, right) => (left.modified ?? 0) - (right.modified ?? 0) || left.id.localeCompare(right.id));
111
+ .map((item) => ({ ...item, earlierAsks: 0 }));
112
+ return [...ordinary, ...you].sort(byAge);
113
+ }
114
+
115
+ export function laterItems(snapshot: Snapshot) {
116
+ if (snapshot.unavailable.length > 0) {
117
+ return [];
118
+ }
119
+ function parked(document: MetadataDocument) {
120
+ const reason = deferredItem(document) ? attentionReason(document, snapshot.model, snapshot.maintainer ?? "maintainer") : undefined;
121
+ return reason && (document.parsed.fields.nextAction ?? reason);
122
+ }
123
+ return directItems(snapshot, requestInbox(snapshot).files, parked).sort(byAge);
99
124
  }
100
125
 
101
126
  export function fleetRecords(model: MetadataModel): ReadonlySet<string> {
@@ -6,6 +6,13 @@ import { questionId, revisionOf } from "#server/responses/records.ts";
6
6
 
7
7
  type Request = NonNullable<Metadata["attention"]>[number];
8
8
 
9
+ export type ManagedRequestState =
10
+ | { readonly _tag: "Open" }
11
+ | { readonly _tag: "Settled"; readonly receipt: RequestReceipt }
12
+ | { readonly _tag: "Conflicting"; readonly receipts: readonly string[] };
13
+
14
+ const open: ManagedRequestState = { _tag: "Open" };
15
+
9
16
  function uniqueDocument(id: string, model: MetadataModel): MetadataDocument | undefined {
10
17
  const matches = model.ids.get(id);
11
18
  return matches?.length === 1 ? matches[0] : undefined;
@@ -38,7 +45,7 @@ function appliedAnswer(question: string, revision: string, responseId: string |
38
45
  return current.length === 1 && current[0]?.id === responseId;
39
46
  }
40
47
 
41
- export function managedRequestReceipt(document: MetadataDocument, request: Request, model: MetadataModel): RequestReceipt | undefined {
48
+ function registeredQuestion(document: MetadataDocument, request: Request, model: MetadataModel) {
42
49
  const item = document.parsed.fields.id;
43
50
  if (
44
51
  request.managed !== true
@@ -57,7 +64,6 @@ export function managedRequestReceipt(document: MetadataDocument, request: Reque
57
64
  const revision = revisionOf(source);
58
65
  const id = questionId(item, request.id, revision, document.file);
59
66
  const archived = uniqueDocument(id, model);
60
- const index = responseIndex(model);
61
67
  const question = registeredContext(archived, model);
62
68
  if (
63
69
  archived?.parsed.diagnostics.length !== 0
@@ -70,14 +76,13 @@ export function managedRequestReceipt(document: MetadataDocument, request: Reque
70
76
  ) {
71
77
  return undefined;
72
78
  }
73
- if (index.receiptProblems) {
74
- return undefined;
75
- }
76
- const receipts = index.receipts.get(id) ?? [];
77
- const saved = receipts.length === 1 ? receipts[0] : undefined;
78
- const receipt = saved?.parsed.fields.requestReceipt;
79
+ return { id, revision };
80
+ }
81
+
82
+ function qualified(saved: MetadataDocument, question: string, revision: string, model: MetadataModel): RequestReceipt | undefined {
83
+ const receipt = saved.parsed.fields.requestReceipt;
79
84
  if (
80
- !(saved && receipt)
85
+ !receipt
81
86
  || saved.parsed.fields.kind !== "result"
82
87
  || saved.parsed.diagnostics.length > 0
83
88
  || !saved.parsed.fields.id
@@ -89,5 +94,31 @@ export function managedRequestReceipt(document: MetadataDocument, request: Reque
89
94
  if (receipt.disposition === "superseded") {
90
95
  return receipt.response === undefined ? receipt : undefined;
91
96
  }
92
- return appliedAnswer(id, revision, receipt.response, model) ? receipt : undefined;
97
+ return appliedAnswer(question, revision, receipt.response, model) ? receipt : undefined;
98
+ }
99
+
100
+ function settling(receipts: readonly RequestReceipt[]): RequestReceipt | undefined {
101
+ return receipts.toSorted(
102
+ (left, right) => Number(right.disposition === "applied") - Number(left.disposition === "applied") || right.recordedAt - left.recordedAt,
103
+ )[0];
104
+ }
105
+
106
+ export function managedRequestState(document: MetadataDocument, request: Request, model: MetadataModel): ManagedRequestState {
107
+ const question = registeredQuestion(document, request, model);
108
+ const index = responseIndex(model);
109
+ if (!question || index.receiptProblems) {
110
+ return open;
111
+ }
112
+ const saved = index.receipts.get(question.id) ?? [];
113
+ const receipts = saved.flatMap((candidate) => qualified(candidate, question.id, question.revision, model) ?? []);
114
+ const receipt = receipts.length === saved.length ? settling(receipts) : undefined;
115
+ if (receipt) {
116
+ return { _tag: "Settled", receipt };
117
+ }
118
+ return receipts.length > 0 ? { _tag: "Conflicting", receipts: saved.map((candidate) => candidate.file) } : open;
119
+ }
120
+
121
+ export function managedRequestReceipt(document: MetadataDocument, request: Request, model: MetadataModel): RequestReceipt | undefined {
122
+ const state = managedRequestState(document, request, model);
123
+ return state._tag === "Settled" ? state.receipt : undefined;
93
124
  }
@@ -1,6 +1,6 @@
1
1
  import type { InboxProjection } from "#browser/views/schema.ts";
2
2
  import type { Snapshot } from "#server/search/snapshot.ts";
3
- import { inboxItems } from "./inbox.ts";
3
+ import { inboxItems, laterItems } from "./inbox.ts";
4
4
  import { requestInbox } from "./requestInbox.ts";
5
5
  import { attentionRequests } from "./requests.ts";
6
6
 
@@ -12,7 +12,9 @@ export function attentionProjection(snapshot: Snapshot): InboxProjection {
12
12
  return cached;
13
13
  }
14
14
  const requests = requestInbox(snapshot);
15
- function project({ document, unblocks: _unblocks, ...item }: ReturnType<typeof inboxItems>[number]) {
15
+ function project<
16
+ TRow extends { readonly document: { readonly file: string }; readonly request: string | undefined; readonly unblocks: readonly string[] },
17
+ >({ document, request: _request, unblocks: _unblocks, ...item }: TRow) {
16
18
  return { ...item, file: document.file };
17
19
  }
18
20
  const you = inboxItems(snapshot).map(project);
@@ -22,6 +24,7 @@ export function attentionProjection(snapshot: Snapshot): InboxProjection {
22
24
  complete: snapshot.unavailable.length === 0,
23
25
  issues,
24
26
  items: you,
27
+ later: laterItems(snapshot).map(project),
25
28
  parked: requests.parked.map(project),
26
29
  resolved: requests.resolved.map(project),
27
30
  sourceIssues: issues.length > 0,
@@ -23,11 +23,12 @@ export function requestInbox(snapshot: Snapshot) {
23
23
  return {
24
24
  document: entry.document,
25
25
  earlierAsks: 0,
26
- href: responseUrl(entry.itemId, entry.request.id),
26
+ href: entry.problem === undefined ? responseUrl(entry.itemId, entry.request.id) : entry.href,
27
27
  id: target?.parsed.fields.id ?? entry.itemId,
28
28
  modified: snapshot.modified.get(entry.document.file),
29
- problem: undefined,
29
+ problem: entry.problem,
30
30
  reason: entry.request.managed ? `Managed decision: ${entry.request.reason}` : entry.request.reason,
31
+ request: entry.request.id,
31
32
  responseFrom: entry.request.responseFrom,
32
33
  title: (target ? titles.get(target.file) : undefined) ?? entry.title,
33
34
  unblocks: entry.request.unblocks,
@@ -1,5 +1,5 @@
1
1
  import type { Metadata } from "#metadata/schema.ts";
2
- import { managedRequestReceipt } from "#server/attention/managedRequestReceipt.ts";
2
+ import { managedRequestState } from "#server/attention/managedRequestReceipt.ts";
3
3
  import { identityUrl, referenceTarget } from "#server/metadata/links.ts";
4
4
  import type { MetadataDocument } from "#server/metadata/model.ts";
5
5
  import type { Snapshot } from "#server/search/snapshot.ts";
@@ -10,6 +10,7 @@ export interface AttentionEntry {
10
10
  readonly href: string;
11
11
  readonly itemId: string;
12
12
  readonly line: number;
13
+ readonly problem: string | undefined;
13
14
  readonly request: Request;
14
15
  readonly title: string;
15
16
  }
@@ -35,16 +36,20 @@ function requestsFor(document: MetadataDocument, snapshot: Snapshot, title: stri
35
36
  request.state !== "open"
36
37
  || requests.filter((other) => other.id === request.id).length !== 1
37
38
  || request.unblocks.some((target) => !referenceTarget(target, snapshot.model))
38
- || managedRequestReceipt(document, request, snapshot.model) !== undefined
39
39
  ) {
40
40
  return [];
41
41
  }
42
+ const receipts = managedRequestState(document, request, snapshot.model);
43
+ if (receipts._tag === "Settled") {
44
+ return [];
45
+ }
42
46
  return [
43
47
  {
44
48
  document,
45
49
  href: `${identityUrl(itemId)}#attention-request-${encodeURIComponent(request.id)}`,
46
50
  itemId,
47
51
  line: lines[`attention.${index}`] ?? lines.attention ?? 2,
52
+ problem: receipts._tag === "Conflicting" ? `Request receipts disagree: ${receipts.receipts.join(", ")}` : undefined,
48
53
  request,
49
54
  title,
50
55
  },
@@ -1,16 +1,15 @@
1
1
  import { Context, Effect, FileSystem, Layer, Path, type PlatformError } from "effect";
2
- import { DiagnosticsSources } from "#diagnostics/source.ts";
2
+ import { DiagnosticsConfig, DiagnosticsSources } from "#diagnostics/source.ts";
3
3
  import type { BoardOptions } from "#server/board.ts";
4
4
  import { boardServices } from "#server/boardServices.ts";
5
5
  import { daemonDiagnostics } from "#server/diagnostics/daemon.ts";
6
- import { readDocument } from "#server/document/read.ts";
7
6
  import { watchChanges } from "#server/files/changes.ts";
8
7
  import { homeIn } from "#server/files/home.ts";
9
8
  import { checkBrowserAssets, mermaidRoot } from "#server/http/assets.ts";
10
9
  import { Highlighter } from "#server/render/Highlighter.ts";
11
10
  import type { HandlerOptions } from "#server/rpc/HandlerOptions.ts";
12
- import { handlers } from "#server/rpc/handlers.ts";
13
11
  import { type Snapshot, searchSnapshot } from "#server/search/snapshot.ts";
12
+ import { Workspace, WorkspaceConfig } from "#server/workspace.ts";
14
13
 
15
14
  export const boardRuntime = Effect.fn("WorkBoard.boardRuntime")(function* (options: BoardOptions, server?: HandlerOptions["server"]) {
16
15
  const home = yield* homeIn(options.root, options.home);
@@ -28,30 +27,26 @@ export const boardRuntime = Effect.fn("WorkBoard.boardRuntime")(function* (optio
28
27
  );
29
28
  const provided = yield* Effect.serviceOption(DiagnosticsSources);
30
29
  const services = yield* Layer.build(
31
- boardServices(
32
- { changes, enabled: options.responses === true, index: Effect.provideContext(index, platform), root: options.root },
33
- provided._tag === "Some" ? { _tag: "Sources", sources: provided.value } : { _tag: "Read", read: daemonDiagnostics(options.fleetPort) },
34
- ),
35
- );
36
- const context = Context.merge(platform, services);
37
- const rpcHandlers = yield* handlers({
38
- ...(options.fleetPort === undefined ? {} : { fleetPort: options.fleetPort }),
39
- changes,
40
- document: (url) =>
41
- Effect.provideContext(
42
- readDocument({
30
+ boardServices({
31
+ ...(options.fleetPort === undefined ? {} : { fleetPort: options.fleetPort }),
32
+ ...(server === undefined ? {} : { server }),
33
+ }).pipe(
34
+ Layer.provideMerge(Workspace.layer),
35
+ Layer.provide([
36
+ Layer.succeed(WorkspaceConfig, {
43
37
  changes,
44
38
  home,
45
- index: Effect.provideContext(index, context),
39
+ index: Effect.provideContext(index, platform),
46
40
  root: options.root,
47
41
  writable: options.responses === true,
48
- })(url),
49
- context,
50
- ),
51
- home,
52
- index: Effect.provideContext(index, context),
53
- ...(server === undefined ? {} : { server }),
54
- writable: options.responses === true,
55
- }).pipe(Effect.provideContext(context));
56
- return { changes, context, home, index, mermaid, rpcHandlers };
42
+ }),
43
+ Layer.succeed(
44
+ DiagnosticsConfig,
45
+ provided._tag === "Some" ? { _tag: "Sources", sources: provided.value } : { _tag: "Read", read: daemonDiagnostics(options.fleetPort) },
46
+ ),
47
+ ]),
48
+ ),
49
+ );
50
+ const context = Context.merge(platform, services);
51
+ return { changes, context, home, index, mermaid, rpcHandlers: Layer.succeedContext(services) };
57
52
  });
@@ -1,10 +1,10 @@
1
- import { type Context, Layer } from "effect";
2
- import { Diagnostics, DiagnosticsConfig } from "#diagnostics/source.ts";
1
+ import { Layer } from "effect";
2
+ import { Diagnostics } from "#diagnostics/source.ts";
3
3
  import { HandoffsService } from "#server/handoffs/Service.ts";
4
- import { ResponseConfig, type ResponseOptions, Responses } from "#server/responses/service.ts";
4
+ import { Responses } from "#server/Responses.ts";
5
+ import type { HandlerOptions } from "#server/rpc/HandlerOptions.ts";
6
+ import { handlers } from "#server/rpc/handlers.ts";
5
7
 
6
- export function boardServices(options: ResponseOptions, diagnostics: Context.Service.Shape<typeof DiagnosticsConfig>) {
7
- return Layer.mergeAll(Responses.layer, HandoffsService.layer, Diagnostics.layer).pipe(
8
- Layer.provide(Layer.merge(Layer.succeed(ResponseConfig, options), Layer.succeed(DiagnosticsConfig, diagnostics))),
9
- );
8
+ export function boardServices(options: HandlerOptions = {}) {
9
+ return Layer.unwrap(handlers(options)).pipe(Layer.provideMerge(Layer.mergeAll(Responses.layer, HandoffsService.layer, Diagnostics.layer)));
10
10
  }
@@ -2,24 +2,17 @@ import { Effect, FileSystem } from "effect";
2
2
  import { Conflict, NotFound, ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
3
3
  import type { Document } from "#browser/document/schema.ts";
4
4
  import { currentDocument } from "#server/currentDocument.ts";
5
- import type { Changes } from "#server/files/changes.ts";
6
5
  import type { MarkdownFile } from "#server/files/list.ts";
7
6
  import { markdownPath } from "#server/files/markdownPath.ts";
7
+ import { locate } from "#server/responses/locate.ts";
8
8
  import { readingActions } from "#server/responses/readingActions.ts";
9
9
  import type { Snapshot } from "#server/search/snapshot.ts";
10
+ import type { WorkspaceSettings } from "#server/workspace.ts";
10
11
  import { answerBody } from "./answerBody.ts";
11
12
  import { readingContext } from "./readingContext.ts";
12
13
  import { documentRequests } from "./requests.ts";
13
14
  import { documentTargets } from "./targets.ts";
14
15
 
15
- export interface DocumentOptions {
16
- readonly changes: Changes;
17
- readonly home: string | undefined;
18
- readonly index: Effect.Effect<Snapshot, unknown>;
19
- readonly root: string;
20
- readonly writable: boolean;
21
- }
22
-
23
16
  const selected = Effect.fn("WorkBoard.selected")(function* (
24
17
  url: string,
25
18
  snapshot: Snapshot,
@@ -45,12 +38,11 @@ const selected = Effect.fn("WorkBoard.selected")(function* (
45
38
  return { file, item };
46
39
  });
47
40
 
48
- export function readDocument({ changes, home, index, root, writable }: DocumentOptions) {
49
- return Effect.fn("WorkBoard.document")(function* (url: string) {
50
- const snapshot = yield* index;
41
+ export function readDocument({ changes, home, root, realRoot, writable }: WorkspaceSettings) {
42
+ return Effect.fn("WorkBoard.document")(function* (url: string, snapshot: Snapshot) {
51
43
  const files = yield* changes.files;
52
44
  const { file, item } = yield* selected(url, snapshot, files, home);
53
- const real = yield* markdownPath(root, changes.realRoot, file.path);
45
+ const real = yield* markdownPath(root, realRoot, file.path);
54
46
  if (!real) {
55
47
  return yield* Effect.fail(new NotFound({ message: "The document is missing." }));
56
48
  }
@@ -65,7 +57,7 @@ export function readDocument({ changes, home, index, root, writable }: DocumentO
65
57
  if (item && identity !== item) {
66
58
  return yield* Effect.fail(new NotFound({ message: "The document is missing." }));
67
59
  }
68
- const questionActions = yield* readingActions(document, source, writable, identity);
60
+ const questionActions = yield* readingActions(document, source, writable, identity, locate(root, realRoot, Effect.succeed(snapshot), fs));
69
61
  const titles = new Map(current.snapshot.entries.filter((entry) => entry.kind === "document").map((entry) => [entry.file, entry.title]));
70
62
  const reading = readingContext(document, model, file.path === home, titles);
71
63
  const targets = documentTargets(
@@ -1,6 +1,5 @@
1
1
  import { ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
2
2
  import type { DocumentRequest } from "#browser/document/schema.ts";
3
- import { responseUrl } from "#responses/url.ts";
4
3
  import { authoredQuestion, openItem } from "#server/attention/inbox.ts";
5
4
  import { requestInbox } from "#server/attention/requestInbox.ts";
6
5
  import { attentionRequests } from "#server/attention/requests.ts";
@@ -84,6 +83,6 @@ function requestOpenInSource(document: MetadataDocument, id: string): boolean {
84
83
 
85
84
  function activeSource(document: MetadataDocument, snapshot: Snapshot, item: string, id: string) {
86
85
  const managed = document.parsed.fields.attention?.find((entry) => entry.id === id)?.managed;
87
- const eligible = !managed || requestInbox(snapshot).items.some((entry) => entry.href === responseUrl(item, id));
86
+ const eligible = !managed || requestInbox(snapshot).items.some((entry) => entry.document.file === document.file && entry.request === id);
88
87
  return eligible ? requestSource(snapshot, item, id) : undefined;
89
88
  }