balladeer 1.0.14 → 1.0.16

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 +5 -0
  5. package/dist/cli.js +77 -2
  6. package/dist/commands/guidance.d.ts +1 -1
  7. package/dist/commands/guidance.js +6 -0
  8. package/dist/commands/judge.d.ts +271 -0
  9. package/dist/commands/judge.js +1170 -0
  10. package/dist/commands/map.d.ts +120 -0
  11. package/dist/commands/map.js +601 -0
  12. package/dist/commands/mcp.d.ts +6 -0
  13. package/dist/commands/mcp.js +53 -0
  14. package/dist/commands/offers.d.ts +265 -0
  15. package/dist/commands/offers.js +801 -0
  16. package/dist/commands/risk.d.ts +29 -0
  17. package/dist/commands/risk.js +133 -0
  18. package/dist/copy.d.ts +24 -1
  19. package/dist/copy.js +91 -0
  20. package/dist/guidance-hook.mjs +230 -74
  21. package/dist/guidance.d.ts +2 -0
  22. package/dist/guidance.js +7 -1
  23. package/dist/headless-agent.d.ts +166 -0
  24. package/dist/headless-agent.js +416 -0
  25. package/dist/hook-trust.d.ts +31 -0
  26. package/dist/hook-trust.js +63 -0
  27. package/dist/judge-brief.d.ts +36 -0
  28. package/dist/judge-brief.js +89 -0
  29. package/dist/judge-hook.d.ts +84 -0
  30. package/dist/judge-hook.js +420 -0
  31. package/dist/map-brief.d.ts +16 -0
  32. package/dist/map-brief.js +41 -0
  33. package/dist/offer-brief.d.ts +95 -0
  34. package/dist/offer-brief.js +217 -0
  35. package/dist/owned-process.d.ts +65 -0
  36. package/dist/owned-process.js +146 -0
  37. package/dist/promise-meaning.d.ts +126 -0
  38. package/dist/promise-meaning.js +292 -0
  39. package/dist/risk/contract.d.ts +145 -0
  40. package/dist/risk/contract.js +74 -0
  41. package/dist/risk/describe.d.ts +7 -0
  42. package/dist/risk/describe.js +45 -0
  43. package/dist/risk/diff.d.ts +26 -0
  44. package/dist/risk/diff.js +174 -0
  45. package/dist/risk/extract.d.ts +43 -0
  46. package/dist/risk/extract.js +336 -0
  47. package/dist/risk/git.d.ts +30 -0
  48. package/dist/risk/git.js +118 -0
  49. package/dist/risk/import-graph.d.ts +41 -0
  50. package/dist/risk/import-graph.js +487 -0
  51. package/dist/risk/index.d.ts +20 -0
  52. package/dist/risk/index.js +20 -0
  53. package/dist/risk/paths.d.ts +16 -0
  54. package/dist/risk/paths.js +73 -0
  55. package/dist/risk/pipeline.d.ts +47 -0
  56. package/dist/risk/pipeline.js +121 -0
  57. package/dist/risk/priors.d.ts +18 -0
  58. package/dist/risk/priors.js +86 -0
  59. package/dist/risk/resources.d.ts +56 -0
  60. package/dist/risk/resources.js +374 -0
  61. package/dist/risk/score.d.ts +85 -0
  62. package/dist/risk/score.js +543 -0
  63. package/dist/risk/symbols.d.ts +35 -0
  64. package/dist/risk/symbols.js +348 -0
  65. package/dist/risk/text.d.ts +43 -0
  66. package/dist/risk/text.js +277 -0
  67. package/dist/risk/validate.d.ts +19 -0
  68. package/dist/risk/validate.js +154 -0
  69. package/dist/scratch-worktree.d.ts +51 -0
  70. package/dist/scratch-worktree.js +153 -0
  71. package/dist/self-update.d.ts +29 -2
  72. package/dist/self-update.js +96 -11
  73. package/dist/user-scope.d.ts +2 -0
  74. package/dist/user-scope.js +26 -2
  75. package/dist/wire.d.ts +55 -2
  76. package/dist/wire.js +5 -1
  77. package/package.json +1 -1
