toolroll 0.9.4 → 0.9.6
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.
- package/CHANGELOG.md +28 -0
- package/README.md +5 -4
- package/dist/assignment-presentation.d.ts +13 -2
- package/dist/assignment-presentation.js +26 -11
- package/dist/assignment-ui.d.ts +10 -15
- package/dist/assignment-ui.js +18 -18
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +21 -20
- package/dist/browser-workspace.d.ts +13 -10
- package/dist/chat-acceptance.js +2 -2
- package/dist/chat-quiet.js +20 -19
- package/dist/cli.js +6 -6
- package/dist/containment.d.ts +5 -0
- package/dist/containment.js +9 -0
- package/dist/demo-chat.d.ts +32 -0
- package/dist/demo-chat.js +158 -0
- package/dist/demo.d.ts +98 -0
- package/dist/demo.js +525 -21
- package/dist/flow-pull-request.js +26 -3
- package/dist/lead-status.d.ts +7 -0
- package/dist/lead-status.js +11 -2
- package/dist/operate.js +25 -9
- package/dist/proof.d.ts +9 -0
- package/dist/proof.js +25 -1
- package/dist/publish.d.ts +15 -0
- package/dist/publish.js +41 -1
- package/dist/pull-request-flow.js +3 -3
- package/dist/serve.d.ts +2 -0
- package/dist/serve.js +260 -81
- package/dist/store.d.ts +1 -0
- package/dist/store.js +2 -1
- package/dist/task-status.d.ts +179 -0
- package/dist/task-status.js +317 -0
- package/dist/telegram-progress.js +62 -100
- package/dist/telegram-status.js +19 -6
- package/dist/work-index.js +25 -9
- package/dist/workspace-ui.d.ts +7 -0
- package/dist/workspace-ui.js +31 -2
- package/package.json +1 -1
package/dist/serve.js
CHANGED
|
@@ -58,11 +58,13 @@ import { prepareWorkspaceRevision, WorkspaceValidatorCache } from "./workspace-r
|
|
|
58
58
|
import { workIndexPage, workCountsByProject, WorkIndexCursorError } from "./work-index.js";
|
|
59
59
|
import { openWorkDecisionOf } from "./work-summary.js";
|
|
60
60
|
import { assignmentOf, checkAssignmentAsOperator } from './assignment.js';
|
|
61
|
+
import { demoChatHtml, demoThreadHtml, DEMO_CHAT_SCRIPT } from "./demo-chat.js";
|
|
61
62
|
import { pullRequestSettingsHtml, PULL_REQUEST_SETTINGS_CSS } from './pull-request-ui.js';
|
|
62
63
|
import { checkPublishing, completeAndOpenPullRequest, mergeAsPerson, pullRequestBlocker, publishingOf, saveMergeSettings, savePublishing, newestPullRequestOf, MERGE_METHODS } from './pull-request-flow.js';
|
|
63
64
|
import { leadBriefHtml, LEAD_CONTEXT_CSS } from './lead-context.js';
|
|
64
65
|
import { assignmentCatchUp } from './assignment-brief.js';
|
|
65
66
|
import { assignmentCardOf, assignmentStatusOf, assignmentSummaryHtml, assignmentWithEvidence, ASSIGNMENT_CSS } from './assignment-ui.js';
|
|
67
|
+
import { pullRequestFactOf, stageOfDispatch, statusDetailsHtml, statusIconSvg, statusWhyHtml, taskStatusOf, TASK_STATUS_CSS } from './task-status.js';
|
|
66
68
|
import { assignmentPresentationOf, historicalAssessmentReason, shortenedMaterialReason } from './assignment-presentation.js';
|
|
67
69
|
import { ledgerBody } from "./ledger-view.js";
|
|
68
70
|
import { EVIDENCE_PACK_CSS, evidencePack, evidencePackHtml, exportDay, ledgerExportChunks } from "./evidence-pack.js";
|
|
@@ -150,8 +152,8 @@ import { TEMPLATES, templateByName } from "./templates.js";
|
|
|
150
152
|
import { starterRecipes, savedRecipes, findRecipe, importRecipe, exportRecipe, createWorkflowPreview, workflowPreview, launchWorkflow, saveWorkflowRecipe, RecipeError, prepareRecipeRun } from "./recipes.js";
|
|
151
153
|
import { recipeFromForm, recipeLibraryHtml, recipeEditorHtml, workflowPreviewHtml, recipeScript, RECIPE_CSS, recipeDefinitionPreviewHtml, recipeRunHtml, recipeAnswersFromForm } from "./recipe-ui.js";
|
|
152
154
|
import { EVIDENCE_CAPS, readVerifiedArtifact, readVerifiedReport, readVerifiedProofForRun, storeEvidence, writeEvidenceFile, scanForSecrets } from "./evidence.js";
|
|
153
|
-
import { GOAL_ASSESSMENT_PENDING, reviewConflict, manualReviewOnly, dispatchStatusToken, passFraction, semanticCoverage, coverageWords, coverageStateWords } from "./proof.js";
|
|
154
|
-
import { WORK_VIEWS, REVIEW_TOKENS, parseWorkView, resultStatusOf, receiptHeadingOf, receiptPublicationWords, reviewFactsOf, workStatusOf, primaryDestinationOf, needsPerson, dispatchActionLabel, } from "./workspace-ui.js";
|
|
155
|
+
import { GOAL_ASSESSMENT_PENDING, reviewConflict, manualReviewOnly, manualReviewCriterionOf, personCheckWords, plainReasonWords, dispatchStatusToken, passFraction, semanticCoverage, coverageWords, coverageStateWords } from "./proof.js";
|
|
156
|
+
import { WORK_VIEWS, REVIEW_TOKENS, parseWorkView, resultStatusOf, resultHeadlineOf, receiptHeadingOf, receiptPublicationWords, reviewFactsOf, workStatusOf, primaryDestinationOf, needsPerson, dispatchActionLabel, } from "./workspace-ui.js";
|
|
155
157
|
import { PRICED_BUILD_MODELS } from "./pricing.js";
|
|
156
158
|
import { RESULT_TABS, RESULT_REVIEW_SCRIPT, isRevisionFeedback, evidenceResultStatusOf, evidenceShortenedWords, parseResultTab, resultLeadOf, evidenceProblemDetailsOf, evidenceHealthOf, resultFactsAttributes, resultFactsAttributeMap, resultReturnTarget, commentSourceKey, REQUEST_TOKEN, revisionBatchOf, parseRevisionBatch, revisionSourceOf, } from "./result-review.js";
|
|
157
159
|
import { parseReport } from "./scout-report.js";
|
|
@@ -1727,6 +1729,7 @@ export function createDecisionServer(options) {
|
|
|
1727
1729
|
url.pathname !== "/projects/browse" && url.pathname !== "/projects/github" && url.pathname !== "/workbench" &&
|
|
1728
1730
|
url.pathname !== "/fleet" &&
|
|
1729
1731
|
url.pathname !== "/chat" && url.pathname !== "/chat/mate/status" && url.pathname !== "/chat/task-status" && url.pathname !== "/chat/stream" &&
|
|
1732
|
+
!url.pathname.startsWith("/chat/demo/") &&
|
|
1730
1733
|
!/^\/chat\/action\/[0-9]{1,15}$/.test(url.pathname) &&
|
|
1731
1734
|
!url.pathname.startsWith("/settings") && url.pathname !== "/logout" && url.pathname !== "/people" && url.pathname !== "/ledger" && url.pathname !== "/ledger/export" && url.pathname !== "/metrics" && url.pathname !== "/spend" && url.pathname !== "/spend/budget" &&
|
|
1732
1735
|
!(url.pathname === "/board" && url.searchParams.get("scope") === "all");
|
|
@@ -3237,6 +3240,26 @@ export function createDecisionServer(options) {
|
|
|
3237
3240
|
...(fragments === null ? {} : { fragments }),
|
|
3238
3241
|
}));
|
|
3239
3242
|
}
|
|
3243
|
+
// `toolroll demo`: Chat is the scripted lead, never a model.
|
|
3244
|
+
if (url.pathname === "/chat/demo/live") {
|
|
3245
|
+
const lead = demoLeadHere();
|
|
3246
|
+
if (lead === null || who.via !== "cookie")
|
|
3247
|
+
return respond(response, 404, "application/json", JSON.stringify({ ok: false }));
|
|
3248
|
+
const exchanges = lead.exchanges();
|
|
3249
|
+
return respond(response, 200, "application/json", JSON.stringify({
|
|
3250
|
+
version: lead.version(), working: exchanges.some(one => one.state === "working"),
|
|
3251
|
+
html: demoThreadHtml(exchanges, who.session.csrf, demoResultView),
|
|
3252
|
+
}));
|
|
3253
|
+
}
|
|
3254
|
+
if (url.pathname === "/chat" && demoLeadHere() !== null && who.via === "cookie" &&
|
|
3255
|
+
!["task", "result", "project", "conversation", "team", "proposal"].some(name => url.searchParams.has(name))) {
|
|
3256
|
+
const lead = demoLeadHere();
|
|
3257
|
+
const said = url.searchParams.get("said")?.slice(0, 300) ?? null;
|
|
3258
|
+
return sendScreen(response, 200, screen("Chat", demoChatHtml({ exchanges: lead.exchanges(), csrf: who.session.csrf, version: lead.version(), problem: said, resultOf: demoResultView }), {
|
|
3259
|
+
chrome: chromeFor(null, "chat", undefined, "all"),
|
|
3260
|
+
functional: { script: DEMO_CHAT_SCRIPT, fetches: true },
|
|
3261
|
+
}));
|
|
3262
|
+
}
|
|
3240
3263
|
if (url.pathname === "/chat") {
|
|
3241
3264
|
// Cookie sessions only (Codex v3 review, change 7): drafts live in
|
|
3242
3265
|
// THIS session's memory; a bearer caller has nowhere to keep them.
|
|
@@ -4146,6 +4169,42 @@ export function createDecisionServer(options) {
|
|
|
4146
4169
|
/** The frozen explicit repo list, digested canonically (sorted) — every
|
|
4147
4170
|
* candidate binds to it and filing re-proves it (v2 new finding 5). */
|
|
4148
4171
|
const chatCeilingDigest = (repos = managedRepos()) => createHash("sha256").update([...repos].sort().join("\n")).digest("hex");
|
|
4172
|
+
/** The scripted lead answers only inside a demo database. */
|
|
4173
|
+
function demoLeadHere() {
|
|
4174
|
+
return options.demoLead !== undefined && store.isDemo() ? options.demoLead : null;
|
|
4175
|
+
}
|
|
4176
|
+
/** A finished demo exchange's result, read back from the evidence its run actually stored. */
|
|
4177
|
+
function demoResultView(exchange) {
|
|
4178
|
+
if (exchange.runId === null || exchange.taskId === null)
|
|
4179
|
+
return null;
|
|
4180
|
+
const artifacts = store.artifactsFor(exchange.runId);
|
|
4181
|
+
const text = (kind) => {
|
|
4182
|
+
const artifact = artifacts.find(one => one.kind === kind);
|
|
4183
|
+
if (artifact === undefined)
|
|
4184
|
+
return null;
|
|
4185
|
+
const read = readVerifiedArtifact(evidenceRoot, artifact);
|
|
4186
|
+
return read.ok ? read.content.toString("utf8") : null;
|
|
4187
|
+
};
|
|
4188
|
+
const shot = artifacts.find(one => one.kind === "screenshot");
|
|
4189
|
+
const stat = (() => { try {
|
|
4190
|
+
return JSON.parse(text("diff-stat") ?? "null");
|
|
4191
|
+
}
|
|
4192
|
+
catch {
|
|
4193
|
+
return null;
|
|
4194
|
+
} })();
|
|
4195
|
+
const assignment = assignmentOf(store, exchange.taskId, clock(), { principal: "operator", repos: managedRepos() }, evidenceRoot);
|
|
4196
|
+
const log = text("check-log");
|
|
4197
|
+
return {
|
|
4198
|
+
diff: text("terminal-diff"),
|
|
4199
|
+
checkLog: log === null ? null : log.slice(log.indexOf("$ ") === -1 ? 0 : log.indexOf("$ ")),
|
|
4200
|
+
checks: { status: assignment?.receipt?.checks.status ?? "unavailable", detail: assignment?.receipt?.checks.detail ?? "Saved checks are unavailable." },
|
|
4201
|
+
screenshot: shot === undefined ? null : { href: `/r/${exchange.runId}/evidence/${shot.id}`, caption: exchange.plan.screenshot.caption },
|
|
4202
|
+
additions: stat?.additions ?? 0,
|
|
4203
|
+
deletions: stat?.deletions ?? 0,
|
|
4204
|
+
files: stat?.fileCount ?? 0,
|
|
4205
|
+
taskHref: taskHref(exchange.taskId),
|
|
4206
|
+
};
|
|
4207
|
+
}
|
|
4149
4208
|
/** Every condition re-proved per request — the render and the POST each
|
|
4150
4209
|
* ask again; nothing is cached into authority. */
|
|
4151
4210
|
function chatEnablement() {
|
|
@@ -4503,7 +4562,7 @@ export function createDecisionServer(options) {
|
|
|
4503
4562
|
s = { ...s, workspace: { ...s.workspace, view: { ...s.workspace.view, stepUp: { label: ssoFacts.label, fresh: ssoFacts.fresh, confirmHref: `/login/sso?reauth=1&return=${encodeURIComponent(back)}` } } } };
|
|
4504
4563
|
}
|
|
4505
4564
|
const teamEntry = requestContext.getStore()?.returnTo;
|
|
4506
|
-
if (teamEntry?.startsWith('/chat') && !s.workspace?.team && !teamEntry.includes('task=') && !teamEntry.includes('proposal=')) {
|
|
4565
|
+
if (teamEntry?.startsWith('/chat') && !s.workspace?.team && !teamEntry.includes('task=') && !teamEntry.includes('proposal=') && demoLeadHere() === null) {
|
|
4507
4566
|
s.body = '<p class="team-entry"><a href="/chat?team=1">Open team chat</a></p>' + s.body;
|
|
4508
4567
|
if (s.workspace?.conversation)
|
|
4509
4568
|
s.workspace.controlsHtml = '<p><a href="/chat?team=1">Open team chat</a></p>' + (s.workspace.controlsHtml ?? '');
|
|
@@ -4524,7 +4583,7 @@ export function createDecisionServer(options) {
|
|
|
4524
4583
|
const extras = s.workspace ?? {};
|
|
4525
4584
|
const notices = [...(extras.notices ?? [])];
|
|
4526
4585
|
if (s.chrome.demo)
|
|
4527
|
-
notices.unshift(
|
|
4586
|
+
notices.unshift(DEMO_BANNER);
|
|
4528
4587
|
if (s.chrome.modeBanner)
|
|
4529
4588
|
notices.push(s.chrome.modeBanner.words);
|
|
4530
4589
|
let crew = { crew: [], crewTruncated: false };
|
|
@@ -4849,8 +4908,6 @@ export function createDecisionServer(options) {
|
|
|
4849
4908
|
const result = receipt === undefined && task.state === "done" && latest !== null && (latest.outcome === "built" || latest.outcome === "no-change")
|
|
4850
4909
|
? completionReceiptView(store, latest, store.artifactsFor(latest.id), evidenceRoot, reviewFactsFor(latest.id)) : receipt;
|
|
4851
4910
|
const status = workStatusOf(facts, result == null ? undefined : receiptStatusOf(result));
|
|
4852
|
-
if (store.lookupRef(task.id)?.revisionOf !== null && live !== null && status.tone === "live")
|
|
4853
|
-
status.label = "Revising";
|
|
4854
4911
|
const earlierLive = task.family === undefined ? [] : earlierLiveVersions(task.family, now);
|
|
4855
4912
|
if (earlierLive.length > 0 && !status.views.includes("running"))
|
|
4856
4913
|
status.views = [...status.views, "running"];
|
|
@@ -5437,8 +5494,12 @@ export function createDecisionServer(options) {
|
|
|
5437
5494
|
const publishing = publishingOf(store, repo);
|
|
5438
5495
|
const target = publishing.on ? publishing.base : null;
|
|
5439
5496
|
const view = newestPullRequestOf(store, family === null ? [taskId] : family.versions.map(one => one.id));
|
|
5440
|
-
if (view !== null)
|
|
5441
|
-
|
|
5497
|
+
if (view !== null) {
|
|
5498
|
+
const publication = store.publicationForRun(view.runId);
|
|
5499
|
+
const fact = publication === null ? null : { ...pullRequestFactOf({ ...publication, merged: view.state === "merged" }),
|
|
5500
|
+
note: view.state === "merged" ? `${view.detail}${view.mergeCommit === null ? "" : ` Merge commit ${view.mergeCommit.slice(0, 12)}.`}` : null };
|
|
5501
|
+
return { taskId: rootId, view: { ...view, canMerge: view.canMerge && who.role === "approver" && who.via === "cookie" }, offer: null, target, fact };
|
|
5502
|
+
}
|
|
5442
5503
|
if (!publishing.on || who.via !== "cookie" || who.role !== "approver")
|
|
5443
5504
|
return null;
|
|
5444
5505
|
const assignment = assignmentOf(store, taskId, clock(), { principal: "operator", repos: admissionList(), includeUnplaced: visible(null) }, evidenceRoot);
|
|
@@ -5922,6 +5983,43 @@ export function createDecisionServer(options) {
|
|
|
5922
5983
|
const denied = authorizeMutation(request, who, body);
|
|
5923
5984
|
if (denied !== null)
|
|
5924
5985
|
return refuse(response, who, denied.status, denied.message);
|
|
5986
|
+
// The scripted demo lead's acts: ask, approve, change a plan, complete or send back a result.
|
|
5987
|
+
const demoAct = /^\/chat\/demo\/(?:ask|([0-9]{1,9})\/(approve|change|revise|complete))$/.exec(url.pathname);
|
|
5988
|
+
if (demoAct !== null) {
|
|
5989
|
+
const lead = demoLeadHere();
|
|
5990
|
+
if (lead === null || who.via !== "cookie")
|
|
5991
|
+
return refuse(response, who, 404, "There's no page at this address.", "/chat");
|
|
5992
|
+
if (who.role !== "approver")
|
|
5993
|
+
return redirect(response, `/chat?said=${encodeURIComponent("Your login can watch. Approving and completing is an approver's act.")}`);
|
|
5994
|
+
const now = clock();
|
|
5995
|
+
if (demoAct[1] === undefined) {
|
|
5996
|
+
const message = (body.get("message") ?? "").trim();
|
|
5997
|
+
if (message === "")
|
|
5998
|
+
return redirect(response, `/chat?said=${encodeURIComponent("Type a request first.")}`);
|
|
5999
|
+
const asked = lead.ask(message, now);
|
|
6000
|
+
return redirect(response, `/chat#demo-${asked.id}`);
|
|
6001
|
+
}
|
|
6002
|
+
const id = Number(demoAct[1]);
|
|
6003
|
+
const note = body.get("note") ?? "";
|
|
6004
|
+
const done = demoAct[2] === "approve" ? lead.approve(id, now)
|
|
6005
|
+
: demoAct[2] === "change" ? lead.change(id, note, now)
|
|
6006
|
+
: demoAct[2] === "revise" ? lead.requestChanges(id, note, now)
|
|
6007
|
+
: lead.complete(id, now, (taskId, runId) => {
|
|
6008
|
+
const principal = matePrincipal(who);
|
|
6009
|
+
if (principal === null)
|
|
6010
|
+
return { ok: false, message: "Your access changed. Sign in again." };
|
|
6011
|
+
const current = assignmentOf(store, taskId, now, { principal: "operator", repos: principal.repos }, evidenceRoot);
|
|
6012
|
+
if (current?.receipt?.runId !== runId)
|
|
6013
|
+
return { ok: false, message: "This result changed. Reload before marking it complete." };
|
|
6014
|
+
const completed = checkAssignmentAsOperator(store, taskId, current.receipt.digest, principal, now, evidenceRoot);
|
|
6015
|
+
return completed.ok ? { ok: true } : { ok: false, message: completed.message };
|
|
6016
|
+
});
|
|
6017
|
+
bustBadge();
|
|
6018
|
+
if (!done.ok)
|
|
6019
|
+
return redirect(response, `/chat?said=${encodeURIComponent(done.message)}`);
|
|
6020
|
+
const latest = lead.exchanges().at(-1);
|
|
6021
|
+
return redirect(response, demoAct[2] === "change" || demoAct[2] === "revise" ? `/chat#demo-${latest?.id ?? id}` : `/chat#demo-${id}-work`);
|
|
6022
|
+
}
|
|
5925
6023
|
// A person says a provider's sign-in works again: its sign-in pause
|
|
5926
6024
|
// lifts, its tasks may start, and one short message says so.
|
|
5927
6025
|
const resumeProvider = /^\/providers\/([a-z]+)\/resume$/.exec(url.pathname);
|
|
@@ -10220,7 +10318,7 @@ export function createDecisionServer(options) {
|
|
|
10220
10318
|
note = validated.note;
|
|
10221
10319
|
}
|
|
10222
10320
|
store.acceptProof(latest.id, verifiedAuthor(who.name), note, now);
|
|
10223
|
-
return redirect(response, taskHref(taskId));
|
|
10321
|
+
return redirect(response, body.get("return") === null ? taskHref(taskId) : safeReturn(body.get("return")));
|
|
10224
10322
|
}
|
|
10225
10323
|
case "complete": {
|
|
10226
10324
|
if (who.via !== "cookie" || who.role !== "approver")
|
|
@@ -11147,7 +11245,8 @@ export function editorFileHref(worktree, path, line) {
|
|
|
11147
11245
|
* chat (v39, extending Priority 2's "one surface, six places" rule from
|
|
11148
11246
|
* the verdict word to the per-criterion state). */
|
|
11149
11247
|
function matrixStateBadge(state) {
|
|
11150
|
-
|
|
11248
|
+
// Red belongs to a Failed headline alone (task-status.ts); an unmet requirement is an amber note.
|
|
11249
|
+
const cls = state === "pass" ? "badge-done" : state === "failed" ? "badge-note" : state === "missing" ? "badge-note" : "badge-manual-review";
|
|
11151
11250
|
const word = state === "pass" ? "pass" : state === "missing" ? "missing" : state === "failed" ? "failed" : "manual review";
|
|
11152
11251
|
return `<span class="badge ${cls}" data-matrix-state="${escape(state)}">${escape(word)}</span>`;
|
|
11153
11252
|
}
|
|
@@ -11233,7 +11332,7 @@ function criterionMatrixHtml(matrix, options = {}) {
|
|
|
11233
11332
|
if (matrix.length === 0)
|
|
11234
11333
|
return "";
|
|
11235
11334
|
const kinds = {
|
|
11236
|
-
check: "Checks", "changed-path": "Changed files", screenshot: "Screenshots", "manual-review": "
|
|
11335
|
+
check: "Checks", "changed-path": "Changed files", screenshot: "Screenshots", "manual-review": "You check",
|
|
11237
11336
|
};
|
|
11238
11337
|
const evidenceLink = (a) => {
|
|
11239
11338
|
const text = `<code>${escape(a.ref)}</code>`;
|
|
@@ -11249,8 +11348,9 @@ function criterionMatrixHtml(matrix, options = {}) {
|
|
|
11249
11348
|
const state = row.state;
|
|
11250
11349
|
const awaitingAssessment = row.assessment?.evidenceState === "pass" && row.review === null;
|
|
11251
11350
|
const confirmed = row.assessment !== undefined && state === "pass";
|
|
11252
|
-
|
|
11253
|
-
const
|
|
11351
|
+
// Plain words (task-status brief): the retired assessment step is not a state a person acts on.
|
|
11352
|
+
const label = awaitingAssessment ? "Met" : confirmed ? "Confirmed" : state === "pass" ? "Met" : state === "manual-review" ? "You check" : state === "missing" ? "Not shown yet" : "Not met";
|
|
11353
|
+
const cls = awaitingAssessment ? "badge-done" : state === "pass" ? "badge-done" : state === "manual-review" ? "badge-manual-review" : "badge-note";
|
|
11254
11354
|
const warnings = [];
|
|
11255
11355
|
// Only replace the known boilerplate. Other recorded failure details
|
|
11256
11356
|
// remain verbatim, so concision cannot hide a different problem.
|
|
@@ -11648,6 +11748,8 @@ function agentsStripHtml(view, taskId) {
|
|
|
11648
11748
|
return `<p class="task-chat-agents">${escape(agentsSummaryWords(view))} <a href="${taskHref(taskId)}#agents">Review agent setup</a></p>`;
|
|
11649
11749
|
return `<details class="task-chat-agents"><summary>Agent setup</summary><p>${escape(agentsSummaryWords(view))}</p><a href="${taskHref(taskId)}#agents">View agents</a></details>`;
|
|
11650
11750
|
}
|
|
11751
|
+
/** The demo's one-line promise, on every page. */
|
|
11752
|
+
const DEMO_BANNER = "Demo: a scripted lead and sample projects. Nothing calls a model, reaches outside or spends.";
|
|
11651
11753
|
/** Every character that could open a tag or an attribute, dead at the sink. */
|
|
11652
11754
|
function escape(text) {
|
|
11653
11755
|
return text
|
|
@@ -11881,6 +11983,10 @@ ${THEME_DARK}
|
|
|
11881
11983
|
background: color-mix(in srgb, var(--muted) 58%, var(--card));
|
|
11882
11984
|
color: color-mix(in srgb, var(--destructive) 68%, var(--foreground)); border-color: var(--border);
|
|
11883
11985
|
}
|
|
11986
|
+
.badge-note {
|
|
11987
|
+
background: color-mix(in srgb, var(--muted) 58%, var(--card));
|
|
11988
|
+
color: var(--so-warning, #ab6400); border-color: var(--border);
|
|
11989
|
+
}
|
|
11884
11990
|
.badge-overdue {
|
|
11885
11991
|
background: color-mix(in srgb, var(--muted) 58%, var(--card)); color: var(--foreground);
|
|
11886
11992
|
border-color: var(--border);
|
|
@@ -12599,6 +12705,66 @@ ${THEME_DARK}
|
|
|
12599
12705
|
padding: .375rem .9rem; font-size: .8125rem; color: var(--muted-foreground);
|
|
12600
12706
|
}
|
|
12601
12707
|
.banner .badge { margin-right: .5rem; }
|
|
12708
|
+
/* Chat in \`toolroll demo\`: the scripted lead's conversation. */
|
|
12709
|
+
.demo-chat { display: flex; flex-direction: column; gap: 1rem; max-width: 46rem; margin: 0 auto; padding-bottom: 1rem; }
|
|
12710
|
+
.demo-thread { display: flex; flex-direction: column; gap: 1.25rem; }
|
|
12711
|
+
.demo-turn { display: flex; flex-direction: column; gap: .75rem; scroll-margin-top: 1rem; }
|
|
12712
|
+
.demo-said p { margin: 0; }
|
|
12713
|
+
.demo-you { align-self: flex-end; max-width: min(34rem, 85%); background: var(--so-ink); color: var(--so-paper); border-radius: .875rem .875rem .25rem .875rem; padding: .55rem .85rem; overflow-wrap: anywhere; }
|
|
12714
|
+
.demo-lead { max-width: 40rem; }
|
|
12715
|
+
.demo-who { font-size: .75rem; font-weight: 600; color: var(--so-muted); margin-bottom: .15rem !important; }
|
|
12716
|
+
.demo-chat .card { margin: 0; padding: 1rem 1.125rem; }
|
|
12717
|
+
.demo-chat .card h2 { font-size: .9375rem; margin: .1rem 0 .4rem; }
|
|
12718
|
+
.demo-chat .card h3 { font-size: .75rem; font-weight: 600; color: var(--so-muted); margin: .75rem 0 .25rem; }
|
|
12719
|
+
.demo-chat .card ul { margin: 0; padding-left: 1.1rem; }
|
|
12720
|
+
.demo-chat .card li { margin: .15rem 0; }
|
|
12721
|
+
.demo-chat .card > p { margin: .25rem 0; }
|
|
12722
|
+
.demo-kicker { font-size: .75rem; color: var(--so-muted); margin: 0 !important; }
|
|
12723
|
+
.demo-actions { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .5rem; margin-top: .9rem; }
|
|
12724
|
+
.demo-actions form { margin: 0; }
|
|
12725
|
+
.demo-actions button, .demo-more > summary { min-height: 2.5rem; }
|
|
12726
|
+
.demo-chat button.demo-primary { background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); font-weight: 600; }
|
|
12727
|
+
.demo-chat details.demo-more { flex: 1 1 12rem; margin: 0; padding: 0; border: 0; border-radius: 0; background: none; }
|
|
12728
|
+
.demo-chat details.demo-more[open] { padding-bottom: 0; }
|
|
12729
|
+
.demo-chat .demo-more > summary { display: inline-flex; align-items: center; color: var(--so-ink); padding: .35rem .85rem; border: 1px solid var(--so-input-line); border-radius: calc(var(--radius) - 3px); background: var(--so-paper); font-weight: 500; font-size: .8125rem; cursor: pointer; list-style: none; }
|
|
12730
|
+
.demo-more > summary::-webkit-details-marker { display: none; }
|
|
12731
|
+
.demo-more[open] > summary { margin-bottom: .5rem; }
|
|
12732
|
+
.demo-more form, .demo-composer { display: flex; flex-direction: column; gap: .4rem; }
|
|
12733
|
+
.demo-more label { font-size: .8125rem; font-weight: 500; }
|
|
12734
|
+
.demo-more textarea, .demo-composer textarea { width: 100%; font: inherit; font-size: 1rem; resize: vertical; }
|
|
12735
|
+
.demo-more form button { align-self: flex-start; }
|
|
12736
|
+
.demo-steps { display: flex; flex-wrap: wrap; gap: .4rem 1rem; list-style: none; padding: 0 !important; margin: .4rem 0 !important; }
|
|
12737
|
+
.demo-steps li { display: inline-flex; align-items: center; gap: .4rem; color: var(--so-muted); font-size: .8125rem; margin: 0 !important; }
|
|
12738
|
+
.demo-steps li::before { content: ""; width: .55rem; height: .55rem; border-radius: 50%; border: 1.5px solid currentColor; }
|
|
12739
|
+
.demo-steps li.done { color: var(--so-ink); }
|
|
12740
|
+
.demo-steps li.done::before { background: var(--so-success); border-color: var(--so-success); }
|
|
12741
|
+
.demo-steps li.now { color: var(--so-ink); font-weight: 600; }
|
|
12742
|
+
.demo-steps li.now::before { border-color: var(--so-info); background: var(--so-info); animation: demo-pulse 1.2s ease-in-out infinite; }
|
|
12743
|
+
@keyframes demo-pulse { 50% { opacity: .35; } }
|
|
12744
|
+
@media (prefers-reduced-motion: reduce) { .demo-steps li.now::before { animation: none; } }
|
|
12745
|
+
.demo-state { display: flex; align-items: center; gap: .5rem; margin: 0 !important; }
|
|
12746
|
+
.demo-chat .badge.demo-ready { background: var(--so-info-soft); color: var(--so-info); }
|
|
12747
|
+
.demo-chat .badge.demo-complete { background: var(--so-success-soft); color: var(--so-success); }
|
|
12748
|
+
.demo-pass { color: var(--so-success); font-weight: 500; }
|
|
12749
|
+
.demo-fail { color: var(--so-danger); font-weight: 500; }
|
|
12750
|
+
.demo-chat details.demo-evidence { margin: .5rem 0 0; padding: 0; border: 0; border-top: 1px solid var(--so-line); border-radius: 0; background: none; box-shadow: none; }
|
|
12751
|
+
.demo-evidence > summary { min-height: 2.5rem; display: list-item; padding-block: .55rem; cursor: pointer; font-weight: 500; font-size: .8125rem; }
|
|
12752
|
+
.demo-evidence pre { margin: 0 0 .5rem; max-height: 22rem; overflow: auto; padding: .6rem .75rem; border-radius: .5rem; background: var(--so-raised); font: 400 .75rem/1.55 var(--font-mono); white-space: pre; overflow-wrap: normal; }
|
|
12753
|
+
.demo-diff span { display: block; min-width: max-content; }
|
|
12754
|
+
.demo-diff-add { background: var(--so-success-soft); color: var(--so-success); }
|
|
12755
|
+
.demo-diff-del { background: var(--so-danger-soft); color: var(--so-danger); }
|
|
12756
|
+
.demo-diff-hunk, .demo-diff-meta { color: var(--so-muted); }
|
|
12757
|
+
.demo-diff-file { font-weight: 600; }
|
|
12758
|
+
.demo-evidence figure { margin: 0 0 .5rem; }
|
|
12759
|
+
.demo-evidence img { display: block; width: 100%; height: auto; border: 1px solid var(--so-line); border-radius: .5rem; }
|
|
12760
|
+
.demo-evidence figcaption { margin-top: .35rem; }
|
|
12761
|
+
.demo-hint { padding: 1.25rem 0 .25rem; }
|
|
12762
|
+
.demo-hint-title { font-size: .9375rem; font-weight: 600; margin: 0 0 .75rem; }
|
|
12763
|
+
.demo-suggestions { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0; }
|
|
12764
|
+
.demo-suggestions button { min-height: 2.5rem; border-radius: 999px; padding: .35rem .95rem; }
|
|
12765
|
+
.demo-composer { position: sticky; bottom: 0; padding: .75rem 0 .25rem; background: var(--so-paper); flex-direction: row; align-items: flex-end; }
|
|
12766
|
+
.demo-chat .demo-composer textarea { flex: 1 1 auto; width: auto; min-width: 0; min-height: 2.75rem; }
|
|
12767
|
+
.demo-chat .demo-composer button { flex: none; width: auto; min-height: 2.75rem; padding-inline: 1.1rem; }
|
|
12602
12768
|
.banner a { color: var(--muted-foreground); }
|
|
12603
12769
|
.sign-in-banner form { display: inline; margin: 0 0 0 .5rem; padding: 0; border: 0; background: transparent; }
|
|
12604
12770
|
|
|
@@ -12661,8 +12827,9 @@ ${THEME_DARK}
|
|
|
12661
12827
|
.dispatch-status[data-dispatch-status="proof-refuted"] .dispatch-copy > strong::before,
|
|
12662
12828
|
.dispatch-status[data-dispatch-status="needs-verification"] .dispatch-copy > strong::before {
|
|
12663
12829
|
content: ""; display: inline-block; width: .45rem; height: .45rem; margin-right: .45rem; border-radius: 50%; vertical-align: .08rem;
|
|
12664
|
-
background: var(--
|
|
12830
|
+
background: var(--so-warning, #ab6400);
|
|
12665
12831
|
}
|
|
12832
|
+
.problem.dispatch-status[data-dispatch-status="proof-refuted"] .dispatch-copy > strong::before { background: var(--destructive); }
|
|
12666
12833
|
.dispatch-status[data-dispatch-status="needs-verification"] .dispatch-copy > strong::before { background: var(--muted-foreground); }
|
|
12667
12834
|
/* A review in flight (review fixes): the box reads neutral — neither the
|
|
12668
12835
|
green of a settled verdict nor the red of a problem — with the dot in
|
|
@@ -13024,6 +13191,9 @@ ${THEME_DARK}
|
|
|
13024
13191
|
.result-notes .recap { margin: .25rem 0 .4rem; font-size: .875rem; }
|
|
13025
13192
|
.result-attention { margin: .75rem 0 .25rem; padding: .7rem .85rem; border-left: 1px solid var(--warning); border-radius: 0 calc(var(--radius) - 3px) calc(var(--radius) - 3px) 0; background: var(--warning-soft); font-size: .8125rem; }
|
|
13026
13193
|
.result-attention strong { display: block; font-size: .78rem; }
|
|
13194
|
+
.result-you-check { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem .75rem; margin: .75rem 0; padding: .65rem .85rem; border-radius: calc(var(--radius) - 3px); background: var(--muted); font-size: .8125rem; }
|
|
13195
|
+
.result-you-check ul { flex: 1 1 14rem; min-width: 0; margin: 0; padding: 0; list-style: none; }
|
|
13196
|
+
.result-you-check form { margin: 0; }
|
|
13027
13197
|
.result-attention ul { margin: .3rem 0 0; padding-left: 1.15rem; }
|
|
13028
13198
|
.result-attention li { margin: .15rem 0; overflow-wrap: anywhere; }
|
|
13029
13199
|
.result-tabs { display: flex; gap: .25rem; margin: .9rem 0 .75rem; border-bottom: 1px solid var(--border); overflow-x: auto; scrollbar-width: none; }
|
|
@@ -13160,7 +13330,7 @@ ${THEME_DARK}
|
|
|
13160
13330
|
.pull-request-head strong { font-size: 1.0625rem; font-weight: 600; color: var(--foreground); display: inline-flex; align-items: center; gap: .5rem; }
|
|
13161
13331
|
.pull-request-head strong::before { content: ""; width: .5rem; height: .5rem; border-radius: 999px; background: var(--muted-foreground); }
|
|
13162
13332
|
.pull-request--ok .pull-request-head strong::before { background: var(--success, #15803d); }
|
|
13163
|
-
.pull-request--problem .pull-request-head strong::before { background: var(--
|
|
13333
|
+
.pull-request--problem .pull-request-head strong::before { background: var(--so-warning, #ab6400); }
|
|
13164
13334
|
.pull-request-act { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .5rem .75rem; margin: .25rem 0 0; }
|
|
13165
13335
|
.pull-request-act .meta { flex: 1 1 100%; }
|
|
13166
13336
|
.pull-request-act label { display: grid; gap: .25rem; flex: 1 1 12rem; margin: 0; }
|
|
@@ -14353,7 +14523,7 @@ button.pick-file { min-height: 1.75rem; padding: 0 .55rem; font-size: .75rem; }
|
|
|
14353
14523
|
const THEME_CONTROLS_CSS = `.task-repo select{width:100%;min-height:2.75rem;font-size:1rem}.task-repo-add{margin:.35rem .1rem .5rem}.task-repo-add a{display:inline-flex;align-items:center;min-height:2.25rem}details.result-request-open.result-request-form>summary{border:0;background:transparent;padding:.5rem 0;min-height:2.75rem;font-weight:600;display:list-item;list-style:revert}details.result-request-open.result-request-form>summary::-webkit-details-marker{display:revert}form.js-autosave button[type=submit]{display:none}.provider-row{border-bottom:1px solid var(--so-line);padding:.35rem 0}.provider-row:first-of-type{border-top:1px solid var(--so-line)}.provider-head{display:flex;align-items:center;gap:.75rem;margin:.4rem 0 0}.provider-status{display:inline-flex;align-items:center;gap:.4rem;color:var(--so-muted);font-size:.875rem}.provider-status i{width:.5rem;height:.5rem;border-radius:50%;background:var(--so-muted)}.provider-status--ok i{background:var(--so-success)}.provider-status--warn i{background:var(--so-attention)}.provider-status--off i{background:transparent;border:1.5px solid var(--so-muted)}details.provider-manage>summary{cursor:pointer;color:var(--so-accent-text);font-size:.875rem;min-height:2.5rem;display:list-item;padding-block:.5rem}.card.props .row{display:grid;gap:.1rem;margin:0 0 .75rem}.card.props .row>.meta{display:block;font-size:.75rem}.card.props .row>.meta::first-letter{text-transform:uppercase}.card.props .row>.mono{font-family:var(--font-sans);font-size:.875rem}.card.props .row>.mono .seal{font-family:var(--font-mono);font-size:.8125rem}details.evidence-files{margin:1rem 0}details.evidence-files>summary{cursor:pointer;min-height:2.75rem;display:list-item;padding-block:.7rem;font-weight:600}details.evidence-files ul{list-style:none;margin:0;padding:0}details.evidence-files li{display:flex;justify-content:space-between;gap:1rem;padding:.5rem 0;border-bottom:1px solid var(--so-line)}.result-action .result-feedback-link{display:inline-flex;align-items:center;min-height:2.5rem;padding:.5rem 1rem;border:1px solid var(--so-input-line);border-radius:.5rem;background:var(--so-paper);color:var(--so-ink);font-weight:600;text-decoration:none}@media(hover:hover) and (pointer:fine){.result-action .result-feedback-link:hover{background:var(--so-raised)}}.so-sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.verdict{margin:.5rem 0 .75rem}.verdict-chips{display:flex;flex-wrap:wrap;gap:.4rem;list-style:none;padding:0;margin:0}.verdict-chip{display:inline-flex;align-items:center;gap:.3rem;min-height:1.75rem;padding:.2rem .65rem;border-radius:999px;font-size:.8125rem;font-weight:600;background:var(--so-neutral-soft);color:var(--so-neutral-ink)}.verdict-chip svg{width:.9rem;height:.9rem}.verdict-chip--success{background:var(--so-success-soft);color:var(--so-success)}.verdict-chip--danger{background:var(--so-danger-soft);color:var(--so-danger)}.verdict-chip--warning{background:var(--so-warning-soft);color:var(--so-warning)}.verdict-chip--info{background:var(--so-info-soft);color:var(--so-info)}.verdict-by{margin:.4rem 0 0}details.result-request-open{margin:.5rem 0}details.result-request-open>summary{display:inline-flex;align-items:center;min-height:2.5rem;padding:.5rem 1rem;border:1px solid var(--so-input-line);border-radius:.5rem;background:var(--so-paper);color:var(--so-ink);font-weight:600;cursor:pointer;list-style:none}details.result-request-open>summary::-webkit-details-marker{display:none}details.result-request-open[open]>summary{margin-bottom:.75rem}.settings-tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(8.5rem,1fr));gap:.5rem;margin:0 0 2rem}.settings-tiles a{display:flex;align-items:center;gap:.6rem;min-height:3rem;padding:.65rem .8rem;border:1px solid var(--so-line);border-radius:.625rem;background:var(--so-paper);color:var(--so-ink);text-decoration:none;font-weight:550;font-size:.875rem}@media(hover:hover) and (pointer:fine){.settings-tiles a:hover{border-color:var(--so-input-line);background:var(--so-raised)}}.settings-tiles svg{width:1.1rem;height:1.1rem;flex-shrink:0;color:var(--so-accent-text)}details.settings-more{margin:.25rem 0 1.25rem}details.settings-more>summary{cursor:pointer;min-height:2.75rem;display:list-item;padding-block:.7rem;font-weight:550}details.settings-more>summary .meta{font-weight:400;margin-left:.35rem}.settings-changed{margin-top:-.25rem}.appearance{margin:0 0 28px}.appearance h2{margin:0 0 10px}.theme-switch{display:inline-flex;flex-wrap:nowrap;max-width:100%;gap:4px;padding:4px;margin:0;border:1px solid var(--so-line);border-radius:10px;background:var(--so-raised)}.theme-switch .theme-choice,.so-native-region .theme-switch .theme-choice{flex:1 1 0;width:auto;white-space:nowrap;min-height:40px;padding:8px 16px;border:0;border-radius:7px;background:transparent;color:var(--so-muted);font:inherit;font-weight:550;box-shadow:none;cursor:pointer}@media(hover:hover) and (pointer:fine){.theme-switch .theme-choice:hover{color:var(--so-ink)}}.theme-switch .theme-choice[aria-pressed="true"]{background:var(--so-paper);color:var(--so-ink);box-shadow:0 1px 2px rgb(0 0 0 / .1)}.appearance .meta{margin:8px 0 0}@media(max-width:600px){.theme-switch .theme-choice{min-height:44px}}.update-notes{white-space:pre-wrap;overflow-wrap:anywhere;font:inherit;max-height:18rem;overflow:auto}`;
|
|
14354
14524
|
/** The page CSS this module writes itself (not the imported modules'), for the stylesheet contract tests. */
|
|
14355
14525
|
export const PAGE_CSS = STYLE + THEME_CONTROLS_CSS;
|
|
14356
|
-
const WORKSPACE_STYLE = styleAsset(STYLE + APPROVAL_RULES_CSS + SPEND_CSS + RETENTION_CSS + STORAGE_CSS + UPDATES_CSS + LIMITS_CSS + MONITORING_CSS + INTEGRATIONS_CSS + BACKUP_CSS + EXPORT_CSS + PROJECT_DELETE_CSS + POLICY_CSS + EVIDENCE_PACK_CSS + THEME_CONTROLS_CSS + CODING_CSS + CODING_SHIPPING_CSS + RECIPE_CSS + SKILLS_CSS + TOOLS_CSS + FLOWS_CSS + TEAMMATE_CSS + KITS_CSS + STARTERS_CSS + SSO_CSS + CREDENTIALS_CSS + KNOWLEDGE_CSS + MODELS_CSS + CHAT_POLISH_CSS + TRANSITIONS_CSS + WORKSPACE_MOTION_CSS + ASSIGNMENT_CSS + LEAD_CONTEXT_CSS + PULL_REQUEST_SETTINGS_CSS + '.learning{min-width:0;overflow-wrap:anywhere}.learning .card{min-width:0}.learning code,.learning blockquote,.learning pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}.learning button,.learning summary,.learning .button-link{min-height:44px}.learning button{white-space:nowrap}.learning summary{padding:12px 0;cursor:pointer}.learning form{margin:12px 0}.learning select{max-width:100%}.learning blockquote{margin:8px 0}.learning ul{padding-left:20px}');
|
|
14526
|
+
const WORKSPACE_STYLE = styleAsset(STYLE + APPROVAL_RULES_CSS + SPEND_CSS + RETENTION_CSS + STORAGE_CSS + UPDATES_CSS + LIMITS_CSS + MONITORING_CSS + INTEGRATIONS_CSS + BACKUP_CSS + EXPORT_CSS + PROJECT_DELETE_CSS + POLICY_CSS + EVIDENCE_PACK_CSS + THEME_CONTROLS_CSS + CODING_CSS + CODING_SHIPPING_CSS + RECIPE_CSS + SKILLS_CSS + TOOLS_CSS + FLOWS_CSS + TEAMMATE_CSS + KITS_CSS + STARTERS_CSS + SSO_CSS + CREDENTIALS_CSS + KNOWLEDGE_CSS + MODELS_CSS + CHAT_POLISH_CSS + TRANSITIONS_CSS + WORKSPACE_MOTION_CSS + ASSIGNMENT_CSS + TASK_STATUS_CSS + LEAD_CONTEXT_CSS + PULL_REQUEST_SETTINGS_CSS + '.learning{min-width:0;overflow-wrap:anywhere}.learning .card{min-width:0}.learning code,.learning blockquote,.learning pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}.learning button,.learning summary,.learning .button-link{min-height:44px}.learning button{white-space:nowrap}.learning summary{padding:12px 0;cursor:pointer}.learning form{margin:12px 0}.learning select{max-width:100%}.learning blockquote{margin:8px 0}.learning ul{padding-left:20px}');
|
|
14357
14527
|
/**
|
|
14358
14528
|
* The board's liveness: fetch this page's own fragment on a timer and swap
|
|
14359
14529
|
* it in place — no flicker, no scroll reset, no long-lived stream to manage,
|
|
@@ -14754,7 +14924,7 @@ function shell(title, body, options = {}) {
|
|
|
14754
14924
|
// must not pass as production (adoption review, finding 8; the FENCE is
|
|
14755
14925
|
// the refuseDemo gate in operate.ts, this is the honest label).
|
|
14756
14926
|
const demoBanner = (chrome.demo === true
|
|
14757
|
-
? `<div class="banner"><span class="badge">
|
|
14927
|
+
? `<div class="banner"><span class="badge">demo</span>${escape(DEMO_BANNER.replace(/^Demo: /, ""))}</div>`
|
|
14758
14928
|
: "") +
|
|
14759
14929
|
(chrome.modeBanner === undefined
|
|
14760
14930
|
? ""
|
|
@@ -15815,7 +15985,7 @@ function chatFleetOverview(snapshot, projects, csrf, interactive) {
|
|
|
15815
15985
|
const dispatch = task.dispatch;
|
|
15816
15986
|
rows.push(`<a class="chat-overview-item decision" href="${taskHref(task.rootId ?? task.id)}" data-dispatch-status="${escape(dispatch.code)}">` +
|
|
15817
15987
|
`<span class="chat-overview-icon">${strokeIcon(`<path d="M12 8v4"/><path d="M12 16h.01"/><circle cx="12" cy="12" r="9"/>`)}</span>` +
|
|
15818
|
-
`<span class="chat-overview-copy"><strong>${escape(task.title)}</strong><span>${escape(projectOf(task.repoIndex))} · ${escape(task.id)} · ${escape(dispatch
|
|
15988
|
+
`<span class="chat-overview-copy"><strong>${escape(task.title)}</strong><span>${escape(projectOf(task.repoIndex))} · ${escape(task.id)} · ${escape(dispatchHeadline(dispatch))}</span></span>` +
|
|
15819
15989
|
`<span class="chat-overview-arrow" aria-hidden="true">→</span></a>`);
|
|
15820
15990
|
}
|
|
15821
15991
|
for (const task of snapshot.tasks.filter(one => one.state === "failed").slice(0, Math.max(0, 3 - rows.length))) {
|
|
@@ -17052,6 +17222,11 @@ function statusActionHref(status, taskId, runId, prUrl, repo) {
|
|
|
17052
17222
|
}
|
|
17053
17223
|
/** The one status line every surface renders: a dot in the tone, the
|
|
17054
17224
|
* label, and the `data-work-status` token tests and CSS key off. */
|
|
17225
|
+
/** A not-yet-finished task's shared headline from its dispatch diagnosis (task-status.ts). */
|
|
17226
|
+
function dispatchHeadline(d) {
|
|
17227
|
+
const read = stageOfDispatch(d);
|
|
17228
|
+
return taskStatusOf({ stage: read.stage, ...(read.need === undefined ? {} : { need: read.need }) }).headline;
|
|
17229
|
+
}
|
|
17055
17230
|
function statusLineHtml(status, extra = "") {
|
|
17056
17231
|
return `<span class="status-line" data-work-status="${escape(status.token)}" data-tone="${escape(status.tone)}"><i class="status-dot" aria-hidden="true"></i><span class="status-label">${escape(status.label)}</span>${extra}</span>`;
|
|
17057
17232
|
}
|
|
@@ -17105,7 +17280,7 @@ function workPage(chrome, data) {
|
|
|
17105
17280
|
const actionHref = row.primaryAction?.code === 'open-result' && target?.runId != null
|
|
17106
17281
|
? `/review?result=${encodeURIComponent(target.taskId)}&run=${target.runId}${row.repo === null ? '' : '&project=' + encodeURIComponent(row.repo)}`
|
|
17107
17282
|
: browserWorkActionHref(row);
|
|
17108
|
-
const needsYouDetail = row.status.views.includes('needs-you') && !['write-scope', 'approve-scope'].includes(row.primaryAction?.code ?? '') && row.status.detail !== row.status.label && row.status.detail !== row.familyProblem;
|
|
17283
|
+
const needsYouDetail = (row.status.views.includes('needs-you') || row.status.label === 'Failed') && !['write-scope', 'approve-scope'].includes(row.primaryAction?.code ?? '') && row.status.label !== 'Ready for review' && row.status.detail !== row.status.label && row.status.detail !== row.familyProblem;
|
|
17109
17284
|
return {
|
|
17110
17285
|
id: row.rootId, title: row.title, href: taskHref(row.rootId),
|
|
17111
17286
|
project: data.multiProject || row.repo === null ? (row.repo === null ? 'Unplaced' : projectName(row.repo)) : null,
|
|
@@ -18171,7 +18346,7 @@ function queueCard(one, csrf, revision, queueRevision, workers, column) {
|
|
|
18171
18346
|
? `<span class="badge">queued</span>`
|
|
18172
18347
|
: `<span class="badge">reserved for ${escape(column)}</span>`;
|
|
18173
18348
|
const chips = ` ${state}` +
|
|
18174
|
-
`${one.dispatch === null || one.dispatch === undefined ? "" : ` <a class="badge" href="${taskHref(one.id)}"
|
|
18349
|
+
`${one.dispatch === null || one.dispatch === undefined ? "" : ` <a class="badge" href="${taskHref(one.id)}" title="${escape(one.dispatch.summary)}">${escape(dispatchHeadline(one.dispatch))}</a>`}`;
|
|
18175
18350
|
const hidden = `<input type="hidden" name="csrf" value="${escape(csrf)}">` +
|
|
18176
18351
|
`<input type="hidden" name="projectRevision" value="${revision}">` +
|
|
18177
18352
|
`<input type="hidden" name="queueRevision" value="${queueRevision}">` +
|
|
@@ -18449,7 +18624,7 @@ function milestoneProgressHtml(milestones) {
|
|
|
18449
18624
|
.map(one => `<li class="milestone milestone-${one.state}"><span class="milestone-badge">${milestoneStateWord(one.state)}</span> ` +
|
|
18450
18625
|
`${escape(one.description)}${one.note === null ? "" : ` <span class="meta">— ${escape(one.note)}</span>`}</li>`)
|
|
18451
18626
|
.join("\n") +
|
|
18452
|
-
`</ul><p class="meta milestone-progress-note">Live checkpoints from the agent
|
|
18627
|
+
`</ul><p class="meta milestone-progress-note">Live checkpoints from the agent.</p></section>`);
|
|
18453
18628
|
}
|
|
18454
18629
|
/** Adaptive execution plans (v44): the current plan revision's own reason
|
|
18455
18630
|
* and evidence, any revision still awaiting an operator's accept/reject,
|
|
@@ -18642,7 +18817,11 @@ function taskBodyParts(data) {
|
|
|
18642
18817
|
// data-work-status is the shared projection's token (package 1) —
|
|
18643
18818
|
// the same one the Work row, chat, and cockpit carry for this task.
|
|
18644
18819
|
const workToken = resultStatus?.token ?? data.dispatch?.code ?? "unknown";
|
|
18645
|
-
|
|
18820
|
+
// A diagnosis under Task options: the exact technical reason, one tap
|
|
18821
|
+
// away from the shared headline (task-status.ts). Red only when that
|
|
18822
|
+
// headline is Failed.
|
|
18823
|
+
const headline = status.label;
|
|
18824
|
+
const box = (kind, title, detail, code, controls = "") => `<div class="${kind === "problem" && !inReview && headline === "Failed" ? "problem" : "answered"} dispatch-status" id="run-status" data-dispatch-status="${escape(code ?? title.toLowerCase().replace(/[^a-z0-9]+/g, "-"))}" data-work-status="${escape(workToken)}">` +
|
|
18646
18825
|
`<div class="dispatch-copy"><strong>${escape(inReview ? "Recorded checks" : title)}</strong><span class="meta">${detail}</span></div>${controls}</div>`;
|
|
18647
18826
|
if (task.state !== "done") {
|
|
18648
18827
|
const diagnosis = data.dispatch ?? null;
|
|
@@ -18726,7 +18905,7 @@ function taskBodyParts(data) {
|
|
|
18726
18905
|
return `<details class="dispatch-proof-details"><summary>Previous assessment</summary><div class="dispatch-proof-body">` +
|
|
18727
18906
|
`<p class="meta">Saved assessment history. Current task status and checks are shown above.</p>` +
|
|
18728
18907
|
(accepted === null ? "" : `<p class="meta">Accepted with an exception by ${escape(accepted.approver)}. Check results are unchanged.${accepted.note === null ? "" : ` ${escape(accepted.note)}`}</p>`) +
|
|
18729
|
-
(proof.proofReasons.length === 0 ? "" : `<ul>${proof.proofReasons.map(reason => `<li>${escape(reason)}</li>`).join("")}</ul>`) +
|
|
18908
|
+
(proof.proofReasons.length === 0 ? "" : `<ul>${[...new Set(proof.proofReasons.map(plainReasonWords))].map(reason => `<li>${escape(reason)}</li>`).join("")}</ul>`) +
|
|
18730
18909
|
criterionMatrixHtml(proof.proofMatrix, { compact: true, runId: proof.runId, links: proof.proofMatrixLinks }) +
|
|
18731
18910
|
semanticCoverageHtml(proof.proofMatrix, proof.qualityMode ?? "default") + `</div></details>`;
|
|
18732
18911
|
}
|
|
@@ -19529,7 +19708,13 @@ function taskBodyParts(data) {
|
|
|
19529
19708
|
: ` · filed via ${escape(data.filedVia)}`}${data.deliverable === "report" ? ` · <span class="badge">scout</span>` : ""}</p>`;
|
|
19530
19709
|
// A pull request that can be merged (or opened) owns the one primary action; the result stays in its section.
|
|
19531
19710
|
const pullRequestActs = data.csrf !== "" && (data.pullRequest?.view?.canMerge === true || (data.pullRequest?.view == null && data.pullRequest?.offer != null));
|
|
19532
|
-
const
|
|
19711
|
+
const pullRequestFact = data.pullRequest?.fact != null && data.pullRequest.view?.runId === data.assignment?.receipt?.runId ? data.pullRequest.fact : undefined;
|
|
19712
|
+
const assignmentOptions = data.assignment == null ? null : { workStatus: status, hideAction: (approveForm !== "" && data.dispatch?.action === "approve-scope") || pullRequestActs, problem: status.tone === "problem",
|
|
19713
|
+
planning: data.dispatch?.code === "running" && data.dispatch.role === "planner", ...(pullRequestFact === undefined ? {} : { pullRequest: pullRequestFact }),
|
|
19714
|
+
diagnostics: [...(status.diagnostics ?? []), ...(status.tone === "problem" && status.detail !== data.assignment.detail ? [status] : [])] };
|
|
19715
|
+
// Say it once: a pull request that couldn't open, or merged, is the status
|
|
19716
|
+
// card's own row (its reason or merge commit under Details), not a second card.
|
|
19717
|
+
const pullRequestCard = data.pullRequest == null || (pullRequestFact !== undefined && (data.pullRequest.view?.state === "failed" || data.pullRequest.view?.state === "merged")) ? "" : pullRequestCardHtml(data.pullRequest, data.csrf);
|
|
19533
19718
|
// The result takes over from the task status as soon as it is ready.
|
|
19534
19719
|
const statusHtml = (data.assignment != null && assignmentOptions !== null ? assignmentSummaryHtml(data.assignment, assignmentOptions) : receiptLeads ? completionReceiptCard(data.completion.receipt, task.id, "task", status) : taskStatusCard(status, task.id, data.dispatch ?? null, liveRunId, approveForm !== "" && data.dispatch?.action === "approve-scope")) + checkProgressHtml(data.checkProgress ?? null);
|
|
19535
19720
|
// The exact-run control (v52), directly under the scheduler's answer:
|
|
@@ -19659,7 +19844,7 @@ function taskBodyParts(data) {
|
|
|
19659
19844
|
approval: approvalHtml,
|
|
19660
19845
|
lead: [
|
|
19661
19846
|
{ key: "history", html: data.history ?? "" }, { key: "control", html: controlHtml }, { key: "problem", html: problemHtml },
|
|
19662
|
-
{ key: "pull-request", html:
|
|
19847
|
+
{ key: "pull-request", html: pullRequestCard },
|
|
19663
19848
|
{ key: "needs-scope", html: needsScopeCard }, { key: "progress", html: progressCard }, { key: "revisions", html: revisionLedgerCard },
|
|
19664
19849
|
{ key: "plan", html: planCard }, { key: "mirror", html: mirrorCard }, { key: "contest", html: contestCard }, { key: "attempt", html: attemptPanel },
|
|
19665
19850
|
].filter(one => one.html !== ""),
|
|
@@ -19911,11 +20096,11 @@ function cockpitStatusOf(view) {
|
|
|
19911
20096
|
// The receipt's own status (package 3): the same projection the chat
|
|
19912
20097
|
// receipt and the run page print, read from the shared detail.
|
|
19913
20098
|
if (view.run === null || view.detail === null)
|
|
19914
|
-
return view.assignment === null ? resultStatusOf(null, null) : assignmentPresentationOf(view.assignment).status;
|
|
20099
|
+
return view.assignment === null ? resultHeadlineOf(resultStatusOf(null, null)) : assignmentPresentationOf(view.assignment).status;
|
|
19915
20100
|
const receiptStatus = receiptStatusOf(view.detail.receipt);
|
|
19916
20101
|
if (view.assignment !== null)
|
|
19917
20102
|
return assignmentStatusOf(assignmentWithEvidence(view.assignment, receiptStatus, view.run.id));
|
|
19918
|
-
return receiptStatus;
|
|
20103
|
+
return resultHeadlineOf(receiptStatus);
|
|
19919
20104
|
}
|
|
19920
20105
|
/** Turn verifier records into one sentence a project owner can act on.
|
|
19921
20106
|
* A command-not-found result means the check could not start, not that the
|
|
@@ -19981,11 +20166,14 @@ function verificationExplanation(verdict, reasons) {
|
|
|
19981
20166
|
if (reason === "the sealed diff is unavailable or truncated; the claimed changed paths cannot be verified against it") {
|
|
19982
20167
|
return "The recorded changes were incomplete, so they could not be verified.";
|
|
19983
20168
|
}
|
|
20169
|
+
const plainWords = plainReasonWords(reason);
|
|
20170
|
+
if (plainWords !== reason)
|
|
20171
|
+
return plainWords;
|
|
19984
20172
|
const sentence = reason.trim();
|
|
19985
20173
|
return sentence === "" ? "" : `${sentence[0]?.toUpperCase() ?? ""}${sentence.slice(1)}${/[.!?]$/.test(sentence) ? "" : "."}`;
|
|
19986
20174
|
}).filter(Boolean);
|
|
19987
20175
|
if (plain.length > 0)
|
|
19988
|
-
return plain.join(" ");
|
|
20176
|
+
return [...new Set(plain)].join(" ");
|
|
19989
20177
|
if (verdict === "verified")
|
|
19990
20178
|
return "Toolroll independently verified this result.";
|
|
19991
20179
|
if (verdict === "attested")
|
|
@@ -20205,7 +20393,8 @@ function pullRequestCardHtml(pr, csrf) {
|
|
|
20205
20393
|
const view = pr.view;
|
|
20206
20394
|
const url = safePrUrl(view.prUrl);
|
|
20207
20395
|
const link = view.prNumber === null ? "" : url === null ? `<span class="mono">PR #${view.prNumber}</span>` : `<a href="${escape(url)}" class="mono" rel="noreferrer" target="_blank">PR #${view.prNumber}</a>`;
|
|
20208
|
-
|
|
20396
|
+
// A pull request's trouble never undoes the result: amber, never red.
|
|
20397
|
+
const tone = view.state === "ready" || view.state === "merged" ? "ok" : view.state === "failing" || view.state === "failed" ? "problem" : "waiting";
|
|
20209
20398
|
const revision = view.revisionTask === null || view.state !== "failing" ? "" : ` <a href="${taskHref(view.revisionTask)}">Open revision</a>`;
|
|
20210
20399
|
const merged = view.mergeCommit === null ? "" : `<p class="meta">Merge commit <span class="mono">${escape(view.mergeCommit.slice(0, 12))}</span></p>`;
|
|
20211
20400
|
const target = pr.target === null ? "the base branch" : pr.target;
|
|
@@ -20940,8 +21129,8 @@ function completionReceiptCard(view, taskId, place, standing = receiptStatusOf(v
|
|
|
20940
21129
|
(view.diff.binaryCount > 0 ? ` · ${view.diff.binaryCount} binary` : "") +
|
|
20941
21130
|
(view.diff.filesTruncated ? " · list shortened" : "");
|
|
20942
21131
|
const criteria = facts.checks === null
|
|
20943
|
-
? "No
|
|
20944
|
-
: `${facts.checks.passed}/${facts.checks.total}
|
|
21132
|
+
? "No requirements set"
|
|
21133
|
+
: `${facts.checks.passed}/${facts.checks.total} requirements met`;
|
|
20945
21134
|
// The deliverable leads (package 3): a scout's report by its title, UI
|
|
20946
21135
|
// work by its validated screenshots. Unverifiable shots are counted as
|
|
20947
21136
|
// unavailable, never shown, never called validated.
|
|
@@ -20962,7 +21151,7 @@ function completionReceiptCard(view, taskId, place, standing = receiptStatusOf(v
|
|
|
20962
21151
|
const attention = current === null ? [...facts.evidenceProblems, ...view.caveats] : [];
|
|
20963
21152
|
const caveats = attention.length === 0
|
|
20964
21153
|
? ""
|
|
20965
|
-
: `<div class="receipt-caveats" data-result-attention="${attention.length}"><strong>Before you move on</strong><ul>${attention.map(one => `<li>${escape(one)}</li>`).join("")}</ul></div>`;
|
|
21154
|
+
: `<div class="receipt-caveats" data-result-attention="${attention.length}"><strong>Before you move on</strong><ul>${attention.map(one => `<li>${escape(plainReasonWords(one))}</li>`).join("")}</ul></div>`;
|
|
20966
21155
|
// v51: semantic coverage, distinct from the machine proof word above —
|
|
20967
21156
|
// the same lines the CLI prints, so chat and terminal cannot disagree.
|
|
20968
21157
|
// Nothing owed (optional and unsettled, or satisfied) folds behind a
|
|
@@ -20981,7 +21170,8 @@ function completionReceiptCard(view, taskId, place, standing = receiptStatusOf(v
|
|
|
20981
21170
|
lead +
|
|
20982
21171
|
`<p class="receipt-summary">${escape(conciseOutcomeOf(view.summary ?? (view.outcome === "no-change" ? "The agent found that no repository change was needed." : "The build finished without a concise handoff.")))}</p>` +
|
|
20983
21172
|
(headline ? `<div class="receipt-actions"><a class="button-link" href="${escape(resultHref)}" data-open-result data-primary-action>${escape(status.action?.label ?? "Open result")}</a></div>` : "") +
|
|
20984
|
-
|
|
21173
|
+
// A pull request that couldn't open never undoes the result: an amber note, not red.
|
|
21174
|
+
(view.publication?.state === "failed" ? `<p class="receipt-note">${statusIconSvg("note")} Pull request couldn't open. The commit is safe locally.</p>` : "") +
|
|
20985
21175
|
caveats +
|
|
20986
21176
|
coverage +
|
|
20987
21177
|
`<details class="receipt-details"><summary>Result details</summary>` +
|
|
@@ -20994,7 +21184,7 @@ function completionReceiptCard(view, taskId, place, standing = receiptStatusOf(v
|
|
|
20994
21184
|
(inReview ? `<details class="receipt-history"><summary>Review history</summary><p class="receipt-review meta" data-receipt-review="${escape(status.token)}">${escape(status.detail)}</p></details>` : "") +
|
|
20995
21185
|
// The matrix count is the proof's own citation; it reads as verified
|
|
20996
21186
|
// only when the machine verified the result (workspace package 1).
|
|
20997
|
-
`<div class="receipt-facts"><span><strong>${escape(criteria)}</strong><small>${stored.tone === "problem" ? "
|
|
21187
|
+
`<div class="receipt-facts"><span><strong>${escape(criteria)}</strong><small>${stored.tone === "problem" ? "the agent's own claim — not checked" : stored.token === "agent-attested" ? "the agent's own claim, no project check" : "against the approved scope"}</small></span>` +
|
|
20998
21188
|
`<span><strong>${escape(diff)}</strong><small>${facts.head === null ? "no commit recorded" : `head ${escape(facts.head.slice(0, 12))} · ${escape(facts.headSource ?? "")}`}</small></span>` +
|
|
20999
21189
|
`<span><strong>${view.screenshots.length} screenshot${view.screenshots.length === 1 ? "" : "s"}</strong><small>${view.screenshots.length === 0 ? "none required or captured" : unavailable === 0 ? "validated visual proof" : `${unavailable} unavailable — not validated`}</small></span></div>` +
|
|
21000
21190
|
`<div class="receipt-actions">` +
|
|
@@ -21032,16 +21222,18 @@ function resultPanelParts(detail, o) {
|
|
|
21032
21222
|
const { run, receipt, proof, terminal, handoff } = detail;
|
|
21033
21223
|
const facts = receipt.facts;
|
|
21034
21224
|
const current = detail.assignment?.receipt?.runId === run.id ? detail.assignment : null;
|
|
21035
|
-
const
|
|
21036
|
-
|
|
21225
|
+
const resultLinks = { result: o.hrefFor("summary"), checks: o.hrefFor("checks"), pullRequest: `${taskHref(detail.rootId ?? detail.taskId)}#merge` };
|
|
21226
|
+
const presentation = current == null ? null : assignmentPresentationOf(current, { additionalAttention: [...facts.evidenceProblems, ...receipt.caveats],
|
|
21227
|
+
...(detail.publication === null ? {} : { pullRequest: pullRequestFactOf({ ...detail.publication, lastCheckState: detail.ciFailing ? "failing" : detail.publication.lastCheckState }) }),
|
|
21228
|
+
evidence: { damaged: facts.evidenceHealth.damaged, missing: facts.evidenceHealth.missing, shortened: facts.evidenceHealth.shortened }, links: resultLinks });
|
|
21229
|
+
const taskStatus = presentation?.taskStatus ?? null;
|
|
21230
|
+
const status = presentation?.status ?? resultHeadlineOf(receiptStatusOf(receipt));
|
|
21037
21231
|
const stored = receiptStatusOf(receipt, null);
|
|
21038
21232
|
const humanReview = manualReviewOnly(proof === null ? null : { ...proof, verdict: proof.verdict ?? "" });
|
|
21039
21233
|
const directAssessment = proof?.matrix.some(row => row.assessment !== undefined) === true;
|
|
21040
21234
|
const awaitingGoalReview = directAssessment && proof?.reasons.length === 1 && proof.reasons[0] === GOAL_ASSESSMENT_PENDING;
|
|
21041
21235
|
const assessmentReasons = directAssessment ? new Set(proof.matrix.flatMap(row => row.review ? [`${row.review.author} ${row.review.judgement === "contradicts" ? "contradicts" : "needs more evidence for"} criterion "${row.id}": ${row.review.note}`] : [])) : new Set();
|
|
21042
21236
|
const physicalReasons = receipt.reasons.filter(reason => !assessmentReasons.has(reason));
|
|
21043
|
-
if (directAssessment && status.token === "verification-needed" && proof?.verdict === "short" && proof.matrix.some(row => row.review?.judgement === "cannot-tell") && physicalReasons.length === 0)
|
|
21044
|
-
status = { ...status, label: "More evidence needed", tone: "attention" };
|
|
21045
21237
|
const runId = run.id;
|
|
21046
21238
|
const shots = proof?.screenshots ?? [];
|
|
21047
21239
|
const shown = shots.filter(one => one.problem === null);
|
|
@@ -21075,14 +21267,31 @@ function resultPanelParts(detail, o) {
|
|
|
21075
21267
|
attention.push(...(handoff?.followUps ?? []).map(one => `Follow-up: ${one}`));
|
|
21076
21268
|
// Publication risks stay in the open (repair 2026-09-14); the routine
|
|
21077
21269
|
// publication fact lives with the build details below.
|
|
21078
|
-
|
|
21270
|
+
// With the shared status, the pull request is its own quiet row (its reason under Details).
|
|
21271
|
+
if (publication !== null && publication.state === "failed" && taskStatus === null) {
|
|
21079
21272
|
attention.push(`Publication failed after ${publication.attempts} attempt${publication.attempts === 1 ? "" : "s"}${publication.lastError === null ? "" : ` — ${oneLineOf(publication.lastError, 200)}`}. No pull request or merge is recorded.`);
|
|
21080
21273
|
}
|
|
21081
|
-
if (detail.ciFailing && publication !== null)
|
|
21274
|
+
if (detail.ciFailing && publication !== null && taskStatus === null)
|
|
21082
21275
|
attention.push(`CI is failing on PR #${publication.prNumber} at the last check.`);
|
|
21276
|
+
// Requirements only a person can confirm: plain words and one Accept,
|
|
21277
|
+
// in neutral ink — nothing failed. Accepting records the person's
|
|
21278
|
+
// decision and leaves the recorded checks as they are.
|
|
21279
|
+
const personChecks = proof === null || proof.accepted !== null ? [] : [
|
|
21280
|
+
...proof.matrix.filter(row => row.state === "manual-review").map(row => personCheckWords(row.statement)),
|
|
21281
|
+
...(proof.matrix.length > 0 ? [] : proof.reasons.filter(reason => manualReviewCriterionOf(reason) !== null).map(() => personCheckWords(null))),
|
|
21282
|
+
];
|
|
21283
|
+
const acceptable = personChecks.length > 0 && humanReview && o.csrf !== "" && (current != null || detail.assignment == null);
|
|
21284
|
+
const youCheck = personChecks.length === 0 ? null : {
|
|
21285
|
+
lines: [...new Set(personChecks)],
|
|
21286
|
+
accept: acceptable ? { action: `${taskHref(detail.taskId)}/accept-proof`, run: run.id, returnTo: o.returnTo } : null,
|
|
21287
|
+
};
|
|
21288
|
+
const youCheckHtml = youCheck === null ? "" :
|
|
21289
|
+
`<div class="result-you-check" data-result-you-check="${youCheck.lines.length}"><ul>${youCheck.lines.map(one => `<li>${escape(one)}</li>`).join("")}</ul>` +
|
|
21290
|
+
(youCheck.accept === null ? "" : `<form method="post" action="${escape(youCheck.accept.action)}"><input type="hidden" name="csrf" value="${escape(o.csrf)}"><input type="hidden" name="run" value="${youCheck.accept.run}"><input type="hidden" name="return" value="${escape(youCheck.accept.returnTo)}"><button type="submit" data-accept-result>Accept</button></form>`) +
|
|
21291
|
+
`</div>`;
|
|
21083
21292
|
const attentionHtml = attention.length === 0
|
|
21084
21293
|
? ""
|
|
21085
|
-
: `<div class="result-attention" data-result-attention="${attention.length}"><ul>${attention.map(one => `<li>${escape(one)}</li>`).join("")}</ul></div>`;
|
|
21294
|
+
: `<div class="result-attention" data-result-attention="${attention.length}"><ul>${attention.map(one => `<li>${escape(plainReasonWords(one))}</li>`).join("")}</ul></div>`;
|
|
21086
21295
|
// ---- one outcome, one action (repair 2026-09-14) ------------------------
|
|
21087
21296
|
// The outcome is the handoff's first sentence, bounded; the agent's full
|
|
21088
21297
|
// account waits behind a disclosure in Summary so nothing is said twice.
|
|
@@ -21338,14 +21547,17 @@ function resultPanelParts(detail, o) {
|
|
|
21338
21547
|
const html = (`<section class="card result-panel" id="result" data-result-panel data-result-place="${o.place}" data-result-lead="${lead}" data-result-task="${escape(detail.rootId ?? detail.taskId)}" data-result-user="${escape(o.user)}"${resultFactsAttributes(facts)}>` +
|
|
21339
21548
|
(o.back === null ? "" : `<p class="result-back"><a href="${escape(o.back.href)}" data-result-back>← ${escape(o.back.label)}</a></p>`) +
|
|
21340
21549
|
(detail.history ?? "") +
|
|
21341
|
-
|
|
21550
|
+
// One headline: with the shared status, its words lead (task-status.ts); "Changes saved" would compete with them.
|
|
21551
|
+
(taskStatus !== null && o.headStatus !== false
|
|
21552
|
+
? `<header class="result-head"><div data-headline-tone="${taskStatus.tone}"><h2 class="status-headline" data-work-status="${escape(status.token)}"><i aria-hidden="true"></i>${escape(taskStatus.headline)}</h2><p class="status-sentence">${escape(taskStatus.sentence)}</p></div></header>`
|
|
21553
|
+
: `<header class="result-head"><div><h2>${escape(heading)}</h2></div>${o.headStatus === false ? "" : statusLineHtml(status)}</header>`) +
|
|
21342
21554
|
`<p class="result-summary">${escape(outcome)}</p>` +
|
|
21343
21555
|
(current == null || o.place === "review" && (current.state === "ready-to-check" || current.state === "complete") ? "" :
|
|
21344
|
-
`<div class="verdict" data-current-outcome>${
|
|
21345
|
-
|
|
21556
|
+
`<div class="verdict" data-current-outcome data-headline="${escape(taskStatus.headline)}">${statusDetailsHtml(taskStatus)}${statusWhyHtml(taskStatus)}</div>`) +
|
|
21557
|
+
attentionHtml +
|
|
21558
|
+
youCheckHtml +
|
|
21346
21559
|
action +
|
|
21347
21560
|
(REVIEW_TOKENS.has(status.token) ? `<details class="receipt-history"><summary>Review history</summary><p class="receipt-review meta" data-receipt-review="${escape(status.token)}">${escape(status.detail)}</p></details>` : "") +
|
|
21348
|
-
attentionHtml +
|
|
21349
21561
|
tabs +
|
|
21350
21562
|
view("summary", summaryParts) +
|
|
21351
21563
|
view("changes", changeParts) +
|
|
@@ -21356,9 +21568,10 @@ function resultPanelParts(detail, o) {
|
|
|
21356
21568
|
const panel = {
|
|
21357
21569
|
attributes: { "data-result-panel": "", "data-result-place": o.place, "data-result-lead": lead, "data-result-task": detail.rootId ?? detail.taskId, "data-result-user": o.user, ...resultFactsAttributeMap(facts) },
|
|
21358
21570
|
heading, outcome,
|
|
21359
|
-
|
|
21571
|
+
status: taskStatus,
|
|
21360
21572
|
reviewHistory: REVIEW_TOKENS.has(status.token) ? status.detail : null,
|
|
21361
|
-
attention: attention.filter(one => !shortenedMaterialReason(one)),
|
|
21573
|
+
attention: attention.filter(one => !shortenedMaterialReason(one)).map(plainReasonWords),
|
|
21574
|
+
youCheck,
|
|
21362
21575
|
limits: attention.filter(one => shortenedMaterialReason(one)),
|
|
21363
21576
|
tabs: RESULT_TABS.map(tab => ({ key: tab.key, label: tab.key === "checks" && proof?.matrix.some(row => row.assessment !== undefined) ? "Requirements" : tab.label, count: tabCounts[tab.key], href: o.hrefFor(tab.key), active: tab.key === o.tab })),
|
|
21364
21577
|
views: [{ key: "summary", html: summaryParts.join("\n") }, { key: "changes", html: changeParts.join("\n") }, { key: "checks", html: checkParts.join("\n") }],
|
|
@@ -21370,40 +21583,6 @@ function resultPanelParts(detail, o) {
|
|
|
21370
21583
|
};
|
|
21371
21584
|
return { html, panel };
|
|
21372
21585
|
}
|
|
21373
|
-
const CHIP_ICONS = { check: `<path d="M20 6 9 17l-5-5"/>`, x: `<path d="M18 6 6 18M6 6l12 12"/>` };
|
|
21374
|
-
/** The chips as data: the server list below and the rebuilt result page. */
|
|
21375
|
-
function verdictChipsOf(current, facts) {
|
|
21376
|
-
const chips = [];
|
|
21377
|
-
const checks = current.receipt?.checks.status ?? null;
|
|
21378
|
-
if (checks === "passed")
|
|
21379
|
-
chips.push({ tone: "success", label: "Checks passed", icon: "check", title: null });
|
|
21380
|
-
else if (checks === "failed")
|
|
21381
|
-
chips.push({ tone: "danger", label: "Checks failed", icon: "x", title: null });
|
|
21382
|
-
else if (checks === "unavailable")
|
|
21383
|
-
chips.push({ tone: "warning", label: "Check unavailable", icon: null, title: null });
|
|
21384
|
-
const publication = { none: ["neutral", "Branch only"], intended: ["info", "Publishing"], pushed: ["info", "Pushed"], opened: ["info", "Pull request open"], failed: ["danger", "Publish failed"] };
|
|
21385
|
-
const [tone, label] = publication[facts.publicationState] ?? ["neutral", facts.publicationState];
|
|
21386
|
-
chips.push({ tone, label, icon: null, title: facts.publicationWords });
|
|
21387
|
-
const health = facts.evidenceHealth;
|
|
21388
|
-
if (health.damaged > 0)
|
|
21389
|
-
chips.push({ tone: "danger", label: "Damaged evidence", icon: null, title: null });
|
|
21390
|
-
else if (health.missing > 0)
|
|
21391
|
-
chips.push({ tone: "warning", label: "Missing evidence", icon: null, title: null });
|
|
21392
|
-
else if (health.shortened > 0)
|
|
21393
|
-
chips.push({ tone: "warning", label: "Partial output", icon: null, title: "Some saved output was shortened when stored" });
|
|
21394
|
-
return { chips, by: current.completion === null ? null : current.completion.actor.replace(/^(?:operator|coordinator|lead):/, "") };
|
|
21395
|
-
}
|
|
21396
|
-
/** The verdict at a glance: checks, where the work lives, and whether any
|
|
21397
|
-
* saved output is partial, as chips instead of sentences. The same facts
|
|
21398
|
-
* stay available in words on the Checks tab and in Build details. */
|
|
21399
|
-
function verdictChipsHtml(current, facts) {
|
|
21400
|
-
const { chips, by } = verdictChipsOf(current, facts);
|
|
21401
|
-
const items = chips.map(one => {
|
|
21402
|
-
const label = `${one.icon === null ? "" : strokeIcon(CHIP_ICONS[one.icon])}${one.label}`;
|
|
21403
|
-
return `<li class="verdict-chip verdict-chip--${one.tone}" title="${escape(one.title ?? label)}">${label}</li>`;
|
|
21404
|
-
});
|
|
21405
|
-
return `<ul class="verdict-chips" aria-label="Result at a glance">${items.join("")}</ul>` + (by === null ? "" : `<p class="meta verdict-by">Completed by ${escape(by)}</p>`);
|
|
21406
|
-
}
|
|
21407
21586
|
/** The outcome in one bounded sentence (repair 2026-09-14): the handoff's
|
|
21408
21587
|
* first sentence, cut at a word when it runs past 180 characters. The
|
|
21409
21588
|
* full conclusion stays available behind the Summary's disclosure. */
|