balladeer 1.0.15 → 1.0.17

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.
Files changed (77) hide show
  1. package/README.md +2 -0
  2. package/dist/behavior-map-schema.d.ts +107 -0
  3. package/dist/behavior-map-schema.js +232 -0
  4. package/dist/cli.d.ts +6 -1
  5. package/dist/cli.js +74 -6
  6. package/dist/commands/guidance.d.ts +2 -1
  7. package/dist/commands/guidance.js +55 -0
  8. package/dist/commands/judge.d.ts +430 -0
  9. package/dist/commands/judge.js +1713 -0
  10. package/dist/commands/map.d.ts +164 -0
  11. package/dist/commands/map.js +838 -0
  12. package/dist/commands/mcp.js +2 -2
  13. package/dist/commands/offers.d.ts +265 -0
  14. package/dist/commands/offers.js +801 -0
  15. package/dist/commands/risk.d.ts +29 -0
  16. package/dist/commands/risk.js +133 -0
  17. package/dist/copy.d.ts +24 -1
  18. package/dist/copy.js +91 -0
  19. package/dist/guidance-hook.mjs +348 -94
  20. package/dist/headless-agent.d.ts +166 -0
  21. package/dist/headless-agent.js +416 -0
  22. package/dist/hook-trust.d.ts +41 -9
  23. package/dist/hook-trust.js +98 -16
  24. package/dist/judge-brief.d.ts +48 -0
  25. package/dist/judge-brief.js +102 -0
  26. package/dist/judge-hook.d.ts +258 -0
  27. package/dist/judge-hook.js +1278 -0
  28. package/dist/judge-said.d.ts +52 -0
  29. package/dist/judge-said.js +181 -0
  30. package/dist/map-brief.d.ts +16 -0
  31. package/dist/map-brief.js +41 -0
  32. package/dist/offer-brief.d.ts +95 -0
  33. package/dist/offer-brief.js +217 -0
  34. package/dist/owned-process.d.ts +65 -0
  35. package/dist/owned-process.js +146 -0
  36. package/dist/promise-meaning.d.ts +134 -0
  37. package/dist/promise-meaning.js +300 -0
  38. package/dist/relay.d.ts +71 -0
  39. package/dist/relay.js +193 -0
  40. package/dist/remove-earlier.js +4 -2
  41. package/dist/risk/contract.d.ts +145 -0
  42. package/dist/risk/contract.js +74 -0
  43. package/dist/risk/describe.d.ts +7 -0
  44. package/dist/risk/describe.js +45 -0
  45. package/dist/risk/diff.d.ts +26 -0
  46. package/dist/risk/diff.js +174 -0
  47. package/dist/risk/extract.d.ts +43 -0
  48. package/dist/risk/extract.js +336 -0
  49. package/dist/risk/git.d.ts +30 -0
  50. package/dist/risk/git.js +118 -0
  51. package/dist/risk/import-graph.d.ts +41 -0
  52. package/dist/risk/import-graph.js +487 -0
  53. package/dist/risk/index.d.ts +20 -0
  54. package/dist/risk/index.js +20 -0
  55. package/dist/risk/paths.d.ts +16 -0
  56. package/dist/risk/paths.js +73 -0
  57. package/dist/risk/pipeline.d.ts +47 -0
  58. package/dist/risk/pipeline.js +121 -0
  59. package/dist/risk/priors.d.ts +18 -0
  60. package/dist/risk/priors.js +86 -0
  61. package/dist/risk/resources.d.ts +56 -0
  62. package/dist/risk/resources.js +374 -0
  63. package/dist/risk/score.d.ts +95 -0
  64. package/dist/risk/score.js +552 -0
  65. package/dist/risk/symbols.d.ts +35 -0
  66. package/dist/risk/symbols.js +348 -0
  67. package/dist/risk/text.d.ts +43 -0
  68. package/dist/risk/text.js +277 -0
  69. package/dist/risk/validate.d.ts +19 -0
  70. package/dist/risk/validate.js +154 -0
  71. package/dist/scratch-worktree.d.ts +55 -0
  72. package/dist/scratch-worktree.js +160 -0
  73. package/dist/user-scope.d.ts +74 -3
  74. package/dist/user-scope.js +270 -9
  75. package/dist/wire.d.ts +52 -3
  76. package/dist/wire.js +2 -2
  77. package/package.json +1 -1