@@ -0,0 +1,271 @@
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 } from "../judge-hook.js";
4
+ import { type PromiseDocument, type PromiseReader } from "../promise-meaning.js";
5
+ /**
6
+ * `balladeer judge`: for a change about to leave this machine, ask the person's
7
+ * own coding agent, one promise at a time, whether the change keeps it.
8
+ *
9
+ * A verdict is only as good as its evidence, so the runner does not take a
10
+ * judge's word for a break. A judge that says "broken" must hand back a command,
11
+ * and the runner runs that command itself, once at head and once in a clean
12
+ * checkout of base: it must fail with the change and pass without it. Anything
13
+ * short of that is "could not tell", with the reason kept. Nothing here ever
14
+ * says a promise is protected; a judge's "kept" is a local reading of one
15
+ * change, not a check that holds.
16
+ */
17
+ export declare const JUDGE_VERDICT_SCHEMA_VERSION = "balladeer-judge-verdict/v1";
18
+ export declare const JUDGE_CONFIG_SCHEMA_VERSION = "balladeer-judge-config/v1";
19
+ export declare const JUDGE_CONFIG_PATH = ".continuity/judge.json";
20
+ export declare const JUDGE_LOG_DIRECTORY = ".continuity/judge-log";
21
+ export declare const STILL_JUDGING = "still judging; rerun `balladeer judge --wait`";
22
+ export type JudgeConfig = Readonly<{
23
+ schemaVersion: typeof JUDGE_CONFIG_SCHEMA_VERSION;
24
+ mode: "report" | "block";
25
+ topK: number;
26
+ concurrency: number;
27
+ budgetSeconds: number;
28
+ sampleBelowLine: number;
29
+ alwaysJudge: readonly string[];
30
+ client: HeadlessClient | "auto";
31
+ /**
32
+ * From the push hook, also read the change for rules no promise records and
33
+ * hand at most three to the pushing session as offers. Off unless set.
34
+ */
35
+ offers: boolean;
36
+ /**
37
+ * Which pushes are read when offers are on: only those that repair
38
+ * something ("fixes", the default), or every change ("all").
39
+ */
40
+ offersOn: "fixes" | "all";
41
+ /** How many offers one push may hand to the person: one by default, three at most. */
42
+ maxOffers: number;
43
+ }>;
44
+ export declare const DEFAULT_JUDGE_CONFIG: JudgeConfig;
45
+ /**
46
+ * `.continuity/judge.json`, with the contract's defaults for anything absent.
47
+ * A field that is present and wrong keeps its default and is named, so a typo
48
+ * in a team's config is visible rather than quietly ignored.
49
+ */
50
+ export declare function readJudgeConfig(root: string): {
51
+ config: JudgeConfig;
52
+ problems: string[];
53
+ };
54
+ export type JudgeVerdict = {
55
+ schemaVersion: typeof JUDGE_VERDICT_SCHEMA_VERSION;
56
+ promiseId: string;
57
+ semanticDigest: string;
58
+ base: string;
59
+ head: string;
60
+ verdict: "kept" | "broken" | "could_not_tell";
61
+ reproduction: {
62
+ command: string;
63
+ cwd: string;
64
+ failsOnHead: boolean;
65
+ passesOnBase: boolean;
66
+ digest: string;
67
+ } | null;
68
+ reproductionRejected: string | null;
69
+ exercisedCases: string[];
70
+ notes: string;
71
+ judge: {
72
+ client: HeadlessClient;
73
+ model: string;
74
+ durationMs: number;
75
+ inputTokens: number;
76
+ outputTokens: number;
77
+ };
78
+ briefRevision: string;
79
+ mapUsed: boolean;
80
+ linkedTestRun: {
81
+ file: string;
82
+ outcome: "pass" | "fail" | "error" | "none";
83
+ };
84
+ };
85
+ /** A command's exit, as one run of a reproduction or a linked test. */
86
+ export type CommandRun = Readonly<{
87
+ exitCode: number | null;
88
+ timedOut: boolean;
89
+ aborted: boolean;
90
+ output: string;
91
+ }>;
92
+ export declare function runShell(command: string, cwd: string, options: Readonly<{
93
+ env: NodeJS.ProcessEnv;
94
+ timeoutMs: number;
95
+ signal?: AbortSignal;
96
+ }>): Promise<CommandRun>;
97
+ export type ReproductionCheck = Readonly<{
98
+ failsOnHead: boolean;
99
+ passesOnBase: boolean;
100
+ digest: string;
101
+ rejected: string | null;
102
+ /** The budget ran out while it was being run, so there is no answer yet. */
103
+ stopped: boolean;
104
+ }>;
105
+ /**
106
+ * Run a judge's reproduction both ways.
107
+ *
108
+ * At head, in the judge's own checkout, reset to head so a judge that left it
109
+ * on base cannot fool the check. At base, in a fresh checkout of base, with the
110
+ * files the judge added carried across so a new test exists on both sides. It
111
+ * must exit non-zero at head and zero at base; either disagreeing rejects it,
112
+ * and the reason says which.
113
+ */
114
+ export declare function verifyReproduction(input: Readonly<{
115
+ root: string;
116
+ headTree: string;
117
+ head: string;
118
+ base: string;
119
+ command: string;
120
+ cwd: string;
121
+ env: NodeJS.ProcessEnv;
122
+ timeoutMs?: number;
123
+ signal?: AbortSignal;
124
+ scratch?: string;
125
+ }>): Promise<ReproductionCheck>;
126
+ /**
127
+ * How this repository runs one test file, when it can be told from the files
128
+ * alone. A runner that cannot be told is `none`, and the judge is left to find
129
+ * its own way.
130
+ */
131
+ export declare function linkedTestCommand(tree: string, file: string): string | undefined;
132
+ type JudgeAnswer = Readonly<{
133
+ verdict: JudgeVerdict["verdict"];
134
+ reproduction?: Readonly<{
135
+ command: string;
136
+ cwd: string;
137
+ }>;
138
+ exercisedCases: string[];
139
+ notes: string;
140
+ }>;
141
+ /** The judge's final JSON, read strictly enough that a malformed answer is not a verdict. */
142
+ export declare function readJudgeAnswer(json: Record<string, unknown> | undefined): JudgeAnswer | undefined;
143
+ export type JudgePromiseInput = Readonly<{
144
+ root: string;
145
+ promise: PromiseDocument;
146
+ map?: BehaviorMap;
147
+ base: string;
148
+ head: string;
149
+ changedFiles: readonly string[];
150
+ login: LoginCheck;
151
+ env: NodeJS.ProcessEnv;
152
+ timeoutMs?: number;
153
+ signal?: AbortSignal;
154
+ scratch?: string;
155
+ runAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
156
+ }>;
157
+ export type JudgeOutcome = Readonly<{
158
+ status: "judged";
159
+ verdict: JudgeVerdict;
160
+ }> | Readonly<{
161
+ status: "stopped";
162
+ }> | Readonly<{
163
+ status: "not_judged";
164
+ reason: string;
165
+ }>;
166
+ /**
167
+ * One promise judged against one change, with its reproduction verified.
168
+ *
169
+ * Returns `stopped` when the caller's budget ran out first, which is not a
170
+ * verdict and is never logged as one: the push is either held or told the
171
+ * judge is still running, never let through as if it had been judged.
172
+ */
173
+ export declare function judgePromise(input: JudgePromiseInput): Promise<JudgeOutcome>;
174
+ export type JudgeArguments = Readonly<{
175
+ promises?: readonly string[];
176
+ topK?: number;
177
+ base?: string;
178
+ head?: string;
179
+ json: boolean;
180
+ mode?: "report" | "block";
181
+ wait: boolean;
182
+ hook?: HookHost;
183
+ installHook?: HookHost | "agents";
184
+ fromFile?: string;
185
+ map?: string;
186
+ repo?: string;
187
+ client?: HeadlessClient | "auto";
188
+ repository?: string;
189
+ controlPlane: string;
190
+ }>;
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";
192
+ export declare function parseJudgeArguments(argv: readonly string[]): JudgeArguments;
193
+ /** The subset of `balladeer-risk/v1` this command reads. */
194
+ export type RiskEntry = Readonly<{
195
+ promiseId: string;
196
+ score: number;
197
+ band: string;
198
+ }>;
199
+ /** Read a risk report out of whatever `balladeer risk --json` printed. */
200
+ export declare function readRiskOutput(stdout: string): RiskEntry[] | undefined;
201
+ /**
202
+ * The commit a change is measured from: the fork point with the remote default
203
+ * branch, else with this branch's upstream, else the parent of head.
204
+ */
205
+ export declare function defaultBase(root: string, head: string): Promise<string | undefined>;
206
+ export declare function changedFiles(root: string, base: string, head: string): Promise<string[]>;
207
+ export type Selection = Readonly<{
208
+ promiseId: string;
209
+ why: "file" | "named" | "risk" | "sample" | "mapped" | "always";
210
+ }>;
211
+ /**
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.
216
+ */
217
+ export declare function selectPromises(input: Readonly<{
218
+ named?: readonly string[];
219
+ risk?: readonly RiskEntry[];
220
+ mapped: readonly string[];
221
+ config: JudgeConfig;
222
+ topK?: number;
223
+ random: () => number;
224
+ }>): Selection[];
225
+ /**
226
+ * Append verdicts to today's log. The directory carries its own `.gitignore`,
227
+ * so a verdict log never lands in a commit whatever the repository ignores.
228
+ */
229
+ export declare function appendVerdicts(root: string, verdicts: readonly JudgeVerdict[], now: Date): void;
230
+ /** A verdict already reached for exactly this promise, meaning, change and brief. */
231
+ export declare function findLoggedVerdict(root: string, key: Readonly<{
232
+ promiseId: string;
233
+ semanticDigest: string;
234
+ base: string;
235
+ head: string;
236
+ }>, 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;
239
+ export type JudgeDependencies = Readonly<{
240
+ runAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
241
+ resolveLogin?: (requested: HeadlessClient | "auto", prefer?: HeadlessClient) => Promise<LoginCheck | undefined>;
242
+ riskReport?: (root: string, base: string, head: string) => Promise<readonly RiskEntry[] | undefined>;
243
+ /** `null` means "no connection", without trying to find one. */
244
+ reader?: PromiseReader | null;
245
+ random?: () => number;
246
+ now?: () => Date;
247
+ stdin?: () => Promise<string>;
248
+ scratch?: string;
249
+ /** Replaces the judge itself, for tests of the flow around it. */
250
+ judge?: (input: JudgePromiseInput) => Promise<JudgeOutcome>;
251
+ /** The agent run that reads the change for offers, when they are on. */
252
+ offerAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
253
+ }>;
254
+ export type JudgeRunOptions = Readonly<{
255
+ args: JudgeArguments;
256
+ cwd: string;
257
+ environment: NodeJS.ProcessEnv;
258
+ write: (text: string) => void;
259
+ error: (text: string) => void;
260
+ deps?: JudgeDependencies;
261
+ }>;
262
+ /** `balladeer judge`, from arguments already parsed. */
263
+ export declare function runJudge(options: JudgeRunOptions): Promise<number>;
264
+ /** `balladeer judge ...` from raw arguments, as `cli.ts` hands them over. */
265
+ export declare function judgeCommand(argv: readonly string[], io: Readonly<{
266
+ cwd: string;
267
+ environment: NodeJS.ProcessEnv;
268
+ write: (text: string) => void;
269
+ error: (text: string) => void;
270
+ }>): Promise<number>;
271
+ export {};