toolroll 0.9.73 → 0.9.75

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 (145) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/THIRD_PARTY_NOTICES.md +40 -0
  3. package/dist/agent-turns.js +5 -0
  4. package/dist/agents-live.d.ts +31 -0
  5. package/dist/agents-live.js +43 -0
  6. package/dist/assignment.d.ts +3 -0
  7. package/dist/assignment.js +27 -8
  8. package/dist/browser/THIRD_PARTY_NOTICES.txt +40 -0
  9. package/dist/browser/workspace.css +2 -2
  10. package/dist/browser/workspace.js +31 -30
  11. package/dist/browser-workspace.d.ts +7 -1
  12. package/dist/builder.js +1 -1
  13. package/dist/chat-acceptance.d.ts +4 -1
  14. package/dist/chat-acceptance.js +13 -3
  15. package/dist/chat-continuity.js +10 -0
  16. package/dist/chat-quiet.js +3 -3
  17. package/dist/cli.js +1 -1
  18. package/dist/coding-handoff.js +4 -3
  19. package/dist/coding-ui.js +2 -2
  20. package/dist/coding-workspace.js +16 -4
  21. package/dist/contracts/console-api.d.ts +5 -1
  22. package/dist/contracts/console-api.js +10 -0
  23. package/dist/control-ui.d.ts +3 -2
  24. package/dist/control-ui.js +7 -2
  25. package/dist/demo.js +5 -3
  26. package/dist/deploy-gate.js +5 -2
  27. package/dist/desktop-update.d.ts +4 -1
  28. package/dist/desktop-update.js +43 -9
  29. package/dist/dispose.js +35 -2
  30. package/dist/exec.js +13 -16
  31. package/dist/first-run.d.ts +20 -1
  32. package/dist/first-run.js +71 -0
  33. package/dist/flow-card-task.js +3 -2
  34. package/dist/flow-unattended.d.ts +2 -0
  35. package/dist/flow-unattended.js +12 -1
  36. package/dist/integrations.d.ts +4 -3
  37. package/dist/integrations.js +5 -6
  38. package/dist/invoke.d.ts +13 -0
  39. package/dist/invoke.js +21 -12
  40. package/dist/knowledge-ui.d.ts +4 -1
  41. package/dist/knowledge-ui.js +10 -5
  42. package/dist/lead-contract.d.ts +1 -1
  43. package/dist/lead-contract.js +2 -2
  44. package/dist/lead-follow.js +3 -1
  45. package/dist/lead-progress.d.ts +4 -1
  46. package/dist/lead-progress.js +3 -1
  47. package/dist/lead.js +4 -0
  48. package/dist/live-bus.d.ts +2 -0
  49. package/dist/live-bus.js +1 -0
  50. package/dist/live-children-read.d.ts +52 -0
  51. package/dist/live-children-read.js +333 -0
  52. package/dist/live-children.d.ts +73 -0
  53. package/dist/live-children.js +318 -0
  54. package/dist/live-detail.d.ts +4 -3
  55. package/dist/live-detail.js +9 -5
  56. package/dist/live-input.js +7 -8
  57. package/dist/live-main-read.d.ts +19 -0
  58. package/dist/live-main-read.js +91 -0
  59. package/dist/live-open.d.ts +28 -0
  60. package/dist/live-open.js +125 -0
  61. package/dist/live-records.d.ts +10 -1
  62. package/dist/live-records.js +16 -5
  63. package/dist/live.d.ts +54 -22
  64. package/dist/live.js +218 -69
  65. package/dist/openrouter-models.d.ts +5 -2
  66. package/dist/openrouter-models.js +7 -4
  67. package/dist/operate.js +5 -2
  68. package/dist/peek-cli.d.ts +10 -3
  69. package/dist/peek-cli.js +17 -8
  70. package/dist/planner.js +1 -1
  71. package/dist/project-learning.js +44 -6
  72. package/dist/project-tools.js +19 -1
  73. package/dist/provider-limits.d.ts +3 -1
  74. package/dist/provider-limits.js +5 -2
  75. package/dist/provider.js +6 -12
  76. package/dist/pull-request-ui.d.ts +1 -1
  77. package/dist/pull-request-ui.js +5 -1
  78. package/dist/push.d.ts +2 -0
  79. package/dist/push.js +24 -19
  80. package/dist/read-only-request.d.ts +4 -0
  81. package/dist/read-only-request.js +15 -0
  82. package/dist/release-adapters.js +4 -0
  83. package/dist/release.d.ts +3 -0
  84. package/dist/release.js +3 -0
  85. package/dist/repository-context-ui.js +2 -1
  86. package/dist/repository-context.js +6 -1
  87. package/dist/result-follow-ups.js +20 -1
  88. package/dist/result-review.js +5 -1
  89. package/dist/run-efficiency.d.ts +3 -3
  90. package/dist/run-efficiency.js +20 -8
  91. package/dist/scout.d.ts +3 -0
  92. package/dist/scout.js +5 -3
  93. package/dist/serve.js +196 -29
  94. package/dist/server/agents-live-filed.d.ts +17 -0
  95. package/dist/server/agents-live-filed.js +97 -0
  96. package/dist/server/agents-live-lead.d.ts +47 -0
  97. package/dist/server/agents-live-lead.js +116 -0
  98. package/dist/server/agents-live-room.d.ts +23 -0
  99. package/dist/server/agents-live-room.js +91 -0
  100. package/dist/server/chat.js +115 -53
  101. package/dist/server/chrome.js +2 -2
  102. package/dist/server/console.css +68 -40
  103. package/dist/server/http.d.ts +5 -0
  104. package/dist/server/last-checks.d.ts +17 -0
  105. package/dist/server/last-checks.js +22 -0
  106. package/dist/server/live.d.ts +4 -0
  107. package/dist/server/live.js +42 -2
  108. package/dist/server/pages.js +98 -4
  109. package/dist/server/people-tokens.js +7 -4
  110. package/dist/server/render-chat.d.ts +15 -1
  111. package/dist/server/render-chat.js +14 -7
  112. package/dist/server/render-pages.d.ts +10 -5
  113. package/dist/server/render-pages.js +83 -74
  114. package/dist/server/render-results.d.ts +11 -2
  115. package/dist/server/render-results.js +54 -12
  116. package/dist/server/render-settings.js +3 -3
  117. package/dist/server/render-tasks.js +13 -3
  118. package/dist/server/route-table.js +19 -2
  119. package/dist/server/runtime.d.ts +17 -2
  120. package/dist/server/settings.js +85 -32
  121. package/dist/server/task-views.js +1 -2
  122. package/dist/server/tasks.js +70 -43
  123. package/dist/storage-ui.d.ts +2 -0
  124. package/dist/storage-ui.js +1 -1
  125. package/dist/storage.js +2 -1
  126. package/dist/store.d.ts +34 -1
  127. package/dist/store.js +108 -11
  128. package/dist/stream-scope.d.ts +18 -0
  129. package/dist/stream-scope.js +28 -0
  130. package/dist/subagent-work.js +3 -0
  131. package/dist/subscription-chat.js +5 -3
  132. package/dist/task-status.d.ts +28 -1
  133. package/dist/task-status.js +93 -11
  134. package/dist/teardown-git.d.ts +27 -1
  135. package/dist/teardown-git.js +185 -39
  136. package/dist/telegram-progress.js +4 -4
  137. package/dist/telegram-status.js +8 -3
  138. package/dist/tool-secret-store.js +8 -0
  139. package/dist/toolroll-update-ui.d.ts +2 -0
  140. package/dist/toolroll-update-ui.js +4 -3
  141. package/dist/toolroll-update.js +10 -9
  142. package/dist/work-index.js +29 -12
  143. package/dist/workspace-ui.d.ts +5 -0
  144. package/dist/workspace-ui.js +38 -10
  145. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,68 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.75 — 2026-10-27
