balladeer 1.0.16 → 1.0.18

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 (58) hide show
  1. package/dist/anchor-budget.d.ts +40 -0
  2. package/dist/anchor-budget.js +110 -0
  3. package/dist/anchoring.d.ts +241 -0
  4. package/dist/anchoring.js +766 -0
  5. package/dist/anchors-client.d.ts +62 -0
  6. package/dist/anchors-client.js +197 -0
  7. package/dist/anchors-schema.d.ts +85 -0
  8. package/dist/anchors-schema.js +246 -0
  9. package/dist/cli.d.ts +1 -1
  10. package/dist/cli.js +24 -7
  11. package/dist/commands/anchor-usage.d.ts +6 -0
  12. package/dist/commands/anchor-usage.js +19 -0
  13. package/dist/commands/anchor.d.ts +127 -0
  14. package/dist/commands/anchor.js +303 -0
  15. package/dist/commands/guidance.d.ts +2 -1
  16. package/dist/commands/guidance.js +55 -0
  17. package/dist/commands/judge.d.ts +219 -12
  18. package/dist/commands/judge.js +911 -103
  19. package/dist/commands/map.d.ts +46 -2
  20. package/dist/commands/map.js +244 -5
  21. package/dist/commands/mcp.js +2 -2
  22. package/dist/guidance-hook.mjs +282 -95
  23. package/dist/guidance.d.ts +7 -0
  24. package/dist/guidance.js +10 -1
  25. package/dist/headless-agent.d.ts +19 -1
  26. package/dist/headless-agent.js +22 -5
  27. package/dist/hook-trust.d.ts +41 -9
  28. package/dist/hook-trust.js +98 -16
  29. package/dist/judge-brief.d.ts +33 -3
  30. package/dist/judge-brief.js +39 -2
  31. package/dist/judge-hook.d.ts +188 -14
  32. package/dist/judge-hook.js +919 -61
  33. package/dist/judge-said.d.ts +52 -0
  34. package/dist/judge-said.js +181 -0
  35. package/dist/promise-meaning.d.ts +25 -0
  36. package/dist/promise-meaning.js +24 -7
  37. package/dist/relay.d.ts +71 -0
  38. package/dist/relay.js +193 -0
  39. package/dist/remove-earlier.js +4 -2
  40. package/dist/risk/git.d.ts +3 -1
  41. package/dist/risk/git.js +3 -3
  42. package/dist/risk/graph-cache.d.ts +83 -0
  43. package/dist/risk/graph-cache.js +291 -0
  44. package/dist/risk/import-graph.d.ts +43 -0
  45. package/dist/risk/import-graph.js +88 -34
  46. package/dist/risk/index.d.ts +1 -1
  47. package/dist/risk/index.js +1 -1
  48. package/dist/risk/pipeline.d.ts +13 -0
  49. package/dist/risk/pipeline.js +15 -4
  50. package/dist/risk/score.d.ts +10 -0
  51. package/dist/risk/score.js +11 -2
  52. package/dist/scratch-worktree.d.ts +5 -1
  53. package/dist/scratch-worktree.js +9 -2
  54. package/dist/user-scope.d.ts +74 -3
  55. package/dist/user-scope.js +270 -9
  56. package/dist/wire.d.ts +19 -3
  57. package/dist/wire.js +2 -2
  58. package/package.json +7 -2
@@ -1,7 +1,13 @@
1
+ import type { AnchorBudget } from "../anchor-budget.js";
2
+ import type { AnchorsClient } from "../anchors-client.js";
1
3
  import { type BehaviorMap } from "../behavior-map-schema.js";
2
4
  import { type HeadlessAgentOptions, type HeadlessAgentResult, type HeadlessClient, type LoginCheck } from "../headless-agent.js";
3
- import { type HookHost } from "../judge-hook.js";
5
+ import { type HookHost, type MergeTarget } from "../judge-hook.js";
4
6
  import { type PromiseDocument, type PromiseReader } from "../promise-meaning.js";
