goal_detail.html 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <pstm-uri>/goals/{goal_id}</pstm-uri>
  5. <pstm-template>main</pstm-template>
  6. <script>
  7. // The goal page: supervision surface of one goal - status head
  8. // with lifecycle actions (plus run-control parity: retry
  9. // supervision, requeue-to-resume), branch/push-state chips, one
  10. // muted activity line, the agent's live progress feed, the ATTENTION
  11. // blocks (the goal agent's answerable questions/RFCs and the
  12. // completion review), the task status strip with per-row
  13. // capability badges, the locked brief, the goal's effective
  14. // rules, the approved plan (revision history behind the
  15. // expander), the supervision narrative (events at full weight,
  16. // comment events as footnotes), the comment thread with
  17. // selection-quoting, and the goal agent's runs. Every long-form
  18. // body (brief, plans, reports, blocks, comments, events) renders
  19. // as markdown via stm-markdown. The plan review collects REMARKS
  20. // through the shared initPlanReviews engine (main.html, plan 16
  21. // R3/arch 15): a per-block queue that renders at the bottom of
  22. // the plan, persists across pulse refreshes (the maps live
  23. // outside statum re-renders), rides the rejection numbered, and
  24. // clears on accept. The status strip's
  25. // filter lives client-side in window.goalTaskFilter; clicking a
  26. // strip button re-runs the sealed refresh action, and the filter
  27. // rides the re-render.
  28. if (window.statum) {
  29. (function () {
  30. 'use strict';
  31. window.goalTaskFilter = window.goalTaskFilter || null;
  32. window.goalFilterClass = function (status) {
  33. return window.goalTaskFilter === status ? 'goal-filter active'
  34. : 'goal-filter';
  35. };
  36. window.goalTaskVisible = function (task) {
  37. return window.goalTaskFilter == null
  38. || window.goalTaskFilter === false
  39. || window.goalTaskFilter === ''
  40. || window.goalTaskFilter === task.status;
  41. };
  42. window.goalSetFilter = function (status) {
  43. window.goalTaskFilter = window.goalTaskFilter === status ? null : status;
  44. refreshGoal('filter');
  45. };
  46. function refreshGoal(source) {
  47. var goal = statum.read('goal');
  48. if (!goal || !goal.refresh) return;
  49. statum.act(goal.refresh).catch(function (error) {
  50. console.warn('[goal] live refresh (' + source + ') failed:', error);
  51. });
  52. }
  53. statum.addStateListener('pulse', function () {
  54. refreshGoal('pulse');
  55. });
  56. document.addEventListener('visibilitychange', function () {
  57. if (document.visibilityState === 'visible') refreshGoal('focus');
  58. });
  59. if (window.initQuoteComments) {
  60. window.initQuoteComments({
  61. matches: function (node) {
  62. // The pending plan body feeds the REMARKS
  63. // popover instead (below) - everything else
  64. // long-form on the goal quote-comments.
  65. return node.closest && node.closest('.goal-body') != null
  66. && node.closest('[data-plan-body]') == null;
  67. },
  68. action: function () {
  69. var goal = statum.read('goal');
  70. return goal && goal.comment;
  71. }
  72. });
  73. }
  74. // The goal's plan-review remarks ride the SHARED
  75. // engine (main.html's initPlanReviews, plan 16 R3/arch
  76. // 15 - the duplicated ~180-line collect-remarks pattern
  77. // now lives once); only the block resolution is page
  78. // state.
  79. // Plan 16 S2: the block for a review container resolves
  80. // from the ACTUAL [data-plan-review] element - the pending
  81. // completion review renders as an attention block (NOT the
  82. // plan section), so goal.plan_pending_block alone never
  83. // resolves it; every open block plus the plan section's
  84. // row are all candidates.
  85. function resolveReviewBlock(id) {
  86. var goal = statum.read('goal');
  87. if (!goal) return null;
  88. var block = null;
  89. (goal.open_blocks || []).forEach(function (candidate) {
  90. if (String(candidate.id) === String(id)) block = candidate;
  91. });
  92. if (!block && goal.plan_pending_block
  93. && String(goal.plan_pending_block.id) === String(id)) {
  94. block = goal.plan_pending_block;
  95. }
  96. return block;
  97. }
  98. if (window.initPlanReviews) {
  99. window.initPlanReviews({
  100. resolveBlock: function (id) { return resolveReviewBlock(id); }
  101. });
  102. }
  103. // The brief card's in-place editor (task 12).
  104. if (window.initLongFormEditors) {
  105. window.initLongFormEditors();
  106. }
  107. })();
  108. }
  109. </script>
  110. </head>
  111. <body>
  112. <div class="page goal-page">
  113. <p><a href="/goals">&larr; Goals</a></p>
  114. <h2 stm-if="goal.exists"><span class="goal-icon">🎯</span> #<span stm-text="goal.id">id</span> —
  115. <span stm-text="goal.title">title</span></h2>
  116. <p stm-if="!goal.exists" class="muted">Goal not found.</p>
  117. <div stm-if="goal.exists">
  118. <!-- Status head (first, always): lifecycle badge, branch
  119. pair with the daemon-reported push-state chips,
  120. priority, timestamps, the assigned/parking agent
  121. saddle and the lifecycle actions — with full run
  122. control parity (plan 14 B1): Retry supervision for a
  123. failed goal, Requeue-to-resume for a run stale/parked
  124. on a dead saddle, Cancel. NO notify-me toggle — goals
  125. notify unconditionally by design. Yeehaw stays on
  126. task rows only. -->
  127. <section class="card goal-head">
  128. <div class="card-head">
  129. <h3>Status</h3>
  130. </div>
  131. <p><span stm-attribute.class="'badge badge-' + goal.status" stm-text="goal.display">status</span>
  132. · goal branch <code stm-text="goal.branch_name">branch</code>
  133. <span class="muted">from</span> <code stm-text="goal.base_branch">base</code>
  134. <a stm-if="goal.compare_url.length > 0" stm-attribute.href="goal.compare_url"
  135. target="_blank" rel="noopener">· compare changes</a>
  136. · priority <strong stm-text="goal.priority">3</strong>
  137. <span stm-if="goal.agent_saddle.length > 0"> · agent saddle
  138. <strong stm-text="goal.agent_saddle">saddle</strong></span></p>
  139. <pstm-fragment name="goal-branches"
  140. note="&quot;(the daemon's /status report)&quot;"/>
  141. <p class="muted">Project:
  142. <a stm-attribute.href="'/projects/' + goal.project_id" stm-text="goal.project_name">project</a>
  143. · Created <span stm-text="goal.created_display">when</span>
  144. <span stm-if="goal.started_display.length > 0"> · Started
  145. <span stm-text="goal.started_display">when</span></span>
  146. <span stm-if="goal.completed_display.length > 0"> · Finished
  147. <span stm-text="goal.completed_display">when</span></span></p>
  148. <p>
  149. <button type="button" class="primary" stm-if="goal.start != null"
  150. stm-action="goal.start">Start goal</button>
  151. <button type="button" stm-if="goal.pause != null"
  152. stm-action="goal.pause">Pause</button>
  153. <button type="button" stm-if="goal.resume != null"
  154. stm-action="goal.resume">Resume</button>
  155. <button type="button" class="primary" stm-if="goal.retry_run != null"
  156. stm-action="goal.retry_run">Retry supervision</button>
  157. <button type="button" stm-if="goal.requeue_run != null"
  158. stm-action="goal.requeue_run"
  159. stm-confirm="Requeue the agent's run? The daemon restores its workspace context on the next dispatch.">Requeue to resume</button>
  160. <!-- Goal 4 task 1: Force wake — the parked-agent
  161. recovery for the no-pending-wake stall. -->
  162. <button type="button" stm-if="goal.force_wake != null"
  163. stm-action="goal.force_wake"
  164. stm-confirm="Force-woke this goal's parked agent? It resumes immediately on its saddle - one wake per press.">Force wake</button>
  165. <button type="button" class="danger" stm-if="goal.cancel != null"
  166. stm-action="goal.cancel"
  167. stm-confirm="Cancel this goal? Its tasks are cancelled with it.">Cancel goal</button>
  168. </p>
  169. <p class="muted" stm-if="goal.status == 'draft'">Starting locks the brief and
  170. hands the goal to its agent (an orchestrate-mode run task, priority 3).</p>
  171. <p class="muted" stm-if="goal.status == 'paused'">Paused — supervision frozen;
  172. task events queue and coalesce into the resume.</p>
  173. <!-- Plan 15 C4: the ONE muted activity line (the
  174. Current-activity card is gone) — what the run is
  175. doing right now, folded into the status head. -->
  176. <p class="muted" stm-if="goal.current_activity.length > 0"
  177. stm-text="goal.current_activity">activity</p>
  178. <p class="muted" stm-if="goal.saddle_health.length > 0"
  179. stm-text="goal.saddle_health">health</p>
  180. </section>
  181. <!-- The agent's live progress feed (task-page pattern):
  182. the newest line leads, its history behind the
  183. expander. Plan 15 C4: a CARD again (the goalui
  184. restyle regression — every section rides the same
  185. card rhythm). -->
  186. <section class="card">
  187. <h3>Agent progress</h3>
  188. <pstm-fragment name="progress-feed" entries="goal.progress"/>
  189. <p stm-if="goal.progress.length == 0" class="muted">No progress reports yet — the goal
  190. agent reports via <code>saddle progress-update</code>.</p>
  191. </section>
  192. <!-- ATTENTION (plan 14 B5 order; plan 15 C4): the goal
  193. agent's OPEN blocks lead — questions and RFCs with
  194. full markdown bodies and inline answer forms — and
  195. the historic answered ones fold behind the
  196. answered-blocks expander (the task page's pattern);
  197. the completion review follows when one awaits. -->
  198. <section class="card"
  199. stm-if="goal.open_blocks.length > 0 || goal.answered_blocks.length > 0">
  200. <h3>Goal agent blocks</h3>
  201. <div stm-for-block-in="goal.open_blocks" class="goal-block">
  202. <p><span stm-attribute.class="'badge badge-block-' + block.block_type"
  203. stm-text="block.block_type">kind</span>
  204. <strong stm-text="block.question">question</strong>
  205. <span class="muted" stm-text="block.created_display">when</span></p>
  206. <!-- The pending completion review renders in the rich
  207. review form (highlight-remarks queue shared with
  208. the plan review; push chips + compare above the
  209. report; accept gate enforced server-side). -->
  210. <div stm-if="block.is_completion_review"
  211. class="plan-review" data-plan-review
  212. stm-attribute.data-block-id="block.id">
  213. <pstm-fragment name="goal-branches"/>
  214. <pstm-fragment name="goal-compare"/>
  215. <div class="md rfc-body goal-body" data-plan-body
  216. stm-markdown="block.body">report</div>
  217. <div class="actions">
  218. <form stm-action="block.answer_action" class="inline">
  219. <input type="hidden" name="verdict" value="accept">
  220. <button type="submit" class="approve" data-plan-accept>Accept goal</button>
  221. </form>
  222. <button type="button" data-plan-toggle>Reject with feedback</button>
  223. </div>
  224. <div data-plan-feedback hidden>
  225. <p class="muted">Highlight report text to attach a remark to it — the queue
  226. rides the rejection numbered, then clears. Accepting ignores the queue.</p>
  227. <ul class="plan-comments" data-plan-remarks></ul>
  228. <textarea name="feedback" rows="2"
  229. placeholder="Additional feedback (optional)"></textarea>
  230. <button type="button" class="primary" data-plan-submit>Submit feedback</button>
  231. </div>
  232. </div>
  233. <div stm-if="!block.is_completion_review">
  234. <div stm-if="block.show_body && block.body.length > 0"
  235. class="md rfc-body goal-body" stm-markdown="block.body">body</div>
  236. <pstm-fragment name="block-answer"/>
  237. <pstm-fragment name="block-reply"/>
  238. </div>
  239. </div>
  240. <details class="history-expander" stm-if="goal.answered_blocks.length > 0">
  241. <summary>Answered blocks (<span stm-text="goal.answered_blocks.length">0</span>)</summary>
  242. <div stm-for-block-in="goal.answered_blocks" class="goal-block">
  243. <p><span stm-attribute.class="'badge badge-block-' + block.block_type"
  244. stm-text="block.block_type">kind</span>
  245. <strong stm-text="block.question">question</strong>
  246. <span class="muted" stm-text="block.created_display">when</span></p>
  247. <div stm-if="block.show_body && block.body.length > 0"
  248. class="md rfc-body goal-body" stm-markdown="block.body">body</div>
  249. <blockquote stm-if="block.answered" class="term-comment-quote">
  250. <span stm-text="block.answered_by">who</span>:
  251. <span stm-text="block.answer">answer</span></blockquote>
  252. </div>
  253. </details>
  254. </section>
  255. <!-- Completion review card: the TERMINAL summary — shown
  256. once the goal is complete/failed, with the report and
  257. the compare link. While PENDING, the review lives in
  258. the attention blocks above (the rich review form). -->
  259. <section class="card" stm-if="goal.terminal && goal.completion_report.length > 0">
  260. <h3>Completion review</h3>
  261. <pstm-fragment name="goal-branches"/>
  262. <pstm-fragment name="goal-compare"/>
  263. <div class="md rfc-body goal-body" stm-markdown="goal.completion_report">report</div>
  264. </section>
  265. <!-- The plan (plan 15 C4): BETWEEN attention and tasks.
  266. While a plan review is open the pending submission
  267. renders EXPANDED with the remarks queue (plan 14 B2);
  268. otherwise the approved plan and the revision history
  269. (approved + rejected) sit behind a collapsed
  270. expander — the goal page leads with what needs a
  271. human, not with settled history. -->
  272. <section class="card">
  273. <h3>Plan</h3>
  274. <div stm-if="goal.plan_pending_block != null && goal.plan_pending_block != false"
  275. class="plan-review" data-plan-review
  276. stm-attribute.data-block-id="goal.plan_pending_block.id">
  277. <h4>Plan submitted for review</h4>
  278. <div class="md rfc-body goal-body" data-plan-body
  279. stm-markdown="goal.plan_pending_block.body">revision</div>
  280. <div class="actions">
  281. <form stm-action="goal.plan_pending_block.answer_action" class="inline">
  282. <input type="hidden" name="verdict" value="accept">
  283. <button type="submit" class="approve" data-plan-accept>Approve plan</button>
  284. </form>
  285. <button type="button" data-plan-toggle>Reject with feedback</button>
  286. </div>
  287. <div data-plan-feedback hidden>
  288. <p class="muted">Highlight plan text to attach a remark to it — the queue
  289. rides the rejection numbered, then clears. Approving ignores the queue.</p>
  290. <ul class="plan-comments" data-plan-remarks></ul>
  291. <textarea name="feedback" rows="2"
  292. placeholder="Additional feedback (optional)"></textarea>
  293. <button type="button" class="primary" data-plan-submit>Submit feedback</button>
  294. </div>
  295. </div>
  296. <details class="history-expander"
  297. stm-if="goal.plan_pending_block == null || goal.plan_pending_block == false">
  298. <summary>Approved plan &amp; revisions (<span
  299. stm-text="goal.plan_revisions.length">0</span>)</summary>
  300. <div stm-if="goal.plan.length > 0" class="md rfc-body goal-body" data-plan-body
  301. stm-markdown="goal.plan">plan</div>
  302. <p stm-if="goal.plan.length == 0" class="muted">No approved plan yet.</p>
  303. <div stm-for-revision-in="goal.plan_revisions" class="plan-revision">
  304. <p><span stm-attribute.class="'badge ' + (revision.approved ? 'badge-done' : (revision.answered ? 'badge-failed' : 'badge-plan'))"
  305. stm-text="revision.approved ? 'approved' : (revision.answered ? 'rejected' : 'pending')">verdict</span>
  306. <span class="muted">Revision #<span stm-text="revision.id">n</span> &mdash; <span stm-text="revision.created_display">when</span></span></p>
  307. <details>
  308. <summary>Submitted markdown</summary>
  309. <div class="md rfc-body" stm-markdown="revision.body">body</div>
  310. </details>
  311. <blockquote stm-if="revision.answered" class="term-comment-quote">
  312. <span stm-text="revision.answered_by">who</span>:
  313. <span stm-text="revision.answer">answer</span></blockquote>
  314. </div>
  315. </details>
  316. </section>
  317. <!-- Task status strip + the goal's task list (plan 14 B5:
  318. tasks above the brief; capability badges per row —
  319. no editing here; yeehaw on rows only). -->
  320. <section class="card">
  321. <h3>Tasks</h3>
  322. <p class="goal-strip">
  323. <button type="button" stm-attribute.class="window.goalFilterClass('draft')"
  324. onclick="window.goalSetFilter('draft')"
  325. stm-text="'draft ' + goal.draft_tasks">draft</button>
  326. <button type="button" stm-attribute.class="window.goalFilterClass('queued')"
  327. onclick="window.goalSetFilter('queued')"
  328. stm-text="'queued ' + goal.queued_tasks">queued</button>
  329. <button type="button" stm-attribute.class="window.goalFilterClass('working')"
  330. onclick="window.goalSetFilter('working')"
  331. stm-text="'running ' + goal.running_tasks">running</button>
  332. <button type="button" stm-attribute.class="window.goalFilterClass('done')"
  333. onclick="window.goalSetFilter('done')"
  334. stm-text="'done ' + goal.done_tasks">done</button>
  335. <button type="button" stm-attribute.class="window.goalFilterClass('failed')"
  336. onclick="window.goalSetFilter('failed')"
  337. stm-text="'failed ' + goal.failed_tasks">failed</button>
  338. <span class="muted">— click a status to filter</span>
  339. </p>
  340. <table>
  341. <thead><tr><th>#</th><th>Title</th><th>Status</th><th>Mode</th><th>Requires</th><th>Created by</th></tr></thead>
  342. <tbody>
  343. <tr stm-for-goal_task-in="goal.goal_tasks"
  344. stm-if="window.goalTaskVisible(goal_task)">
  345. <td stm-text="goal_task.id">id</td>
  346. <td><a stm-attribute.href="'/tasks/' + goal_task.id"
  347. stm-text="goal_task.title">title</a></td>
  348. <td><span stm-attribute.class="'badge badge-' + goal_task.status"
  349. stm-text="goal_task.display">status</span>
  350. </td>
  351. <td><span stm-attribute.class="'badge badge-mode-' + goal_task.mode"
  352. stm-text="goal_task.mode">mode</span></td>
  353. <td><span stm-for-capability-in="goal_task.capabilities">
  354. <span class="badge badge-capability"
  355. stm-text="capability">tag</span> </span></td>
  356. <td class="muted" stm-text="goal_task.creator">who</td>
  357. </tr>
  358. <tr stm-if="goal.goal_tasks.length == 0">
  359. <td colspan="6" class="muted">No tasks yet — the goal agent fans
  360. them out once the plan is approved.</td></tr>
  361. </tbody>
  362. </table>
  363. </section>
  364. <!-- The locked brief. Task 12: DRAFT goals swap the
  365. rendered markdown for an in-place editor
  366. (initLongFormEditors in main.html) carrying title,
  367. brief and priority — a draft goal has no other edit
  368. surface (this also unblocks the draft priority, which
  369. nothing else could change). Starting locks the brief:
  370. the editor's stm-if wrapper disappears with the
  371. update action and the card stays the plain read view. -->
  372. <section class="card" data-long-form data-field="goal"
  373. data-lf-open="0">
  374. <h3>Initial brief
  375. <button type="button" class="inline" stm-if="goal.update != null"
  376. data-lf-toggle>Edit</button>
  377. <span class="muted" stm-if="goal.started_display.length > 0">(locked)</span></h3>
  378. <div data-lf-view>
  379. <div class="md rfc-body goal-body" data-brief-body stm-markdown="goal.brief">brief</div>
  380. </div>
  381. <div stm-if="goal.update != null">
  382. <form data-lf-edit hidden stm-action="goal.update">
  383. <div class="form-row"><label>Title</label>
  384. <input type="text" name="title" data-lf-src="goal.title"></div>
  385. <div class="form-row"><label>Brief (markdown)</label>
  386. <textarea name="brief" rows="8"
  387. data-lf-src="goal.brief">brief</textarea></div>
  388. <div class="form-row"><label>Priority (1–9)</label>
  389. <input type="number" name="priority" min="1" max="9"
  390. data-lf-src="goal.priority"></div>
  391. <button type="submit">Save</button>
  392. <button type="button" data-lf-toggle>Cancel</button>
  393. </form>
  394. </div>
  395. </section>
  396. <!-- Involved projects. -->
  397. <section class="card">
  398. <h3>Involved projects</h3>
  399. <p stm-if="goal.links.length > 0"><span stm-for-link-in="goal.links">
  400. <span stm-attribute.class="'badge badge-' + link.relationship"
  401. stm-text="link.name + ' (' + link.relationship + ')'">link</span>
  402. <button type="button" class="inline" stm-action="link.remove"
  403. stm-confirm="Remove this link?">&times;</button>
  404. <input type="hidden" name="link_id" stm-attribute.value="link.id">
  405. </span></p>
  406. <p stm-if="goal.links.length == 0" class="muted">Only the primary project.</p>
  407. <details stm-if="!goal.terminal">
  408. <summary>Link a project</summary>
  409. <form class="inline" stm-action="goal.add_link">
  410. <select name="linked_project_id" required>
  411. <option stm-for-project-in="goal.project_options" stm-attribute.value="project.id"
  412. stm-text="project.name">project</option>
  413. </select>
  414. <select name="relationship">
  415. <option value="co-developed">co-developed</option>
  416. <option value="reference">reference</option>
  417. </select>
  418. <button type="submit">Link</button>
  419. </form>
  420. <form class="inline" stm-action="goal.apply_stack"
  421. stm-if="goal.stack_options.length > 0" style="margin-top: 0.5em;">
  422. <label for="goal-stack">Apply stack</label>
  423. <select id="goal-stack" name="stack_id" required>
  424. <option stm-for-stack-in="goal.stack_options" stm-attribute.value="stack.id"
  425. stm-text="stack.name + ' (' + stack.member_count + ')'">stack</option>
  426. </select>
  427. <button type="submit">Apply</button>
  428. </form>
  429. </details>
  430. </section>
  431. <!-- Plan 14 B5: the goal's effective rules — they govern
  432. the agent and every worker it fans out. -->
  433. <section class="card">
  434. <h3>Rules</h3>
  435. <p class="muted">The effective set dispatched with this goal's work — task rules,
  436. the primary project's, then co-developed projects' in link order.</p>
  437. <div stm-for-rule-in="goal.rules" class="rule-card">
  438. <h4 stm-text="rule.name">name</h4>
  439. <div class="md" stm-markdown="rule.body">body</div>
  440. </div>
  441. <p stm-if="goal.rules.length == 0" class="muted">No rules apply to this goal.</p>
  442. </section>
  443. <!-- The supervision narrative (plan 14 B6): state events
  444. at full weight, comment events as .footnote — the
  445. feed reads as events-with-occasional-footnotes. -->
  446. <section class="card">
  447. <h3>Supervision narrative</h3>
  448. <p stm-if="goal.events.length == 0" class="muted">Nothing yet — start the goal.</p>
  449. <div stm-for-event-in="goal.events"
  450. stm-attribute.class="'goal-event' + (event.kind == 'comment' ? ' footnote' : '')">
  451. <span class="muted" stm-text="event.at_display">when</span>
  452. <span stm-attribute.class="'badge badge-goal-' + event.kind"
  453. stm-text="event.kind.replace('-', ' ')">kind</span>
  454. <!-- Comment events render as footnotes (plan 13 B4
  455. + 14 B6): the whole row is muted and smaller,
  456. the body-first text leading. -->
  457. <div class="md goal-event-text" stm-markdown="event.text">text</div>
  458. <a stm-if="event.task_id > 0"
  459. stm-attribute.href="'/tasks/' + event.task_id">task</a>
  460. </div>
  461. </section>
  462. <!-- Comments with selection-quoting (markdown bodies).
  463. Plan 15 C2: the thread IS the goal-run task's own
  464. task_comments — the agent's pushes and the human's
  465. composer land on the same rows; harness-authored
  466. bookkeeping renders as distinct muted system rows,
  467. never agent comment rows. The composer opens with
  468. the run (a draft goal has no thread to inject into). -->
  469. <section class="card">
  470. <h3>Comments</h3>
  471. <!-- Plan 15 C4: timeless-comment footnote styling —
  472. the BODY leads; the author and time read as a
  473. muted footnote (status belongs in progress
  474. updates). System rows keep their labelled style. -->
  475. <div stm-for-comment-in="goal.comments"
  476. stm-attribute.class="'comment' + (comment.from_agent ? ' agent' : '') + (comment.is_system ? ' system' : '')">
  477. <div class="md" stm-markdown="comment.body">body</div>
  478. <blockquote stm-if="comment.quote.length > 0" stm-text="comment.quote">quote</blockquote>
  479. <p class="comment-meta">
  480. <strong stm-if="!comment.is_system" stm-text="comment.author">author</strong>
  481. <span stm-if="comment.is_system" class="system-label">system</span>
  482. <span class="muted" stm-text="comment.created_display">when</span>
  483. </p>
  484. </div>
  485. <p stm-if="goal.comments.length == 0" class="muted">No comments yet.</p>
  486. <p stm-if="goal.run_task_id == 0" class="muted">The goal agent has not started
  487. yet — comments open with its run.</p>
  488. <form stm-if="goal.run_task_id > 0" stm-action="goal.comment">
  489. <div class="form-row"><label for="goal-comment-body">Add comment</label>
  490. <textarea id="goal-comment-body" name="body" rows="3"
  491. placeholder="A timeless note for a future reader — status belongs in progress updates (markdown)"></textarea></div>
  492. <button type="submit" class="primary">Comment</button>
  493. </form>
  494. <dialog id="quote-comment-dialog">
  495. <h3>Comment on selected text</h3>
  496. <blockquote id="quote-text" class="term-comment-quote"></blockquote>
  497. <form stm-action="goal.comment">
  498. <input type="hidden" name="quote">
  499. <input type="hidden" name="return_to">
  500. <textarea name="body" rows="4" placeholder="Your comment…"
  501. style="width:100%; margin:0.5em 0;"></textarea>
  502. <button type="submit" class="primary">Send comment</button>
  503. <button type="button"
  504. onclick="document.getElementById('quote-comment-dialog').close()">Cancel</button>
  505. </form>
  506. </dialog>
  507. </section>
  508. <!-- Goal agent runs: every round with its host saddle; the
  509. event log links open in a new tab. -->
  510. <section class="card" stm-if="goal.runs.length > 0">
  511. <h3>Goal agent runs</h3>
  512. <p><a stm-attribute.href="'/tasks/' + goal.run_task_id + '/log'"
  513. target="_blank" rel="noopener">Event log</a>
  514. <span class="muted">— run task
  515. #<span stm-text="goal.run_task_id">id</span></span></p>
  516. <table>
  517. <thead><tr><th>Run</th><th>Saddle</th><th>Started</th><th>Ended</th><th>Outcome</th></tr></thead>
  518. <tbody>
  519. <tr stm-for-run-in="goal.runs">
  520. <td><a stm-attribute.href="run.terminal_url" target="_blank" rel="noopener"
  521. stm-text="'session ' + run.id">run</a></td>
  522. <td class="muted" stm-text="run.saddle_name">saddle</td>
  523. <td class="muted" stm-text="run.started_display">when</td>
  524. <td class="muted" stm-text="run.ended_display">when</td>
  525. <td><span stm-if="run.open" class="badge badge-working">open</span>
  526. <span stm-if="!run.open" stm-text="run.outcome">outcome</span></td>
  527. </tr>
  528. </tbody>
  529. </table>
  530. </section>
  531. </div>
  532. </div>
  533. </body>
  534. </html>