4
+
5
+ - **Opening a page only shows it.** Pages no longer run checks, tidy up or
6
+ call out as you open them. Sign-ins, integrations, update checks, storage
7
+ and Codex's tools are checked in the background, and Models, Updates,
8
+ Storage, Pull requests, Tools, Project setup and the GitHub listing show
9
+ their last answer and when it was checked, with a **Check now** button to
10
+ ask again. On a coding session, **Review for shipping** and the changes
11
+ view are buttons, so a link preview or prefetch can't run them. Reading a
12
+ checkout an agent wrote never runs that checkout's own git settings.
13
+ - **Reading an agent's checkout can't be raced into running a filter.** Git
14
+ reads the checkout through a private copy of its settings taken once, so a
15
+ filter the agent adds to its git config and `.git/info/attributes` after
16
+ that copy never runs.
17
+ - **The task card never contradicts itself.** One answer to "did the
18
+ approved check pass" across the task page, the road, the Tasks list, the
19
+ result page and chat. A check whose saved log can't be read is never
20
+ shown as passed, and a check whose command was approved again since it ran
21
+ says so: "The check command changed since this check; run checks again."
22
+ - **Lead-filed runs nest under the lead on Agents → Live.** Runs your own
23
+ lead filed sit under its tile. Once its turn has gone, they stay grouped
24
+ under a quiet "Filed by lead chat" header. The lead's full screen lists
25
+ every task it filed. Only you see what your lead filed.
26
+ - **The lead passes asks to a helper instead of answering for them.** "Ask
27
+ Maya to …" now reaches Maya even when she seems to lack a tool or the
28
+ knowledge for it; she says so herself or asks you first.
29
+ - **A helper's "can't" reaches whoever asked.** When a helper can't do
30
+ something asked at its desk, its note (or its reason) goes back to the
31
+ person or agent who asked, instead of waiting in an inbox.
32
+ - Database: schema v136 (unchanged).
33
+
34
+ ## 0.9.74 — 2026-10-27
35
+
36
+ - **Agents → Live: every agent and subagent at a glance.** A read-only grid
37
+ of everything running, on desktop and phone: what each agent is doing
38
+ right now, how long it has run and its latest lines. Subagents get their
39
+ own tiles nested under their parent; they appear when they start and show
40
+ Done for 30 seconds before leaving. A quiet agent reads **Needs you** with
41
+ a thin amber outline. Click a tile's title to open it full screen (Escape
42
+ brings you back). Your own lead chats show as lead tiles. Nothing on the
43
+ page acts, and the grid holds still while you look at it. Terminals you
44
+ drive yourself stay off the grid; one line links to Live terminals. Only a
45
+ signed-in browser can read it: tokens are refused.
46
+ - **Visiting a page starts nothing.** Opening Chat, a task, a result or a
47
+ project shows the page with a ready composer; your first message starts
48
+ the conversation. A retry or double submit lands in the conversation it
49
+ started. Link previews and prefetches can't start one.
50
+ - **A cleaner console.** One status chip everywhere, framed panels on the
51
+ Board, Settings, Knowledge and Projects, and a road on every task (Plan →
52
+ You approve → Build → Checks → Ready) built only from what happened.
53
+ Attempts show as a timeline and warnings as plain notices. Amber now means
54
+ only "waits on you". Built from ReUI's free, MIT-licensed components
55
+ (credited in THIRD_PARTY_NOTICES.md).
56
+ - **The old task list is retired.** `/tasks` now opens Work, which gained a
57
+ state filter (`/work?state=`), templates (`/tasks/new?template=`) and keeps
58
+ your draft when a filing is refused.
59
+ - **Rerunning a failed release check keeps your approval.**
60
+ `toolroll task regate` queues the same approved commit again instead of
61
+ re-filing it, and `toolroll release` waits for the rerun and uses it. A
62
+ changed commit, scope or approval still refuses.
63
+ - Database: schema v136 (each run's "Show commands" answer is kept for the
64
+ live view).
65
+
3
66
  ## 0.9.73 — 2026-10-25