7
+ import type { RepositoryGraph } from "../risk/graph-cache.js";
8
+ import { type CarriedFile } from "../scratch-worktree.js";
9
+ import type { Features } from "../risk/contract.js";
10
+ import type { AnchorStage, JudgeAnchors } from "./anchor.js";
5
11
  /**
6
12
  * `balladeer judge`: for a change about to leave this machine, ask the person's
7
13
  * own coding agent, one promise at a time, whether the change keeps it.
@@ -18,7 +24,15 @@ export declare const JUDGE_VERDICT_SCHEMA_VERSION = "balladeer-judge-verdict/v1"
18
24
  export declare const JUDGE_CONFIG_SCHEMA_VERSION = "balladeer-judge-config/v1";
19
25
  export declare const JUDGE_CONFIG_PATH = ".continuity/judge.json";
20
26
  export declare const JUDGE_LOG_DIRECTORY = ".continuity/judge-log";
27
+ /**
28
+ * Where a verified reproduction's files are kept, one folder per promise and
29
+ * pushed commit, inside the judge log so git ignores them like the log itself.
30
+ */
31
+ export declare const KEPT_REPRODUCTIONS_DIRECTORY = ".continuity/judge-log/reproductions";
32
+ export declare const KEPT_REPRODUCTION_SCHEMA_VERSION = "balladeer-kept-reproduction/v1";
21
33
  export declare const STILL_JUDGING = "still judging; rerun `balladeer judge --wait`";
