balladeer 1.0.19 → 1.0.21
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/commands/judge.d.ts +50 -1
- package/dist/commands/judge.js +768 -110
- package/dist/commands/mcp.d.ts +4 -0
- package/dist/commands/mcp.js +24 -1
- package/dist/currency.js +9 -3
- package/dist/guidance-hook.mjs +13 -3
- 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 +9 -0
- package/dist/self-update.js +35 -1
- 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/dist/commands/mcp.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SelfUpdateDeps } from "../self-update.js";
|
|
1
2
|
import { type StoredAgent } from "../store.js";
|
|
2
3
|
export { forwarderHeaders, selectAgent, type AgentSelection } from "../agent.js";
|
|
3
4
|
export type McpOptions = Readonly<{
|
|
@@ -15,6 +16,9 @@ export type McpOptions = Readonly<{
|
|
|
15
16
|
stdin: NodeJS.ReadableStream;
|
|
16
17
|
write: (text: string) => void;
|
|
17
18
|
error: (text: string) => void;
|
|
19
|
+
now?: () => number;
|
|
20
|
+
/** Runs the background update; injected so tests never reach npm. */
|
|
21
|
+
startUpdate?: SelfUpdateDeps["run"];
|
|
18
22
|
}>;
|
|
19
23
|
export type ProbeResult = Readonly<{
|
|
20
24
|
kind: "answered";
|
package/dist/commands/mcp.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
2
|
import { callAgentTool, forwarderHeaders, noAgentCredentialSentence, safeJson, selectAgent, structuredString, updateLine, } from "../agent.js";
|
|
3
|
-
import { noteServerVersion, updateNotice } from "../currency.js";
|
|
3
|
+
import { newerVersionPublished, noteServerVersion, updateNotice } from "../currency.js";
|
|
4
4
|
import { repositoryHint, repositoryHints } from "../repository.js";
|
|
5
5
|
import { CLI_VERSION } from "../wire.js";
|
|
6
6
|
import { untrackedLine } from "./guidance.js";
|
|
7
7
|
import { hooksAwaitingTrust, hooksAwaitingTrustNotice, trustingHost } from "../hook-trust.js";
|
|
8
|
+
import { startSelfUpdateIfDue } from "../self-update.js";
|
|
8
9
|
import { StoreError, readCredentials } from "../store.js";
|
|
9
10
|
// The three commands that use an agent connection select and address it through
|
|
10
11
|
// one module. These are re-exported because this is where the forwarder's
|
|
@@ -12,6 +13,8 @@ import { StoreError, readCredentials } from "../store.js";
|
|
|
12
13
|
export { forwarderHeaders, selectAgent } from "../agent.js";
|
|
13
14
|
const MAX_REQUEST_BYTES = 1024 * 1024;
|
|
14
15
|
const MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
16
|
+
/** How often one long-running forwarder looks at whether an update is due. */
|
|
17
|
+
const UPDATE_CHECK_MS = 60 * 60 * 1000;
|
|
15
18
|
/**
|
|
16
19
|
* One real call over the connection just issued, before anything claims it
|
|
17
20
|
* works.
|
|
@@ -195,6 +198,8 @@ export async function runMcp(options) {
|
|
|
195
198
|
// user-scope hook had just switched off. It installs nothing now.
|
|
196
199
|
const { agent } = selection;
|
|
197
200
|
let saidUpdate = false;
|
|
201
|
+
let updateCheckedAt = Number.NEGATIVE_INFINITY;
|
|
202
|
+
const clock = options.now ?? Date.now;
|
|
198
203
|
// Which host this session belongs to is known from its hello; whether its
|
|
199
204
|
// hooks are waiting for the person's trust is asked at the first tool call,
|
|
200
205
|
// by which time a trusted session-start hook has certainly run.
|
|
@@ -230,6 +235,24 @@ export async function runMcp(options) {
|
|
|
230
235
|
saidUpdate = true;
|
|
231
236
|
options.error(`${notice}\n`);
|
|
232
237
|
}
|
|
238
|
+
// The guidance hook is not the only way in. Codex skips a hook nobody has
|
|
239
|
+
// trusted, and a Codex-only laptop (Luke's, at Didero) then never started
|
|
240
|
+
// an update at all, while this forwarder ran every session. So the
|
|
241
|
+
// forwarder starts the same background install, under the same stamp,
|
|
242
|
+
// looking at most once an hour however many frames pass.
|
|
243
|
+
if (newerVersionPublished() !== undefined && clock() - updateCheckedAt >= UPDATE_CHECK_MS) {
|
|
244
|
+
updateCheckedAt = clock();
|
|
245
|
+
try {
|
|
246
|
+
startSelfUpdateIfDue(newerVersionPublished(), {
|
|
247
|
+
environment: options.environment,
|
|
248
|
+
now: clock,
|
|
249
|
+
...(options.startUpdate ? { run: options.startUpdate } : {}),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
/* An update that cannot even be considered never costs the session its tools. */
|
|
254
|
+
}
|
|
255
|
+
}
|
|
233
256
|
if (response.status === 401) {
|
|
234
257
|
options.write(`${revokedConnectionFrame(frameId(line), options.controlPlane)}\n`);
|
|
235
258
|
options.error("Balladeer refused this agent connection. Rotate it in Balladeer.\n");
|
package/dist/currency.js
CHANGED
|
@@ -60,10 +60,16 @@ export function noteServerVersion(headers) {
|
|
|
60
60
|
newestSeen = newest;
|
|
61
61
|
const advice = headers.get(CLI_ADVICE_HEADER);
|
|
62
62
|
const line = advice === null ? "" : bounded(advice);
|
|
63
|
+
// On this copy's own major the background update installs it (it lands
|
|
64
|
+
// from 1.0.21), so there is nothing to tell anyone to run. Another major
|
|
65
|
+
// is a choice a person makes.
|
|
66
|
+
const sameMajor = newest.split(".")[0] === CLI_VERSION.split(".")[0];
|
|
63
67
|
notice =
|
|
64
|
-
line
|
|
65
|
-
?
|
|
66
|
-
:
|
|
68
|
+
line !== ""
|
|
69
|
+
? line
|
|
70
|
+
: sameMajor
|
|
71
|
+
? `A newer Balladeer is available (${newest}; this is ${CLI_VERSION}). It installs in the background; nothing needs to be run.`
|
|
72
|
+
: `A newer Balladeer is available (${newest}; this is ${CLI_VERSION}). Run npx -y balladeer@latest setup --refresh to update the command and instructions, then restart this session.`;
|
|
67
73
|
}
|
|
68
74
|
/** The one line this run prints about itself, or nothing when it is current. */
|
|
69
75
|
export function updateNotice() {
|
package/dist/guidance-hook.mjs
CHANGED
|
@@ -2152,7 +2152,7 @@ function relayHook(input, directory) {
|
|
|
2152
2152
|
}
|
|
2153
2153
|
|
|
2154
2154
|
// src/wire.ts
|
|
2155
|
-
var CLI_VERSION = "1.0.
|
|
2155
|
+
var CLI_VERSION = "1.0.21";
|
|
2156
2156
|
var CLIENT_HEADER = "x-balladeer-client";
|
|
2157
2157
|
var UPDATE_STATE_HEADER = "x-balladeer-update-state";
|
|
2158
2158
|
var HOOK_HOST_HEADER = "x-balladeer-hook";
|
|
@@ -2289,7 +2289,8 @@ function noteServerVersion(headers) {
|
|
|
2289
2289
|
newestSeen = newest;
|
|
2290
2290
|
const advice = headers.get(CLI_ADVICE_HEADER);
|
|
2291
2291
|
const line = advice === null ? "" : bounded(advice);
|
|
2292
|
-
|
|
2292
|
+
const sameMajor = newest.split(".")[0] === CLI_VERSION.split(".")[0];
|
|
2293
|
+
notice = line !== "" ? line : sameMajor ? `A newer Balladeer is available (${newest}; this is ${CLI_VERSION}). It installs in the background; nothing needs to be run.` : `A newer Balladeer is available (${newest}; this is ${CLI_VERSION}). Run npx -y balladeer@latest setup --refresh to update the command and instructions, then restart this session.`;
|
|
2293
2294
|
}
|
|
2294
2295
|
function newerVersionPublished() {
|
|
2295
2296
|
return newestSeen;
|
|
@@ -2334,6 +2335,13 @@ function due(newer, last, now) {
|
|
|
2334
2335
|
const newerTarget = last.toward === void 0 || older(last.toward, newer);
|
|
2335
2336
|
return newerTarget && since >= HOUR_MS;
|
|
2336
2337
|
}
|
|
2338
|
+
function updateSpecifier(newer) {
|
|
2339
|
+
if (!VERSION2.test(newer)) return void 0;
|
|
2340
|
+
const [name, major] = HOOK_SPECIFIER.split("@");
|
|
2341
|
+
if (newer.split(".")[0] !== major) return void 0;
|
|
2342
|
+
if (!older(CLI_VERSION, newer)) return void 0;
|
|
2343
|
+
return `${name}@${newer}`;
|
|
2344
|
+
}
|
|
2337
2345
|
function writePrivate(path, value, environment) {
|
|
2338
2346
|
mkdirSync3(configHome(environment), { recursive: true, mode: 448 });
|
|
2339
2347
|
writeFileSync2(path, JSON.stringify(value) + "\n", { mode: 384 });
|
|
@@ -2375,9 +2383,11 @@ function detached(command, args, environment) {
|
|
|
2375
2383
|
}
|
|
2376
2384
|
function startSelfUpdateIfDue(newer, deps) {
|
|
2377
2385
|
if (newer === void 0) return false;
|
|
2386
|
+
const specifier = updateSpecifier(newer);
|
|
2387
|
+
if (specifier === void 0) return false;
|
|
2378
2388
|
const now = deps.now ?? Date.now;
|
|
2379
2389
|
if (!due(newer, lastAttempt(deps.environment), now())) return false;
|
|
2380
|
-
const launch = resolveLaunch(
|
|
2390
|
+
const launch = resolveLaunch(specifier, deps.environment, deps.execPath, deps.exists);
|
|
2381
2391
|
let started;
|
|
2382
2392
|
try {
|
|
2383
2393
|
started = (deps.run ?? detached)(launch.command, launch.args, launch.environment) !== false;
|
package/dist/headless-agent.d.ts
CHANGED
|
@@ -133,6 +133,7 @@ export declare function verifyLogin(client: HeadlessClient, options: Readonly<{
|
|
|
133
133
|
cwd: string;
|
|
134
134
|
spawn?: SpawnLike;
|
|
135
135
|
killGroup?: KillGroup;
|
|
136
|
+
deadline?: number;
|
|
136
137
|
}>): Promise<LoginCheck>;
|
|
137
138
|
/**
|
|
138
139
|
* The client to run, or none. `auto` tries the hook's own client first, then
|
|
@@ -144,6 +145,7 @@ export declare function resolveClient(requested: HeadlessClient | "auto", option
|
|
|
144
145
|
prefer?: HeadlessClient;
|
|
145
146
|
spawn?: SpawnLike;
|
|
146
147
|
killGroup?: KillGroup;
|
|
148
|
+
deadline?: number;
|
|
147
149
|
}>): Promise<LoginCheck | undefined>;
|
|
148
150
|
/**
|
|
149
151
|
* The last JSON object in a piece of text.
|
package/dist/headless-agent.js
CHANGED
|
@@ -99,13 +99,15 @@ export function codexArguments(input) {
|
|
|
99
99
|
* dropped, the same way the compatibility canary treats them.
|
|
100
100
|
*/
|
|
101
101
|
export async function verifyLogin(client, options) {
|
|
102
|
+
if (options.deadline !== undefined && Date.now() >= options.deadline)
|
|
103
|
+
return { client, verified: false };
|
|
102
104
|
const env = agentEnvironment(options.env);
|
|
103
105
|
const run = await runOwnedProcess({
|
|
104
106
|
command: clientBinary(client, options.env),
|
|
105
107
|
args: client === "claude" ? ["auth", "status", "--json"] : ["login", "status"],
|
|
106
108
|
cwd: options.cwd,
|
|
107
109
|
env,
|
|
108
|
-
timeoutMs: 15_000,
|
|
110
|
+
timeoutMs: Math.max(1, Math.min(15_000, (options.deadline ?? Number.POSITIVE_INFINITY) - Date.now())),
|
|
109
111
|
maxOutputBytes: 64 * 1024,
|
|
110
112
|
...(options.spawn === undefined ? {} : { spawn: options.spawn }),
|
|
111
113
|
...(options.killGroup === undefined ? {} : { killGroup: options.killGroup }),
|
|
@@ -136,6 +138,8 @@ export async function resolveClient(requested, options) {
|
|
|
136
138
|
? ["codex", "claude"]
|
|
137
139
|
: ["claude", "codex"];
|
|
138
140
|
for (const client of order) {
|
|
141
|
+
if (options.deadline !== undefined && Date.now() >= options.deadline)
|
|
142
|
+
return undefined;
|
|
139
143
|
const check = await verifyLogin(client, options);
|
|
140
144
|
if (check.verified)
|
|
141
145
|
return check;
|
package/dist/judge-brief.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ import type { PromiseAnchors } from "./anchors-schema.js";
|
|
|
2
2
|
import type { BehaviorMap } from "./behavior-map-schema.js";
|
|
3
3
|
import type { PromiseDocument } from "./promise-meaning.js";
|
|
4
4
|
/**
|
|
5
|
-
* The fixed instructions a judge works from, `judge-brief/
|
|
5
|
+
* The fixed instructions a judge works from, `judge-brief/v7`.
|
|
6
6
|
*
|
|
7
7
|
* One promise, one change, one disposable checkout at the change's head. The
|
|
8
|
-
* rules are the contract's: cases first,
|
|
8
|
+
* rules are the contract's: cases first, source-grounded review, broken only with a
|
|
9
9
|
* command that fails with the change and passes without it, kept only after
|
|
10
10
|
* each passing case was exercised, and could not tell for everything else,
|
|
11
11
|
* with what stopped the judge. The runner checks the reproduction itself by
|
|
@@ -15,7 +15,13 @@ import type { PromiseDocument } from "./promise-meaning.js";
|
|
|
15
15
|
* The text is a constant so a verdict can name the exact brief it was given.
|
|
16
16
|
* Changing a word here is a new revision.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
18
|
+
* v7 requires a substantive code review before settling a verdict: trace affected
|
|
19
|
+
* behavior, assess test blind spots, and preserve risks and uncertainty locally.
|
|
20
|
+
* Tests support the review; writing new tests is optional. v6 requires explicit
|
|
21
|
+
* commands for passing-case evidence and names every new
|
|
22
|
+
* evidence file. The runner executes frozen artifacts in fresh checkouts, then
|
|
23
|
+
* a separate local reviewer assesses their semantic fidelity. v5 used v3's
|
|
24
|
+
* rules; what it added is a section of the prompt
|
|
19
25
|
* (1 October 2026, with promise anchors): a promise with anchors and no
|
|
20
26
|
* behavior map is handed its anchors, the linked tests the graph gives at
|
|
21
27
|
* head, and the files one import step from its anchor files, where v3 said
|
|
@@ -24,7 +30,7 @@ import type { PromiseDocument } from "./promise-meaning.js";
|
|
|
24
30
|
* adopted on 30 September, so it is never reused. The judge with anchors in
|
|
25
31
|
* place of a map has not been measured yet.
|
|
26
32
|
*/
|
|
27
|
-
export declare const JUDGE_BRIEF_REVISION = "judge-brief/
|
|
33
|
+
export declare const JUDGE_BRIEF_REVISION = "judge-brief/v7";
|
|
28
34
|
/**
|
|
29
35
|
* What `judge-brief/v3` added to v1, and all it added (Robert, 30 September
|
|
30
36
|
* 2026): a way forward when the tests need something the machine lacks.
|
|
@@ -37,7 +43,7 @@ export declare const JUDGE_BRIEF_REVISION = "judge-brief/v5";
|
|
|
37
43
|
* thing in the answer is `nextStep`, the one step that would let the tests run.
|
|
38
44
|
*/
|
|
39
45
|
export declare const JUDGE_BRIEF_SETUP_RULE = "9. Only when you would otherwise answer \"could_not_tell\" because the tests need something this machine does not have, such as a test database, a running service, a browser or an environment variable, and only after you have tried to show a break or a keep as the rules above say: look for how the repository documents running its tests, in its README, its agent instruction files (AGENTS.md, CLAUDE.md and the like) and its package scripts, and try the steps it documents. Take no step it does not document, and even among those never install system software, and never ask for or read credentials. If the tests then run, carry on under the rules above. If they still cannot run, or trying would leave no time to answer, the answer stays \"could_not_tell\", never \"kept\", and you add \"nextStep\" to your JSON answer: the one step that would let the tests run, written so the person's agent can act on it, such as the exact documented command that starts the test database. Otherwise leave nextStep out.";
|
|
40
|
-
export declare const JUDGE_BRIEF = "You are judging one change to this repository against one promise about the software's behavior.
|
|
46
|
+
export declare const JUDGE_BRIEF = "You are judging one change to this repository against one promise about the software's behavior. The supplied meaning may be recorded or agreed; use its supplied standing and never infer human agreement or protection. Your working directory is a disposable copy of the repository checked out at the change's head commit; anything you write here is thrown away afterwards.\n\nYour primary job is a substantive code review against this promise. Understand the affected behavior and investigate plausible failures, then decide whether this change keeps the promise, breaks it, or whether you could not tell. Tests and experiments support your review; generating tests is not the goal.\n\nReview approach:\n- Read the actual diff and the relevant implementation at head and base. Trace the affected behavior from its entry points through changed functions, unchanged callers, dependencies and observable effects. Follow a path beyond the supplied anchors when the behavior requires it; shared words or nearby files are not a reason to expand scope.\n- Explain what changed behaviorally and why it matters to this promise. Investigate realistic failure conditions, alternate branches and boundary cases grounded in this code. Consider state, retries, ordering, error handling and permissions where they affect the promised behavior; do not manufacture concerns that do not fit its scope.\n- Inspect the assertions and inputs of relevant existing tests. A passing suite does not settle your review: identify which affected paths it covers and what it misses. Prefer the repository's own entry points and existing checks. Use a focused command, experiment or new test when it resolves a concrete question. Do not mock away the behavior under review or assert implementation spelling instead of observable behavior.\n- Spend the available time on the most consequential uncertainties. Do not stop after a superficial glance or a trivially passing test, and do not pad the review with extra files or tests. Return a concise assessment, not private deliberation: the causal path you established, relevant files you actually inspected, concrete remaining risks and limits of your investigation. State no unresolved risks when you found none; distinguish a plausible concern from an observed failure.\n\nRules:\n\n1. Read the promise's cases first. The passing cases (passing-1, passing-2, and so on, in the order listed) say what must keep happening. The failing cases (failing-1, and so on) say what must never happen. The non-goals say what the promise does not cover; a change there is not a break.\n2. If linked tests are listed, inspect how they exercise this promise and run the relevant checks as part of your investigation. If the runner already reports a linked test's outcome below, use it and rerun only when you need its output. Neither a passing linked test nor its failure substitutes for reviewing the affected code.\n3. Read the code, not descriptions of it. Commit messages, comments, documentation and pull request text say what someone intended, not what the code does.\n4. Report \"broken\" only with a command that fails (exits non-zero) with this change and passes (exits zero) without it, and only after you have run it both ways yourself. To run it without the change, check out the base commit in this copy with `git checkout --detach <base>`, run the command, then return with `git checkout --detach <head>`. If the command needs a test you wrote, put that test in a new file rather than editing an existing one, so it exists on both commits. The command must run from the repository root or from the directory you name as its cwd, and must not depend on anything outside this copy.\n5. Report \"kept\" only after reviewing the affected behavior and exercising each passing case against the changed code, by running a test or a command that shows it. Return \"caseEvidence\" entries containing caseIds, command and cwd for every passing case. One command may cover several cases only when its inputs and assertions exercise each one. The runner will execute these commands itself in a fresh head checkout; listing case IDs or inspecting code alone cannot establish kept. If a concrete concern about this promise remains unresolved, answer \"could_not_tell\" and preserve that concern in your review.\n6. Anything else is \"could_not_tell\". Say in notes what stopped you: a missing dependency, a service you could not reach, a case you could not exercise, time running out.\n7. Name the promise, never the person. Do not put anyone's name, email or account in your answer.\n8. Do not push, open or merge pull requests, publish anything, or change anything outside this copy. Do not read or print credentials.\n9. Only when you would otherwise answer \"could_not_tell\" because the tests need something this machine does not have, such as a test database, a running service, a browser or an environment variable, and only after you have tried to show a break or a keep as the rules above say: look for how the repository documents running its tests, in its README, its agent instruction files (AGENTS.md, CLAUDE.md and the like) and its package scripts, and try the steps it documents. Take no step it does not document, and even among those never install system software, and never ask for or read credentials. If the tests then run, carry on under the rules above. If they still cannot run, or trying would leave no time to answer, the answer stays \"could_not_tell\", never \"kept\", and you add \"nextStep\" to your JSON answer: the one step that would let the tests run, written so the person's agent can act on it, such as the exact documented command that starts the test database. Otherwise leave nextStep out.\n\nList every new file your evidence commands need in \"evidenceFiles\", as repository-relative paths. Include only new regular files, never edits to tracked files or symlinks. Use [] when the commands need no new files. The runner carries only these frozen files into fresh execution checkouts, with the laptop's existing local dependencies. Ignored files or state from your exploration do not travel unless explicitly named. Evidence commands must not change tracked code, their own evidence artifacts or the checked-out commit. A separate local reader reviews whether the commands and assertions actually exercise the promise. Neither independent execution nor that review establishes protection.\n\nYour final message must be exactly one JSON object and nothing after it:\n\n{\"promiseId\": \"<the promise id below>\", \"verdict\": \"kept\" | \"broken\" | \"could_not_tell\", \"reproduction\": {\"command\": \"<shell command>\", \"cwd\": \".\"} or null, \"exercisedCases\": [\"passing-1\", ...], \"caseEvidence\": [{\"caseIds\": [\"passing-1\"], \"command\": \"<shell command>\", \"cwd\": \".\"}], \"evidenceFiles\": [\"<new evidence file>\"], \"review\": {\"summary\": \"<concise behavioral assessment with the causal path and test blind spots>\", \"inspectedFiles\": [\"<repository-relative files actually inspected>\"], \"risks\": [\"<path/function, triggering condition and promised consequence of an unresolved concern>\"], \"limitations\": [\"<what you could not investigate or exercise>\"]}, \"notes\": \"<at most a few sentences>\"}\n\nA reproduction is required for \"broken\" and optional otherwise.";
|
|
41
47
|
export type JudgeBriefInput = Readonly<{
|
|
42
48
|
promise: PromiseDocument;
|
|
43
49
|
map?: BehaviorMap;
|
package/dist/judge-brief.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The fixed instructions a judge works from, `judge-brief/
|
|
2
|
+
* The fixed instructions a judge works from, `judge-brief/v7`.
|
|
3
3
|
*
|
|
4
4
|
* One promise, one change, one disposable checkout at the change's head. The
|
|
5
|
-
* rules are the contract's: cases first,
|
|
5
|
+
* rules are the contract's: cases first, source-grounded review, broken only with a
|
|
6
6
|
* command that fails with the change and passes without it, kept only after
|
|
7
7
|
* each passing case was exercised, and could not tell for everything else,
|
|
8
8
|
* with what stopped the judge. The runner checks the reproduction itself by
|
|
@@ -12,7 +12,13 @@
|
|
|
12
12
|
* The text is a constant so a verdict can name the exact brief it was given.
|
|
13
13
|
* Changing a word here is a new revision.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
15
|
+
* v7 requires a substantive code review before settling a verdict: trace affected
|
|
16
|
+
* behavior, assess test blind spots, and preserve risks and uncertainty locally.
|
|
17
|
+
* Tests support the review; writing new tests is optional. v6 requires explicit
|
|
18
|
+
* commands for passing-case evidence and names every new
|
|
19
|
+
* evidence file. The runner executes frozen artifacts in fresh checkouts, then
|
|
20
|
+
* a separate local reviewer assesses their semantic fidelity. v5 used v3's
|
|
21
|
+
* rules; what it added is a section of the prompt
|
|
16
22
|
* (1 October 2026, with promise anchors): a promise with anchors and no
|
|
17
23
|
* behavior map is handed its anchors, the linked tests the graph gives at
|
|
18
24
|
* head, and the files one import step from its anchor files, where v3 said
|
|
@@ -21,7 +27,7 @@
|
|
|
21
27
|
* adopted on 30 September, so it is never reused. The judge with anchors in
|
|
22
28
|
* place of a map has not been measured yet.
|
|
23
29
|
*/
|
|
24
|
-
export const JUDGE_BRIEF_REVISION = "judge-brief/
|
|
30
|
+
export const JUDGE_BRIEF_REVISION = "judge-brief/v7";
|
|
25
31
|
/**
|
|
26
32
|
* What `judge-brief/v3` added to v1, and all it added (Robert, 30 September
|
|
27
33
|
* 2026): a way forward when the tests need something the machine lacks.
|
|
@@ -34,25 +40,33 @@ export const JUDGE_BRIEF_REVISION = "judge-brief/v5";
|
|
|
34
40
|
* thing in the answer is `nextStep`, the one step that would let the tests run.
|
|
35
41
|
*/
|
|
36
42
|
export const JUDGE_BRIEF_SETUP_RULE = `9. Only when you would otherwise answer "could_not_tell" because the tests need something this machine does not have, such as a test database, a running service, a browser or an environment variable, and only after you have tried to show a break or a keep as the rules above say: look for how the repository documents running its tests, in its README, its agent instruction files (AGENTS.md, CLAUDE.md and the like) and its package scripts, and try the steps it documents. Take no step it does not document, and even among those never install system software, and never ask for or read credentials. If the tests then run, carry on under the rules above. If they still cannot run, or trying would leave no time to answer, the answer stays "could_not_tell", never "kept", and you add "nextStep" to your JSON answer: the one step that would let the tests run, written so the person's agent can act on it, such as the exact documented command that starts the test database. Otherwise leave nextStep out.`;
|
|
37
|
-
export const JUDGE_BRIEF = `You are judging one change to this repository against one promise about the software's behavior.
|
|
43
|
+
export const JUDGE_BRIEF = `You are judging one change to this repository against one promise about the software's behavior. The supplied meaning may be recorded or agreed; use its supplied standing and never infer human agreement or protection. Your working directory is a disposable copy of the repository checked out at the change's head commit; anything you write here is thrown away afterwards.
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
Your primary job is a substantive code review against this promise. Understand the affected behavior and investigate plausible failures, then decide whether this change keeps the promise, breaks it, or whether you could not tell. Tests and experiments support your review; generating tests is not the goal.
|
|
46
|
+
|
|
47
|
+
Review approach:
|
|
48
|
+
- Read the actual diff and the relevant implementation at head and base. Trace the affected behavior from its entry points through changed functions, unchanged callers, dependencies and observable effects. Follow a path beyond the supplied anchors when the behavior requires it; shared words or nearby files are not a reason to expand scope.
|
|
49
|
+
- Explain what changed behaviorally and why it matters to this promise. Investigate realistic failure conditions, alternate branches and boundary cases grounded in this code. Consider state, retries, ordering, error handling and permissions where they affect the promised behavior; do not manufacture concerns that do not fit its scope.
|
|
50
|
+
- Inspect the assertions and inputs of relevant existing tests. A passing suite does not settle your review: identify which affected paths it covers and what it misses. Prefer the repository's own entry points and existing checks. Use a focused command, experiment or new test when it resolves a concrete question. Do not mock away the behavior under review or assert implementation spelling instead of observable behavior.
|
|
51
|
+
- Spend the available time on the most consequential uncertainties. Do not stop after a superficial glance or a trivially passing test, and do not pad the review with extra files or tests. Return a concise assessment, not private deliberation: the causal path you established, relevant files you actually inspected, concrete remaining risks and limits of your investigation. State no unresolved risks when you found none; distinguish a plausible concern from an observed failure.
|
|
40
52
|
|
|
41
53
|
Rules:
|
|
42
54
|
|
|
43
55
|
1. Read the promise's cases first. The passing cases (passing-1, passing-2, and so on, in the order listed) say what must keep happening. The failing cases (failing-1, and so on) say what must never happen. The non-goals say what the promise does not cover; a change there is not a break.
|
|
44
|
-
2. If linked tests are listed, run
|
|
56
|
+
2. If linked tests are listed, inspect how they exercise this promise and run the relevant checks as part of your investigation. If the runner already reports a linked test's outcome below, use it and rerun only when you need its output. Neither a passing linked test nor its failure substitutes for reviewing the affected code.
|
|
45
57
|
3. Read the code, not descriptions of it. Commit messages, comments, documentation and pull request text say what someone intended, not what the code does.
|
|
46
58
|
4. Report "broken" only with a command that fails (exits non-zero) with this change and passes (exits zero) without it, and only after you have run it both ways yourself. To run it without the change, check out the base commit in this copy with \`git checkout --detach <base>\`, run the command, then return with \`git checkout --detach <head>\`. If the command needs a test you wrote, put that test in a new file rather than editing an existing one, so it exists on both commits. The command must run from the repository root or from the directory you name as its cwd, and must not depend on anything outside this copy.
|
|
47
|
-
5. Report "kept" only after
|
|
59
|
+
5. Report "kept" only after reviewing the affected behavior and exercising each passing case against the changed code, by running a test or a command that shows it. Return "caseEvidence" entries containing caseIds, command and cwd for every passing case. One command may cover several cases only when its inputs and assertions exercise each one. The runner will execute these commands itself in a fresh head checkout; listing case IDs or inspecting code alone cannot establish kept. If a concrete concern about this promise remains unresolved, answer "could_not_tell" and preserve that concern in your review.
|
|
48
60
|
6. Anything else is "could_not_tell". Say in notes what stopped you: a missing dependency, a service you could not reach, a case you could not exercise, time running out.
|
|
49
61
|
7. Name the promise, never the person. Do not put anyone's name, email or account in your answer.
|
|
50
62
|
8. Do not push, open or merge pull requests, publish anything, or change anything outside this copy. Do not read or print credentials.
|
|
51
63
|
${JUDGE_BRIEF_SETUP_RULE}
|
|
52
64
|
|
|
65
|
+
List every new file your evidence commands need in "evidenceFiles", as repository-relative paths. Include only new regular files, never edits to tracked files or symlinks. Use [] when the commands need no new files. The runner carries only these frozen files into fresh execution checkouts, with the laptop's existing local dependencies. Ignored files or state from your exploration do not travel unless explicitly named. Evidence commands must not change tracked code, their own evidence artifacts or the checked-out commit. A separate local reader reviews whether the commands and assertions actually exercise the promise. Neither independent execution nor that review establishes protection.
|
|
66
|
+
|
|
53
67
|
Your final message must be exactly one JSON object and nothing after it:
|
|
54
68
|
|
|
55
|
-
{"promiseId": "<the promise id below>", "verdict": "kept" | "broken" | "could_not_tell", "reproduction": {"command": "<shell command>", "cwd": "."} or null, "exercisedCases": ["passing-1", ...], "notes": "<at most a few sentences>"}
|
|
69
|
+
{"promiseId": "<the promise id below>", "verdict": "kept" | "broken" | "could_not_tell", "reproduction": {"command": "<shell command>", "cwd": "."} or null, "exercisedCases": ["passing-1", ...], "caseEvidence": [{"caseIds": ["passing-1"], "command": "<shell command>", "cwd": "."}], "evidenceFiles": ["<new evidence file>"], "review": {"summary": "<concise behavioral assessment with the causal path and test blind spots>", "inspectedFiles": ["<repository-relative files actually inspected>"], "risks": ["<path/function, triggering condition and promised consequence of an unresolved concern>"], "limitations": ["<what you could not investigate or exercise>"]}, "notes": "<at most a few sentences>"}
|
|
56
70
|
|
|
57
71
|
A reproduction is required for "broken" and optional otherwise.`;
|
|
58
72
|
const CHANGED_FILES_SHOWN = 300;
|
|
@@ -69,10 +83,12 @@ export function judgePrompt(input) {
|
|
|
69
83
|
promiseId: input.promise.promiseId,
|
|
70
84
|
title: input.promise.title,
|
|
71
85
|
semanticDigest: input.promise.semanticDigest,
|
|
86
|
+
standing: input.promise.standing,
|
|
87
|
+
subject: input.promise.subject,
|
|
72
88
|
meaning: input.promise.meaning,
|
|
73
89
|
}, null, 2));
|
|
74
90
|
if (input.promise.meaning.passingExamples.length === 0)
|
|
75
|
-
sections.push("Its passing cases were not available here, so
|
|
91
|
+
sections.push("Its passing cases were not available here, so kept cannot be established by executable case evidence; answer broken with a reproduction or could_not_tell.");
|
|
76
92
|
sections.push("");
|
|
77
93
|
if (input.map !== undefined) {
|
|
78
94
|
// The excerpt repeats the promise printed above; the judge reads it once.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type HeadlessAgentOptions, type HeadlessAgentResult, type LoginCheck } from "./headless-agent.js";
|
|
2
|
+
import { type PromiseDocument } from "./promise-meaning.js";
|
|
3
|
+
import { type CarriedFile } from "./scratch-worktree.js";
|
|
4
|
+
/** Local execution provenance, not proof that an assertion expresses the promise. */
|
|
5
|
+
export declare const EVIDENCE_PROTOCOL_REVISION = "judge-evidence/v1";
|
|
6
|
+
export type CaseEvidence = Readonly<{
|
|
7
|
+
caseIds: readonly string[];
|
|
8
|
+
command: string;
|
|
9
|
+
cwd: string;
|
|
10
|
+
}>;
|
|
11
|
+
export type FrozenEvidenceFile = CarriedFile & Readonly<{
|
|
12
|
+
bytes: Buffer;
|
|
13
|
+
}>;
|
|
14
|
+
export type FrozenEvidenceManifest = Readonly<{
|
|
15
|
+
files: readonly FrozenEvidenceFile[];
|
|
16
|
+
digest: string;
|
|
17
|
+
}>;
|
|
18
|
+
export type EvidenceReceipt = Readonly<{
|
|
19
|
+
caseIds: readonly string[];
|
|
20
|
+
command: string;
|
|
21
|
+
cwd: string;
|
|
22
|
+
commit: string;
|
|
23
|
+
exitCode: number | null;
|
|
24
|
+
timedOut: boolean;
|
|
25
|
+
aborted: boolean;
|
|
26
|
+
durationMs: number;
|
|
27
|
+
artifactDigest: string;
|
|
28
|
+
dependencyMode: "shared-local";
|
|
29
|
+
/** Local lockfile fingerprints describe declared dependencies, not installed-byte integrity. */
|
|
30
|
+
localDependencyLocks: readonly CarriedFile[];
|
|
31
|
+
nodeVersion: string;
|
|
32
|
+
platform: NodeJS.Platform;
|
|
33
|
+
output: string;
|
|
34
|
+
}>;
|
|
35
|
+
export type EvidenceExecution = Readonly<{
|
|
36
|
+
receipts: readonly EvidenceReceipt[];
|
|
37
|
+
rejected: string | null;
|
|
38
|
+
stopped: boolean;
|
|
39
|
+
}>;
|
|
40
|
+
type LocalInput = Readonly<{
|
|
41
|
+
root: string;
|
|
42
|
+
sourceTree: string;
|
|
43
|
+
head: string;
|
|
44
|
+
manifest: FrozenEvidenceManifest;
|
|
45
|
+
env: NodeJS.ProcessEnv;
|
|
46
|
+
timeoutMs?: number;
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
scratch?: string;
|
|
49
|
+
}>;
|
|
50
|
+
export declare function freezeEvidenceFiles(sourceTree: string, paths: readonly string[]): Promise<{
|
|
51
|
+
ok: true;
|
|
52
|
+
manifest: FrozenEvidenceManifest;
|
|
53
|
+
} | {
|
|
54
|
+
ok: false;
|
|
55
|
+
reason: string;
|
|
56
|
+
}>;
|
|
57
|
+
export declare function executeKeptEvidence(input: LocalInput & Readonly<{
|
|
58
|
+
caseEvidence: readonly CaseEvidence[];
|
|
59
|
+
passingCaseIds: readonly string[];
|
|
60
|
+
}>): Promise<EvidenceExecution>;
|
|
61
|
+
export declare function executeFreshReproduction(input: LocalInput & Readonly<{
|
|
62
|
+
base: string;
|
|
63
|
+
command: string;
|
|
64
|
+
cwd: string;
|
|
65
|
+
}>): Promise<EvidenceExecution & Readonly<{
|
|
66
|
+
failsOnHead: boolean;
|
|
67
|
+
passesOnBase: boolean;
|
|
68
|
+
digest: string;
|
|
69
|
+
files: readonly CarriedFile[];
|
|
70
|
+
}>>;
|
|
71
|
+
export type EvidenceReview = Readonly<{
|
|
72
|
+
accepted: boolean;
|
|
73
|
+
reason: string;
|
|
74
|
+
stopped: boolean;
|
|
75
|
+
agent?: HeadlessAgentResult;
|
|
76
|
+
}>;
|
|
77
|
+
/** A fresh local reader assesses semantics; execution receipts alone cannot establish fidelity. */
|
|
78
|
+
export declare function reviewExecutedEvidence(input: LocalInput & Readonly<{
|
|
79
|
+
promise: PromiseDocument;
|
|
80
|
+
base: string;
|
|
81
|
+
claim: "kept" | "broken";
|
|
82
|
+
review?: import("./judge-review.js").CodeReview;
|
|
83
|
+
receipts: readonly EvidenceReceipt[];
|
|
84
|
+
login: LoginCheck;
|
|
85
|
+
runAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
|
|
86
|
+
}>): Promise<EvidenceReview>;
|
|
87
|
+
export {};
|