toolroll 0.9.74 → 0.9.76
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 +80 -0
- package/dist/agent-fence.js +8 -0
- package/dist/approval-authority.js +6 -2
- package/dist/approval-rules-ui.d.ts +11 -0
- package/dist/approval-rules-ui.js +13 -1
- package/dist/assignment.d.ts +3 -0
- package/dist/assignment.js +27 -8
- package/dist/batch-checks.js +3 -1
- package/dist/browser/workspace.js +27 -27
- package/dist/browser-workspace.d.ts +1 -1
- package/dist/builder.js +1 -1
- package/dist/chat-acceptance.d.ts +4 -1
- package/dist/chat-acceptance.js +13 -3
- package/dist/cli.js +1 -1
- package/dist/coding-handoff.js +4 -3
- package/dist/coding-ui.js +2 -2
- package/dist/coding-workspace.js +23 -9
- package/dist/contracts/console-api.d.ts +6 -1
- package/dist/contracts/console-api.js +11 -0
- package/dist/control-ui.d.ts +3 -2
- package/dist/control-ui.js +7 -2
- package/dist/demo.js +5 -3
- package/dist/deploy-gate.d.ts +3 -0
- package/dist/deploy-gate.js +3 -2
- package/dist/desktop-update.d.ts +4 -1
- package/dist/desktop-update.js +43 -9
- package/dist/discover.js +6 -1
- package/dist/first-run.d.ts +8 -5
- package/dist/first-run.js +42 -7
- package/dist/flow-card-task.js +3 -2
- package/dist/flow-unattended.d.ts +2 -0
- package/dist/flow-unattended.js +12 -1
- package/dist/git-incident.d.ts +49 -0
- package/dist/git-incident.js +69 -0
- package/dist/git-views.d.ts +20 -0
- package/dist/git-views.js +120 -0
- package/dist/grant.js +3 -1
- package/dist/integrations.d.ts +4 -3
- package/dist/integrations.js +5 -6
- package/dist/lead-contract.d.ts +1 -1
- package/dist/lead-contract.js +2 -2
- package/dist/lead-follow.js +3 -1
- package/dist/openrouter-models.d.ts +5 -2
- package/dist/openrouter-models.js +7 -4
- package/dist/operate.js +9 -1
- package/dist/planner.js +5 -1
- package/dist/project-knowledge.js +1 -1
- package/dist/project-learning.js +65 -7
- package/dist/project-tools.js +19 -1
- package/dist/pull-request-flow.js +4 -0
- package/dist/pull-request-ui.d.ts +1 -1
- package/dist/pull-request-ui.js +5 -1
- package/dist/push.d.ts +2 -0
- package/dist/push.js +24 -19
- package/dist/read-only-request.d.ts +4 -0
- package/dist/read-only-request.js +15 -0
- package/dist/release-adapters.d.ts +24 -2
- package/dist/release-adapters.js +136 -10
- package/dist/release-authority.d.ts +46 -0
- package/dist/release-authority.js +64 -0
- package/dist/release-auto.d.ts +153 -0
- package/dist/release-auto.js +496 -0
- package/dist/release-cli.js +56 -2
- package/dist/release-coverage.js +1 -1
- package/dist/release.d.ts +105 -1
- package/dist/release.js +229 -9
- package/dist/repository-context.js +5 -2
- package/dist/result-follow-ups.js +23 -2
- package/dist/result-review.js +5 -1
- package/dist/scope.d.ts +31 -2
- package/dist/scope.js +44 -3
- package/dist/scout.js +5 -1
- package/dist/serve.js +162 -6
- package/dist/server/agents-live-filed.d.ts +17 -0
- package/dist/server/agents-live-filed.js +97 -0
- package/dist/server/agents-live-room.js +4 -1
- package/dist/server/chat.js +31 -23
- package/dist/server/http.d.ts +5 -0
- package/dist/server/last-checks.d.ts +17 -0
- package/dist/server/last-checks.js +22 -0
- package/dist/server/pages.js +18 -4
- package/dist/server/people-tokens.js +7 -4
- package/dist/server/render-pages.d.ts +2 -1
- package/dist/server/render-pages.js +28 -24
- package/dist/server/render-results.d.ts +11 -2
- package/dist/server/render-results.js +54 -12
- package/dist/server/render-settings.js +3 -3
- package/dist/server/render-tasks.d.ts +10 -0
- package/dist/server/render-tasks.js +30 -17
- package/dist/server/route-table.js +17 -1
- package/dist/server/runtime.d.ts +16 -1
- package/dist/server/settings.js +111 -31
- package/dist/server/task-views.js +8 -1
- package/dist/server/tasks.js +11 -2
- package/dist/storage-ui.d.ts +2 -0
- package/dist/storage-ui.js +1 -1
- package/dist/storage.js +2 -1
- package/dist/store.d.ts +4 -1
- package/dist/store.js +52 -13
- package/dist/subagent-work.js +3 -0
- package/dist/task-base.js +1 -1
- package/dist/task-status.d.ts +28 -1
- package/dist/task-status.js +93 -11
- package/dist/teardown-git.d.ts +59 -5
- package/dist/teardown-git.js +1022 -90
- package/dist/telegram-progress.js +4 -4
- package/dist/telegram-status.js +8 -3
- package/dist/test-infra.d.ts +26 -0
- package/dist/test-infra.js +321 -0
- package/dist/tool-secret-store.js +8 -0
- package/dist/toolroll-update-ui.d.ts +2 -0
- package/dist/toolroll-update-ui.js +4 -3
- package/dist/toolroll-update.js +10 -9
- package/dist/work-index.js +29 -12
- package/dist/workspace-ui.d.ts +5 -0
- package/dist/workspace-ui.js +38 -10
- package/dist/worktree.d.ts +4 -0
- package/dist/worktree.js +13 -7
- package/package.json +1 -1
package/dist/storage-ui.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export type StorageView = {
|
|
|
13
13
|
schedule?: SweepSchedule;
|
|
14
14
|
};
|
|
15
15
|
landed?: LandedSummary;
|
|
16
|
+
/** When the checkouts were last measured, and its Check now (docs/plans/get-side-effects.md). */
|
|
17
|
+
measured?: Html;
|
|
16
18
|
};
|
|
17
19
|
export declare function storageHtml(view: StorageView, notice: {
|
|
18
20
|
said?: string | null;
|
package/dist/storage-ui.js
CHANGED
|
@@ -85,5 +85,5 @@ export function storageHtml(view, notice) {
|
|
|
85
85
|
const preview = html `<details class="clean-up" data-clean-up${notice.problem?.startsWith("Enter your Toolroll password to clean") ? html ` open` : ""}><summary>Clean up</summary><div class="preview">${plan.go.length === 0 ? html `<p>Nothing to remove now.</p>` :
|
|
86
86
|
html `<h3>Removes ${plural(plan.go.length, "checkout")}, about ${bytesWords(plan.freeBytes)}</h3><p class="meta">Their branches stay, so nothing committed is lost.</p><ul class="checkouts" data-goes>${plan.go.map(one => item(one))}</ul>`}${plan.stay.length === 0 ? "" : html `<h3>Stays (${plan.stay.length})</h3><ul class="checkouts" data-stays>${plan.stay.map(one => item(one))}</ul>`}${remove}</div></details>`;
|
|
87
87
|
const options = CLEANUP_CHOICES.map(one => html `<option value="${one.value}"${one.value === plan.cleanup ? html ` selected` : ""}>${one.label}</option>`);
|
|
88
|
-
return html `<section class="storage">${note}<div class="card storage-state"><h2 data-checkout-bytes="${plan.totalBytes}">Checkouts use ${bytesWords(plan.totalBytes)}</h2><p class="meta">${facts}</p
|
|
88
|
+
return html `<section class="storage">${note}<div class="card storage-state"><h2 data-checkout-bytes="${plan.totalBytes}">Checkouts use ${bytesWords(plan.totalBytes)}</h2><p class="meta">${facts}</p>${view.measured ?? ""}<p data-clean-bytes="${plan.freeBytes}">${frees}</p>${view.sweep === undefined ? "" : sweepHtml(view.sweep)}${view.landed === undefined ? "" : landedHtml(view.landed)}${preview}</div><h2>Automatic cleanup</h2>${postForm("/settings/storage", html `<label>Remove a finished task's clean checkout<select name="cleanup">${options}</select></label><p class="meta">Its branch stays. Checkouts with changes, commits on no branch, in use, or waiting for review are kept. When a task's work is already in main, Toolroll marks it complete and removes its checkout and local branches.</p>${reauth(html `<label>Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label>`)}<button type="submit">Save</button>`, { attrs: { class: "setting" } })}</section>`;
|
|
89
89
|
}
|
package/dist/storage.js
CHANGED
|
@@ -101,7 +101,8 @@ export function diskBytes(paths) {
|
|
|
101
101
|
if (process.platform !== "win32") {
|
|
102
102
|
for (let at = 0; at < paths.length; at += 200) {
|
|
103
103
|
const batch = paths.slice(at, at + 200);
|
|
104
|
-
|
|
104
|
+
// Bounded: a slow disk leaves the rest unmeasured rather than holding everything up.
|
|
105
|
+
const du = spawnSync("du", ["-sk", ...batch], { encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 60_000 });
|
|
105
106
|
for (const line of (du.stdout ?? "").split("\n")) {
|
|
106
107
|
const match = /^(\d+)\t(.+)$/.exec(line);
|
|
107
108
|
if (match !== null && batch.includes(match[2]))
|
package/dist/store.d.ts
CHANGED
|
@@ -2355,7 +2355,7 @@ export declare class Store {
|
|
|
2355
2355
|
* exactly what the signed digest was bound to. Returns false when the
|
|
2356
2356
|
* scope vanished mid-ceremony. */
|
|
2357
2357
|
sealScopeApproval(taskId: string, by: string, now: Date, mutation?: Mutation, basis?: {
|
|
2358
|
-
kind: "mode" | "chat" | "grant" | "schedule";
|
|
2358
|
+
kind: "mode" | "chat" | "grant" | "schedule" | "auto-check" | "auto-green";
|
|
2359
2359
|
modeDigest: string;
|
|
2360
2360
|
/** A chat seal of a deploy gate: the release card it came from (chat-management.md §0a.5). */
|
|
2361
2361
|
card?: "release";
|
|
@@ -2397,6 +2397,9 @@ export declare class Store {
|
|
|
2397
2397
|
signedBy: string;
|
|
2398
2398
|
absoluteExpiry: string;
|
|
2399
2399
|
} | null;
|
|
2400
|
+
/** Close every operating mode past its expiry (the background pass). Returns how many were closed. */
|
|
2401
|
+
closeExpiredModes(now: Date): number;
|
|
2402
|
+
private closeExpiredMode;
|
|
2400
2403
|
/** Sign (or renew) a mode: closes any predecessor with a typed event and
|
|
2401
2404
|
* ATOMICALLY reconciles the repo's nonterminal merge intents to the new
|
|
2402
2405
|
* posture (F2). The caller has already run the password ceremony. */
|
package/dist/store.js
CHANGED
|
@@ -97,6 +97,7 @@ import { REVIEW_SCHEMA, reviewSwitchWords } from "./review-switch.js";
|
|
|
97
97
|
import { SPEND_SCHEMA, billingOf, budgetStates, canPrice, claudeMachineBilling, countsToward, filersOf, monthOf, priceWork, seenBilling as seenBillingOf, spendItems, subagentFilers, usd } from "./spend.js";
|
|
98
98
|
import { DEFAULT_PERIODS, RETENTION_SCHEMA, periodWords, widenRetentionSchema } from "./retention.js";
|
|
99
99
|
import { CHECKOUT_CLEANUP_SCHEMA, LANDED_CLEANUP_SCHEMA, STORAGE_SWEEP_SCHEMA, DEFAULT_CLEANUP, cleanupWords } from "./storage.js";
|
|
100
|
+
import { inReadOnlyRequest } from "./read-only-request.js";
|
|
100
101
|
import { IN_RANGE, LEDGER_CHAIN_SCHEMA, safeWhole, sealLedger, verifyLedgerChain } from "./ledger-chain.js";
|
|
101
102
|
import { NO_RULES, filerFor, isProtectedWork, protectedChanges, rulesWords } from "./approval-policy.js";
|
|
102
103
|
import { configBase, envValue, namedPath } from "./names.js";
|
|
@@ -7013,7 +7014,17 @@ export class Store {
|
|
|
7013
7014
|
const deployGate = (typeof working["candidate"] === "string" && working["candidate"] !== "") || working["release_terms_json"] != null;
|
|
7014
7015
|
if (deployGate && basis?.kind === "chat" && !(basis.card === "release" && typeof basis.cardDigest === "string" && /^[0-9a-f]{64}$/.test(basis.cardDigest)))
|
|
7015
7016
|
return false;
|
|
7016
|
-
|
|
7017
|
+
// AUTOMATIC RELEASE SEALS (auto-approve-green.md): only on a release task (a candidate and release terms signed
|
|
7018
|
+
// in), only naming the evidence row the plane wrote for exactly this task, kind, digest and candidate.
|
|
7019
|
+
const automatic = basis?.kind === "auto-check" || basis?.kind === "auto-green";
|
|
7020
|
+
if (automatic) {
|
|
7021
|
+
if (terms.terms === null || typeof working["candidate"] !== "string" || working["candidate"] === "")
|
|
7022
|
+
return false;
|
|
7023
|
+
const evidence = autoEvidenceOf(this, taskId, basis.modeDigest, basis.kind);
|
|
7024
|
+
if (evidence === null || evidence.digest !== String(working["digest"]) || evidence.candidate !== working["candidate"] || evidence.by !== by)
|
|
7025
|
+
return false;
|
|
7026
|
+
}
|
|
7027
|
+
if (terms.terms !== null && basis !== undefined && basis.kind !== "chat" && !automatic)
|
|
7017
7028
|
return false;
|
|
7018
7029
|
if (terms.terms !== null && releaseSealProblem(this, terms.terms) !== null)
|
|
7019
7030
|
return false;
|
|
@@ -7032,7 +7043,8 @@ export class Store {
|
|
|
7032
7043
|
approved_route_json = proposed_route_json
|
|
7033
7044
|
WHERE task_id = ?`)
|
|
7034
7045
|
.run(now.toISOString(), by, basis === undefined ? "password" : basis.kind, basis === undefined ? null : basis.modeDigest, taskId);
|
|
7035
|
-
|
|
7046
|
+
// `auto-check` only lets the check run: it opens no release authority (only `auto-green` and a person's yes do).
|
|
7047
|
+
if (changed.changes > 0 && terms.ok && terms.terms !== null && basis?.kind !== "auto-check") {
|
|
7036
7048
|
if (openReleaseAuthority(this, { taskId, digest: String(working["digest"]), terms: terms.terms, by, now }) === null)
|
|
7037
7049
|
throw new ReleaseSealRefused();
|
|
7038
7050
|
}
|
|
@@ -7065,7 +7077,11 @@ export class Store {
|
|
|
7065
7077
|
? "Approved automatically: its flow runs on its own. A connected worker can take it next."
|
|
7066
7078
|
: basis.kind === "schedule"
|
|
7067
7079
|
? "Approved by its standing order. A connected worker can take it next."
|
|
7068
|
-
:
|
|
7080
|
+
: basis.kind === "auto-check"
|
|
7081
|
+
? "Checking automatically: it approves itself once CI and the release check pass."
|
|
7082
|
+
: basis.kind === "auto-green"
|
|
7083
|
+
? "Approved automatically: CI and the release check passed."
|
|
7084
|
+
: "Approved automatically under the operating mode. A connected worker can take it next.",
|
|
7069
7085
|
link: chatControlHref("task", taskId),
|
|
7070
7086
|
}, now);
|
|
7071
7087
|
}
|
|
@@ -7163,15 +7179,10 @@ export class Store {
|
|
|
7163
7179
|
if (row === undefined)
|
|
7164
7180
|
return null;
|
|
7165
7181
|
if (Date.parse(String(row["absolute_expiry"])) <= now.getTime()) {
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
this.db
|
|
7171
|
-
.prepare("INSERT INTO operating_mode_event (mode, kind, actor, at) VALUES (?, 'expired-closed', 'the-clock', ?)")
|
|
7172
|
-
.run(Number(row["id"]), now.toISOString());
|
|
7173
|
-
this.reconcileIntentsForMode(String(row["repo"]), null, now);
|
|
7174
|
-
});
|
|
7182
|
+
// A page visit only reads (docs/plans/get-side-effects.md): it sees no mode, and the background pass or the
|
|
7183
|
+
// next path that acts closes it.
|
|
7184
|
+
if (!inReadOnlyRequest())
|
|
7185
|
+
this.closeExpiredMode(Number(row["id"]), String(row["repo"]), now);
|
|
7175
7186
|
return null;
|
|
7176
7187
|
}
|
|
7177
7188
|
return {
|
|
@@ -7183,6 +7194,34 @@ export class Store {
|
|
|
7183
7194
|
absoluteExpiry: String(row["absolute_expiry"]),
|
|
7184
7195
|
};
|
|
7185
7196
|
}
|
|
7197
|
+
/** Close every operating mode past its expiry (the background pass). Returns how many were closed. */
|
|
7198
|
+
closeExpiredModes(now) {
|
|
7199
|
+
// The same rows activeMode would close: unrevoked, signed by a standing approver, past their expiry.
|
|
7200
|
+
const rows = this.db.prepare(`SELECT operating_mode.id, operating_mode.repo, operating_mode.absolute_expiry FROM operating_mode
|
|
7201
|
+
JOIN approver ON approver.name = operating_mode.signed_by AND approver.revoked_at IS NULL AND approver.role = 'approver'
|
|
7202
|
+
WHERE operating_mode.revoked_at IS NULL`).all();
|
|
7203
|
+
let closed = 0;
|
|
7204
|
+
for (const row of rows) {
|
|
7205
|
+
if (Date.parse(String(row["absolute_expiry"])) > now.getTime())
|
|
7206
|
+
continue;
|
|
7207
|
+
this.closeExpiredMode(Number(row["id"]), String(row["repo"]), now);
|
|
7208
|
+
closed++;
|
|
7209
|
+
}
|
|
7210
|
+
return closed;
|
|
7211
|
+
}
|
|
7212
|
+
closeExpiredMode(id, repo, now) {
|
|
7213
|
+
this.transact(() => {
|
|
7214
|
+
const closed = this.db
|
|
7215
|
+
.prepare("UPDATE operating_mode SET revoked_at = ?, revoked_by = ?, revoke_reason = 'expired' WHERE id = ? AND revoked_at IS NULL")
|
|
7216
|
+
.run(now.toISOString(), "the-clock", id);
|
|
7217
|
+
if (Number(closed.changes) !== 1)
|
|
7218
|
+
return;
|
|
7219
|
+
this.db
|
|
7220
|
+
.prepare("INSERT INTO operating_mode_event (mode, kind, actor, at) VALUES (?, 'expired-closed', 'the-clock', ?)")
|
|
7221
|
+
.run(id, now.toISOString());
|
|
7222
|
+
this.reconcileIntentsForMode(repo, null, now);
|
|
7223
|
+
});
|
|
7224
|
+
}
|
|
7186
7225
|
/** Sign (or renew) a mode: closes any predecessor with a typed event and
|
|
7187
7226
|
* ATOMICALLY reconciles the repo's nonterminal merge intents to the new
|
|
7188
7227
|
* posture (F2). The caller has already run the password ceremony. */
|
|
@@ -20398,7 +20437,7 @@ export function serializeReleaseTerms(terms) {
|
|
|
20398
20437
|
}
|
|
20399
20438
|
// ---- one approval per release: the authority record (release-once.md, part 1) --------------------------------------
|
|
20400
20439
|
export { LIVE_RELEASE_STATES, belongsToRelease, newestReleaseGeneration, outstandingReleaseOperations, releaseAuthority, releaseAuthorityOfTask, releaseCarries, releaseCarryTo, releaseOperation, releaseOperations, releaseSealProblem, } from "./release-authority.js";
|
|
20401
|
-
import { LIVE_RELEASE_STATES, belongsToRelease, newestReleaseGeneration, outstandingReleaseOperations, releaseAuthority, releaseCarryTo, releaseOperation, releaseSealProblem, } from "./release-authority.js";
|
|
20440
|
+
import { LIVE_RELEASE_STATES, autoEvidenceOf, belongsToRelease, newestReleaseGeneration, outstandingReleaseOperations, releaseAuthority, releaseCarryTo, releaseOperation, releaseSealProblem, } from "./release-authority.js";
|
|
20402
20441
|
/**
|
|
20403
20442
|
* Open a release's authority for a person's seal of its terms (inside the seal's transaction): the next generation,
|
|
20404
20443
|
* superseding the live one. False when it can't (releaseSealProblem), which refuses the seal.
|
package/dist/subagent-work.js
CHANGED
|
@@ -235,6 +235,9 @@ function carryOut(store, flow, definition, stage, card, mate, context, answer, n
|
|
|
235
235
|
}
|
|
236
236
|
if (answer.action === "cant") {
|
|
237
237
|
event("handled", `Couldn't handle “${card.title}”: ${answer.note}`);
|
|
238
|
+
// A zone that answers whoever asked tells them it couldn't, in its own words when it gave some: they always hear back.
|
|
239
|
+
if (stage.reply === true)
|
|
240
|
+
replyToAsker(store, flow, card, mate, answer.note || (answer.reason === "" ? `${context.name} couldn't handle this.` : `${context.name} couldn't handle this: ${answer.reason}`), now);
|
|
238
241
|
if (stage.onFail !== null)
|
|
239
242
|
store.moveFlowCard(card.id, { to: stage.onFail, outcome: "fail", actor, note: answer.note === "" ? null : note(answer.note), expectEntry: card.entry }, now);
|
|
240
243
|
else
|
package/dist/task-base.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { execFileSync } from "node:child_process";
|
|
8
8
|
export const readGit = (cwd, args) => {
|
|
9
9
|
try {
|
|
10
|
-
return execFileSync("git", ["--no-optional-locks", ...args], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5_000, maxBuffer: 65_536 }).trim();
|
|
10
|
+
return execFileSync("git", ["--no-optional-locks", "--no-lazy-fetch", "-c", "core.fsmonitor=false", "-c", "core.hooksPath=/dev/null", ...args], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5_000, maxBuffer: 65_536 }).trim();
|
|
11
11
|
}
|
|
12
12
|
catch {
|
|
13
13
|
return null;
|
package/dist/task-status.d.ts
CHANGED
|
@@ -43,6 +43,8 @@ export type TaskStatus = {
|
|
|
43
43
|
key: NeedKey;
|
|
44
44
|
action: NeedAction;
|
|
45
45
|
} | null;
|
|
46
|
+
/** The approved check as the Checks row shows it (`approvedCheckStateOf`): the road reads this, never the row's words. */
|
|
47
|
+
check?: ApprovedCheckState | null;
|
|
46
48
|
};
|
|
47
49
|
/** `reviewing`: the one automatic review (build-review.ts) is reading a finished build; it reads Building, not a headline of its own. */
|
|
48
50
|
export type TaskStage = "queued" | "planning" | "needs-you" | "waiting" | "building" | "checking" | "reviewing" | "finished" | "complete" | "failed" | "stopped";
|
|
@@ -61,6 +63,8 @@ export type ChecksFact = {
|
|
|
61
63
|
run: number;
|
|
62
64
|
head: string;
|
|
63
65
|
} | null;
|
|
66
|
+
/** Why it can't be read, when that is the project's check command approved again since it ran. */
|
|
67
|
+
cause?: "command-changed" | null;
|
|
64
68
|
};
|
|
65
69
|
/** What checks Off leaves for a release: `pending` until a passing full release check contains the commit, then
|
|
66
70
|
* `covered`. `running`: a check is waiting for its batch or running on this commit. */
|
|
@@ -70,6 +74,25 @@ export declare function releaseStateOf(checks: Pick<ChecksFact, "status" | "leve
|
|
|
70
74
|
/** How the project check stands for requirements that rest on it, read now: `covered` once a check passed on this
|
|
71
75
|
* commit (its own, a follow-up, or a release check containing it), `pending` while checks Off wait for a release. */
|
|
72
76
|
export declare function checkBackingOf(checks: Pick<ChecksFact, "status" | "level" | "release" | "running" | "batch"> | null | undefined): ReleaseState | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Did the approved check pass for this result? The ONE answer the status headline and sentence, the Project checks
|
|
79
|
+
* row, the road's Checks step and the result page all read, so none of them can say "passed" while another says the
|
|
80
|
+
* check couldn't be read. Two records speak to it: the machine verdict sealed when the build finished, and the saved
|
|
81
|
+
* check receipt read now (with any follow-up check on the same commit). Fail closed: the check passed only when the
|
|
82
|
+
* receipt read now says so. A verdict that recorded a pass whose receipt is missing, says nothing ran, or can't be
|
|
83
|
+
* read is unreadable; a verdict that recorded a failure stays failed unless a later check on the same commit passed.
|
|
84
|
+
* `checks` undefined or null: nothing was read. Returns the check as every surface shows it (null: nothing to show).
|
|
85
|
+
*/
|
|
86
|
+
export declare function approvedCheckOf(verdict: string | null | undefined, reasons: readonly string[], checks: ChecksFact | null | undefined): ChecksFact | null;
|
|
87
|
+
/** How the approved check reads, in one word every surface shares (from `approvedCheckOf`'s answer): `running` while a
|
|
88
|
+
* check is underway on the commit, else what it recorded. `unreadable`: it couldn't be read or the records disagree. */
|
|
89
|
+
export type ApprovedCheckState = "passed" | "failed" | "running" | "not-run" | "off" | "unreadable";
|
|
90
|
+
export declare function approvedCheckStateOf(checks: Pick<ChecksFact, "status" | "level" | "release" | "running" | "batch"> | null | undefined): ApprovedCheckState | null;
|
|
91
|
+
/** A saved check that can't be read, in the words every surface uses. */
|
|
92
|
+
export declare const CHECK_UNREADABLE = "The check result couldn't be read.";
|
|
93
|
+
export declare const CHECK_UNREADABLE_ASK = "The check result couldn't be read. Run checks on it, or open the result to see why.";
|
|
94
|
+
/** A saved check whose files are intact but whose command was approved again since it ran. */
|
|
95
|
+
export declare const CHECK_COMMAND_CHANGED = "The check command changed since this check; run checks again.";
|
|
73
96
|
/** What stands between a finished result and Complete (`completionBlockersOf`): each with the exact next step. */
|
|
74
97
|
export type CompletionBlocker = {
|
|
75
98
|
key: "check-failed" | "check-missing" | "criteria" | "high";
|
|
@@ -224,7 +247,7 @@ export declare function requirementsOf(matrix: readonly RequirementRow[] | null
|
|
|
224
247
|
* the rest); an unresolved HIGH review finding. A release check that covers the commit counts as passed. */
|
|
225
248
|
export declare function completionBlockersOf(f: {
|
|
226
249
|
report: boolean;
|
|
227
|
-
checks: Pick<ChecksFact, "status" | "exitCode" | "level" | "release" | "running" | "batch"> | null;
|
|
250
|
+
checks: Pick<ChecksFact, "status" | "exitCode" | "level" | "release" | "running" | "batch" | "cause"> | null;
|
|
228
251
|
checkRequired: boolean;
|
|
229
252
|
matrix: readonly RequirementRow[] | null | undefined;
|
|
230
253
|
verdict: string | null | undefined;
|
|
@@ -235,6 +258,10 @@ export declare function completionBlockersOf(f: {
|
|
|
235
258
|
export declare function unverifiedWhenRefuted(requirements: NonNullable<TaskStatusFacts["requirements"]> | null, verdict: string | null | undefined): NonNullable<TaskStatusFacts["requirements"]> | null;
|
|
236
259
|
/** Saved evidence health from the receipt's artifact list and its caveats. */
|
|
237
260
|
export declare function evidenceOf(receipt: AssignmentSnapshot["receipt"], problems?: readonly string[]): NonNullable<TaskStatusFacts["evidence"]> | null;
|
|
261
|
+
/** A saved check reading as the shared fact (head, level, a follow-up running, a batch, release coverage). */
|
|
262
|
+
export declare function checksFactOf(checks: NonNullable<AssignmentSnapshot["receipt"]>["checks"], head: string | null): ChecksFact;
|
|
263
|
+
/** A receipt's approved check, as every surface reads it (`approvedCheckOf` over its verdict and its saved check). */
|
|
264
|
+
export declare function receiptChecksOf(receipt: NonNullable<AssignmentSnapshot["receipt"]>): ChecksFact | null;
|
|
238
265
|
/** The status facts of one assignment, as every surface reads them. */
|
|
239
266
|
export declare function assignmentStatusFacts(assignment: AssignmentSnapshot, options?: {
|
|
240
267
|
work?: {
|
package/dist/task-status.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* technical reason stays one tap away in `why`. */
|
|
13
13
|
import { DEFAULT_LEAD_NAME } from "./lead-identity.js";
|
|
14
14
|
import { html } from "./html.js";
|
|
15
|
+
import { FAILED_CHECK } from "./result-acts.js";
|
|
15
16
|
import { CODE_NEED, NEEDS, needSentence, waitSentence } from "./needs-you.js";
|
|
16
17
|
/** A cancelled task with a successor reads "Replaced by <id>" everywhere, never "Cancelled". */
|
|
17
18
|
export const replacedWords = (successor) => `Replaced by ${successor}`;
|
|
@@ -38,6 +39,67 @@ export function releaseStateOf(checks) {
|
|
|
38
39
|
export function checkBackingOf(checks) {
|
|
39
40
|
return checks?.status === "passed" ? "covered" : checksUnderway(checks) ? "running" : releaseStateOf(checks);
|
|
40
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Did the approved check pass for this result? The ONE answer the status headline and sentence, the Project checks
|
|
44
|
+
* row, the road's Checks step and the result page all read, so none of them can say "passed" while another says the
|
|
45
|
+
* check couldn't be read. Two records speak to it: the machine verdict sealed when the build finished, and the saved
|
|
46
|
+
* check receipt read now (with any follow-up check on the same commit). Fail closed: the check passed only when the
|
|
47
|
+
* receipt read now says so. A verdict that recorded a pass whose receipt is missing, says nothing ran, or can't be
|
|
48
|
+
* read is unreadable; a verdict that recorded a failure stays failed unless a later check on the same commit passed.
|
|
49
|
+
* `checks` undefined or null: nothing was read. Returns the check as every surface shows it (null: nothing to show).
|
|
50
|
+
*/
|
|
51
|
+
export function approvedCheckOf(verdict, reasons, checks) {
|
|
52
|
+
let failedExit = null;
|
|
53
|
+
for (const reason of reasons) {
|
|
54
|
+
const found = FAILED_CHECK.exec(reason);
|
|
55
|
+
if (found !== null) {
|
|
56
|
+
failedExit = Number(found[1]);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const recordedFail = verdict === "refuted" && failedExit !== null;
|
|
61
|
+
const recordedPass = verdict === "verified";
|
|
62
|
+
const read = checks ?? null;
|
|
63
|
+
if (read === null) {
|
|
64
|
+
if (recordedFail)
|
|
65
|
+
return { status: "failed", exitCode: failedExit, head: null };
|
|
66
|
+
return recordedPass ? { status: "unavailable", exitCode: null, head: null } : null;
|
|
67
|
+
}
|
|
68
|
+
if (read.status === "passed" || read.status === "failed" || read.status === "running")
|
|
69
|
+
return read;
|
|
70
|
+
// Checks Off covered by a passing release check read as passed (the row says on which commit); nothing to correct.
|
|
71
|
+
if (read.level === "off" && read.release != null && !recordedFail)
|
|
72
|
+
return read;
|
|
73
|
+
if (recordedFail)
|
|
74
|
+
return { ...read, status: "failed", exitCode: failedExit };
|
|
75
|
+
// The verdict says the check passed, the receipt read now can't show it: the two disagree, so it doesn't count.
|
|
76
|
+
if (recordedPass)
|
|
77
|
+
return { ...read, status: "unavailable", exitCode: null };
|
|
78
|
+
return read;
|
|
79
|
+
}
|
|
80
|
+
export function approvedCheckStateOf(checks) {
|
|
81
|
+
if (checks == null)
|
|
82
|
+
return null;
|
|
83
|
+
// A failure stands while a new check runs: only its result can change the answer. A check that couldn't be read (a
|
|
84
|
+
// result waiting for its batch check has none yet) reads running while one is underway, and never passed.
|
|
85
|
+
if (checks.status === "failed")
|
|
86
|
+
return "failed";
|
|
87
|
+
if (checksUnderway(checks) || checks.status === "running")
|
|
88
|
+
return "running";
|
|
89
|
+
if (checks.status === "unavailable")
|
|
90
|
+
return "unreadable";
|
|
91
|
+
if (checks.status === "passed")
|
|
92
|
+
return "passed";
|
|
93
|
+
if (checks.level === "off")
|
|
94
|
+
return checks.release != null ? "passed" : "off";
|
|
95
|
+
return "not-run";
|
|
96
|
+
}
|
|
97
|
+
/** A saved check that can't be read, in the words every surface uses. */
|
|
98
|
+
export const CHECK_UNREADABLE = "The check result couldn't be read.";
|
|
99
|
+
export const CHECK_UNREADABLE_ASK = `${CHECK_UNREADABLE} Run checks on it, or open the result to see why.`;
|
|
100
|
+
/** A saved check whose files are intact but whose command was approved again since it ran. */
|
|
101
|
+
export const CHECK_COMMAND_CHANGED = "The check command changed since this check; run checks again.";
|
|
102
|
+
const unreadableAsk = (checks) => checks?.cause === "command-changed" ? CHECK_COMMAND_CHANGED : CHECK_UNREADABLE_ASK;
|
|
41
103
|
/** "T2", "T2 and T3", "T2, T3 and 2 more": short enough for one line. The full list is on the result. */
|
|
42
104
|
export function peerWords(peers, shown = 2) {
|
|
43
105
|
if (peers.length <= shown)
|
|
@@ -65,10 +127,11 @@ function resultAsk(facts) {
|
|
|
65
127
|
if (blocker !== undefined)
|
|
66
128
|
return blocker.message;
|
|
67
129
|
const req = facts.requirements;
|
|
68
|
-
if (req
|
|
69
|
-
return null;
|
|
70
|
-
if ((req.yours > 0 || (req.unshown ?? 0) > 0) && req.ask != null)
|
|
130
|
+
if (req != null && facts.accepted !== true && req.unverified !== true && (req.yours > 0 || (req.unshown ?? 0) > 0) && req.ask != null)
|
|
71
131
|
return req.ask;
|
|
132
|
+
// A check that can't be read is never a pass: a person runs it again or looks at why, unless they already accepted.
|
|
133
|
+
if (facts.accepted !== true && approvedCheckStateOf(facts.checks) === "unreadable")
|
|
134
|
+
return unreadableAsk(facts.checks);
|
|
72
135
|
return null;
|
|
73
136
|
}
|
|
74
137
|
export function headlineOf(facts) {
|
|
@@ -122,6 +185,8 @@ function sentenceOf(headline, facts) {
|
|
|
122
185
|
}
|
|
123
186
|
if (facts.checks?.batch?.state === "waiting")
|
|
124
187
|
return "Its full check runs with other results within 10 minutes. Review the change, then mark it complete.";
|
|
188
|
+
if (facts.checks?.status === "unavailable")
|
|
189
|
+
return `${facts.checks.cause === "command-changed" ? CHECK_COMMAND_CHANGED : CHECK_UNREADABLE} Review the change, then mark it complete.`;
|
|
125
190
|
// Never claim a check that isn't known to have passed.
|
|
126
191
|
if (facts.checks == null)
|
|
127
192
|
return "Review the change, then mark it complete.";
|
|
@@ -155,7 +220,11 @@ function detailsOf(headline, facts) {
|
|
|
155
220
|
const batch = checks.batch ?? null;
|
|
156
221
|
const together = batch === null ? null : checkedTogetherWords(batch);
|
|
157
222
|
const batchWhy = batch === null ? null : BATCH_WHY[batch.state] ?? null;
|
|
158
|
-
|
|
223
|
+
// A failure or an unreadable check keeps its words while a new check runs, so the row never reads better than the answer.
|
|
224
|
+
const again = checksUnderway(checks) && checks.status === "failed" ? " · running again" : "";
|
|
225
|
+
if (again !== "")
|
|
226
|
+
row("checks", CHECKS_LABEL, `${quick ? "Quick checks failed" : "Failed"}${checks.exitCode === null ? "" : ` (exit ${checks.exitCode})`}${again}`, problem(headline), headline === "Failed" ? { href: checksHref } : { action: { label: "See what failed", href: checksHref } });
|
|
227
|
+
else if (batch?.state === "waiting")
|
|
159
228
|
row("checks", CHECKS_LABEL, "Waiting to check with other results", "running", { href: checksHref });
|
|
160
229
|
else if (checks.running != null)
|
|
161
230
|
row("checks", CHECKS_LABEL, `${checks.running === "quick" ? "Quick" : "Full"} checks running`, "running", { href: checksHref });
|
|
@@ -178,6 +247,8 @@ function detailsOf(headline, facts) {
|
|
|
178
247
|
else if (checks.status === "not-run")
|
|
179
248
|
row("checks", CHECKS_LABEL, "Didn't run", "none", runChecks === null ? {} : { action: runChecks });
|
|
180
249
|
// A saved check that can't be read is run again where that is possible.
|
|
250
|
+
else if (checks.cause === "command-changed")
|
|
251
|
+
row("checks", CHECKS_LABEL, "Check command changed since", "note", { action: runChecks ?? { label: OPEN_RESULT, href: facts.links?.result ?? null }, why: CHECK_COMMAND_CHANGED });
|
|
181
252
|
else
|
|
182
253
|
row("checks", CHECKS_LABEL, "Couldn't be read", "note", { action: runChecks ?? { label: OPEN_RESULT, href: facts.links?.result ?? null } });
|
|
183
254
|
}
|
|
@@ -255,7 +326,7 @@ export function statusOf(facts) {
|
|
|
255
326
|
const primaryAction = need === null || ownWords ? facts.action ?? null : { label: need.action.label, href: facts.action?.href ?? null };
|
|
256
327
|
const sentence = lead === "on-it" ? leadSentence(headline, facts) : lead === "lapsed" ? `${leadLapsed(facts.leadName)} ${sentenceOf(headline, facts)}` : sentenceOf(headline, facts);
|
|
257
328
|
return { headline, tone: HEADLINE_TONE[headline], sentence, details: detailsOf(headline, facts),
|
|
258
|
-
primaryAction, why: [...new Set(facts.why ?? [])].filter(one => one.trim() !== ""), need };
|
|
329
|
+
primaryAction, why: [...new Set(facts.why ?? [])].filter(one => one.trim() !== ""), need, check: facts.report ? null : approvedCheckStateOf(facts.checks) };
|
|
259
330
|
}
|
|
260
331
|
/** The name most surfaces already import. */
|
|
261
332
|
export const taskStatusOf = statusOf;
|
|
@@ -267,7 +338,7 @@ const CODE_STAGE = {
|
|
|
267
338
|
"waiting-dependency": ["queued"], "scouting-ready": ["queued"], "planning-ready": ["queued"], "provider-quota": ["queued"],
|
|
268
339
|
// A request the planner cannot be given (too long to hand back, say) waits on a person to shorten it.
|
|
269
340
|
"planner-source": ["needs-you", "fix-request"],
|
|
270
|
-
running: ["building"], reviewing: ["reviewing"], "review-pending": ["building"],
|
|
341
|
+
running: ["building"], reviewing: ["reviewing"], "review-pending": ["building"], "checks-running": ["checking"],
|
|
271
342
|
"signed-out": ["needs-you", "sign-in"],
|
|
272
343
|
"needs-approval": ["needs-you", "approval"],
|
|
273
344
|
"waiting-decision": ["needs-you", "answer"], "decision-queue": ["needs-you", "answer"],
|
|
@@ -432,7 +503,8 @@ export function completionBlockersOf(f) {
|
|
|
432
503
|
}
|
|
433
504
|
else if (!f.report && f.checkRequired && f.checks?.status !== "passed" && release === undefined) {
|
|
434
505
|
blockers.push({ key: "check-missing", message: checksUnderway(f.checks) ? "Its project check hasn't finished yet. Mark it complete once it passes."
|
|
435
|
-
: "
|
|
506
|
+
: f.checks?.status === "unavailable" ? f.checks.cause === "command-changed" ? `${CHECK_COMMAND_CHANGED.replace(/; run checks again\.$/, ".")} Run checks again, then mark it complete.` : `${CHECK_UNREADABLE} Run checks on it, then mark it complete.`
|
|
507
|
+
: "No passing project check is recorded for this result. Run checks on it, then mark it complete." });
|
|
436
508
|
}
|
|
437
509
|
if (!f.accepted && f.checks?.status !== "failed") {
|
|
438
510
|
const refuted = f.verdict === "refuted";
|
|
@@ -457,11 +529,24 @@ export function unverifiedWhenRefuted(requirements, verdict) {
|
|
|
457
529
|
export function evidenceOf(receipt, problems = receipt?.caveats ?? []) {
|
|
458
530
|
if (receipt === null)
|
|
459
531
|
return null;
|
|
532
|
+
// The check's own reading is the Project checks row's to say (a changed command is not a missing file).
|
|
533
|
+
problems = problems.filter(one => one !== receipt.checks.detail);
|
|
460
534
|
const shortened = receipt.artifacts.filter(one => !one.complete).length + problems.filter(one => /shortened|cut short/.test(one)).length;
|
|
461
535
|
const damaged = problems.filter(one => /unavailable or changed|no longer verif|cannot be read|could not be read|failed capture|altered|damaged/i.test(one)).length;
|
|
462
536
|
const missing = problems.filter(one => /missing|ambiguous|no longer exists/i.test(one) && !/unavailable or changed/.test(one)).length;
|
|
463
537
|
return { shortened, missing, damaged };
|
|
464
538
|
}
|
|
539
|
+
/** A saved check reading as the shared fact (head, level, a follow-up running, a batch, release coverage). */
|
|
540
|
+
export function checksFactOf(checks, head) {
|
|
541
|
+
return { status: checks.status, exitCode: checks.exitCode, head,
|
|
542
|
+
...(checks.level == null ? {} : { level: checks.level }), ...(checks.running == null ? {} : { running: checks.running }),
|
|
543
|
+
...(checks.batch == null ? {} : { batch: checks.batch }), ...(checks.release == null ? {} : { release: checks.release }),
|
|
544
|
+
...(checks.cause == null ? {} : { cause: checks.cause }) };
|
|
545
|
+
}
|
|
546
|
+
/** A receipt's approved check, as every surface reads it (`approvedCheckOf` over its verdict and its saved check). */
|
|
547
|
+
export function receiptChecksOf(receipt) {
|
|
548
|
+
return approvedCheckOf(receipt.proof?.verdict ?? null, receipt.proof?.reasons ?? [], checksFactOf(receipt.checks, receipt.head));
|
|
549
|
+
}
|
|
465
550
|
/** The status facts of one assignment, as every surface reads them. */
|
|
466
551
|
export function assignmentStatusFacts(assignment, options = {}) {
|
|
467
552
|
const { stage, need, wait } = assignmentStageOf(assignment, options.work ?? null, options.planning ?? false);
|
|
@@ -471,10 +556,7 @@ export function assignmentStatusFacts(assignment, options = {}) {
|
|
|
471
556
|
// A saved result's rows show whenever it is the reason a person is needed, too.
|
|
472
557
|
const withResult = finished || stage === "failed" || ((stage === "needs-you" || stage === "waiting") && receipt !== null);
|
|
473
558
|
const report = receipt?.completionKind === "research-report";
|
|
474
|
-
const checks = receipt === null || !withResult ? null
|
|
475
|
-
: { status: receipt.checks.status, exitCode: receipt.checks.exitCode, head: receipt.head,
|
|
476
|
-
...(receipt.checks.level == null ? {} : { level: receipt.checks.level }), ...(receipt.checks.running == null ? {} : { running: receipt.checks.running }),
|
|
477
|
-
...(receipt.checks.batch == null ? {} : { batch: receipt.checks.batch }), ...(receipt.checks.release == null ? {} : { release: receipt.checks.release }) };
|
|
559
|
+
const checks = receipt === null || !withResult ? null : receiptChecksOf(receipt);
|
|
478
560
|
const publication = assignment.publication;
|
|
479
561
|
const pullRequest = options.pullRequest !== undefined ? options.pullRequest
|
|
480
562
|
: publication === null ? (withResult && !report ? { state: "none", number: null, url: null, ci: null, error: null } : null)
|
package/dist/teardown-git.d.ts
CHANGED
|
@@ -6,14 +6,24 @@ export declare const TEARDOWN_FLAGS: readonly string[];
|
|
|
6
6
|
/** Exit code of a refused command (git's own fatal code, so no caller reads it as "not found" or "no"). */
|
|
7
7
|
export declare const REFUSED_CODE = 128;
|
|
8
8
|
/** The command-running setting a `git config --list --show-scope -z` listing sets, or null. Only local and worktree
|
|
9
|
-
* scope count: teardown commands never read system or global config.
|
|
10
|
-
|
|
9
|
+
* scope count: teardown commands never read system or global config. `lfsChecked`: the caller refuses a checkout whose
|
|
10
|
+
* `.lfsconfig` names a git-lfs extension (lfsExtensionIn), so git-lfs's own exact filters may stand (runsCommand).
|
|
11
|
+
* Fails closed: a listing that can't be classified names a setting. */
|
|
12
|
+
export declare function commandSetting(listing: string, options?: {
|
|
13
|
+
lfsChecked?: boolean;
|
|
14
|
+
}): string | null;
|
|
11
15
|
/** The `-c` settings that carry the operator's own values (CARRIED_VALUES, LFS_FILTER) from a global/system listing. */
|
|
12
16
|
export declare function carriedSettings(listing: string): string[];
|
|
13
17
|
/** Why a teardown command was refused, or null when `result` isn't a refusal. */
|
|
14
18
|
export declare function refusal(result: ExecResult): string | null;
|
|
15
|
-
/** The one way teardown runs git, including the target checkout immediately before removal.
|
|
16
|
-
|
|
19
|
+
/** The one way teardown runs git, including the target checkout immediately before removal. Every command runs
|
|
20
|
+
* against a write view of its repository (gitView), whose config is the one inspected and whose info is a copy, so a
|
|
21
|
+
* setting or attributes file an agent adds after the inspection is never read (a build's process can outlive its turn:
|
|
22
|
+
* on macOS containment is observational, src/containment.ts, and a sibling build shares the repository). A removal's
|
|
23
|
+
* target is bound to the same repository as the folder it is removed from. A removal without --force runs under the
|
|
24
|
+
* target's own view (its own config.worktree folded in), so git's last check of whether it is clean reads it as the
|
|
25
|
+
* target itself would. `binding.repo`: the project's main checkout, from the plane's records, when the caller has it. */
|
|
26
|
+
export declare function teardownGit(runner?: Runner, binding?: GitBinding): Git;
|
|
17
27
|
/**
|
|
18
28
|
* Git on a build's checkout, as the plane runs it outside every fence (staging, committing and inspecting the build's
|
|
19
29
|
* work): the same fixed hardening as teardown (no hooks, fsmonitor, ext transport, credential helpers or automatic
|
|
@@ -29,8 +39,52 @@ export declare function teardownGit(runner?: Runner): Git;
|
|
|
29
39
|
* that can't be read so counts as including.
|
|
30
40
|
*/
|
|
31
41
|
export declare function checkoutIncludes(checkout: string): boolean;
|
|
42
|
+
export type GitBinding = {
|
|
43
|
+
repo?: string | null;
|
|
44
|
+
};
|
|
45
|
+
/** Settings fixed on every write through a view: `worktree add` records absolute paths (relative ones would need a
|
|
46
|
+
* config write, which a view keeps); no transport at all (a caller that fetches allows its one protocol by name, as the
|
|
47
|
+
* nightly check's local fetch does with protocol.file.allow); and no split index (its shared index files would be
|
|
48
|
+
* written in the plane's GIT_DIR). */
|
|
49
|
+
export declare const WRITE_VIEW_FLAGS: readonly string[];
|
|
32
50
|
/** Whether the checkout's .lfsconfig (read as git parses it, that file only) sets any git-lfs key that could name a
|
|
33
51
|
* program (anything but INERT_LFS). A file that
|
|
34
52
|
* isn't a plain one, or doesn't parse, counts as naming one. */
|
|
35
53
|
export declare function lfsExtensionIn(checkout: string): boolean;
|
|
36
|
-
|
|
54
|
+
/** Every command runs against a view of the checkout's repository (gitView) whose config and info are copies the
|
|
55
|
+
* plane made, inspected there, so neither can change between the inspection and the run. `mode` "read" is for reading
|
|
56
|
+
* a checkout an agent may still be writing (the code page): every command also takes readFlags(), and the view holds
|
|
57
|
+
* no info/attributes. "build" is the plane's staging and committing of a build's work, which writes the repository
|
|
58
|
+
* through the view (gitView). */
|
|
59
|
+
export declare function checkoutGit(inner?: Runner, mode?: "build" | "read", binding?: GitBinding): Runner;
|
|
60
|
+
/**
|
|
61
|
+
* Flags for reading a checkout an agent writes while a page waits (docs/plans/get-side-effects.md). Command-line
|
|
62
|
+
* settings win over anything the checkout's config says, even one changed after it was inspected: no fsmonitor, hooks,
|
|
63
|
+
* pager or credential helper; no network (lazy fetch off, every protocol refused); and attributes read from the empty
|
|
64
|
+
* tree, so no filter or textconv a .gitattributes names can run. A diff also takes --no-ext-diff --no-textconv.
|
|
65
|
+
*/
|
|
66
|
+
export declare const PAGE_READ_FLAGS: readonly string[];
|
|
67
|
+
/** The read flags, for every read of an agent-written checkout, whatever the HTTP method (security review of
|
|
68
|
+
* 01bf3114): the worktree's attributes come from the empty tree. Git still reads the repository's info/attributes and
|
|
69
|
+
* its config as it starts, so the flags alone don't stop a filter planted in both after the inspection: every read
|
|
70
|
+
* also runs against a view of the checkout's repository (gitView; checkoutGitSync, checkoutGit "read"), whose config and info are the plane's. The cost is cosmetic: a checkout's line-ending or encoding rules are not applied, so
|
|
71
|
+
* a file they would normalize can read as changed (fail closed: shipping then asks for a clean commit). */
|
|
72
|
+
export declare function readFlags(): readonly string[];
|
|
73
|
+
/**
|
|
74
|
+
* checkoutGit, synchronously, for reads a page shows from a checkout an agent writes (docs/plans/get-side-effects.md):
|
|
75
|
+
* the same refusals (an include, an .lfsconfig extension, a config key that can run a command), no system or global
|
|
76
|
+
* config, the operator's own values carried, PAGE_READ_FLAGS and a view of the checkout's repository (gitView: they hold even if its config
|
|
77
|
+
* or info/attributes change after they were read), submodules skipped, bounded. A refusal throws.
|
|
78
|
+
*/
|
|
79
|
+
export declare function checkoutGitSync(cwd: string, args: readonly string[], options?: {
|
|
80
|
+
timeoutMs?: number;
|
|
81
|
+
maxBuffer?: number;
|
|
82
|
+
binding?: GitBinding;
|
|
83
|
+
}): string;
|
|
84
|
+
/**
|
|
85
|
+
* checkoutGit ("build": the repository written through its view) for callers that name the repository with a leading
|
|
86
|
+
* `git -C <dir>` instead of a cwd: worktree add and remove in a project's repository, a merge in a checkout of it.
|
|
87
|
+
* The project's repository is shared with every build, which can write its config, info and hooks; nothing here runs
|
|
88
|
+
* what they name. Anything but git goes to `inner` as given.
|
|
89
|
+
*/
|
|
90
|
+
export declare function hardenedGit(inner?: Runner, mode?: "build" | "read", binding?: GitBinding): Runner;
|