@tokenfactory/acc-runner 0.14.0 → 0.16.0

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 CHANGED
@@ -238,6 +238,35 @@ remain uncovered by its budget/backoff. The client is the sanctioned path
238
238
  for any future **direct** REST call (`fetch` against `api.github.com`) the
239
239
  runner adds.
240
240
 
241
+ ## Conflict-resolution integrity guardrail (v0.57, T-57-2)
242
+
243
+ When a Claude/worktree task whose job is to **resolve a merge conflict**
244
+ finishes, the runner does not trust the agent's self-reported gate results.
245
+ Before the resolution is pushed, `src/runtime/resolution-integrity.ts`
246
+ re-verifies it from the runner's own vantage point. The invariant —
247
+ **a conflict resolution is shippable only if all three hold**:
248
+
249
+ 1. **Zero conflict markers** remain in any tracked file
250
+ (`<<<<<<<` / `=======` / `>>>>>>>`).
251
+ 2. **`pnpm typecheck` passes** (or the task's `typecheck_cmd`).
252
+ 3. **No version regression**: if a `package.json` `"version"` changed in the
253
+ resolution it must move **forward** (semver-greater than the base ref,
254
+ never backward) **and** `CHANGELOG.md` must carry a matching entry for the
255
+ new version.
256
+
257
+ On ANY failure the resolution is **rejected**: the runner does not push and
258
+ does not mark the task resolved. The task transitions to `failed` (so it is
259
+ reworked, not merged broken), an `append_task_event` of kind
260
+ `resolution_integrity_failed` records the offending assertion(s), and a
261
+ `conflict.integrity_rejected` activity event surfaces it to the operator. A
262
+ guardrail that itself errors **fails closed** (rejects). The check is a no-op
263
+ for every non-conflict task — it is gated by `isConflictResolutionTask`.
264
+
265
+ This exists because v0.55–v0.56 agent resolutions repeatedly shipped broken
266
+ merges that were still marked "resolved": stale-variable splices
267
+ (task-runner.ts), duplicate declarations (health.ts), and a silently dropped
268
+ version bump (#496 reverted `package.json` 0.14.0 → 0.13.0).
269
+
241
270
  ## Security notes
242
271
 
243
272
  - The CLI never reads or writes your git, gh, or Anthropic credentials.
@@ -1,20 +1,32 @@
1
1
  /**
2
- * v0.48: classify a non-zero claude CLI exit into one of four categories
2
+ * v0.48: classify a non-zero claude CLI exit into one of five categories
3
3
  * so the watch loop can distinguish machine-level failures (which should
4
4
  * quarantine the runner) from genuine task errors (which count toward the
5
- * task's retry budget).
5
+ * task's retry budget) and from a spent capacity window (which should
6
+ * PAUSE, not quarantine).
6
7
  *
7
- * The four classes:
8
- * usage_limit — Claude/Anthropic plan limit or rate limit reached.
8
+ * The five classes:
9
+ * capacity_exhausted — Claude/Anthropic session or usage limit reached
10
+ * (api_error_status 429 + a "resets <time>" message).
11
+ * A NEW v0.56 (T-56-1) retryable-infra class: capacity is
12
+ * a resource, not a failure. NEVER task_error and NEVER
13
+ * quarantine — the runner pauses claiming until the stated
14
+ * reset time (or a default backoff) then resumes with a
15
+ * single probe. Root cause of the 2026-06-05 brownout.
16
+ * usage_limit — A hard Claude/Anthropic plan limit that will NOT reset
17
+ * on its own soon (out of credits, monthly spend cap,
18
+ * quota exhausted). Quarantine is still the right response
19
+ * — an operator has to top up or wait out the billing cycle.
9
20
  * auth_expired — API key invalid or session expired on the user's machine.
10
21
  * env_broken — Host-side environment failure (dyld, missing binary, OOM).
11
22
  * Root cause of the pd36 claim-and-fail storm (INCIDENT_2026-06-02).
12
23
  * task_error — Everything else; genuine task-level failure.
13
24
  *
14
- * Only task_error counts toward a task's retry budget. The other three
15
- * trigger runner quarantine in watch.ts.
25
+ * Only task_error counts toward a task's retry budget. usage_limit /
26
+ * auth_expired / env_broken trigger runner quarantine in watch.ts;
27
+ * capacity_exhausted triggers the pause/resume path instead.
16
28
  */
17
- export type ClaudeExitClass = "usage_limit" | "auth_expired" | "env_broken" | "task_error";
29
+ export type ClaudeExitClass = "capacity_exhausted" | "usage_limit" | "auth_expired" | "env_broken" | "task_error";
18
30
  export interface ClassifiedExit {
19
31
  exitCode: number | null;
20
32
  class: ClaudeExitClass;
@@ -33,6 +45,32 @@ export interface ClassifiedExit {
33
45
  */
34
46
  heuristic?: boolean;
35
47
  }
48
+ /**
49
+ * v0.56 (T-56-1): pull `api_error_status` out of claude's
50
+ * `--output-format=json` envelope. On a session/usage limit the CLI exits
51
+ * 1 with a JSON object carrying `{ "is_error": true, "api_error_status":
52
+ * 429, "result": "You've hit your session limit - resets 3:20pm
53
+ * (Asia/Calcutta)" }`. Returns the numeric status when present, else null.
54
+ * Kept self-contained (a local parse, no cost-pricing import) so the
55
+ * classifier stays a pure (strings) → class function for unit tests.
56
+ */
57
+ export declare function parseApiErrorStatus(stdout: string): number | null;
58
+ /**
59
+ * v0.56 (T-56-1): parse the reset time Claude states when it pauses a
60
+ * session ("...resets 3:20pm (Asia/Calcutta)") into an absolute epoch
61
+ * (ms). The runner schedules its capacity resume off this so it wakes
62
+ * exactly when the window reopens rather than guessing a fixed backoff.
63
+ *
64
+ * - 12h ("3:20pm") and 24h ("15:20") clock forms are both accepted.
65
+ * - A parenthetical IANA zone ("(Asia/Calcutta)") is honoured; without
66
+ * one the time is read in the runner's local zone.
67
+ * - The soonest FUTURE occurrence wins: a stated time already past today
68
+ * rolls to tomorrow.
69
+ *
70
+ * Returns null when no reset time is present or the zone is unparseable —
71
+ * the caller then falls back to a default backoff.
72
+ */
73
+ export declare function extractResetTime(text: string, now?: number): number | null;
36
74
  /**
37
75
  * Classify a non-zero claude exit.
38
76
  *
@@ -1 +1 @@
1
- {"version":3,"file":"failure-classifier.d.ts","sourceRoot":"","sources":["../src/failure-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,eAAe,GACvB,aAAa,GACb,cAAc,GACd,YAAY,GACZ,YAAY,CAAC;AAEjB,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,eAAe,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AA0DD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,cAAc,CA8DhB"}
1
+ {"version":3,"file":"failure-classifier.d.ts","sourceRoot":"","sources":["../src/failure-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,MAAM,eAAe,GACvB,oBAAoB,GACpB,aAAa,GACb,cAAc,GACd,YAAY,GACZ,YAAY,CAAC;AAEjB,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,eAAe,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AA0ED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYjE;AA2DD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,GAAG,IAAI,CAiCtF;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,cAAc,CA2EhB"}
@@ -1,19 +1,46 @@
1
1
  /**
2
- * v0.48: classify a non-zero claude CLI exit into one of four categories
2
+ * v0.48: classify a non-zero claude CLI exit into one of five categories
3
3
  * so the watch loop can distinguish machine-level failures (which should
4
4
  * quarantine the runner) from genuine task errors (which count toward the
5
- * task's retry budget).
5
+ * task's retry budget) and from a spent capacity window (which should
6
+ * PAUSE, not quarantine).
6
7
  *
7
- * The four classes:
8
- * usage_limit — Claude/Anthropic plan limit or rate limit reached.
8
+ * The five classes:
9
+ * capacity_exhausted — Claude/Anthropic session or usage limit reached
10
+ * (api_error_status 429 + a "resets <time>" message).
11
+ * A NEW v0.56 (T-56-1) retryable-infra class: capacity is
12
+ * a resource, not a failure. NEVER task_error and NEVER
13
+ * quarantine — the runner pauses claiming until the stated
14
+ * reset time (or a default backoff) then resumes with a
15
+ * single probe. Root cause of the 2026-06-05 brownout.
16
+ * usage_limit — A hard Claude/Anthropic plan limit that will NOT reset
17
+ * on its own soon (out of credits, monthly spend cap,
18
+ * quota exhausted). Quarantine is still the right response
19
+ * — an operator has to top up or wait out the billing cycle.
9
20
  * auth_expired — API key invalid or session expired on the user's machine.
10
21
  * env_broken — Host-side environment failure (dyld, missing binary, OOM).
11
22
  * Root cause of the pd36 claim-and-fail storm (INCIDENT_2026-06-02).
12
23
  * task_error — Everything else; genuine task-level failure.
13
24
  *
14
- * Only task_error counts toward a task's retry budget. The other three
15
- * trigger runner quarantine in watch.ts.
25
+ * Only task_error counts toward a task's retry budget. usage_limit /
26
+ * auth_expired / env_broken trigger runner quarantine in watch.ts;
27
+ * capacity_exhausted triggers the pause/resume path instead.
16
28
  */
29
+ // v0.56 (T-56-1): signatures of a SPENT-BUT-RECOVERABLE capacity window —
30
+ // the session/usage limit Claude resets on a clock (vs. usage_limit below,
31
+ // which is a hard billing wall). These must stay disjoint from the
32
+ // usage_limit patterns so the live 2026-06-05 signature
33
+ // ("You've hit your session limit - resets 3:20pm (Asia/Calcutta)")
34
+ // routes to pause, while "out of credits" / "monthly spend limit" still
35
+ // route to quarantine. The authoritative signal is api_error_status 429 in
36
+ // the result JSON; these message patterns are the fallback when the runner
37
+ // only has plain text (older claude, or the JSON envelope was not emitted).
38
+ const CAPACITY_PATTERNS = [
39
+ /hit your (?:session|usage|account) limit/i,
40
+ /session limit/i,
41
+ /(?:session|usage|account|limit).*resets?\b/i,
42
+ /resets?\s+(?:at\s+)?\d{1,2}(?::\d{2})?\s*(?:am|pm)/i,
43
+ ];
17
44
  const USAGE_LIMIT_PATTERNS = [
18
45
  /usage limit/i,
19
46
  /you have reached the maximum number of tokens/i,
@@ -65,6 +92,129 @@ const FAST_EXIT_MAX_OUTPUT_BYTES = 100;
65
92
  function matchesAny(text, patterns) {
66
93
  return patterns.some((p) => p.test(text));
67
94
  }
95
+ /**
96
+ * v0.56 (T-56-1): pull `api_error_status` out of claude's
97
+ * `--output-format=json` envelope. On a session/usage limit the CLI exits
98
+ * 1 with a JSON object carrying `{ "is_error": true, "api_error_status":
99
+ * 429, "result": "You've hit your session limit - resets 3:20pm
100
+ * (Asia/Calcutta)" }`. Returns the numeric status when present, else null.
101
+ * Kept self-contained (a local parse, no cost-pricing import) so the
102
+ * classifier stays a pure (strings) → class function for unit tests.
103
+ */
104
+ export function parseApiErrorStatus(stdout) {
105
+ const trimmed = stdout.trim();
106
+ if (!trimmed.startsWith("{"))
107
+ return null;
108
+ try {
109
+ const parsed = JSON.parse(trimmed);
110
+ const status = parsed?.api_error_status;
111
+ if (typeof status === "number")
112
+ return status;
113
+ if (typeof status === "string" && /^\d+$/.test(status))
114
+ return Number(status);
115
+ return null;
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ }
121
+ const RESET_TIME_RE = /resets?\s+(?:at\s+|by\s+)?(\d{1,2})(?::(\d{2}))?\s*(am|pm)?\s*(?:\(([^)]+)\))?/i;
122
+ function tzDateParts(instant, tz) {
123
+ const dtf = new Intl.DateTimeFormat("en-US", {
124
+ timeZone: tz,
125
+ year: "numeric",
126
+ month: "2-digit",
127
+ day: "2-digit",
128
+ hour: "2-digit",
129
+ minute: "2-digit",
130
+ second: "2-digit",
131
+ hour12: false,
132
+ });
133
+ const map = {};
134
+ for (const p of dtf.formatToParts(instant)) {
135
+ if (p.type !== "literal")
136
+ map[p.type] = p.value;
137
+ }
138
+ // Intl can emit "24" for midnight in hour12:false mode; normalise to 0.
139
+ const hour = Number(map.hour) === 24 ? 0 : Number(map.hour);
140
+ return {
141
+ year: Number(map.year),
142
+ month: Number(map.month),
143
+ day: Number(map.day),
144
+ hour,
145
+ minute: Number(map.minute),
146
+ second: Number(map.second),
147
+ };
148
+ }
149
+ /** Offset (ms) of `tz` relative to UTC at the given instant (east = positive). */
150
+ function zoneOffsetMs(instant, tz) {
151
+ const p = tzDateParts(instant, tz);
152
+ const asUtc = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
153
+ return asUtc - instant;
154
+ }
155
+ /** Resolve a wall-clock Y-M-D H:M in `tz` to a UTC epoch (one DST refinement). */
156
+ function wallClockToUtc(year, month, day, hour, minute, tz) {
157
+ const guess = Date.UTC(year, month - 1, day, hour, minute, 0);
158
+ let instant = guess - zoneOffsetMs(guess, tz);
159
+ const off2 = zoneOffsetMs(instant, tz);
160
+ const refined = guess - off2;
161
+ if (refined !== instant)
162
+ instant = refined;
163
+ return instant;
164
+ }
165
+ /**
166
+ * v0.56 (T-56-1): parse the reset time Claude states when it pauses a
167
+ * session ("...resets 3:20pm (Asia/Calcutta)") into an absolute epoch
168
+ * (ms). The runner schedules its capacity resume off this so it wakes
169
+ * exactly when the window reopens rather than guessing a fixed backoff.
170
+ *
171
+ * - 12h ("3:20pm") and 24h ("15:20") clock forms are both accepted.
172
+ * - A parenthetical IANA zone ("(Asia/Calcutta)") is honoured; without
173
+ * one the time is read in the runner's local zone.
174
+ * - The soonest FUTURE occurrence wins: a stated time already past today
175
+ * rolls to tomorrow.
176
+ *
177
+ * Returns null when no reset time is present or the zone is unparseable —
178
+ * the caller then falls back to a default backoff.
179
+ */
180
+ export function extractResetTime(text, now = Date.now()) {
181
+ if (!text)
182
+ return null;
183
+ const m = RESET_TIME_RE.exec(text);
184
+ if (!m)
185
+ return null;
186
+ let hour = Number(m[1]);
187
+ const minute = m[2] ? Number(m[2]) : 0;
188
+ const ampm = m[3]?.toLowerCase();
189
+ const tz = m[4]?.trim();
190
+ if (Number.isNaN(hour) || hour > 23 || minute > 59)
191
+ return null;
192
+ if (ampm === "pm" && hour < 12)
193
+ hour += 12;
194
+ if (ampm === "am" && hour === 12)
195
+ hour = 0;
196
+ try {
197
+ if (tz) {
198
+ const today = tzDateParts(now, tz);
199
+ let instant = wallClockToUtc(today.year, today.month, today.day, hour, minute, tz);
200
+ if (instant <= now) {
201
+ const tomorrow = tzDateParts(now + 24 * 3_600_000, tz);
202
+ instant = wallClockToUtc(tomorrow.year, tomorrow.month, tomorrow.day, hour, minute, tz);
203
+ }
204
+ return instant;
205
+ }
206
+ const local = new Date(now);
207
+ local.setHours(hour, minute, 0, 0);
208
+ let instant = local.getTime();
209
+ if (instant <= now)
210
+ instant += 24 * 3_600_000;
211
+ return instant;
212
+ }
213
+ catch {
214
+ // Unknown/garbled IANA zone — let the caller apply its default backoff.
215
+ return null;
216
+ }
217
+ }
68
218
  /**
69
219
  * Classify a non-zero claude exit.
70
220
  *
@@ -84,6 +234,18 @@ export function classifyClaudeExit(exitCode, stderr, stdout, durationMs) {
84
234
  };
85
235
  }
86
236
  const combined = `${stderr}\n${stdout}`;
237
+ // v0.56 (T-56-1): capacity check FIRST so the recoverable session/usage
238
+ // window routes to pause, not quarantine. Authoritative signal is the
239
+ // result JSON's api_error_status 429; the message patterns are the
240
+ // plain-text fallback. Disjoint from USAGE_LIMIT_PATTERNS below, which
241
+ // stay quarantine-worthy (hard billing walls).
242
+ if (parseApiErrorStatus(stdout) === 429 || matchesAny(combined, CAPACITY_PATTERNS)) {
243
+ return {
244
+ exitCode,
245
+ class: "capacity_exhausted",
246
+ detail: "Claude session/usage capacity reached (429) — runner will pause until reset",
247
+ };
248
+ }
87
249
  if (matchesAny(combined, USAGE_LIMIT_PATTERNS)) {
88
250
  return {
89
251
  exitCode,
@@ -1 +1 @@
1
- {"version":3,"file":"failure-classifier.js","sourceRoot":"","sources":["../src/failure-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA2BH,MAAM,oBAAoB,GAAa;IACrC,cAAc;IACd,gDAAgD;IAChD,sBAAsB;IACtB,iBAAiB;IACjB,sBAAsB;IACtB,aAAa;IACb,iBAAiB;IACjB,sBAAsB;IACtB,oCAAoC;CACrC,CAAC;AAEF,MAAM,qBAAqB,GAAa;IACtC,kBAAkB;IAClB,yBAAyB;IACzB,oBAAoB;IACpB,eAAe;IACf,0CAA0C;IAC1C,mBAAmB;IACnB,yCAAyC;IACzC,oBAAoB;IACpB,uBAAuB;IACvB,gBAAgB;CACjB,CAAC;AAEF,yEAAyE;AACzE,4EAA4E;AAC5E,2EAA2E;AAC3E,MAAM,mBAAmB,GAAa;IACpC,8BAA8B;IAC9B,qBAAqB;IACrB,kBAAkB;IAClB,oCAAoC;IACpC,QAAQ;IACR,aAAa;IACb,qBAAqB;IACrB,SAAS;IACT,eAAe;IACf,oBAAoB;IACpB,4BAA4B;IAC5B,0BAA0B;IAC1B,WAAW;IACX,SAAS;IACT,6BAA6B;CAC9B,CAAC;AAEF,kEAAkE;AAClE,sEAAsE;AACtE,4EAA4E;AAC5E,MAAM,YAAY,GAAG,KAAK,CAAC;AAC3B,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAEvC,SAAS,UAAU,CAAC,IAAY,EAAE,QAAkB;IAClD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAAuB,EACvB,MAAc,EACd,MAAc,EACd,UAAmB;IAEnB,mEAAmE;IACnE,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;QACrB,OAAO;YACL,QAAQ;YACR,KAAK,EAAE,YAAY;YACnB,MAAM,EAAE,4CAA4C;SACrD,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,MAAM,KAAK,MAAM,EAAE,CAAC;IAExC,IAAI,UAAU,CAAC,QAAQ,EAAE,oBAAoB,CAAC,EAAE,CAAC;QAC/C,OAAO;YACL,QAAQ;YACR,KAAK,EAAE,aAAa;YACpB,MAAM,EAAE,wCAAwC;SACjD,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,EAAE,qBAAqB,CAAC,EAAE,CAAC;QAChD,OAAO;YACL,QAAQ;YACR,KAAK,EAAE,cAAc;YACrB,MAAM,EAAE,6DAA6D;SACtE,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,EAAE,mBAAmB,CAAC,EAAE,CAAC;QAC9C,OAAO;YACL,QAAQ;YACR,KAAK,EAAE,YAAY;YACnB,MAAM,EAAE,mDAAmD;SAC5D,CAAC;IACJ,CAAC;IAED,kEAAkE;IAClE,wEAAwE;IACxE,qEAAqE;IACrE,qEAAqE;IACrE,IACE,UAAU,KAAK,SAAS;QACxB,UAAU,GAAG,YAAY;QACzB,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,0BAA0B,EACjD,CAAC;QACD,OAAO;YACL,QAAQ;YACR,KAAK,EAAE,YAAY;YACnB,MAAM,EAAE,iBAAiB,QAAQ,IAAI,QAAQ,OAAO,UAAU,6CAA6C;YAC3G,qEAAqE;YACrE,sEAAsE;YACtE,uDAAuD;YACvD,SAAS,EAAE,IAAI;SAChB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ;QACR,KAAK,EAAE,YAAY;QACnB,MAAM,EAAE,iBAAiB,QAAQ,IAAI,QAAQ,uBAAuB;KACrE,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"failure-classifier.js","sourceRoot":"","sources":["../src/failure-classifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ export type IntegrityAssertion = "conflict_markers" | "typecheck" | "version_regression" | "changelog_missing";
2
+ export interface IntegrityFailure {
3
+ assertion: IntegrityAssertion;
4
+ /** Human-readable, log-safe (no secrets) explanation of the failure. */
5
+ detail: string;
6
+ /** Offending tracked files, when the assertion is file-scoped. */
7
+ files?: string[];
8
+ }
9
+ export interface ResolutionIntegrityResult {
10
+ ok: boolean;
11
+ /** Assertions that actually ran (version checks skip when nothing changed). */
12
+ checked: IntegrityAssertion[];
13
+ failures: IntegrityFailure[];
14
+ }
15
+ /** A package.json / CHANGELOG.md pair to version-check, relative to workdir. */
16
+ export interface VersionTarget {
17
+ packageJsonPath: string;
18
+ changelogPath: string;
19
+ }
20
+ export interface AssertResolutionOptions {
21
+ /**
22
+ * Base ref the resolution should not regress against (e.g. the integration
23
+ * branch). When omitted, or when the base file can't be read, the version
24
+ * assertion is skipped — never false-failed.
25
+ */
26
+ baseRef?: string;
27
+ /** Typecheck command (defaults to `pnpm typecheck`). */
28
+ typecheckCmd?: string;
29
+ /**
30
+ * package.json / CHANGELOG.md pairs to version-check. Defaults to the repo
31
+ * root pair. The runner passes the acc-runner package pair (the #496 site).
32
+ */
33
+ versionTargets?: VersionTarget[];
34
+ }
35
+ export interface ResolutionIntegrityDeps {
36
+ /** Tracked files still carrying a conflict marker. Default: `git grep`. */
37
+ findConflictMarkers?: (workdir: string) => Promise<string[]>;
38
+ /** Run the typecheck command. Default: spawn `pnpm typecheck` in workdir. */
39
+ runTypecheck?: (workdir: string, cmd: string) => Promise<{
40
+ ok: boolean;
41
+ output: string;
42
+ }>;
43
+ /** Resolved (post-resolution, on-disk) version for a target, or null. */
44
+ readResolvedVersion?: (workdir: string, target: VersionTarget) => Promise<string | null>;
45
+ /** Base-ref version for a target, or null when unreadable (→ skip check). */
46
+ readBaseVersion?: (workdir: string, baseRef: string, target: VersionTarget) => Promise<string | null>;
47
+ /** CHANGELOG.md text for a target, or null when absent. */
48
+ readChangelog?: (workdir: string, target: VersionTarget) => Promise<string | null>;
49
+ }
50
+ /**
51
+ * Conflict-marker line shape: 7 of the same marker char at the start of a
52
+ * line. `<<<<<<<` and `>>>>>>>` are unambiguous; the `=======` divider is
53
+ * matched only as a whole line so a 7-equals markdown rule plus trailing text
54
+ * does not trip it.
55
+ */
56
+ export declare const CONFLICT_MARKER_RE: RegExp;
57
+ export declare function textHasConflictMarker(text: string): boolean;
58
+ /** Semver compare (a-b sign); tolerant of pre-release/build suffixes. */
59
+ export declare function cmpSemver(a: string, b: string): number;
60
+ /**
61
+ * True when `changelog` carries an entry for `version`. The version must
62
+ * appear bounded by non-version characters so 0.14.0 does not match inside
63
+ * 0.14.00 or 10.14.0.
64
+ */
65
+ export declare function changelogMentionsVersion(changelog: string, version: string): boolean;
66
+ export declare const DEFAULT_VERSION_TARGET: VersionTarget;
67
+ /**
68
+ * Assert that a conflict resolution in `workdir` is shippable. Returns a
69
+ * structured result; `ok === false` means the caller MUST reject the
70
+ * resolution (do not push, do not mark resolved).
71
+ */
72
+ export declare function assertResolutionIntegrity(workdir: string, opts?: AssertResolutionOptions, deps?: ResolutionIntegrityDeps): Promise<ResolutionIntegrityResult>;
73
+ /**
74
+ * True when a task that reaches the worktree/push path is a conflict
75
+ * resolution and must clear the integrity guardrail. `conflict_resolution`
76
+ * task types are handled mechanically upstream and never reach the push path,
77
+ * so the live signal here is the title an operator/manager files when the
78
+ * mechanical resolver bails and a Claude session takes over the merge.
79
+ */
80
+ export declare function isConflictResolutionTask(task: {
81
+ type?: string | null;
82
+ title?: string | null;
83
+ }): boolean;
84
+ /** One-line, log-safe summary of which assertions failed. */
85
+ export declare function summariseFailures(failures: IntegrityFailure[]): string;
86
+ //# sourceMappingURL=resolution-integrity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolution-integrity.d.ts","sourceRoot":"","sources":["../../src/runtime/resolution-integrity.ts"],"names":[],"mappings":"AAsCA,MAAM,MAAM,kBAAkB,GAC1B,kBAAkB,GAClB,WAAW,GACX,oBAAoB,GACpB,mBAAmB,CAAC;AAExB,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,kBAAkB,CAAC;IAC9B,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,OAAO,CAAC;IACZ,+EAA+E;IAC/E,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,uBAAuB;IACtC,2EAA2E;IAC3E,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7D,6EAA6E;IAC7E,YAAY,CAAC,EAAE,CACb,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,KACR,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,CACpB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,aAAa,KAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,6EAA6E;IAC7E,eAAe,CAAC,EAAE,CAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,aAAa,KAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,2DAA2D;IAC3D,aAAa,CAAC,EAAE,CACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,aAAa,KAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC7B;AAMD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QAAsC,CAAC;AAEtE,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAQtD;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAGT;AAED,eAAO,MAAM,sBAAsB,EAAE,aAGpC,CAAC;AAqGF;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,uBAA4B,EAClC,IAAI,GAAE,uBAA4B,GACjC,OAAO,CAAC,yBAAyB,CAAC,CAgFpC;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,GAAG,OAAO,CAIV;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAEtE"}