balladeer 1.0.18 → 1.0.20
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/README.md +18 -0
- package/dist/anchoring.js +2 -2
- package/dist/anchors-schema.d.ts +3 -1
- package/dist/anchors-schema.js +5 -3
- package/dist/checkout-form.d.ts +11 -0
- package/dist/checkout-form.js +14 -0
- package/dist/cli.js +4 -3
- package/dist/commands/guidance.js +23 -8
- package/dist/commands/judge.d.ts +50 -1
- package/dist/commands/judge.js +768 -110
- package/dist/commands/mcp.js +5 -9
- package/dist/commands/setup.js +5 -23
- package/dist/currency.d.ts +2 -0
- package/dist/currency.js +2 -1
- package/dist/guidance-hook.mjs +162 -56
- package/dist/guidance-runtime.d.ts +26 -0
- package/dist/guidance-runtime.js +71 -1
- package/dist/headless-agent.d.ts +2 -0
- package/dist/headless-agent.js +5 -1
- package/dist/judge-brief.d.ts +11 -5
- package/dist/judge-brief.js +26 -10
- package/dist/judge-evidence.d.ts +87 -0
- package/dist/judge-evidence.js +393 -0
- package/dist/judge-hook.d.ts +14 -1
- package/dist/judge-hook.js +34 -5
- package/dist/judge-review.d.ts +10 -0
- package/dist/judge-review.js +36 -0
- package/dist/judge-said.d.ts +1 -0
- package/dist/judge-said.js +3 -0
- package/dist/promise-meaning.d.ts +4 -0
- package/dist/promise-review-client.d.ts +84 -0
- package/dist/promise-review-client.js +357 -0
- package/dist/push-operation.d.ts +61 -0
- package/dist/push-operation.js +472 -0
- package/dist/read-only-review.d.ts +2 -0
- package/dist/read-only-review.js +2 -0
- package/dist/review-backstop.d.ts +20 -0
- package/dist/review-backstop.js +54 -0
- package/dist/review-reporting.d.ts +32 -0
- package/dist/review-reporting.js +319 -0
- package/dist/review-selection.d.ts +33 -0
- package/dist/review-selection.js +64 -0
- package/dist/review-settings.d.ts +6 -0
- package/dist/review-settings.js +9 -0
- package/dist/self-update.d.ts +3 -3
- package/dist/self-update.js +38 -8
- package/dist/user-scope.d.ts +1 -10
- package/dist/user-scope.js +12 -7
- package/dist/wire.d.ts +3 -3
- package/dist/wire.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -288,3 +288,21 @@ is replaced only after a new pairing starts; all stored agent connections and ot
|
|
|
288
288
|
remain. `--create-workspace "Team name"` likewise opens fresh pairing instead of silently reusing
|
|
289
289
|
the previous workspace. It pre-fills the name; a human still creates the workspace. An interrupted
|
|
290
290
|
command can resume the same pending choice by running it again with the same flags.
|
|
291
|
+
|
|
292
|
+
## Push review results
|
|
293
|
+
|
|
294
|
+
The push reviewer sends each completed review to Balladeer automatically. The app groups issues by
|
|
295
|
+
commitment and labels specific change results Passed, Broken, or Couldn’t verify. This is separate
|
|
296
|
+
from qualified CI evidence and never starts or changes protection. The reviewer currently selects
|
|
297
|
+
agreed promises; recorded intent can still guide agent work under the workspace’s retrieval
|
|
298
|
+
settings.
|
|
299
|
+
|
|
300
|
+
Only bounded metadata leaves the repository: promise and meaning identities, head and base commit
|
|
301
|
+
SHAs, the reviewed branch when known, verdict, a closed reason code, an optional session and
|
|
302
|
+
reproduction digest, and locally verified ancestry. Notes, reproduction commands, exact setup
|
|
303
|
+
instructions, tests, source and output stay in `.continuity/judge-log/` on the machine.
|
|
304
|
+
|
|
305
|
+
Unacknowledged results remain in a local queue for retry on a later judge run. A pass can resolve a
|
|
306
|
+
reported break only for the same meaning and branch, on a locally established descendant. A pass on
|
|
307
|
+
a different branch or a changed meaning cannot clear it. The app’s coverage remains Unknown without
|
|
308
|
+
a complete PR inventory.
|
package/dist/anchoring.js
CHANGED
|
@@ -348,7 +348,7 @@ export function seedFromMap(map, at) {
|
|
|
348
348
|
.slice(0, ANCHOR_LIMITS.linkedTests)
|
|
349
349
|
.map((test) => ({ file: test.file })),
|
|
350
350
|
};
|
|
351
|
-
return checkPromiseAnchors(anchors).ok ? anchors : undefined;
|
|
351
|
+
return checkPromiseAnchors(anchors, { allowRecordedId: true }).ok ? anchors : undefined;
|
|
352
352
|
}
|
|
353
353
|
/**
|
|
354
354
|
* Which anchors have gone at a commit, read off the graph with no model: an
|
|
@@ -468,7 +468,7 @@ export async function anchorWithCall(input) {
|
|
|
468
468
|
...picks.symbols.map((symbol) => symbol.file),
|
|
469
469
|
])),
|
|
470
470
|
};
|
|
471
|
-
const checked = checkPromiseAnchors(anchors);
|
|
471
|
+
const checked = checkPromiseAnchors(anchors, { allowRecordedId: true });
|
|
472
472
|
if (!checked.ok)
|
|
473
473
|
return { status: "failed", reason: "its anchors failed the data boundary", tokens };
|
|
474
474
|
return {
|
package/dist/anchors-schema.d.ts
CHANGED
|
@@ -80,6 +80,8 @@ export declare function isPromiseAnchorId(value: unknown): value is string;
|
|
|
80
80
|
* boundary. Problems name the field and the rule, never the value, so a
|
|
81
81
|
* refusal never repeats what it refused.
|
|
82
82
|
*/
|
|
83
|
-
export declare function checkPromiseAnchors(value: unknown
|
|
83
|
+
export declare function checkPromiseAnchors(value: unknown, options?: Readonly<{
|
|
84
|
+
allowRecordedId?: boolean;
|
|
85
|
+
}>): AnchorsCheck;
|
|
84
86
|
/** The files a promise is selected by: its entry points' and its symbols' files. */
|
|
85
87
|
export declare function anchorFiles(anchors: PromiseAnchors): string[];
|
package/dist/anchors-schema.js
CHANGED
|
@@ -28,6 +28,7 @@ export const ANCHOR_LIMITS = {
|
|
|
28
28
|
// holds the two copies to the same text and the same answers.
|
|
29
29
|
/** `prom_` ids as Balladeer issues them. */
|
|
30
30
|
const PROMISE_ID_RULE = /^prom_[a-z0-9]{8,64}$/;
|
|
31
|
+
const RECORDED_ID_RULE = /^cand_[a-z0-9]{8,64}$/;
|
|
31
32
|
const DIGEST_RULE = /^sha256:[0-9a-f]{64}$/;
|
|
32
33
|
const SHA_RULE = /^[0-9a-f]{40}$/;
|
|
33
34
|
const INSTANT_RULE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
@@ -135,7 +136,7 @@ function hasExactKeys(value, keys) {
|
|
|
135
136
|
* boundary. Problems name the field and the rule, never the value, so a
|
|
136
137
|
* refusal never repeats what it refused.
|
|
137
138
|
*/
|
|
138
|
-
export function checkPromiseAnchors(value) {
|
|
139
|
+
export function checkPromiseAnchors(value, options = {}) {
|
|
139
140
|
const problems = [];
|
|
140
141
|
const fail = (at, rule) => problems.push(`${at} ${rule}`);
|
|
141
142
|
if (!isRecord(value))
|
|
@@ -153,8 +154,9 @@ export function checkPromiseAnchors(value) {
|
|
|
153
154
|
fail("the document", "has fields the contract does not define, or lacks one");
|
|
154
155
|
if (value.schemaVersion !== PROMISE_ANCHORS_SCHEMA_VERSION)
|
|
155
156
|
fail("schemaVersion", `is not ${PROMISE_ANCHORS_SCHEMA_VERSION}`);
|
|
156
|
-
if (!isPromiseAnchorId(value.promiseId)
|
|
157
|
-
|
|
157
|
+
if (!isPromiseAnchorId(value.promiseId) &&
|
|
158
|
+
!(options.allowRecordedId && isText(value.promiseId) && RECORDED_ID_RULE.test(value.promiseId)))
|
|
159
|
+
fail("promiseId", "is not a promise or proposal id");
|
|
158
160
|
if (!isText(value.semanticDigest) || !DIGEST_RULE.test(value.semanticDigest))
|
|
159
161
|
fail("semanticDigest", "is not sha256: and 64 hex characters");
|
|
160
162
|
const anchoredAt = value.anchoredAt;
|
package/dist/checkout-form.d.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether an install may take the older form out of the checkout it runs in:
|
|
3
|
+
* only when a person ran setup or install and the user-scope form is fully in
|
|
4
|
+
* place. The background update a session hook starts leaves the repository's
|
|
5
|
+
* files alone, because an edit to a file the team commits is a diff nobody
|
|
6
|
+
* asked for, and the user-scope hook already switches the older hook off from
|
|
7
|
+
* the laptop side (`retireCheckoutRuntime`), so nothing is lost.
|
|
8
|
+
*/
|
|
9
|
+
export declare function checkoutFormRemovalAllowed(environment: NodeJS.ProcessEnv, writes: readonly {
|
|
10
|
+
status: string;
|
|
11
|
+
}[]): boolean;
|
|
1
12
|
/**
|
|
2
13
|
* Taking this product's own older, per-repository form out of a checkout once
|
|
3
14
|
* the user-scope form is in place, so one hook speaks per session.
|
package/dist/checkout-form.js
CHANGED
|
@@ -2,6 +2,20 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { copyFileSync, existsSync, lstatSync, readFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { isOurEntry, writeJsonAtomically } from "./mcp-config.js";
|
|
5
|
+
import { SELF_UPDATE_ENVIRONMENT } from "./self-update.js";
|
|
6
|
+
/**
|
|
7
|
+
* Whether an install may take the older form out of the checkout it runs in:
|
|
8
|
+
* only when a person ran setup or install and the user-scope form is fully in
|
|
9
|
+
* place. The background update a session hook starts leaves the repository's
|
|
10
|
+
* files alone, because an edit to a file the team commits is a diff nobody
|
|
11
|
+
* asked for, and the user-scope hook already switches the older hook off from
|
|
12
|
+
* the laptop side (`retireCheckoutRuntime`), so nothing is lost.
|
|
13
|
+
*/
|
|
14
|
+
export function checkoutFormRemovalAllowed(environment, writes) {
|
|
15
|
+
return (environment[SELF_UPDATE_ENVIRONMENT] !== "1" &&
|
|
16
|
+
writes.length > 0 &&
|
|
17
|
+
!writes.some((w) => w.status === "refused"));
|
|
18
|
+
}
|
|
5
19
|
const PROJECT_OWNER = "Balladeer current guidance (loader 1)";
|
|
6
20
|
const CODEX_BLOCKS = [
|
|
7
21
|
["# balladeer:guidance:start", "# balladeer:guidance:end"],
|
package/dist/cli.js
CHANGED
|
@@ -31,7 +31,7 @@ import { updateNotice } from "./currency.js";
|
|
|
31
31
|
import { describeEarlier, describeRemoval, findEarlier, removeEarlier } from "./remove-earlier.js";
|
|
32
32
|
import { recordSelfUpdateState, SELF_UPDATE_ENVIRONMENT } from "./self-update.js";
|
|
33
33
|
import { hooksAwaitingTrust } from "./hook-trust.js";
|
|
34
|
-
import { describeCheckoutFormRemoval, findCheckoutForm, removeCheckoutForm, } from "./checkout-form.js";
|
|
34
|
+
import { checkoutFormRemovalAllowed, describeCheckoutFormRemoval, findCheckoutForm, removeCheckoutForm, } from "./checkout-form.js";
|
|
35
35
|
import { StoreError, normalizeControlPlane } from "./store.js";
|
|
36
36
|
import { CLI_INVOCATION, CLI_VERSION, DEFAULT_CONTROL_PLANE } from "./wire.js";
|
|
37
37
|
const USAGE = `balladeer ${CLI_VERSION}
|
|
@@ -545,8 +545,9 @@ function installEverything(options) {
|
|
|
545
545
|
}
|
|
546
546
|
// With the user-scope form in place, the older per-repository form in this
|
|
547
547
|
// checkout only makes the session hear the guidance twice. Out it goes, with
|
|
548
|
-
// a copy beside any file git does not already keep
|
|
549
|
-
|
|
548
|
+
// a copy beside any file git does not already keep, and never from the
|
|
549
|
+
// background update (see `checkoutFormRemovalAllowed`).
|
|
550
|
+
if (checkoutFormRemovalAllowed(process.env, writes ?? [])) {
|
|
550
551
|
const checkoutForm = findCheckoutForm({
|
|
551
552
|
cwd: process.cwd(),
|
|
552
553
|
controlPlane: options.controlPlane,
|
|
@@ -7,6 +7,7 @@ import { repositoryHints } from "../repository.js";
|
|
|
7
7
|
import { newerVersionPublished } from "../currency.js";
|
|
8
8
|
import { startSelfUpdateIfDue } from "../self-update.js";
|
|
9
9
|
import { noteHookFired } from "../hook-trust.js";
|
|
10
|
+
import { retireCheckoutRuntime } from "../guidance-runtime.js";
|
|
10
11
|
/** A repository-specific setup step for explicit status and empty-catalog responses. */
|
|
11
12
|
export function untrackedLine(remote) {
|
|
12
13
|
const here = remote === "unknown/unknown" ? "This folder" : `This folder (${remote})`;
|
|
@@ -158,6 +159,18 @@ export async function runGuidance(options) {
|
|
|
158
159
|
// trust are the ones this matters to.
|
|
159
160
|
if (options.hook === "codex")
|
|
160
161
|
noteHookFired(options.environment, "codex");
|
|
162
|
+
// This hook covers this checkout now, so the older per-repository hook
|
|
163
|
+
// here (releases before 1.0.8) only makes the session hear the guidance
|
|
164
|
+
// twice. Switch it off from the laptop side, once, before the fetch:
|
|
165
|
+
// nothing printed, no repository file touched, nobody asked to run a thing.
|
|
166
|
+
if (!projectScoped && event === "SessionStart")
|
|
167
|
+
retireCheckoutRuntime({
|
|
168
|
+
cwd: options.cwd,
|
|
169
|
+
host: options.hook,
|
|
170
|
+
repositoryId: agents[0].repositoryId,
|
|
171
|
+
controlPlane: agents[0].controlPlane,
|
|
172
|
+
environment: options.environment,
|
|
173
|
+
});
|
|
161
174
|
const loaded = await loadGuidance({
|
|
162
175
|
agent: agents[0],
|
|
163
176
|
environment: options.environment,
|
|
@@ -165,14 +178,16 @@ export async function runGuidance(options) {
|
|
|
165
178
|
hook: options.hook,
|
|
166
179
|
...(options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}),
|
|
167
180
|
});
|
|
168
|
-
// Behind? Start the install in the background
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
})
|
|
181
|
+
// Behind? Start the install in the background and carry on serving this
|
|
182
|
+
// session from the copy that is here. Any hook that loaded guidance may
|
|
183
|
+
// start it, not only a session start: sessions run for days, and a laptop
|
|
184
|
+
// whose sessions never restart would otherwise never update (Robert's own,
|
|
185
|
+
// 1 October 2026). The stamp keeps it to once a day per release.
|
|
186
|
+
startSelfUpdateIfDue(newerVersionPublished(), {
|
|
187
|
+
environment: options.environment,
|
|
188
|
+
...(options.startUpdate ? { run: options.startUpdate } : {}),
|
|
189
|
+
...(options.now ? { now: options.now } : {}),
|
|
190
|
+
});
|
|
176
191
|
const sessionId = identifier(record.session_id);
|
|
177
192
|
const agentId = identifier(record.agent_id);
|
|
178
193
|
recordGuidanceContext({
|
package/dist/commands/judge.d.ts
CHANGED
|
@@ -2,7 +2,13 @@ import type { AnchorBudget } from "../anchor-budget.js";
|
|
|
2
2
|
import type { AnchorsClient } from "../anchors-client.js";
|
|
3
3
|
import { type BehaviorMap } from "../behavior-map-schema.js";
|
|
4
4
|
import { type HeadlessAgentOptions, type HeadlessAgentResult, type HeadlessClient, type LoginCheck } from "../headless-agent.js";
|
|
5
|
+
import { type CodeReview } from "../judge-review.js";
|
|
6
|
+
import type { ReviewSettings } from "../review-settings.js";
|
|
7
|
+
import { type CaseEvidence, type EvidenceReceipt, type FrozenEvidenceFile } from "../judge-evidence.js";
|
|
8
|
+
import { type ReviewsClient } from "../promise-review-client.js";
|
|
9
|
+
import { reviewBackstop } from "../review-backstop.js";
|
|
5
10
|
import { type HookHost, type MergeTarget } from "../judge-hook.js";
|
|
11
|
+
import { type ReviewPayload } from "../review-reporting.js";
|
|
6
12
|
import { type PromiseDocument, type PromiseReader } from "../promise-meaning.js";
|
|
7
13
|
import type { RepositoryGraph } from "../risk/graph-cache.js";
|
|
8
14
|
import { type CarriedFile } from "../scratch-worktree.js";
|
|
@@ -98,6 +104,8 @@ export type JudgeVerdict = {
|
|
|
98
104
|
reproductionRejected: string | null;
|
|
99
105
|
exercisedCases: string[];
|
|
100
106
|
notes: string;
|
|
107
|
+
/** The model's code assessment. Local only; never part of a server receipt. */
|
|
108
|
+
review?: CodeReview;
|
|
101
109
|
/**
|
|
102
110
|
* On a could-not-tell the judge itself reached because the tests need
|
|
103
111
|
* something this machine lacks: the one step that would let them run, as
|
|
@@ -119,6 +127,17 @@ export type JudgeVerdict = {
|
|
|
119
127
|
file: string;
|
|
120
128
|
outcome: "pass" | "fail" | "error" | "none";
|
|
121
129
|
};
|
|
130
|
+
standing?: "recorded" | "agreed";
|
|
131
|
+
/** Local dependencies and runner identity; never credentials. */
|
|
132
|
+
environmentDigest?: string;
|
|
133
|
+
evidenceReceipts?: readonly Omit<EvidenceReceipt, "output">[];
|
|
134
|
+
independentReview?: Readonly<{
|
|
135
|
+
accepted: boolean;
|
|
136
|
+
reason: string;
|
|
137
|
+
durationMs?: number;
|
|
138
|
+
inputTokens?: number;
|
|
139
|
+
outputTokens?: number;
|
|
140
|
+
}>;
|
|
122
141
|
};
|
|
123
142
|
/** A command's exit, as one run of a reproduction or a linked test. */
|
|
124
143
|
export type CommandRun = Readonly<{
|
|
@@ -198,6 +217,8 @@ export declare function keepReproduction(input: Readonly<{
|
|
|
198
217
|
cwd: string;
|
|
199
218
|
digest: string;
|
|
200
219
|
files: readonly CarriedFile[];
|
|
220
|
+
/** Exact bytes that the runner executed; the exploratory copy may change later. */
|
|
221
|
+
frozenFiles?: readonly FrozenEvidenceFile[];
|
|
201
222
|
}>): KeptReproduction | undefined;
|
|
202
223
|
/**
|
|
203
224
|
* How this repository runs one test file, when it can be told from the files
|
|
@@ -213,7 +234,10 @@ type JudgeAnswer = Readonly<{
|
|
|
213
234
|
}>;
|
|
214
235
|
exercisedCases: string[];
|
|
215
236
|
notes: string;
|
|
237
|
+
review?: CodeReview;
|
|
216
238
|
nextStep?: string;
|
|
239
|
+
evidenceFiles: readonly string[];
|
|
240
|
+
caseEvidence: readonly CaseEvidence[];
|
|
217
241
|
}>;
|
|
218
242
|
/** The longest `nextStep` kept from a judge's answer, in characters. */
|
|
219
243
|
export declare const NEXT_STEP_LIMIT = 500;
|
|
@@ -261,6 +285,14 @@ export type JudgeArguments = Readonly<{
|
|
|
261
285
|
json: boolean;
|
|
262
286
|
mode?: "report" | "block";
|
|
263
287
|
wait: boolean;
|
|
288
|
+
retry?: boolean;
|
|
289
|
+
/** Verified by a captured local push operation, not guessed from HEAD. */
|
|
290
|
+
destinationRef?: string;
|
|
291
|
+
destinationDigest?: string;
|
|
292
|
+
pushSessionId?: string;
|
|
293
|
+
operationDeadline?: number;
|
|
294
|
+
/** Original host for offers when a native push fans out into per-ref reviews. */
|
|
295
|
+
pushHost?: HookHost;
|
|
264
296
|
hook?: HookHost;
|
|
265
297
|
/** Run by the entry setup writes at the host's user scope, which stands aside where it has nothing to do. */
|
|
266
298
|
userScope?: true;
|
|
@@ -274,7 +306,7 @@ export type JudgeArguments = Readonly<{
|
|
|
274
306
|
repository?: string;
|
|
275
307
|
controlPlane: string;
|
|
276
308
|
}>;
|
|
277
|
-
export declare const JUDGE_USAGE = " npx -y balladeer@latest judge [--promises <id,id>] [--top <k>] [--base <ref>] [--head <ref>]\n [--report-only | --block] [--wait] [--json] [--client auto|claude|codex]\n [--from-file <promise.json> [--map <map.json>]]\n [--hook claude|codex|git] [--install-hook [claude|codex|git]]\n [--push-check on|off]\n Judge whether this change keeps the promises it could break, one promise\n at a time, on your own Claude Code or Codex login. A break counts only\n with a command that fails with the change and passes without it, and\n this command runs it
|
|
309
|
+
export declare const JUDGE_USAGE = " npx -y balladeer@latest judge [--promises <id,id>] [--top <k>] [--base <ref>] [--head <ref>]\n [--report-only | --block] [--wait] [--retry] [--json] [--client auto|claude|codex]\n [--from-file <promise.json> [--map <map.json>]]\n [--hook claude|codex|git] [--install-hook [claude|codex|git]]\n [--push-check on|off]\n Judge whether this change keeps the promises it could break, one promise\n at a time, on your own Claude Code or Codex login. Recorded meaning is\n advisory and never blocks. Kept requires executable passing-case evidence\n and a fresh agent assessment. A break counts only\n with a command that fails with the change and passes without it, and\n this command runs it in fresh checkouts and a second local agent checks\n that its assertion actually expresses the promise; anything unproven is \"could not\n tell\". Report mode prints one line per promise and never stops anything;\n --block exits 2 on a reproduced break, and when judges are still running\n at the time limit it holds the push rather than letting it through.\n --wait runs without the time limit. Promises come from --promises, else\n every visible recorded or agreed promise whose anchored code the change edits, and every\n promise with a behavior map whose own code or tests it edits (with\n \"select\": \"ranked\" in .continuity/judge.json, the top of the maps'\n ranking and a small random sample); plus that file's alwaysJudge. A\n remaining current promise receives a bounded local relevance reading; strong\n resource, sink and intent signals can add a promise beyond direct files. A\n promise with no anchors yet is anchored first, by one small call on\n your own login (see `balladeer anchor`), at most 20 a day on this\n laptop, and a line says what that cost. Verdicts are kept in\n .continuity/judge-log/, which git ignores. Coverage and pending work are\n logged separately; --retry retries an inconclusive result. A known pushed\n branch also sends a bounded receipt to Balladeer, with no source or logs. --install-hook adds the push\n hook to .claude/settings.json and .codex/hooks.json, before and after\n each shell command: report mode judges after a push has landed, so the\n push never waits, and block mode judges before it. With git it writes\n .git/hooks/pre-push, which judges before the push in both modes. With\n \"offers\": true in .continuity/judge.json, the push hook also reads the\n change for rules no promise records and offers at most three to\n whoever is pushing; it records nothing and never holds a push (see\n `balladeer offers`). Setup also writes the same pair of entries at\n each host's user scope, running `judge --hook <host> --user-scope`,\n so the check runs in every connected repository with visible promises\n and says nothing anywhere else; a checkout with its own entry keeps\n the check to that entry. --push-check off turns the user-scope check\n off on this laptop, and --push-check on turns it back on.\n";
|
|
278
310
|
export declare function parseJudgeArguments(argv: readonly string[]): JudgeArguments;
|
|
279
311
|
/**
|
|
280
312
|
* The subset of `balladeer-risk/v1` this command reads: enough to order the
|
|
@@ -347,7 +379,16 @@ export declare function findLoggedVerdict(root: string, key: Readonly<{
|
|
|
347
379
|
semanticDigest: string;
|
|
348
380
|
base: string;
|
|
349
381
|
head: string;
|
|
382
|
+
environmentDigest?: string;
|
|
383
|
+
retry?: boolean;
|
|
350
384
|
}>, now: Date): JudgeVerdict | undefined;
|
|
385
|
+
/**
|
|
386
|
+
* A judge's words, shortened to fit one line an agent relays. A cut ends on a
|
|
387
|
+
* whole sentence, or failing that a whole word, and says where the rest is:
|
|
388
|
+
* the agent repeats the line as it reads it, so a reason cut mid-word reached
|
|
389
|
+
* the person mid-word (found 1 October 2026).
|
|
390
|
+
*/
|
|
391
|
+
export declare function sentence(text: string, limit?: number): string;
|
|
351
392
|
/** Said after "fix the behavior": a deliberate change is the person's, and the promise its owner's. */
|
|
352
393
|
export declare const DELIBERATE_CHANGE = "If the person changed this behavior on purpose, do not undo their change: tell them it breaks this promise, whose meaning only its owner can change.";
|
|
353
394
|
export type VerdictLineContext = Readonly<{
|
|
@@ -382,6 +423,7 @@ export declare function stillKept(root: string, verdict: JudgeVerdict): JudgeVer
|
|
|
382
423
|
*/
|
|
383
424
|
export declare function verdictLine(verdict: JudgeVerdict, title: string, context?: VerdictLineContext): string;
|
|
384
425
|
export type JudgeDependencies = Readonly<{
|
|
426
|
+
reportReviews?: ((payload: ReviewPayload) => Promise<number>) | null;
|
|
385
427
|
runAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
|
|
386
428
|
resolveLogin?: (requested: HeadlessClient | "auto", prefer?: HeadlessClient) => Promise<LoginCheck | undefined>;
|
|
387
429
|
riskReport?: (root: string, base: string, head: string,
|
|
@@ -391,6 +433,11 @@ export type JudgeDependencies = Readonly<{
|
|
|
391
433
|
reader?: PromiseReader | null;
|
|
392
434
|
/** The anchors route; `null` means none, without trying to find one. */
|
|
393
435
|
anchors?: AnchorsClient | null;
|
|
436
|
+
reviews?: ReviewsClient | null;
|
|
437
|
+
/** Internal operation snapshot, reused across every pushed ref. */
|
|
438
|
+
reviewSettings?: ReviewSettings;
|
|
439
|
+
reviewSettingsResolved?: true;
|
|
440
|
+
backstop?: typeof reviewBackstop;
|
|
394
441
|
/** The anchoring call, when it is not the person's own agent. */
|
|
395
442
|
anchorAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
|
|
396
443
|
/** The repository graph at a commit, when it is not built from this laptop's cache. */
|
|
@@ -415,6 +462,8 @@ export declare const PUSH_REPOSITORY_UNKNOWN = "Balladeer judge: this push was n
|
|
|
415
462
|
/** Said instead of verdicts when every pull request the command opens or merges is in another repository. */
|
|
416
463
|
export declare function pullRequestElsewhereLine(repo: string): string;
|
|
417
464
|
export type JudgeRunOptions = Readonly<{
|
|
465
|
+
/** Collect a child ref's offers for delivery after the whole operation's verdicts. */
|
|
466
|
+
deferOffers?: (reading: Promise<readonly string[]>) => void;
|
|
418
467
|
args: JudgeArguments;
|
|
419
468
|
cwd: string;
|
|
420
469
|
environment: NodeJS.ProcessEnv;
|