toolroll 0.9.75 → 0.9.77
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 +241 -0
- package/README.md +11 -4
- package/THIRD_PARTY_NOTICES.md +5 -5
- package/dist/agent-fence.js +36 -8
- 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.js +3 -0
- package/dist/batch-checks.js +3 -1
- package/dist/browser/THIRD_PARTY_NOTICES.txt +5 -5
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +31 -31
- package/dist/builder.d.ts +14 -0
- package/dist/builder.js +141 -42
- package/dist/chat-decide.js +4 -0
- package/dist/check-failure.d.ts +92 -0
- package/dist/check-failure.js +267 -0
- package/dist/check-progress.d.ts +28 -1
- package/dist/check-progress.js +105 -6
- package/dist/check-report.d.ts +52 -6
- package/dist/check-report.js +193 -8
- package/dist/checkout-cleanup.js +2 -2
- package/dist/cli.js +8 -5
- package/dist/coding-handoff.d.ts +1 -0
- package/dist/coding-handoff.js +20 -11
- package/dist/coding-workspace.js +7 -5
- package/dist/commit-extract.d.ts +6 -0
- package/dist/commit-extract.js +63 -8
- package/dist/console-chrome.d.ts +41 -0
- package/dist/console-chrome.js +166 -0
- package/dist/contracts/console-api.d.ts +1 -0
- package/dist/contracts/console-api.js +1 -0
- package/dist/contracts/release-check.d.ts +12 -0
- package/dist/contracts/release-check.js +17 -0
- package/dist/contracts/verification-receipt.d.ts +20 -2
- package/dist/contracts/verification-receipt.js +15 -3
- package/dist/contracts/verification-view.d.ts +8 -0
- package/dist/controller-supervisor.d.ts +8 -0
- package/dist/controller-supervisor.js +33 -1
- package/dist/demo.js +3 -2
- package/dist/deploy-gate.d.ts +3 -0
- package/dist/deploy-gate.js +8 -2
- package/dist/desktop-update.d.ts +1 -1
- package/dist/discover.js +6 -1
- package/dist/dispose.d.ts +3 -0
- package/dist/dispose.js +6 -3
- package/dist/doctor.d.ts +7 -0
- package/dist/doctor.js +10 -2
- package/dist/envelope.js +2 -1
- package/dist/evidence.js +4 -1
- package/dist/exec.d.ts +7 -0
- package/dist/exec.js +7 -1
- package/dist/first-run.js +3 -1
- package/dist/git-floor.d.ts +16 -0
- package/dist/git-floor.js +36 -0
- 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 +7 -1
- package/dist/guides.js +4 -3
- package/dist/operate.d.ts +13 -0
- package/dist/operate.js +779 -319
- package/dist/planner.js +5 -1
- package/dist/prepared-evidence.d.ts +2 -1
- package/dist/prepared-evidence.js +8 -3
- package/dist/project-knowledge.js +6 -2
- package/dist/project-learning.js +23 -3
- package/dist/proof.js +6 -0
- package/dist/pull-request-flow.js +4 -0
- package/dist/read-executor-worker.js +8 -4
- package/dist/read-executor.d.ts +18 -1
- package/dist/read-executor.js +8 -0
- package/dist/release-adapters.d.ts +71 -2
- package/dist/release-adapters.js +471 -12
- package/dist/release-authority.d.ts +46 -0
- package/dist/release-authority.js +68 -0
- package/dist/release-auto.d.ts +184 -0
- package/dist/release-auto.js +612 -0
- package/dist/release-card.d.ts +15 -3
- package/dist/release-card.js +25 -9
- package/dist/release-ci-hold.d.ts +41 -0
- package/dist/release-ci-hold.js +53 -0
- package/dist/release-cli.js +77 -14
- package/dist/release-coverage.js +5 -2
- package/dist/release-recheck.d.ts +1 -1
- package/dist/release-recheck.js +6 -1
- package/dist/release.d.ts +207 -6
- package/dist/release.js +495 -20
- package/dist/repository-context.js +4 -6
- package/dist/result-follow-ups.js +3 -1
- package/dist/review-context.js +8 -6
- package/dist/runner.js +2 -1
- package/dist/sandbox-runtime.d.ts +8 -0
- package/dist/sandbox-runtime.js +14 -2
- package/dist/scope.d.ts +31 -2
- package/dist/scope.js +51 -4
- package/dist/scout.js +5 -1
- package/dist/secret-mint.d.ts +1 -0
- package/dist/secret-mint.js +14 -0
- package/dist/serve.js +101 -51
- package/dist/server/chat.js +5 -5
- package/dist/server/render-tasks.d.ts +12 -0
- package/dist/server/render-tasks.js +25 -14
- package/dist/server/request-context.d.ts +6 -0
- package/dist/server/route-table.js +3 -0
- package/dist/server/runtime.d.ts +2 -0
- package/dist/server/settings.js +29 -2
- package/dist/server/task-views.js +15 -2
- package/dist/setup-guide.d.ts +2 -2
- package/dist/storage.d.ts +5 -0
- package/dist/storage.js +28 -1
- package/dist/store.d.ts +11 -1
- package/dist/store.js +44 -5
- package/dist/task-base.d.ts +4 -0
- package/dist/task-base.js +22 -2
- package/dist/teardown-git.d.ts +90 -13
- package/dist/teardown-git.js +1027 -129
- package/dist/test-infra.d.ts +26 -0
- package/dist/test-infra.js +321 -0
- package/dist/up-processes.d.ts +137 -0
- package/dist/up-processes.js +165 -0
- package/dist/up-role-child.d.ts +1 -0
- package/dist/up-role-child.js +77 -0
- package/dist/verification-evidence.d.ts +1 -0
- package/dist/verification-evidence.js +13 -1
- package/dist/worktree.d.ts +4 -0
- package/dist/worktree.js +13 -7
- package/package.json +2 -1
package/dist/planner.js
CHANGED
|
@@ -20,6 +20,7 @@ import { unlinkSync } from "node:fs";
|
|
|
20
20
|
import { homedir } from "node:os";
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
import { run } from "./exec.js";
|
|
23
|
+
import { checkoutGit } from "./teardown-git.js";
|
|
23
24
|
import { auditOf, isProviderId } from "./provider.js";
|
|
24
25
|
import { currentClaim, heartbeat } from "./claim.js";
|
|
25
26
|
import { heartbeat as runnerHeartbeat } from "./runner.js";
|
|
@@ -491,7 +492,10 @@ function draftedOutcome(root, runId, plan, repairRunId = null) {
|
|
|
491
492
|
return { ok: true, drafted: { plan, artifact, repairRunId } };
|
|
492
493
|
}
|
|
493
494
|
export async function plan(store, request) {
|
|
494
|
-
const { taskId, taskRef, runner, worktree, branch, now, agent, git = run, timeoutMs = DEFAULT_PLAN_TIMEOUT_MS, maxTurns = DEFAULT_PLAN_TURNS, } = request;
|
|
495
|
+
const { taskId, taskRef, runner, worktree, branch, now, agent, git: givenGit = run, timeoutMs = DEFAULT_PLAN_TIMEOUT_MS, maxTurns = DEFAULT_PLAN_TURNS, } = request;
|
|
496
|
+
// The plane's git on the plan's checkout (agent-written, and its repository shared with every build) runs hardened, against
|
|
497
|
+
// a frozen view of the repository's config and info (teardown-git.ts checkoutGit); an injected runner is used as given.
|
|
498
|
+
const git = givenGit === run ? checkoutGit(run, "build", { repo: store.getWorktree(worktree)?.repo ?? null }) : givenGit;
|
|
495
499
|
// An ACP agent works with full access inside the fence: it builds and repairs, and is never handed a plan (routing refuses it first).
|
|
496
500
|
if (request.provider !== undefined && isAcpProvider(request.provider)) {
|
|
497
501
|
return { ok: false, kind: "failure", reason: "provider-unsupported", message: `${ACP_AGENTS[request.provider].name} builds and repairs only — configure the planner to claude, codex or gemini` };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ParsedProof } from './proof.js';
|
|
2
2
|
export declare const PREPARED_EVIDENCE_FILE = "standing-orders.evidence.json";
|
|
3
|
-
|
|
3
|
+
/** Read flags: --no-lazy-fetch where git has it; a partial clone is refused where it doesn't (teardown-git.ts). */
|
|
4
|
+
export declare const preparedEvidenceGit: () => string[];
|
|
4
5
|
export type PreparedEvidence = {
|
|
5
6
|
proof: ParsedProof;
|
|
6
7
|
files: {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/** Committed image inventory, never claimed checks, completion, or approval. */
|
|
2
|
-
import {
|
|
2
|
+
import { noLazyFetch, viewedGitSync } from './teardown-git.js';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { lstatSync, writeFileSync } from 'node:fs';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { imageDimensions, looksLikeProtocolFile, readMailbox, SCREENSHOT_BYTE_CAP, validateScreenshotBytes } from './evidence.js';
|
|
7
7
|
import { parseProof, PROOF_LIMITS, SCREENSHOT_EVIDENCE_MIN_BYTES, SCREENSHOT_EVIDENCE_MIN_HEIGHT, SCREENSHOT_EVIDENCE_MIN_WIDTH, serializeProof } from './proof.js';
|
|
8
8
|
export const PREPARED_EVIDENCE_FILE = 'standing-orders.evidence.json';
|
|
9
|
-
|
|
9
|
+
/** Read flags: --no-lazy-fetch where git has it; a partial clone is refused where it doesn't (teardown-git.ts). */
|
|
10
|
+
export const preparedEvidenceGit = () => ['--literal-pathspecs', ...noLazyFetch(), '-c', 'protocol.allow=never', '--no-replace-objects', '--no-optional-locks'];
|
|
10
11
|
const nextAction = 'Ask Codex to prepare review screenshots and commit them with standing-orders.evidence.json, then review the new result.';
|
|
11
12
|
const fail = (detail) => { throw Error(`Review screenshots are not ready. ${nextAction} ${detail}`); };
|
|
12
13
|
const digest = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
@@ -16,7 +17,11 @@ export function readPreparedEvidence(repo, candidate, required = false) {
|
|
|
16
17
|
fail('The saved commit is invalid.');
|
|
17
18
|
const git = (args, cap) => {
|
|
18
19
|
try {
|
|
19
|
-
|
|
20
|
+
// Every git version through a frozen read view, bounded (eval-harness review 1, finding 7).
|
|
21
|
+
const ran = viewedGitSync(repo, [...preparedEvidenceGit(), ...args], { timeoutMs: 10_000, maxBuffer: cap, buffer: true });
|
|
22
|
+
if (ran.status !== 0)
|
|
23
|
+
throw new Error('git failed');
|
|
24
|
+
return ran.stdout;
|
|
20
25
|
}
|
|
21
26
|
catch {
|
|
22
27
|
return fail('The committed screenshot inventory could not be read.');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Project-owned context, independent of provider memory. Never grants authority. */
|
|
2
|
-
import {
|
|
2
|
+
import { checkoutGitSync } from './teardown-git.js';
|
|
3
3
|
import { acceptedIdentities, carryProject, learningIdentity, learningSha } from './project-learning.js';
|
|
4
4
|
import { currentActor } from './actor.js';
|
|
5
5
|
import { decisionLines, getDecision, listDecisions } from './project-memory.js';
|
|
@@ -35,7 +35,11 @@ CREATE TRIGGER IF NOT EXISTS knowledge_snapshot_no_update BEFORE UPDATE ON knowl
|
|
|
35
35
|
CREATE TRIGGER IF NOT EXISTS knowledge_snapshot_no_delete BEFORE DELETE ON knowledge_snapshot BEGIN SELECT RAISE(ABORT,'Knowledge context is immutable'); END;
|
|
36
36
|
`;
|
|
37
37
|
const EMPTY = { instructions: '', references: [] };
|
|
38
|
-
|
|
38
|
+
// No --no-lazy-fetch: through a frozen read view, which refuses a partial clone from the config git runs with.
|
|
39
|
+
// Every git version through a frozen read view (eval-harness review 1, finding 7): the repository's config is copied
|
|
40
|
+
// once and inspected (an include, a command or a partial clone refused), so an include pointing at a FIFO can't hang
|
|
41
|
+
// the plane, and every call is bounded.
|
|
42
|
+
const git = (repo, args) => checkoutGitSync(repo, args, { timeoutMs: 10_000, maxBuffer: 200_000 }).trimEnd();
|
|
39
43
|
function clean(value, bytes) {
|
|
40
44
|
if (Buffer.byteLength(value) > bytes || /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f\ufffd]/u.test(value))
|
|
41
45
|
throw Error('This text is too long or is not readable text.');
|
package/dist/project-learning.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Optional, project-local advice. Core review and approval never depend on it. */
|
|
2
2
|
import { inReadOnlyRequest } from "./read-only-request.js";
|
|
3
|
+
import { checkoutGitSync } from './teardown-git.js';
|
|
3
4
|
import { createHash } from 'node:crypto';
|
|
4
|
-
import { execFileSync } from 'node:child_process';
|
|
5
5
|
import { closeSync, constants as fsConstants, existsSync, fstatSync, lstatSync, openSync, readSync, realpathSync, statSync } from 'node:fs';
|
|
6
6
|
import { join, resolve } from 'node:path';
|
|
7
7
|
import { readVerifiedArtifact, scanForSecrets } from './evidence.js';
|
|
@@ -52,7 +52,9 @@ const git = (repo, args, seen) => {
|
|
|
52
52
|
if (known !== undefined)
|
|
53
53
|
return known;
|
|
54
54
|
// Bounded, and no hook or fsmonitor program: it also runs from the console's background pass.
|
|
55
|
-
|
|
55
|
+
// No lazy fetch either: a partial clone's fetch runs whatever transport command the shared config names.
|
|
56
|
+
// Every git version through a frozen read view, bounded (eval-harness review 1, finding 7).
|
|
57
|
+
const out = checkoutGitSync(repo, args, { timeoutMs: 10_000, maxBuffer: 2 * 1024 * 1024 }).trimEnd();
|
|
56
58
|
seen?.set(key, out);
|
|
57
59
|
return out;
|
|
58
60
|
};
|
|
@@ -542,6 +544,24 @@ export function learningContext(store, root, runId, phase, now = new Date(), val
|
|
|
542
544
|
// advice may inform its proposal; only an approval can authorize a build.
|
|
543
545
|
const unscopedPlan = phase === 'plan' && scope === null && !run.scopeDigest;
|
|
544
546
|
const eligible = [];
|
|
547
|
+
// The checkout's status, read once for every lesson (one command on one checkout): a hardened read costs a
|
|
548
|
+
// private view and several git processes, and this runs inside the transaction. A failed read fails every lesson
|
|
549
|
+
// that reaches it, as each read alone would have.
|
|
550
|
+
let status = null;
|
|
551
|
+
const statusLines = (cwd) => {
|
|
552
|
+
status ??= (() => {
|
|
553
|
+
// A build's checkout: status runs filters, so the hardened read (teardown-git.ts checkoutGitSync).
|
|
554
|
+
try {
|
|
555
|
+
return { lines: checkoutGitSync(cwd, ['status', '--porcelain', '--untracked-files=all'], { timeoutMs: 10_000, maxBuffer: 2 * 1024 * 1024 }).trimEnd().split('\n') };
|
|
556
|
+
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
return { error };
|
|
559
|
+
}
|
|
560
|
+
})();
|
|
561
|
+
if ('error' in status)
|
|
562
|
+
throw status.error;
|
|
563
|
+
return status.lines;
|
|
564
|
+
};
|
|
545
565
|
if (policy && (paths.length > 0 || unscopedPlan)) {
|
|
546
566
|
for (const row of store.handle.prepare("SELECT * FROM project_lesson WHERE repo=? AND identity IN (SELECT value FROM json_each(?)) AND status='adopted' ORDER BY id DESC LIMIT 100").all(repo, carried)) {
|
|
547
567
|
try {
|
|
@@ -552,7 +572,7 @@ export function learningContext(store, root, runId, phase, now = new Date(), val
|
|
|
552
572
|
if (l.payload.environment !== environmentFingerprint(store, repo) || learningFingerprint(repo, head, l.payload.paths, seen) !== l.payload.fingerprint)
|
|
553
573
|
continue;
|
|
554
574
|
const cwd = run.worktree ?? repo;
|
|
555
|
-
const dirty =
|
|
575
|
+
const dirty = statusLines(cwd).some(line => { const p = line.slice(3); return configPath(p) || l.payload.paths.includes(p); });
|
|
556
576
|
if (!dirty)
|
|
557
577
|
eligible.push(l);
|
|
558
578
|
}
|
package/dist/proof.js
CHANGED
|
@@ -622,6 +622,12 @@ function verificationFailureWords(failure) {
|
|
|
622
622
|
return "automatic recovery stopped because this worker no longer owned the build";
|
|
623
623
|
case "coverage-incomplete":
|
|
624
624
|
return "the release check didn't cover everything it owed, or a required test went missing or was skipped";
|
|
625
|
+
case "no-result":
|
|
626
|
+
return "the release check gave no complete result (no valid report, or a part, file or journey with no outcome), so it can't count as a pass";
|
|
627
|
+
case "baseline-unverifiable":
|
|
628
|
+
return "the last release Toolroll recorded couldn't be verified, so what the release check owed is unknown and it wasn't run";
|
|
629
|
+
case "stopped-by-toolroll":
|
|
630
|
+
return "Toolroll's own shutdown stopped the approved verification command before it finished";
|
|
625
631
|
case "timed-out":
|
|
626
632
|
return "the approved verification command timed out before checks finished";
|
|
627
633
|
case "spawn-failed":
|
|
@@ -25,6 +25,7 @@ import { authenticateApprover, personProof } from "./scope.js";
|
|
|
25
25
|
import { BRANCH_PREFIX, headWithin } from "./names.js";
|
|
26
26
|
import { NIGHTLY_CHECK_ONLY, RELEASE_MERGES_ONLY, nightlyTaskRef, releaseTaskRef } from "./store.js";
|
|
27
27
|
import { belongsToRelease } from "./release-authority.js";
|
|
28
|
+
import { autoReleaseSealed } from "./release-auto.js";
|
|
28
29
|
import { fullCheckGate, requestFollowUpChecks } from "./result-follow-ups.js";
|
|
29
30
|
export const MERGE_METHODS = ["squash", "merge", "rebase"];
|
|
30
31
|
/** CI revisions one task may file across its revisions before a person is asked instead. */
|
|
@@ -402,6 +403,9 @@ export async function mergePullRequest(store, input) {
|
|
|
402
403
|
// the merge at its lease's epoch and checks the exact pull request: never here, by hand or automatically.
|
|
403
404
|
if (mergedTask !== null && belongsToRelease(store, mergedTask))
|
|
404
405
|
return refuse("release", "This result belongs to a release; it merges only through `toolroll release`.");
|
|
406
|
+
// A release task the plane approved (or is checking) automatically (auto-approve-green.md) never merges here either.
|
|
407
|
+
if (mergedTask !== null && autoReleaseSealed(store, mergedTask))
|
|
408
|
+
return refuse("release", "This result was approved automatically for a release; it merges only through `toolroll release`.");
|
|
405
409
|
const publication = store.publicationForRun(input.runId);
|
|
406
410
|
const follow = publication === null ? null : followOf(store, publication.id);
|
|
407
411
|
if (publication === null || follow === null || publication.prNumber === null)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parentPort, workerData } from 'node:worker_threads';
|
|
2
2
|
import { openStoreReadOnly } from './store.js';
|
|
3
|
-
import { workIndexPage } from './work-index.js';
|
|
3
|
+
import { workCountsByProject, workIndexPage } from './work-index.js';
|
|
4
|
+
import { projectSummaryOf } from './console-chrome.js';
|
|
4
5
|
/** One read-only, query-only connection per worker (read-executor.ts). Jobs arrive one at a time. */
|
|
5
6
|
let store = null;
|
|
6
7
|
function connection() {
|
|
@@ -15,9 +16,12 @@ function connection() {
|
|
|
15
16
|
parentPort.on('message', ({ id, job }) => {
|
|
16
17
|
let reply;
|
|
17
18
|
try {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const now = new Date(job.now);
|
|
20
|
+
const value = job.kind === 'work-index-page' ? workIndexPage(connection(), now, job.access, job.options)
|
|
21
|
+
: job.kind === 'work-counts' ? workCountsByProject(connection(), now, job.access)
|
|
22
|
+
: job.kind === 'project-summary' ? projectSummaryOf(connection(), now, job.access, job.actor, job.projects, job.evidenceRoot ?? undefined)
|
|
23
|
+
: (() => { throw new Error('Unknown read.'); })();
|
|
24
|
+
reply = { id, ok: true, value };
|
|
21
25
|
}
|
|
22
26
|
catch (error) {
|
|
23
27
|
reply = { id, ok: false, error: { name: error instanceof Error ? error.name : 'Error', message: error instanceof Error ? error.message : String(error) } };
|
package/dist/read-executor.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Store } from './store.js';
|
|
2
|
-
import { type WorkIndexOptions, type WorkIndexPage } from './work-index.js';
|
|
2
|
+
import { type WorkIndexOptions, type WorkIndexPage, type WorkProjectCounts } from './work-index.js';
|
|
3
|
+
import type { BrowserProjectState } from './browser-workspace.js';
|
|
4
|
+
import type { SidebarProject } from './console-chrome.js';
|
|
3
5
|
import type { WorkSummaryAccess } from './work-summary.js';
|
|
4
6
|
/**
|
|
5
7
|
* Heavy, read-only database work for the server, run on worker threads (docs/server-performance.md).
|
|
@@ -14,6 +16,17 @@ export type ReadJob = {
|
|
|
14
16
|
now: string;
|
|
15
17
|
access: WorkSummaryAccess;
|
|
16
18
|
options: WorkIndexOptions;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'work-counts';
|
|
21
|
+
now: string;
|
|
22
|
+
access: WorkSummaryAccess;
|
|
23
|
+
} | {
|
|
24
|
+
kind: 'project-summary';
|
|
25
|
+
now: string;
|
|
26
|
+
access: WorkSummaryAccess;
|
|
27
|
+
actor: string;
|
|
28
|
+
projects: SidebarProject[];
|
|
29
|
+
evidenceRoot: string | null;
|
|
17
30
|
};
|
|
18
31
|
export type ReadReply = {
|
|
19
32
|
id: number;
|
|
@@ -38,6 +51,10 @@ export declare class ReadExecutor {
|
|
|
38
51
|
get size(): number;
|
|
39
52
|
/** workIndexPage(store, now, access, options) on a worker's read-only connection: same rows, same errors. */
|
|
40
53
|
workIndexPage(now: Date, access: WorkSummaryAccess, options: WorkIndexOptions): Promise<WorkIndexPage>;
|
|
54
|
+
/** workCountsByProject(store, now, access) on a worker: the console's Tasks count and live beat. */
|
|
55
|
+
workCounts(now: Date, access: WorkSummaryAccess): Promise<WorkProjectCounts[]>;
|
|
56
|
+
/** projectSummaryOf (console-chrome.ts) on a worker: the sidebar's project states for one already-admitted reader. */
|
|
57
|
+
projectSummary(now: Date, access: WorkSummaryAccess, actor: string, projects: readonly SidebarProject[], evidenceRoot: string | undefined): Promise<BrowserProjectState[]>;
|
|
41
58
|
/** Fails every queued and running read once, then stops the workers. */
|
|
42
59
|
close(): Promise<void>;
|
|
43
60
|
}
|
package/dist/read-executor.js
CHANGED
|
@@ -29,6 +29,14 @@ export class ReadExecutor {
|
|
|
29
29
|
throw error;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
/** workCountsByProject(store, now, access) on a worker: the console's Tasks count and live beat. */
|
|
33
|
+
async workCounts(now, access) {
|
|
34
|
+
return await this.#run({ kind: 'work-counts', now: now.toISOString(), access });
|
|
35
|
+
}
|
|
36
|
+
/** projectSummaryOf (console-chrome.ts) on a worker: the sidebar's project states for one already-admitted reader. */
|
|
37
|
+
async projectSummary(now, access, actor, projects, evidenceRoot) {
|
|
38
|
+
return await this.#run({ kind: 'project-summary', now: now.toISOString(), access, actor, projects: projects.map(one => ({ path: one.path, name: one.name })), evidenceRoot: evidenceRoot ?? null });
|
|
39
|
+
}
|
|
32
40
|
#run(job) {
|
|
33
41
|
if (this.#closed)
|
|
34
42
|
return Promise.reject(new ReadExecutorError('The server is closing.'));
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Homebrew tap through `gh`. Each call is bounded; none asks for or stores a credential — `gh`, npm and Toolroll use
|
|
5
5
|
* the sign-ins this machine already has.
|
|
6
6
|
*/
|
|
7
|
-
import type { GateState, Installed, ReleaseAdapters } from "./release.js";
|
|
7
|
+
import type { CheckRun, GateState, Installed, MainProtection, ReleaseAdapters } from "./release.js";
|
|
8
8
|
import { Store } from "./store.js";
|
|
9
|
+
import { type CiObservation } from "./release-auto.js";
|
|
9
10
|
import { type BuildRunner } from "./release-carry.js";
|
|
10
11
|
import { type ReleaseRepo, type ReleaseTools } from "./release-git.js";
|
|
11
12
|
export { RELEASE_PATH, RELEASE_REMOTE, releaseConfigProblem, releaseGitArgs, releaseGitEnv, releaseRemote, releaseTools, type ReleaseRepo, type ReleaseTools } from "./release-git.js";
|
|
@@ -81,4 +82,72 @@ export declare function bumpFormula(text: string, url: string, sha256: string):
|
|
|
81
82
|
* The release's authority record through a store (release-once.md): what `toolroll release` reads and writes for its
|
|
82
83
|
* lease, its carries and its operations. `open` hands a store for each call and `done` lets it go.
|
|
83
84
|
*/
|
|
84
|
-
export declare function storeReleaseAdapter(open: () => Store, done: (store: Store) => void, now: () => Date, runner?: BuildRunner
|
|
85
|
+
export declare function storeReleaseAdapter(open: () => Store, done: (store: Store) => void, now: () => Date, runner?: BuildRunner,
|
|
86
|
+
/** Releases that approve themselves (auto-approve-green.md): the plane's own look at CI on a commit, and where the
|
|
87
|
+
* check receipts are kept. Absent: no automatic approval, every release asks a person. */
|
|
88
|
+
auto?: AutoObservers): NonNullable<ReleaseAdapters["release"]>;
|
|
89
|
+
/** What the plane itself reads for an automatic release approval. */
|
|
90
|
+
export type AutoObservers = {
|
|
91
|
+
/** The plane's executor policy, never a report field or project setting. Unknown refuses. Production currently
|
|
92
|
+
* cannot establish this; synthetic observers may supply protected execution to exercise the remaining gates. */
|
|
93
|
+
reportsProtected?: boolean;
|
|
94
|
+
observe: (sha: string) => Promise<CiObservation>;
|
|
95
|
+
/** GitHub's merge base of `sha` with the branch `base` of the recorded repository. */
|
|
96
|
+
mergeBase: (base: string, sha: string) => Promise<string>;
|
|
97
|
+
/** Whether main's protection holds for the account that merges (readMainProtection); null when unreadable. */
|
|
98
|
+
upToDateRequired: () => Promise<boolean | null>;
|
|
99
|
+
evidenceRoot: string;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Every check run GitHub holds for one commit — every attempt, not only the latest (`filter=all`; GitHub's default
|
|
103
|
+
* lists only the latest of each), every page — with its id, app, check suite and the commit it is filed under, oldest
|
|
104
|
+
* first. A row that doesn't parse stops the read: a partial answer is never taken for the whole.
|
|
105
|
+
*/
|
|
106
|
+
export declare function readCheckRuns(run: Exec, gh: string, repo: string, sha: string): Promise<CheckRun[]>;
|
|
107
|
+
/** One job's step outcomes as GitHub's Actions API reports them. */
|
|
108
|
+
export type JobRecord = {
|
|
109
|
+
id: number;
|
|
110
|
+
status: string;
|
|
111
|
+
conclusion: string | null;
|
|
112
|
+
steps: {
|
|
113
|
+
number: number;
|
|
114
|
+
name: string;
|
|
115
|
+
status: string;
|
|
116
|
+
conclusion: string | null;
|
|
117
|
+
}[];
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Whether a finished, unsuccessful job ended before any of the workflow's own steps ran: every step after GitHub's own
|
|
121
|
+
* first one ("Set up job", always number 1) never ran (no conclusion, or skipped) — leaving out only GitHub's own
|
|
122
|
+
* closing "Complete job" when it is the last step. Step numbers and outcomes are the runner's records; nothing the
|
|
123
|
+
* candidate prints is read. A job with no steps never reached one. Anything else (a step that ran, even the checkout)
|
|
124
|
+
* is not confirmed infrastructure.
|
|
125
|
+
*/
|
|
126
|
+
export declare function failedBeforeTests(job: JobRecord): boolean;
|
|
127
|
+
/** GitHub's job record for one check run (its job), read strictly: null when it can't be read whole. */
|
|
128
|
+
export declare function readJobBeforeTests(run: Exec, gh: string, repo: string, id: number): Promise<boolean | null>;
|
|
129
|
+
/** GitHub lists check runs from at most the latest 1000 check suites of a commit: the history is whole only below that. */
|
|
130
|
+
export declare const CHECK_SUITE_CEILING = 1000;
|
|
131
|
+
export declare function readSuitesComplete(run: Exec, gh: string, repo: string, sha: string): Promise<boolean>;
|
|
132
|
+
/** GitHub's merge base of `sha` with branch `base` (the compare API), read by the plane. */
|
|
133
|
+
export declare function readMergeBase(run: Exec, gh: string, repo: string, base: string, sha: string): Promise<string>;
|
|
134
|
+
/**
|
|
135
|
+
* Main's head as GitHub has it now (eval-harness review 1, finding 4): read by the plane's own gh from GitHub's API,
|
|
136
|
+
* never from a ref in a checkout a build can write.
|
|
137
|
+
*/
|
|
138
|
+
export declare function readMainHead(run: Exec, gh: string, repo: string, branch?: string): Promise<string>;
|
|
139
|
+
/** Whether `sha` already holds `ancestor` (GitHub's compare: `sha` is not behind it), as GitHub answers now. */
|
|
140
|
+
export declare function readContains(run: Exec, gh: string, repo: string, sha: string, ancestor: string): Promise<boolean>;
|
|
141
|
+
/**
|
|
142
|
+
* Main's protection, judged for the principal that merges (eval-harness review 2, finding 1; review 1, finding 4): the
|
|
143
|
+
* plane's own gh, which merges as the same account. A layer holds only when it requires branches to be up to date, names
|
|
144
|
+
* a non-empty set of required checks covering PROTECTION_MINIMUM_CHECKS, bound to GitHub Actions (app 15368), and
|
|
145
|
+
* can't be bypassed by that account:
|
|
146
|
+
* classic protection with "Do not allow bypassing the above settings" (`enforce_admins`); an active ruleset whose
|
|
147
|
+
* bypass list reads whole, names no role, team or organization admin, and says this account never bypasses it. Read
|
|
148
|
+
* only — Toolroll never changes a repository's settings. A layer that can't be read never holds; null only when nothing
|
|
149
|
+
* could be read at all.
|
|
150
|
+
*/
|
|
151
|
+
export declare function readMainProtection(run: Exec, gh: string, repo: string, branch?: string): Promise<MainProtection | null>;
|
|
152
|
+
/** Whether main's protection holds, as a yes/no (release-auto.ts sealAutoGreen): null when it can't be read. */
|
|
153
|
+
export declare function readUpToDateRequired(run: Exec, gh: string, repo: string, branch?: string): Promise<boolean | null>;
|