toolroll 0.9.92 → 0.9.94
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 +87 -0
- package/dist/activity-line.d.ts +7 -4
- package/dist/activity-line.js +9 -7
- package/dist/agent-fence.d.ts +4 -0
- package/dist/agent-fence.js +4 -1
- package/dist/approval-authority.d.ts +7 -1
- package/dist/approval-authority.js +11 -2
- package/dist/approval-password.d.ts +31 -0
- package/dist/approval-password.js +26 -0
- package/dist/assignment-status.d.ts +1 -1
- package/dist/assignment-ui.js +3 -3
- package/dist/assignment.js +16 -3
- package/dist/boot-identity.d.ts +2 -1
- package/dist/boot-identity.js +6 -4
- package/dist/bounded-spawn.d.ts +57 -0
- package/dist/bounded-spawn.js +220 -0
- package/dist/browser/terminal.js +42 -42
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +26 -26
- package/dist/browser-crew.js +2 -2
- package/dist/browser-shell.d.ts +1 -1
- package/dist/browser-shell.js +2 -2
- package/dist/browser-workspace.d.ts +48 -7
- package/dist/browser-workspace.js +3 -3
- package/dist/builder-restart.d.ts +21 -0
- package/dist/builder-restart.js +50 -0
- package/dist/builder.d.ts +4 -0
- package/dist/builder.js +73 -25
- package/dist/chat-acceptance.js +1 -1
- package/dist/chat-actions.d.ts +12 -2
- package/dist/chat-actions.js +25 -2
- package/dist/chat-channel.js +11 -0
- package/dist/chat-decide.d.ts +7 -2
- package/dist/chat-decide.js +37 -9
- package/dist/chat-quiet.js +3 -3
- package/dist/chat-voice.d.ts +1 -1
- package/dist/chat-voice.js +2 -2
- package/dist/check-facts.d.ts +126 -0
- package/dist/check-facts.js +318 -0
- package/dist/check-failure.d.ts +22 -3
- package/dist/check-failure.js +57 -13
- package/dist/check-levels-ui.d.ts +4 -0
- package/dist/check-levels-ui.js +10 -1
- package/dist/check-load.d.ts +85 -0
- package/dist/check-load.js +147 -0
- package/dist/check-only-standing.d.ts +36 -0
- package/dist/check-only-standing.js +39 -0
- package/dist/check-only.d.ts +15 -0
- package/dist/check-only.js +99 -0
- package/dist/check-report.d.ts +7 -0
- package/dist/check-report.js +4 -3
- package/dist/check-stamp.d.ts +46 -0
- package/dist/check-stamp.js +93 -0
- package/dist/checkout-cleanup.d.ts +36 -5
- package/dist/checkout-cleanup.js +166 -14
- package/dist/cli-help.d.ts +4 -1
- package/dist/cli-help.js +47 -30
- package/dist/cli.js +16 -1
- package/dist/code-extract.d.ts +44 -0
- package/dist/code-extract.js +88 -0
- package/dist/code-map-acts.d.ts +24 -0
- package/dist/code-map-acts.js +53 -0
- package/dist/code-map-admission.d.ts +52 -0
- package/dist/code-map-admission.js +295 -0
- package/dist/code-map-build.d.ts +63 -0
- package/dist/code-map-build.js +415 -0
- package/dist/code-map-cli.d.ts +153 -0
- package/dist/code-map-cli.js +103 -0
- package/dist/code-map-main.d.ts +1 -0
- package/dist/code-map-main.js +48 -0
- package/dist/code-map-read.d.ts +106 -0
- package/dist/code-map-read.js +171 -0
- package/dist/code-map-report.d.ts +37 -0
- package/dist/code-map-report.js +95 -0
- package/dist/code-map-shape.d.ts +172 -0
- package/dist/code-map-shape.js +327 -0
- package/dist/code-map-store.d.ts +136 -0
- package/dist/code-map-store.js +233 -0
- package/dist/coding-provider.d.ts +2 -0
- package/dist/coding-provider.js +3 -2
- package/dist/coding-update.js +3 -2
- package/dist/coding-workspace.js +4 -4
- package/dist/console-chrome.js +2 -2
- package/dist/containment.d.ts +5 -0
- package/dist/containment.js +14 -1
- package/dist/contracts/chat-actions.d.ts +9 -0
- package/dist/contracts/chat-actions.js +2 -0
- package/dist/contracts/cli.d.ts +52 -0
- package/dist/contracts/cli.js +4 -0
- package/dist/contracts/console-api.d.ts +12 -5
- package/dist/contracts/console-api.js +12 -5
- package/dist/contracts/gateway-tools.d.ts +17 -0
- package/dist/contracts/gateway-tools.js +3 -1
- package/dist/contracts/lead-tools.d.ts +61 -0
- package/dist/contracts/lead-tools.js +10 -1
- package/dist/contracts/project-knowledge.d.ts +38 -5
- package/dist/contracts/project-knowledge.js +11 -5
- package/dist/contracts/project-memory.d.ts +3 -1
- package/dist/contracts/project-memory.js +3 -2
- package/dist/contracts/release-check.d.ts +25 -1
- package/dist/contracts/release-check.js +19 -0
- package/dist/contracts/storage.d.ts +14 -0
- package/dist/contracts/storage.js +3 -1
- package/dist/contracts/store-json.d.ts +2 -0
- package/dist/contracts/youiyoux-request.d.ts +1 -1
- package/dist/contracts/youiyoux-request.js +1 -1
- package/dist/desktop-access.js +6 -2
- package/dist/desktop-update.d.ts +2 -2
- package/dist/desktop-update.js +13 -9
- package/dist/dispatch.js +18 -15
- package/dist/dispose.js +8 -2
- package/dist/easy-start.js +2 -3
- package/dist/eval-hooks.d.ts +55 -0
- package/dist/eval-hooks.js +227 -4
- package/dist/evals.js +2 -1
- package/dist/fast-start.d.ts +136 -0
- package/dist/fast-start.js +431 -0
- package/dist/fast-terms.d.ts +9 -0
- package/dist/fast-terms.js +23 -0
- package/dist/fenced-check-coverage.d.ts +62 -0
- package/dist/fenced-check-coverage.js +54 -0
- package/dist/first-run.js +1 -1
- package/dist/flaky-ci.d.ts +31 -0
- package/dist/flaky-ci.js +149 -0
- package/dist/flaky-cli.d.ts +60 -0
- package/dist/flaky-cli.js +60 -0
- package/dist/flaky.d.ts +83 -0
- package/dist/flaky.js +264 -0
- package/dist/flow-card-task.js +3 -0
- package/dist/flow-triggers.d.ts +1 -1
- package/dist/flow-triggers.js +1 -1
- package/dist/github-import.d.ts +62 -0
- package/dist/github-import.js +146 -0
- package/dist/guides.js +15 -10
- package/dist/invoke.d.ts +5 -1
- package/dist/invoke.js +49 -14
- package/dist/job-object-helper.ps1 +10 -3
- package/dist/keymap.d.ts +41 -0
- package/dist/keymap.js +51 -0
- package/dist/knowledge-cli.js +1 -1
- package/dist/knowledge-ui.d.ts +21 -4
- package/dist/knowledge-ui.js +75 -7
- package/dist/laptop-background.d.ts +16 -0
- package/dist/laptop-background.js +108 -2
- package/dist/lead-context.js +1 -1
- package/dist/lead-contract.d.ts +1 -1
- package/dist/lead-contract.js +2 -2
- package/dist/lead-progress.js +3 -0
- package/dist/lead-status.d.ts +2 -0
- package/dist/lead-status.js +6 -2
- package/dist/lead-tools.js +51 -2
- package/dist/lead.js +1 -1
- package/dist/lesson-notes.d.ts +81 -0
- package/dist/lesson-notes.js +336 -0
- package/dist/limits-ui.js +1 -1
- package/dist/local-owner.d.ts +1 -1
- package/dist/local-owner.js +11 -0
- package/dist/machine-check.d.ts +16 -0
- package/dist/machine-check.js +26 -0
- package/dist/machine-identity.d.ts +75 -0
- package/dist/machine-identity.js +170 -0
- package/dist/mcp-core.js +11 -1
- package/dist/memory-cli.d.ts +300 -1
- package/dist/memory-cli.js +62 -9
- package/dist/memory-pass.d.ts +16 -0
- package/dist/memory-pass.js +67 -3
- package/dist/memory-tidy.d.ts +181 -0
- package/dist/memory-tidy.js +523 -0
- package/dist/memory-use.d.ts +35 -0
- package/dist/memory-use.js +109 -0
- package/dist/merge-checklist.d.ts +55 -0
- package/dist/merge-checklist.js +182 -0
- package/dist/needs-you.d.ts +32 -13
- package/dist/needs-you.js +108 -49
- package/dist/nightly-github.d.ts +2 -0
- package/dist/nightly-github.js +4 -4
- package/dist/operate-remote-arguments.js +6 -1
- package/dist/operate-remote.js +3 -1
- package/dist/operate.d.ts +6 -1
- package/dist/operate.js +540 -70
- package/dist/phase-routing.js +2 -2
- package/dist/plain-schema.d.ts +23 -0
- package/dist/plain-schema.js +94 -0
- package/dist/plain-words.d.ts +20 -0
- package/dist/plain-words.js +66 -0
- package/dist/planner.js +2 -1
- package/dist/process-liveness.d.ts +2 -0
- package/dist/process-liveness.js +61 -17
- package/dist/project-delete-ui.d.ts +7 -2
- package/dist/project-delete-ui.js +12 -1
- package/dist/project-delete.js +13 -2
- package/dist/project-knowledge.d.ts +18 -11
- package/dist/project-knowledge.js +39 -9
- package/dist/project-learning.d.ts +2 -1
- package/dist/project-learning.js +15 -45
- package/dist/project-memory.d.ts +5 -0
- package/dist/project-memory.js +22 -2
- package/dist/proof.js +30 -5
- package/dist/provider-auth.js +1 -1
- package/dist/publish.js +9 -1
- package/dist/pull-request-flow.js +5 -0
- package/dist/push.d.ts +26 -3
- package/dist/push.js +55 -15
- package/dist/release-adapters.d.ts +9 -0
- package/dist/release-adapters.js +87 -1
- package/dist/release-approval.d.ts +191 -0
- package/dist/release-approval.js +382 -0
- package/dist/release-auto.js +26 -17
- package/dist/release-card.d.ts +74 -6
- package/dist/release-card.js +179 -23
- package/dist/release-carry.d.ts +7 -2
- package/dist/release-carry.js +39 -9
- package/dist/release-cli.d.ts +4 -0
- package/dist/release-cli.js +38 -23
- package/dist/release-coverage.js +1 -1
- package/dist/release-next.d.ts +48 -0
- package/dist/release-next.js +161 -0
- package/dist/release.d.ts +50 -1
- package/dist/release.js +118 -11
- package/dist/remote-destination.d.ts +36 -0
- package/dist/remote-destination.js +217 -0
- package/dist/remote-main.d.ts +48 -0
- package/dist/remote-main.js +84 -5
- package/dist/repository-context.d.ts +3 -2
- package/dist/repository-context.js +112 -70
- package/dist/restart-certification.d.ts +1 -0
- package/dist/restart-certification.js +8 -6
- package/dist/result-acts.d.ts +29 -21
- package/dist/result-acts.js +36 -25
- package/dist/result-follow-ups.d.ts +2 -2
- package/dist/result-follow-ups.js +18 -4
- package/dist/result-review.d.ts +1 -1
- package/dist/route-change-check.d.ts +4 -0
- package/dist/route-change-check.js +13 -0
- package/dist/route-change.d.ts +22 -0
- package/dist/route-change.js +89 -0
- package/dist/route-hooks.js +3 -2
- package/dist/route-results.d.ts +6 -1
- package/dist/route-results.js +14 -5
- package/dist/run-closeout.d.ts +1 -1
- package/dist/run-closeout.js +7 -5
- package/dist/runner-process.js +3 -2
- package/dist/runner.js +2 -1
- package/dist/scope.d.ts +6 -1
- package/dist/scope.js +22 -9
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +24 -18
- package/dist/server/chat.js +14 -12
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +26 -35
- package/dist/server/console.css +23 -0
- package/dist/server/guards.js +4 -0
- package/dist/server/home.d.ts +6 -0
- package/dist/server/home.js +21 -13
- package/dist/server/http.js +1 -1
- package/dist/server/kit.js +1 -1
- package/dist/server/merge-ready-view.d.ts +10 -0
- package/dist/server/merge-ready-view.js +30 -0
- package/dist/server/pages-assets.d.ts +1 -0
- package/dist/server/pages-assets.js +8 -5
- package/dist/server/pages.js +103 -82
- package/dist/server/render-chat.js +2 -2
- package/dist/server/render-code-map.d.ts +30 -0
- package/dist/server/render-code-map.js +137 -0
- package/dist/server/render-flaky.d.ts +20 -0
- package/dist/server/render-flaky.js +59 -0
- package/dist/server/render-history.js +1 -1
- package/dist/server/render-new-task.d.ts +26 -1
- package/dist/server/render-new-task.js +30 -5
- package/dist/server/render-pages.d.ts +1 -175
- package/dist/server/render-pages.js +38 -313
- package/dist/server/render-projects.d.ts +2 -1
- package/dist/server/render-projects.js +9 -7
- package/dist/server/render-release.d.ts +19 -0
- package/dist/server/render-release.js +84 -0
- package/dist/server/render-results.d.ts +2 -2
- package/dist/server/render-results.js +71 -73
- package/dist/server/render-tasks.d.ts +13 -2
- package/dist/server/render-tasks.js +156 -139
- package/dist/server/retired-routes.d.ts +17 -0
- package/dist/server/retired-routes.js +53 -0
- package/dist/server/route-table.d.ts +4 -1
- package/dist/server/route-table.js +52 -13
- package/dist/server/settings.js +129 -22
- package/dist/server/task-views.js +61 -26
- package/dist/server/tasks.d.ts +17 -6
- package/dist/server/tasks.js +273 -338
- package/dist/server/welcome.js +1 -1
- package/dist/settings-groups-ui.js +1 -1
- package/dist/setup-guide.d.ts +2 -2
- package/dist/stop-proof.d.ts +67 -0
- package/dist/stop-proof.js +295 -0
- package/dist/storage-sweep-process.js +1 -1
- package/dist/storage-sweep.d.ts +5 -3
- package/dist/storage-sweep.js +30 -9
- package/dist/storage-ui.js +28 -17
- package/dist/storage.d.ts +27 -2
- package/dist/storage.js +39 -2
- package/dist/store.d.ts +148 -34
- package/dist/store.js +626 -177
- package/dist/surface.js +46 -5
- package/dist/sweep-quarantine.d.ts +1 -0
- package/dist/sweep-quarantine.js +6 -4
- package/dist/tamper-scan.d.ts +4 -0
- package/dist/tamper-scan.js +90 -0
- package/dist/task-aliases.d.ts +5 -0
- package/dist/task-aliases.js +9 -0
- package/dist/task-control.js +4 -1
- package/dist/task-outcome-cli.d.ts +2 -0
- package/dist/task-outcome-cli.js +36 -17
- package/dist/task-sizing.d.ts +5 -0
- package/dist/task-sizing.js +2 -0
- package/dist/task-source.d.ts +7 -0
- package/dist/task-source.js +14 -0
- package/dist/task-status.d.ts +9 -0
- package/dist/task-status.js +40 -49
- package/dist/teardown-git.d.ts +16 -0
- package/dist/teardown-git.js +63 -5
- package/dist/telegram-progress.js +5 -3
- package/dist/telegram-status.js +6 -4
- package/dist/test-infra.d.ts +8 -0
- package/dist/test-infra.js +18 -0
- package/dist/tidy.js +1 -1
- package/dist/toolroll-update-ui.js +2 -2
- package/dist/toolroll-update.js +18 -5
- package/dist/tui-contract.d.ts +24 -0
- package/dist/tui-source.js +67 -8
- package/dist/tui.js +51 -51
- package/dist/vanished-retry.d.ts +30 -0
- package/dist/vanished-retry.js +174 -0
- package/dist/work-index.d.ts +2 -0
- package/dist/work-index.js +82 -70
- package/dist/work-summary.d.ts +2 -2
- package/dist/work-summary.js +2 -0
- package/dist/workspace-ui.d.ts +4 -2
- package/dist/workspace-ui.js +28 -22
- package/dist/worktree.d.ts +12 -3
- package/dist/worktree.js +68 -13
- package/package.json +1 -1
- package/dist/server/render-next.d.ts +0 -63
- package/dist/server/render-next.js +0 -109
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type Store } from "./store.js";
|
|
2
|
+
/** The automatic-retry limits, in one place. */
|
|
3
|
+
export declare const RETRY_LIMIT = 3;
|
|
4
|
+
export declare const RETRY_SPEND_TIMES = 2;
|
|
5
|
+
/** claim.ts MAX_STRIKES (store.ts loads this file, so the claim road isn't imported here; a test pins them equal). */
|
|
6
|
+
export declare const VANISHED_MAX_STRIKES = 3;
|
|
7
|
+
/** What recovery did with a task: back in the queue, waiting for its old process to be proven stopped, stopped for a
|
|
8
|
+
* person, or nothing at all (not a task of ours to move). */
|
|
9
|
+
export type VanishedAnswer = "queued" | "waiting" | "stopped" | "none";
|
|
10
|
+
export type RetryStop = "no-budget" | "attempts" | "spend";
|
|
11
|
+
/**
|
|
12
|
+
* The one recovery decision, once per run. The caller has proved the runner gone; this re-reads everything else. A
|
|
13
|
+
* task a tick claimed never left `queued`, so releasing its lease alone would dispatch it again: what stops a retry here
|
|
14
|
+
* is the state or hold this writes, in this transaction. Safe to call again: the run is charged once, decided once.
|
|
15
|
+
*/
|
|
16
|
+
export declare function settleVanished(store: Store, taskRef: number, now: Date): VanishedAnswer;
|
|
17
|
+
/** Each sweep: a charged run whose old process wasn't yet proven stopped gets its decision now. */
|
|
18
|
+
export declare function retryVanishedWhenStopped(store: Store, now: Date): number;
|
|
19
|
+
/** A person acted, so a new episode starts. The strikes are theirs to clear on their own road. */
|
|
20
|
+
export declare function clearRetryEpisode(store: Store, taskRef: number): void;
|
|
21
|
+
/** Why Toolroll stopped retrying this task, or null: it reads as a need until a person acts. */
|
|
22
|
+
export declare function vanishedStopOf(store: Store, taskRef: number): {
|
|
23
|
+
why: RetryStop;
|
|
24
|
+
run: number;
|
|
25
|
+
} | null;
|
|
26
|
+
/** The need's words: the first stop on a plan with no budget, or a limit reached. */
|
|
27
|
+
export declare function vanishedStopSentence(stop: {
|
|
28
|
+
why: RetryStop;
|
|
29
|
+
run: number | null;
|
|
30
|
+
}): string;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A build that vanished: its process died with its runner, so no one settled it (docs/plans/plain-toolroll.md P2.1).
|
|
3
|
+
*
|
|
4
|
+
* Every recovery road (a dead runner, a takeover, a superseded watch) ends here, in its own transaction. The vanished
|
|
5
|
+
* attempt is charged one strike, once, whoever recovers it: `vanished_charge` is keyed by the run, so a second recoverer
|
|
6
|
+
* or a later pass charges nothing, and restarting can no longer evade MAX_STRIKES.
|
|
7
|
+
*
|
|
8
|
+
* Toolroll retries by itself only inside an episode (`retry_episode`: the automatic retries and reserved spend since the
|
|
9
|
+
* last person act), at most RETRY_LIMIT retries and RETRY_SPEND_TIMES the plan's budget, each claim reserving its whole
|
|
10
|
+
* budget against that ceiling. A vanished attempt's spend is unknown, so its reservation is never given back. A plan
|
|
11
|
+
* with no budget bounds nothing and gets no automatic retry. A prepared candidate runs a machine check and no agent, so
|
|
12
|
+
* it spends nothing and needs no budget; it still counts its attempts.
|
|
13
|
+
*
|
|
14
|
+
* Stop, hold, archive and approval standing are re-read here, and each keeps the retry from dispatching: the task stays
|
|
15
|
+
* in the queue where that gate holds it, as a person's stop always has, reserving nothing, until a person acts. A
|
|
16
|
+
* process not yet proven stopped holds the task until it is (retryVanishedWhenStopped, every sweep): the retry never
|
|
17
|
+
* starts beside the old attempt.
|
|
18
|
+
*/
|
|
19
|
+
import { authorityStandsSql } from "./approval-authority.js";
|
|
20
|
+
import { stopFactOf } from "./store.js";
|
|
21
|
+
import { refArchived } from "./task-archive.js";
|
|
22
|
+
/** The automatic-retry limits, in one place. */
|
|
23
|
+
export const RETRY_LIMIT = 3;
|
|
24
|
+
export const RETRY_SPEND_TIMES = 2;
|
|
25
|
+
/** claim.ts MAX_STRIKES (store.ts loads this file, so the claim road isn't imported here; a test pins them equal). */
|
|
26
|
+
export const VANISHED_MAX_STRIKES = 3;
|
|
27
|
+
/** The newest attempt that spends: repair children ride their parent's attempt, and a reviewer has no task claim. */
|
|
28
|
+
function attemptOf(store, taskRef) {
|
|
29
|
+
const row = store.handle.prepare("SELECT id, role, outcome, reason FROM run WHERE task_ref = ? AND role IN ('builder', 'scout', 'planner') ORDER BY id DESC LIMIT 1").get(taskRef);
|
|
30
|
+
// A planner spends no approved budget and has its own strikes: it goes back to the queue as it always has.
|
|
31
|
+
if (row === undefined || String(row["role"]) === "planner")
|
|
32
|
+
return null;
|
|
33
|
+
return { id: Number(row["id"]), outcome: row["outcome"] === null ? null : String(row["outcome"]), reason: row["reason"] === null ? null : String(row["reason"]) };
|
|
34
|
+
}
|
|
35
|
+
/** Whether any run of this task still has a process no record proves stopped. */
|
|
36
|
+
function processUnsettled(store, taskRef) {
|
|
37
|
+
for (const run of store.runsFor(taskRef)) {
|
|
38
|
+
const fact = stopFactOf(store, run.id);
|
|
39
|
+
if (fact !== null && fact.kind !== "open")
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
/** dispatch.ts scopeApprovedForDispatch, read here because store.ts loads this file. */
|
|
45
|
+
function approvalStands(store, taskRef, now) {
|
|
46
|
+
const stamp = now.toISOString();
|
|
47
|
+
return store.handle.prepare(`SELECT 1 AS hit FROM task_scope JOIN task_ref ON task_ref.id = ? AND task_scope.task_id = task_ref.external_id
|
|
48
|
+
WHERE task_scope.approved_digest = task_scope.digest AND task_scope.approved_at IS NOT NULL AND ${authorityStandsSql("task_scope", "task_ref.repo", "?")}`).get(taskRef, stamp, stamp, stamp, stamp) !== undefined;
|
|
49
|
+
}
|
|
50
|
+
function requeue(store, taskId, now) {
|
|
51
|
+
return Number(store.handle.prepare("UPDATE task SET state = 'queued', updated_at = ? WHERE id = ? AND state = 'running'").run(now.toISOString(), taskId).changes) > 0;
|
|
52
|
+
}
|
|
53
|
+
function episodeOf(store, taskRef) {
|
|
54
|
+
const row = store.handle.prepare("SELECT retries, reserved_microusd FROM retry_episode WHERE task_ref = ?").get(taskRef);
|
|
55
|
+
return row === undefined ? null : { retries: Number(row["retries"]), reservedMicrousd: Number(row["reserved_microusd"]) };
|
|
56
|
+
}
|
|
57
|
+
function stopFor(store, input) {
|
|
58
|
+
const stamp = input.now.toISOString();
|
|
59
|
+
store.handle.prepare(`INSERT INTO retry_episode (task_ref, retries, reserved_microusd, stopped, stopped_run, started_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
60
|
+
ON CONFLICT (task_ref) DO UPDATE SET stopped = excluded.stopped, stopped_run = excluded.stopped_run, reserved_microusd = excluded.reserved_microusd, updated_at = excluded.updated_at`)
|
|
61
|
+
.run(input.taskRef, input.retries, input.reservedMicrousd, input.why, input.run, stamp, stamp);
|
|
62
|
+
store.handle.prepare("UPDATE task SET state = 'failed', updated_at = ? WHERE id = ? AND state IN ('running', 'queued')").run(stamp, input.taskId);
|
|
63
|
+
store.enqueueNotification({
|
|
64
|
+
source: { run: input.run },
|
|
65
|
+
dedupeKey: `vanished:${input.run}`,
|
|
66
|
+
kind: "build-failed",
|
|
67
|
+
pushClass: "attention",
|
|
68
|
+
link: `/r/${input.run}`,
|
|
69
|
+
subject: `${input.taskId}: ${vanishedStopSentence({ why: input.why, run: input.run })}`,
|
|
70
|
+
body: "Open the task to retry it or change the plan.",
|
|
71
|
+
}, input.now);
|
|
72
|
+
return "stopped";
|
|
73
|
+
}
|
|
74
|
+
/** The custody wait's hold: nothing starts beside a process no record proves stopped, and it never lapses by itself. */
|
|
75
|
+
const waitHold = (taskRef) => `vanished:${taskRef}`;
|
|
76
|
+
/**
|
|
77
|
+
* The one recovery decision, once per run. The caller has proved the runner gone; this re-reads everything else. A
|
|
78
|
+
* task a tick claimed never left `queued`, so releasing its lease alone would dispatch it again: what stops a retry here
|
|
79
|
+
* is the state or hold this writes, in this transaction. Safe to call again: the run is charged once, decided once.
|
|
80
|
+
*/
|
|
81
|
+
export function settleVanished(store, taskRef, now) {
|
|
82
|
+
return store.transact(() => {
|
|
83
|
+
if (store.currentLiveLease(taskRef, now) !== null)
|
|
84
|
+
return "none";
|
|
85
|
+
const ref = store.refForId(taskRef);
|
|
86
|
+
if (ref === null || ref.backend !== "built-in")
|
|
87
|
+
return "none";
|
|
88
|
+
const taskId = ref.externalId;
|
|
89
|
+
const task = store.getTask(taskId);
|
|
90
|
+
if (task === null)
|
|
91
|
+
return "none";
|
|
92
|
+
const attempt = attemptOf(store, taskRef);
|
|
93
|
+
// No attempt, or one a real outcome already settled: nothing vanished here, and recovery only unsticks the task.
|
|
94
|
+
if (attempt === null || (attempt.outcome !== null && attempt.reason !== "interrupted"))
|
|
95
|
+
return requeue(store, taskId, now) ? "queued" : "none";
|
|
96
|
+
// Still open: its recoverer closes it and comes back here, in the same transaction. Nothing is decided on a run that
|
|
97
|
+
// may not be over.
|
|
98
|
+
if (attempt.outcome === null)
|
|
99
|
+
return requeue(store, taskId, now) ? "queued" : "none";
|
|
100
|
+
// A person's stop isn't a vanish: nothing is charged, and its hold keeps the task where the stop always has.
|
|
101
|
+
if (store.handle.prepare("SELECT 1 AS hit FROM run_stop WHERE run = ?").get(attempt.id) !== undefined)
|
|
102
|
+
return requeue(store, taskId, now) ? "queued" : "none";
|
|
103
|
+
const stamp = now.toISOString();
|
|
104
|
+
const charged = store.handle.prepare("INSERT INTO vanished_charge (run, task_ref, charged_at) VALUES (?, ?, ?) ON CONFLICT (run) DO NOTHING").run(attempt.id, taskRef, stamp);
|
|
105
|
+
const strikes = Number(charged.changes) > 0 ? store.addStrike(taskRef) : Number(store.handle.prepare("SELECT strikes FROM task_ref WHERE id = ?").get(taskRef)?.["strikes"] ?? 0);
|
|
106
|
+
if (store.handle.prepare("SELECT settled_at FROM vanished_charge WHERE run = ?").get(attempt.id)?.["settled_at"] != null)
|
|
107
|
+
return "none";
|
|
108
|
+
const settled = () => {
|
|
109
|
+
store.handle.prepare("UPDATE vanished_charge SET settled_at = ? WHERE run = ?").run(stamp, attempt.id);
|
|
110
|
+
store.releaseOwnedHold("backoff", waitHold(taskRef));
|
|
111
|
+
};
|
|
112
|
+
if (task.state !== "queued" && task.state !== "running") {
|
|
113
|
+
settled();
|
|
114
|
+
return "none";
|
|
115
|
+
}
|
|
116
|
+
// Blocked by something a person holds: back to the queue, where that gate keeps it. Nothing is reserved, because
|
|
117
|
+
// nothing starts until that person acts.
|
|
118
|
+
const held = store.handle.prepare("SELECT 1 AS hit FROM hold WHERE task_ref = ? AND owner_kind <> 'backoff' AND (until IS NULL OR until > ?) LIMIT 1").get(taskRef, stamp);
|
|
119
|
+
if (held !== undefined || refArchived(store, taskRef) || !approvalStands(store, taskRef, now)) {
|
|
120
|
+
settled();
|
|
121
|
+
return requeue(store, taskId, now) || task.state === "queued" ? "queued" : "none";
|
|
122
|
+
}
|
|
123
|
+
if (processUnsettled(store, taskRef)) {
|
|
124
|
+
requeue(store, taskId, now);
|
|
125
|
+
store.holdOwned({ taskRef, ownerKind: "backoff", ownerId: waitHold(taskRef), reason: `Waiting: Toolroll is making sure build #${attempt.id} stopped.`, until: null }, now);
|
|
126
|
+
return "waiting";
|
|
127
|
+
}
|
|
128
|
+
const scope = store.getScope(taskId);
|
|
129
|
+
const budget = scope?.budgetMicrousd ?? null;
|
|
130
|
+
const prepared = (scope?.candidate ?? null) !== null;
|
|
131
|
+
const episode = episodeOf(store, taskRef) ?? { retries: 0, reservedMicrousd: budget ?? 0 };
|
|
132
|
+
const stop = (why) => { settled(); return stopFor(store, { taskRef, taskId, run: attempt.id, why, now, reservedMicrousd: episode.reservedMicrousd, retries: episode.retries }); };
|
|
133
|
+
if (budget === null && !prepared)
|
|
134
|
+
return stop("no-budget");
|
|
135
|
+
if (strikes >= VANISHED_MAX_STRIKES || episode.retries >= RETRY_LIMIT)
|
|
136
|
+
return stop("attempts");
|
|
137
|
+
const reserved = episode.reservedMicrousd + (budget ?? 0);
|
|
138
|
+
if (budget !== null && reserved > budget * RETRY_SPEND_TIMES)
|
|
139
|
+
return stop("spend");
|
|
140
|
+
store.handle.prepare(`INSERT INTO retry_episode (task_ref, retries, reserved_microusd, started_at, updated_at) VALUES (?, 1, ?, ?, ?)
|
|
141
|
+
ON CONFLICT (task_ref) DO UPDATE SET retries = retries + 1, reserved_microusd = excluded.reserved_microusd, stopped = NULL, stopped_run = NULL, updated_at = excluded.updated_at`)
|
|
142
|
+
.run(taskRef, reserved, stamp, stamp);
|
|
143
|
+
settled();
|
|
144
|
+
requeue(store, taskId, now);
|
|
145
|
+
return "queued";
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/** Each sweep: a charged run whose old process wasn't yet proven stopped gets its decision now. */
|
|
149
|
+
export function retryVanishedWhenStopped(store, now) {
|
|
150
|
+
const waiting = store.handle.prepare("SELECT DISTINCT task_ref FROM vanished_charge WHERE settled_at IS NULL").all();
|
|
151
|
+
let moved = 0;
|
|
152
|
+
for (const row of waiting) {
|
|
153
|
+
const answer = settleVanished(store, Number(row["task_ref"]), now);
|
|
154
|
+
if (answer === "queued" || answer === "stopped")
|
|
155
|
+
moved++;
|
|
156
|
+
}
|
|
157
|
+
if (moved > 0)
|
|
158
|
+
store.bumpWake();
|
|
159
|
+
return moved;
|
|
160
|
+
}
|
|
161
|
+
/** A person acted, so a new episode starts. The strikes are theirs to clear on their own road. */
|
|
162
|
+
export function clearRetryEpisode(store, taskRef) {
|
|
163
|
+
store.handle.prepare("DELETE FROM retry_episode WHERE task_ref = ?").run(taskRef);
|
|
164
|
+
}
|
|
165
|
+
/** Why Toolroll stopped retrying this task, or null: it reads as a need until a person acts. */
|
|
166
|
+
export function vanishedStopOf(store, taskRef) {
|
|
167
|
+
const row = store.handle.prepare("SELECT stopped, stopped_run FROM retry_episode WHERE task_ref = ? AND stopped IS NOT NULL").get(taskRef);
|
|
168
|
+
return row === undefined ? null : { why: String(row["stopped"]), run: Number(row["stopped_run"]) };
|
|
169
|
+
}
|
|
170
|
+
/** The need's words: the first stop on a plan with no budget, or a limit reached. */
|
|
171
|
+
export function vanishedStopSentence(stop) {
|
|
172
|
+
const build = stop.run === null ? "This build" : `Build #${stop.run}`;
|
|
173
|
+
return stop.why === "no-budget" ? `${build} stopped unexpectedly. Retry it.` : `${build} keeps stopping. Retry, or change the plan.`;
|
|
174
|
+
}
|
package/dist/work-index.d.ts
CHANGED
|
@@ -77,6 +77,8 @@ export type WorkProjectCounts = {
|
|
|
77
77
|
};
|
|
78
78
|
export declare const WORK_INDEX_PAGE_LIMIT = 40;
|
|
79
79
|
export declare const WORK_INDEX_MAX_LIMIT = 100;
|
|
80
|
+
/** The index codes that wait on nobody: finished or running work, and every wait (needs-you.ts CODE_READING). */
|
|
81
|
+
export declare const WAITING_CODES: readonly string[];
|
|
80
82
|
/** A code's ask: a saved result is Review (unless its build may still run), otherwise its need's ask. */
|
|
81
83
|
export declare function askOfCode(code: string, custody?: boolean): Ask;
|
|
82
84
|
export declare class WorkIndexCursorError extends Error {
|
package/dist/work-index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* not verify artifacts or authorize task operations; a build Toolroll can't
|
|
3
3
|
* yet confirm stopped is read through the store's own read-only probe. */
|
|
4
4
|
import { authorityStandsSql } from "./approval-authority.js";
|
|
5
|
+
import { vanishedStopSentence } from "./vanished-retry.js";
|
|
5
6
|
import { leadNameOf } from './lead-identity.js';
|
|
6
7
|
import { createHash } from 'node:crypto';
|
|
7
8
|
import { existsSync } from 'node:fs';
|
|
@@ -18,14 +19,15 @@ import { isCheckLevel, requiredCheckCommandFor } from './check-levels.js';
|
|
|
18
19
|
import { MARKER } from './worktree.js';
|
|
19
20
|
import { withFollowUps } from './result-follow-ups.js';
|
|
20
21
|
import { LANDED_ACTOR, landedReasonOf } from './result-completion.js';
|
|
21
|
-
import { ASKS, NEED_ASK, NEEDS, askChipOf, failedAttemptSentence, processNeedOf, resultHoldUpSentence } from './needs-you.js';
|
|
22
|
+
import { ASKS, CODE_READING, NEED_ASK, NEEDS, askChipOf, failedAttemptSentence, isWaitCode, processNeedOf, resultHoldUpSentence } from './needs-you.js';
|
|
23
|
+
import { retryWhenWords } from './dispatch.js';
|
|
22
24
|
import { LEAD_CLAIM_SQL, LEAD_IDLE_MS } from './lead-voice.js';
|
|
23
25
|
export const WORK_INDEX_PAGE_LIMIT = 40;
|
|
24
26
|
export const WORK_INDEX_MAX_LIMIT = 100;
|
|
25
|
-
const EMPTY = { all: 0, 'needs-you': 0, running: 0, queued: 0, completed: 0, archived: 0 };
|
|
27
|
+
const EMPTY = { all: 0, 'needs-you': 0, ready: 0, running: 0, queued: 0, completed: 0, archived: 0 };
|
|
26
28
|
const NO_GROUPS = { decide: 0, review: 0, unblock: 0, building: 0, rest: 0 };
|
|
27
29
|
const FAMILY_PROBLEM = 'Task history is unavailable or incomplete. This task is shown separately.';
|
|
28
|
-
const VIEWS = ['all', 'needs-you', 'running', 'queued', 'completed', 'archived'];
|
|
30
|
+
const VIEWS = ['all', 'needs-you', 'ready', 'running', 'queued', 'completed', 'archived'];
|
|
29
31
|
/** The family's current version is put away (v145): it waits on nobody and only the Archived view lists it. */
|
|
30
32
|
const ARCHIVED = `EXISTS(SELECT 1 FROM task_archive ta WHERE ta.task_ref=c.ref_id)`;
|
|
31
33
|
const n = (row, key) => Number(row[key] ?? 0);
|
|
@@ -39,14 +41,22 @@ const AGENT_ASK = `(SELECT q.id FROM agent_question q JOIN run qr ON qr.id=q.run
|
|
|
39
41
|
* MATERIALIZED prevents counts + page selection repeating correlated reads. */
|
|
40
42
|
/** A gate note counts only while its pause is on one of the task's current providers. */
|
|
41
43
|
const GATE_PAUSE_ON_TASK = pauseOnTaskProviders('p', 'c.ref_id');
|
|
42
|
-
/** Index codes that wait on a person, as their need
|
|
43
|
-
const INDEX_NEED =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/** Every index code that can wait on a person. */
|
|
44
|
+
/** Index codes that wait on a person, as their need, where the index reads one the dispatch codes don't. */
|
|
45
|
+
const INDEX_NEED = Object.fromEntries(['earlier-active', 'process-needs-attention', 'invalid-scope', 'decision-queue', 'history-problem']
|
|
46
|
+
.map(code => [code, CODE_READING[code].need]));
|
|
47
|
+
/** Every index code that can wait on a person. A wait (stopping, waiting its turn, a retry later) is never one. */
|
|
47
48
|
const PERSON_CODES = ['history-problem', 'earlier-active', 'ready-to-check', 'waiting-decision', 'decision-queue', 'result-needs-attention',
|
|
48
|
-
'process-needs-attention', 'failed', 'vanished-run', '
|
|
49
|
+
'process-needs-attention', 'failed', 'vanished-run', 'stopped', 'held', 'waiting-incident', 'terminal-dependency', 'needs-project',
|
|
49
50
|
'needs-scope', 'invalid-scope', 'needs-agent-profile', 'needs-approval', 'missing-requirement', 'no-worker-registered', 'no-worker-online', 'signed-out'];
|
|
51
|
+
/** The index codes that wait on nobody: finished or running work, and every wait (needs-you.ts CODE_READING). */
|
|
52
|
+
export const WAITING_CODES = ['cancelled', 'complete', 'running', 'queued',
|
|
53
|
+
...['stopping', 'updating', 'retry-scheduled', 'waiting-dependency', 'worker-at-capacity', 'planning-ready', 'scouting-ready'].filter(code => isWaitCode(code))];
|
|
54
|
+
const WAITING_SQL = WAITING_CODES.map(code => `'${code}'`).join(',');
|
|
55
|
+
/** The words on a state's own act (a need's act wears its need's label). */
|
|
56
|
+
const STATE_ACT_LABEL = {
|
|
57
|
+
'ready-to-check': 'Open result', complete: 'Open result', cancelled: 'View task details', failed: 'Review and retry', 'waiting-incident': 'Review and retry',
|
|
58
|
+
stopped: 'Review pause', held: 'Review hold', 'result-needs-attention': 'Open the build', 'process-needs-attention': 'Open the build',
|
|
59
|
+
};
|
|
50
60
|
/** A code's ask: a saved result is Review (unless its build may still run), otherwise its need's ask. */
|
|
51
61
|
export function askOfCode(code, custody = false) {
|
|
52
62
|
if (custody)
|
|
@@ -129,6 +139,8 @@ const PROJECTION = `WITH RECURSIVE admitted AS MATERIALIZED (
|
|
|
129
139
|
(SELECT id FROM run WHERE task_ref=c.ref_id AND lease_id=c.live_lease AND outcome IS NULL ORDER BY id DESC LIMIT 1) live_run,
|
|
130
140
|
h.owner_kind hold_kind,h.reason hold_reason,h.until hold_until,
|
|
131
141
|
sc.task_id scope_id,sc.digest,sc.approved_at,sc.proposed_at,sc.profile_state,
|
|
142
|
+
-- A prepared candidate: the machine checks out that commit and runs its check; no agent runs (machine-check.ts).
|
|
143
|
+
(typeof(sc.candidate)='text' AND sc.candidate<>'') machine_check,
|
|
132
144
|
-- The pause the dispatch gate itself left this task waiting on (the provider it resolved, pins,
|
|
133
145
|
-- configuration and planners included) first; the approved terms answer before any gate has looked.
|
|
134
146
|
EXISTS(SELECT 1 FROM provider_auth_pause p WHERE p.id=c.auth_wait_pause AND p.lifted_at IS NULL AND ${GATE_PAUSE_ON_TASK}) gate_signed_out,
|
|
@@ -239,11 +251,11 @@ const PROJECTION = `WITH RECURSIVE admitted AS MATERIALIZED (
|
|
|
239
251
|
SELECT c.*,CASE WHEN code IN ('cancelled','complete') THEN 0 ELSE ${LEAD_CLAIM_SQL} END lead_claim FROM classified c
|
|
240
252
|
), ranked AS MATERIALIZED (
|
|
241
253
|
-- A task the person's lead is on (lead-voice.ts) waits on nobody: it leaves Needs you until the claim ends or lapses.
|
|
242
|
-
SELECT c.*,code NOT IN (
|
|
243
|
-
code NOT IN (
|
|
254
|
+
SELECT c.*,code NOT IN (${WAITING_SQL}) AND NOT ${CUSTODY_WAITS} AND lead_claim<>1 AND NOT ${ARCHIVED} needs,
|
|
255
|
+
code NOT IN (${WAITING_SQL}) AND NOT ${CUSTODY_WAITS} AND NOT ${ARCHIVED} person_need,
|
|
244
256
|
${ARCHIVED} archived,
|
|
245
257
|
CASE WHEN code='complete' THEN 3 WHEN code='cancelled' THEN 4 WHEN code='running' THEN 1
|
|
246
|
-
WHEN ${CUSTODY_WAITS} OR code IN ('
|
|
258
|
+
WHEN ${CUSTODY_WAITS} OR code IN (${WAITING_SQL.replace(/'(?:cancelled|complete|running)',/g, '')}) THEN 2 ELSE 0 END rank,
|
|
247
259
|
${ASK_RANK} ask_rank,
|
|
248
260
|
CASE WHEN code='complete' THEN completed.at ELSE family_updated END sort_at,
|
|
249
261
|
completed.actor checked_actor,completed.at checked_at,completed.outcome checked_digest,completed.detail checked_detail
|
|
@@ -333,12 +345,14 @@ function parameters(now, access, options, custody) {
|
|
|
333
345
|
$leadOf: access.principal === 'operator' && access.viewer != null && access.viewer !== '' ? `lead for ${access.viewer}` : null };
|
|
334
346
|
}
|
|
335
347
|
function counts(row) {
|
|
336
|
-
return { all: n(row, 'all_count'), 'needs-you': n(row, 'needs_count'), running: n(row, 'running_count'), queued: n(row, 'queued_count'), completed: n(row, 'completed_count'), archived: n(row, 'archived_count') };
|
|
348
|
+
return { all: n(row, 'all_count'), 'needs-you': n(row, 'needs_count'), ready: n(row, 'ready_count'), running: n(row, 'running_count'), queued: n(row, 'queued_count'), completed: n(row, 'completed_count'), archived: n(row, 'archived_count') };
|
|
337
349
|
}
|
|
338
350
|
// Put-away families (v145) count only as archived.
|
|
339
351
|
// Queued: waits on someone or something else (rank 2), not a person and not a build of its own family.
|
|
340
352
|
const QUEUED = `rank=2 AND family_running=0 AND archived=0`;
|
|
341
|
-
|
|
353
|
+
// Ready (P1.4): what waits on a person to accept or send back, the Needs you rows under Review.
|
|
354
|
+
const READY = `needs AND rank=0 AND ask_rank=1 AND archived=0`;
|
|
355
|
+
const TOTALS = `COALESCE(SUM(archived=0),0) all_count,COALESCE(SUM(needs),0) needs_count,COALESCE(SUM(${READY}),0) ready_count,COALESCE(SUM(family_running AND archived=0),0) running_count,`
|
|
342
356
|
+ `COALESCE(SUM(${QUEUED}),0) queued_count,COALESCE(SUM(code='complete' AND archived=0),0) completed_count,COALESCE(SUM(archived),0) archived_count`;
|
|
343
357
|
const GROUP_TOTALS = `COALESCE(SUM(rank=0 AND ask_rank=0 AND archived=0),0) decide_count,COALESCE(SUM(rank=0 AND ask_rank=1 AND archived=0),0) review_count,COALESCE(SUM(rank=0 AND ask_rank=2 AND archived=0),0) unblock_count,`
|
|
344
358
|
+ `COALESCE(SUM(rank=1 AND archived=0),0) building_count,COALESCE(SUM(rank>1 AND archived=0),0) rest_count`;
|
|
@@ -391,7 +405,7 @@ function readWorkIndexPage(store, now, access, options, taskId = null) {
|
|
|
391
405
|
const view = VIEWS.includes(options.view ?? 'all') ? options.view ?? 'all' : 'all';
|
|
392
406
|
const limit = Number.isFinite(options.limit) ? Math.max(1, Math.min(WORK_INDEX_MAX_LIMIT, Math.floor(options.limit))) : WORK_INDEX_PAGE_LIMIT;
|
|
393
407
|
const scope = cursorScope(access, options, view), cursor = readCursor(options.cursor, scope);
|
|
394
|
-
const filter = view === 'archived' ? 'archived=1' : view === 'needs-you' ? 'needs=1' : view === 'running' ? 'family_running=1 AND archived=0' : view === 'queued' ? QUEUED : view === 'completed' ? "code='complete' AND archived=0" : 'archived=0';
|
|
408
|
+
const filter = view === 'archived' ? 'archived=1' : view === 'needs-you' ? 'needs=1' : view === 'ready' ? READY : view === 'running' ? 'family_running=1 AND archived=0' : view === 'queued' ? QUEUED : view === 'completed' ? "code='complete' AND archived=0" : 'archived=0';
|
|
395
409
|
const rows = prepared(store, `${projection}, selected_page AS MATERIALIZED (
|
|
396
410
|
SELECT * FROM ranked WHERE ($taskId IS NOT NULL OR ${filter}) AND ($taskId IS NULL OR root_ref IN (SELECT root_ref FROM members WHERE id=$taskId)) AND ($roots IS NULL OR root_id IN (SELECT value FROM json_each($roots))) AND ($cursorRoot=0 OR
|
|
397
411
|
${ORDER}>$cursorRank OR (${ORDER}=$cursorRank AND (sort_at<$cursorAt OR (sort_at=$cursorAt AND root_ref<$cursorRoot))))
|
|
@@ -400,13 +414,13 @@ function readWorkIndexPage(store, now, access, options, taskId = null) {
|
|
|
400
414
|
SELECT repo,${TOTALS},SUM(state='queued' AND family_running=0 AND archived=0) queued,
|
|
401
415
|
SUM(state='done' AND family_running=0 AND archived=0 AND updated_at>=$recent) done_recently FROM ranked GROUP BY repo
|
|
402
416
|
)
|
|
403
|
-
SELECT 0 record_kind,totals.*,(SELECT json_group_array(json_object('repo',repo,'all_count',all_count,'needs_count',needs_count,
|
|
417
|
+
SELECT 0 record_kind,totals.*,(SELECT json_group_array(json_object('repo',repo,'all_count',all_count,'needs_count',needs_count,'ready_count',ready_count,
|
|
404
418
|
'running_count',running_count,'queued_count',queued_count,'completed_count',completed_count,'archived_count',archived_count,'queued',queued,'done_recently',done_recently)) FROM project_totals) row_json FROM totals
|
|
405
|
-
UNION ALL SELECT 1,0,0,0,0,0,0,0,0,0,0,0,json_object('archived',archived,'archived_by',(SELECT by FROM task_archive WHERE task_ref=page.ref_id),
|
|
419
|
+
UNION ALL SELECT 1,0,0,0,0,0,0,0,0,0,0,0,0,json_object('archived',archived,'archived_by',(SELECT by FROM task_archive WHERE task_ref=page.ref_id),
|
|
406
420
|
'archived_kind',(SELECT by_kind FROM task_archive WHERE task_ref=page.ref_id),'archived_reason',(SELECT reason FROM task_archive WHERE task_ref=page.ref_id),
|
|
407
421
|
'archived_at',(SELECT at FROM task_archive WHERE task_ref=page.ref_id),'root_ref',root_ref,'root_id',root_id,'id',id,'root_title',root_title,'repo',repo,
|
|
408
422
|
'state',state,'created_at',created_at,'family_updated',family_updated,'version_count',version_count,'earlier_active',earlier_active,
|
|
409
|
-
'earlier_id',earlier_id,'broken',broken,'live_run',live_run,'result_id',page_result_id,'result_outcome',(SELECT outcome FROM run WHERE id=page_result_id),'attempt_reason',(SELECT substr(reason,1,1000) FROM run WHERE task_ref IN (SELECT ref_id FROM members WHERE root_ref=page.root_ref) AND (finished_at IS NOT NULL OR outcome IS NOT NULL) AND role NOT IN ('planner','reviewer') ORDER BY id DESC LIMIT 1),
|
|
423
|
+
'earlier_id',earlier_id,'route_change',(SELECT rc.words FROM route_change rc JOIN task_scope rs ON rs.task_id=page.id AND rs.digest=rc.scope_digest AND rs.approved_digest IS NOT rs.digest WHERE rc.task_ref=page.ref_id),'retry_stopped',(SELECT stopped FROM retry_episode WHERE task_ref=page.ref_id),'retry_stopped_run',(SELECT stopped_run FROM retry_episode WHERE task_ref=page.ref_id),'broken',broken,'live_run',live_run,'result_id',page_result_id,'result_outcome',(SELECT outcome FROM run WHERE id=page_result_id),'attempt_reason',(SELECT substr(reason,1,1000) FROM run WHERE task_ref IN (SELECT ref_id FROM members WHERE root_ref=page.root_ref) AND (finished_at IS NOT NULL OR outcome IS NOT NULL) AND role NOT IN ('planner','reviewer') ORDER BY id DESC LIMIT 1),
|
|
410
424
|
'publication_url',(SELECT pr_url FROM publication WHERE run=page_result_id),
|
|
411
425
|
'result_role',(SELECT role FROM run WHERE id=page_result_id),'result_head',(SELECT head_revision FROM run WHERE id=page_result_id),'live_role',(SELECT role FROM run WHERE id=page.live_run),
|
|
412
426
|
'check_status',(SELECT status FROM run_check WHERE run=page_result_id),
|
|
@@ -416,11 +430,11 @@ function readWorkIndexPage(store, now, access, options, taskId = null) {
|
|
|
416
430
|
'proof_verdict',(SELECT COALESCE(machine_verdict,verdict) FROM proof_verdict WHERE run=page_result_id),
|
|
417
431
|
'proof_failed_check',(SELECT verdict='refuted' AND reasons_json LIKE '%approved verification command exited%' FROM proof_verdict WHERE run=page_result_id),'code',code,'needs',needs,'person_need',person_need,'lead_claim',lead_claim,'family_running',family_running,'rank',rank,'ask_rank',ask_rank,'list_order',${ORDER},'sort_at',sort_at,
|
|
418
432
|
'checked_actor',checked_actor,'checked_at',checked_at,'checked_digest',checked_digest,'checked_detail',checked_detail,'question_id',question_id,
|
|
419
|
-
'dependency_id',dependency_id,'dependency_state',dependency_state,'custody_unresolved',custody_unresolved,'hold_kind',hold_kind,'hold_reason',substr(hold_reason,1,300),'hold_id',hold_id,'live_lease',live_lease IS NOT NULL,'approved',COALESCE(approved,0),
|
|
433
|
+
'dependency_id',dependency_id,'dependency_state',dependency_state,'hold_until',hold_until,'attempt_run',(SELECT id FROM run WHERE task_ref IN (SELECT ref_id FROM members WHERE root_ref=page.root_ref) AND (finished_at IS NOT NULL OR outcome IS NOT NULL) AND role NOT IN ('planner','reviewer') ORDER BY id DESC LIMIT 1),'custody_unresolved',custody_unresolved,'hold_kind',hold_kind,'hold_reason',substr(hold_reason,1,300),'hold_id',hold_id,'live_lease',live_lease IS NOT NULL,'approved',COALESCE(approved,0),
|
|
420
434
|
'plan_changed',scope_digest IS NOT NULL AND digest IS NOT scope_digest,'head_ok',length(head_revision)=40 AND head_revision NOT GLOB '*[^a-f0-9]*','stop_run',stop_run,'unfinished',unfinished,'signed_out',signed_out,
|
|
421
435
|
'question_run',(SELECT run FROM decision WHERE id=page.question_id),'agent_ask',page.agent_ask,
|
|
422
436
|
'agent_ask_run',(SELECT run FROM agent_question WHERE id=page.agent_ask),'agent_ask_text',(SELECT substr(question,1,300) FROM agent_question WHERE id=page.agent_ask),'replaced_by',(SELECT successor FROM task_replacement WHERE task_ref=page.ref_id),
|
|
423
|
-
'completed_by_lead',(SELECT lead FROM task_act WHERE task_ref=page.root_ref AND act='completed' ORDER BY id DESC LIMIT 1),
|
|
437
|
+
'completed_by_lead',(SELECT lead FROM task_act WHERE task_ref=page.root_ref AND act='completed' ORDER BY id DESC LIMIT 1),'machine_check',COALESCE(machine_check,0),
|
|
424
438
|
'question_task',(SELECT r.external_id FROM decision d JOIN run ON run.id=d.run JOIN task_ref r ON r.id=run.task_ref WHERE d.id=page.question_id)) FROM page`)
|
|
425
439
|
.all({ ...parameters(now, access, options, custody), $taskId: taskId, $roots: options.roots === undefined ? null : JSON.stringify(options.roots), $cursorRoot: cursor?.root ?? 0, $cursorRank: cursor?.rank ?? 0, $cursorAt: cursor?.at ?? '', $limit: limit + 1, $recent: new Date(now.getTime() - 86_400_000).toISOString() });
|
|
426
440
|
const selected = rows.slice(1).map(row => JSON.parse(String(row['row_json'])));
|
|
@@ -439,7 +453,7 @@ function readWorkIndexPage(store, now, access, options, taskId = null) {
|
|
|
439
453
|
// automatic review and, with checks Off, the release check that covers it.
|
|
440
454
|
const memo = new Map();
|
|
441
455
|
const readiness = (row, checks) => resultReadinessFacts(store, now, options.root, row, checks, memo);
|
|
442
|
-
return { items: page.map(row => itemOf(row, access.principal, probe, leadName, readiness, options.root !== undefined)), projects, totals: rows[0] === undefined ? { ...EMPTY } : counts(rows[0]),
|
|
456
|
+
return { items: page.map(row => itemOf(row, access.principal, probe, leadName, readiness, options.root !== undefined, now)), projects, totals: rows[0] === undefined ? { ...EMPTY } : counts(rows[0]),
|
|
443
457
|
groups: rows[0] === undefined ? { ...NO_GROUPS } : groupCounts(rows[0]), nextCursor, limit, view };
|
|
444
458
|
}
|
|
445
459
|
export function workCountsByProject(store, now, access) {
|
|
@@ -568,61 +582,54 @@ function resultReadinessFacts(store, now, root, row, recorded, memo) {
|
|
|
568
582
|
const blockers = completionBlockersOf({ report, checks, checkRequired, matrix: proof?.matrix, verdict: proof?.verdict ?? null, accepted, high });
|
|
569
583
|
return { checks, requirements: unverifiedWhenRefuted(requirements, verdict), ...(accepted ? { accepted } : {}), ...(blockers.length === 0 ? {} : { blockers }) };
|
|
570
584
|
}
|
|
571
|
-
function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
585
|
+
function itemOf(row, principal, probe, leadName, readiness, readable = false, now = new Date()) {
|
|
572
586
|
const code = String(row['code']), id = String(row['id']);
|
|
573
587
|
const custody = code === 'process-needs-attention' || (code === 'result-needs-attention' && n(row, 'custody_unresolved') === 1);
|
|
574
588
|
const process = custody && probe !== undefined ? processNeedOf(probe(id)) : null;
|
|
575
|
-
const waiting = process !== null && 'wait' in process;
|
|
589
|
+
const waiting = (process !== null && 'wait' in process) || (custody && probe === undefined);
|
|
576
590
|
const need = n(row, 'needs') === 1 && !waiting, running = n(row, 'family_running') === 1;
|
|
577
591
|
// The lead's claim (lead-voice.ts): the row still reads its own stage, worded "<name> is on it".
|
|
578
592
|
const lead = n(row, 'lead_claim') === 1 ? 'on-it' : n(row, 'lead_claim') === 2 ? 'lapsed' : null;
|
|
579
593
|
const personNeed = (n(row, 'person_need') === 1 || need) && !waiting;
|
|
580
594
|
const assignmentState = code === 'complete' ? 'complete' : code === 'ready-to-check' ? 'ready-to-check'
|
|
581
595
|
: code === 'cancelled' ? 'cancelled' : need ? 'needs-decision' : 'working';
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
'
|
|
591
|
-
|
|
592
|
-
'
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
'
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
'retry-scheduled': ['Retry scheduled', 'The attempt is backing off before retrying.', null, ''],
|
|
613
|
-
updating: ['Waiting for app update', 'New work resumes after the update.', null, ''],
|
|
614
|
-
'planning-ready': ['Planner ready', 'A connected worker can draft the plan.', null, ''],
|
|
615
|
-
'scouting-ready': ['Scout ready', 'A connected worker can produce the report.', null, ''],
|
|
616
|
-
queued: ['Queued', '', null, ''],
|
|
617
|
-
};
|
|
618
|
-
let [label, detail, actionCode, actionLabel] = words[code];
|
|
596
|
+
// What the code means (needs-you.ts CODE_READING, the one table): its act is the one it has always had; a need's
|
|
597
|
+
// words and label come from NEEDS, a wait's from WAITS. Only facts the row itself holds are added here.
|
|
598
|
+
const meaning = CODE_READING[code];
|
|
599
|
+
let label = '';
|
|
600
|
+
let detail = '';
|
|
601
|
+
let actionCode = (meaning?.act ?? null);
|
|
602
|
+
let actionLabel = actionCode === null ? '' : STATE_ACT_LABEL[code] ?? (meaning?.need === undefined ? '' : NEEDS[meaning.need].action.label);
|
|
603
|
+
if (code === 'waiting-decision')
|
|
604
|
+
actionCode = n(row, 'question_id') ? 'answer-decision' : n(row, 'agent_ask') ? 'answer-agent' : 'inspect-decisions';
|
|
605
|
+
if (code === 'held') {
|
|
606
|
+
actionCode = s(row, 'hold_kind') === 'operator' ? 'unhold' : 'inspect-hold';
|
|
607
|
+
detail = s(row, 'hold_reason') ?? 'This task is on hold.';
|
|
608
|
+
}
|
|
609
|
+
if (code === 'stopping') {
|
|
610
|
+
actionCode = 'inspect-stop';
|
|
611
|
+
actionLabel = 'View stop details';
|
|
612
|
+
}
|
|
613
|
+
if (code === 'stopped')
|
|
614
|
+
detail = 'Paused by a person. The work so far is kept.';
|
|
615
|
+
if (code === 'cancelled')
|
|
616
|
+
detail = 'This task was cancelled.';
|
|
617
|
+
// The same failure again and again: what failed, and the two ways on.
|
|
618
|
+
if (code === 'waiting-incident')
|
|
619
|
+
detail = `The last attempts failed the same way (${failedAttemptSentence(s(row, 'attempt_reason')).replace(/[.\s]+$/, '').replace(/^./, first => first.toLowerCase())}). Retry, or change the plan.`;
|
|
620
|
+
/** The build, the task or the time a sentence names. */
|
|
621
|
+
const context = code === 'stopping' ? { build: n(row, 'stop_run') || null }
|
|
622
|
+
: code === 'retry-scheduled' ? { build: n(row, 'attempt_run') || null, retry: s(row, 'hold_until') === null ? null : retryWhenWords(s(row, 'hold_until'), now) }
|
|
623
|
+
: code === 'vanished-run' ? { build: n(row, 'unfinished') || null }
|
|
624
|
+
: code === 'earlier-active' ? { task: s(row, 'earlier_id') }
|
|
625
|
+
: code === 'waiting-dependency' ? { task: s(row, 'dependency_id') } : {};
|
|
619
626
|
if (code === 'signed-out') {
|
|
620
627
|
const [provider = '', authMode = 'subscription'] = (s(row, 'signed_out') ?? '').split(':');
|
|
621
628
|
label = signInReason({ provider: provider, authMode: authMode === 'api-key' ? 'api-key' : 'subscription' });
|
|
622
629
|
detail = `${label}. It starts again on its own once ${providerName(provider)} works.`;
|
|
623
630
|
}
|
|
624
631
|
if (code === 'running' && n(row, 'version_count') > 1)
|
|
625
|
-
label = '
|
|
632
|
+
label = 'Building again';
|
|
626
633
|
// Replaced, never "Cancelled": the successor is one tap away.
|
|
627
634
|
const successor = code === 'cancelled' ? s(row, 'replaced_by') : null;
|
|
628
635
|
if (successor !== null) {
|
|
@@ -633,7 +640,11 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
633
640
|
detail = `${s(row, 'dependency_id')} ${s(row, 'dependency_state') === 'cancelled' ? 'was cancelled' : 'failed'} before it finished.`;
|
|
634
641
|
// A failed task says the latest finished attempt across its whole family, whatever its outcome: its recorded reason, or that none was recorded.
|
|
635
642
|
if (code === 'failed')
|
|
636
|
-
detail = failedAttemptSentence(s(row, 'attempt_reason'))
|
|
643
|
+
detail = s(row, 'retry_stopped') === null ? failedAttemptSentence(s(row, 'attempt_reason'))
|
|
644
|
+
: vanishedStopSentence({ why: s(row, 'retry_stopped'), run: n(row, 'retry_stopped_run') || null });
|
|
645
|
+
// A new route waiting for its one Approve says what changed (route-change.ts).
|
|
646
|
+
if (code === 'needs-approval' && s(row, 'route_change') !== null)
|
|
647
|
+
detail = s(row, 'route_change');
|
|
637
648
|
// A saved result that can't be accepted yet says its own reason, never one line for every such row.
|
|
638
649
|
const planChanged = code === 'result-needs-attention' && n(row, 'plan_changed') === 1;
|
|
639
650
|
if (code === 'result-needs-attention')
|
|
@@ -647,14 +658,11 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
647
658
|
actionCode = 'open-result';
|
|
648
659
|
actionLabel = 'Open result';
|
|
649
660
|
}
|
|
661
|
+
// Toolroll proves a build stopped itself; only a stop it can't prove from here offers "Check again", never a vouch.
|
|
650
662
|
if (process !== null && 'need' in process) {
|
|
651
|
-
actionCode = '
|
|
663
|
+
actionCode = 'recheck-stopped';
|
|
652
664
|
actionLabel = NEEDS[process.need].action.label;
|
|
653
665
|
}
|
|
654
|
-
else if (custody && probe === undefined) {
|
|
655
|
-
actionCode = 'confirm-stopped';
|
|
656
|
-
actionLabel = NEEDS['confirm-stopped'].action.label;
|
|
657
|
-
}
|
|
658
666
|
if (code === 'signed-out') {
|
|
659
667
|
actionCode = 'sign-in';
|
|
660
668
|
actionLabel = NEEDS['sign-in'].action.label;
|
|
@@ -668,7 +676,7 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
668
676
|
target: { taskId: successor ?? (code === 'earlier-active' ? s(row, 'earlier_id') ?? id : actionCode === 'answer-decision' ? s(row, 'question_task') ?? id : id),
|
|
669
677
|
runId: actionCode === 'answer-decision' ? n(row, 'question_run') || null : actionCode === 'answer-agent' ? n(row, 'agent_ask_run') || null : actionCode === 'inspect-run' && code === 'running' ? n(row, 'live_run') || null : actionCode === 'open-result' || actionCode === 'inspect-run' ? n(row, 'result_id') || null
|
|
670
678
|
: actionCode === 'reconcile-run' ? n(row, 'unfinished') || null : ['resume-run', 'inspect-stop'].includes(actionCode) ? n(row, 'stop_run') || null
|
|
671
|
-
: actionCode === '
|
|
679
|
+
: actionCode === 'recheck-stopped' ? process?.build ?? (n(row, 'result_id') || null) : null,
|
|
672
680
|
decisionId: actionCode === 'answer-decision' ? n(row, 'question_id') || null : null },
|
|
673
681
|
access: read ? 'read' : principal === 'operator' ? 'operator-control' : ['answer-decision', 'answer-agent', 'write-scope', 'unhold'].includes(actionCode) ? 'proposal-only' : 'operator-handoff',
|
|
674
682
|
retry: actionCode === 'reconcile-run' ? 'reconcile-before-retry' : read ? 'read-again' : 'refresh-before-acting' };
|
|
@@ -676,7 +684,8 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
676
684
|
const coded = stageOfCode(code, { needsPerson: personNeed, planning: s(row, 'live_role') === 'planner', operatorHold: s(row, 'hold_kind') === 'operator' });
|
|
677
685
|
const reading = process !== null
|
|
678
686
|
? 'wait' in process ? { stage: 'waiting', wait: process.wait } : { stage: 'needs-you', need: process.need }
|
|
679
|
-
|
|
687
|
+
// Not read here (no process probe): Toolroll's own stop check settles it, so it waits on no one.
|
|
688
|
+
: custody ? { stage: 'waiting', wait: 'build-checking' }
|
|
680
689
|
: coded.stage === 'needs-you' && (coded.need === undefined || coded.need === 'other') && INDEX_NEED[code] !== undefined ? { stage: 'needs-you', need: INDEX_NEED[code] } : coded;
|
|
681
690
|
const checkStatus = s(row, 'check_status'), verdict = s(row, 'proof_verdict');
|
|
682
691
|
const finished = reading.stage === 'finished' || reading.stage === 'complete';
|
|
@@ -689,8 +698,9 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
689
698
|
// Toolroll marked it complete because its work landed: the reason, said once.
|
|
690
699
|
const landed = code === 'complete' ? landedReasonOf(s(row, 'checked_actor'), s(row, 'checked_detail')) : null;
|
|
691
700
|
const shared = taskStatusOf({ stage: reading.stage, ...(reading.need === undefined ? {} : { need: reading.need }), ...(reading.wait === undefined ? {} : { wait: reading.wait }),
|
|
692
|
-
...(process === null ? {} : { needContext: { build: process.build } }),
|
|
701
|
+
...(process === null ? (Object.keys(context).length === 0 ? {} : { needContext: context }) : { needContext: { build: process.build } }),
|
|
693
702
|
reason: finished ? null : plainReasonOf(reading.stage, code, detail || null), report: s(row, 'result_role') === 'scout',
|
|
703
|
+
...(n(row, 'machine_check') === 1 ? { machine: true } : {}),
|
|
694
704
|
checks: ready !== null ? ready.checks : listedChecks,
|
|
695
705
|
...(ready === null ? {} : { requirements: ready.requirements ?? null, ...(ready.accepted ? { accepted: true } : {}), ...(ready.blockers === undefined ? {} : { blockers: ready.blockers }) }),
|
|
696
706
|
completedBy: code !== 'complete' || landed !== null ? null : n(row, 'completed_by_lead') === 1 ? 'the lead' : String(row['checked_actor']).replace(/^(?:operator|coordinator|lead):/, ''),
|
|
@@ -716,6 +726,8 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
716
726
|
const views = archived !== null ? ['archived'] : ['all'];
|
|
717
727
|
if (archived === null && need)
|
|
718
728
|
views.push('needs-you');
|
|
729
|
+
if (archived === null && need && n(row, 'rank') === 0 && n(row, 'ask_rank') === 1)
|
|
730
|
+
views.push('ready');
|
|
719
731
|
if (archived === null && running)
|
|
720
732
|
views.push('running');
|
|
721
733
|
if (archived === null && !need && !running && n(row, 'rank') === 2)
|
package/dist/work-summary.d.ts
CHANGED
|
@@ -8,9 +8,9 @@ export type WorkTarget = {
|
|
|
8
8
|
decisionId: number | null;
|
|
9
9
|
};
|
|
10
10
|
export type WorkAction = {
|
|
11
|
-
/** `
|
|
11
|
+
/** `recheck-stopped`: Toolroll looks again whether a finished build stopped (nobody vouches for it);
|
|
12
12
|
* `sign-in`: the agent's sign-in, in Settings; `answer-agent`: Reply to a running agent's question on the Agents board. */
|
|
13
|
-
code: DispatchAction | "inspect-task" | "inspect-run" | "inspect-stop" | "inspect-decisions" | "reconcile-run" | "open-pr" | "
|
|
13
|
+
code: DispatchAction | "inspect-task" | "inspect-run" | "inspect-stop" | "inspect-decisions" | "reconcile-run" | "open-pr" | "recheck-stopped" | "sign-in" | "answer-agent";
|
|
14
14
|
label: string;
|
|
15
15
|
target: WorkTarget;
|
|
16
16
|
access: "read" | "operator-control" | "proposal-only" | "operator-handoff";
|
package/dist/work-summary.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* State, decisions, approval, process custody and proof still belong to
|
|
3
3
|
* their existing owners. Action hints are navigation/proposal guidance,
|
|
4
4
|
* never credentials or permission to bypass an owning operation. */
|
|
5
|
+
import { machineChecked } from "./machine-check.js";
|
|
5
6
|
import { resultCheckLevel } from "./result-follow-ups.js";
|
|
6
7
|
import { diagnoseTaskDispatch } from "./dispatch.js";
|
|
7
8
|
import { isAlive } from "./runner.js";
|
|
@@ -94,6 +95,7 @@ export function taskWorkSummaryOf(store, taskId, now, access) {
|
|
|
94
95
|
const publication = result === null ? null : store.publicationForRun(result.id);
|
|
95
96
|
const facts = {
|
|
96
97
|
id: task.id, title: task.title, repo: ref.repo, state: task.state, updatedAt: task.updatedAt,
|
|
98
|
+
...(machineChecked(store, task.id) ? { machine: true } : {}),
|
|
97
99
|
dispatch: diagnoseTaskDispatch(store, taskId, now, access.dependencyRepos ?? null), liveRunId: live?.id ?? null,
|
|
98
100
|
unfinishedRunId: runs.find(one => one.outcome === null && one.role !== "reviewer")?.id ?? null,
|
|
99
101
|
control: taskControlOf(store, ref.id, now), openDecision: openWorkDecisionOf(store, ref.id, now),
|
package/dist/workspace-ui.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ import { type ChecksFact } from "./task-status.js";
|
|
|
26
26
|
import { type Html } from "./html.js";
|
|
27
27
|
/** The Work destination's views — shortcuts over the same rows, never a
|
|
28
28
|
* persisted state. All is the default. */
|
|
29
|
-
export type WorkView = "all" | "needs-you" | "running" | "queued" | "completed" | "archived";
|
|
29
|
+
export type WorkView = "all" | "needs-you" | "ready" | "running" | "queued" | "completed" | "archived";
|
|
30
30
|
export declare const WORK_VIEWS: readonly {
|
|
31
31
|
key: WorkView;
|
|
32
32
|
label: string;
|
|
@@ -154,6 +154,8 @@ export declare function receiptPublicationWords(publication: PublicationFacts):
|
|
|
154
154
|
* already reads elsewhere. */
|
|
155
155
|
export type WorkFacts = {
|
|
156
156
|
id: string;
|
|
157
|
+
/** The machine runs this task's check on a prepared candidate; no agent runs (machine-check.ts). */
|
|
158
|
+
machine?: boolean;
|
|
157
159
|
title: string;
|
|
158
160
|
repo: string | null;
|
|
159
161
|
state: TaskState;
|
|
@@ -233,7 +235,7 @@ export declare function lastErrorLineOf(log: string): {
|
|
|
233
235
|
text: string;
|
|
234
236
|
} | null;
|
|
235
237
|
/** The result page's own sentence when the decision is on it. */
|
|
236
|
-
export declare const RESULT_DECISION_SENTENCE = "Review the change, then accept it or
|
|
238
|
+
export declare const RESULT_DECISION_SENTENCE = "Review the change, then accept it or send it back.";
|
|
237
239
|
/** What the result page's Accept says, from facts it already shows: the
|
|
238
240
|
* recorded checks, the requirements not met, whether the saved proof reads,
|
|
239
241
|
* and what the button posts. "Accept and finish" only when everything is met
|