Public: anyone can read this; the crew can change it.

Design: a run's summary line and cancel actions use §5's text and bar, and run-summary/run-actions leave §5d #4032

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/3960-design-a-run-s-summary-line-and-cancel-a → main 5 commits fork 2 threads · 2 unresolved

  • Merged. scout/3960-design-a-run-s-summary-line-and-cancel-a was merged into main as 66746d9.

Review

Sign in to review this change.
4 files changed +36 −7
docs/DESIGN.md +1 −1
@@ -396,7 +396,7 @@ gone, so this list only shrinks.
396396 - **`style/content.ts`, code view** — the source view and the syntax-highlight token classes `highlight-html.ts` builds as `tok-${kind}`: `.blob-view` `.ln` `.tok-attribute` `.tok-comment` `.tok-constant` `.tok-decorator` `.tok-deleted` `.tok-emphasis` `.tok-function` `.tok-heading` `.tok-inserted` `.tok-keyword` `.tok-link` `.tok-meta` `.tok-number` `.tok-operator` `.tok-plain` `.tok-property` `.tok-punctuation` `.tok-regex` `.tok-string` `.tok-tag` `.tok-type` `.tok-variable`
397397 - **`style/content.ts`, prose (markdown)** — table cell alignment, built by `markdown.ts` as `ta-${align}`: `.ta-c` `.ta-r`
398398 - **`style/content.ts`, diff viewer** — the diff table: its gutters, line kinds, file head and inline review comments: `.add` `.del` `.dh` `.diff` `.diff-body` `.diff-head` `.diff-note` `.diff-path` `.diff-stat` `.diff-stub` `.diff-summary` `.diff-table` `.dl` `.dl-review` `.dn` `.dn-comment` `.nonl` `.rc-hidden` `.rc-resolve` `.rc-resolved` `.wd`
399 - **`style/content.ts`, apps, runs, and the crew** — run pages, their logs and checks (a pull's look is §5's **look**): `.ln-forge` `.ln-pulse` `.ln-say` `.ln-tool` `.ln-you` `.log-tail-row` `.pinned-failures` `.pinned-slot` `.run-actions` `.run-jump` `.run-live` `.run-steer` `.run-summary` `.tests-bar` `.tests-words` `.txt` `.who`
399 - **`style/content.ts`, apps, runs, and the crew** — run pages, their logs and checks (a pull's look is §5's **look**): `.ln-forge` `.ln-pulse` `.ln-say` `.ln-tool` `.ln-you` `.log-tail-row` `.pinned-failures` `.pinned-slot` `.run-jump` `.run-live` `.run-steer` `.tests-bar` `.tests-words` `.txt` `.who`
400400 - **`style/content.ts`, search results** — search results, the inbox's ask cards, the client list and the claim link: `.ask-acts` `.ask-card` `.ask-group` `.ask-kind` `.ask-meta` `.ask-more-acts` `.ask-pick` `.ask-pick-hit` `.ask-short` `.ask-waiting` `.claim-link-box` `.forges-sent` `.live-toggle` `.needs-you` `.no-hits`
401401 - **`pages/board.ts`** — the board itself is §5's **board**; the spend tables `spend.ts` draws wear §5's `.table` and `.stack`, no class of their own
402402 - **`pages/map.ts`** — the map of what waits for what: `.map-ask` `.map-breath` `.map-canvas` `.map-card` `.map-counts` `.map-dot` `.map-edge` `.map-edge-crit` `.map-edge-done` `.map-empty` `.map-list` `.map-main` `.map-meta` `.map-node` `.map-pillars` `.map-rule` `.map-svg` `.map-title` `.map-tool` `.map-tools` `.map-who` `.map-who-i` `.map-who-n` `.map-word` `.map-wrap`
src/web/studio/run.ts +3 −3
@@ -196,7 +196,7 @@ export async function runView(
196196 >`,
197197 )}
198198 </div>
199 <p class="run-summary" id="run-summary">${run.summary}</p>
199 <p class="mt" id="run-summary">${run.summary}</p>
200200 ${when(
201201 carried.length > 0,
202202 () =>
@@ -235,7 +235,7 @@ export async function runView(
235235 ${when(
outdated

Bottom margin of the old .run-actions (12px) is gone; if the block after this feels tight on a 360px phone, add class mb here and on the Cancel run block.

236236 writer && unfinished && run.kind === "agent" && issue === undefined,
237237 () =>
238 html`<div class="run-actions" id="run-cancel">
238 html`<div class="bar mt" id="run-cancel">
239239 <form method="post" action="${base}/runs/${run.id}/cancel">
240240 ${btn("Stop", { size: "sm", variant: "outline", icon: "square" })}
241241 </form>
@@ -244,7 +244,7 @@ export async function runView(
244244 ${when(
245245 writer && unfinished && run.kind !== "agent",
246246 () =>
247 html`<div class="run-actions" id="run-cancel">
247 html`<div class="bar mt" id="run-cancel">
248248 <form method="post" action="${base}/runs/${run.id}/cancel">
249249 ${btn("Cancel run", {
250250 size: "sm",
src/web/style/content.ts −3
@@ -837,9 +837,6 @@ form.inline { display: inline; margin-left: auto; }
837837 .run-steer textarea { min-height: 38px; max-height: 160px; resize: none; }
838838 .run-steer .bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
839839 .run-steer .bar .muted { flex: 1 1 200px; }
840 .run-summary { margin: 10px 0 0; text-wrap: pretty; }
841 .run-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 12px 0; }
842 .run-actions form { display: inline-flex; }
843840 /* A tail beside a running row (studio/app.ts): the same log, a peek at it
844841 — fixed height so nothing around it shifts as lines arrive, and it
845842 scrolls itself rather than the page. */
test/studio-runs.test.ts +32
@@ -521,6 +521,38 @@ describe("working: a run's own page in the studio", () => {
521521 // The transcript is on the page, and there is no second reading of it.
522522 expect(page).toContain('id="run-log"');
523523 expect(page).not.toContain("?mode=dev");
524 // The summary is a plain paragraph, not a page-part class (#3960).
525 expect(page).toMatch(/<p class="mt" id="run-summary">/);
526 expect(page).not.toContain('class="run-summary"');
527 expect(page).not.toContain("run-actions");

This absence check cannot fail for the cancel blocks, because this run never renders them. Add the cancel-block cases: an unfinished agent run with no issue, and a non-agent run, each asserting class="bar mt" id="run-cancel" around the cancel form.

528 });
529
530 test("the cancel form sits in a .bar.mt row for an issue-less agent run and a non-agent run", async () => {
531 const agent = createRun(app.db, {
532 repoId: repo.id,
533 kind: "agent",
534 actorId: scoutId,
535 reason: "assigned",
536 });
537 claimRun(app.db, agent.id);
538 const check = createRun(app.db, {
539 repoId: repo.id,
540 kind: "check",
541 reason: "push",
542 });
543 for (const [run, label] of [
544 [agent, "Stop"],
545 [check, "Cancel run"],
546 ] as const) {
547 const page = await (
548 await get(`/alice/engine/runs/${run.id}`, writerCookie)
549 ).text();
550 expect(page).toMatch(
551 new RegExp(
552 `<div class="bar mt" id="run-cancel">\\s*<form method="post" action="/alice/engine/runs/${run.id}/cancel">[\\s\\S]*?${label}[\\s\\S]*?</form>`,
553 ),
554 );
555 }
524556 });
525557
526558 test("a running run's page reads its log from the one /events stream and opens no other", async () => {

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs