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
package/dist/task-status.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { DEFAULT_LEAD_NAME } from "./lead-identity.js";
|
|
14
14
|
import { html } from "./html.js";
|
|
15
15
|
import { FAILED_CHECK } from "./result-acts.js";
|
|
16
|
-
import {
|
|
16
|
+
import { CODE_READING, NEEDS, needSentence, waitSentence } from "./needs-you.js";
|
|
17
17
|
/** A cancelled task with a successor reads "Replaced by <id>" everywhere, never "Cancelled". */
|
|
18
18
|
export const replacedWords = (successor) => `Replaced by ${successor}`;
|
|
19
19
|
const REPLACED = /^Replaced by \S+/;
|
|
@@ -28,6 +28,8 @@ export const CHECKS_LABEL = "Project checks";
|
|
|
28
28
|
export const PR_CI_LABEL = "PR CI";
|
|
29
29
|
/** The one verb for opening a saved result, on every surface. */
|
|
30
30
|
export const OPEN_RESULT = "Open result";
|
|
31
|
+
/** A machine-run check under way (a prepared candidate: no agent runs), on every surface that has no richer words. */
|
|
32
|
+
export const MACHINE_CHECK_RUNNING = "Running the check on the prepared version.";
|
|
31
33
|
export const CHECKED_AT_RELEASE = "Checked at release";
|
|
32
34
|
export function releaseStateOf(checks) {
|
|
33
35
|
if (checks?.level !== "off" || checks.status === "passed" || checks.status === "failed")
|
|
@@ -162,46 +164,47 @@ function sentenceOf(headline, facts) {
|
|
|
162
164
|
const reason = facts.reason?.trim() || null;
|
|
163
165
|
const sha = short(facts.checks?.head ?? null);
|
|
164
166
|
switch (headline) {
|
|
165
|
-
case "Queued": return reason ?? "Waiting for a
|
|
167
|
+
case "Queued": return reason ?? "Waiting for a builder.";
|
|
166
168
|
case "Planning": return reason ?? "The lead is writing the plan.";
|
|
167
169
|
// One plain sentence per need (needs-you.ts); a recorded reason only where the need keeps one.
|
|
168
170
|
case "Needs you": return facts.stage === "finished" ? resultAsk(facts) ?? needSentence("review-result", reason, facts.needContext) : needSentence(facts.need ?? "other", reason, facts.needContext);
|
|
169
171
|
case "Waiting": return waitSentence(facts.wait, reason, facts.needContext);
|
|
170
172
|
case "Building": return facts.stage === "checking" ? "Checks are running on the change."
|
|
171
|
-
: facts.stage === "reviewing" ? "Reviewing the change before it reaches you."
|
|
173
|
+
: facts.stage === "reviewing" ? "Reviewing the change before it reaches you."
|
|
174
|
+
: facts.machine === true ? MACHINE_CHECK_RUNNING : reason ?? "An agent is working on it.";
|
|
172
175
|
case "Ready for review":
|
|
173
176
|
if (facts.report)
|
|
174
|
-
return "The report is ready to read. Read it, then
|
|
177
|
+
return "The report is ready to read. Read it, then accept it.";
|
|
175
178
|
if (releaseStateOf(facts.checks) === "covered")
|
|
176
|
-
return `Checked at release on ${short(facts.checks.release.head) ?? "its release"}. Review the change, then
|
|
179
|
+
return `Checked at release on ${short(facts.checks.release.head) ?? "its release"}. Review the change, then accept it.`;
|
|
177
180
|
if (facts.checks?.level === "off" && facts.checks.status !== "passed")
|
|
178
|
-
return "Checked at release. Review the change, then
|
|
181
|
+
return "Checked at release. Review the change, then accept it.";
|
|
179
182
|
if (facts.checks?.status === "passed") {
|
|
180
183
|
// The Checks row names the others and the batch commit; the sentence doesn't repeat them.
|
|
181
184
|
const peers = facts.checks.batch?.state === "together" ? facts.checks.batch.peers.length : 0;
|
|
182
185
|
if (peers > 0)
|
|
183
|
-
return `Checks passed together with ${peers === 1 ? "1 other result" : `${peers} other results`}. Review the change, then
|
|
184
|
-
return `${facts.checks.level === "quick" ? "Quick checks" : "Checks"} passed${sha === null ? "" : ` on ${sha}`}. Review the change, then
|
|
186
|
+
return `Checks passed together with ${peers === 1 ? "1 other result" : `${peers} other results`}. Review the change, then accept it.`;
|
|
187
|
+
return `${facts.checks.level === "quick" ? "Quick checks" : "Checks"} passed${sha === null ? "" : ` on ${sha}`}. Review the change, then accept it.`;
|
|
185
188
|
}
|
|
186
189
|
if (facts.checks?.batch?.state === "waiting")
|
|
187
|
-
return "Its full check runs with other results within 10 minutes. Review the change, then
|
|
190
|
+
return "Its full check runs with other results within 10 minutes. Review the change, then accept it.";
|
|
188
191
|
if (facts.checks?.status === "unavailable")
|
|
189
|
-
return `${facts.checks.cause === "command-changed" ? CHECK_COMMAND_CHANGED : CHECK_UNREADABLE} Review the change, then
|
|
192
|
+
return `${facts.checks.cause === "command-changed" ? CHECK_COMMAND_CHANGED : CHECK_UNREADABLE} Review the change, then accept it.`;
|
|
190
193
|
// Never claim a check that isn't known to have passed.
|
|
191
194
|
if (facts.checks == null)
|
|
192
|
-
return "Review the change, then
|
|
193
|
-
return "Built without a passing project check. Review the change, then
|
|
195
|
+
return "Review the change, then accept it.";
|
|
196
|
+
return "Built without a passing project check. Review the change, then accept it.";
|
|
194
197
|
case "Complete": {
|
|
195
198
|
// Landed work says why once; its merged pull request is that reason, not a second sentence.
|
|
196
199
|
if (facts.landed)
|
|
197
|
-
return `
|
|
198
|
-
const by = facts.completedBy ? `
|
|
200
|
+
return `Accepted automatically: ${facts.landed}.`;
|
|
201
|
+
const by = facts.completedBy ? `Accepted by ${facts.completedBy}.` : "Accepted.";
|
|
199
202
|
const pr = facts.pullRequest;
|
|
200
203
|
return pr?.state === "merged" ? `${by} ${pr.number === null ? "Its pull request" : `Pull request #${pr.number}`} merged.` : by;
|
|
201
204
|
}
|
|
202
205
|
case "Failed":
|
|
203
206
|
if (facts.checks?.status === "failed")
|
|
204
|
-
return `${facts.checks.level === "quick" ? "Quick checks" : "Checks"} failed${sha === null ? "" : ` on ${sha}`}. See what broke, then retry or
|
|
207
|
+
return `${facts.checks.level === "quick" ? "Quick checks" : "Checks"} failed${sha === null ? "" : ` on ${sha}`}. See what broke, then retry or send it back.`;
|
|
205
208
|
return reason ?? "The last attempt stopped before it finished. Review it, then retry.";
|
|
206
209
|
case "Stopped": return reason ?? "Stopped by a person. The work so far is kept.";
|
|
207
210
|
}
|
|
@@ -291,13 +294,13 @@ function detailsOf(headline, facts) {
|
|
|
291
294
|
if (evidence != null) {
|
|
292
295
|
const action = { label: "See what was kept", href: facts.links?.result ?? null };
|
|
293
296
|
if (evidence.damaged > 0)
|
|
294
|
-
row("evidence", "Saved
|
|
297
|
+
row("evidence", "Saved files", "Some saved files changed", problem(headline), headline === "Failed" ? {} : { action });
|
|
295
298
|
else if (evidence.missing > 0)
|
|
296
|
-
row("evidence", "Saved
|
|
299
|
+
row("evidence", "Saved files", "Some saved files are missing", problem(headline), headline === "Failed" ? {} : { action });
|
|
297
300
|
else if (evidence.shortened > 0)
|
|
298
|
-
row("evidence", "Saved
|
|
301
|
+
row("evidence", "Saved files", "Some output shortened", "note", { action });
|
|
299
302
|
else
|
|
300
|
-
row("evidence", "Saved
|
|
303
|
+
row("evidence", "Saved files", "All kept", "ok");
|
|
301
304
|
}
|
|
302
305
|
return rows;
|
|
303
306
|
}
|
|
@@ -331,24 +334,9 @@ export function statusOf(facts) {
|
|
|
331
334
|
/** The name most surfaces already import. */
|
|
332
335
|
export const taskStatusOf = statusOf;
|
|
333
336
|
// ---- reading the existing projections ----------------------------------------
|
|
334
|
-
/**
|
|
335
|
-
*
|
|
336
|
-
const
|
|
337
|
-
queued: ["queued"], ready: ["queued"], "worker-at-capacity": ["queued"], "retry-scheduled": ["queued"], updating: ["queued"],
|
|
338
|
-
"waiting-dependency": ["queued"], "scouting-ready": ["queued"], "planning-ready": ["queued"], "provider-quota": ["queued"],
|
|
339
|
-
// A request the planner cannot be given (too long to hand back, say) waits on a person to shorten it.
|
|
340
|
-
"planner-source": ["needs-you", "fix-request"],
|
|
341
|
-
running: ["building"], reviewing: ["reviewing"], "review-pending": ["building"], "checks-running": ["checking"],
|
|
342
|
-
"signed-out": ["needs-you", "sign-in"],
|
|
343
|
-
"needs-approval": ["needs-you", "approval"],
|
|
344
|
-
"waiting-decision": ["needs-you", "answer"], "decision-queue": ["needs-you", "answer"],
|
|
345
|
-
"build-stopping": ["waiting"], "other-computer": ["waiting"],
|
|
346
|
-
stopping: ["stopped"], stopped: ["stopped"], paused: ["stopped"], cancelled: ["stopped"],
|
|
347
|
-
failed: ["failed"], "waiting-incident": ["failed"], "checks-failed": ["failed"],
|
|
348
|
-
"ready-to-check": ["finished"], "ready-to-review": ["finished"], "report-ready": ["finished"], "no-change": ["finished"],
|
|
349
|
-
"pr-opened": ["finished"], "merge-observed": ["finished"], "pr-closed": ["finished"], "agent-attested": ["finished"], "accepted-exception": ["finished"],
|
|
350
|
-
complete: ["complete"],
|
|
351
|
-
};
|
|
337
|
+
/** Codes that read Needs you whoever asks (their reason is a person's by nature). Any other need reads Needs you
|
|
338
|
+
* only when the caller says a person is needed; otherwise it waits in the queue. */
|
|
339
|
+
const ALWAYS_NEEDS = new Set(["planner-source", "signed-out", "needs-approval", "waiting-decision", "decision-queue"]);
|
|
352
340
|
/** Queue reasons worth a sentence of their own; any other queued or
|
|
353
341
|
* building reason is scheduler wording, and the default sentence is plainer. */
|
|
354
342
|
const QUEUED_REASONS = new Set(["worker-at-capacity", "waiting-dependency", "retry-scheduled", "updating", "planning-ready", "scouting-ready", "provider-quota"]);
|
|
@@ -359,15 +347,18 @@ export function plainReasonOf(stage, code, detail) {
|
|
|
359
347
|
return QUEUED_REASONS.has(code) ? detail ?? null : null;
|
|
360
348
|
return detail ?? null;
|
|
361
349
|
}
|
|
350
|
+
/** Every dispatch or work-index code, as the stage it means (needs-you.ts CODE_READING, the one table). Unknown
|
|
351
|
+
* codes that wait on a person stay Needs you; everything else waits in the queue. */
|
|
362
352
|
export function stageOfCode(code, options = {}) {
|
|
363
353
|
if (code === "running" && options.planning)
|
|
364
354
|
return { stage: "planning" };
|
|
365
355
|
if (code === "held")
|
|
366
356
|
return options.operatorHold === false ? { stage: "needs-you", need: "hold" } : { stage: "stopped" };
|
|
367
|
-
const known =
|
|
368
|
-
if (known !== undefined)
|
|
369
|
-
return known
|
|
370
|
-
|
|
357
|
+
const known = Object.prototype.hasOwnProperty.call(CODE_READING, code) ? CODE_READING[code] : undefined;
|
|
358
|
+
if (known !== undefined && (known.stage !== "needs-you" || ALWAYS_NEEDS.has(code))) {
|
|
359
|
+
return { stage: known.stage, ...(known.need === undefined ? {} : { need: known.need }), ...(known.wait === undefined ? {} : { wait: known.wait }) };
|
|
360
|
+
}
|
|
361
|
+
return options.needsPerson === false ? { stage: "queued" } : { stage: "needs-you", need: known?.need ?? "other" };
|
|
371
362
|
}
|
|
372
363
|
/** The pull request as the task page's follower reports it, or the bare publication row. */
|
|
373
364
|
export function pullRequestFactOf(publication) {
|
|
@@ -473,7 +464,7 @@ const quoted = (statement) => {
|
|
|
473
464
|
function unresolvedAsk(row, word) {
|
|
474
465
|
const what = word === "Not shown yet" && screenshotMissing(row) ? "A screenshot is missing for"
|
|
475
466
|
: word === "Not shown yet" ? "Nothing shows this requirement is met" : word === "Unverified" ? "This requirement couldn't be verified" : "This requirement isn't met";
|
|
476
|
-
return `${what}: ${quoted(row.statement) || row.id || "a requirement"}.
|
|
467
|
+
return `${what}: ${quoted(row.statement) || row.id || "a requirement"}. Send it back, or accept the result with a reason.`;
|
|
477
468
|
}
|
|
478
469
|
/** Requirements from the stored matrix: met, the ones only a person confirms, and the total. */
|
|
479
470
|
export function requirementsOf(matrix, release) {
|
|
@@ -482,7 +473,7 @@ export function requirementsOf(matrix, release) {
|
|
|
482
473
|
const words = matrix.map(row => requirementWordOf(row, null, release));
|
|
483
474
|
const yours = matrix.find((_, index) => words[index] === "You check");
|
|
484
475
|
const unshown = matrix.filter((row, index) => words[index] === "Not shown yet" && screenshotMissing(row));
|
|
485
|
-
const ask = yours !== undefined ? `Check this requirement yourself, then
|
|
476
|
+
const ask = yours !== undefined ? `Check this requirement yourself, then accept it: ${quoted(yours.statement)}`
|
|
486
477
|
: unshown.length > 0 ? unresolvedAsk(unshown[0], "Not shown yet") : null;
|
|
487
478
|
return { met: words.filter(word => word === "Met").length, total: matrix.length, yours: words.filter(word => word === "You check").length,
|
|
488
479
|
missed: words.filter(word => word === "Not met" || word === "Not shown yet").length,
|
|
@@ -499,12 +490,12 @@ export function completionBlockersOf(f) {
|
|
|
499
490
|
const release = releaseStateOf(f.checks);
|
|
500
491
|
const backing = checkBackingOf(f.checks);
|
|
501
492
|
if (!f.report && f.checks?.status === "failed") {
|
|
502
|
-
blockers.push({ key: "check-failed", message: `Checks failed${f.checks.exitCode === null ? "" : ` (exit ${f.checks.exitCode})`}. Fix them and run checks again, or
|
|
493
|
+
blockers.push({ key: "check-failed", message: `Checks failed${f.checks.exitCode === null ? "" : ` (exit ${f.checks.exitCode})`}. Fix them and run checks again, or send it back.` });
|
|
503
494
|
}
|
|
504
495
|
else if (!f.report && f.checkRequired && f.checks?.status !== "passed" && release === undefined) {
|
|
505
|
-
blockers.push({ key: "check-missing", message: checksUnderway(f.checks) ? "Its project check hasn't finished yet.
|
|
506
|
-
: f.checks?.status === "unavailable" ? f.checks.cause === "command-changed" ? `${CHECK_COMMAND_CHANGED.replace(/; run checks again\.$/, ".")} Run checks again, then
|
|
507
|
-
: "No passing project check is recorded for this result. Run checks on it, then
|
|
496
|
+
blockers.push({ key: "check-missing", message: checksUnderway(f.checks) ? "Its project check hasn't finished yet. Accept it once it passes."
|
|
497
|
+
: f.checks?.status === "unavailable" ? f.checks.cause === "command-changed" ? `${CHECK_COMMAND_CHANGED.replace(/; run checks again\.$/, ".")} Run checks again, then accept it.` : `${CHECK_UNREADABLE} Run checks on it, then accept it.`
|
|
498
|
+
: "No passing project check is recorded for this result. Run checks on it, then accept it." });
|
|
508
499
|
}
|
|
509
500
|
if (!f.accepted && f.checks?.status !== "failed") {
|
|
510
501
|
const refuted = f.verdict === "refuted";
|
|
@@ -514,11 +505,11 @@ export function completionBlockersOf(f) {
|
|
|
514
505
|
blockers.push({ key: "criteria", message: unresolvedAsk(unresolved[0].row, unresolved[0].word) });
|
|
515
506
|
else if (unresolved.length > 1) {
|
|
516
507
|
const ids = unresolved.map(one => one.row.id ?? "?");
|
|
517
|
-
blockers.push({ key: "criteria", message: `${unresolved.length} requirements ${unresolved.every(one => one.word === "Unverified") ? "couldn't be verified" : "aren't met yet"} (${ids.slice(0, 6).join(", ")}${ids.length > 6 ? ", …" : ""}).
|
|
508
|
+
blockers.push({ key: "criteria", message: `${unresolved.length} requirements ${unresolved.every(one => one.word === "Unverified") ? "couldn't be verified" : "aren't met yet"} (${ids.slice(0, 6).join(", ")}${ids.length > 6 ? ", …" : ""}). Send it back, or accept the result with a reason.` });
|
|
518
509
|
}
|
|
519
510
|
}
|
|
520
511
|
if (f.high > 0)
|
|
521
|
-
blockers.push({ key: "high", message: `The automatic review found ${f.high === 1 ? "a high-severity problem" : `${f.high} high-severity problems`}.
|
|
512
|
+
blockers.push({ key: "high", message: `The automatic review found ${f.high === 1 ? "a high-severity problem" : `${f.high} high-severity problems`}. Send it back before accepting it.` });
|
|
522
513
|
return blockers;
|
|
523
514
|
}
|
|
524
515
|
/** A refuted report verifies none of its requirements, whatever it marked met (a person's acceptance doesn't change that). */
|
package/dist/teardown-git.d.ts
CHANGED
|
@@ -46,6 +46,10 @@ export declare const INCLUDE_PARSE_TIMED_OUT = "Git didn't finish reading it in
|
|
|
46
46
|
export type GitBinding = {
|
|
47
47
|
repo?: string | null;
|
|
48
48
|
};
|
|
49
|
+
/** The shared git folder of a recorded project, read from disk and proven as boundDirs proves it: the project's own
|
|
50
|
+
* real `.git` folder, holding no `commondir`. No pointer file or `commondir` an agent can write decides it, and no git
|
|
51
|
+
* runs (a page visit reads this). Throws with the reason otherwise. */
|
|
52
|
+
export declare function recordedCommonDir(repo: string): string;
|
|
49
53
|
/** Settings fixed on every write through a view: `worktree add` records absolute paths (relative ones would need a
|
|
50
54
|
* config write, which a view keeps); no transport at all (a caller that fetches allows its one protocol by name, as the
|
|
51
55
|
* nightly check's local fetch does with protocol.file.allow); and no split index (its shared index files would be
|
|
@@ -55,6 +59,12 @@ export declare const WRITE_VIEW_FLAGS: readonly string[];
|
|
|
55
59
|
* program (anything but INERT_LFS). A file that
|
|
56
60
|
* isn't a plain one, or doesn't parse, counts as naming one. */
|
|
57
61
|
export declare function lfsExtensionIn(checkout: string, gitFile?: string, limit?: Limit): boolean;
|
|
62
|
+
/** The hardened runners' own reads before the caller's command (the checkout's config listing): their own bounds, never
|
|
63
|
+
* the caller's. A caller's small `maxBuffer` is for its own command's output (builder.ts reads one ls-tree line with
|
|
64
|
+
* 2 KiB): inherited, it failed the config listing once a project's .git/config grew past 2 KiB (one [branch] section per
|
|
65
|
+
* work branch), and every prepared release check then failed as "its config couldn't be read" (runs 3316–3324). */
|
|
66
|
+
export declare const CONFIG_READ_BYTES: number;
|
|
67
|
+
export declare const CONFIG_READ_MS = 30000;
|
|
58
68
|
/** Every command runs against a view of the checkout's repository (gitView) whose config and info are copies the
|
|
59
69
|
* plane made, inspected there, so neither can change between the inspection and the run. `mode` "read" is for reading
|
|
60
70
|
* a checkout an agent may still be writing (the code page): every command also takes readFlags(), and the view holds
|
|
@@ -143,4 +153,10 @@ export declare function lazyFetchSetting(listing: string): string | null;
|
|
|
143
153
|
* command and protocol settings refused, a partial clone refused) is the config git then runs with.
|
|
144
154
|
*/
|
|
145
155
|
export declare function lazyFetchSafeGit(inner?: Runner): Runner;
|
|
156
|
+
/**
|
|
157
|
+
* The checkout a folder sits in, for choosing a folder (easy-start): the nearest parent holding a `.git`, then read
|
|
158
|
+
* through checkoutGitSync, so a config that includes another file (a FIFO), or sets a command, is refused and the read
|
|
159
|
+
* is bounded. Null when there is no checkout or it can't be read that way.
|
|
160
|
+
*/
|
|
161
|
+
export declare function checkoutTop(dir: string, timeoutMs?: number): string | null;
|
|
146
162
|
export {};
|
package/dist/teardown-git.js
CHANGED
|
@@ -477,6 +477,12 @@ function boundDirs(checkout, binding = {}) {
|
|
|
477
477
|
}
|
|
478
478
|
return dirs;
|
|
479
479
|
}
|
|
480
|
+
/** The shared git folder of a recorded project, read from disk and proven as boundDirs proves it: the project's own
|
|
481
|
+
* real `.git` folder, holding no `commondir`. No pointer file or `commondir` an agent can write decides it, and no git
|
|
482
|
+
* runs (a page visit reads this). Throws with the reason otherwise. */
|
|
483
|
+
export function recordedCommonDir(repo) {
|
|
484
|
+
return boundDirs(repo, { repo }).common;
|
|
485
|
+
}
|
|
480
486
|
/** What git keeps only per worktree and the plane never links: HEAD (git won't take it as a link) and the pointers. */
|
|
481
487
|
/** What the plane links of a worktree's own git folder (git writes them in place through the link: the index and its
|
|
482
488
|
* logs by lockfile, per-worktree refs and logs inside the folders), whether or not they exist yet; a rebase's,
|
|
@@ -1061,6 +1067,28 @@ export function lfsExtensionIn(checkout, gitFile = "git", limit) {
|
|
|
1061
1067
|
const parsed = spawnSync(gitFile, ["config", "--file", path, "--no-includes", "--name-only", "--list", "-z"], { cwd: "/", encoding: "utf8", timeout: setupTimeout(limit), killSignal: "SIGKILL", env: { PATH: process.env["PATH"] ?? "", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", HOME: "/nonexistent" } });
|
|
1062
1068
|
return parsed.status !== 0 || parsed.stdout.split("\0").filter(Boolean).some(name => lfsCommand(name) || ["include", "includeif"].includes(keyParts(name).section));
|
|
1063
1069
|
}
|
|
1070
|
+
/** The hardened runners' own reads before the caller's command (the checkout's config listing): their own bounds, never
|
|
1071
|
+
* the caller's. A caller's small `maxBuffer` is for its own command's output (builder.ts reads one ls-tree line with
|
|
1072
|
+
* 2 KiB): inherited, it failed the config listing once a project's .git/config grew past 2 KiB (one [branch] section per
|
|
1073
|
+
* work branch), and every prepared release check then failed as "its config couldn't be read" (runs 3316–3324). */
|
|
1074
|
+
export const CONFIG_READ_BYTES = 16 * 1024 * 1024;
|
|
1075
|
+
export const CONFIG_READ_MS = 30_000;
|
|
1076
|
+
/** What of a caller's options an internal read keeps: where it runs and with what environment and containment —
|
|
1077
|
+
* never its time limit, output limit, stdin or output callbacks. */
|
|
1078
|
+
function internalOptions(options, env) {
|
|
1079
|
+
const { cwd, envAllowlist, omitEnv, fence, fenceGrants, confinement, processGroup, owner, signal } = options ?? {};
|
|
1080
|
+
return { ...(cwd === undefined ? {} : { cwd }), ...(envAllowlist === undefined ? {} : { envAllowlist }), ...(omitEnv === undefined ? {} : { omitEnv }),
|
|
1081
|
+
...(fence === undefined ? {} : { fence }), ...(fenceGrants === undefined ? {} : { fenceGrants }), ...(confinement === undefined ? {} : { confinement }),
|
|
1082
|
+
...(processGroup === undefined ? {} : { processGroup }), ...(owner === undefined ? {} : { owner }), ...(signal === undefined ? {} : { signal }),
|
|
1083
|
+
env, timeoutMs: CONFIG_READ_MS, maxBuffer: CONFIG_READ_BYTES };
|
|
1084
|
+
}
|
|
1085
|
+
/** Why the checkout's config listing failed, in words, for the refusal: git's own first line, or what stopped it. */
|
|
1086
|
+
function listingFailure(result) {
|
|
1087
|
+
if (result.timedOut)
|
|
1088
|
+
return `its config couldn't be read (the read ran past ${CONFIG_READ_MS / 1000} s)`;
|
|
1089
|
+
const said = `${result.stderr}`.split("\n").map(line => line.trim()).find(line => line !== "");
|
|
1090
|
+
return `its config couldn't be read${said === undefined ? ` (git exited ${result.code})` : ` (${said.slice(0, 300)})`}`;
|
|
1091
|
+
}
|
|
1064
1092
|
/** Every command runs against a view of the checkout's repository (gitView) whose config and info are copies the
|
|
1065
1093
|
* plane made, inspected there, so neither can change between the inspection and the run. `mode` "read" is for reading
|
|
1066
1094
|
* a checkout an agent may still be writing (the code page): every command also takes readFlags(), and the view holds
|
|
@@ -1089,10 +1117,10 @@ export function checkoutGit(inner = run, mode = "build", binding = {}) {
|
|
|
1089
1117
|
}
|
|
1090
1118
|
try {
|
|
1091
1119
|
const viewed = { ...options, env: { ...(options?.env ?? {}), ...view.env } };
|
|
1092
|
-
// The checkout's own config may name no command.
|
|
1093
|
-
const own = await inner("git", ["--no-optional-locks", "config", "--list", "--show-scope", "-z"], viewed);
|
|
1120
|
+
// The checkout's own config may name no command. Read with this runner's own bounds (internalOptions).
|
|
1121
|
+
const own = await inner("git", ["--no-optional-locks", "config", "--list", "--show-scope", "-z"], internalOptions(options, viewed.env));
|
|
1094
1122
|
if (own.code !== 0)
|
|
1095
|
-
return refused(
|
|
1123
|
+
return refused(listingFailure(own));
|
|
1096
1124
|
const key = commandSetting(own.stdout, { lfsChecked: true });
|
|
1097
1125
|
if (key !== null)
|
|
1098
1126
|
return refused(`the checkout's git config sets ${key}, which can run a command`);
|
|
@@ -1185,9 +1213,11 @@ export function viewedGitSync(cwd, args, options = {}) {
|
|
|
1185
1213
|
const env = { PATH: process.env["PATH"] ?? "", HOME: process.env["HOME"] ?? "", LANG: "C", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", GIT_TERMINAL_PROMPT: "0", ...view.env };
|
|
1186
1214
|
const timeoutNow = () => { expire(); return limit === undefined ? options.timeoutMs ?? 60_000 : Math.max(1, Math.min(options.timeoutMs ?? 60_000, limit())); };
|
|
1187
1215
|
const fixed = { cwd, env, encoding: "utf8", killSignal: "SIGKILL", maxBuffer: options.maxBuffer ?? 1_048_576, stdio: ["ignore", "pipe", "pipe"] };
|
|
1188
|
-
|
|
1216
|
+
// Its own bounds, never the caller's output limit (CONFIG_READ_BYTES); a caller's deadline still holds.
|
|
1217
|
+
const own = spawnSync(gitFile, ["--no-optional-locks", "config", "--list", "--show-scope", "-z"], { ...fixed, maxBuffer: CONFIG_READ_BYTES,
|
|
1218
|
+
timeout: limit === undefined ? CONFIG_READ_MS : Math.max(1, Math.min(CONFIG_READ_MS, limit())) });
|
|
1189
1219
|
if (own.status !== 0)
|
|
1190
|
-
throw new Error("its config couldn't be read");
|
|
1220
|
+
throw new Error(own.error !== undefined ? `its config couldn't be read (${own.error.message.slice(0, 300)})` : "its config couldn't be read");
|
|
1191
1221
|
const key = commandSetting(own.stdout, { lfsChecked: true });
|
|
1192
1222
|
if (key !== null)
|
|
1193
1223
|
throw new Error(`the checkout's git config sets ${key}, which can run a command`);
|
|
@@ -1333,3 +1363,31 @@ export function lazyFetchSafeGit(inner = run) {
|
|
|
1333
1363
|
// repository's config and its includes as git found them, and an include naming a FIFO could hang the plane.
|
|
1334
1364
|
return checkoutGit(inner, "read");
|
|
1335
1365
|
}
|
|
1366
|
+
/**
|
|
1367
|
+
* The checkout a folder sits in, for choosing a folder (easy-start): the nearest parent holding a `.git`, then read
|
|
1368
|
+
* through checkoutGitSync, so a config that includes another file (a FIFO), or sets a command, is refused and the read
|
|
1369
|
+
* is bounded. Null when there is no checkout or it can't be read that way.
|
|
1370
|
+
*/
|
|
1371
|
+
export function checkoutTop(dir, timeoutMs = 10_000) {
|
|
1372
|
+
let at;
|
|
1373
|
+
try {
|
|
1374
|
+
at = realpathSync(dir);
|
|
1375
|
+
}
|
|
1376
|
+
catch {
|
|
1377
|
+
return null;
|
|
1378
|
+
}
|
|
1379
|
+
for (;;) {
|
|
1380
|
+
if (existsSync(join(at, ".git"))) {
|
|
1381
|
+
try {
|
|
1382
|
+
return checkoutGitSync(at, ["rev-parse", "--show-toplevel"], { timeoutMs }).trim() || null;
|
|
1383
|
+
}
|
|
1384
|
+
catch {
|
|
1385
|
+
return null;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
const up = dirname(at);
|
|
1389
|
+
if (up === at)
|
|
1390
|
+
return null;
|
|
1391
|
+
at = up;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* CLI show: Ready for review once the saved result can be inspected, Complete
|
|
5
5
|
* once the lead or a person has marked that exact result. Slack, Discord and
|
|
6
6
|
* Teams send this same card. */
|
|
7
|
+
import { machineChecked } from "./machine-check.js";
|
|
7
8
|
import { assignmentOf } from "./assignment.js";
|
|
8
9
|
import { chatResultHref, chatControlHref } from "./chat-controls.js";
|
|
9
10
|
import { phoneText, projectLabel } from "./telegram-status.js";
|
|
@@ -55,8 +56,8 @@ export function telegramProgressCard(store, run, taskId, project, now = new Date
|
|
|
55
56
|
}
|
|
56
57
|
else if (accessBlocked) {
|
|
57
58
|
facts = { stage: "needs-you", need: "other", reason: holds.some(hold => hold.ownerKind === "backoff")
|
|
58
|
-
? "The
|
|
59
|
-
: "The
|
|
59
|
+
? "The builder can't read the project folder. It retries after a short pause; check its folder access."
|
|
60
|
+
: "The builder can't read the project folder. Restore its access, then resume." };
|
|
60
61
|
}
|
|
61
62
|
else if (run.outcome === null && stop !== null && stop.resumedAt === null) {
|
|
62
63
|
facts = { stage: "stopped", reason: stop.settledAt === null ? "Stopping. Waiting for the running process to end." : "Paused by a person. Inspect the saved work, then resume." };
|
|
@@ -65,7 +66,8 @@ export function telegramProgressCard(store, run, taskId, project, now = new Date
|
|
|
65
66
|
facts = { stage: "stopped", reason: `On hold: ${phoneText(operatorHold.reason, 150)}` };
|
|
66
67
|
}
|
|
67
68
|
else if (run.outcome === null) {
|
|
68
|
-
facts = { stage: run.phase !== null && run.phase !== "agent-running" ? "checking" : run.role === "planner" ? "planning" : "building"
|
|
69
|
+
facts = { stage: run.phase !== null && run.phase !== "agent-running" ? "checking" : run.role === "planner" ? "planning" : "building",
|
|
70
|
+
...(machineChecked(store, taskId) ? { machine: true } : {}) };
|
|
69
71
|
}
|
|
70
72
|
else if (run.outcome === "parked") {
|
|
71
73
|
facts = { stage: "needs-you", need: "answer" };
|
package/dist/telegram-status.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { publicChatText } from './chat-display.js';
|
|
2
|
+
import { machineChecked } from "./machine-check.js";
|
|
2
3
|
import { manualReviewOnly } from "./proof.js";
|
|
3
4
|
/** Read-only phone views over the same dispatch and proof records as chat.
|
|
4
5
|
* No provider calls, repository access, new workflow state, or inferred success. */
|
|
@@ -65,7 +66,8 @@ function headlineFor(store, id, d, read_ = {}) {
|
|
|
65
66
|
return taskStatusOf(assignmentStatusFacts(assignment));
|
|
66
67
|
}
|
|
67
68
|
const finished = read.stage === "finished" || read.stage === "complete" || read.stage === "failed";
|
|
68
|
-
return taskStatusOf({ stage: read.stage, ...(read.need === undefined ? {} : { need: read.need }), reason: finished ? null : plainReasonOf(read.stage, d.code, d.detail),
|
|
69
|
+
return taskStatusOf({ stage: read.stage, ...(read.need === undefined ? {} : { need: read.need }), ...(read.wait === undefined ? {} : { wait: read.wait }), reason: finished ? null : plainReasonOf(read.stage, d.code, d.detail),
|
|
70
|
+
...(machineChecked(store, id) ? { machine: true } : {}),
|
|
69
71
|
checks: read.stage === "failed" && d.code === "proof-refuted" ? { status: "failed", exitCode: null, head: null } : null });
|
|
70
72
|
}
|
|
71
73
|
function groupOf(d) {
|
|
@@ -238,7 +240,7 @@ export function phoneTaskView(store, repos, id, now, root) {
|
|
|
238
240
|
const hiddenDependency = blocker !== null && (blocker.repo === null || !repos.includes(blocker.repo));
|
|
239
241
|
lines.push(hiddenDependency ? "A required task outside this phone view has not finished. Open the dependency in the console." : plain(shared.sentence, 650));
|
|
240
242
|
if (d.nextAt !== null)
|
|
241
|
-
lines.push(`Earliest recorded wake: ${plain(d.nextAt, 40)} (a connected
|
|
243
|
+
lines.push(`Earliest recorded wake: ${plain(d.nextAt, 40)} (a connected builder is still required).`);
|
|
242
244
|
const runs = store.runsFor(ref.id);
|
|
243
245
|
const result = task.state === "done" ? runs.find(r => (r.role === "builder" || r.role === "scout") && (r.outcome === "built" || r.outcome === "no-change") && r.finishedAt !== null) : undefined;
|
|
244
246
|
const latest = result ?? runs[0];
|
|
@@ -262,14 +264,14 @@ export function phoneTaskView(store, repos, id, now, root) {
|
|
|
262
264
|
: approvedCheckStateOf(approvedCheckOf(proof.verdict, proof.reasons, checksFactOf(assignmentChecksForRun(store, root, result.id, now), result.headRevision)));
|
|
263
265
|
const verifiedWords = root === undefined ? "Open the result to see its checks" : checkNow === "passed" ? "Checks passed at completion"
|
|
264
266
|
: checkNow === "failed" ? "Checks failed" : checkNow === "running" ? "Checks running" : "The check result couldn't be read";
|
|
265
|
-
const proofWords = { verified: verifiedWords, attested: "Checks reported by the agent, not run by Toolroll", short: "Required saved material is missing", refuted: "The saved result
|
|
267
|
+
const proofWords = { verified: verifiedWords, attested: "Checks reported by the agent, not run by Toolroll", short: "Required saved material is missing", refuted: "The saved result doesn't match the approved plan" };
|
|
266
268
|
lines.push(`Checks: ${proof === null ? "No completion record saved" : manualReviewOnly(proof) ? accepted ? "Accepted by a person; the recorded checks are unchanged" : "A person must inspect this result; no recorded check failed" : proofWords[proof.verdict]}.`);
|
|
267
269
|
if (manualReviewOnly(proof) && !accepted)
|
|
268
270
|
lines.push('Reply “Send the result summary” for the checks, notes and screenshots.');
|
|
269
271
|
if (proof !== null && proof.matrix.length > 0)
|
|
270
272
|
lines.push(`Requirements: ${proof.matrix.filter(row => row.state === "pass").length}/${proof.matrix.length} satisfied in the saved record.`);
|
|
271
273
|
if (accepted && !manualReviewOnly(proof))
|
|
272
|
-
lines.push("An
|
|
274
|
+
lines.push("An approver accepted this result; that does not change its recorded checks.");
|
|
273
275
|
const publication = store.publicationForRun(result.id);
|
|
274
276
|
const delivery = publication?.remoteState === "MERGED" ? `#${publication.prNumber ?? "?"} merged` : publication?.remoteState === "CLOSED" ? `#${publication.prNumber ?? "?"} closed without merging` : publication?.state === "opened" ? `#${publication.prNumber ?? "?"} open` : publication?.state === "pushed" || publication?.state === "intended" ? "Opening…" : publication?.state === "failed" ? "Couldn't open; the commit is safe locally" : null;
|
|
275
277
|
if (delivery !== null)
|
package/dist/test-infra.d.ts
CHANGED
|
@@ -24,3 +24,11 @@ export declare function testInfrastructure(files: ReadonlyMap<string, string>, a
|
|
|
24
24
|
* the other rules don't already trust: any source file that is neither a test nor product code.
|
|
25
25
|
*/
|
|
26
26
|
export declare function mightName(open: Unresolved, path: string, found: TestInfrastructure): boolean;
|
|
27
|
+
/** Whether a path is a test file by any extension a test runner could be pointed at, not only those run today. */
|
|
28
|
+
export declare const isTestPath: (path: string) => boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Why a new test file can't carry on what it imports, or null (docs/plans/trusted-release-reports.md, H4). A test file is
|
|
31
|
+
* never infrastructure here, so nothing else looks at an edge between two of them, and importing one runs its top level
|
|
32
|
+
* (its `vi.mock`s and hooks included) in the importer. A computed import might name one, so it refuses too.
|
|
33
|
+
*/
|
|
34
|
+
export declare function testImportProblem(path: string, text: string): string | null;
|
package/dist/test-infra.js
CHANGED
|
@@ -319,3 +319,21 @@ export function mightName(open, path, found) {
|
|
|
319
319
|
const stem = path.replace(/\.[^/.]+$/, "");
|
|
320
320
|
return path === prefix || stem === prefix.replace(/\.(?:[cm]?js|jsx)$/, "") || path.startsWith(`${prefix}/`) || (open.specifier !== null && open.specifier.endsWith("/") === false && path.startsWith(prefix));
|
|
321
321
|
}
|
|
322
|
+
/** Whether a path is a test file by any extension a test runner could be pointed at, not only those run today. */
|
|
323
|
+
export const isTestPath = (path) => TEST.test(path);
|
|
324
|
+
/** A specifier that names a test file, whatever it is spelled as: `./a.test.js`, `../b.test`, `@/c.test.ts`. */
|
|
325
|
+
const NAMES_TEST = /\.test(?:\.[cm]?[jt]sx?)?(?:[?#].*)?$/;
|
|
326
|
+
/**
|
|
327
|
+
* Why a new test file can't carry on what it imports, or null (docs/plans/trusted-release-reports.md, H4). A test file is
|
|
328
|
+
* never infrastructure here, so nothing else looks at an edge between two of them, and importing one runs its top level
|
|
329
|
+
* (its `vi.mock`s and hooks included) in the importer. A computed import might name one, so it refuses too.
|
|
330
|
+
*/
|
|
331
|
+
export function testImportProblem(path, text) {
|
|
332
|
+
const { literal, computed, soft } = specifiersOf(path, text);
|
|
333
|
+
const named = [...literal, ...soft].find(spec => NAMES_TEST.test(spec));
|
|
334
|
+
if (named !== undefined)
|
|
335
|
+
return `${path} imports another test file (${named}), which would run its code here`;
|
|
336
|
+
if (computed.length > 0)
|
|
337
|
+
return `${path} imports a path it computes, which might name another test file`;
|
|
338
|
+
return null;
|
|
339
|
+
}
|
package/dist/tidy.js
CHANGED
|
@@ -478,7 +478,7 @@ export function familyRepoOf(store, refs) {
|
|
|
478
478
|
}
|
|
479
479
|
/** "Archived by tidy: <reason>" or "Archived by <person>: <reason>": the one sentence every surface shows. */
|
|
480
480
|
export function archivedWords(archived) {
|
|
481
|
-
return
|
|
481
|
+
return `${archived.byKind === "tidy" ? "Archived automatically" : `Archived by ${archived.by}`}: ${archived.reason}`;
|
|
482
482
|
}
|
|
483
483
|
/** The release journals beside the store's own database file (none for an in-memory one). */
|
|
484
484
|
export function releaseFactsOf(store) {
|
|
@@ -24,7 +24,7 @@ export function updateStepsHtml(j, running, options = {}) {
|
|
|
24
24
|
const items = UPDATE_STEPS.map((step, i) => {
|
|
25
25
|
const state = j.phase === "complete" || i < last ? "done" : i > last ? "waiting" : failed ? "failed" : finished ? "waiting" : "now";
|
|
26
26
|
// Only where it adds something: which work it waits for, or what went wrong.
|
|
27
|
-
const detail = state === "now" && step === "draining" && j.waiting ? html `${j.waiting.on}.${j.waiting.action ? html `
|
|
27
|
+
const detail = state === "now" && step === "draining" && j.waiting ? html `${j.waiting.on}.${j.waiting.action ? html ` To have Toolroll check again, run <code class="k-mono">${j.waiting.action}</code>.` : ""}`
|
|
28
28
|
: (state === "now" && step === "draining") || (state === "failed" && j.phase === "rolling-back") ? html `${j.detail}` : "";
|
|
29
29
|
return settingsRecord({ title: STEP_WORDS[step], sub: detail, when: STEP_CHIP[state], attrs: { "data-step": step, "data-state": state } });
|
|
30
30
|
});
|
|
@@ -43,7 +43,7 @@ function outcomeHtml(j) {
|
|
|
43
43
|
// Stopped on a finished run it can't show has ended: what is in the way and the one command, nothing more.
|
|
44
44
|
if (j.phase === "refused" && j.waiting) {
|
|
45
45
|
return settingsSection({ id: "update-outcome", title: `Update to ${j.to.version} stopped: run #${j.waiting.run} is in the way`,
|
|
46
|
-
body: html `<div data-update-outcome="stopped">${infoLine(`${j.waiting.on}. New work resumed.`)}${j.waiting.action ? note(html `
|
|
46
|
+
body: html `<div data-update-outcome="stopped">${infoLine(`${j.waiting.on}. New work resumed.`)}${j.waiting.action ? note(html `To have Toolroll check again, run <code class="k-mono">${j.waiting.action}</code>, then update again.`) : note("Update again once it has stopped.")}</div>` });
|
|
47
47
|
}
|
|
48
48
|
const title = j.phase === "refused" ? `Didn't update to ${j.to.version}` : j.phase === "restored" ? `Update to ${j.to.version} didn't finish` : "The update needs attention";
|
|
49
49
|
return settingsSection({ id: "update-outcome", title, state: html ` ${chip("Failed", "failed")}`,
|
package/dist/toolroll-update.js
CHANGED
|
@@ -39,7 +39,10 @@ import { installLaunchdService, launchdPlist, stopLaunchdService, writeFileDurab
|
|
|
39
39
|
import { LAPTOP_LABEL_DARWIN, LAPTOP_LABEL_LINUX, unitFlag } from "./laptop-service.js";
|
|
40
40
|
import { processGone, readRunnerProcessRecord, realProcessProbe, recordStandsFor, RUNNER_PROCESS_DIR } from "./runner-process.js";
|
|
41
41
|
import { NAME } from "./names.js";
|
|
42
|
-
import {
|
|
42
|
+
import { V151_ADDED_COLUMNS } from "./storage.js";
|
|
43
|
+
import { orphanKey, updateSafeSchema, V117_RENAMED_COLUMNS, V117_RENAMED_TABLES, V118_MERGED_TABLES, V119_ADDED_COLUMNS, V120_ADDED_COLUMNS, V121_ADDED_COLUMNS, V122_ADDED_COLUMNS, V123_ADDED_COLUMNS, V125_ADDED_COLUMNS, V126_ADDED_COLUMNS, V128_ADDED_COLUMNS, V129_ADDED_COLUMNS, V134_ADDED_COLUMNS, V135_ADDED_COLUMNS, V138_ADDED_COLUMNS, V143_ADDED_COLUMNS, V147_ADDED_COLUMNS, V148_ADDED_COLUMNS, V150_ADDED_COLUMNS } from "./store.js";
|
|
44
|
+
import { V152_ADDED_COLUMNS } from "./code-map-store.js";
|
|
45
|
+
import { V153_ADDED_COLUMNS } from "./plain-schema.js";
|
|
43
46
|
import { isNewer, REGISTRY } from "./releases.js";
|
|
44
47
|
import { markNeverIndex } from "./never-index.js";
|
|
45
48
|
import { readRuntimeUpdateJournal, RUNTIME_PHASES, RUNTIME_UPDATE_STEPS, stagedStartedAt, updaterStartingOf } from "./contracts/update-journal.js";
|
|
@@ -379,12 +382,16 @@ function addedColumns(...versions) {
|
|
|
379
382
|
return rules;
|
|
380
383
|
}
|
|
381
384
|
/** Rules on tables that later gained declared columns (addedColumns merges into these instead of replacing them). */
|
|
382
|
-
const EARLIER_RULES = { flow_trigger: { fromRoutines: "trigger" }
|
|
385
|
+
const EARLIER_RULES = { flow_trigger: { fromRoutines: "trigger" },
|
|
386
|
+
// v114: a finished run whose process witnesses have all exited keeps one summary row in place of them (v150 adds
|
|
387
|
+
// their machine id).
|
|
388
|
+
run_process: { compacted: { into: "run_process_summary", sum: "witnesses" } } };
|
|
383
389
|
const HISTORY_RULES = {
|
|
384
390
|
...Object.fromEntries(Object.entries(CHAT_MOVES).map(([table, moved]) => [table, { moved }])),
|
|
385
391
|
action_ledger: { appendOnly: true }, ledger_seal: { appendOnly: true }, ledger_checkpoint: { appendOnly: true },
|
|
386
|
-
// v114: a finished run whose process witnesses have all exited keeps one summary row in place of them
|
|
387
|
-
run_process
|
|
392
|
+
// v114: a finished run whose process witnesses have all exited keeps one summary row in place of them (the
|
|
393
|
+
// run_process rule is in EARLIER_RULES, under its v150 column).
|
|
394
|
+
run_process: EARLIER_RULES["run_process"], run_process_summary: { summarizes: "run_process" },
|
|
388
395
|
// v114: single-destination delivery columns may be dropped only once their values are legacy receipts.
|
|
389
396
|
notification: { dropped: ["attempts", "last_attempt_at", "last_error", "delivered_at", "receipt", "claim_owner", "claim_expires_at"], carried: { into: "notification_delivery", destination: LEGACY_DESTINATION } },
|
|
390
397
|
notification_delivery: { receives: LEGACY_DESTINATION },
|
|
@@ -414,7 +421,13 @@ const HISTORY_RULES = {
|
|
|
414
421
|
// v147: an account's password flag reads set (1), a browser session reads full with no local binding or key, and the
|
|
415
422
|
// console record reads no key epoch.
|
|
416
423
|
// v148: a run's execution class and a revision's origin read none (NULL), and a project's suggestion switch reads on.
|
|
417
|
-
|
|
424
|
+
// v149 adds tables only (the learning-loops records): no saved row changes, so no rule is needed for it.
|
|
425
|
+
// v150: a saved process witness reads no machine id (NULL), and is matched by host name as before.
|
|
426
|
+
// v151: a saved cleanup setting's idle limit reads none (NULL): the default.
|
|
427
|
+
// v152: a saved project's code map reads on, with no enrollment yet; the code map's tables are new.
|
|
428
|
+
// v153: a saved task reads no fast start and a saved project no fast-start setting (NULL); its tables are new.
|
|
429
|
+
// v154 adds a table only (check_stamp): no saved row changes, so no rule is needed for it.
|
|
430
|
+
...Object.fromEntries(Object.entries(addedColumns(V119_ADDED_COLUMNS, V120_ADDED_COLUMNS, V121_ADDED_COLUMNS, V122_ADDED_COLUMNS, V123_ADDED_COLUMNS, V125_ADDED_COLUMNS, V126_ADDED_COLUMNS, V128_ADDED_COLUMNS, V129_ADDED_COLUMNS, V134_ADDED_COLUMNS, V135_ADDED_COLUMNS, V138_ADDED_COLUMNS, V143_ADDED_COLUMNS, V147_ADDED_COLUMNS, V148_ADDED_COLUMNS, V150_ADDED_COLUMNS, V151_ADDED_COLUMNS, V152_ADDED_COLUMNS, V153_ADDED_COLUMNS))
|
|
418
431
|
.map(([table, rule]) => [table, { ...EARLIER_RULES[table], ...rule }])),
|
|
419
432
|
};
|
|
420
433
|
/** The tables a migration may remove whole (v115's removed features). */
|
package/dist/tui-contract.d.ts
CHANGED
|
@@ -42,6 +42,8 @@ export type TuiTask = {
|
|
|
42
42
|
/** A finished result that can be marked Complete or sent back with a revision. */
|
|
43
43
|
readonly hasResult: boolean;
|
|
44
44
|
readonly decisions: readonly TuiDecision[];
|
|
45
|
+
/** "Can I merge?" (merge-checklist.ts, `toolroll task ready`'s lines): the answer, then each check. Empty: no result. */
|
|
46
|
+
readonly ready?: readonly string[];
|
|
45
47
|
};
|
|
46
48
|
/** The welcome's steps, as lines (welcome-facts.ts). */
|
|
47
49
|
export type TuiSetup = {
|
|
@@ -56,11 +58,19 @@ export type TuiSnapshot = {
|
|
|
56
58
|
readonly projects: readonly string[];
|
|
57
59
|
/** Whether the local owner has set a password (decides where a refusal leads). */
|
|
58
60
|
readonly passwordSet: boolean;
|
|
61
|
+
/** One line about flaky tests and project memory waiting, and the commands that show them; null when there's nothing. */
|
|
62
|
+
readonly learning?: string | null;
|
|
59
63
|
};
|
|
60
64
|
export type TuiAct = {
|
|
61
65
|
readonly kind: "new";
|
|
62
66
|
readonly text: string;
|
|
63
67
|
readonly repo: string | null;
|
|
68
|
+
}
|
|
69
|
+
/** Build: the terms the person just read (fast-start.ts). */
|
|
70
|
+
| {
|
|
71
|
+
readonly kind: "build";
|
|
72
|
+
readonly preview: string;
|
|
73
|
+
readonly digest: string;
|
|
64
74
|
} | {
|
|
65
75
|
readonly kind: "approve";
|
|
66
76
|
readonly id: string;
|
|
@@ -76,11 +86,23 @@ export type TuiAct = {
|
|
|
76
86
|
} | {
|
|
77
87
|
readonly kind: "complete";
|
|
78
88
|
readonly id: string;
|
|
89
|
+
} | {
|
|
90
|
+
readonly kind: "retry";
|
|
91
|
+
readonly id: string;
|
|
79
92
|
} | {
|
|
80
93
|
readonly kind: "answer";
|
|
81
94
|
readonly decision: number;
|
|
82
95
|
readonly option: string;
|
|
83
96
|
};
|
|
97
|
+
/** What Build would approve: the same lines every approval surface reads. */
|
|
98
|
+
export type TuiFastTerms = {
|
|
99
|
+
readonly preview: string;
|
|
100
|
+
readonly digest: string;
|
|
101
|
+
readonly terms: readonly {
|
|
102
|
+
readonly label: string;
|
|
103
|
+
readonly words: string;
|
|
104
|
+
}[];
|
|
105
|
+
};
|
|
84
106
|
/** A write's answer: its words. `needsPassword`: refused because the local owner may not do it. */
|
|
85
107
|
export type TuiActResult = {
|
|
86
108
|
readonly ok: true;
|
|
@@ -100,6 +122,8 @@ export interface TuiSource {
|
|
|
100
122
|
/** A counter that moves when the plane's work changes (the store's wake sequence). */
|
|
101
123
|
changed(): number;
|
|
102
124
|
act(act: TuiAct): Promise<TuiActResult>;
|
|
125
|
+
/** A small task's exact terms, or null when it takes the ordinary road (New task then files it). */
|
|
126
|
+
fastTerms(text: string, repo: string | null): Promise<TuiFastTerms | null>;
|
|
103
127
|
/** The welcome's steps, or null once setup is done. */
|
|
104
128
|
setup(): Promise<TuiSetup | null>;
|
|
105
129
|
}
|