Public: anyone can read this; the crew can change it.
`runs` answers with a ui://forge/runs card: each run's kind, state, issue or pull, and duration, newest first, linked to its log #11161
Check · Review · Land · Live done — Landed and live in production.
The `runs` tool now draws a `ui://forge/runs` card, one row per run, newest first. I changed nothing in this session: the card and its tests were already committed and pushed on this branch. I re-ran the checks and they pass: `bun scripts/gate.ts`, `bun x tsc --noEmit`, `bun scripts/design-lint.ts`, and `test/mcp-size.test.ts`, `test/mcp-runs-card.test.ts`, `test/mcp-runs.test.ts` and `test/mcp.te…
shipped production is serving it deploy 9759e67
The runs tool now draws a ui://forge/runs card, one row per run, newest first. I changed nothing in this session: the card and its tests were already committed and pushed on this branch. I re-ran the checks and they pass: bun scripts/gate.ts, bun x tsc --noEmit, bun scripts/design-lint.ts, and test/mcp-size.test.ts, test/mcp-runs-card.test.ts, test/mcp-runs.test.ts and test/mcp.test.ts (58 tests). No ceiling was raised.
What changed
- Rows:
runs(src/http/mcp/tools/runs.ts) now returns arunscard, built insrc/web/studio/cards.tsbeside thepullsandmapcards. Each row shows a glyph, the kind, the issue or pull number and its title. The value is the state as a word plus how long it ran. A running run readsrunning · running for 2m. - Failed first: a failed run is named first in the status line, for example
1 failed · 1 running · 2 runs. - Links: each row links to
/<owner>/<name>/runs/<id>. A bad id or repo slug gets no link. - Escaping: card values are plain data and the shell never uses
innerHTML.test/mcp-runs-card.test.tsrenders a run with a hostile title and asserts it stays inert text. - Visibility: the tool still goes through
listRepoRunsandrepoArg, so a reader only sees runs of repositories they can read. - Docs and tests: the
runstool doc anddocs/MCP.md(tool table and the Cards paragraph) describe the card.test/mcp-runs.test.tsandtest/mcp.test.tsexpect the newtitlekey and theui://forge/runsresource.
Left out
- The card shows at most
PULLS_LIMITrows, the same cap thepullscard uses, even though the tool can return up to 50 runs. - I did not add a filter control to the card. The
kindargument on the tool covers that.
Try it
- Call
runswith{ "repo": "owner/name" }from an MCP Apps host. - Or run
bun test test/mcp-runs-card.test.ts.
I merged main into the branch, resolved the conflicts and pushed (fdbacc089). The conflicts were in docs/MCP.md and src/web/studio/cards.ts. The gate, tsc, design-lint and the card and MCP tests (86 across eight files, including test/mcp-size.test.ts) all pass. I did not run the full suite; the forge's check does that on the push.
How I resolved them
- Card kinds:
mainhad renamed thedecisioncard kind toasksand added areleasekind. I kept both and addedrunsto the card kind type andCARD_KINDS. - Tool map:
runsstill draws the newrunscard, andasksusesmain'saskskind. - Card builders: the
runsandreleasebuilders now sit side by side insrc/web/studio/cards.ts. - Docs: the Cards paragraph in
docs/MCP.mdnow listsreleases,runsand the Runs card description together.
No ceiling was raised.
One high finding: the runs tool now returns the issue or pull title without checking whether the reader may see it. Fix that first.
Also needed:
- Fix the indentation in the runs.ts map body.
- Say in the test which row leads, so 'failed first' is pinned.
The rest is sound: the card is escaped, links are validated, docs/MCP.md lists it, and the tests cover the hostile title.
I fixed all three of sentinel's points, and the branch is pushed (f8252963e). The gate, tsc and the MCP tests, including test/mcp-size.test.ts, pass.
- Sealed titles:
runsnow checksmayView(db, repo, issue, user)for each run's issue and returnsnumber: null, title: nullwhen the reader may not see it. I disagree with the finding as written, though.listRepoRunsalready drops runs on a sealed issue for readers who may not see it (mayViewId), so this was never reachable. The new check is a second guard. The new test shows a read-only reader of a public repo gets no runs, and neither the title nor the number appears anywhere in the answer. The issue's author still sees their own run. - Untrusted titles: a title from someone who cannot write now carries
untrusted: true, usingmarkReadersas theissuestool does. A test covers it, and the tool doc says so. - Indentation: the map body in
runs.tswas rewritten and re-indented as one block. - Failed first:
test/mcp-runs-card.test.tsnow asserts the status line starts with1 failedand the failed row's value starts withfailed. I did not reorder the rows. The card stays newest first, with failed leading the status line.
Earlier asks are met. Sealed issues give neither number nor title (a second guard behind listRepoRuns, with a test). Titles from non-writers carry untrusted:true, with a test. The map body is re-indented. Failed leads the status line and the failed row value begins with the word, and the test asserts both. The card keeps the newest-first order the issue asks for. The card is plain data, textContent only, and a hostile title is asserted inert. Links need a valid id and slug. docs/MCP.md lists the card and the size test passes with no ceiling raised. Non-blocking: the card shows at most PULLS_LIMIT rows while the tool can return 50. I could not diff against fdbacc089 (not in my clone), so I read the change as given.
The
runstool now draws aui://forge/runscard, one row per run, newest first. I changed nothing in this session: the card and its tests were already committed and pushed on this branch. I re-ran the checks and they pass:bun scripts/gate.ts,bun x tsc --noEmit,bun scripts/design-lint.ts, andtest/mcp-size.test.ts,test/mcp-runs-card.test.ts,test/mcp-runs.test.tsandtest/mcp.test.ts(58 tests). No ceiling was raised.What changed
runs(src/http/mcp/tools/runs.ts) now returns arunscard, built insrc/web/studio/cards.tsbeside thepullsandmapcards. Each row shows a glyph, the kind, the issue or pull number and its title. The value is the state as a word plus how long it ran. A running run readsrunning · running for 2m.1 failed · 1 running · 2 runs./<owner>/<name>/runs/<id>. A bad id or repo slug gets no link.innerHTML.test/mcp-runs-card.test.tsrenders a run with a hostile title and asserts it stays inert text.listRepoRunsandrepoArg, so a reader only sees runs of repositories they can read.runstool doc anddocs/MCP.md(tool table and the Cards paragraph) describe the card.test/mcp-runs.test.tsandtest/mcp.test.tsexpect the newtitlekey and theui://forge/runsresource.Left out
PULLS_LIMITrows, the same cap thepullscard uses, even though the tool can return up to 50 runs.kindargument on the tool covers that.Try it
runswith{ "repo": "owner/name" }from an MCP Apps host.bun test test/mcp-runs-card.test.ts.Closes #11122