34
+ /** Block mode's answer to a line that commits and then pushes: the check runs before the line. */
35
+ 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.";
22
36
  export type JudgeConfig = Readonly<{
23
37
  schemaVersion: typeof JUDGE_CONFIG_SCHEMA_VERSION;
24
38
  mode: "report" | "block";
@@ -28,6 +42,13 @@ export type JudgeConfig = Readonly<{
28
42
  sampleBelowLine: number;
29
43
  alwaysJudge: readonly string[];
30
44
  client: HeadlessClient | "auto";
45
+ /**
46
+ * Which promises the risk report hands to a judge: "direct" (the default)
47
+ * takes every promise whose own code or tests the change edits, and nothing
48
+ * else; "ranked" takes the top `topK` in the report's order plus a random
49
+ * sample below that line.
50
+ */
51
+ select: "direct" | "ranked";
31
52
  /**
32
53
  * From the push hook, also read the change for rules no promise records and
33
54
  * hand at most three to the pushing session as offers. Off unless set.
@@ -64,10 +85,25 @@ export type JudgeVerdict = {
64
85
  failsOnHead: boolean;
65
86
  passesOnBase: boolean;
66
87
  digest: string;
88
+ /**
89
+ * Where the reproduction was kept on this machine, repository-relative:
90
+ * a folder holding `manifest.json` and, under `files/`, the files the
91
+ * judge added, at the paths it wrote them. Only for a break verified both
92
+ * ways, and absent when keeping it failed.
93
+ */
94
+ keptIn?: string;
95
+ /** The kept files' repository-relative paths, each under `<keptIn>/files/`. */
96
+ keptFiles?: string[];
67
97
  } | null;
68
98
  reproductionRejected: string | null;
69
99
  exercisedCases: string[];
70
100
  notes: string;
101
+ /**
102
+ * On a could-not-tell the judge itself reached because the tests need
103
+ * something this machine lacks: the one step that would let them run, as
104
+ * the judge wrote it. Absent otherwise. It never changes the verdict.
105
+ */
106
+ nextStep?: string;
71
107
  judge: {
72
108
  client: HeadlessClient;
73
109
  model: string;
@@ -77,6 +113,8 @@ export type JudgeVerdict = {
77
113
  };
78
114
  briefRevision: string;
79
115
  mapUsed: boolean;
116
+ /** The judge was handed the promise's anchors and their neighborhood, where it had no map. */
117
+ anchorsUsed?: boolean;
80
118
  linkedTestRun: {
81
119
  file: string;
82
120
  outcome: "pass" | "fail" | "error" | "none";
@@ -101,6 +139,8 @@ export type ReproductionCheck = Readonly<{
101
139
  rejected: string | null;
102
140
  /** The budget ran out while it was being run, so there is no answer yet. */
103
141
  stopped: boolean;
142
+ /** The files the judge added, carried to base and fingerprinted into `digest`. */
143
+ files: readonly CarriedFile[];
104
144
  }>;
105
145
  /**
106
146
  * Run a judge's reproduction both ways.
@@ -123,6 +163,42 @@ export declare function verifyReproduction(input: Readonly<{
123
163
  signal?: AbortSignal;
124
164
  scratch?: string;
125
165
  }>): Promise<ReproductionCheck>;
166
+ export type KeptReproduction = Readonly<{
167
+ /** Repository-relative, forward slashes: the folder under the judge log. */
168
+ keptIn: string;
169
+ /** The files kept, repository-relative, each at `<keptIn>/files/<path>`. */
170
+ files: string[];
171
+ }>;
172
+ /**
173
+ * Keep a verified reproduction on this machine, so the break can become a
174
+ * lasting test instead of vanishing with the judge's checkout.
175
+ *
176
+ * The files the judge added are copied from its checkout into
177
+ * `.continuity/judge-log/reproductions/<promise id>-<first 12 of head>/files/`
178
+ * at the paths the judge wrote them, beside a `manifest.json` that says which
179
+ * promise, meaning and change they reproduce a break of, with the command,
180
+ * its cwd, the digest and each file's own fingerprint. The digest can be
181
+ * recomputed from the manifest alone. A reproduction that added no files
182
+ * keeps just the manifest. The judge log carries its own `.gitignore`, so
183
+ * nothing here is ever committed, and nothing here is sent anywhere.
184
+ *
185
+ * The folder is written beside its final name and then renamed into place,
186
+ * replacing an earlier one for the same promise and commit, so a reader never
187
+ * sees half of one. Keeping is a courtesy to the person, never a condition of
188
+ * the verdict: when it fails, the verdict stands and says nothing was kept.
189
+ */
190
+ export declare function keepReproduction(input: Readonly<{
191
+ root: string;
192
+ headTree: string;
193
+ promiseId: string;
194
+ semanticDigest: string;
195
+ base: string;
196
+ head: string;
197
+ command: string;
198
+ cwd: string;
199
+ digest: string;
200
+ files: readonly CarriedFile[];
201
+ }>): KeptReproduction | undefined;
126
202
  /**
127
203
  * How this repository runs one test file, when it can be told from the files
128
204
  * alone. A runner that cannot be told is `none`, and the judge is left to find
@@ -137,13 +213,19 @@ type JudgeAnswer = Readonly<{
137
213
  }>;
138
214
  exercisedCases: string[];
139
215
  notes: string;
216
+ nextStep?: string;
140
217
  }>;
218
+ /** The longest `nextStep` kept from a judge's answer, in characters. */
219
+ export declare const NEXT_STEP_LIMIT = 500;
141
220
  /** The judge's final JSON, read strictly enough that a malformed answer is not a verdict. */
142
221
  export declare function readJudgeAnswer(json: Record<string, unknown> | undefined): JudgeAnswer | undefined;
222
+ export type { JudgeAnchors } from "./anchor.js";
143
223
  export type JudgePromiseInput = Readonly<{
144
224
  root: string;
145
225
  promise: PromiseDocument;
146
226
  map?: BehaviorMap;
227
+ /** Handed only when there is no map. */
228
+ anchors?: JudgeAnchors;
147
229
  base: string;
148
230
  head: string;
149
231
  changedFiles: readonly string[];
@@ -180,6 +262,10 @@ export type JudgeArguments = Readonly<{
180
262
  mode?: "report" | "block";
181
263
  wait: boolean;
182
264
  hook?: HookHost;
265
+ /** Run by the entry setup writes at the host's user scope, which stands aside where it has nothing to do. */
266
+ userScope?: true;
267
+ /** Turn this laptop's user-scope push check on or off. */
268
+ pushCheck?: "on" | "off";
183
269
  installHook?: HookHost | "agents";
184
270
  fromFile?: string;
185
271
  map?: string;
@@ -188,13 +274,20 @@ export type JudgeArguments = Readonly<{
188
274
  repository?: string;
189
275
  controlPlane: string;
190
276
  }>;
191
- 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 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 the top of `balladeer risk`, else every mapped promise, plus\n .continuity/judge.json's alwaysJudge and a small random sample. Verdicts\n are kept in .continuity/judge-log/, which git ignores. --install-hook\n adds the push hook to .claude/settings.json and .codex/hooks.json, or\n .git/hooks/pre-push with git. With \"offers\": true in that file, the push\n hook also reads the change for rules no promise records and offers at\n most three to whoever is pushing; it records nothing and never holds a\n push (see `balladeer offers`).\n";
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 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 every 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 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. --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 agreed 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";
192
278
  export declare function parseJudgeArguments(argv: readonly string[]): JudgeArguments;
193
- /** The subset of `balladeer-risk/v1` this command reads. */
279
+ /**
280
+ * The subset of `balladeer-risk/v1` this command reads: enough to order the
281
+ * entries the way `balladeer risk` does. An entry without `features` reads as
282
+ * reaching its promise not at all, and one without `evidence` as none.
283
+ */
194
284
  export type RiskEntry = Readonly<{
195
285
  promiseId: string;
196
286
  score: number;
197
287
  band: string;
288
+ features?: Features;
289
+ /** The total weight of the entry's reasons. */
290
+ evidence?: number;
198
291
  }>;
199
292
  /** Read a risk report out of whatever `balladeer risk --json` printed. */
200
293
  export declare function readRiskOutput(stdout: string): RiskEntry[] | undefined;
@@ -206,18 +299,39 @@ export declare function defaultBase(root: string, head: string): Promise<string
206
299
  export declare function changedFiles(root: string, base: string, head: string): Promise<string[]>;
207
300
  export type Selection = Readonly<{
208
301
  promiseId: string;
209
- why: "file" | "named" | "risk" | "sample" | "mapped" | "always";
302
+ why: "file" | "named" | "risk" | "anchored" | "sample" | "mapped" | "always";
210
303
  }>;
211
304
  /**
212
- * Which promises to judge: named ones, else the top of the risk report plus a
213
- * random sample below its line, else every mapped promise; then `alwaysJudge`.
214
- * The sample is what keeps the ranking honest: a break found below the line is
215
- * a miss the ranking made, counted rather than never seen.
305
+ * Which promises to judge: named ones; else the promises the change selects
306
+ * by their anchors and by their maps; then `alwaysJudge`.
307
+ *
308
+ * By anchors (`anchored`, chosen before this from the files the change edits):
309
+ * every promise one of whose anchor files (an entry point's or a symbol's
310
+ * file) the change edits, with no widening by import distance. The
311
+ * measurement of 1 October 2026 found that widening multiplied the promises
312
+ * selected and caught nothing more.
313
+ *
314
+ * By maps, from the risk report, in the order `balladeer risk` lists it (how
315
+ * directly the change reaches each promise, then score, then evidence, then
316
+ * id; a score alone ties at 1 and falls to the alphabet):
317
+ *
318
+ * - "direct" takes every mapped promise whose own code or tests the change
319
+ * edits (directness tier 3), and nothing else. Only a `--top` given by hand
320
+ * caps it, and it caps anchored and mapped promises together.
321
+ * - "ranked" takes the top k mapped promises with a score above zero, plus a
322
+ * random sample below that line. The sample is what keeps a ranking honest:
323
+ * a break found below the line is a miss the ranking made, counted rather
324
+ * than never seen. Anchored promises are taken too, before the sample.
325
+ *
326
+ * Without a risk report, every mapped promise. Anchored and mapped promises
327
+ * are listed together in the report's order, and one the report does not
328
+ * list comes after those it does.
216
329
  */
217
330
  export declare function selectPromises(input: Readonly<{
218
331
  named?: readonly string[];
219
332
  risk?: readonly RiskEntry[];
220
333
  mapped: readonly string[];
334
+ anchored?: readonly string[];
221
335
  config: JudgeConfig;
222
336
  topK?: number;
223
337
  random: () => number;
@@ -234,14 +348,55 @@ export declare function findLoggedVerdict(root: string, key: Readonly<{
234
348
  base: string;
235
349
  head: string;
236
350
  }>, now: Date): JudgeVerdict | undefined;
237
- /** One line per verdict, the promise named by its title and id. */
238
- export declare function verdictLine(verdict: JudgeVerdict, title: string, reused?: boolean): string;
351
+ /** Said after "fix the behavior": a deliberate change is the person's, and the promise its owner's. */
352
+ 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
+ export type VerdictLineContext = Readonly<{
354
+ /** The verdict was reached earlier for this same commit and read back from the log. */
355
+ reused?: boolean;
356
+ /**
357
+ * Whether the promise has a Balladeer check bound, as its connection said.
358
+ * Absent when that is unknown (a promise from a file or a map's excerpt),
359
+ * and then the line says nothing about a check rather than guessing.
360
+ */
361
+ checkBound?: boolean;
362
+ }>;
363
+ /**
364
+ * What to do after a reproduced break, as one line an agent reads in order:
365
+ * where the test that shows it is kept, fix the behavior rather than the
366
+ * test, then make the break a lasting check in this repository's own tests,
367
+ * committed with the fix and linked to the promise's map, and tell the person
368
+ * in one line what was added. When the promise's own Balladeer check missed
369
+ * the break, a repair of that check is proposed to its owner as well.
370
+ */
371
+ export declare function afterBreakLine(verdict: JudgeVerdict, checkBound?: boolean): string;
372
+ /**
373
+ * The verdict as it may be told now: a reproduction said to be kept whose
374
+ * folder is no longer on this machine (a verdict read back from the log after
375
+ * somebody cleared it) loses its kept fields, so the line never points at a
376
+ * file that is not there.
377
+ */
378
+ export declare function stillKept(root: string, verdict: JudgeVerdict): JudgeVerdict;
379
+ /**
380
+ * One line per verdict, the promise named by its title and id. A broken one
381
+ * carries a second, indented line saying what to do next.
382
+ */
383
+ export declare function verdictLine(verdict: JudgeVerdict, title: string, context?: VerdictLineContext): string;
239
384
  export type JudgeDependencies = Readonly<{
240
385
  runAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
241
386
  resolveLogin?: (requested: HeadlessClient | "auto", prefer?: HeadlessClient) => Promise<LoginCheck | undefined>;
242
- riskReport?: (root: string, base: string, head: string) => Promise<readonly RiskEntry[] | undefined>;
387
+ riskReport?: (root: string, base: string, head: string,
388
+ /** The maps in use and, in a map's shape, the anchors of the promises they select. */
389
+ maps: readonly BehaviorMap[]) => Promise<readonly RiskEntry[] | undefined>;
243
390
  /** `null` means "no connection", without trying to find one. */
244
391
  reader?: PromiseReader | null;
392
+ /** The anchors route; `null` means none, without trying to find one. */
393
+ anchors?: AnchorsClient | null;
394
+ /** The anchoring call, when it is not the person's own agent. */
395
+ anchorAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
396
+ /** The repository graph at a commit, when it is not built from this laptop's cache. */
397
+ graph?: (root: string, head: string) => RepositoryGraph;
398
+ /** Today's anchoring budget, when it is not this laptop's own. */
399
+ anchorBudget?: AnchorBudget;
245
400
  random?: () => number;
246
401
  now?: () => Date;
247
402
  stdin?: () => Promise<string>;
@@ -250,7 +405,15 @@ export type JudgeDependencies = Readonly<{
250
405
  judge?: (input: JudgePromiseInput) => Promise<JudgeOutcome>;
251
406
  /** The agent run that reads the change for offers, when they are on. */
252
407
  offerAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
408
+ /** The head commit of the pull request a `gh pr merge` names. */
409
+ pullRequestHead?: (root: string, target: MergeTarget) => Promise<string | undefined>;
410
+ /** Whether this laptop has a connection for the checkout, read locally. */
411
+ connected?: (root: string) => boolean;
253
412
  }>;
413
+ /** Said instead of verdicts when the check cannot tell which repository a push leaves from. */
414
+ export declare const PUSH_REPOSITORY_UNKNOWN = "Balladeer judge: this push was not judged, because the check could not tell which repository it pushes.";
415
+ /** Said instead of verdicts when every pull request the command opens or merges is in another repository. */
416
+ export declare function pullRequestElsewhereLine(repo: string): string;
254
417
  export type JudgeRunOptions = Readonly<{
255
418
  args: JudgeArguments;
256
419
  cwd: string;
@@ -259,7 +422,52 @@ export type JudgeRunOptions = Readonly<{
259
422
  error: (text: string) => void;
260
423
  deps?: JudgeDependencies;
261
424
  }>;
425
+ /** What `--push-check off` and `--push-check on` say. */
426
+ 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`.";
427
+ export declare const PUSH_CHECK_ON_LINE = "The push check is back on for this laptop: after a push in a connected repository with agreed promises, it reports what the push may have broken, and it never holds the push.";
428
+ /**
429
+ * Whether the push check's user-scope entry has this push to itself, decided
430
+ * before anything is started or fetched: the entry runs on every shell command
431
+ * in every folder on the laptop, and most folders have nothing to judge. It
432
+ * stands aside, saying nothing, when the laptop turned the check off, the
433
+ * folder is in no checkout, or the checkout has the push check's own project
434
+ * entry for this host (that entry owns the check there, so a push is judged
435
+ * once). Only then is the connection looked for, which reads the checkout's
436
+ * remotes, and a checkout this laptop has not connected is left alone too.
437
+ *
438
+ * A connected checkout is not left alone for having no behavior map: since
439
+ * promise anchors (approved 30 September 2026), its agreed promises are
440
+ * anchored and checked without one. Whether it has any agreed promise is the
441
+ * server's to say, after the push, and with none (and no map) the entry still
442
+ * says nothing.
443
+ */
444
+ export declare function userScopeTakesThePush(input: {
445
+ cwd: string;
446
+ host: "claude" | "codex";
447
+ environment: NodeJS.ProcessEnv;
448
+ connected: (root: string) => boolean;
449
+ }): boolean;
450
+ /**
451
+ * The one line said when nothing was selected, and its code: no agreed
452
+ * promise here; nothing anchored or mapped (with why, when the anchors could
453
+ * not be read); or nothing the change touches.
454
+ */
455
+ export declare function nothingSelected(input: Readonly<{
456
+ stage: AnchorStage | undefined;
457
+ maps: number;
458
+ anchors: number;
459
+ push: boolean;
460
+ }>): {
461
+ reason: string;
462
+ message: string;
463
+ };
262
464
  /** `balladeer judge`, from arguments already parsed. */
465
+ /**
466
+ * The line an answer opens with when the command makes a commit before it
467
+ * pushes. Robert approved it on 30 September 2026: the check keeps running
468
+ * before the push, and says plainly that it did not see the commit.
469
+ */
470
+ export declare function commitFirstLine(made: string): string;
263
471
  export declare function runJudge(options: JudgeRunOptions): Promise<number>;
264
472
  /** `balladeer judge ...` from raw arguments, as `cli.ts` hands them over. */
265
473
  export declare function judgeCommand(argv: readonly string[], io: Readonly<{
@@ -268,4 +476,3 @@ export declare function judgeCommand(argv: readonly string[], io: Readonly<{
268
476
  write: (text: string) => void;
269
477
  error: (text: string) => void;
270
478
  }>): Promise<number>;
271
- export {};