mergejudge-runner 0.4.4 → 0.4.5

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 (3) hide show
  1. package/README.md +1 -0
  2. package/dist/cli.js +38 -17
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -124,6 +124,7 @@ Logs go to stderr.
124
124
 
125
125
  ## Changelog
126
126
 
127
+ - **0.4.5** (2026-10-17): policy files share the total budget fairly (MergeJudge #100): a large first file (e.g. AGENTS.md) no longer silently drops later ones (e.g. APPROVAL_POLICY.md); truncation is reported. Local-code mode keeps one extra char per file so the cut is visible. No config or protocol changes; works with older Workers.
127
128
  - **0.4.4** (2026-10-17): learning v2 (MergeJudge #94/#96/#97): the runner prompt now includes the repo's team preferences (known false alarms, recent writer lessons, outcome hints) sent by the Worker, re-sanitised and capped; context only, never lowers a lane. Additive job field; works with older Workers. No config changes.
128
129
  - **0.4.3** (2026-10-17): bundles the MergeJudge #92 rules fix: a safety guard (e.g. `persist-credentials: false`) removed together with its whole YAML step or job no longer forces block. No config or protocol changes.
129
130
  - **0.4.2** (2026-10-17): a heartbeat answered with `done` after the result was accepted is treated as benign, not as a lost lease (MergeJudge #86). In deep-local mode the runner secret-scans the full local diff, including files GitHub strips from the API diff (large or generated files) (MergeJudge #87). No config changes.
package/dist/cli.js CHANGED
@@ -30535,28 +30535,48 @@ async function retrieveDeepContext(snap, files, opts = {}, log = () => {
30535
30535
  // ../../src/policy.ts
30536
30536
  var POLICY_MAX_EACH = 8 * 1024;
30537
30537
  var POLICY_MAX_TOTAL = 8 * 1024;
30538
+ function fairShares(sizes, total) {
30539
+ const out = sizes.map(() => 0);
30540
+ let left = Math.max(0, total);
30541
+ let pending = sizes.map((_, i) => i).sort((x, y) => sizes[x] - sizes[y]);
30542
+ while (pending.length) {
30543
+ const share = Math.floor(left / pending.length);
30544
+ const i = pending[0];
30545
+ if (sizes[i] <= share) {
30546
+ out[i] = sizes[i];
30547
+ left -= sizes[i];
30548
+ pending = pending.slice(1);
30549
+ continue;
30550
+ }
30551
+ for (const j of pending) out[j] = share;
30552
+ break;
30553
+ }
30554
+ return out;
30555
+ }
30538
30556
  async function loadPolicies(paths, fetchFile, caps = { each: POLICY_MAX_EACH, total: POLICY_MAX_TOTAL }) {
30539
- const parts = [];
30540
- const used = [];
30541
- let total = 0;
30557
+ const found = [];
30558
+ const skipped = [];
30542
30559
  for (const path2 of paths) {
30543
- if (total >= caps.total) break;
30544
30560
  let t;
30545
30561
  try {
30546
30562
  t = await fetchFile(path2);
30547
30563
  } catch {
30548
30564
  t = null;
30549
30565
  }
30550
- if (!t || !t.trim()) continue;
30551
- const budget = Math.min(caps.each, caps.total - total);
30566
+ if (!t || !t.trim()) skipped.push(path2);
30567
+ else found.push({ path: path2, t });
30568
+ }
30569
+ const shares = fairShares(found.map((f) => Math.min(f.t.length, caps.each)), caps.total);
30570
+ const parts = [];
30571
+ const used = [];
30572
+ found.forEach(({ path: path2, t }, i) => {
30573
+ const budget = shares[i];
30552
30574
  const truncated = t.length > budget;
30553
- const body = truncated ? t.slice(0, budget) + "\n[... truncated by MergeJudge ...]" : t;
30554
- total += Math.min(t.length, budget);
30555
30575
  parts.push(`--- ${path2} ---
30556
- ${body}`);
30576
+ ${truncated ? t.slice(0, budget) + "\n[... truncated by MergeJudge ...]" : t}`);
30557
30577
  used.push({ path: path2, truncated });
30558
- }
30559
- return { text: parts.join("\n\n"), used };
30578
+ });
30579
+ return { text: parts.join("\n\n"), used, skipped };
30560
30580
  }
30561
30581
  function filterPolicyPaths(paths, exclude) {
30562
30582
  if (!exclude.length) return paths;
@@ -31445,7 +31465,7 @@ async function prepareLocalJob(o, j, log, signal) {
31445
31465
  const policySource = cfgSha === j.baseSha ? base : new GitSource(dir, cfgSha, o.env, o.limits, signal);
31446
31466
  for (const p of filterPolicyPaths(j.cfg.policy_files, j.cfg.policy_exclude)) {
31447
31467
  const t = await policySource.readFile(p).catch(() => null);
31448
- if (t) policyFiles[p] = t.slice(0, j.cfg.policy.max_each_bytes);
31468
+ if (t) policyFiles[p] = t.slice(0, j.cfg.policy.max_each_bytes + 1);
31449
31469
  }
31450
31470
  if (policySource !== base) policySource.close();
31451
31471
  notes.push("Deep mode on a runner with a local clone: repo context was retrieved from an index on this machine and reviewed in one pass (no tool calls).");
@@ -32423,7 +32443,7 @@ async function produceReviewResult(o) {
32423
32443
  const labels = (pr.labels ?? []).map((l) => l.name);
32424
32444
  if (o.providerNote && cfg.provider.type !== "none") notes.push(`LLM disabled (${!provider && o.keyNote || o.providerNote}); rules-only.`);
32425
32445
  let llm = null;
32426
- let policies = { text: "", used: [] };
32446
+ let policies = { text: "", used: [], skipped: [] };
32427
32447
  let tokens2 = 0;
32428
32448
  let usedMode = cfg.review.mode;
32429
32449
  let breakdown = null;
@@ -32432,15 +32452,16 @@ async function produceReviewResult(o) {
32432
32452
  llm = cached2.llm;
32433
32453
  tokens2 = cached2.tokens;
32434
32454
  breakdown = cached2.breakdown;
32435
- policies = { text: "", used: cached2.policiesUsed };
32455
+ policies = { text: "", used: cached2.policiesUsed, skipped: [] };
32436
32456
  log.info(`Re-using LLM result for head ${pr.head.sha.slice(0, 7)} (no LLM call).`);
32437
32457
  } else if (provider) {
32438
32458
  const paths = filterPolicyPaths(cfg.policy_files, cfg.policy_exclude);
32439
32459
  policies = paths.length ? await loadPolicies(paths, (p) => o.readRepoFile(p, cfgRef), {
32440
32460
  each: cfg.policy.max_each_bytes,
32441
32461
  total: cfg.policy.max_total_bytes
32442
- }) : { text: "", used: [] };
32443
- if (policies.used.length) log.info(`Policy files: ${policies.used.map((u) => u.path).join(", ")}`);
32462
+ }) : { text: "", used: [], skipped: [] };
32463
+ if (policies.used.length) log.info(`Policy files: ${policies.used.map((u) => u.path).join(", ")}${policies.used.some((u) => u.truncated) ? " (truncated)" : ""}`);
32464
+ if (policies.skipped.length) log.info(`Policy files not found or empty: ${policies.skipped.join(", ")}`);
32444
32465
  else if (!cfg.policy_files.length) log.info("Policy files disabled (policy_files: []).");
32445
32466
  const criteria = cfg.rubrics.enabled ? selectCriteria(cfg.rubrics.packs, cfg.rubrics.custom, cfg.rubrics.notes) : [];
32446
32467
  const { kept, skipped } = excludeFromLlm(files, cfg.diff_exclude);
@@ -32790,7 +32811,7 @@ function installAutostart(d, bundle) {
32790
32811
  }
32791
32812
 
32792
32813
  // src/version.ts
32793
- var VERSION = "0.4.4";
32814
+ var VERSION = "0.4.5";
32794
32815
  var MODES = ["diff", "context", "deep"];
32795
32816
  var FEATURES = ["local-code"];
32796
32817
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mergejudge-runner",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Run MergeJudge reviews on your own machine with your own Claude Code login (runner protocol /runner/v1)",