@@ -0,0 +1,430 @@
1
+ import { type BehaviorMap } from "../behavior-map-schema.js";
2
+ import { type HeadlessAgentOptions, type HeadlessAgentResult, type HeadlessClient, type LoginCheck } from "../headless-agent.js";
3
+ import { type HookHost, type MergeTarget } from "../judge-hook.js";
4
+ import { type PromiseDocument, type PromiseReader } from "../promise-meaning.js";
5
+ import { type CarriedFile } from "../scratch-worktree.js";
6
+ import type { Features } from "../risk/contract.js";
7
+ /**
8
+ * `balladeer judge`: for a change about to leave this machine, ask the person's
9
+ * own coding agent, one promise at a time, whether the change keeps it.
10
+ *
11
+ * A verdict is only as good as its evidence, so the runner does not take a
12
+ * judge's word for a break. A judge that says "broken" must hand back a command,
13
+ * and the runner runs that command itself, once at head and once in a clean
14
+ * checkout of base: it must fail with the change and pass without it. Anything
15
+ * short of that is "could not tell", with the reason kept. Nothing here ever
16
+ * says a promise is protected; a judge's "kept" is a local reading of one
17
+ * change, not a check that holds.
18
+ */
19
+ export declare const JUDGE_VERDICT_SCHEMA_VERSION = "balladeer-judge-verdict/v1";
20
+ export declare const JUDGE_CONFIG_SCHEMA_VERSION = "balladeer-judge-config/v1";
21
+ export declare const JUDGE_CONFIG_PATH = ".continuity/judge.json";
22
+ export declare const JUDGE_LOG_DIRECTORY = ".continuity/judge-log";
23
+ /**
24
+ * Where a verified reproduction's files are kept, one folder per promise and
25
+ * pushed commit, inside the judge log so git ignores them like the log itself.
26
+ */
27
+ export declare const KEPT_REPRODUCTIONS_DIRECTORY = ".continuity/judge-log/reproductions";
28
+ export declare const KEPT_REPRODUCTION_SCHEMA_VERSION = "balladeer-kept-reproduction/v1";
29
+ export declare const STILL_JUDGING = "still judging; rerun `balladeer judge --wait`";
30
+ /** Block mode's answer to a line that commits and then pushes: the check runs before the line. */
31
+ export declare const COMMIT_THEN_PUSH = "Held: this command makes a commit and then pushes it, and the check runs before the command, so it cannot see a commit that has not been made yet. Commit first, then push in a separate command.";
32
+ export type JudgeConfig = Readonly<{
33
+ schemaVersion: typeof JUDGE_CONFIG_SCHEMA_VERSION;
34
+ mode: "report" | "block";
35
+ topK: number;
36
+ concurrency: number;
37
+ budgetSeconds: number;
38
+ sampleBelowLine: number;
39
+ alwaysJudge: readonly string[];
40
+ client: HeadlessClient | "auto";
41
+ /**
42
+ * Which promises the risk report hands to a judge: "direct" (the default)
43
+ * takes every promise whose own code or tests the change edits, and nothing
44
+ * else; "ranked" takes the top `topK` in the report's order plus a random
45
+ * sample below that line.
46
+ */
47
+ select: "direct" | "ranked";
48
+ /**
49
+ * From the push hook, also read the change for rules no promise records and
50
+ * hand at most three to the pushing session as offers. Off unless set.
51
+ */
52
+ offers: boolean;
53
+ /**
54
+ * Which pushes are read when offers are on: only those that repair
55
+ * something ("fixes", the default), or every change ("all").
56
+ */
57
+ offersOn: "fixes" | "all";
58
+ /** How many offers one push may hand to the person: one by default, three at most. */
59
+ maxOffers: number;
60
+ }>;
61
+ export declare const DEFAULT_JUDGE_CONFIG: JudgeConfig;
62
+ /**
63
+ * `.continuity/judge.json`, with the contract's defaults for anything absent.
64
+ * A field that is present and wrong keeps its default and is named, so a typo
65
+ * in a team's config is visible rather than quietly ignored.
66
+ */
67
+ export declare function readJudgeConfig(root: string): {
68
+ config: JudgeConfig;
69
+ problems: string[];
70
+ };
71
+ export type JudgeVerdict = {
72
+ schemaVersion: typeof JUDGE_VERDICT_SCHEMA_VERSION;
73
+ promiseId: string;
74
+ semanticDigest: string;
75
+ base: string;
76
+ head: string;
77
+ verdict: "kept" | "broken" | "could_not_tell";
78
+ reproduction: {
79
+ command: string;
80
+ cwd: string;
81
+ failsOnHead: boolean;
82
+ passesOnBase: boolean;
83
+ digest: string;
84
+ /**
85
+ * Where the reproduction was kept on this machine, repository-relative:
86
+ * a folder holding `manifest.json` and, under `files/`, the files the
87
+ * judge added, at the paths it wrote them. Only for a break verified both
88
+ * ways, and absent when keeping it failed.
89
+ */
90
+ keptIn?: string;
91
+ /** The kept files' repository-relative paths, each under `<keptIn>/files/`. */
92
+ keptFiles?: string[];
93
+ } | null;
94
+ reproductionRejected: string | null;
95
+ exercisedCases: string[];
96
+ notes: string;
97
+ /**
98
+ * On a could-not-tell the judge itself reached because the tests need
99
+ * something this machine lacks: the one step that would let them run, as
100
+ * the judge wrote it. Absent otherwise. It never changes the verdict.
101
+ */
102
+ nextStep?: string;
103
+ judge: {
104
+ client: HeadlessClient;
105
+ model: string;
106
+ durationMs: number;
107
+ inputTokens: number;
108
+ outputTokens: number;
109
+ };
110
+ briefRevision: string;
111
+ mapUsed: boolean;
112
+ linkedTestRun: {
113
+ file: string;
114
+ outcome: "pass" | "fail" | "error" | "none";
115
+ };
116
+ };
117
+ /** A command's exit, as one run of a reproduction or a linked test. */
118
+ export type CommandRun = Readonly<{
119
+ exitCode: number | null;
120
+ timedOut: boolean;
121
+ aborted: boolean;
122
+ output: string;
123
+ }>;
124
+ export declare function runShell(command: string, cwd: string, options: Readonly<{
125
+ env: NodeJS.ProcessEnv;
126
+ timeoutMs: number;
127
+ signal?: AbortSignal;
128
+ }>): Promise<CommandRun>;
129
+ export type ReproductionCheck = Readonly<{
130
+ failsOnHead: boolean;
131
+ passesOnBase: boolean;
132
+ digest: string;
133
+ rejected: string | null;
134
+ /** The budget ran out while it was being run, so there is no answer yet. */
135
+ stopped: boolean;
136
+ /** The files the judge added, carried to base and fingerprinted into `digest`. */
137
+ files: readonly CarriedFile[];
138
+ }>;
139
+ /**
140
+ * Run a judge's reproduction both ways.
141
+ *
142
+ * At head, in the judge's own checkout, reset to head so a judge that left it
143
+ * on base cannot fool the check. At base, in a fresh checkout of base, with the
144
+ * files the judge added carried across so a new test exists on both sides. It
145
+ * must exit non-zero at head and zero at base; either disagreeing rejects it,
146
+ * and the reason says which.
147
+ */
148
+ export declare function verifyReproduction(input: Readonly<{
149
+ root: string;
150
+ headTree: string;
151
+ head: string;
152
+ base: string;
153
+ command: string;
154
+ cwd: string;
155
+ env: NodeJS.ProcessEnv;
156
+ timeoutMs?: number;
157
+ signal?: AbortSignal;
158
+ scratch?: string;
159
+ }>): Promise<ReproductionCheck>;
160
+ export type KeptReproduction = Readonly<{
161
+ /** Repository-relative, forward slashes: the folder under the judge log. */
162
+ keptIn: string;
163
+ /** The files kept, repository-relative, each at `<keptIn>/files/<path>`. */
164
+ files: string[];
165
+ }>;
166
+ /**
167
+ * Keep a verified reproduction on this machine, so the break can become a
168
+ * lasting test instead of vanishing with the judge's checkout.
169
+ *
170
+ * The files the judge added are copied from its checkout into
171
+ * `.continuity/judge-log/reproductions/<promise id>-<first 12 of head>/files/`
172
+ * at the paths the judge wrote them, beside a `manifest.json` that says which
173
+ * promise, meaning and change they reproduce a break of, with the command,
174
+ * its cwd, the digest and each file's own fingerprint. The digest can be
175
+ * recomputed from the manifest alone. A reproduction that added no files
176
+ * keeps just the manifest. The judge log carries its own `.gitignore`, so
177
+ * nothing here is ever committed, and nothing here is sent anywhere.
178
+ *
179
+ * The folder is written beside its final name and then renamed into place,
180
+ * replacing an earlier one for the same promise and commit, so a reader never
181
+ * sees half of one. Keeping is a courtesy to the person, never a condition of
182
+ * the verdict: when it fails, the verdict stands and says nothing was kept.
183
+ */
184
+ export declare function keepReproduction(input: Readonly<{
185
+ root: string;
186
+ headTree: string;
187
+ promiseId: string;
188
+ semanticDigest: string;
189
+ base: string;
190
+ head: string;
191
+ command: string;
192
+ cwd: string;
193
+ digest: string;
194
+ files: readonly CarriedFile[];
195
+ }>): KeptReproduction | undefined;
196
+ /**
197
+ * How this repository runs one test file, when it can be told from the files
198
+ * alone. A runner that cannot be told is `none`, and the judge is left to find
199
+ * its own way.
200
+ */
201
+ export declare function linkedTestCommand(tree: string, file: string): string | undefined;
202
+ type JudgeAnswer = Readonly<{
203
+ verdict: JudgeVerdict["verdict"];
204
+ reproduction?: Readonly<{
205
+ command: string;
206
+ cwd: string;
207
+ }>;
208
+ exercisedCases: string[];
209
+ notes: string;
210
+ nextStep?: string;
211
+ }>;
212
+ /** The longest `nextStep` kept from a judge's answer, in characters. */
213
+ export declare const NEXT_STEP_LIMIT = 500;
214
+ /** The judge's final JSON, read strictly enough that a malformed answer is not a verdict. */
215
+ export declare function readJudgeAnswer(json: Record<string, unknown> | undefined): JudgeAnswer | undefined;
216
+ export type JudgePromiseInput = Readonly<{
217
+ root: string;
218
+ promise: PromiseDocument;
219
+ map?: BehaviorMap;
220
+ base: string;
221
+ head: string;
222
+ changedFiles: readonly string[];
223
+ login: LoginCheck;
224
+ env: NodeJS.ProcessEnv;
225
+ timeoutMs?: number;
226
+ signal?: AbortSignal;
227
+ scratch?: string;
228
+ runAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
229
+ }>;
230
+ export type JudgeOutcome = Readonly<{
231
+ status: "judged";
232
+ verdict: JudgeVerdict;
233
+ }> | Readonly<{
234
+ status: "stopped";
235
+ }> | Readonly<{
236
+ status: "not_judged";
237
+ reason: string;
238
+ }>;
239
+ /**
240
+ * One promise judged against one change, with its reproduction verified.
241
+ *
242
+ * Returns `stopped` when the caller's budget ran out first, which is not a
243
+ * verdict and is never logged as one: the push is either held or told the
244
+ * judge is still running, never let through as if it had been judged.
245
+ */
246
+ export declare function judgePromise(input: JudgePromiseInput): Promise<JudgeOutcome>;
247
+ export type JudgeArguments = Readonly<{
248
+ promises?: readonly string[];
249
+ topK?: number;
250
+ base?: string;
251
+ head?: string;
252
+ json: boolean;
253
+ mode?: "report" | "block";
254
+ wait: boolean;
255
+ hook?: HookHost;
256
+ /** Run by the entry setup writes at the host's user scope, which stands aside where it has nothing to do. */
257
+ userScope?: true;
258
+ /** Turn this laptop's user-scope push check on or off. */
259
+ pushCheck?: "on" | "off";
260
+ installHook?: HookHost | "agents";
261
+ fromFile?: string;
262
+ map?: string;
263
+ repo?: string;
264
+ client?: HeadlessClient | "auto";
265
+ repository?: string;
266
+ controlPlane: string;
267
+ }>;
268
+ 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 both ways itself; 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 from `balladeer risk`: every promise whose own code or tests the\n change edits, or with \"select\": \"ranked\" in .continuity/judge.json the\n top of its ranking and a small random sample; else every mapped\n promise; plus that file's alwaysJudge. Verdicts are kept in\n .continuity/judge-log/, which git ignores. --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 mapped 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";
269
+ export declare function parseJudgeArguments(argv: readonly string[]): JudgeArguments;
270
+ /**
271
+ * The subset of `balladeer-risk/v1` this command reads: enough to order the
272
+ * entries the way `balladeer risk` does. An entry without `features` reads as
273
+ * reaching its promise not at all, and one without `evidence` as none.
274
+ */
275
+ export type RiskEntry = Readonly<{
276
+ promiseId: string;
277
+ score: number;
278
+ band: string;
279
+ features?: Features;
280
+ /** The total weight of the entry's reasons. */
281
+ evidence?: number;
282
+ }>;
283
+ /** Read a risk report out of whatever `balladeer risk --json` printed. */
284
+ export declare function readRiskOutput(stdout: string): RiskEntry[] | undefined;
285
+ /**
286
+ * The commit a change is measured from: the fork point with the remote default
287
+ * branch, else with this branch's upstream, else the parent of head.
288
+ */
289
+ export declare function defaultBase(root: string, head: string): Promise<string | undefined>;
290
+ export declare function changedFiles(root: string, base: string, head: string): Promise<string[]>;
291
+ export type Selection = Readonly<{
292
+ promiseId: string;
293
+ why: "file" | "named" | "risk" | "sample" | "mapped" | "always";
294
+ }>;
295
+ /**
296
+ * Which promises to judge: named ones, else from the risk report, else every
297
+ * mapped promise; then `alwaysJudge`.
298
+ *
299
+ * From the risk report, in the order `balladeer risk` lists it (how directly
300
+ * the change reaches each promise, then score, then evidence, then id; a score
301
+ * alone ties at 1 and falls to the alphabet):
302
+ *
303
+ * - "direct" takes every promise whose own code or tests the change edits
304
+ * (directness tier 3), and nothing else. The first live evaluation found
305
+ * that this selects about 1.8 promises a change and included the broken one
306
+ * in 17 of 17 cases. Only a `--top` given by hand caps it.
307
+ * - "ranked" takes the top k with a score above zero, plus a random sample
308
+ * below that line. The sample is what keeps a ranking honest: a break found
309
+ * below the line is a miss the ranking made, counted rather than never seen.
310
+ */
311
+ export declare function selectPromises(input: Readonly<{
312
+ named?: readonly string[];
313
+ risk?: readonly RiskEntry[];
314
+ mapped: readonly string[];
315
+ config: JudgeConfig;
316
+ topK?: number;
317
+ random: () => number;
318
+ }>): Selection[];
319
+ /**
320
+ * Append verdicts to today's log. The directory carries its own `.gitignore`,
321
+ * so a verdict log never lands in a commit whatever the repository ignores.
322
+ */
323
+ export declare function appendVerdicts(root: string, verdicts: readonly JudgeVerdict[], now: Date): void;
324
+ /** A verdict already reached for exactly this promise, meaning, change and brief. */
325
+ export declare function findLoggedVerdict(root: string, key: Readonly<{
326
+ promiseId: string;
327
+ semanticDigest: string;
328
+ base: string;
329
+ head: string;
330
+ }>, now: Date): JudgeVerdict | undefined;
331
+ /** Said after "fix the behavior": a deliberate change is the person's, and the promise its owner's. */
332
+ 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.";
333
+ export type VerdictLineContext = Readonly<{
334
+ /** The verdict was reached earlier for this same commit and read back from the log. */
335
+ reused?: boolean;
336
+ /**
337
+ * Whether the promise has a Balladeer check bound, as its connection said.
338
+ * Absent when that is unknown (a promise from a file or a map's excerpt),
339
+ * and then the line says nothing about a check rather than guessing.
340
+ */
341
+ checkBound?: boolean;
342
+ }>;
343
+ /**
344
+ * What to do after a reproduced break, as one line an agent reads in order:
345
+ * where the test that shows it is kept, fix the behavior rather than the
346
+ * test, then make the break a lasting check in this repository's own tests,
347
+ * committed with the fix and linked to the promise's map, and tell the person
348
+ * in one line what was added. When the promise's own Balladeer check missed
349
+ * the break, a repair of that check is proposed to its owner as well.
350
+ */
351
+ export declare function afterBreakLine(verdict: JudgeVerdict, checkBound?: boolean): string;
352
+ /**
353
+ * The verdict as it may be told now: a reproduction said to be kept whose
354
+ * folder is no longer on this machine (a verdict read back from the log after
355
+ * somebody cleared it) loses its kept fields, so the line never points at a
356
+ * file that is not there.
357
+ */
358
+ export declare function stillKept(root: string, verdict: JudgeVerdict): JudgeVerdict;
359
+ /**
360
+ * One line per verdict, the promise named by its title and id. A broken one
361
+ * carries a second, indented line saying what to do next.
362
+ */
363
+ export declare function verdictLine(verdict: JudgeVerdict, title: string, context?: VerdictLineContext): string;
364
+ export type JudgeDependencies = Readonly<{
365
+ runAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
366
+ resolveLogin?: (requested: HeadlessClient | "auto", prefer?: HeadlessClient) => Promise<LoginCheck | undefined>;
367
+ riskReport?: (root: string, base: string, head: string) => Promise<readonly RiskEntry[] | undefined>;
368
+ /** `null` means "no connection", without trying to find one. */
369
+ reader?: PromiseReader | null;
370
+ random?: () => number;
371
+ now?: () => Date;
372
+ stdin?: () => Promise<string>;
373
+ scratch?: string;
374
+ /** Replaces the judge itself, for tests of the flow around it. */
375
+ judge?: (input: JudgePromiseInput) => Promise<JudgeOutcome>;
376
+ /** The agent run that reads the change for offers, when they are on. */
377
+ offerAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
378
+ /** The head commit of the pull request a `gh pr merge` names. */
379
+ pullRequestHead?: (root: string, target: MergeTarget) => Promise<string | undefined>;
380
+ /** Whether this laptop has a connection for the checkout, read locally. */
381
+ connected?: (root: string) => boolean;
382
+ }>;
383
+ /** Said instead of verdicts when the check cannot tell which repository a push leaves from. */
384
+ export declare const PUSH_REPOSITORY_UNKNOWN = "Balladeer judge: this push was not judged, because the check could not tell which repository it pushes.";
385
+ /** Said instead of verdicts when every pull request the command opens or merges is in another repository. */
386
+ export declare function pullRequestElsewhereLine(repo: string): string;
387
+ export type JudgeRunOptions = Readonly<{
388
+ args: JudgeArguments;
389
+ cwd: string;
390
+ environment: NodeJS.ProcessEnv;
391
+ write: (text: string) => void;
392
+ error: (text: string) => void;
393
+ deps?: JudgeDependencies;
394
+ }>;
395
+ /** What `--push-check off` and `--push-check on` say. */
396
+ export declare const PUSH_CHECK_OFF_LINE = "The push check is off on this laptop: after a push it now says nothing, in every repository. A repository's own entry from `balladeer judge --install-hook` still runs. Turn it back on with `balladeer judge --push-check on`.";
397
+ export declare const PUSH_CHECK_ON_LINE = "The push check is back on for this laptop: after a push in a connected repository whose promises have behavior maps, it reports what the push may have broken, and it never holds the push.";
398
+ /**
399
+ * Whether the push check's user-scope entry has this push to itself, decided
400
+ * before anything is started or fetched: the entry runs on every shell command
401
+ * in every folder on the laptop, and most folders have nothing to judge. It
402
+ * stands aside, saying nothing, when the laptop turned the check off, the
403
+ * folder is in no checkout, the checkout has the push check's own project
404
+ * entry for this host (that entry owns the check there, so a push is judged
405
+ * once), or no promise here has a behavior map. Only then is the connection
406
+ * looked for, which reads the checkout's remotes, and a checkout this laptop
407
+ * has not connected is left alone too.
408
+ */
409
+ export declare function userScopeTakesThePush(input: {
410
+ cwd: string;
411
+ host: "claude" | "codex";
412
+ environment: NodeJS.ProcessEnv;
413
+ connected: (root: string) => boolean;
414
+ }): boolean;
415
+ /** `balladeer judge`, from arguments already parsed. */
416
+ /**
417
+ * The line an answer opens with when the command makes a commit before it
418
+ * pushes. Robert approved it on 30 September 2026: the check keeps running
419
+ * before the push, and says plainly that it did not see the commit.
420
+ */
421
+ export declare function commitFirstLine(made: string): string;
422
+ export declare function runJudge(options: JudgeRunOptions): Promise<number>;
423
+ /** `balladeer judge ...` from raw arguments, as `cli.ts` hands them over. */
424
+ export declare function judgeCommand(argv: readonly string[], io: Readonly<{
425
+ cwd: string;
426
+ environment: NodeJS.ProcessEnv;
427
+ write: (text: string) => void;
428
+ error: (text: string) => void;
429
+ }>): Promise<number>;
430
+ export {};