4
67
 
5
68
  - **Carry release approvals when checks aren't sandboxed.** A project whose
@@ -321,6 +321,46 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
321
321
  SOFTWARE.
322
322
 
323
323
 
324
+ ## ReUI — MIT
325
+
326
+ Components in `src/browser/components/ui/` adapt free, MIT-licensed ReUI
327
+ primitives and examples (the Radix base) from:
328
+ https://github.com/keenthemes/reui/tree/067aa0d/registry-reui/bases/radix/reui
329
+
330
+ Source inspected 27 October 2026, revision `067aa0d`. Files copied by hand (the
331
+ shadcn CLI and ReUI's registry CSS variables are not used) and cut to Toolroll's
332
+ tokens:
333
+
334
+ - `badge.tsx` — the light badge and its status-dot example (`c-badge-22`): one
335
+ 20px chip size and a decorative dot on the existing tones, plus a "Needs you"
336
+ tone. The style variants, solid fills and focus/invert tones are omitted.
337
+ - `frame.tsx` — the well of panels: one geometry (a raised well, hairline,
338
+ concentric panel corners) with the style variants, spacing scale, stacking
339
+ modes and panel shadows omitted; `FramePanel` can render as its child.
340
+ - `alert.tsx` — cut to a neutral and a destructive tone with an icon column;
341
+ the info, warning, success and invert tones, the title/action slots and the
342
+ style variants are omitted.
343
+ - `timeline.tsx` — cut to a read-only vertical list (item, indicator on a
344
+ hairline rail, content, date); the active-step context, horizontal mode and
345
+ styled indicators are omitted.
346
+ - `stepper.tsx` — its read-only horizontal layout (indicator, separator,
347
+ title, `aria-current="step"`) is followed by the task page's road
348
+ (`src/browser/first-run.tsx` `Journey`); no stepper source is copied, and
349
+ the controlled state, keyboard navigation and triggers are omitted.
350
+
351
+ No ReUI Pro blocks, icons or templates are included.
352
+
353
+ MIT License
354
+
355
+ Copyright (c) 2025 Keenthemes Inc
356
+
357
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
358
+
359
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
360
+
361
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
362
+
363
+
324
364
  ## Integration logos: Simple Icons (CC0 1.0) and svgl (MIT)
325
365
 
326
366
  The integration logos in `src/brand-icons.ts` are generated by
@@ -1,6 +1,11 @@
1
+ import { topLevelEvent } from "./stream-scope.js";
1
2
  /** What one stream event says about the turn; null when it says nothing. */
2
3
  export function turnSignalOf(event) {
3
4
  const type = typeof event["type"] === "string" ? event["type"] : "";
5
+ // A subagent's events are not the parent's turn: its result never marks the parent idle or clears its open tools,
6
+ // and its init or words never mark it busy (stream-scope.ts).
7
+ if (!topLevelEvent(event))
8
+ return null;
4
9
  switch (type) {
5
10
  // Claude stream-json: the session starts, the assistant speaks or calls a tool, a tool answers; then one result.
6
11
  case "system": return event["subtype"] === "init" ? "busy" : null;
@@ -0,0 +1,31 @@
1
+ import { type AgentsLiveRun, type Store } from "./store.js";
2
+ /** An ended agent stays on the grid this long, then leaves (it remains on the List). */
3
+ export declare const LIVE_ENDED_KEEP_MS: number;
4
+ export type AgentLiveState = "working" | "needs-you" | "done" | "failed" | "stopped" | "parked";
5
+ export type AgentLiveTile = {
6
+ runId: number;
7
+ role: AgentsLiveRun["role"];
8
+ taskId: string;
9
+ title: string;
10
+ project: string | null;
11
+ agent: string;
12
+ model: string | null;
13
+ startedAt: string;
14
+ endedAt: string | null;
15
+ state: AgentLiveState;
16
+ /** Why it needs you: it has been quiet (the board's Stuck rule) or it asked a question. */
17
+ why: "quiet" | "question" | null;
18
+ /** Since when it has been quiet, when that is why. */
19
+ quietSince: string | null;
20
+ /** Its output is on this computer (another runner's can't be read here). */
21
+ local: boolean;
22
+ /** A repair's build: the tile it nests under. */
23
+ parentRun: number | null;
24
+ /** The task's own page, for a task this console keeps (an outside tracker's task has none here). */
25
+ taskHref: string | null;
26
+ };
27
+ /** The grid's tiles for one viewer: `canSee` is the caller's proof (ceiling and account access) for each run. */
28
+ export declare function agentsLiveOf(store: Store, now: Date, viewer: {
29
+ canSee: (run: AgentsLiveRun) => boolean;
30
+ localRunner: string | undefined;
31
+ }): AgentLiveTile[];
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The Agents view's Live grid (docs/plans/agents-view.md §1, §2.5): which agents are running now (every role) and
3
+ * which ended in the last ten minutes, with the facts a tile shows. Store facts only — it verifies nothing, grants
4
+ * nothing, and reads no live file; visibility is proved by the caller's `canSee` for each run.
5
+ */
6
+ import { attemptKeyOf, agentNowForRun, openAgentQuestions } from "./agent-board.js";
7
+ import { agentsLiveRuns } from "./store.js";
8
+ /** An ended agent stays on the grid this long, then leaves (it remains on the List). */
9
+ export const LIVE_ENDED_KEEP_MS = 10 * 60_000;
10
+ const stateOf = (outcome) => outcome === null ? "working" : outcome === "built" || outcome === "no-change" ? "done" : outcome === "parked" ? "parked" : outcome === "interrupted" ? "stopped" : "failed";
11
+ /** The grid's tiles for one viewer: `canSee` is the caller's proof (ceiling and account access) for each run. */
12
+ export function agentsLiveOf(store, now, viewer) {
13
+ const tiles = [];
14
+ for (const run of agentsLiveRuns(store, new Date(now.getTime() - LIVE_ENDED_KEEP_MS))) {
15
+ if (!viewer.canSee(run))
16
+ continue;
17
+ let state = stateOf(run.outcome);
18
+ let why = null, quietSince = null;
19
+ if (state === "working") {
20
+ try {
21
+ const key = attemptKeyOf(store, run.id);
22
+ const facts = key === null ? null : agentNowForRun(store, run.id, key);
23
+ if (run.role === "builder" && openAgentQuestions(store, run.taskRef).length > 0)
24
+ why = "question";
25
+ else if (facts !== null && facts.stuckAt !== null) {
26
+ why = "quiet";
27
+ quietSince = facts.stuckAt;
28
+ }
29
+ }
30
+ catch { /* display state: no word yet */ }
31
+ if (why !== null)
32
+ state = "needs-you";
33
+ }
34
+ tiles.push({
35
+ runId: run.id, role: run.role, taskId: run.taskId, title: run.title, project: run.repo, agent: run.provider, model: run.model,
36
+ startedAt: run.startedAt, endedAt: run.finishedAt, state, why, quietSince,
37
+ local: viewer.localRunner !== undefined && run.runner === viewer.localRunner,
38
+ parentRun: run.role === "repair" ? run.parentRun : null,
39
+ taskHref: run.builtIn ? `/t/${encodeURIComponent(run.taskId)}` : null,
40
+ });
41
+ }
42
+ return tiles;
43
+ }
@@ -28,6 +28,9 @@ export type AssignmentChecks = {
28
28
  batch?: ChecksBatch | null;
29
29
  /** Checks Off: the passing full release check that contains this commit (release-coverage.ts), read now, never sealed. */
30
30
  release?: ReleaseCoverage | null;
31
+ /** Why a saved check can't be read, when it is the project's check command approved again since it ran (its files
32
+ * are intact; running checks again settles it). Presentation only, never sealed. */
33
+ cause?: "command-changed" | null;
31
34
  };
32
35
  export type AssignmentReceipt = {
33
36
  digest: string;
@@ -21,7 +21,7 @@ import { requiredCheckCommandFor, runCheckLevel } from "./check-levels.js";
21
21
  import { followUpChecksOf, withFollowUps } from "./result-follow-ups.js";
22
22
  import { buildReviewOf, findingWords } from "./review-switch.js";
23
23
  import { NEEDS, WAITS, processNeedOf, resultHoldUpSentence } from "./needs-you.js";
24
- import { assignmentStageOf, checkBackingOf, completionBlockersOf, requirementWordOf } from "./task-status.js";
24
+ import { assignmentStageOf, checkBackingOf, completionBlockersOf, receiptChecksOf, requirementWordOf } from "./task-status.js";
25
25
  import { releaseCoverageOf } from "./release-coverage.js";
26
26
  import { leadClaimOf } from "./lead-voice.js";
27
27
  /** Status-first handoff for routine reads. Fetch get_assignment only when
@@ -53,7 +53,7 @@ function olderStoreSafe(read, absent) {
53
53
  }
54
54
  }
55
55
  /** A check as the receipt digest seals it: presentation-only fields (check-levels.ts) left out. */
56
- const sealedChecks = ({ level: _level, running: _running, batch: _batch, release: _release, ...proof }) => proof;
56
+ const sealedChecks = ({ level: _level, running: _running, batch: _batch, release: _release, cause: _cause, ...proof }) => proof;
57
57
  const OWNER_ACTION = "assignment claimed";
58
58
  const CHECK_ACTION = COMPLETION_ACTION;
59
59
  const actorOf = (owner) => `${owner.kind}:${owner.id}`;
@@ -87,7 +87,7 @@ function ownerOf(store, rootId, repo) {
87
87
  // Report the recorded check outcome, never turn a model verdict into a check.
88
88
  // Follow-up checks on the same commit (Run checks) upgrade a pass and show a failure.
89
89
  export function assignmentChecksForRun(store, root, runId, now) {
90
- const own = buildChecks(store, root, runId);
90
+ const own = recordedFailureStands(store, runId, buildChecks(store, root, runId));
91
91
  const run = store.getRun(runId);
92
92
  const followUps = followUpChecksOf(store, runId, now, root);
93
93
  if (followUps.length === 0)
@@ -106,6 +106,18 @@ export function assignmentChecksForRun(store, root, runId, now) {
106
106
  ...(changed ? { status: read.status, level: read.level, exitCode: read.exitCode, logArtifactId: latest?.logArtifactId ?? own.logArtifactId,
107
107
  detail: read.status === "passed" ? `${words} passed.` : `${words} failed (exit ${read.exitCode}).` } : {}) };
108
108
  }
109
+ /** A failure the builder recorded (run_check) stands when its saved receipt can't be read now or says nothing ran:
110
+ * fail closed, every surface reads the same known failure and its exit code (task-status.ts approvedCheckOf). */
111
+ function recordedFailureStands(store, runId, own) {
112
+ if (own.status !== "unavailable" && own.status !== "not-run")
113
+ return own;
114
+ const recorded = olderStoreSafe(() => store.runCheckFor(runId), null);
115
+ if (recorded?.status !== "failed")
116
+ return own;
117
+ return { ...own, status: "failed", exitCode: recorded.exitCode,
118
+ detail: `${own.level === "quick" ? "Quick checks" : "Checks"} failed${recorded.exitCode === null ? "" : ` (exit ${recorded.exitCode})`}. ${own.detail}` };
119
+ }
120
+ const COMMAND_MISSING_OR_CHANGED = "The original approved verification command is missing or changed.";
109
121
  function buildChecks(store, root, runId) {
110
122
  const level = runCheckLevel(store, runId);
111
123
  const unavailable = (detail) => ({ status: "unavailable", exitCode: null, command: null, logArtifactId: null, detail, level });
@@ -113,8 +125,13 @@ function buildChecks(store, root, runId) {
113
125
  return unavailable("Saved checks are unavailable.");
114
126
  try {
115
127
  const gate = verificationEvidence(store, root, runId);
116
- if (!gate.ok)
117
- return unavailable(gate.problem);
128
+ if (!gate.ok) {
129
+ // The approved command changed since this check ran: nothing is missing, the check just no longer binds to it.
130
+ const run = store.getRun(runId), repo = run === null ? null : store.refById(run.taskRef)?.repo ?? null;
131
+ const live = repo === null ? null : store.liveVerifyCommand(repo);
132
+ const changed = gate.problem === COMMAND_MISSING_OR_CHANGED && live !== null && run !== null && live.approvedAt > run.startedAt;
133
+ return changed ? { ...unavailable(gate.problem), cause: "command-changed" } : unavailable(gate.problem);
134
+ }
118
135
  const receipt = gate.bytes === null ? null : JSON.parse(gate.bytes);
119
136
  if (receipt === null)
120
137
  return { ...unavailable(level === "off" ? "Checks were off for this build." : "No machine check is recorded."), status: "not-run" };
@@ -390,10 +407,12 @@ function readinessOf(store, receipt, repo, review) {
390
407
  const high = review === null || review.sentBackAs !== null || review.state !== "reviewed" ? 0 : review.high.length;
391
408
  const checkRequired = olderStoreSafe(() => requiredCheckCommandFor(store, repo, receipt.taskId, receipt.checks.level), null) !== null;
392
409
  const matrix = receipt.proof?.matrix ?? [];
393
- const backing = checkBackingOf(receipt.checks);
394
- const verdict = shownVerdictOf(receipt.proof?.verdict ?? null, { checkFailed: !report && receipt.checks.status === "failed", highFindings: high,
410
+ // The approved check as every surface reads it: a recorded pass the saved receipt can't show doesn't count.
411
+ const checks = receiptChecksOf(receipt);
412
+ const backing = checkBackingOf(checks);
413
+ const verdict = shownVerdictOf(receipt.proof?.verdict ?? null, { checkFailed: !report && checks?.status === "failed", highFindings: high,
395
414
  reviewPending: review?.state === "pending", criteriaPassed: matrix.every(row => requirementWordOf(row, null, backing) === "Met") });
396
- const blockers = completionBlockersOf({ report, checks: receipt.checks, checkRequired, matrix, verdict: receipt.proof?.verdict ?? null,
415
+ const blockers = completionBlockersOf({ report, checks, checkRequired, matrix, verdict: receipt.proof?.verdict ?? null,
397
416
  accepted: receipt.proofAcceptance !== null, high });
398
417
  return { verdict, checksOff: !report && receipt.checks.level === "off", blockers };
399
418
  }
@@ -321,6 +321,46 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
321
321
  SOFTWARE.
322
322
 
323
323
 
324
+ ## ReUI — MIT
325
+
326
+ Components in `src/browser/components/ui/` adapt free, MIT-licensed ReUI
327
+ primitives and examples (the Radix base) from:
328
+ https://github.com/keenthemes/reui/tree/067aa0d/registry-reui/bases/radix/reui
329
+
330
+ Source inspected 27 October 2026, revision `067aa0d`. Files copied by hand (the
331
+ shadcn CLI and ReUI's registry CSS variables are not used) and cut to Toolroll's
332
+ tokens:
333
+
334
+ - `badge.tsx` — the light badge and its status-dot example (`c-badge-22`): one
335
+ 20px chip size and a decorative dot on the existing tones, plus a "Needs you"
336
+ tone. The style variants, solid fills and focus/invert tones are omitted.
337
+ - `frame.tsx` — the well of panels: one geometry (a raised well, hairline,
338
+ concentric panel corners) with the style variants, spacing scale, stacking
339
+ modes and panel shadows omitted; `FramePanel` can render as its child.
340
+ - `alert.tsx` — cut to a neutral and a destructive tone with an icon column;
341
+ the info, warning, success and invert tones, the title/action slots and the
342
+ style variants are omitted.
343
+ - `timeline.tsx` — cut to a read-only vertical list (item, indicator on a
344
+ hairline rail, content, date); the active-step context, horizontal mode and
345
+ styled indicators are omitted.
346
+ - `stepper.tsx` — its read-only horizontal layout (indicator, separator,
347
+ title, `aria-current="step"`) is followed by the task page's road
348
+ (`src/browser/first-run.tsx` `Journey`); no stepper source is copied, and
349
+ the controlled state, keyboard navigation and triggers are omitted.
350
+
351
+ No ReUI Pro blocks, icons or templates are included.
352
+
353
+ MIT License
354
+
355
+ Copyright (c) 2025 Keenthemes Inc
356
+
357
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
358
+
359
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
360
+
361
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
362
+
363
+
324
364
  ## Integration logos: Simple Icons (CC0 1.0) and svgl (MIT)
325
365
 
326
366
  The integration logos in `src/brand-icons.ts` are generated by