toolroll 0.9.93 → 0.9.95
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 +79 -0
- package/dist/activity-line.d.ts +7 -4
- package/dist/activity-line.js +9 -7
- package/dist/agent-fence.d.ts +10 -0
- package/dist/agent-fence.js +6 -3
- 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 -1
- package/dist/assignment.js +5 -0
- package/dist/attest.d.ts +3 -0
- package/dist/attest.js +10 -0
- 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 +1 -1
- package/dist/browser/workspace.js +32 -32
- package/dist/browser-crew.js +2 -0
- package/dist/browser-shell.d.ts +1 -1
- package/dist/browser-shell.js +2 -2
- package/dist/browser-workspace.d.ts +50 -2
- package/dist/browser-workspace.js +3 -3
- package/dist/build-review.js +2 -0
- package/dist/builder-restart.d.ts +21 -0
- package/dist/builder-restart.js +50 -0
- package/dist/builder.d.ts +11 -2
- package/dist/builder.js +234 -101
- package/dist/card-catalog.d.ts +209 -0
- package/dist/card-catalog.js +133 -0
- package/dist/card-cli.d.ts +43 -0
- package/dist/card-cli.js +44 -0
- package/dist/card-render.d.ts +26 -0
- package/dist/card-render.js +223 -0
- package/dist/card-sources.d.ts +68 -0
- package/dist/card-sources.js +234 -0
- package/dist/chat-acceptance.js +1 -1
- package/dist/chat-actions.d.ts +106 -9
- package/dist/chat-actions.js +473 -46
- package/dist/chat-approval.d.ts +13 -0
- package/dist/chat-approval.js +39 -9
- package/dist/chat-card-part.d.ts +11 -0
- package/dist/chat-card-part.js +18 -0
- package/dist/chat-card.d.ts +46 -0
- package/dist/chat-card.js +99 -0
- package/dist/chat-channel.d.ts +2 -0
- package/dist/chat-channel.js +37 -14
- package/dist/chat-controls.d.ts +4 -0
- package/dist/chat-controls.js +4 -0
- package/dist/chat-decide.d.ts +10 -16
- package/dist/chat-decide.js +79 -62
- package/dist/chat-delivery.js +13 -4
- package/dist/chat-flow.d.ts +0 -27
- package/dist/chat-flow.js +31 -2
- package/dist/chat-quiet.js +4 -4
- package/dist/chat-task-actions.d.ts +3 -2
- package/dist/chat-task-actions.js +19 -5
- package/dist/chat-terms.d.ts +14 -2
- package/dist/chat-terms.js +35 -0
- package/dist/chat-voice.d.ts +1 -1
- package/dist/chat-voice.js +2 -2
- package/dist/check-facts.d.ts +3 -1
- package/dist/check-facts.js +57 -11
- package/dist/check-failure.d.ts +24 -3
- package/dist/check-failure.js +89 -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-repair.d.ts +47 -0
- package/dist/check-repair.js +240 -0
- package/dist/check-report.d.ts +2 -0
- package/dist/check-report.js +1 -1
- 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/claim.d.ts +6 -0
- package/dist/claim.js +13 -2
- package/dist/cli-help.d.ts +4 -1
- package/dist/cli-help.js +49 -30
- package/dist/cli.js +15 -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 +3 -1
- package/dist/coding-provider.js +12 -7
- package/dist/coding-update.js +3 -2
- package/dist/coding-workspace.d.ts +1 -1
- package/dist/coding-workspace.js +8 -8
- 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 +92 -0
- package/dist/contracts/chat-actions.js +20 -0
- package/dist/contracts/chat-card.d.ts +91 -0
- package/dist/contracts/chat-card.js +54 -0
- package/dist/contracts/chat-content.d.ts +191 -0
- package/dist/contracts/chat-content.js +4 -0
- package/dist/contracts/cli.d.ts +52 -0
- package/dist/contracts/cli.js +4 -0
- package/dist/contracts/console-api.d.ts +12 -3
- package/dist/contracts/console-api.js +12 -3
- package/dist/contracts/flow-propose.d.ts +8 -1
- package/dist/contracts/flow-propose.js +3 -1
- package/dist/contracts/flow-send.d.ts +126 -0
- package/dist/contracts/flow-send.js +3 -0
- package/dist/contracts/gateway-tools.d.ts +17 -0
- package/dist/contracts/gateway-tools.js +3 -1
- package/dist/contracts/lead-tools.d.ts +234 -0
- package/dist/contracts/lead-tools.js +25 -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/storage.d.ts +14 -0
- package/dist/contracts/storage.js +3 -1
- package/dist/contracts/store-json.d.ts +18 -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/discord-chat.js +14 -0
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +25 -16
- package/dist/dispose.d.ts +19 -0
- package/dist/dispose.js +55 -9
- package/dist/easy-start.js +2 -3
- package/dist/eval-oracle.js +4 -1
- package/dist/evals.js +2 -1
- package/dist/evidence-pair.d.ts +53 -0
- package/dist/evidence-pair.js +128 -0
- package/dist/exhaustion.d.ts +17 -4
- package/dist/exhaustion.js +44 -4
- package/dist/export.js +1 -1
- package/dist/failing-first.d.ts +31 -0
- package/dist/failing-first.js +44 -0
- package/dist/fallback-schema.d.ts +8 -0
- package/dist/fallback-schema.js +40 -0
- package/dist/fallback.d.ts +138 -0
- package/dist/fallback.js +605 -0
- 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/findings.d.ts +224 -0
- package/dist/findings.js +449 -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 +75 -9
- package/dist/flaky.js +255 -0
- package/dist/fleet-schema.d.ts +17 -0
- package/dist/fleet-schema.js +135 -0
- package/dist/flow-browser.js +2 -2
- package/dist/flow-card-task.js +3 -0
- package/dist/flow-code.d.ts +2 -2
- package/dist/flow-code.js +9 -5
- package/dist/flow-result-card.d.ts +22 -0
- package/dist/flow-result-card.js +55 -0
- package/dist/flow-send.js +12 -9
- package/dist/flow-steps.js +1 -1
- package/dist/flow-triggers.d.ts +1 -1
- package/dist/flow-triggers.js +2 -2
- package/dist/flow-unattended.d.ts +26 -0
- package/dist/flow-unattended.js +12 -3
- package/dist/flows-ui.js +10 -2
- package/dist/follow-through-brief.d.ts +12 -0
- package/dist/follow-through-brief.js +31 -0
- package/dist/follow-through-cli.d.ts +23 -0
- package/dist/follow-through-cli.js +153 -0
- package/dist/git-incident.d.ts +16 -0
- package/dist/git-incident.js +69 -6
- package/dist/github-import.d.ts +62 -0
- package/dist/github-import.js +146 -0
- package/dist/guides.js +15 -10
- package/dist/handover-note.d.ts +33 -0
- package/dist/handover-note.js +106 -0
- package/dist/invoke.d.ts +25 -2
- package/dist/invoke.js +157 -58
- 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-cli.js +26 -24
- package/dist/lead-context.js +1 -1
- package/dist/lead-contract.d.ts +1 -1
- package/dist/lead-contract.js +3 -2
- package/dist/lead-doors.d.ts +4 -0
- package/dist/lead-doors.js +2 -2
- package/dist/lead-progress.js +5 -0
- package/dist/lead-status.d.ts +2 -0
- package/dist/lead-status.js +6 -2
- package/dist/lead-tools.js +162 -34
- package/dist/lead.js +2 -2
- 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 +16 -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/modes.d.ts +6 -6
- package/dist/modes.js +3 -1
- package/dist/needs-you.d.ts +31 -9
- package/dist/needs-you.js +110 -41
- package/dist/nightly-github.d.ts +2 -0
- package/dist/nightly-github.js +4 -4
- package/dist/operate-remote-arguments.js +7 -1
- package/dist/operate-remote.js +3 -1
- package/dist/operate.d.ts +6 -1
- package/dist/operate.js +565 -64
- 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/plan-login.d.ts +14 -0
- package/dist/plan-login.js +58 -0
- package/dist/planner.d.ts +1 -1
- package/dist/planner.js +4 -2
- package/dist/process-liveness.d.ts +2 -0
- package/dist/process-liveness.js +61 -17
- package/dist/project-delete-ui.d.ts +19 -2
- package/dist/project-delete-ui.js +29 -1
- package/dist/project-delete.js +14 -1
- 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/project-tools.d.ts +4 -4
- package/dist/project-tools.js +23 -7
- package/dist/proof.js +30 -5
- package/dist/protected-storage-look.d.ts +1 -0
- package/dist/protected-storage-look.js +13 -0
- package/dist/protected-storage.d.ts +70 -39
- package/dist/protected-storage.js +196 -147
- package/dist/provider-auth.js +1 -1
- package/dist/provider-capture.d.ts +34 -0
- package/dist/provider-capture.js +53 -0
- package/dist/publish.js +9 -1
- package/dist/pull-request-flow.js +5 -0
- package/dist/push.d.ts +18 -2
- package/dist/push.js +40 -16
- package/dist/release-adapters.d.ts +9 -0
- package/dist/release-adapters.js +76 -0
- package/dist/release-auto.js +13 -13
- package/dist/release-card.d.ts +1 -2
- package/dist/release-card.js +11 -0
- package/dist/release-carry.d.ts +7 -2
- package/dist/release-carry.js +49 -10
- package/dist/release-cli.d.ts +4 -0
- package/dist/release-cli.js +29 -22
- 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 +20 -1
- package/dist/release.js +85 -9
- 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 +27 -19
- 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 +28 -4
- package/dist/scope.js +92 -28
- package/dist/scout.d.ts +1 -1
- package/dist/scout.js +2 -1
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +26 -18
- package/dist/server/chat.js +16 -14
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +26 -35
- package/dist/server/console.css +73 -0
- package/dist/server/guards.js +3 -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 +16 -3
- 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-follow-through.d.ts +11 -0
- package/dist/server/render-follow-through.js +68 -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 +2 -0
- package/dist/server/render-release.js +3 -3
- package/dist/server/render-results.d.ts +2 -0
- package/dist/server/render-results.js +68 -68
- package/dist/server/render-tasks.d.ts +16 -2
- package/dist/server/render-tasks.js +177 -141
- package/dist/server/retired-routes.d.ts +17 -0
- package/dist/server/retired-routes.js +53 -0
- package/dist/server/route-table.d.ts +3 -0
- package/dist/server/route-table.js +48 -12
- package/dist/server/settings.js +166 -34
- package/dist/server/task-views.js +66 -26
- package/dist/server/tasks.d.ts +2 -6
- package/dist/server/tasks.js +250 -321
- package/dist/server/welcome.js +1 -1
- package/dist/settings-groups-ui.d.ts +1 -0
- package/dist/settings-groups-ui.js +4 -2
- package/dist/settings-kit.js +3 -2
- package/dist/settings-proofs.js +3 -1
- package/dist/slack-chat.js +14 -1
- package/dist/steer-mailbox.d.ts +8 -1
- package/dist/steer-mailbox.js +164 -6
- package/dist/stop-proof.js +6 -4
- 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 +89 -7
- package/dist/store.js +386 -114
- package/dist/surface.js +67 -6
- 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.d.ts +2 -1
- package/dist/task-control.js +4 -1
- package/dist/task-outcome-cli.d.ts +2 -0
- package/dist/task-outcome-cli.js +17 -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 +45 -49
- package/dist/teardown-git.d.ts +18 -0
- package/dist/teardown-git.js +105 -21
- package/dist/telegram-lead.js +23 -6
- package/dist/telegram-progress.js +5 -3
- package/dist/telegram-status.js +6 -4
- package/dist/telegram.js +12 -1
- 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.js +19 -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 +95 -66
- package/dist/work-summary.d.ts +1 -1
- package/dist/work-summary.js +7 -2
- package/dist/workspace-ui.d.ts +7 -3
- package/dist/workspace-ui.js +38 -23
- package/dist/worktree.d.ts +12 -3
- package/dist/worktree.js +91 -14
- package/package.json +2 -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, STORAGE_HOLD_PREFIX, 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,24 @@ 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
|
-
|
|
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. */
|
|
48
|
+
const PERSON_CODES = ['finding-held', 'history-problem', 'earlier-active', 'ready-to-check', 'waiting-decision', 'decision-queue', 'result-needs-attention',
|
|
49
|
+
'process-needs-attention', 'failed', 'vanished-run', 'stopped', 'held', 'waiting-incident', 'terminal-dependency', 'needs-project',
|
|
50
|
+
'needs-scope', 'invalid-scope', 'needs-agent-profile', 'needs-approval', 'missing-requirement', 'no-worker-registered', 'no-worker-online', 'signed-out', 'storage-blocked'];
|
|
51
|
+
/** A family's findings held for a person (findings.ts). A database not yet at v155 has no such table: see registerValidators. */
|
|
52
|
+
const HELD_FINDINGS = `(SELECT COUNT(*) FROM finding fd WHERE fd.family_root=root.id AND fd.state='with-person')`;
|
|
53
|
+
/** The index codes that wait on nobody: finished or running work, and every wait (needs-you.ts CODE_READING). */
|
|
54
|
+
export const WAITING_CODES = ['cancelled', 'complete', 'running', 'queued',
|
|
55
|
+
...['stopping', 'updating', 'retry-scheduled', 'waiting-dependency', 'worker-at-capacity', 'planning-ready', 'scouting-ready'].filter(code => isWaitCode(code))];
|
|
56
|
+
const WAITING_SQL = WAITING_CODES.map(code => `'${code}'`).join(',');
|
|
57
|
+
/** The words on a state's own act (a need's act wears its need's label). */
|
|
58
|
+
const STATE_ACT_LABEL = {
|
|
59
|
+
'ready-to-check': 'Open result', complete: 'Open result', cancelled: 'View task details', failed: 'Review and retry', 'waiting-incident': 'Review and retry',
|
|
60
|
+
stopped: 'Review pause', held: 'Review hold', 'result-needs-attention': 'Open the build', 'process-needs-attention': 'Open the build',
|
|
45
61
|
};
|
|
46
|
-
/** Every index code that can wait on a person. */
|
|
47
|
-
const PERSON_CODES = ['history-problem', 'earlier-active', 'ready-to-check', 'waiting-decision', 'decision-queue', 'result-needs-attention',
|
|
48
|
-
'process-needs-attention', 'failed', 'vanished-run', 'stopping', 'stopped', 'held', 'waiting-incident', 'terminal-dependency', 'needs-project',
|
|
49
|
-
'needs-scope', 'invalid-scope', 'needs-agent-profile', 'needs-approval', 'missing-requirement', 'no-worker-registered', 'no-worker-online', 'signed-out'];
|
|
50
62
|
/** A code's ask: a saved result is Review (unless its build may still run), otherwise its need's ask. */
|
|
51
63
|
export function askOfCode(code, custody = false) {
|
|
52
64
|
if (custody)
|
|
@@ -129,6 +141,8 @@ const PROJECTION = `WITH RECURSIVE admitted AS MATERIALIZED (
|
|
|
129
141
|
(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
142
|
h.owner_kind hold_kind,h.reason hold_reason,h.until hold_until,
|
|
131
143
|
sc.task_id scope_id,sc.digest,sc.approved_at,sc.proposed_at,sc.profile_state,
|
|
144
|
+
-- A prepared candidate: the machine checks out that commit and runs its check; no agent runs (machine-check.ts).
|
|
145
|
+
(typeof(sc.candidate)='text' AND sc.candidate<>'') machine_check,
|
|
132
146
|
-- The pause the dispatch gate itself left this task waiting on (the provider it resolved, pins,
|
|
133
147
|
-- configuration and planners included) first; the approved terms answer before any gate has looked.
|
|
134
148
|
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,
|
|
@@ -167,6 +181,7 @@ const PROJECTION = `WITH RECURSIVE admitted AS MATERIALIZED (
|
|
|
167
181
|
(SELECT run FROM run_stop WHERE task_ref=c.ref_id AND resumed_at IS NULL ORDER BY requested_at DESC LIMIT 1) stop_run,
|
|
168
182
|
(SELECT settled_at FROM run_stop WHERE task_ref=c.ref_id AND resumed_at IS NULL ORDER BY requested_at DESC LIMIT 1) stop_settled,
|
|
169
183
|
COALESCE((SELECT substr(actor,13) FROM action_ledger WHERE task_id=root.id AND action='assignment claimed' AND source='work' ORDER BY id DESC LIMIT 1),root.coordinator_cid) owner_id,
|
|
184
|
+
${HELD_FINDINGS} held_findings,
|
|
170
185
|
pub.pr_url publication_url
|
|
171
186
|
FROM ordered c JOIN families f USING(root_ref) JOIN admitted root ON root.ref_id=c.root_ref
|
|
172
187
|
LEFT JOIN run result INDEXED BY work_result ON result.task_ref=c.ref_id AND result.id=c.result_id
|
|
@@ -204,6 +219,7 @@ const PROJECTION = `WITH RECURSIVE admitted AS MATERIALIZED (
|
|
|
204
219
|
WHEN state='cancelled' THEN 'cancelled'
|
|
205
220
|
WHEN earlier_active>0 AND state='done' THEN 'earlier-active'
|
|
206
221
|
WHEN stop_run IS NOT NULL THEN CASE WHEN stop_settled IS NULL THEN 'stopping' ELSE 'stopped' END
|
|
222
|
+
WHEN held_findings>0 AND state<>'failed' THEN 'finding-held'
|
|
207
223
|
WHEN ready THEN CASE WHEN checked_id IS NULL THEN 'ready-to-check' ELSE 'complete' END
|
|
208
224
|
WHEN state='done' AND question_id IS NOT NULL AND hold_id IS NULL THEN 'waiting-decision'
|
|
209
225
|
WHEN state='done' THEN 'result-needs-attention'
|
|
@@ -212,6 +228,7 @@ const PROJECTION = `WITH RECURSIVE admitted AS MATERIALIZED (
|
|
|
212
228
|
WHEN live_lease IS NOT NULL THEN CASE WHEN question_id IS NOT NULL OR agent_ask IS NOT NULL THEN 'waiting-decision' ELSE 'running' END
|
|
213
229
|
WHEN state='running' OR unfinished IS NOT NULL THEN 'vanished-run'
|
|
214
230
|
WHEN EXISTS(SELECT 1 FROM sqlite_master WHERE type='trigger' AND name GLOB 'so_desktop_update_*') THEN 'updating'
|
|
231
|
+
WHEN hold_kind='backoff' AND substr(hold_reason,1,${STORAGE_HOLD_PREFIX.length})='${STORAGE_HOLD_PREFIX}' THEN 'storage-blocked'
|
|
215
232
|
WHEN hold_kind='backoff' THEN 'retry-scheduled'
|
|
216
233
|
WHEN hold_kind='decision' THEN 'waiting-decision'
|
|
217
234
|
WHEN hold_kind='incident' THEN 'waiting-incident'
|
|
@@ -239,11 +256,11 @@ const PROJECTION = `WITH RECURSIVE admitted AS MATERIALIZED (
|
|
|
239
256
|
SELECT c.*,CASE WHEN code IN ('cancelled','complete') THEN 0 ELSE ${LEAD_CLAIM_SQL} END lead_claim FROM classified c
|
|
240
257
|
), ranked AS MATERIALIZED (
|
|
241
258
|
-- 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 (
|
|
259
|
+
SELECT c.*,code NOT IN (${WAITING_SQL}) AND NOT ${CUSTODY_WAITS} AND lead_claim<>1 AND NOT ${ARCHIVED} needs,
|
|
260
|
+
code NOT IN (${WAITING_SQL}) AND NOT ${CUSTODY_WAITS} AND NOT ${ARCHIVED} person_need,
|
|
244
261
|
${ARCHIVED} archived,
|
|
245
262
|
CASE WHEN code='complete' THEN 3 WHEN code='cancelled' THEN 4 WHEN code='running' THEN 1
|
|
246
|
-
WHEN ${CUSTODY_WAITS} OR code IN ('
|
|
263
|
+
WHEN ${CUSTODY_WAITS} OR code IN (${WAITING_SQL.replace(/'(?:cancelled|complete|running)',/g, '')}) THEN 2 ELSE 0 END rank,
|
|
247
264
|
${ASK_RANK} ask_rank,
|
|
248
265
|
CASE WHEN code='complete' THEN completed.at ELSE family_updated END sort_at,
|
|
249
266
|
completed.actor checked_actor,completed.at checked_at,completed.outcome checked_digest,completed.detail checked_detail
|
|
@@ -292,8 +309,11 @@ function registerValidators(store) {
|
|
|
292
309
|
const asks = db.prepare("SELECT 1 AS hit FROM sqlite_master WHERE type='table' AND name='agent_question'").get() !== undefined;
|
|
293
310
|
const projection = asks ? indexed : indexed.replace(AGENT_ASK, 'NULL')
|
|
294
311
|
.replace(`'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),`, '');
|
|
295
|
-
|
|
296
|
-
|
|
312
|
+
// Nor, before v155, any held findings: readingFleet's rule for a list read.
|
|
313
|
+
const findings = db.prepare("SELECT 1 AS hit FROM sqlite_master WHERE type='table' AND name='finding'").get() !== undefined;
|
|
314
|
+
const read = findings ? projection : projection.replace(HELD_FINDINGS, '0');
|
|
315
|
+
registered.set(db, read);
|
|
316
|
+
return read;
|
|
297
317
|
}
|
|
298
318
|
/** The list can't see processes: each task whose build Toolroll can't yet
|
|
299
319
|
* confirm stopped, as the store reads it now (the task page's own reading).
|
|
@@ -333,12 +353,14 @@ function parameters(now, access, options, custody) {
|
|
|
333
353
|
$leadOf: access.principal === 'operator' && access.viewer != null && access.viewer !== '' ? `lead for ${access.viewer}` : null };
|
|
334
354
|
}
|
|
335
355
|
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') };
|
|
356
|
+
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
357
|
}
|
|
338
358
|
// Put-away families (v145) count only as archived.
|
|
339
359
|
// Queued: waits on someone or something else (rank 2), not a person and not a build of its own family.
|
|
340
360
|
const QUEUED = `rank=2 AND family_running=0 AND archived=0`;
|
|
341
|
-
|
|
361
|
+
// Ready (P1.4): what waits on a person to accept or send back, the Needs you rows under Review.
|
|
362
|
+
const READY = `needs AND rank=0 AND ask_rank=1 AND archived=0`;
|
|
363
|
+
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
364
|
+ `COALESCE(SUM(${QUEUED}),0) queued_count,COALESCE(SUM(code='complete' AND archived=0),0) completed_count,COALESCE(SUM(archived),0) archived_count`;
|
|
343
365
|
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
366
|
+ `COALESCE(SUM(rank=1 AND archived=0),0) building_count,COALESCE(SUM(rank>1 AND archived=0),0) rest_count`;
|
|
@@ -391,7 +413,7 @@ function readWorkIndexPage(store, now, access, options, taskId = null) {
|
|
|
391
413
|
const view = VIEWS.includes(options.view ?? 'all') ? options.view ?? 'all' : 'all';
|
|
392
414
|
const limit = Number.isFinite(options.limit) ? Math.max(1, Math.min(WORK_INDEX_MAX_LIMIT, Math.floor(options.limit))) : WORK_INDEX_PAGE_LIMIT;
|
|
393
415
|
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';
|
|
416
|
+
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
417
|
const rows = prepared(store, `${projection}, selected_page AS MATERIALIZED (
|
|
396
418
|
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
419
|
${ORDER}>$cursorRank OR (${ORDER}=$cursorRank AND (sort_at<$cursorAt OR (sort_at=$cursorAt AND root_ref<$cursorRoot))))
|
|
@@ -400,13 +422,13 @@ function readWorkIndexPage(store, now, access, options, taskId = null) {
|
|
|
400
422
|
SELECT repo,${TOTALS},SUM(state='queued' AND family_running=0 AND archived=0) queued,
|
|
401
423
|
SUM(state='done' AND family_running=0 AND archived=0 AND updated_at>=$recent) done_recently FROM ranked GROUP BY repo
|
|
402
424
|
)
|
|
403
|
-
SELECT 0 record_kind,totals.*,(SELECT json_group_array(json_object('repo',repo,'all_count',all_count,'needs_count',needs_count,
|
|
425
|
+
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
426
|
'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),
|
|
427
|
+
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
428
|
'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
429
|
'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
430
|
'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),
|
|
431
|
+
'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
432
|
'publication_url',(SELECT pr_url FROM publication WHERE run=page_result_id),
|
|
411
433
|
'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
434
|
'check_status',(SELECT status FROM run_check WHERE run=page_result_id),
|
|
@@ -416,11 +438,11 @@ function readWorkIndexPage(store, now, access, options, taskId = null) {
|
|
|
416
438
|
'proof_verdict',(SELECT COALESCE(machine_verdict,verdict) FROM proof_verdict WHERE run=page_result_id),
|
|
417
439
|
'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
440
|
'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,
|
|
441
|
+
'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,600),'hold_id',hold_id,'live_lease',live_lease IS NOT NULL,'approved',COALESCE(approved,0),
|
|
420
442
|
'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
443
|
'question_run',(SELECT run FROM decision WHERE id=page.question_id),'agent_ask',page.agent_ask,
|
|
422
444
|
'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),
|
|
445
|
+
'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
446
|
'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
447
|
.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
448
|
const selected = rows.slice(1).map(row => JSON.parse(String(row['row_json'])));
|
|
@@ -439,7 +461,7 @@ function readWorkIndexPage(store, now, access, options, taskId = null) {
|
|
|
439
461
|
// automatic review and, with checks Off, the release check that covers it.
|
|
440
462
|
const memo = new Map();
|
|
441
463
|
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]),
|
|
464
|
+
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
465
|
groups: rows[0] === undefined ? { ...NO_GROUPS } : groupCounts(rows[0]), nextCursor, limit, view };
|
|
444
466
|
}
|
|
445
467
|
export function workCountsByProject(store, now, access) {
|
|
@@ -568,7 +590,7 @@ function resultReadinessFacts(store, now, root, row, recorded, memo) {
|
|
|
568
590
|
const blockers = completionBlockersOf({ report, checks, checkRequired, matrix: proof?.matrix, verdict: proof?.verdict ?? null, accepted, high });
|
|
569
591
|
return { checks, requirements: unverifiedWhenRefuted(requirements, verdict), ...(accepted ? { accepted } : {}), ...(blockers.length === 0 ? {} : { blockers }) };
|
|
570
592
|
}
|
|
571
|
-
function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
593
|
+
function itemOf(row, principal, probe, leadName, readiness, readable = false, now = new Date()) {
|
|
572
594
|
const code = String(row['code']), id = String(row['id']);
|
|
573
595
|
const custody = code === 'process-needs-attention' || (code === 'result-needs-attention' && n(row, 'custody_unresolved') === 1);
|
|
574
596
|
const process = custody && probe !== undefined ? processNeedOf(probe(id)) : null;
|
|
@@ -579,50 +601,50 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
579
601
|
const personNeed = (n(row, 'person_need') === 1 || need) && !waiting;
|
|
580
602
|
const assignmentState = code === 'complete' ? 'complete' : code === 'ready-to-check' ? 'ready-to-check'
|
|
581
603
|
: 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
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
'
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
604
|
+
// What the code means (needs-you.ts CODE_READING, the one table): its act is the one it has always had; a need's
|
|
605
|
+
// words and label come from NEEDS, a wait's from WAITS. Only facts the row itself holds are added here.
|
|
606
|
+
const meaning = CODE_READING[code];
|
|
607
|
+
let label = '';
|
|
608
|
+
let detail = '';
|
|
609
|
+
let actionCode = (meaning?.act ?? null);
|
|
610
|
+
let actionLabel = actionCode === null ? '' : STATE_ACT_LABEL[code] ?? (meaning?.need === undefined ? '' : NEEDS[meaning.need].action.label);
|
|
611
|
+
if (code === 'waiting-decision')
|
|
612
|
+
actionCode = n(row, 'question_id') ? 'answer-decision' : n(row, 'agent_ask') ? 'answer-agent' : 'inspect-decisions';
|
|
613
|
+
if (code === 'held') {
|
|
614
|
+
actionCode = s(row, 'hold_kind') === 'operator' ? 'unhold' : 'inspect-hold';
|
|
615
|
+
detail = s(row, 'hold_reason') ?? 'This task is on hold.';
|
|
616
|
+
}
|
|
617
|
+
if (code === 'stopping') {
|
|
618
|
+
actionCode = 'inspect-stop';
|
|
619
|
+
actionLabel = 'View stop details';
|
|
620
|
+
}
|
|
621
|
+
if (code === 'stopped')
|
|
622
|
+
detail = 'Paused by a person. The work so far is kept.';
|
|
623
|
+
// The storage check couldn't finish (dispose.ts holdForStorage): its own words, when it retries, and Try again.
|
|
624
|
+
if (code === 'storage-blocked') {
|
|
625
|
+
const until = s(row, 'hold_until');
|
|
626
|
+
detail = `${(s(row, 'hold_reason') ?? '').slice(STORAGE_HOLD_PREFIX.length)}${until === null ? '' : ` It tries again on its own ${retryWhenWords(until, now)}.`}`;
|
|
627
|
+
actionCode = 'unhold';
|
|
628
|
+
actionLabel = NEEDS.storage.action.label;
|
|
629
|
+
}
|
|
630
|
+
if (code === 'cancelled')
|
|
631
|
+
detail = 'This task was cancelled.';
|
|
632
|
+
// The same failure again and again: what failed, and the two ways on.
|
|
633
|
+
if (code === 'waiting-incident')
|
|
634
|
+
detail = `The last attempts failed the same way (${failedAttemptSentence(s(row, 'attempt_reason')).replace(/[.\s]+$/, '').replace(/^./, first => first.toLowerCase())}). Retry, or change the plan.`;
|
|
635
|
+
/** The build, the task or the time a sentence names. */
|
|
636
|
+
const context = code === 'stopping' ? { build: n(row, 'stop_run') || null }
|
|
637
|
+
: code === 'retry-scheduled' ? { build: n(row, 'attempt_run') || null, retry: s(row, 'hold_until') === null ? null : retryWhenWords(s(row, 'hold_until'), now) }
|
|
638
|
+
: code === 'vanished-run' ? { build: n(row, 'unfinished') || null }
|
|
639
|
+
: code === 'earlier-active' ? { task: s(row, 'earlier_id') }
|
|
640
|
+
: code === 'waiting-dependency' ? { task: s(row, 'dependency_id') } : {};
|
|
619
641
|
if (code === 'signed-out') {
|
|
620
642
|
const [provider = '', authMode = 'subscription'] = (s(row, 'signed_out') ?? '').split(':');
|
|
621
643
|
label = signInReason({ provider: provider, authMode: authMode === 'api-key' ? 'api-key' : 'subscription' });
|
|
622
644
|
detail = `${label}. It starts again on its own once ${providerName(provider)} works.`;
|
|
623
645
|
}
|
|
624
646
|
if (code === 'running' && n(row, 'version_count') > 1)
|
|
625
|
-
label = '
|
|
647
|
+
label = 'Building again';
|
|
626
648
|
// Replaced, never "Cancelled": the successor is one tap away.
|
|
627
649
|
const successor = code === 'cancelled' ? s(row, 'replaced_by') : null;
|
|
628
650
|
if (successor !== null) {
|
|
@@ -633,7 +655,11 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
633
655
|
detail = `${s(row, 'dependency_id')} ${s(row, 'dependency_state') === 'cancelled' ? 'was cancelled' : 'failed'} before it finished.`;
|
|
634
656
|
// A failed task says the latest finished attempt across its whole family, whatever its outcome: its recorded reason, or that none was recorded.
|
|
635
657
|
if (code === 'failed')
|
|
636
|
-
detail = failedAttemptSentence(s(row, 'attempt_reason'))
|
|
658
|
+
detail = s(row, 'retry_stopped') === null ? failedAttemptSentence(s(row, 'attempt_reason'))
|
|
659
|
+
: vanishedStopSentence({ why: s(row, 'retry_stopped'), run: n(row, 'retry_stopped_run') || null });
|
|
660
|
+
// A new route waiting for its one Approve says what changed (route-change.ts).
|
|
661
|
+
if (code === 'needs-approval' && s(row, 'route_change') !== null)
|
|
662
|
+
detail = s(row, 'route_change');
|
|
637
663
|
// A saved result that can't be accepted yet says its own reason, never one line for every such row.
|
|
638
664
|
const planChanged = code === 'result-needs-attention' && n(row, 'plan_changed') === 1;
|
|
639
665
|
if (code === 'result-needs-attention')
|
|
@@ -660,7 +686,7 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
660
686
|
actionCode = code === 'running' ? 'inspect-run' : 'inspect-task';
|
|
661
687
|
actionLabel = code === 'running' ? 'Watch the build' : 'View task details';
|
|
662
688
|
}
|
|
663
|
-
const read = actionCode !== null && ['inspect-task', 'inspect-run', 'inspect-stop', 'inspect-decisions', 'open-result'].includes(actionCode);
|
|
689
|
+
const read = actionCode !== null && ['inspect-task', 'inspect-findings', 'inspect-run', 'inspect-stop', 'inspect-decisions', 'open-result'].includes(actionCode);
|
|
664
690
|
const primaryAction = actionCode === null ? null : { code: actionCode, label: actionLabel,
|
|
665
691
|
target: { taskId: successor ?? (code === 'earlier-active' ? s(row, 'earlier_id') ?? id : actionCode === 'answer-decision' ? s(row, 'question_task') ?? id : id),
|
|
666
692
|
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
|
|
@@ -687,8 +713,9 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
687
713
|
// Toolroll marked it complete because its work landed: the reason, said once.
|
|
688
714
|
const landed = code === 'complete' ? landedReasonOf(s(row, 'checked_actor'), s(row, 'checked_detail')) : null;
|
|
689
715
|
const shared = taskStatusOf({ stage: reading.stage, ...(reading.need === undefined ? {} : { need: reading.need }), ...(reading.wait === undefined ? {} : { wait: reading.wait }),
|
|
690
|
-
...(process === null ? {} : { needContext: { build: process.build } }),
|
|
716
|
+
...(process === null ? (Object.keys(context).length === 0 ? {} : { needContext: context }) : { needContext: { build: process.build } }),
|
|
691
717
|
reason: finished ? null : plainReasonOf(reading.stage, code, detail || null), report: s(row, 'result_role') === 'scout',
|
|
718
|
+
...(n(row, 'machine_check') === 1 ? { machine: true } : {}),
|
|
692
719
|
checks: ready !== null ? ready.checks : listedChecks,
|
|
693
720
|
...(ready === null ? {} : { requirements: ready.requirements ?? null, ...(ready.accepted ? { accepted: true } : {}), ...(ready.blockers === undefined ? {} : { blockers: ready.blockers }) }),
|
|
694
721
|
completedBy: code !== 'complete' || landed !== null ? null : n(row, 'completed_by_lead') === 1 ? 'the lead' : String(row['checked_actor']).replace(/^(?:operator|coordinator|lead):/, ''),
|
|
@@ -714,6 +741,8 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
714
741
|
const views = archived !== null ? ['archived'] : ['all'];
|
|
715
742
|
if (archived === null && need)
|
|
716
743
|
views.push('needs-you');
|
|
744
|
+
if (archived === null && need && n(row, 'rank') === 0 && n(row, 'ask_rank') === 1)
|
|
745
|
+
views.push('ready');
|
|
717
746
|
if (archived === null && running)
|
|
718
747
|
views.push('running');
|
|
719
748
|
if (archived === null && !need && !running && n(row, 'rank') === 2)
|
package/dist/work-summary.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export type WorkTarget = {
|
|
|
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" | "recheck-stopped" | "sign-in" | "answer-agent";
|
|
13
|
+
code: DispatchAction | "inspect-task" | "inspect-findings" | "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";
|