toolroll 0.9.65 → 0.9.68
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 +93 -0
- package/README.md +20 -2
- package/dist/acp-client.js +1 -3
- package/dist/agent-board.d.ts +0 -1
- package/dist/agent-board.js +0 -1
- package/dist/agent-fence.d.ts +69 -2
- package/dist/agent-fence.js +294 -11
- package/dist/agentconfig.js +4 -1
- package/dist/batch-checks.js +3 -2
- package/dist/browser/THIRD_PARTY_NOTICES.txt +46 -0
- package/dist/browser/terminal.js +134 -23
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +31 -29
- package/dist/builder.d.ts +4 -0
- package/dist/builder.js +56 -37
- package/dist/canonical-json.d.ts +14 -0
- package/dist/canonical-json.js +36 -0
- package/dist/chat-actions.js +1 -2
- package/dist/chat-admission.d.ts +19 -0
- package/dist/chat-admission.js +22 -0
- package/dist/chat-channel.js +1 -1
- package/dist/chat-delivery.js +0 -1
- package/dist/chat-follow.d.ts +17 -0
- package/dist/chat-follow.js +32 -0
- package/dist/chat-messages.js +0 -1
- package/dist/chat-settings-page.d.ts +23 -0
- package/dist/chat-settings-page.js +41 -0
- package/dist/claim.js +3 -6
- package/dist/claude-print.d.ts +12 -0
- package/dist/claude-print.js +34 -0
- package/dist/cli-http.d.ts +0 -7
- package/dist/cli-http.js +0 -21
- package/dist/cli.d.ts +3 -10
- package/dist/cli.js +15 -24
- package/dist/coding-provider.js +2 -1
- package/dist/coding-workspace.js +1 -1
- package/dist/{team-cli.d.ts → connect-cli.d.ts} +5 -9
- package/dist/connect-cli.js +331 -0
- package/dist/containment.d.ts +0 -2
- package/dist/containment.js +2 -6
- package/dist/contracts/chat-actions.d.ts +0 -1
- package/dist/contracts/chat-content.d.ts +0 -4
- package/dist/contracts/chat-content.js +0 -2
- package/dist/contracts/chat-tables.d.ts +0 -1
- package/dist/contracts/cli.d.ts +0 -62
- package/dist/contracts/cli.js +0 -4
- package/dist/contracts/coding-workspace.js +1 -1
- package/dist/contracts/console-api.d.ts +1 -0
- package/dist/contracts/console-api.js +1 -0
- package/dist/contracts/contract.js +1 -1
- package/dist/contracts/flow-card.d.ts +0 -1
- package/dist/contracts/flow-propose.d.ts +0 -1
- package/dist/contracts/flow-send.js +1 -1
- package/dist/contracts/gateway-tools.d.ts +0 -1
- package/dist/contracts/integration-metadata.js +1 -1
- package/dist/contracts/lead-tools.d.ts +0 -1
- package/dist/contracts/memory-pass.d.ts +0 -1
- package/dist/contracts/memory-pass.js +1 -1
- package/dist/contracts/plan.js +1 -1
- package/dist/contracts/project-knowledge.js +1 -1
- package/dist/contracts/project-skills.d.ts +0 -1
- package/dist/contracts/proof.js +1 -1
- package/dist/contracts/recipes.js +1 -1
- package/dist/contracts/scope.js +1 -1
- package/dist/contracts/scout-report.js +1 -1
- package/dist/contracts/teams-callback.d.ts +0 -1
- package/dist/converse.d.ts +0 -6
- package/dist/converse.js +0 -19
- package/dist/daemon.js +3 -2
- package/dist/decision.d.ts +1 -3
- package/dist/deprecations.d.ts +12 -4
- package/dist/deprecations.js +27 -4
- package/dist/desktop-update-recovery.js +1 -1
- package/dist/desktop-update.js +3 -2
- package/dist/discord-chat.js +4 -15
- package/dist/discord-settings.d.ts +2 -6
- package/dist/discord-settings.js +10 -38
- package/dist/discord.js +12 -23
- package/dist/evidence.d.ts +0 -1
- package/dist/evidence.js +0 -3
- package/dist/exec.d.ts +0 -9
- package/dist/exec.js +3 -81
- package/dist/flow-actions.js +0 -3
- package/dist/flow-draft.d.ts +1 -3
- package/dist/flow-draft.js +15 -28
- package/dist/flow-scripts.d.ts +1 -8
- package/dist/flow-scripts.js +0 -13
- package/dist/flow-send.js +1 -1
- package/dist/flow-share.d.ts +0 -2
- package/dist/flow-share.js +1 -6
- package/dist/flow-steps.js +1 -1
- package/dist/flow-triggers.d.ts +0 -1
- package/dist/flow-triggers.js +1 -2
- package/dist/flow-unattended.js +1 -12
- package/dist/flows.js +1 -1
- package/dist/health-cli.d.ts +2 -2
- package/dist/health-cli.js +1 -1
- package/dist/invoke.d.ts +18 -3
- package/dist/invoke.js +142 -17
- package/dist/is-record.d.ts +2 -0
- package/dist/is-record.js +2 -0
- package/dist/lead-doors.d.ts +1 -5
- package/dist/lead-doors.js +0 -4
- package/dist/lead-tools.js +3 -6
- package/dist/live-detail.d.ts +0 -1
- package/dist/live-detail.js +0 -6
- package/dist/live-terminal.d.ts +24 -13
- package/dist/live-terminal.js +81 -43
- package/dist/live.d.ts +0 -1
- package/dist/live.js +1 -2
- package/dist/mcp-core.js +0 -2
- package/dist/memory-pass.js +1 -1
- package/dist/model-catalog.js +1 -1
- package/dist/modes.js +1 -12
- package/dist/operate.d.ts +2 -0
- package/dist/operate.js +436 -745
- package/dist/password-bearer.d.ts +0 -2
- package/dist/password-bearer.js +0 -4
- package/dist/peek-cli.js +2 -1
- package/dist/peek.d.ts +2 -26
- package/dist/peek.js +2 -168
- package/dist/phase-routing.d.ts +0 -4
- package/dist/phase-routing.js +2 -21
- package/dist/planner-source.d.ts +2 -1
- package/dist/planner-source.js +2 -12
- package/dist/planner.js +2 -14
- package/dist/process-tree.d.ts +0 -2
- package/dist/process-tree.js +0 -10
- package/dist/project-delete-ui.d.ts +6 -0
- package/dist/project-delete-ui.js +20 -1
- package/dist/project-delete.d.ts +0 -2
- package/dist/project-delete.js +1 -3
- package/dist/project-learning.d.ts +0 -14
- package/dist/project-skills.d.ts +0 -1
- package/dist/project-skills.js +0 -14
- package/dist/project-tools.js +2 -10
- package/dist/provider.d.ts +0 -17
- package/dist/provider.js +0 -26
- package/dist/pulls.d.ts +0 -2
- package/dist/pulls.js +0 -9
- package/dist/release-adapters.d.ts +9 -0
- package/dist/release-adapters.js +81 -4
- package/dist/release.d.ts +7 -1
- package/dist/release.js +20 -7
- package/dist/remote-exec.d.ts +3 -2
- package/dist/remote-exec.js +3 -2
- package/dist/repos.d.ts +0 -5
- package/dist/repos.js +2 -10
- package/dist/repository-context.d.ts +0 -2
- package/dist/repository-context.js +0 -4
- package/dist/restart-certification.d.ts +0 -2
- package/dist/restart-certification.js +1 -5
- package/dist/result-follow-ups.d.ts +1 -4
- package/dist/result-follow-ups.js +4 -8
- package/dist/result-review.d.ts +0 -2
- package/dist/result-review.js +0 -2
- package/dist/retention.d.ts +0 -1
- package/dist/retention.js +0 -1
- package/dist/review-context.d.ts +0 -1
- package/dist/review-context.js +1 -12
- package/dist/reviewer.d.ts +0 -16
- package/dist/reviewer.js +0 -18
- package/dist/run-closeout.js +2 -1
- package/dist/scope.d.ts +13 -0
- package/dist/scope.js +11 -18
- package/dist/scout-browser.js +2 -1
- package/dist/serve.js +3 -2
- package/dist/server/chrome.d.ts +10 -1
- package/dist/server/chrome.js +45 -4
- package/dist/server/console.css +60 -137
- package/dist/server/flows.js +2 -2
- package/dist/server/http.d.ts +2 -0
- package/dist/server/http.js +5 -0
- package/dist/server/pages.js +2 -2
- package/dist/server/people-tokens.js +3 -4
- package/dist/server/remote.js +1 -6
- package/dist/server/render-chat.d.ts +9 -2
- package/dist/server/render-chat.js +53 -7
- package/dist/server/render-pages.d.ts +28 -3
- package/dist/server/render-pages.js +78 -9
- package/dist/server/render-results.d.ts +0 -6
- package/dist/server/render-results.js +3 -9
- package/dist/server/render-tasks.js +6 -6
- package/dist/server/route-table.d.ts +0 -4
- package/dist/server/route-table.js +1 -6
- package/dist/server/settings.js +43 -22
- package/dist/server/task-views.js +1 -1
- package/dist/server/tasks.js +10 -7
- package/dist/setup-guide.d.ts +2 -2
- package/dist/skills.d.ts +0 -6
- package/dist/skills.js +0 -8
- package/dist/slack-chat.d.ts +1 -7
- package/dist/slack-chat.js +5 -18
- package/dist/slack-settings.d.ts +2 -6
- package/dist/slack-settings.js +10 -38
- package/dist/slack.js +12 -23
- package/dist/sleep.d.ts +2 -0
- package/dist/sleep.js +2 -0
- package/dist/store.d.ts +11 -74
- package/dist/store.js +36 -179
- package/dist/structured-output.d.ts +0 -1
- package/dist/structured-output.js +0 -1
- package/dist/subagent-work.d.ts +0 -2
- package/dist/subagent-work.js +0 -2
- package/dist/subagents.d.ts +1 -3
- package/dist/subagents.js +20 -33
- package/dist/subscription-chat.d.ts +1 -3
- package/dist/subscription-chat.js +2 -3
- package/dist/task-sizing.d.ts +1 -4
- package/dist/task-sizing.js +3 -10
- package/dist/teams-chat.js +4 -11
- package/dist/teams.js +12 -19
- package/dist/teardown-git.d.ts +22 -0
- package/dist/teardown-git.js +136 -2
- package/dist/telegram-lead.js +2 -2
- package/dist/telegram.js +1 -1
- package/dist/token-notices.d.ts +0 -11
- package/dist/token-notices.js +1 -5
- package/dist/tool-broker.d.ts +12 -0
- package/dist/tool-broker.js +66 -2
- package/dist/tool-secret-store.d.ts +0 -3
- package/dist/tool-secret-store.js +2 -11
- package/dist/toolroll-update.d.ts +0 -2
- package/dist/toolroll-update.js +2 -2
- package/dist/unfenced-test-support.d.ts +2 -0
- package/dist/unfenced-test-support.js +11 -0
- package/dist/verification-evidence.d.ts +0 -1
- package/dist/verification-evidence.js +0 -1
- package/dist/webhooks.d.ts +0 -1
- package/dist/webhooks.js +0 -1
- package/dist/workspace-ui.d.ts +0 -8
- package/dist/workspace-ui.js +1 -6
- package/dist/writes-mode.d.ts +9 -0
- package/dist/writes-mode.js +8 -0
- package/package.json +5 -1
- package/dist/contracts/native-census.d.ts +0 -42
- package/dist/contracts/native-census.js +0 -36
- package/dist/process-recovery-anchor.d.ts +0 -75
- package/dist/process-recovery-anchor.js +0 -188
- package/dist/process-recovery-coalition.d.ts +0 -25
- package/dist/process-recovery-coalition.js +0 -105
- package/dist/process-recovery-eligibility.d.ts +0 -179
- package/dist/process-recovery-eligibility.js +0 -174
- package/dist/process-recovery-native.d.ts +0 -161
- package/dist/process-recovery-native.js +0 -595
- package/dist/process-recovery-provenance.d.ts +0 -163
- package/dist/process-recovery-provenance.js +0 -292
- package/dist/process-recovery-services.d.ts +0 -56
- package/dist/process-recovery-services.js +0 -191
- package/dist/process-recovery-settlement.d.ts +0 -32
- package/dist/process-recovery-settlement.js +0 -133
- package/dist/process-recovery.d.ts +0 -41
- package/dist/process-recovery.js +0 -91
- package/dist/session-brief.d.ts +0 -8
- package/dist/session-brief.js +0 -24
- package/dist/session-cli.d.ts +0 -42
- package/dist/session-cli.js +0 -271
- package/dist/session-contract.d.ts +0 -183
- package/dist/session-contract.js +0 -122
- package/dist/session-remote.d.ts +0 -19
- package/dist/session-remote.js +0 -86
- package/dist/session-service.d.ts +0 -22
- package/dist/session-service.js +0 -157
- package/dist/team-cli.js +0 -692
package/dist/provider.js
CHANGED
|
@@ -999,32 +999,6 @@ export const MONEY_CAPABILITIES = withAcp({
|
|
|
999
999
|
nativeDollarCapFlag: null,
|
|
1000
1000
|
usageSemantics: "per-invocation",
|
|
1001
1001
|
}));
|
|
1002
|
-
/**
|
|
1003
|
-
* The fail-closed budget-flag probe (finding 24's amendment): resolve
|
|
1004
|
-
* the EXACT executable that will spawn, read its version, and prove the
|
|
1005
|
-
* flag exists in that binary's own help — presence is a feature check
|
|
1006
|
-
* and nothing more.
|
|
1007
|
-
*/
|
|
1008
|
-
export async function probeBudgetCap(provider, runner) {
|
|
1009
|
-
const flag = MONEY_CAPABILITIES[provider].nativeDollarCapFlag;
|
|
1010
|
-
if (flag === null)
|
|
1011
|
-
return { ok: false, problem: `${provider} has no native dollar cap` };
|
|
1012
|
-
const binary = provider === "claude" ? "claude" : provider;
|
|
1013
|
-
// A feature probe needs no credential (round 2, finding 2): strip them all.
|
|
1014
|
-
const strip = { omitEnv: ALL_CREDENTIAL_ENV };
|
|
1015
|
-
const where = await runner("which", [binary], { timeoutMs: 5_000, ...strip });
|
|
1016
|
-
if (where.code !== 0)
|
|
1017
|
-
return { ok: false, problem: `${binary} is not on PATH` };
|
|
1018
|
-
const executable = where.stdout.trim().split("\n")[0] ?? "";
|
|
1019
|
-
const version = await runner(executable, ["--version"], { timeoutMs: 10_000, ...strip });
|
|
1020
|
-
if (version.code !== 0)
|
|
1021
|
-
return { ok: false, problem: `${executable} did not answer --version` };
|
|
1022
|
-
const help = await runner(executable, ["--help"], { timeoutMs: 10_000, ...strip });
|
|
1023
|
-
if (help.code !== 0 || !help.stdout.includes(flag)) {
|
|
1024
|
-
return { ok: false, problem: `${executable} does not advertise ${flag} — the dollar cap cannot be enforced` };
|
|
1025
|
-
}
|
|
1026
|
-
return { ok: true, executable, version: version.stdout.trim() };
|
|
1027
|
-
}
|
|
1028
1002
|
export function auditOf(provider) {
|
|
1029
1003
|
return AUDITS[provider];
|
|
1030
1004
|
}
|
package/dist/pulls.d.ts
CHANGED
|
@@ -119,5 +119,3 @@ export declare function describe(pull: Pull, markers?: readonly string[]): strin
|
|
|
119
119
|
* and not evidence of freshness.
|
|
120
120
|
*/
|
|
121
121
|
export declare function idleDays(pull: Pull, now: Date): number | null;
|
|
122
|
-
/** Waiting on someone, and has been for longer than they would have chosen. */
|
|
123
|
-
export declare function isStalled(pull: Pull, now: Date, staleDays?: number, markers?: readonly string[]): boolean;
|
package/dist/pulls.js
CHANGED
|
@@ -236,15 +236,6 @@ export function idleDays(pull, now) {
|
|
|
236
236
|
return null;
|
|
237
237
|
return Math.max(0, (now.getTime() - updated) / MS_PER_DAY);
|
|
238
238
|
}
|
|
239
|
-
/** Waiting on someone, and has been for longer than they would have chosen. */
|
|
240
|
-
export function isStalled(pull, now, staleDays = DEFAULT_STALE_DAYS, markers = DEFAULT_REVISION_MARKERS) {
|
|
241
|
-
const attention = classify(pull, markers);
|
|
242
|
-
// Nobody is blocked on a draft, and a machine that is still working is not late.
|
|
243
|
-
if (attention === "none" || attention === "machine")
|
|
244
|
-
return false;
|
|
245
|
-
const idle = idleDays(pull, now);
|
|
246
|
-
return idle !== null && idle >= staleDays;
|
|
247
|
-
}
|
|
248
239
|
function firstLine(text) {
|
|
249
240
|
const [line = ""] = text.trim().split("\n");
|
|
250
241
|
return line;
|
|
@@ -54,7 +54,16 @@ export type AdapterOptions = {
|
|
|
54
54
|
now?: () => Date;
|
|
55
55
|
sleep?: (ms: number) => Promise<void>;
|
|
56
56
|
};
|
|
57
|
+
/** How many times, and how far apart, a merged branch's deletion is looked for after the push lost a race. */
|
|
58
|
+
export declare const BRANCH_GONE_LOOKS = 5;
|
|
59
|
+
export declare const BRANCH_GONE_PAUSE_MS = 2000;
|
|
57
60
|
export declare function releaseAdapters(options: AdapterOptions): ReleaseAdapters;
|
|
61
|
+
/**
|
|
62
|
+
* Whether a workflow's triggers (its top-level `on:` as a name, a list or a map) include a change to the repository.
|
|
63
|
+
* A file that doesn't parse, or whose triggers aren't one of those shapes, counts as running, so the release waits
|
|
64
|
+
* rather than skipping checks it owes.
|
|
65
|
+
*/
|
|
66
|
+
export declare function workflowRunsOnChange(text: string): boolean;
|
|
58
67
|
/** The formula with its first `url` and `sha256` replaced; anything else is left exactly as it was. */
|
|
59
68
|
export declare function bumpFormula(text: string, url: string, sha256: string): string;
|
|
60
69
|
export {};
|
package/dist/release-adapters.js
CHANGED
|
@@ -10,6 +10,8 @@ import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs";
|
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
11
|
import { basename, delimiter, dirname, join } from "node:path";
|
|
12
12
|
import { createRequire } from "node:module";
|
|
13
|
+
import { parse as parseYaml } from "yaml";
|
|
14
|
+
import { sleep } from "./sleep.js";
|
|
13
15
|
// Loaded on first use, like the store: a static import prints Node's SQLite warning before the CLI can quiet it.
|
|
14
16
|
const sqlite = () => createRequire(import.meta.url)("node:sqlite");
|
|
15
17
|
import { activeUpdateWork } from "./desktop-update-gate.js";
|
|
@@ -147,6 +149,9 @@ export function stageGate(stage, owner) {
|
|
|
147
149
|
const id = textOr(journal["id"]), phase = textOr(journal["phase"]);
|
|
148
150
|
return id !== null && phase !== null && BEFORE_SWAP.includes(phase) && owner() === id ? { id, phase } : null;
|
|
149
151
|
}
|
|
152
|
+
/** How many times, and how far apart, a merged branch's deletion is looked for after the push lost a race. */
|
|
153
|
+
export const BRANCH_GONE_LOOKS = 5;
|
|
154
|
+
export const BRANCH_GONE_PAUSE_MS = 2_000;
|
|
150
155
|
export function releaseAdapters(options) {
|
|
151
156
|
const run = options.run ?? exec;
|
|
152
157
|
const get = options.fetch ?? fetch;
|
|
@@ -154,6 +159,7 @@ export function releaseAdapters(options) {
|
|
|
154
159
|
const tap = options.tap ?? "ap9000/homebrew-toolroll";
|
|
155
160
|
const formula = options.formula ?? "Formula/toolroll.rb";
|
|
156
161
|
const git = (...args) => run("git", ["-C", checkout, ...args]);
|
|
162
|
+
const pause = options.sleep ?? ((ms) => new Promise(done => setTimeout(done, ms)));
|
|
157
163
|
const gh = (...args) => run("gh", args);
|
|
158
164
|
const ghJson = async (what, ...args) => JSON.parse(await must(gh(...args), what));
|
|
159
165
|
const toolroll = async (...args) => {
|
|
@@ -189,7 +195,7 @@ export function releaseAdapters(options) {
|
|
|
189
195
|
});
|
|
190
196
|
return {
|
|
191
197
|
now: options.now ?? (() => new Date()),
|
|
192
|
-
sleep: options.sleep ??
|
|
198
|
+
sleep: options.sleep ?? sleep,
|
|
193
199
|
say: options.say ?? (line => process.stdout.write(`${line}\n`)),
|
|
194
200
|
repo: {
|
|
195
201
|
async github() {
|
|
@@ -225,7 +231,19 @@ export function releaseAdapters(options) {
|
|
|
225
231
|
return;
|
|
226
232
|
if (current !== head)
|
|
227
233
|
throw new Error(`${branch} moved after merging; its new head was not deleted.`);
|
|
228
|
-
await
|
|
234
|
+
const pushed = await git("push", `--force-with-lease=${ref}:${head}`, "origin", `:${ref}`);
|
|
235
|
+
if (pushed.code === 0)
|
|
236
|
+
return;
|
|
237
|
+
// GitHub may delete a merged branch itself between the look and the push; gone is what was wanted. Its refs can
|
|
238
|
+
// lag the deletion by a few seconds, so look a few times before calling it a failure.
|
|
239
|
+
for (let look = 0; look < BRANCH_GONE_LOOKS; look++) {
|
|
240
|
+
if (look > 0)
|
|
241
|
+
await pause(BRANCH_GONE_PAUSE_MS);
|
|
242
|
+
const after = (await must(git("ls-remote", "origin", ref), "git ls-remote")).trim().split(/\s+/)[0] || null;
|
|
243
|
+
if (after === null)
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
throw new Error(`deleting merged branch ${branch} failed: ${pushed.stderr.trim() || `exit ${pushed.code}`}`);
|
|
229
247
|
},
|
|
230
248
|
async tree(sha) { return (await must(git("rev-parse", `${sha}^{tree}`), `git rev-parse ${sha.slice(0, 7)}^{tree}`)).trim(); },
|
|
231
249
|
async fileAt(sha, path) {
|
|
@@ -338,10 +356,10 @@ export function releaseAdapters(options) {
|
|
|
338
356
|
return prs.find(one => one.state === "open") ?? prs.filter(one => one.state === "merged").sort((a, b) => b.number - a.number)[0] ?? null;
|
|
339
357
|
},
|
|
340
358
|
async checks(repo, sha) {
|
|
341
|
-
const out = await must(gh("api", "--paginate", `repos/${repo}/commits/${sha}/check-runs?per_page=100`, "--jq", ".check_runs[] | {id, name, status, conclusion}"), "gh api check-runs");
|
|
359
|
+
const out = await must(gh("api", "--paginate", `repos/${repo}/commits/${sha}/check-runs?per_page=100`, "--jq", ".check_runs[] | {id, name, status, conclusion, app: .app.slug}"), "gh api check-runs");
|
|
342
360
|
const runs = out.split("\n").filter(Boolean).map(line => object(JSON.parse(line)));
|
|
343
361
|
// Oldest first, so a rerun's attempt is the last one listed for its name.
|
|
344
|
-
return runs.sort((a, b) => Number(a["id"]) - Number(b["id"])).map((one) => ({ name: String(one["name"]), status: String(one["status"]), conclusion: textOr(one["conclusion"]) }));
|
|
362
|
+
return runs.sort((a, b) => Number(a["id"]) - Number(b["id"])).map((one) => ({ name: String(one["name"]), status: String(one["status"]), conclusion: textOr(one["conclusion"]), app: textOr(one["app"]) }));
|
|
345
363
|
},
|
|
346
364
|
async merge(repo, pr, head) {
|
|
347
365
|
const merged = object(await ghJson("gh api pulls merge", "api", "-X", "PUT", `repos/${repo}/pulls/${pr}/merge`, "-f", "merge_method=squash", "-f", `sha=${head}`));
|
|
@@ -379,6 +397,37 @@ export function releaseAdapters(options) {
|
|
|
379
397
|
},
|
|
380
398
|
homebrew: {
|
|
381
399
|
repo: tap,
|
|
400
|
+
async checked(head) {
|
|
401
|
+
// Only a workflow that runs on pull requests (or pushes) puts checks on the tap's change; apps that open a
|
|
402
|
+
// check suite and never run it don't count. Workflows are read as the change has them and as the tap's default
|
|
403
|
+
// branch has them (pull_request_target runs from there); a file this can't read counts as checking.
|
|
404
|
+
for (const ref of [head, await defaultBranch()]) {
|
|
405
|
+
const listed = await gh("api", `repos/${tap}/contents/.github/workflows?ref=${encodeURIComponent(ref)}`, "--jq", ".[] | select(.type == \"file\") | .path");
|
|
406
|
+
if (listed.code !== 0) {
|
|
407
|
+
if (/Not Found|HTTP 404/.test(listed.stderr + listed.stdout))
|
|
408
|
+
continue;
|
|
409
|
+
throw new Error(`listing ${tap}'s workflows at ${ref}: ${listed.stderr.trim()}`);
|
|
410
|
+
}
|
|
411
|
+
for (const path of listed.stdout.split("\n").filter(Boolean)) {
|
|
412
|
+
if (!/\.ya?ml$/.test(path))
|
|
413
|
+
continue;
|
|
414
|
+
const file = await gh("api", `repos/${tap}/contents/${path}?ref=${encodeURIComponent(ref)}`, "--jq", ".content");
|
|
415
|
+
if (file.code !== 0)
|
|
416
|
+
return true;
|
|
417
|
+
if (workflowRunsOnChange(Buffer.from(file.stdout.replace(/\\n|\s/g, ""), "base64").toString("utf8")))
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return false;
|
|
422
|
+
},
|
|
423
|
+
async statuses(head) {
|
|
424
|
+
// The older commit-status API, which some CI reports through instead of checks; newest first, so reversed.
|
|
425
|
+
const out = await must(gh("api", "--paginate", `repos/${tap}/commits/${head}/statuses?per_page=100`, "--jq", ".[] | {context, state}"), "gh api statuses");
|
|
426
|
+
return out.split("\n").filter(Boolean).map(line => object(JSON.parse(line))).reverse().map((one) => {
|
|
427
|
+
const state = String(one["state"]);
|
|
428
|
+
return { name: `status: ${String(one["context"])}`, status: state === "pending" ? "in_progress" : "completed", conclusion: state === "pending" ? null : state === "success" ? "success" : "failure" };
|
|
429
|
+
});
|
|
430
|
+
},
|
|
382
431
|
async state(version) {
|
|
383
432
|
const branch = await defaultBranch();
|
|
384
433
|
const current = await formulaAt(branch);
|
|
@@ -427,6 +476,34 @@ export function releaseAdapters(options) {
|
|
|
427
476
|
}
|
|
428
477
|
}
|
|
429
478
|
const brewBranch = (version) => `toolroll-${version}`;
|
|
479
|
+
/** The events that put checks on a change. */
|
|
480
|
+
const CHANGE_EVENTS = new Set(["pull_request", "pull_request_target", "push", "merge_group"]);
|
|
481
|
+
/**
|
|
482
|
+
* Whether a workflow's triggers (its top-level `on:` as a name, a list or a map) include a change to the repository.
|
|
483
|
+
* A file that doesn't parse, or whose triggers aren't one of those shapes, counts as running, so the release waits
|
|
484
|
+
* rather than skipping checks it owes.
|
|
485
|
+
*/
|
|
486
|
+
export function workflowRunsOnChange(text) {
|
|
487
|
+
let parsed;
|
|
488
|
+
try {
|
|
489
|
+
parsed = parseYaml(text);
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
494
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
495
|
+
return true;
|
|
496
|
+
const workflow = parsed;
|
|
497
|
+
// YAML 1.1 readers take a bare `on` for true; either key names the triggers.
|
|
498
|
+
const on = "on" in workflow ? workflow["on"] : workflow["true"];
|
|
499
|
+
const events = typeof on === "string" ? [on]
|
|
500
|
+
: Array.isArray(on) ? on
|
|
501
|
+
: on !== null && typeof on === "object" ? Object.keys(on)
|
|
502
|
+
: null;
|
|
503
|
+
if (events === null || !events.every(one => typeof one === "string"))
|
|
504
|
+
return true;
|
|
505
|
+
return events.some(one => CHANGE_EVENTS.has(one));
|
|
506
|
+
}
|
|
430
507
|
/** The formula with its first `url` and `sha256` replaced; anything else is left exactly as it was. */
|
|
431
508
|
export function bumpFormula(text, url, sha256) {
|
|
432
509
|
if (!/^\s*url "[^"]*"/m.test(text) || !/^\s*sha256 "[0-9a-f]{64}"/m.test(text))
|
package/dist/release.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* skipped or failed never merges. merge
|
|
18
18
|
* squash-merges with the head pinned to the gated commit, tree proves main's new commit has exactly the gated tree,
|
|
19
19
|
* and only then tag pushes the version tag, whose macOS matrix must pass before npm publication (publish.yml). publish
|
|
20
|
-
* waits for npm, the GitHub release and the macOS checks; homebrew updates the tap's formula and merges it.
|
|
20
|
+
* waits for npm, the GitHub release and the macOS checks; homebrew updates the tap's formula and merges it (after its checks, when the tap runs any).
|
|
21
21
|
*
|
|
22
22
|
* A private journal (mode 0600, written whole then renamed) records what each step is about to do before it does
|
|
23
23
|
* anything outside this machine, and what it saw after. A stop — a time limit, a failure, a crash, Ctrl-C — leaves the
|
|
@@ -38,10 +38,12 @@ export type ReleaseStep = typeof RELEASE_STEPS[number];
|
|
|
38
38
|
type WaitingStep = "approval" | "check" | "quiet" | "deploy" | "ci" | "publish" | "homebrew";
|
|
39
39
|
/** How long each waiting step may take, in minutes, before the release stops there (rerunning continues from it). */
|
|
40
40
|
export declare const RELEASE_LIMITS: Readonly<Record<WaitingStep, number>>;
|
|
41
|
+
/** One check attempt; `app` is the GitHub App that made it (github-actions for a workflow), when known. */
|
|
41
42
|
export type CheckRun = {
|
|
42
43
|
name: string;
|
|
43
44
|
status: string;
|
|
44
45
|
conclusion: string | null;
|
|
46
|
+
app?: string | null;
|
|
45
47
|
};
|
|
46
48
|
export type PullRequest = {
|
|
47
49
|
number: number;
|
|
@@ -195,6 +197,10 @@ export type ReleaseAdapters = {
|
|
|
195
197
|
};
|
|
196
198
|
homebrew: {
|
|
197
199
|
repo: string;
|
|
200
|
+
/** Whether any of the tap's workflows, at the change or its base, runs on a change; if none does, no checks are owed. */
|
|
201
|
+
checked(head: string): Promise<boolean>;
|
|
202
|
+
/** The change's commit statuses, as check runs oldest first. */
|
|
203
|
+
statuses(head: string): Promise<CheckRun[]>;
|
|
198
204
|
state(version: string): Promise<HomebrewState>;
|
|
199
205
|
propose(version: string): Promise<{
|
|
200
206
|
pr: number;
|
package/dist/release.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* skipped or failed never merges. merge
|
|
18
18
|
* squash-merges with the head pinned to the gated commit, tree proves main's new commit has exactly the gated tree,
|
|
19
19
|
* and only then tag pushes the version tag, whose macOS matrix must pass before npm publication (publish.yml). publish
|
|
20
|
-
* waits for npm, the GitHub release and the macOS checks; homebrew updates the tap's formula and merges it.
|
|
20
|
+
* waits for npm, the GitHub release and the macOS checks; homebrew updates the tap's formula and merges it (after its checks, when the tap runs any).
|
|
21
21
|
*
|
|
22
22
|
* A private journal (mode 0600, written whole then renamed) records what each step is about to do before it does
|
|
23
23
|
* anything outside this machine, and what it saw after. A stop — a time limit, a failure, a crash, Ctrl-C — leaves the
|
|
@@ -454,15 +454,28 @@ export async function runRelease(options, adapters) {
|
|
|
454
454
|
const proposed = state;
|
|
455
455
|
j.homebrew = { pr: proposed.pr, head: proposed.head, merged: false };
|
|
456
456
|
save();
|
|
457
|
+
// Whatever checks do register are always judged; an empty set passes only for a tap whose workflows never run
|
|
458
|
+
// on a change, and only when it's still empty a poll later.
|
|
459
|
+
const checked = await brew.checked(proposed.head);
|
|
460
|
+
let emptyLooks = 0;
|
|
457
461
|
await waitFor("homebrew", `the Homebrew change's checks (${brew.repo} #${proposed.pr})`, async () => {
|
|
458
|
-
|
|
462
|
+
// Check runs and commit statuses are judged apart, so neither stands in for the other or hides its failure.
|
|
463
|
+
// Two apps may name a check alike; each app's attempts stand for themselves.
|
|
464
|
+
const runs = (await adapters.github.checks(brew.repo, proposed.head)).map(run => ({ ...run, name: `${run.app ?? "unknown app"}: ${run.name}` }));
|
|
465
|
+
const statuses = await brew.statuses(proposed.head);
|
|
466
|
+
const verdicts = [runs, statuses].filter(one => one.length > 0).map(one => checksVerdict(one, [...new Set(one.map(run => run.name))]));
|
|
467
|
+
const failed = verdicts.flatMap(one => one.state === "failed" ? one.failed : []);
|
|
468
|
+
if (failed.length > 0)
|
|
469
|
+
throw new Stop("homebrew-failed", `The Homebrew change's checks failed: ${failed.join(", ")}.`);
|
|
470
|
+
if (verdicts.some(one => one.state === "pending"))
|
|
471
|
+
return null;
|
|
459
472
|
// GitHub registers checks asynchronously; an empty set says nothing about success.
|
|
460
|
-
|
|
473
|
+
// A workflow is owed: only its own run counts, not another app's green check.
|
|
474
|
+
if (checked && !runs.some(run => run.app === "github-actions"))
|
|
461
475
|
return null;
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
return verdict.state === "passed" ? true : null;
|
|
476
|
+
if (runs.length === 0 && statuses.length === 0)
|
|
477
|
+
return ++emptyLooks >= 2 ? true : null;
|
|
478
|
+
return true;
|
|
466
479
|
});
|
|
467
480
|
await brew.merge(proposed.pr, proposed.head);
|
|
468
481
|
}
|
package/dist/remote-exec.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ConnectOptions } from './connect-cli.js';
|
|
2
2
|
import { type CliRequest } from './cli-http.js';
|
|
3
3
|
import { type RemoteCommandLookup } from './remote-command.js';
|
|
4
4
|
export { contractRow, STEP_UP_MESSAGE, type RemoteMode } from './remote-command.js';
|
|
5
|
-
export type RemoteExecOptions =
|
|
5
|
+
export type RemoteExecOptions = ConnectOptions & {
|
|
6
|
+
sleep?: (milliseconds: number, signal: AbortSignal) => Promise<void>;
|
|
6
7
|
cwd?: string;
|
|
7
8
|
/** Raw output streams; remote bytes are written exactly as the server returned them. */
|
|
8
9
|
stdout?: (chunk: string) => void;
|
package/dist/remote-exec.js
CHANGED
|
@@ -12,13 +12,14 @@ import { resolve } from 'node:path';
|
|
|
12
12
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
13
13
|
import { envelopeJson } from './envelope.js';
|
|
14
14
|
import { COMMAND_GUIDE } from './surface.js';
|
|
15
|
-
import { centralProfile, UsageError } from './
|
|
15
|
+
import { centralProfile, UsageError } from './connect-cli.js';
|
|
16
16
|
import { CLI_FILES_BYTES, CLI_WAIT_SECONDS } from './cli-http.js';
|
|
17
17
|
import { TOKEN_REPLACEMENT } from './password-bearer.js';
|
|
18
18
|
import { contractRow, fileArguments, STEP_UP_MESSAGE } from './remote-command.js';
|
|
19
19
|
export { contractRow, STEP_UP_MESSAGE } from './remote-command.js';
|
|
20
20
|
const EXIT = { ok: 0, failed: 1, usage: 2, refused: 3 };
|
|
21
|
-
/**
|
|
21
|
+
/** Never sent remotely: `connect` saves the profile itself, the lead's commands are this computer's, and the removed
|
|
22
|
+
* central team and session verbs answer with their removal notice (removed-commands.ts). */
|
|
22
23
|
const TEAM_COMMANDS = new Set(['connect', 'lead', 'conversation', 'chat', 'brief', 'session']);
|
|
23
24
|
const RESPONSE_BYTES = 16 * 1024 * 1024;
|
|
24
25
|
const REQUEST_TIMEOUT_MS = (CLI_WAIT_SECONDS + 35) * 1_000;
|
package/dist/repos.d.ts
CHANGED
|
@@ -50,11 +50,6 @@ export declare function updateRepos(file: string, transform: (repos: string[]) =
|
|
|
50
50
|
ok: true;
|
|
51
51
|
repos: string[];
|
|
52
52
|
} | RegistryUpdateFailure>;
|
|
53
|
-
/** Persist project roots through the same lock as repo enrollment. */
|
|
54
|
-
export declare function updateProjectRoots(file: string, transform: (roots: string[]) => string[]): Promise<{
|
|
55
|
-
ok: true;
|
|
56
|
-
roots: string[];
|
|
57
|
-
} | RegistryUpdateFailure>;
|
|
58
53
|
/** Atomically update repositories and roots without either writer erasing the other. */
|
|
59
54
|
export declare function updateProjectRegistry(file: string, transform: (registry: ProjectRegistry) => ProjectRegistry): Promise<{
|
|
60
55
|
ok: true;
|
package/dist/repos.js
CHANGED
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
* untrusted input and gets validated like any other.
|
|
12
12
|
*/
|
|
13
13
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
14
|
-
import { dirname
|
|
14
|
+
import { dirname } from "node:path";
|
|
15
15
|
import { configBase, namedPath } from "./names.js";
|
|
16
|
+
import { sleep } from "./sleep.js";
|
|
16
17
|
export const CONFIG_VERSION = 2;
|
|
17
18
|
const FILE_NAME = "repos.json";
|
|
18
19
|
export function configPath(env, home) {
|
|
@@ -76,14 +77,6 @@ export async function updateRepos(file, transform) {
|
|
|
76
77
|
}));
|
|
77
78
|
return updated.ok ? { ok: true, repos: updated.registry.repos } : updated;
|
|
78
79
|
}
|
|
79
|
-
/** Persist project roots through the same lock as repo enrollment. */
|
|
80
|
-
export async function updateProjectRoots(file, transform) {
|
|
81
|
-
const updated = await updateProjectRegistry(file, current => ({
|
|
82
|
-
...current,
|
|
83
|
-
roots: transform(current.roots),
|
|
84
|
-
}));
|
|
85
|
-
return updated.ok ? { ok: true, roots: updated.registry.roots } : updated;
|
|
86
|
-
}
|
|
87
80
|
/** Atomically update repositories and roots without either writer erasing the other. */
|
|
88
81
|
export async function updateProjectRegistry(file, transform) {
|
|
89
82
|
const { openSync, closeSync, writeSync, readFileSync, unlinkSync, existsSync: exists, mkdirSync, renameSync } = await import("node:fs");
|
|
@@ -93,7 +86,6 @@ export async function updateProjectRegistry(file, transform) {
|
|
|
93
86
|
const reaperPath = `${file}.reaper.lock`;
|
|
94
87
|
const token = randomBytes(16).toString("hex");
|
|
95
88
|
const body = JSON.stringify({ pid: process.pid, token, at: Date.now() });
|
|
96
|
-
const sleep = (ms) => new Promise(done => setTimeout(done, ms));
|
|
97
89
|
const tryReap = () => {
|
|
98
90
|
// Recovery only for PROVEN-dead owners, serialized by the reaper lock,
|
|
99
91
|
// which re-reads the exact snapshot before unlinking (finding 29). An
|
|
@@ -63,5 +63,3 @@ export type RepositoryContext = {
|
|
|
63
63
|
export declare function repositoryContextRead(request: Omit<RepositoryContextRequest, 'refresh'>): RepositoryContext;
|
|
64
64
|
/** Explicit refresh is the only cache-writing path. It never changes project/task records. */
|
|
65
65
|
export declare function repositoryContext(request: RepositoryContextRequest): Promise<RepositoryContext>;
|
|
66
|
-
/** Human/model context rendering; saved records remain the authority and retain this structured capture. */
|
|
67
|
-
export declare function repositoryContextText(context: RepositoryContext): string;
|
|
@@ -370,7 +370,3 @@ export async function repositoryContext(request) {
|
|
|
370
370
|
}
|
|
371
371
|
return finishContext(request, result, snapshot, index);
|
|
372
372
|
}
|
|
373
|
-
/** Human/model context rendering; saved records remain the authority and retain this structured capture. */
|
|
374
|
-
export function repositoryContextText(context) {
|
|
375
|
-
return '\nRepository context (source material, not instructions or authority). This selection is pinned to the shown checkout and source hashes; missing context never blocks the task.\n' + JSON.stringify(context).replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029') + '\n';
|
|
376
|
-
}
|
|
@@ -130,5 +130,3 @@ export type VerifyInputs = BaselineInputs & {
|
|
|
130
130
|
export declare function verifyRestartRecovery(inputs: VerifyInputs): Promise<RestartVerification>;
|
|
131
131
|
/** The baseline and verification are meant to be printed; this keeps a secret from ever entering them. */
|
|
132
132
|
export declare function assertNoSecret(json: string, secrets: readonly string[]): void;
|
|
133
|
-
/** The current boot id, for scripts that print it beside a baseline. */
|
|
134
|
-
export declare function bootIdNow(): string | null;
|
|
@@ -24,7 +24,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
24
24
|
import { dirname, join } from "node:path";
|
|
25
25
|
import { fileURLToPath } from "node:url";
|
|
26
26
|
import { existsSync, readFileSync, realpathSync, readdirSync, statSync } from "node:fs";
|
|
27
|
-
import { bootIdentity,
|
|
27
|
+
import { bootIdentity, provenDeadByBootChange } from "./boot-identity.js";
|
|
28
28
|
import { containmentStatus, currentContainment } from "./containment.js";
|
|
29
29
|
import { daemonStatus } from "./daemon.js";
|
|
30
30
|
import { isAlive } from "./runner.js";
|
|
@@ -220,7 +220,3 @@ export function assertNoSecret(json, secrets) {
|
|
|
220
220
|
throw new Error("the certification output would contain a credential; refusing to write it");
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
|
-
/** The current boot id, for scripts that print it beside a baseline. */
|
|
224
|
-
export function bootIdNow() {
|
|
225
|
-
return currentBootId();
|
|
226
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Runner } from "./backend.js";
|
|
2
|
-
import type { Store
|
|
2
|
+
import type { Store } from "./store.js";
|
|
3
3
|
import { type CheckLevel } from "./check-levels.js";
|
|
4
4
|
export declare const CHECKS_REQUESTED = "checks requested";
|
|
5
5
|
export declare const CHECKS_STARTED = "checks started";
|
|
@@ -93,8 +93,6 @@ export declare function withFollowUps(original: CheckReading, followUps: readonl
|
|
|
93
93
|
/** The level a result's checks stand at now: the build's own level, raised by
|
|
94
94
|
* a follow-up that passed on the same commit. null for results from before levels. */
|
|
95
95
|
export declare function resultCheckLevel(store: Store, runId: number, now: Date): CheckLevel | null;
|
|
96
|
-
/** Whether this result's commit has passed the project's full check, here or in a follow-up. */
|
|
97
|
-
export declare function fullCheckPassed(reading: CheckReading): boolean;
|
|
98
96
|
/** Before a Quick or Off result merges: the project's full check on its exact
|
|
99
97
|
* commit. Clear when the build already ran the full check, when the project has
|
|
100
98
|
* no full command (its CI covers it), or when a follow-up full check passed. */
|
|
@@ -105,7 +103,6 @@ export type FullCheckGate = {
|
|
|
105
103
|
message: string;
|
|
106
104
|
};
|
|
107
105
|
export declare function fullCheckGate(store: Store, runId: number, now: Date): FullCheckGate;
|
|
108
|
-
export declare const isQuickCommand: (command: VerifyCommand | null, repo: string) => boolean;
|
|
109
106
|
/** File a small task to write tests for a result's change. It is filed like any
|
|
110
107
|
* other task: nothing runs until a person approves it. */
|
|
111
108
|
export declare function fileAddTestsTask(store: Store, root: string | null, input: {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Add tests files a small task, unapproved like any other filing, to write
|
|
11
11
|
* tests for that change. */
|
|
12
|
+
import { fencedCheckoutCommands } from "./invoke.js";
|
|
12
13
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
13
14
|
import { tmpdir } from "node:os";
|
|
14
15
|
import { join } from "node:path";
|
|
@@ -16,7 +17,7 @@ import { run as execRun } from "./exec.js";
|
|
|
16
17
|
import { approvedCommandShell, SETUP_ENV_ALLOWLIST, SETUP_ENV_DENYLIST } from "./builder.js";
|
|
17
18
|
import { runWithIsolatedDatabase } from "./child-database.js";
|
|
18
19
|
import { boundStreamHeadTail, readVerifiedArtifact, redactSecretLines, scanForSecrets, storeEvidence } from "./evidence.js";
|
|
19
|
-
import { CHECK_LEVEL_WORDS, checkCommandFor,
|
|
20
|
+
import { CHECK_LEVEL_WORDS, checkCommandFor, repoClause, repoOfRun, runCheckLevel } from "./check-levels.js";
|
|
20
21
|
import { fileTaskProposal } from "./proposal.js";
|
|
21
22
|
export const CHECKS_REQUESTED = "checks requested";
|
|
22
23
|
export const CHECKS_STARTED = "checks started";
|
|
@@ -201,7 +202,7 @@ export async function runFollowUpCheck(store, root, request, deps = {}) {
|
|
|
201
202
|
let ready = true;
|
|
202
203
|
if (setup !== null) {
|
|
203
204
|
const shell = approvedCommandShell(setup.command);
|
|
204
|
-
const prepared = await runWithIsolatedDatabase(runner, shell.file, shell.args, { cwd: checkout, timeoutMs: setup.timeoutMs, processGroup: true, envAllowlist: SETUP_ENV_ALLOWLIST, omitEnv: SETUP_ENV_DENYLIST });
|
|
205
|
+
const prepared = await runWithIsolatedDatabase(fencedCheckoutCommands(store, target.repo, checkout, runner), shell.file, shell.args, { cwd: checkout, timeoutMs: setup.timeoutMs, processGroup: true, envAllowlist: SETUP_ENV_ALLOWLIST, omitEnv: SETUP_ENV_DENYLIST });
|
|
205
206
|
note("Approved project setup", setup.command, prepared);
|
|
206
207
|
if (prepared.code !== 0 || prepared.timedOut || prepared.notFound) {
|
|
207
208
|
ready = false;
|
|
@@ -216,7 +217,7 @@ export async function runFollowUpCheck(store, root, request, deps = {}) {
|
|
|
216
217
|
}
|
|
217
218
|
else {
|
|
218
219
|
const shell = approvedCommandShell(live.command);
|
|
219
|
-
const ran = await runWithIsolatedDatabase(runner, shell.file, shell.args, { cwd: checkout, timeoutMs: live.timeoutMs, processGroup: true, envAllowlist: SETUP_ENV_ALLOWLIST, omitEnv: SETUP_ENV_DENYLIST });
|
|
220
|
+
const ran = await runWithIsolatedDatabase(fencedCheckoutCommands(store, target.repo, checkout, runner), shell.file, shell.args, { cwd: checkout, timeoutMs: live.timeoutMs, processGroup: true, envAllowlist: SETUP_ENV_ALLOWLIST, omitEnv: SETUP_ENV_DENYLIST });
|
|
220
221
|
note(`${CHECK_LEVEL_WORDS[target.level]} check`, live.command, ran);
|
|
221
222
|
result = ran.timedOut ? { ran: false, failure: "The check ran out of time." } : ran.notFound ? { ran: false, failure: "The check couldn't start." } : { ran: true, exitCode: ran.code };
|
|
222
223
|
}
|
|
@@ -283,10 +284,6 @@ export function resultCheckLevel(store, runId, now) {
|
|
|
283
284
|
const read = withFollowUps({ status: level === "off" ? "not-run" : "passed", level, exitCode: null, head: run?.headRevision ?? null }, follows);
|
|
284
285
|
return read.level;
|
|
285
286
|
}
|
|
286
|
-
/** Whether this result's commit has passed the project's full check, here or in a follow-up. */
|
|
287
|
-
export function fullCheckPassed(reading) {
|
|
288
|
-
return reading.status === "passed" && (reading.level === "full" || reading.level === null);
|
|
289
|
-
}
|
|
290
287
|
export function fullCheckGate(store, runId, now) {
|
|
291
288
|
const level = runCheckLevel(store, runId);
|
|
292
289
|
const repo = repoOfRun(store, runId);
|
|
@@ -309,7 +306,6 @@ export function fullCheckGate(store, runId, now) {
|
|
|
309
306
|
return { state: "not-run", message: `The full check didn't finish on this commit${last.note === null ? "" : `: ${last.note}`} Run it again, or merge anyway.` };
|
|
310
307
|
return { state: "waiting", message: "The full check is still running on this commit. Merge once it passes, or merge anyway." };
|
|
311
308
|
}
|
|
312
|
-
export const isQuickCommand = (command, repo) => command !== null && command.repo === quickVerifyKey(repo);
|
|
313
309
|
// ---- Add tests --------------------------------------------------------------
|
|
314
310
|
/** File a small task to write tests for a result's change. It is filed like any
|
|
315
311
|
* other task: nothing runs until a person approves it. */
|
package/dist/result-review.d.ts
CHANGED
|
@@ -182,8 +182,6 @@ export declare function parseRevisionBatch(raw: string | null | undefined): numb
|
|
|
182
182
|
/** The source binding the revision form carries: the scope digest the
|
|
183
183
|
* page was rendered against, or "none". */
|
|
184
184
|
export declare function revisionSourceOf(scopeDigest: string | null): string;
|
|
185
|
-
export declare const REVIEW_DRAFT_PREFIX = "standing-orders:review-draft:";
|
|
186
|
-
export declare const RESULT_SCROLL_PREFIX = "standing-orders:result-scroll:";
|
|
187
185
|
/** The in-page half of the result presentation. Everything it does is
|
|
188
186
|
* presentational or a bounded draft: no fetch, no endpoint, no submit.
|
|
189
187
|
*
|
package/dist/result-review.js
CHANGED
|
@@ -210,8 +210,6 @@ export function parseRevisionBatch(raw) {
|
|
|
210
210
|
export function revisionSourceOf(scopeDigest) {
|
|
211
211
|
return scopeDigest === null ? "none" : scopeDigest;
|
|
212
212
|
}
|
|
213
|
-
export const REVIEW_DRAFT_PREFIX = "standing-orders:review-draft:";
|
|
214
|
-
export const RESULT_SCROLL_PREFIX = "standing-orders:result-scroll:";
|
|
215
213
|
/** The in-page half of the result presentation. Everything it does is
|
|
216
214
|
* presentational or a bounded draft: no fetch, no endpoint, no submit.
|
|
217
215
|
*
|
package/dist/retention.d.ts
CHANGED
|
@@ -12,7 +12,6 @@ export declare const RETENTION_KINDS: readonly {
|
|
|
12
12
|
}[];
|
|
13
13
|
export declare const PERIOD_CHOICES: Readonly<Record<RetentionKind, readonly (number | null)[]>>;
|
|
14
14
|
export declare const MIN_DAYS: 1;
|
|
15
|
-
export declare const MAX_DAYS: 3650;
|
|
16
15
|
/** A sweep is due once a day. */
|
|
17
16
|
export declare const SWEEP_EVERY_MS = 86400000;
|
|
18
17
|
/** At most this many runs, messages, notifications or records go per kind per sweep; the next sweep takes the rest. */
|
package/dist/retention.js
CHANGED
|
@@ -69,7 +69,6 @@ export const PERIOD_CHOICES = {
|
|
|
69
69
|
evidence: [1, 7, 14, 28, null], checkouts: LONG_CHOICES, chat: LONG_CHOICES, notifications: LONG_CHOICES,
|
|
70
70
|
};
|
|
71
71
|
export const MIN_DAYS = RETENTION_DAYS.min;
|
|
72
|
-
export const MAX_DAYS = RETENTION_DAYS.max;
|
|
73
72
|
const DAY_MS = 86_400_000;
|
|
74
73
|
/** A sweep is due once a day. */
|
|
75
74
|
export const SWEEP_EVERY_MS = DAY_MS;
|
package/dist/review-context.d.ts
CHANGED
|
@@ -32,7 +32,6 @@ export declare const REVIEW_CONTEXT_LIMITS: {
|
|
|
32
32
|
/** Only this compact projection enters prompts. Full bytes remain sealed in
|
|
33
33
|
* the artifact and are materialized under generated, flat scratch names. */
|
|
34
34
|
export declare function reviewContextFileName(id: string): string;
|
|
35
|
-
export declare function reviewContextManifest(inventory: ReviewContextInventory): string;
|
|
36
35
|
export type ContextGapReason = "source-missing" | "source-unverified" | "source-proof-missing" | "source-scope-changed" | "criterion-changed" | "stale-ancestry" | "missing-at-head" | "not-a-file" | "binary" | "over-limit" | "aggregate-limit" | "item-cap" | "secret-redacted" | "capture-failed";
|
|
37
36
|
export type ContextItemWhy = "source-criterion-evidence" | "source-changed-path" | "candidate-changed-path";
|
|
38
37
|
export type ReviewContextItem = {
|
package/dist/review-context.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Assignment direct assessments reuse custody checks; gaps remain explicit.
|
|
2
|
-
import { readObservationEvidence, OBSERVATION_CAPTURE
|
|
2
|
+
import { readObservationEvidence, OBSERVATION_CAPTURE } from "./observations.js";
|
|
3
3
|
import { isVerificationReceipt } from "./verification-evidence.js";
|
|
4
4
|
/**
|
|
5
5
|
* Inherited review context (v51, contract handoff task 3): the bounded,
|
|
@@ -75,17 +75,6 @@ export function reviewContextFileName(id) {
|
|
|
75
75
|
throw new Error("invalid context item id");
|
|
76
76
|
return `REVIEW-CONTEXT-${id}.txt`;
|
|
77
77
|
}
|
|
78
|
-
export function reviewContextManifest(inventory) {
|
|
79
|
-
return JSON.stringify({
|
|
80
|
-
...inventory,
|
|
81
|
-
...(inventory.handoff === undefined ? {} : { handoff: { artifact: inventory.handoff.artifact, sha256: inventory.handoff.sha256, file: "REVIEW-BUILDER-NOTES.json" } }),
|
|
82
|
-
...(inventory.observations === undefined ? {} : { observations: { artifact: inventory.observations.artifact, sha256: inventory.observations.sha256, file: OBSERVATION_FILE } }),
|
|
83
|
-
format: "review-context-manifest-v1",
|
|
84
|
-
items: inventory.items.map(({ content: _content, ...item }) => ({
|
|
85
|
-
...item, file: reviewContextFileName(item.id),
|
|
86
|
-
})),
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
78
|
const CONTEXT_INPUT_KINDS = new Set(["terminal-diff", "diff-stat", "proof", "check-log", "screenshot", "review-context"]);
|
|
90
79
|
/** A shortened check log is the exact bounded log the reviewer saw.
|
|
91
80
|
* Completeness of patches, proofs and other structured inputs is still
|
package/dist/reviewer.d.ts
CHANGED
|
@@ -8,22 +8,6 @@ import { type BuildFinding } from "./contracts/review-findings.js";
|
|
|
8
8
|
* screenshots only when this run actually has one (v40) — a grandfathered
|
|
9
9
|
* run (no rubric) writes exactly what it always has, byte for byte. */
|
|
10
10
|
export declare const REVIEW_PATCH_NAME = "REVIEW-DIFF.patch";
|
|
11
|
-
export declare const REVIEW_RUBRIC_NAME = "REVIEW-RUBRIC.json";
|
|
12
|
-
export declare const REVIEW_PROOF_NAME = "REVIEW-PROOF.json";
|
|
13
|
-
/** Audit hardening (still evidence-review-v1, unreleased): the repository's
|
|
14
|
-
* approved verification command's own captured stdout/stderr — a
|
|
15
|
-
* criterion citing `check` evidence was previously judged from the proof's
|
|
16
|
-
* bare claim, never the actual output. */
|
|
17
|
-
export declare const REVIEW_CHECK_LOG_NAME = "REVIEW-CHECK-LOG.txt";
|
|
18
|
-
/** v51 (inherited review context): a REVISION's sealed source-and-ancestry
|
|
19
|
-
* inventory — bounded files at the exact sealed head, bound to the source
|
|
20
|
-
* run, plus prior review provenance as context. Materialized only when
|
|
21
|
-
* the run captured one; every other run writes exactly what it always has. */
|
|
22
|
-
export declare const REVIEW_CONTEXT_NAME = "REVIEW-CONTEXT.json";
|
|
23
|
-
/** One claimed screenshot's name in the scratch — stable per artifact id,
|
|
24
|
-
* so a reviewer's own tool output naming it is reproducible across a
|
|
25
|
-
* retried materialization within the same pass. */
|
|
26
|
-
export declare function reviewScreenshotName(artifactId: number, kind: "png" | "jpeg"): string;
|
|
27
11
|
export declare const REVIEW_LIMITS: {
|
|
28
12
|
readonly comments: 40;
|
|
29
13
|
readonly criteria: 12;
|
package/dist/reviewer.js
CHANGED
|
@@ -6,24 +6,6 @@ import { readBuildFindings } from "./contracts/review-findings.js";
|
|
|
6
6
|
* screenshots only when this run actually has one (v40) — a grandfathered
|
|
7
7
|
* run (no rubric) writes exactly what it always has, byte for byte. */
|
|
8
8
|
export const REVIEW_PATCH_NAME = "REVIEW-DIFF.patch";
|
|
9
|
-
export const REVIEW_RUBRIC_NAME = "REVIEW-RUBRIC.json";
|
|
10
|
-
export const REVIEW_PROOF_NAME = "REVIEW-PROOF.json";
|
|
11
|
-
/** Audit hardening (still evidence-review-v1, unreleased): the repository's
|
|
12
|
-
* approved verification command's own captured stdout/stderr — a
|
|
13
|
-
* criterion citing `check` evidence was previously judged from the proof's
|
|
14
|
-
* bare claim, never the actual output. */
|
|
15
|
-
export const REVIEW_CHECK_LOG_NAME = "REVIEW-CHECK-LOG.txt";
|
|
16
|
-
/** v51 (inherited review context): a REVISION's sealed source-and-ancestry
|
|
17
|
-
* inventory — bounded files at the exact sealed head, bound to the source
|
|
18
|
-
* run, plus prior review provenance as context. Materialized only when
|
|
19
|
-
* the run captured one; every other run writes exactly what it always has. */
|
|
20
|
-
export const REVIEW_CONTEXT_NAME = "REVIEW-CONTEXT.json";
|
|
21
|
-
/** One claimed screenshot's name in the scratch — stable per artifact id,
|
|
22
|
-
* so a reviewer's own tool output naming it is reproducible across a
|
|
23
|
-
* retried materialization within the same pass. */
|
|
24
|
-
export function reviewScreenshotName(artifactId, kind) {
|
|
25
|
-
return `REVIEW-SCREENSHOT-${artifactId}.${kind === "png" ? "png" : "jpg"}`;
|
|
26
|
-
}
|
|
27
9
|
export const REVIEW_LIMITS = {
|
|
28
10
|
...REVIEW_OUTPUT_LIMITS,
|
|
29
11
|
/** The mailbox read cap: 40 maximal comments plus 12 judgements fit with headroom. */
|