@brainervirus/workit-codex 8.24.0 → 8.25.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.
@@ -15400,6 +15400,26 @@ function resolveRef(cwd, ref) {
15400
15400
  return null;
15401
15401
  return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
15402
15402
  }
15403
+ function originHeadRef(cwd) {
15404
+ return line(git(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]));
15405
+ }
15406
+ function ownChangeDrift(cwd, range) {
15407
+ const before = diffRaw(cwd, range.fromFork, range.from);
15408
+ const after = diffRaw(cwd, range.toFork, range.to);
15409
+ if (!before || !after)
15410
+ return null;
15411
+ const judged = new Map(before.map((entry) => [entry.path, entry]));
15412
+ const current = new Map(after.map((entry) => [entry.path, entry]));
15413
+ const same = (a, b) => a !== undefined && b !== undefined && a.oldMode === b.oldMode && a.oldBlob === b.oldBlob && a.newMode === b.newMode && a.newBlob === b.newBlob;
15414
+ return [...new Set([...judged.keys(), ...current.keys()])].filter((file) => !same(judged.get(file), current.get(file))).toSorted();
15415
+ }
15416
+ function sameOwnChange(cwd, range) {
15417
+ const fromFork = mergeBase(cwd, range.fromBase, range.from);
15418
+ const toFork = mergeBase(cwd, range.toBase, range.to);
15419
+ if (!fromFork || !toFork)
15420
+ return false;
15421
+ return ownChangeDrift(cwd, { from: range.from, to: range.to, fromFork, toFork })?.length === 0;
15422
+ }
15403
15423
  var LANE_DIFF = [
15404
15424
  "-c",
15405
15425
  "core.quotePath=false",
@@ -19026,9 +19046,9 @@ var workerBlocksTransition = (transition, state) => transition === "pause" ? fal
19026
19046
 
19027
19047
  // packages/workit-core/src/core/task-evaluation.ts
19028
19048
  import { createHash as createHash10 } from "node:crypto";
19029
- import * as fs14 from "node:fs";
19030
- import { spawnSync as spawnSync11 } from "node:child_process";
19031
- import path18 from "node:path";
19049
+ import * as fs13 from "node:fs";
19050
+ import { spawnSync as spawnSync10 } from "node:child_process";
19051
+ import path17 from "node:path";
19032
19052
 
19033
19053
  // packages/workit-core/src/check-config.ts
19034
19054
  import { spawnSync as spawnSync8 } from "node:child_process";
@@ -19249,68 +19269,20 @@ function checkCommand(args, version, onPath = workitAvailable()) {
19249
19269
 
19250
19270
  // packages/workit-core/src/core/policy/derive.ts
19251
19271
  import { existsSync as existsSync5 } from "node:fs";
19252
- import path17 from "node:path";
19272
+ import path16 from "node:path";
19253
19273
 
19254
19274
  // packages/workit-core/src/ledger.ts
19255
19275
  import { spawnSync as spawnSync9 } from "node:child_process";
19256
19276
  import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
19257
19277
  import fs12 from "node:fs";
19278
+ import path15 from "node:path";
19279
+
19280
+ // packages/workit-core/src/core/policy/lane.ts
19281
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
19258
19282
  import path14 from "node:path";
19259
- var LEDGER_VERSION = 1;
19260
- var MAX_LINE_BYTES = 4096;
19261
- var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
19262
- var FAILING = new Set(["failed", "blocked"]);
19263
- var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
19264
- var PR_ROW_TYPES = new Set([
19265
- "pr.created",
19266
- "pr.status",
19267
- "pr.merged",
19268
- "pr.linked"
19269
- ]);
19270
- var err = (code, error, unblock) => ({
19271
- ok: false,
19272
- code,
19273
- error,
19274
- ...unblock ? { unblock } : {}
19275
- });
19276
- var gitRun2 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
19277
- const run = spawnSync9("git", args, {
19278
- cwd,
19279
- encoding: "utf8",
19280
- timeout: timeoutMs,
19281
- killSignal: "SIGKILL",
19282
- windowsHide: true,
19283
- maxBuffer: 256 * 1024 * 1024,
19284
- env: {
19285
- ...process.env,
19286
- GIT_TERMINAL_PROMPT: "0",
19287
- GIT_OPTIONAL_LOCKS: "0",
19288
- LC_ALL: "C",
19289
- LANGUAGE: "C"
19290
- }
19291
- });
19292
- return {
19293
- ok: run.status === 0,
19294
- stdout: run.stdout ?? "",
19295
- stderr: run.error ? String(run.error.message) : run.stderr ?? ""
19296
- };
19297
- };
19298
- var git4 = (cwd, args) => {
19299
- const run = gitRun2(cwd, args);
19300
- const value = run.ok ? run.stdout.trim() : "";
19301
- return value ? value : null;
19302
- };
19283
+
19284
+ // packages/workit-core/src/git/default-base.ts
19303
19285
  var safeRef = (value) => value.length > 0 && !value.startsWith("-");
19304
- function resolveCommit(cwd, ref) {
19305
- if (!safeRef(ref))
19306
- return null;
19307
- return git4(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
19308
- }
19309
- function branchHead(cwd, branch) {
19310
- if (!safeRef(branch))
19311
- return null;
19312
- return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
19313
- }
19314
19286
  var trackTrunk = (cwd) => {
19315
19287
  try {
19316
19288
  const resolved = vcsConfig("resolve", cwd);
@@ -19326,11 +19298,11 @@ function defaultBase(cwd) {
19326
19298
  const trunk = trackTrunk(cwd);
19327
19299
  if (trunk) {
19328
19300
  for (const candidate of [`origin/${trunk}`, trunk])
19329
- if (resolveCommit(cwd, candidate))
19301
+ if (resolveRef(cwd, candidate))
19330
19302
  return candidate;
19331
19303
  }
19332
- const originHead = git4(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
19333
- if (originHead && resolveCommit(cwd, originHead))
19304
+ const originHead = originHeadRef(cwd);
19305
+ if (originHead && resolveRef(cwd, originHead))
19334
19306
  return originHead;
19335
19307
  for (const candidate of [
19336
19308
  "origin/main",
@@ -19340,1489 +19312,1611 @@ function defaultBase(cwd) {
19340
19312
  "origin/develop",
19341
19313
  "develop"
19342
19314
  ])
19343
- if (resolveCommit(cwd, candidate))
19315
+ if (resolveRef(cwd, candidate))
19344
19316
  return candidate;
19345
19317
  return null;
19346
19318
  }
19347
- function diffHash(cwd, base, head) {
19348
- if (!safeRef(base) || !safeRef(head))
19349
- return null;
19350
- const diff = gitRun2(cwd, [
19351
- "diff",
19352
- "-U3",
19353
- "--diff-algorithm=myers",
19354
- "--indent-heuristic",
19355
- "--no-color",
19356
- "--no-ext-diff",
19357
- "--no-textconv",
19358
- "--no-renames",
19359
- "--full-index",
19360
- "--binary",
19361
- "--src-prefix=a/",
19362
- "--dst-prefix=b/",
19363
- `${base}...${head}`,
19364
- "--"
19365
- ], GIT_TIMEOUTS.worktree);
19366
- if (!diff.ok || !diff.stdout)
19319
+
19320
+ // packages/workit-core/src/core/policy/attributes.ts
19321
+ function patternRegex(dir, pattern) {
19322
+ if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
19367
19323
  return null;
19368
- const hash = createHash9("sha256");
19369
- for (const line of diff.stdout.split(`
19370
- `)) {
19371
- if (line.startsWith("index "))
19372
- continue;
19373
- hash.update(line.startsWith("@@ ") ? "@@" : line);
19374
- hash.update(`
19375
- `);
19376
- }
19377
- return `sha256:${hash.digest("hex")}`;
19378
- }
19379
- function storeRoot(cwd) {
19380
- const location = resolveStore(path14.resolve(cwd));
19381
- if (location instanceof Error)
19382
- return err("unavailable", location.message.replace(/; fix the repository.*$/u, ""), "fix the repository (git status should work here) or run from a non-git directory");
19383
- return { ok: true, value: { root: location.dir, shared: location.shared } };
19384
- }
19385
- function ledgerPath(cwd) {
19386
- const root = storeRoot(cwd);
19387
- return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
19388
- }
19389
- var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
19390
- var NETWORK_FS_TYPES = new Set([
19391
- 26985,
19392
- 20859,
19393
- 4266872130,
19394
- 4283649346,
19395
- 1702057286,
19396
- 1397113167,
19397
- 12805120,
19398
- 16914839,
19399
- 1196443219,
19400
- 198183888
19401
- ]);
19402
- var isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0);
19403
- var onNetworkFs = (dir) => {
19404
- if (process.platform !== "linux")
19405
- return false;
19406
- try {
19407
- return isNetworkFsType(fs12.statfsSync(dir).type);
19408
- } catch {
19409
- return false;
19324
+ const anchored = pattern.includes("/");
19325
+ const body = pattern.replace(/^\//u, "");
19326
+ let source = "";
19327
+ for (let index = 0;index < body.length; index += 1) {
19328
+ const char = body[index];
19329
+ if (char === "*" && body[index + 1] === "*") {
19330
+ const slash = body[index + 2] === "/";
19331
+ source += slash ? "(?:.*/)?" : ".*";
19332
+ index += slash ? 2 : 1;
19333
+ } else if (char === "*")
19334
+ source += "[^/]*";
19335
+ else if (char === "?")
19336
+ source += "[^/]";
19337
+ else if (char === "[") {
19338
+ const end = body.indexOf("]", index + 2);
19339
+ if (end < 0)
19340
+ return null;
19341
+ const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
19342
+ source += `[${set}]`;
19343
+ index = end;
19344
+ } else if (char === "\\" && index + 1 < body.length) {
19345
+ source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
19346
+ index += 1;
19347
+ } else
19348
+ source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
19410
19349
  }
19411
- };
19412
- var LOCK_WAIT_MS = 5000;
19413
- var LOCK_STALE_MS = 30000;
19414
- var LOCK_OWNER = "owner";
19415
- var lockToken = (lock) => {
19350
+ const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
19416
19351
  try {
19417
- return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
19352
+ return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
19418
19353
  } catch {
19419
19354
  return null;
19420
19355
  }
19421
- };
19422
- var lockAgeMs = (lock) => {
19423
- for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
19424
- try {
19425
- return Date.now() - fs12.statSync(target).mtimeMs;
19426
- } catch {}
19427
- }
19428
- return null;
19429
- };
19430
- var ledgerLock = {
19431
- acquire(lock) {
19432
- try {
19433
- fs12.mkdirSync(lock);
19434
- } catch (error) {
19435
- if (error.code === "EEXIST")
19436
- return null;
19437
- throw error;
19438
- }
19439
- const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
19440
- fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
19441
- return token;
19442
- },
19443
- release(lock, token) {
19444
- if (lockToken(lock) !== token)
19445
- return false;
19446
- const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
19447
- try {
19448
- fs12.renameSync(lock, tomb);
19449
- } catch {
19450
- return false;
19451
- }
19452
- if (lockToken(tomb) !== token) {
19453
- try {
19454
- fs12.renameSync(tomb, lock);
19455
- } catch {}
19456
- return false;
19457
- }
19458
- fs12.rmSync(tomb, { recursive: true, force: true });
19459
- return true;
19460
- },
19461
- token: lockToken,
19462
- reap(lock, observed) {
19463
- const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
19464
- try {
19465
- fs12.renameSync(lock, tomb);
19466
- } catch {
19467
- return false;
19468
- }
19469
- if (lockToken(tomb) === observed) {
19470
- fs12.rmSync(tomb, { recursive: true, force: true });
19471
- return true;
19472
- }
19473
- try {
19474
- fs12.renameSync(tomb, lock);
19475
- } catch {
19476
- fs12.rmSync(tomb, { recursive: true, force: true });
19356
+ }
19357
+ function rulesOf(dir, text) {
19358
+ const out = [];
19359
+ for (const raw of text.split(`
19360
+ `)) {
19361
+ const line = raw.trim();
19362
+ if (!line || line.startsWith("#") || line.startsWith("[attr]"))
19363
+ continue;
19364
+ const [pattern, ...attributes] = line.split(/\s+/u);
19365
+ let generated = null;
19366
+ for (const attribute of attributes) {
19367
+ if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
19368
+ generated = true;
19369
+ else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
19370
+ generated = false;
19477
19371
  }
19478
- return false;
19479
- }
19480
- };
19481
- function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
19482
- const lock = `${file}.lock`;
19483
- const deadline = Date.now() + waitMs;
19484
- let token = null;
19485
- for (;; ) {
19486
- token = ledgerLock.acquire(lock);
19487
- if (token)
19488
- break;
19489
- const observed = lockToken(lock);
19490
- const age = lockAgeMs(lock);
19491
- if (age !== null && age > LOCK_STALE_MS && ledgerLock.reap(lock, observed))
19372
+ if (generated === null)
19492
19373
  continue;
19493
- if (Date.now() >= deadline)
19494
- return null;
19495
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
19374
+ const regex = patternRegex(dir, pattern);
19375
+ if (regex)
19376
+ out.push({ regex, generated });
19496
19377
  }
19497
- try {
19498
- return fn();
19499
- } finally {
19500
- ledgerLock.release(lock, token);
19378
+ return out;
19379
+ }
19380
+ function generatedByAttributes(files, paths) {
19381
+ const rules = [...files].map(([file, text]) => {
19382
+ const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
19383
+ return {
19384
+ depth: dir ? dir.split("/").length : 0,
19385
+ rules: rulesOf(dir, text)
19386
+ };
19387
+ }).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
19388
+ const out = new Set;
19389
+ for (const file of paths) {
19390
+ const last = rules.findLast((rule) => rule.regex.test(file));
19391
+ if (last?.generated)
19392
+ out.add(file);
19501
19393
  }
19394
+ return out;
19502
19395
  }
19503
- var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
19504
- var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
19505
- ${body}`);
19506
- var TAIL_BYTES = 64 * 1024;
19507
- var lastLine = (fd, size) => {
19508
- for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
19509
- const buffer = Buffer.alloc(span);
19510
- fs12.readSync(fd, buffer, 0, span, size - span);
19511
- const end = buffer.lastIndexOf(10);
19512
- if (end < 0) {
19513
- if (span === size)
19514
- return null;
19515
- continue;
19396
+
19397
+ // packages/workit-core/src/core/policy/substance.ts
19398
+ var SLASH_BLOCK = [["/*", "*/"]];
19399
+ var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
19400
+ var SYNTAXES = Object.fromEntries([
19401
+ ...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
19402
+ ...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
19403
+ ext,
19404
+ { ...C_LIKE, triple: true }
19405
+ ]),
19406
+ ["go", { ...C_LIKE, rawBacktick: true }],
19407
+ ...["css", "scss", "less"].map((ext) => [
19408
+ ext,
19409
+ { line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
19410
+ ]),
19411
+ ...["py", "pyi"].map((ext) => [
19412
+ ext,
19413
+ { line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
19414
+ ]),
19415
+ ["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
19416
+ ...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
19417
+ ext,
19418
+ { line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
19419
+ ])
19420
+ ]);
19421
+ var DIRECTIVE = /@ts-|eslint|prettier|biome|oxlint|istanbul|\bc8\b|\bv8\b|@jsx|@flow|@vite|webpack|__PURE__|__NO_SIDE_EFFECTS__|source(?:Mapping)?URL|^\/\s*<|go:|\+build|nolint|noqa|type:|pyright|mypy|pylint|pragma|coding[:=]|fmt:|NOSONAR|@license|@preserve|@refresh|environment|language=|cython|ruff|isort|deno-|@vitest|@jest|swiftlint|ktlint|checkstyle|suppress|nullable|#(?:end)?region/iu;
19422
+ var extensionOf = (file) => {
19423
+ const name = file.slice(file.lastIndexOf("/") + 1);
19424
+ const dot = name.lastIndexOf(".");
19425
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
19426
+ };
19427
+ var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
19428
+ var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
19429
+ var SPACE = /[ \t\r\f\v]/u;
19430
+ var WORD = /[\w$]/u;
19431
+ var NO_SPACE_AFTER = "([{,;";
19432
+ var NO_SPACE_BEFORE = ")]},;";
19433
+ var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
19434
+ var REGEX_KEYWORDS = new Set([
19435
+ "return",
19436
+ "typeof",
19437
+ "instanceof",
19438
+ "case",
19439
+ "do",
19440
+ "else",
19441
+ "in",
19442
+ "of",
19443
+ "new",
19444
+ "delete",
19445
+ "void",
19446
+ "throw",
19447
+ "yield",
19448
+ "await"
19449
+ ]);
19450
+ function substanceOf(file, text) {
19451
+ const syntax = syntaxOf(file);
19452
+ if (!syntax)
19453
+ return plainSubstance(text);
19454
+ const markup = syntax.block.some(([open]) => open === "<!--");
19455
+ if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
19456
+ return plainSubstance(text);
19457
+ return lex(syntax, text);
19458
+ }
19459
+ function plainSubstance(text) {
19460
+ return text.split(`
19461
+ `).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
19462
+ `);
19463
+ }
19464
+ function lex(syntax, text) {
19465
+ const collapse = !syntax.indented;
19466
+ let out = "";
19467
+ let lineStart = 0;
19468
+ let kept = 0;
19469
+ let pendingSpace = false;
19470
+ const frames = [{ kind: "code", braces: 0 }];
19471
+ let index = 0;
19472
+ if (text.startsWith("#!")) {
19473
+ const end = text.indexOf(`
19474
+ `);
19475
+ out = end < 0 ? text : text.slice(0, end);
19476
+ kept = out.length;
19477
+ index = end < 0 ? text.length : end;
19478
+ }
19479
+ const emitCode = (chunk) => {
19480
+ if (collapse && pendingSpace && out.length > lineStart) {
19481
+ const before = out[out.length - 1];
19482
+ if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
19483
+ out += " ";
19516
19484
  }
19517
- let stop = end;
19518
- let text = "";
19519
- while (stop >= 0) {
19520
- const start = buffer.lastIndexOf(10, stop - 1);
19521
- text = buffer.toString("utf8", start + 1, stop).trim();
19522
- if (text) {
19523
- if (start >= 0 || span === size)
19524
- return text;
19525
- break;
19526
- }
19527
- if (start < 0) {
19528
- if (span === size)
19529
- return null;
19530
- break;
19531
- }
19532
- stop = start;
19485
+ pendingSpace = false;
19486
+ out += chunk;
19487
+ };
19488
+ const emitVerbatim = (chunk) => {
19489
+ emitCode(chunk);
19490
+ kept = out.length;
19491
+ };
19492
+ const newline = () => {
19493
+ if (!collapse) {
19494
+ let end = out.length;
19495
+ while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
19496
+ end -= 1;
19497
+ out = out.slice(0, end);
19533
19498
  }
19534
- if (span === size)
19535
- return text || null;
19536
- }
19537
- return null;
19538
- };
19539
- var tighten = (fd) => {
19540
- try {
19541
- if ((fs12.fstatSync(fd).mode & 63) !== 0)
19542
- fs12.fchmodSync(fd, 384);
19543
- } catch {}
19544
- };
19545
- function appendLine(file, build, options) {
19546
- const write = () => {
19547
- const fd = fs12.openSync(file, "a+", 384);
19548
- try {
19549
- if (process.platform !== "win32")
19550
- tighten(fd);
19551
- const size = fs12.fstatSync(fd).size;
19552
- let fence = "";
19553
- if (size > 0) {
19554
- const last = Buffer.alloc(1);
19555
- fs12.readSync(fd, last, 0, 1, size - 1);
19556
- if (last[0] !== 10)
19557
- fence = `
19499
+ pendingSpace = false;
19500
+ if (out.length > lineStart) {
19501
+ out += `
19558
19502
  `;
19559
- }
19560
- const line = build(lastLine(fd, size));
19561
- if (!line.ok)
19562
- return line;
19563
- const bytes = Buffer.from(fence + line.value);
19564
- const written = fs12.writeSync(fd, bytes);
19565
- if (written !== bytes.length)
19566
- return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
19567
- return { ok: true, value: undefined };
19568
- } finally {
19569
- fs12.closeSync(fd);
19503
+ lineStart = out.length;
19570
19504
  }
19571
19505
  };
19572
- try {
19573
- fs12.mkdirSync(path14.dirname(file), { recursive: true });
19574
- if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
19575
- return write();
19576
- const locked = withLedgerLock(file, write);
19577
- return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
19578
- } catch (error) {
19579
- return err("unavailable", `cannot append to ${file}: ${error.message}`);
19580
- }
19581
- }
19582
- var sealed = (row, id, at, previous) => {
19583
- const { prevHash: _prev, rowHash: _row, ...fields } = row;
19584
- const prevHash = previous === null ? null : sha2562(previous);
19585
- const body = { v: LEDGER_VERSION, id, at, ...fields, prevHash };
19586
- const full = { ...body, rowHash: chainHash(prevHash, JSON.stringify(body)) };
19587
- return { full, line: `${JSON.stringify(full)}
19588
- ` };
19589
- };
19590
- function appendRaw(cwd, row, options) {
19591
- const file = ledgerPath(cwd);
19592
- if (!file.ok)
19593
- return file;
19594
- const id = newId2();
19595
- const at = (options.now ?? new Date).toISOString();
19596
- let full = null;
19597
- const written = appendLine(file.value, (previous) => {
19598
- const next = sealed(row, id, at, previous);
19599
- const bytes = Buffer.byteLength(next.line);
19600
- if (bytes > MAX_LINE_BYTES)
19601
- return err("invalid_input", `ledger row is ${bytes} bytes; the limit is ${MAX_LINE_BYTES}. Shorten the text and pass details by --ref <path|url>`);
19602
- full = next.full;
19603
- return { ok: true, value: next.line };
19604
- }, options);
19605
- return written.ok ? { ok: true, value: full } : written;
19606
- }
19607
- function observedRowBytes(row) {
19608
- const sample = sealed({ ...row, observer: "workit_cli" }, newId2(), new Date().toISOString(), "");
19609
- return Buffer.byteLength(sample.line);
19610
- }
19611
- function appendObserved(cwd, row, options = {}) {
19612
- return appendRaw(cwd, { ...row, observer: "workit_cli" }, options);
19613
- }
19614
- var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
19615
- var str2 = (value) => typeof value === "string" ? value : null;
19616
- var normalizeActor = (value) => {
19617
- const actor = isRecord3(value) ? value : {};
19618
- return {
19619
- host: str2(actor.host) ?? "unknown",
19620
- session: str2(actor.session),
19621
- agentId: str2(actor.agentId)
19506
+ const regexAllowed = () => {
19507
+ let end = out.length;
19508
+ while (end > 0 && /\s/u.test(out[end - 1]))
19509
+ end -= 1;
19510
+ if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
19511
+ return true;
19512
+ let start = end;
19513
+ while (start > 0 && WORD.test(out[start - 1]))
19514
+ start -= 1;
19515
+ return REGEX_KEYWORDS.has(out.slice(start, end));
19622
19516
  };
19623
- };
19624
- function supersedeAllowed(by, target, onBranch = (row) => (by.branch ?? null) === (row.branch ?? null)) {
19625
- if (by.type !== target.type)
19626
- return `a ${by.type} row cannot supersede a ${target.type} row`;
19627
- if (!by.actor.session || by.actor.session !== target.actor.session)
19628
- return "only the session that wrote a row can supersede it";
19629
- if (!onBranch(target))
19630
- return `the row is on another branch (${String(target.branch ?? "none")}); a row supersedes only one on its own branch`;
19631
- if (by.type === "verdict" && (by.kind ?? "review") !== (target.kind ?? "review"))
19632
- return `a ${String(by.kind ?? "review")} verdict cannot supersede a ${String(target.kind ?? "review")} verdict; record it with --kind ${String(target.kind ?? "review")}`;
19633
- if (by.type === "verdict" && by.self === true && target.self !== true)
19634
- return "a self verdict cannot replace an independent verdict";
19635
- return null;
19636
- }
19637
- function readLedger(cwd, options = {}) {
19638
- const file = ledgerPath(cwd);
19639
- if (!file.ok)
19640
- return file;
19641
- let text = "";
19642
- let stat = null;
19643
- try {
19644
- stat = fs12.statSync(file.value);
19645
- text = fs12.readFileSync(file.value, "utf8");
19646
- } catch (error) {
19647
- if (error.code === "ENOENT")
19648
- return {
19649
- ok: true,
19650
- value: {
19651
- path: file.value,
19652
- rows: [],
19653
- skipped: 0,
19654
- integrity: options.integrity ? { chained: 0, legacy: 0, unverified: [] } : null
19655
- }
19656
- };
19657
- return err("unavailable", `cannot read ${file.value}: ${error.message}`);
19658
- }
19659
- const rows = [];
19660
- let skipped = 0;
19661
- const integrity = { chained: 0, legacy: 0, unverified: [] };
19662
- const earlier = new Set([null]);
19663
- let chainStarted = false;
19664
- const ids = new Set;
19665
- for (const raw of text.split(`
19666
- `)) {
19667
- const trimmed = raw.trim();
19668
- if (!trimmed)
19517
+ const scanString = (quote, escapes, multiline) => {
19518
+ const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
19519
+ const fstring = syntax.fstring === true && /f/iu.test(prefix);
19520
+ let end = index + quote.length;
19521
+ while (end < text.length) {
19522
+ const char = text[end];
19523
+ if (escapes && char === "\\") {
19524
+ end += 2;
19525
+ continue;
19526
+ }
19527
+ if (text.startsWith(quote, end)) {
19528
+ if (fstring && unbalanced(text.slice(index + quote.length, end)))
19529
+ return false;
19530
+ emitVerbatim(text.slice(index, end + quote.length));
19531
+ index = end + quote.length;
19532
+ return true;
19533
+ }
19534
+ if (char === `
19535
+ ` && !multiline)
19536
+ return false;
19537
+ end += 1;
19538
+ }
19539
+ return false;
19540
+ };
19541
+ while (index < text.length) {
19542
+ const frame = frames[frames.length - 1];
19543
+ const char = text[index];
19544
+ if (frame.kind === "template") {
19545
+ if (char === "\\") {
19546
+ emitVerbatim(text.slice(index, index + 2));
19547
+ index += 2;
19548
+ } else if (char === "`") {
19549
+ emitVerbatim(char);
19550
+ frames.pop();
19551
+ index += 1;
19552
+ } else if (text.startsWith("${", index)) {
19553
+ emitVerbatim("${");
19554
+ frames.push({ kind: "code", braces: 0 });
19555
+ index += 2;
19556
+ } else {
19557
+ out += char;
19558
+ kept = out.length;
19559
+ index += 1;
19560
+ }
19669
19561
  continue;
19670
- const linked = (prevHash) => earlier.has(prevHash);
19671
- if (options.integrity)
19672
- earlier.add(sha2562(trimmed));
19673
- let parsed;
19674
- try {
19675
- parsed = JSON.parse(trimmed);
19676
- } catch {
19677
- skipped += 1;
19562
+ }
19563
+ if (char === `
19564
+ `) {
19565
+ newline();
19566
+ index += 1;
19678
19567
  continue;
19679
19568
  }
19680
- if (!isRecord3(parsed) || !str2(parsed.id) || !str2(parsed.type) || !str2(parsed.at)) {
19681
- skipped += 1;
19569
+ if (SPACE.test(char)) {
19570
+ if (collapse)
19571
+ pendingSpace = true;
19572
+ else
19573
+ out += char;
19574
+ index += 1;
19682
19575
  continue;
19683
19576
  }
19684
- let rowIntegrity = options.integrity ? "verified" : undefined;
19685
- let reason = null;
19686
- if (!options.integrity) {} else if (typeof parsed.rowHash === "string") {
19687
- chainStarted = true;
19688
- const { rowHash, ...body } = parsed;
19689
- const prevHash = str2(parsed.prevHash);
19690
- if (chainHash(prevHash, JSON.stringify(body)) !== rowHash)
19691
- reason = "hash_mismatch";
19692
- else if (!linked(prevHash))
19693
- reason = "broken_link";
19694
- } else if (chainStarted)
19695
- reason = "unsigned";
19696
- else
19697
- rowIntegrity = "legacy";
19698
- if (options.integrity && !reason && ids.has(parsed.id))
19699
- reason = "duplicate_id";
19700
- if (options.integrity)
19701
- ids.add(parsed.id);
19702
- if (reason)
19703
- rowIntegrity = "unverified";
19704
- const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
19705
- const row = {
19706
- ...parsed,
19707
- v: typeof parsed.v === "number" ? parsed.v : LEDGER_VERSION,
19708
- id: parsed.id,
19709
- at: parsed.at,
19710
- type: parsed.type,
19711
- actor: normalizeActor(parsed.actor),
19712
- branch: str2(parsed.branch),
19713
- head: str2(parsed.head),
19714
- base: str2(parsed.base),
19715
- baseSha: str2(parsed.baseSha),
19716
- patchId: str2(parsed.patchId),
19717
- diffHash: str2(parsed.diffHash),
19718
- tree: str2(parsed.tree),
19719
- dirty: typeof parsed.dirty === "boolean" ? parsed.dirty : null,
19720
- ...pr === undefined ? {} : { pr },
19721
- ...str2(parsed.supersedes) ? { supersedes: parsed.supersedes } : {},
19722
- seq: rows.length + 1,
19723
- superseded: false,
19724
- supersedeIgnored: false,
19725
- ...rowIntegrity ? { integrity: rowIntegrity } : {},
19726
- ...reason ? { integrityReason: reason } : {}
19727
- };
19728
- rows.push(row);
19729
- if (!options.integrity)
19577
+ const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
19578
+ if (lineOpener) {
19579
+ const end = text.indexOf(`
19580
+ `, index);
19581
+ const comment = text.slice(index, end < 0 ? text.length : end);
19582
+ if (DIRECTIVE.test(comment.slice(lineOpener.length)))
19583
+ emitVerbatim(comment);
19584
+ else
19585
+ pendingSpace = true;
19586
+ index = end < 0 ? text.length : end;
19730
19587
  continue;
19731
- if (rowIntegrity === "verified")
19732
- integrity.chained += 1;
19733
- else if (rowIntegrity === "legacy")
19734
- integrity.legacy += 1;
19735
- else if (reason)
19736
- integrity.unverified.push({
19737
- seq: row.seq,
19738
- id: row.id,
19739
- type: row.type,
19740
- branch: row.branch,
19741
- reason
19742
- });
19588
+ }
19589
+ const block = syntax.block.find(([open]) => text.startsWith(open, index));
19590
+ if (block) {
19591
+ const end = text.indexOf(block[1], index + block[0].length);
19592
+ if (end < 0)
19593
+ return null;
19594
+ const comment = text.slice(index, end + block[1].length);
19595
+ if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
19596
+ emitVerbatim(comment);
19597
+ else
19598
+ pendingSpace = true;
19599
+ if (comment.includes(`
19600
+ `))
19601
+ newline();
19602
+ index = end + block[1].length;
19603
+ continue;
19604
+ }
19605
+ if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
19606
+ if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
19607
+ return null;
19608
+ continue;
19609
+ }
19610
+ if (syntax.quotes.includes(char)) {
19611
+ if (!scanString(char, true, Boolean(syntax.multiline)))
19612
+ return null;
19613
+ continue;
19614
+ }
19615
+ if (syntax.literal?.includes(char)) {
19616
+ if (!scanString(char, false, false))
19617
+ return null;
19618
+ continue;
19619
+ }
19620
+ if (syntax.rawBacktick && char === "`") {
19621
+ if (!scanString("`", false, true))
19622
+ return null;
19623
+ continue;
19624
+ }
19625
+ if (syntax.js && char === "`") {
19626
+ emitVerbatim(char);
19627
+ frames.push({ kind: "template" });
19628
+ index += 1;
19629
+ continue;
19630
+ }
19631
+ if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
19632
+ return null;
19633
+ if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
19634
+ const lineEnd = text.indexOf(`
19635
+ `, index);
19636
+ if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
19637
+ return null;
19638
+ }
19639
+ if (syntax.js && char === "/" && regexAllowed()) {
19640
+ let end = index + 1;
19641
+ let inClass = false;
19642
+ while (end < text.length && (inClass || text[end] !== "/")) {
19643
+ if (text[end] === `
19644
+ `)
19645
+ return null;
19646
+ if (text[end] === "\\")
19647
+ end += 1;
19648
+ else if (text[end] === "[")
19649
+ inClass = true;
19650
+ else if (text[end] === "]")
19651
+ inClass = false;
19652
+ end += 1;
19653
+ }
19654
+ if (end >= text.length)
19655
+ return null;
19656
+ emitVerbatim(text.slice(index, end + 1));
19657
+ index = end + 1;
19658
+ continue;
19659
+ }
19660
+ if (frames.length > 1 && frame.kind === "code") {
19661
+ if (char === "{")
19662
+ frame.braces += 1;
19663
+ else if (char === "}") {
19664
+ if (frame.braces === 0) {
19665
+ emitVerbatim(char);
19666
+ frames.pop();
19667
+ index += 1;
19668
+ continue;
19669
+ }
19670
+ frame.braces -= 1;
19671
+ }
19672
+ }
19673
+ emitCode(char);
19674
+ index += 1;
19743
19675
  }
19744
- const scopes = new Map;
19745
- const scopeOf = (branch) => {
19746
- let scope = scopes.get(branch);
19747
- if (!scope)
19748
- scopes.set(branch, scope = branchScope(cwd, branch));
19749
- return scope;
19750
- };
19751
- const byId = new Map;
19752
- for (const row of rows) {
19753
- const target = row.supersedes ? byId.get(row.supersedes) : undefined;
19754
- if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
19755
- target.superseded = true;
19756
- else if (row.supersedes)
19757
- row.supersedeIgnored = true;
19758
- if (!byId.has(row.id))
19759
- byId.set(row.id, row);
19676
+ if (frames.length !== 1)
19677
+ return null;
19678
+ newline();
19679
+ return out;
19680
+ }
19681
+ function unbalanced(body) {
19682
+ let depth = 0;
19683
+ for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
19684
+ if (part === "{")
19685
+ depth += 1;
19686
+ else if (part === "}")
19687
+ depth = Math.max(0, depth - 1);
19760
19688
  }
19761
- if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
19762
- tamperCache.set(file.value, {
19763
- size: stat.size,
19764
- stamp: fileStamp(stat),
19765
- breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
19766
- });
19767
- return {
19768
- ok: true,
19769
- value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
19770
- };
19689
+ return depth > 0;
19771
19690
  }
19772
- var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
19773
- var tamperCache = new Map;
19774
- var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
19775
- function ledgerTampering(cwd) {
19776
- const file = ledgerPath(cwd);
19777
- if (!file.ok)
19778
- return [];
19691
+ function hasOpaqueLineBreak(text) {
19692
+ return /\r(?!\n)|[\u2028\u2029]/u.test(text);
19693
+ }
19694
+ var decoder = new TextDecoder("utf-8", { fatal: true });
19695
+ function textOf(bytes) {
19696
+ if (bytes.includes(0))
19697
+ return null;
19779
19698
  try {
19780
- const stat = fs12.statSync(file.value);
19781
- const cached = tamperCache.get(file.value);
19782
- if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
19783
- return cached.breaks;
19699
+ return decoder.decode(bytes);
19784
19700
  } catch {
19785
- return [];
19701
+ return null;
19786
19702
  }
19787
- const read = readLedger(cwd, { integrity: true });
19788
- return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
19789
- }
19790
- function tamperedUnblock(cwd) {
19791
- const file = ledgerPath(cwd);
19792
- const where = file.ok ? file.value : "the ledger file";
19793
- return `workit ledger verify-integrity # lists the edited or missing rows; if the edit is understood, move ${where} aside (mv ${where} ${where}.tampered-<date>) and record the verdicts again`;
19794
19703
  }
19795
- var tamperedWarning = (cwd, breaks) => `the ledger was edited, or a row was removed or moved (${breaks.slice(0, 3).map((row) => `${row.reason} at row ${row.seq}`).join(", ")}${breaks.length > 3 ? ", …" : ""}): no verdict is accepted until it is repaired. ${tamperedUnblock(cwd)}`;
19796
- function verdictBasis(verdict, current) {
19797
- if (verdict.head && verdict.head === current.head)
19798
- return "fresh";
19799
- if (verdict.patchId && verdict.patchId === current.patchId && verdict.diffHash && verdict.diffHash === current.diffHash)
19800
- return "carried";
19801
- return "stale";
19704
+ function cosmeticChange(file, before, after) {
19705
+ const left = textOf(before);
19706
+ const right = textOf(after);
19707
+ if (left === null || right === null)
19708
+ return false;
19709
+ if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
19710
+ return false;
19711
+ const a = substanceOf(file, left);
19712
+ return a !== null && a === substanceOf(file, right);
19802
19713
  }
19803
- var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
19804
- function trailerSessions(cwd, base, head) {
19805
- if (!base || !head || !safeRef(base) || !safeRef(head))
19806
- return [];
19807
- const out = git4(cwd, [
19808
- "log",
19809
- "--format=%(trailers:key=Workit-Session,valueonly,separator=%x00)",
19810
- `${base}..${head}`
19811
- ]);
19812
- return (out ?? "").split(/[\0\n]/u).map((value) => value.trim()).filter(Boolean);
19714
+
19715
+ // packages/workit-core/src/core/policy/lane.ts
19716
+ var LANE_THRESHOLDS = {
19717
+ selfMaxLines: 100,
19718
+ selfMaxFiles: 5,
19719
+ fullMinLines: 500
19720
+ };
19721
+ var RISK_TOKENS = new Set([
19722
+ "auth",
19723
+ "authn",
19724
+ "authz",
19725
+ "oauth",
19726
+ "security",
19727
+ "permission",
19728
+ "permissions",
19729
+ "secret",
19730
+ "secrets",
19731
+ "crypto",
19732
+ "gate",
19733
+ "gates",
19734
+ "policy",
19735
+ "policies",
19736
+ "hook",
19737
+ "hooks",
19738
+ "husky",
19739
+ "lefthook",
19740
+ "release",
19741
+ "releases",
19742
+ "migration",
19743
+ "migrations",
19744
+ "migrate",
19745
+ "workflows"
19746
+ ]);
19747
+ var RISK_FILES = new Set([
19748
+ ".gitlab-ci.yml",
19749
+ "Jenkinsfile",
19750
+ "azure-pipelines.yml",
19751
+ ".pre-commit-config.yaml",
19752
+ ".workit/risk-paths"
19753
+ ]);
19754
+ var RISK_NAMES = new Set([".gitattributes"]);
19755
+ var RISK_PATHS_FILE = ".workit/risk-paths";
19756
+ var LOCKFILES = new Set([
19757
+ "bun.lock",
19758
+ "bun.lockb",
19759
+ "package-lock.json",
19760
+ "npm-shrinkwrap.json",
19761
+ "yarn.lock",
19762
+ "pnpm-lock.yaml",
19763
+ "Cargo.lock",
19764
+ "go.sum",
19765
+ "poetry.lock",
19766
+ "uv.lock",
19767
+ "Pipfile.lock",
19768
+ "Gemfile.lock",
19769
+ "composer.lock"
19770
+ ]);
19771
+ var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc)$/iu;
19772
+ var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
19773
+ var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
19774
+ var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__)\/|\.(?:test|spec)\.[^/]+$|_test\.[^/]+$/iu;
19775
+ var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
19776
+ var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
19777
+ function kindOf(file) {
19778
+ if (file.binary)
19779
+ return "behavior";
19780
+ if (file.generated)
19781
+ return "generated";
19782
+ if (LOCKFILES.has(basename(file.path)))
19783
+ return "lockfile";
19784
+ if (isDoc(file.path))
19785
+ return "doc";
19786
+ if (file.cosmetic)
19787
+ return "cosmetic";
19788
+ return TEST_PATH.test(file.path) ? "test" : "behavior";
19813
19789
  }
19814
- var RENAMED = /^branch: renamed refs\/heads\/(.+) to refs\/heads\/(.+)$/iu;
19815
- function branchScope(cwd, branch) {
19816
- const renamedAt = new Map;
19817
- let since = Number.NEGATIVE_INFINITY;
19818
- if (safeRef(branch)) {
19819
- const log = git4(cwd, [
19820
- "log",
19821
- "-g",
19822
- "--date=unix",
19823
- "--format=%gd%x09%gs",
19824
- `refs/heads/${branch}`,
19825
- "--"
19826
- ]);
19827
- for (const line of (log ?? "").split(`
19828
- `)) {
19829
- const [selector = "", subject = ""] = line.split("\t");
19830
- const seconds = Number(/@\{(\d+)\}$/u.exec(selector)?.[1]);
19831
- if (!Number.isFinite(seconds))
19832
- continue;
19833
- since = seconds * 1000;
19834
- const match = RENAMED.exec(subject);
19835
- if (!match || match[1] === branch)
19836
- continue;
19837
- const until = (seconds + 1) * 1000;
19838
- renamedAt.set(match[1], Math.max(renamedAt.get(match[1]) ?? 0, until));
19790
+ var globCache = new Map;
19791
+ function globMatch(pattern, file) {
19792
+ let regex = globCache.get(pattern);
19793
+ if (!regex) {
19794
+ let source = "";
19795
+ for (let index = 0;index < pattern.length; index += 1) {
19796
+ const char = pattern[index];
19797
+ if (char === "*" && pattern[index + 1] === "*") {
19798
+ source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
19799
+ index += pattern[index + 2] === "/" ? 2 : 1;
19800
+ } else if (char === "*")
19801
+ source += "[^/]*";
19802
+ else if (char === "?")
19803
+ source += "[^/]";
19804
+ else
19805
+ source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
19839
19806
  }
19807
+ regex = new RegExp(`^${source}$`, "u");
19808
+ globCache.set(pattern, regex);
19840
19809
  }
19841
- return (row) => {
19842
- if (row.branch === branch)
19843
- return true;
19844
- const until = row.branch === null ? undefined : renamedAt.get(row.branch);
19845
- if (until === undefined)
19846
- return false;
19847
- const at = Date.parse(row.at);
19848
- return at >= since && at <= until;
19849
- };
19850
- }
19851
- var renameAwareBranch = (scopeOf, branch) => {
19852
- return (target) => {
19853
- const own = typeof branch === "string" ? branch : null;
19854
- const theirs = typeof target.branch === "string" ? target.branch : null;
19855
- if (own === theirs)
19856
- return true;
19857
- if (own === null || theirs === null || typeof target.at !== "string")
19858
- return false;
19859
- return scopeOf(own)({ branch: theirs, at: target.at });
19860
- };
19861
- };
19862
- function authorSessions(cwd, rows, branch, range = { base: null, head: null }, scope = branchScope(cwd, branch)) {
19863
- const sessions = new Set;
19864
- for (const row of rows) {
19865
- if (!scope(row) || row.type !== "commit.recorded" && row.type !== "task.opened")
19866
- continue;
19867
- const session = str2(row.session) ?? row.actor.session;
19868
- if (session)
19869
- sessions.add(session);
19870
- }
19871
- for (const session of trailerSessions(cwd, range.base, range.head))
19872
- sessions.add(session);
19873
- return sessions;
19810
+ return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
19874
19811
  }
19875
- var derivesFrom = (derived, bare) => !bare.includes(":") && derived.endsWith(`:${bare}`);
19876
- var authoredBy = (authors, session) => {
19877
- if (authors.has(session))
19812
+ function isRiskPath(file, extra = []) {
19813
+ if (extra.some((pattern) => globMatch(pattern, file)))
19878
19814
  return true;
19879
- for (const author of authors)
19880
- if (derivesFrom(author, session) || derivesFrom(session, author))
19881
- return true;
19882
- return false;
19883
- };
19884
- var TRUST_REASONS = new Set([
19885
- "unsigned",
19886
- "hash_mismatch",
19887
- "broken_link",
19888
- "duplicate_id",
19889
- "unissued_session",
19890
- "unbound_session"
19891
- ]);
19892
- var SHELL_VOUCHED_HOSTS = new Set([
19893
- "cli",
19894
- "opencode",
19895
- "pi",
19896
- "codex_cli",
19897
- "codex_desktop"
19898
- ]);
19899
- function sessionTrust(cwd, row) {
19900
- const session = row.actor.session;
19901
- if (!session || row.self === true)
19902
- return null;
19903
- const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
19904
- const host = row.actor.host;
19905
- const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
19906
- if (!issuedOnly)
19907
- return null;
19908
- const binding = issuedBinding(cwd, session);
19909
- if (binding === null)
19910
- return "unissued_session";
19911
- return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
19912
- }
19913
- function trustReasons(cwd, row) {
19914
- if (row.integrity === "unverified")
19915
- return [row.integrityReason ?? "unsigned"];
19916
- if (row.integrity !== "verified")
19917
- return ["unsigned"];
19918
- const session = sessionTrust(cwd, row);
19919
- return session ? [session] : [];
19815
+ if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
19816
+ return true;
19817
+ if (TEST_PATH.test(file) || isDoc(file))
19818
+ return false;
19819
+ return file.split("/").map((segment, index, all) => index === all.length - 1 ? segment.replace(/\.[^.]+$/u, "") : segment).some((segment) => segment.toLowerCase().split(/[-_.]/u).some((token) => RISK_TOKENS.has(token)));
19920
19820
  }
19921
- function withIntegrity(cwd, rows) {
19922
- if (rows.every((row) => row.integrity !== undefined))
19923
- return rows;
19924
- const read = readLedger(cwd, { integrity: true });
19925
- const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
19926
- return rows.map((row) => {
19927
- if (row.integrity !== undefined)
19928
- return row;
19929
- const fresh = bySeq.get(row.seq);
19930
- return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
19821
+ var parseRiskPaths = (text) => text.split(`
19822
+ `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
19823
+ function computeLane(input) {
19824
+ const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
19825
+ const risky = new Set(riskFiles);
19826
+ const kinds = input.files.map((file) => ({
19827
+ file,
19828
+ kind: risky.has(file.path) ? "behavior" : kindOf(file)
19829
+ }));
19830
+ const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
19831
+ const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
19832
+ const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
19833
+ const result = (lane, reasons) => ({
19834
+ lane,
19835
+ reasons,
19836
+ lines,
19837
+ files: behavior.length,
19838
+ riskFiles
19931
19839
  });
19840
+ const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
19841
+ if (input.riskTier === "high")
19842
+ return result("full", ["the task judgment records high risk"]);
19843
+ if (riskFiles.length)
19844
+ return result("full", [
19845
+ `touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
19846
+ ]);
19847
+ if (!behavior.length)
19848
+ return result("none", [
19849
+ input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
19850
+ ]);
19851
+ if (lines >= fullMinLines)
19852
+ return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
19853
+ if (lines > selfMaxLines)
19854
+ return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
19855
+ if (behavior.length > selfMaxFiles)
19856
+ return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
19857
+ if (opaque.length)
19858
+ return result("focused", [
19859
+ `a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
19860
+ ]);
19861
+ return result("self", [
19862
+ `small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
19863
+ ]);
19932
19864
  }
19933
- var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
19934
- var independenceReasons = (row, authors) => {
19935
- const reasons = [];
19936
- if (!row.actor.session)
19937
- reasons.push("no_session");
19938
- else if (authoredBy(authors, row.actor.session))
19939
- reasons.push("author_session");
19940
- if (row.self === true && !reasons.includes("no_session"))
19941
- reasons.push("self");
19942
- return reasons;
19943
- };
19944
- function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
19945
- let effective = null;
19946
- for (const row of rows) {
19947
- if (!effective) {
19948
- effective = row;
19949
- continue;
19950
- }
19951
- if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
19952
- continue;
19953
- const rowIndependent = reasonsOf(row).length === 0;
19954
- const effIndependent = reasonsOf(effective).length === 0;
19955
- if (effIndependent && !rowIndependent && isCurrent(effective))
19956
- continue;
19957
- if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
19958
- continue;
19959
- effective = row;
19865
+ function untrackedStat(cwd, file) {
19866
+ try {
19867
+ const bytes = readFileSync4(path14.join(cwd, file));
19868
+ if (bytes.includes(0))
19869
+ return { path: file, added: 0, deleted: 0, binary: true };
19870
+ const text = bytes.toString("utf8");
19871
+ if (hasOpaqueLineBreak(text))
19872
+ return { path: file, added: 0, deleted: 0, binary: true };
19873
+ const lines = text.split(`
19874
+ `).length - (text.endsWith(`
19875
+ `) ? 1 : 0);
19876
+ return { path: file, added: lines, deleted: 0 };
19877
+ } catch {
19878
+ return { path: file, added: 0, deleted: 0, binary: true };
19960
19879
  }
19961
- return effective;
19962
19880
  }
19963
- function restackCarries(rows, branch, from, to) {
19964
- if (!from || !to)
19965
- return false;
19966
- const edges = new Map;
19967
- for (const row of rows)
19968
- if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
19969
- edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
19970
- const seen = new Set([from]);
19971
- const queue = [from];
19972
- while (queue.length) {
19973
- const next = queue.shift();
19974
- if (next === to)
19975
- return true;
19976
- for (const target of edges.get(next) ?? [])
19977
- if (!seen.has(target)) {
19978
- seen.add(target);
19979
- queue.push(target);
19881
+ var REGULAR = new Set(["100644", "100755"]);
19882
+ var ABSENT = /^0+$/u;
19883
+ function cosmeticFiles(cwd, fork, head) {
19884
+ const raw = diffRaw(cwd, fork, head);
19885
+ if (!raw)
19886
+ return null;
19887
+ const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
19888
+ const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
19889
+ if (!blobs)
19890
+ return null;
19891
+ const out = new Set;
19892
+ const opaque = new Set;
19893
+ for (const entry of candidates) {
19894
+ const before = blobs.get(entry.oldBlob);
19895
+ let after = blobs.get(entry.newBlob);
19896
+ if (!after && head === null && ABSENT.test(entry.newBlob))
19897
+ try {
19898
+ after = readFileSync4(path14.join(cwd, entry.path));
19899
+ } catch {
19900
+ after = undefined;
19980
19901
  }
19902
+ if (before && after && cosmeticChange(entry.path, before, after))
19903
+ out.add(entry.path);
19904
+ else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
19905
+ opaque.add(entry.path);
19981
19906
  }
19982
- return false;
19907
+ return { cosmetic: out, opaque };
19983
19908
  }
19984
- function checkVerdicts(cwd, branch, ledgerRows) {
19985
- const rows = withIntegrity(cwd, ledgerRows);
19986
- const head = branchHead(cwd, branch);
19987
- const fallbackBase = defaultBase(cwd);
19988
- const scope = branchScope(cwd, branch);
19989
- const keyCache = new Map;
19990
- const keysAt = (base, at) => {
19991
- if (!base || !at)
19992
- return { patchId: null, diffHash: null };
19993
- const cacheKey = `${base}\x00${at}`;
19994
- let key = keyCache.get(cacheKey);
19995
- if (!key) {
19996
- key = { patchId: patchId(cwd, base, at), diffHash: diffHash(cwd, base, at) };
19997
- keyCache.set(cacheKey, key);
19998
- }
19999
- return key;
20000
- };
20001
- const keyFor = (base) => keysAt(base, head);
20002
- const basisOf = (row) => {
20003
- const basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
20004
- return basis === "stale" && restackCarries(rows, branch, row.head, head) ? "carried" : basis;
20005
- };
20006
- const sameCode = (failed, row) => {
20007
- if (failed.head && failed.head === row.head)
20008
- return true;
20009
- const base = failed.base ?? fallbackBase;
20010
- const stored = (row.base ?? fallbackBase) === base && row.baseSha !== null && row.baseSha === failed.baseSha;
20011
- const keys = stored ? { patchId: row.patchId, diffHash: row.diffHash } : keysAt(base, row.head);
20012
- return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
20013
- };
20014
- const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
20015
- const failureAuthorCache = new Map;
20016
- const failureAuthors = (row) => {
20017
- let set = failureAuthorCache.get(row.seq);
20018
- if (!set) {
20019
- set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
20020
- failureAuthorCache.set(row.seq, set);
20021
- }
20022
- return set;
20023
- };
20024
- const reasonCache = new Map;
20025
- const reasonsOf = (row) => {
20026
- let reasons = reasonCache.get(row);
20027
- if (!reasons) {
20028
- const failing = FAILING.has(String(row.result));
20029
- reasons = [
20030
- ...trustReasons(cwd, row),
20031
- ...independenceReasons(row, failing ? failureAuthors(row) : authors)
20032
- ];
20033
- reasonCache.set(row, reasons);
20034
- }
20035
- return reasons;
20036
- };
20037
- const retired = new Set;
20038
- for (const row of rows)
20039
- if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
20040
- retired.add(row.supersedes);
20041
- const byKind = new Map;
20042
- for (const row of rows)
20043
- if (isLiveVerdict(row, retired) && scope(row)) {
20044
- const kind = typeof row.kind === "string" ? row.kind : "review";
20045
- byKind.set(kind, [...byKind.get(kind) ?? [], row]);
20046
- }
20047
- const verdicts = [];
20048
- const warnings = [];
20049
- for (const [kind, list] of byKind) {
20050
- const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
20051
- if (!row)
20052
- continue;
20053
- const basis = basisOf(row);
20054
- const current = basis !== "stale";
20055
- const independence = reasonsOf(row);
20056
- const reasons = [
20057
- ...current ? [] : ["stale"],
20058
- ...PASSING.has(String(row.result)) ? [] : ["not_passing"],
20059
- ...independence
20060
- ];
20061
- verdicts.push({
20062
- kind,
20063
- basis,
20064
- current,
20065
- independent: independence.length === 0,
20066
- accepted: reasons.length === 0,
20067
- reasons,
20068
- verdict: row
20069
- });
20070
- if (reasons.length === 0) {
20071
- for (const later of list)
20072
- if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
20073
- warnings.push(`the author's own (self) ${String(later.result)} ${kind} verdict ${later.id} by ${later.actor.session ?? "no session"} on the current code is newer than the accepted pass ${row.id}: read it before merging`);
20074
- }
20075
- }
20076
- for (const row of rows)
20077
- if (row.integrity === "unverified" && scope(row))
20078
- warnings.push(`ledger row ${row.id} (${row.type}) on ${row.branch ?? branch} is unverified: the workit CLI did not write it or it changed afterwards (workit ledger verify-integrity)`);
20079
- const tampered = ledgerTampering(cwd);
20080
- if (tampered.length) {
20081
- warnings.push(tamperedWarning(cwd, tampered));
20082
- for (const entry of verdicts) {
20083
- entry.accepted = false;
20084
- entry.reasons = [...entry.reasons, "ledger_tampered"];
20085
- }
20086
- }
20087
- verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
20088
- const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
20089
- const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
20090
- const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
20091
- const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
20092
- accepted: false,
20093
- verdict: failing?.verdict ?? null,
20094
- reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
20095
- };
20096
- const latestSelf = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && (STRONG_RESULTS.has(String(entry.verdict.result)) || FAILING.has(String(entry.verdict.result))));
20097
- const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
20098
- return {
20099
- branch,
20100
- head,
20101
- review: accepted.accepted ? "verified" : selfEntry ? "self-reviewed" : "unreviewed",
20102
- selfVerdict: accepted.accepted ? null : selfEntry?.verdict ?? null,
20103
- base: newestCurrent?.verdict.base ?? fallbackBase,
20104
- patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
20105
- current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
20106
- accepted,
20107
- verdicts,
20108
- authors: [...authors].toSorted(),
20109
- warnings
19909
+ function generatedAt(cwd, fork, paths) {
19910
+ const files = treeFilesNamed(cwd, fork, ".gitattributes");
19911
+ const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
19912
+ if (!files || !blobs)
19913
+ return new Set;
19914
+ const texts = new Map;
19915
+ for (const [file, id] of files) {
19916
+ const bytes = blobs.get(id);
19917
+ if (bytes)
19918
+ texts.set(file, bytes.toString("utf8"));
19919
+ }
19920
+ return generatedByAttributes(texts, paths);
19921
+ }
19922
+ var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
19923
+ function laneFiles(cwd, fork, head, attributesAt = fork) {
19924
+ const stats = diffNumstat(cwd, fork, head);
19925
+ const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
19926
+ if (!stats || !cosmetic)
19927
+ return null;
19928
+ const files = [
19929
+ ...stats.map((entry) => ({
19930
+ ...entry,
19931
+ ...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
19932
+ cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
19933
+ })),
19934
+ ...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
19935
+ ];
19936
+ const generated = generatedAt(cwd, attributesAt, files.map((entry) => entry.path));
19937
+ return files.map((entry) => ({
19938
+ ...entry,
19939
+ generated: generated.has(entry.path)
19940
+ }));
19941
+ }
19942
+ function driftFiles(cwd, range) {
19943
+ if (range.fromFork === range.toFork)
19944
+ return laneFiles(cwd, range.from, range.to, range.toFork);
19945
+ const drifted = ownChangeDrift(cwd, range);
19946
+ const stats = drifted ? diffNumstat(cwd, range.from, range.to) : null;
19947
+ if (!drifted || !stats)
19948
+ return null;
19949
+ const byPath = new Map(stats.map((entry) => [entry.path, entry]));
19950
+ const generated = generatedAt(cwd, range.toFork, drifted);
19951
+ return drifted.map((file) => ({
19952
+ path: file,
19953
+ added: byPath.get(file)?.added ?? 0,
19954
+ deleted: byPath.get(file)?.deleted ?? 0,
19955
+ ...byPath.get(file)?.binary ? { binary: true } : {},
19956
+ cosmetic: false,
19957
+ generated: generated.has(file)
19958
+ }));
19959
+ }
19960
+ function branchLane(cwd, options) {
19961
+ const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
19962
+ const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
19963
+ const head = options.workingTree ? null : options.head;
19964
+ const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
19965
+ const files = fork ? laneFiles(cwd, fork, head) : null;
19966
+ const riskTier = options.judgment?.riskTier ?? null;
19967
+ if (!fork || !files) {
19968
+ const high = riskTier === "high";
19969
+ return {
19970
+ lane: high ? "full" : "focused",
19971
+ reasons: [
19972
+ `the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
19973
+ ...high ? ["the task judgment records high risk"] : []
19974
+ ],
19975
+ lines: 0,
19976
+ files: 0,
19977
+ riskFiles: [],
19978
+ base,
19979
+ head: options.head
19980
+ };
19981
+ }
19982
+ const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
19983
+ return {
19984
+ ...computeLane({
19985
+ files,
19986
+ riskTier,
19987
+ riskPaths: [...repoRisk, ...options.riskPaths ?? []]
19988
+ }),
19989
+ base,
19990
+ head: options.head
20110
19991
  };
20111
19992
  }
20112
- var OBSERVED_ONLY = new Set([
20113
- "check",
20114
- "merge.unverified",
20115
- "policy.judged",
20116
- ...PR_ROW_TYPES
20117
- ]);
20118
-
20119
- // packages/workit-core/src/core/policy/lane.ts
20120
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
20121
- import path16 from "node:path";
19993
+ var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
20122
19994
 
20123
- // packages/workit-core/src/core/pr-create.ts
20124
- import fs13 from "node:fs";
20125
- import path15 from "node:path";
20126
- import { spawnSync as spawnSync10 } from "node:child_process";
20127
- // packages/workit-core/src/forge/pr-body.ts
20128
- function parseGhIssue(value) {
20129
- const m = /issues\/(\d+)/.exec(value);
20130
- return m ? m[1] : String(value).trim().replace(/^#/, "");
19995
+ // packages/workit-core/src/core/policy/carry.ts
19996
+ var TEST_ONLY_KINDS = new Set(["doc", "cosmetic", "generated", "test"]);
19997
+ function verdictDrift(cwd, input) {
19998
+ const since = input.from;
19999
+ const stale = (reason, files = null, focused = false) => ({
20000
+ carries: false,
20001
+ since,
20002
+ files,
20003
+ focused,
20004
+ reason
20005
+ });
20006
+ const fromFork = mergeBase(cwd, input.fromBase, input.from);
20007
+ const toFork = mergeBase(cwd, input.toBase, input.to);
20008
+ const files = fromFork && toFork ? driftFiles(cwd, { from: input.from, to: input.to, fromFork, toFork }) : null;
20009
+ if (!fromFork || !toFork || !files)
20010
+ return stale(`the change since ${since.slice(0, 12)} cannot be read`);
20011
+ const riskPaths = [fromFork, toFork].flatMap((fork) => parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? ""));
20012
+ const lane = computeLane({ files, riskPaths });
20013
+ const paths = files.map((file) => file.path);
20014
+ if (lane.lane === "none")
20015
+ return { carries: true, rule: "lane-none", since, files: paths };
20016
+ if (input.kind === "unit" && lane.lane === "self" && files.every((file) => TEST_ONLY_KINDS.has(kindOf(file))))
20017
+ return { carries: true, rule: "tests-only", since, files: paths };
20018
+ return stale(lane.reasons[0] ?? `lane ${lane.lane}`, paths, lane.lane === "self");
20131
20019
  }
20132
20020
 
20133
- // packages/workit-core/src/core/pr-create.ts
20134
- function whichOnPath(tool) {
20135
- const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
20136
- for (const dir of (process.env.PATH ?? "").split(path15.delimiter)) {
20137
- if (!dir)
20138
- continue;
20139
- for (const name of names) {
20140
- const candidate = path15.join(dir, name);
20141
- try {
20142
- fs13.accessSync(candidate, fs13.constants.X_OK);
20143
- return candidate;
20144
- } catch {}
20145
- }
20146
- }
20147
- return null;
20148
- }
20149
- var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
20150
- var pushRemote = (cwd) => {
20151
- const result = spawnSync10("git", ["remote", "get-url", "--push", "--all", "origin"], {
20021
+ // packages/workit-core/src/ledger.ts
20022
+ var LEDGER_VERSION = 1;
20023
+ var MAX_LINE_BYTES = 4096;
20024
+ var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
20025
+ var FAILING = new Set(["failed", "blocked"]);
20026
+ var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
20027
+ var PR_ROW_TYPES = new Set([
20028
+ "pr.created",
20029
+ "pr.status",
20030
+ "pr.merged",
20031
+ "pr.linked"
20032
+ ]);
20033
+ var err = (code, error, unblock) => ({
20034
+ ok: false,
20035
+ code,
20036
+ error,
20037
+ ...unblock ? { unblock } : {}
20038
+ });
20039
+ var gitRun2 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
20040
+ const run = spawnSync9("git", args, {
20152
20041
  cwd,
20153
- encoding: "utf8"
20042
+ encoding: "utf8",
20043
+ timeout: timeoutMs,
20044
+ killSignal: "SIGKILL",
20045
+ windowsHide: true,
20046
+ maxBuffer: 256 * 1024 * 1024,
20047
+ env: {
20048
+ ...process.env,
20049
+ GIT_TERMINAL_PROMPT: "0",
20050
+ GIT_OPTIONAL_LOCKS: "0",
20051
+ LC_ALL: "C",
20052
+ LANGUAGE: "C"
20053
+ }
20154
20054
  });
20155
- if (result.status !== 0)
20156
- return null;
20157
- const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
20158
- return urls.length === 1 ? urls[0] : null;
20055
+ return {
20056
+ ok: run.status === 0,
20057
+ stdout: run.stdout ?? "",
20058
+ stderr: run.error ? String(run.error.message) : run.stderr ?? ""
20059
+ };
20159
20060
  };
20160
-
20161
- // packages/workit-core/src/forge/redact.ts
20162
- var KEY = String.raw`[A-Za-z0-9_.-]*?(?:token|secret|password|passwd|pwd|api[_-]?key|access[_-]?key|account[_-]?key|private[_-]?key|client[_-]?secret|credentials?|authorization|cookie|signature)[A-Za-z0-9_.-]*`;
20163
- var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
20164
- var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
20165
- var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
20166
-
20167
- // packages/workit-core/src/git/ops.ts
20168
- var COMMIT_TIMEOUT_MS = 10 * 60000;
20169
-
20170
- // packages/workit-core/src/core/policy/attributes.ts
20171
- function patternRegex(dir, pattern) {
20172
- if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
20061
+ var git4 = (cwd, args) => {
20062
+ const run = gitRun2(cwd, args);
20063
+ const value = run.ok ? run.stdout.trim() : "";
20064
+ return value ? value : null;
20065
+ };
20066
+ var safeRef2 = (value) => value.length > 0 && !value.startsWith("-");
20067
+ function resolveCommit(cwd, ref) {
20068
+ if (!safeRef2(ref))
20173
20069
  return null;
20174
- const anchored = pattern.includes("/");
20175
- const body = pattern.replace(/^\//u, "");
20176
- let source = "";
20177
- for (let index = 0;index < body.length; index += 1) {
20178
- const char = body[index];
20179
- if (char === "*" && body[index + 1] === "*") {
20180
- const slash = body[index + 2] === "/";
20181
- source += slash ? "(?:.*/)?" : ".*";
20182
- index += slash ? 2 : 1;
20183
- } else if (char === "*")
20184
- source += "[^/]*";
20185
- else if (char === "?")
20186
- source += "[^/]";
20187
- else if (char === "[") {
20188
- const end = body.indexOf("]", index + 2);
20189
- if (end < 0)
20190
- return null;
20191
- const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
20192
- source += `[${set}]`;
20193
- index = end;
20194
- } else if (char === "\\" && index + 1 < body.length) {
20195
- source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
20196
- index += 1;
20197
- } else
20198
- source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
20199
- }
20200
- const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
20201
- try {
20202
- return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
20203
- } catch {
20070
+ return git4(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
20071
+ }
20072
+ function branchHead(cwd, branch) {
20073
+ if (!safeRef2(branch))
20204
20074
  return null;
20205
- }
20075
+ return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
20206
20076
  }
20207
- function rulesOf(dir, text) {
20208
- const out = [];
20209
- for (const raw of text.split(`
20077
+ function diffHash(cwd, base, head) {
20078
+ if (!safeRef2(base) || !safeRef2(head))
20079
+ return null;
20080
+ const diff = gitRun2(cwd, [
20081
+ "diff",
20082
+ "-U3",
20083
+ "--diff-algorithm=myers",
20084
+ "--indent-heuristic",
20085
+ "--no-color",
20086
+ "--no-ext-diff",
20087
+ "--no-textconv",
20088
+ "--no-renames",
20089
+ "--full-index",
20090
+ "--binary",
20091
+ "--src-prefix=a/",
20092
+ "--dst-prefix=b/",
20093
+ `${base}...${head}`,
20094
+ "--"
20095
+ ], GIT_TIMEOUTS.worktree);
20096
+ if (!diff.ok || !diff.stdout)
20097
+ return null;
20098
+ const hash = createHash9("sha256");
20099
+ for (const line of diff.stdout.split(`
20210
20100
  `)) {
20211
- const line = raw.trim();
20212
- if (!line || line.startsWith("#") || line.startsWith("[attr]"))
20101
+ if (line.startsWith("index "))
20213
20102
  continue;
20214
- const [pattern, ...attributes] = line.split(/\s+/u);
20215
- let generated = null;
20216
- for (const attribute of attributes) {
20217
- if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
20218
- generated = true;
20219
- else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
20220
- generated = false;
20221
- }
20222
- if (generated === null)
20223
- continue;
20224
- const regex = patternRegex(dir, pattern);
20225
- if (regex)
20226
- out.push({ regex, generated });
20103
+ hash.update(line.startsWith("@@ ") ? "@@" : line);
20104
+ hash.update(`
20105
+ `);
20227
20106
  }
20228
- return out;
20107
+ return `sha256:${hash.digest("hex")}`;
20229
20108
  }
20230
- function generatedByAttributes(files, paths) {
20231
- const rules = [...files].map(([file, text]) => {
20232
- const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
20233
- return {
20234
- depth: dir ? dir.split("/").length : 0,
20235
- rules: rulesOf(dir, text)
20236
- };
20237
- }).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
20238
- const out = new Set;
20239
- for (const file of paths) {
20240
- const last = rules.findLast((rule) => rule.regex.test(file));
20241
- if (last?.generated)
20242
- out.add(file);
20243
- }
20244
- return out;
20109
+ function storeRoot(cwd) {
20110
+ const location = resolveStore(path15.resolve(cwd));
20111
+ if (location instanceof Error)
20112
+ return err("unavailable", location.message.replace(/; fix the repository.*$/u, ""), "fix the repository (git status should work here) or run from a non-git directory");
20113
+ return { ok: true, value: { root: location.dir, shared: location.shared } };
20245
20114
  }
20246
-
20247
- // packages/workit-core/src/core/policy/substance.ts
20248
- var SLASH_BLOCK = [["/*", "*/"]];
20249
- var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
20250
- var SYNTAXES = Object.fromEntries([
20251
- ...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
20252
- ...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
20253
- ext,
20254
- { ...C_LIKE, triple: true }
20255
- ]),
20256
- ["go", { ...C_LIKE, rawBacktick: true }],
20257
- ...["css", "scss", "less"].map((ext) => [
20258
- ext,
20259
- { line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
20260
- ]),
20261
- ...["py", "pyi"].map((ext) => [
20262
- ext,
20263
- { line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
20264
- ]),
20265
- ["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
20266
- ...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
20267
- ext,
20268
- { line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
20269
- ])
20115
+ function ledgerPath(cwd) {
20116
+ const root = storeRoot(cwd);
20117
+ return root.ok ? { ok: true, value: path15.join(root.value.root, "ledger", "ledger.jsonl") } : root;
20118
+ }
20119
+ var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
20120
+ var NETWORK_FS_TYPES = new Set([
20121
+ 26985,
20122
+ 20859,
20123
+ 4266872130,
20124
+ 4283649346,
20125
+ 1702057286,
20126
+ 1397113167,
20127
+ 12805120,
20128
+ 16914839,
20129
+ 1196443219,
20130
+ 198183888
20270
20131
  ]);
20271
- var DIRECTIVE = /@ts-|eslint|prettier|biome|oxlint|istanbul|\bc8\b|\bv8\b|@jsx|@flow|@vite|webpack|__PURE__|__NO_SIDE_EFFECTS__|source(?:Mapping)?URL|^\/\s*<|go:|\+build|nolint|noqa|type:|pyright|mypy|pylint|pragma|coding[:=]|fmt:|NOSONAR|@license|@preserve|@refresh|environment|language=|cython|ruff|isort|deno-|@vitest|@jest|swiftlint|ktlint|checkstyle|suppress|nullable|#(?:end)?region/iu;
20272
- var extensionOf = (file) => {
20273
- const name = file.slice(file.lastIndexOf("/") + 1);
20274
- const dot = name.lastIndexOf(".");
20275
- return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
20132
+ var isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0);
20133
+ var onNetworkFs = (dir) => {
20134
+ if (process.platform !== "linux")
20135
+ return false;
20136
+ try {
20137
+ return isNetworkFsType(fs12.statfsSync(dir).type);
20138
+ } catch {
20139
+ return false;
20140
+ }
20276
20141
  };
20277
- var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
20278
- var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
20279
- var SPACE = /[ \t\r\f\v]/u;
20280
- var WORD = /[\w$]/u;
20281
- var NO_SPACE_AFTER = "([{,;";
20282
- var NO_SPACE_BEFORE = ")]},;";
20283
- var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
20284
- var REGEX_KEYWORDS = new Set([
20285
- "return",
20286
- "typeof",
20287
- "instanceof",
20288
- "case",
20289
- "do",
20290
- "else",
20291
- "in",
20292
- "of",
20293
- "new",
20294
- "delete",
20295
- "void",
20296
- "throw",
20297
- "yield",
20298
- "await"
20299
- ]);
20300
- function substanceOf(file, text) {
20301
- const syntax = syntaxOf(file);
20302
- if (!syntax)
20303
- return plainSubstance(text);
20304
- const markup = syntax.block.some(([open]) => open === "<!--");
20305
- if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
20306
- return plainSubstance(text);
20307
- return lex(syntax, text);
20308
- }
20309
- function plainSubstance(text) {
20310
- return text.split(`
20311
- `).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
20312
- `);
20313
- }
20314
- function lex(syntax, text) {
20315
- const collapse = !syntax.indented;
20316
- let out = "";
20317
- let lineStart = 0;
20318
- let kept = 0;
20319
- let pendingSpace = false;
20320
- const frames = [{ kind: "code", braces: 0 }];
20321
- let index = 0;
20322
- if (text.startsWith("#!")) {
20323
- const end = text.indexOf(`
20324
- `);
20325
- out = end < 0 ? text : text.slice(0, end);
20326
- kept = out.length;
20327
- index = end < 0 ? text.length : end;
20142
+ var LOCK_WAIT_MS = 5000;
20143
+ var LOCK_STALE_MS = 30000;
20144
+ var LOCK_OWNER = "owner";
20145
+ var lockToken = (lock) => {
20146
+ try {
20147
+ return fs12.readFileSync(path15.join(lock, LOCK_OWNER), "utf8");
20148
+ } catch {
20149
+ return null;
20328
20150
  }
20329
- const emitCode = (chunk) => {
20330
- if (collapse && pendingSpace && out.length > lineStart) {
20331
- const before = out[out.length - 1];
20332
- if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
20333
- out += " ";
20334
- }
20335
- pendingSpace = false;
20336
- out += chunk;
20337
- };
20338
- const emitVerbatim = (chunk) => {
20339
- emitCode(chunk);
20340
- kept = out.length;
20341
- };
20342
- const newline = () => {
20343
- if (!collapse) {
20344
- let end = out.length;
20345
- while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
20346
- end -= 1;
20347
- out = out.slice(0, end);
20348
- }
20349
- pendingSpace = false;
20350
- if (out.length > lineStart) {
20351
- out += `
20352
- `;
20353
- lineStart = out.length;
20354
- }
20355
- };
20356
- const regexAllowed = () => {
20357
- let end = out.length;
20358
- while (end > 0 && /\s/u.test(out[end - 1]))
20359
- end -= 1;
20360
- if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
20361
- return true;
20362
- let start = end;
20363
- while (start > 0 && WORD.test(out[start - 1]))
20364
- start -= 1;
20365
- return REGEX_KEYWORDS.has(out.slice(start, end));
20366
- };
20367
- const scanString = (quote, escapes, multiline) => {
20368
- const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
20369
- const fstring = syntax.fstring === true && /f/iu.test(prefix);
20370
- let end = index + quote.length;
20371
- while (end < text.length) {
20372
- const char = text[end];
20373
- if (escapes && char === "\\") {
20374
- end += 2;
20375
- continue;
20376
- }
20377
- if (text.startsWith(quote, end)) {
20378
- if (fstring && unbalanced(text.slice(index + quote.length, end)))
20379
- return false;
20380
- emitVerbatim(text.slice(index, end + quote.length));
20381
- index = end + quote.length;
20382
- return true;
20383
- }
20384
- if (char === `
20385
- ` && !multiline)
20386
- return false;
20387
- end += 1;
20388
- }
20389
- return false;
20390
- };
20391
- while (index < text.length) {
20392
- const frame = frames[frames.length - 1];
20393
- const char = text[index];
20394
- if (frame.kind === "template") {
20395
- if (char === "\\") {
20396
- emitVerbatim(text.slice(index, index + 2));
20397
- index += 2;
20398
- } else if (char === "`") {
20399
- emitVerbatim(char);
20400
- frames.pop();
20401
- index += 1;
20402
- } else if (text.startsWith("${", index)) {
20403
- emitVerbatim("${");
20404
- frames.push({ kind: "code", braces: 0 });
20405
- index += 2;
20406
- } else {
20407
- out += char;
20408
- kept = out.length;
20409
- index += 1;
20410
- }
20411
- continue;
20412
- }
20413
- if (char === `
20414
- `) {
20415
- newline();
20416
- index += 1;
20417
- continue;
20418
- }
20419
- if (SPACE.test(char)) {
20420
- if (collapse)
20421
- pendingSpace = true;
20422
- else
20423
- out += char;
20424
- index += 1;
20425
- continue;
20426
- }
20427
- const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
20428
- if (lineOpener) {
20429
- const end = text.indexOf(`
20430
- `, index);
20431
- const comment = text.slice(index, end < 0 ? text.length : end);
20432
- if (DIRECTIVE.test(comment.slice(lineOpener.length)))
20433
- emitVerbatim(comment);
20434
- else
20435
- pendingSpace = true;
20436
- index = end < 0 ? text.length : end;
20437
- continue;
20438
- }
20439
- const block = syntax.block.find(([open]) => text.startsWith(open, index));
20440
- if (block) {
20441
- const end = text.indexOf(block[1], index + block[0].length);
20442
- if (end < 0)
20443
- return null;
20444
- const comment = text.slice(index, end + block[1].length);
20445
- if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
20446
- emitVerbatim(comment);
20447
- else
20448
- pendingSpace = true;
20449
- if (comment.includes(`
20450
- `))
20451
- newline();
20452
- index = end + block[1].length;
20453
- continue;
20454
- }
20455
- if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
20456
- if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
20151
+ };
20152
+ var lockAgeMs = (lock) => {
20153
+ for (const target of [path15.join(lock, LOCK_OWNER), lock]) {
20154
+ try {
20155
+ return Date.now() - fs12.statSync(target).mtimeMs;
20156
+ } catch {}
20157
+ }
20158
+ return null;
20159
+ };
20160
+ var ledgerLock = {
20161
+ acquire(lock) {
20162
+ try {
20163
+ fs12.mkdirSync(lock);
20164
+ } catch (error) {
20165
+ if (error.code === "EEXIST")
20457
20166
  return null;
20458
- continue;
20167
+ throw error;
20459
20168
  }
20460
- if (syntax.quotes.includes(char)) {
20461
- if (!scanString(char, true, Boolean(syntax.multiline)))
20462
- return null;
20463
- continue;
20169
+ const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
20170
+ fs12.writeFileSync(path15.join(lock, LOCK_OWNER), token);
20171
+ return token;
20172
+ },
20173
+ release(lock, token) {
20174
+ if (lockToken(lock) !== token)
20175
+ return false;
20176
+ const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
20177
+ try {
20178
+ fs12.renameSync(lock, tomb);
20179
+ } catch {
20180
+ return false;
20464
20181
  }
20465
- if (syntax.literal?.includes(char)) {
20466
- if (!scanString(char, false, false))
20467
- return null;
20468
- continue;
20182
+ if (lockToken(tomb) !== token) {
20183
+ try {
20184
+ fs12.renameSync(tomb, lock);
20185
+ } catch {}
20186
+ return false;
20469
20187
  }
20470
- if (syntax.rawBacktick && char === "`") {
20471
- if (!scanString("`", false, true))
20472
- return null;
20473
- continue;
20188
+ fs12.rmSync(tomb, { recursive: true, force: true });
20189
+ return true;
20190
+ },
20191
+ token: lockToken,
20192
+ reap(lock, observed) {
20193
+ const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
20194
+ try {
20195
+ fs12.renameSync(lock, tomb);
20196
+ } catch {
20197
+ return false;
20474
20198
  }
20475
- if (syntax.js && char === "`") {
20476
- emitVerbatim(char);
20477
- frames.push({ kind: "template" });
20478
- index += 1;
20479
- continue;
20199
+ if (lockToken(tomb) === observed) {
20200
+ fs12.rmSync(tomb, { recursive: true, force: true });
20201
+ return true;
20480
20202
  }
20481
- if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
20203
+ try {
20204
+ fs12.renameSync(tomb, lock);
20205
+ } catch {
20206
+ fs12.rmSync(tomb, { recursive: true, force: true });
20207
+ }
20208
+ return false;
20209
+ }
20210
+ };
20211
+ function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
20212
+ const lock = `${file}.lock`;
20213
+ const deadline = Date.now() + waitMs;
20214
+ let token = null;
20215
+ for (;; ) {
20216
+ token = ledgerLock.acquire(lock);
20217
+ if (token)
20218
+ break;
20219
+ const observed = lockToken(lock);
20220
+ const age = lockAgeMs(lock);
20221
+ if (age !== null && age > LOCK_STALE_MS && ledgerLock.reap(lock, observed))
20222
+ continue;
20223
+ if (Date.now() >= deadline)
20482
20224
  return null;
20483
- if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
20484
- const lineEnd = text.indexOf(`
20485
- `, index);
20486
- if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
20225
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
20226
+ }
20227
+ try {
20228
+ return fn();
20229
+ } finally {
20230
+ ledgerLock.release(lock, token);
20231
+ }
20232
+ }
20233
+ var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
20234
+ var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
20235
+ ${body}`);
20236
+ var TAIL_BYTES = 64 * 1024;
20237
+ var lastLine = (fd, size) => {
20238
+ for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
20239
+ const buffer = Buffer.alloc(span);
20240
+ fs12.readSync(fd, buffer, 0, span, size - span);
20241
+ const end = buffer.lastIndexOf(10);
20242
+ if (end < 0) {
20243
+ if (span === size)
20487
20244
  return null;
20245
+ continue;
20488
20246
  }
20489
- if (syntax.js && char === "/" && regexAllowed()) {
20490
- let end = index + 1;
20491
- let inClass = false;
20492
- while (end < text.length && (inClass || text[end] !== "/")) {
20493
- if (text[end] === `
20494
- `)
20247
+ let stop = end;
20248
+ let text = "";
20249
+ while (stop >= 0) {
20250
+ const start = buffer.lastIndexOf(10, stop - 1);
20251
+ text = buffer.toString("utf8", start + 1, stop).trim();
20252
+ if (text) {
20253
+ if (start >= 0 || span === size)
20254
+ return text;
20255
+ break;
20256
+ }
20257
+ if (start < 0) {
20258
+ if (span === size)
20495
20259
  return null;
20496
- if (text[end] === "\\")
20497
- end += 1;
20498
- else if (text[end] === "[")
20499
- inClass = true;
20500
- else if (text[end] === "]")
20501
- inClass = false;
20502
- end += 1;
20260
+ break;
20503
20261
  }
20504
- if (end >= text.length)
20505
- return null;
20506
- emitVerbatim(text.slice(index, end + 1));
20507
- index = end + 1;
20262
+ stop = start;
20263
+ }
20264
+ if (span === size)
20265
+ return text || null;
20266
+ }
20267
+ return null;
20268
+ };
20269
+ var tighten = (fd) => {
20270
+ try {
20271
+ if ((fs12.fstatSync(fd).mode & 63) !== 0)
20272
+ fs12.fchmodSync(fd, 384);
20273
+ } catch {}
20274
+ };
20275
+ function appendLine(file, build, options) {
20276
+ const write = () => {
20277
+ const fd = fs12.openSync(file, "a+", 384);
20278
+ try {
20279
+ if (process.platform !== "win32")
20280
+ tighten(fd);
20281
+ const size = fs12.fstatSync(fd).size;
20282
+ let fence = "";
20283
+ if (size > 0) {
20284
+ const last = Buffer.alloc(1);
20285
+ fs12.readSync(fd, last, 0, 1, size - 1);
20286
+ if (last[0] !== 10)
20287
+ fence = `
20288
+ `;
20289
+ }
20290
+ const line = build(lastLine(fd, size));
20291
+ if (!line.ok)
20292
+ return line;
20293
+ const bytes = Buffer.from(fence + line.value);
20294
+ const written = fs12.writeSync(fd, bytes);
20295
+ if (written !== bytes.length)
20296
+ return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
20297
+ return { ok: true, value: undefined };
20298
+ } finally {
20299
+ fs12.closeSync(fd);
20300
+ }
20301
+ };
20302
+ try {
20303
+ fs12.mkdirSync(path15.dirname(file), { recursive: true });
20304
+ if (!options.forceLock && !onNetworkFs(path15.dirname(file)))
20305
+ return write();
20306
+ const locked = withLedgerLock(file, write);
20307
+ return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
20308
+ } catch (error) {
20309
+ return err("unavailable", `cannot append to ${file}: ${error.message}`);
20310
+ }
20311
+ }
20312
+ var sealed = (row, id, at, previous) => {
20313
+ const { prevHash: _prev, rowHash: _row, ...fields } = row;
20314
+ const prevHash = previous === null ? null : sha2562(previous);
20315
+ const body = { v: LEDGER_VERSION, id, at, ...fields, prevHash };
20316
+ const full = { ...body, rowHash: chainHash(prevHash, JSON.stringify(body)) };
20317
+ return { full, line: `${JSON.stringify(full)}
20318
+ ` };
20319
+ };
20320
+ function appendRaw(cwd, row, options) {
20321
+ const file = ledgerPath(cwd);
20322
+ if (!file.ok)
20323
+ return file;
20324
+ const id = newId2();
20325
+ const at = (options.now ?? new Date).toISOString();
20326
+ let full = null;
20327
+ const written = appendLine(file.value, (previous) => {
20328
+ const next = sealed(row, id, at, previous);
20329
+ const bytes = Buffer.byteLength(next.line);
20330
+ if (bytes > MAX_LINE_BYTES)
20331
+ return err("invalid_input", `ledger row is ${bytes} bytes; the limit is ${MAX_LINE_BYTES}. Shorten the text and pass details by --ref <path|url>`);
20332
+ full = next.full;
20333
+ return { ok: true, value: next.line };
20334
+ }, options);
20335
+ return written.ok ? { ok: true, value: full } : written;
20336
+ }
20337
+ function observedRowBytes(row) {
20338
+ const sample = sealed({ ...row, observer: "workit_cli" }, newId2(), new Date().toISOString(), "");
20339
+ return Buffer.byteLength(sample.line);
20340
+ }
20341
+ function appendObserved(cwd, row, options = {}) {
20342
+ return appendRaw(cwd, { ...row, observer: "workit_cli" }, options);
20343
+ }
20344
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
20345
+ var str2 = (value) => typeof value === "string" ? value : null;
20346
+ var normalizeActor = (value) => {
20347
+ const actor = isRecord3(value) ? value : {};
20348
+ return {
20349
+ host: str2(actor.host) ?? "unknown",
20350
+ session: str2(actor.session),
20351
+ agentId: str2(actor.agentId)
20352
+ };
20353
+ };
20354
+ function supersedeAllowed(by, target, onBranch = (row) => (by.branch ?? null) === (row.branch ?? null)) {
20355
+ if (by.type !== target.type)
20356
+ return `a ${by.type} row cannot supersede a ${target.type} row`;
20357
+ if (!by.actor.session || by.actor.session !== target.actor.session)
20358
+ return "only the session that wrote a row can supersede it";
20359
+ if (!onBranch(target))
20360
+ return `the row is on another branch (${String(target.branch ?? "none")}); a row supersedes only one on its own branch`;
20361
+ if (by.type === "verdict" && (by.kind ?? "review") !== (target.kind ?? "review"))
20362
+ return `a ${String(by.kind ?? "review")} verdict cannot supersede a ${String(target.kind ?? "review")} verdict; record it with --kind ${String(target.kind ?? "review")}`;
20363
+ if (by.type === "verdict" && by.self === true && target.self !== true)
20364
+ return "a self verdict cannot replace an independent verdict";
20365
+ return null;
20366
+ }
20367
+ function readLedger(cwd, options = {}) {
20368
+ const file = ledgerPath(cwd);
20369
+ if (!file.ok)
20370
+ return file;
20371
+ let text = "";
20372
+ let stat = null;
20373
+ try {
20374
+ stat = fs12.statSync(file.value);
20375
+ text = fs12.readFileSync(file.value, "utf8");
20376
+ } catch (error) {
20377
+ if (error.code === "ENOENT")
20378
+ return {
20379
+ ok: true,
20380
+ value: {
20381
+ path: file.value,
20382
+ rows: [],
20383
+ skipped: 0,
20384
+ integrity: options.integrity ? { chained: 0, legacy: 0, unverified: [] } : null
20385
+ }
20386
+ };
20387
+ return err("unavailable", `cannot read ${file.value}: ${error.message}`);
20388
+ }
20389
+ const rows = [];
20390
+ let skipped = 0;
20391
+ const integrity = { chained: 0, legacy: 0, unverified: [] };
20392
+ const earlier = new Set([null]);
20393
+ let chainStarted = false;
20394
+ const ids = new Set;
20395
+ for (const raw of text.split(`
20396
+ `)) {
20397
+ const trimmed = raw.trim();
20398
+ if (!trimmed)
20399
+ continue;
20400
+ const linked = (prevHash) => earlier.has(prevHash);
20401
+ if (options.integrity)
20402
+ earlier.add(sha2562(trimmed));
20403
+ let parsed;
20404
+ try {
20405
+ parsed = JSON.parse(trimmed);
20406
+ } catch {
20407
+ skipped += 1;
20508
20408
  continue;
20509
20409
  }
20510
- if (frames.length > 1 && frame.kind === "code") {
20511
- if (char === "{")
20512
- frame.braces += 1;
20513
- else if (char === "}") {
20514
- if (frame.braces === 0) {
20515
- emitVerbatim(char);
20516
- frames.pop();
20517
- index += 1;
20518
- continue;
20519
- }
20520
- frame.braces -= 1;
20521
- }
20410
+ if (!isRecord3(parsed) || !str2(parsed.id) || !str2(parsed.type) || !str2(parsed.at)) {
20411
+ skipped += 1;
20412
+ continue;
20522
20413
  }
20523
- emitCode(char);
20524
- index += 1;
20414
+ let rowIntegrity = options.integrity ? "verified" : undefined;
20415
+ let reason = null;
20416
+ if (!options.integrity) {} else if (typeof parsed.rowHash === "string") {
20417
+ chainStarted = true;
20418
+ const { rowHash, ...body } = parsed;
20419
+ const prevHash = str2(parsed.prevHash);
20420
+ if (chainHash(prevHash, JSON.stringify(body)) !== rowHash)
20421
+ reason = "hash_mismatch";
20422
+ else if (!linked(prevHash))
20423
+ reason = "broken_link";
20424
+ } else if (chainStarted)
20425
+ reason = "unsigned";
20426
+ else
20427
+ rowIntegrity = "legacy";
20428
+ if (options.integrity && !reason && ids.has(parsed.id))
20429
+ reason = "duplicate_id";
20430
+ if (options.integrity)
20431
+ ids.add(parsed.id);
20432
+ if (reason)
20433
+ rowIntegrity = "unverified";
20434
+ const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
20435
+ const row = {
20436
+ ...parsed,
20437
+ v: typeof parsed.v === "number" ? parsed.v : LEDGER_VERSION,
20438
+ id: parsed.id,
20439
+ at: parsed.at,
20440
+ type: parsed.type,
20441
+ actor: normalizeActor(parsed.actor),
20442
+ branch: str2(parsed.branch),
20443
+ head: str2(parsed.head),
20444
+ base: str2(parsed.base),
20445
+ baseSha: str2(parsed.baseSha),
20446
+ patchId: str2(parsed.patchId),
20447
+ diffHash: str2(parsed.diffHash),
20448
+ tree: str2(parsed.tree),
20449
+ dirty: typeof parsed.dirty === "boolean" ? parsed.dirty : null,
20450
+ ...pr === undefined ? {} : { pr },
20451
+ ...str2(parsed.supersedes) ? { supersedes: parsed.supersedes } : {},
20452
+ seq: rows.length + 1,
20453
+ superseded: false,
20454
+ supersedeIgnored: false,
20455
+ ...rowIntegrity ? { integrity: rowIntegrity } : {},
20456
+ ...reason ? { integrityReason: reason } : {}
20457
+ };
20458
+ rows.push(row);
20459
+ if (!options.integrity)
20460
+ continue;
20461
+ if (rowIntegrity === "verified")
20462
+ integrity.chained += 1;
20463
+ else if (rowIntegrity === "legacy")
20464
+ integrity.legacy += 1;
20465
+ else if (reason)
20466
+ integrity.unverified.push({
20467
+ seq: row.seq,
20468
+ id: row.id,
20469
+ type: row.type,
20470
+ branch: row.branch,
20471
+ reason
20472
+ });
20525
20473
  }
20526
- if (frames.length !== 1)
20527
- return null;
20528
- newline();
20529
- return out;
20530
- }
20531
- function unbalanced(body) {
20532
- let depth = 0;
20533
- for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
20534
- if (part === "{")
20535
- depth += 1;
20536
- else if (part === "}")
20537
- depth = Math.max(0, depth - 1);
20474
+ const scopes = new Map;
20475
+ const scopeOf = (branch) => {
20476
+ let scope = scopes.get(branch);
20477
+ if (!scope)
20478
+ scopes.set(branch, scope = branchScope(cwd, branch));
20479
+ return scope;
20480
+ };
20481
+ const byId = new Map;
20482
+ for (const row of rows) {
20483
+ const target = row.supersedes ? byId.get(row.supersedes) : undefined;
20484
+ if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
20485
+ target.superseded = true;
20486
+ else if (row.supersedes)
20487
+ row.supersedeIgnored = true;
20488
+ if (!byId.has(row.id))
20489
+ byId.set(row.id, row);
20538
20490
  }
20539
- return depth > 0;
20540
- }
20541
- function hasOpaqueLineBreak(text) {
20542
- return /\r(?!\n)|[\u2028\u2029]/u.test(text);
20491
+ if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
20492
+ tamperCache.set(file.value, {
20493
+ size: stat.size,
20494
+ stamp: fileStamp(stat),
20495
+ breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
20496
+ });
20497
+ return {
20498
+ ok: true,
20499
+ value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
20500
+ };
20543
20501
  }
20544
- var decoder = new TextDecoder("utf-8", { fatal: true });
20545
- function textOf(bytes) {
20546
- if (bytes.includes(0))
20547
- return null;
20502
+ var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
20503
+ var tamperCache = new Map;
20504
+ var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
20505
+ function ledgerTampering(cwd) {
20506
+ const file = ledgerPath(cwd);
20507
+ if (!file.ok)
20508
+ return [];
20548
20509
  try {
20549
- return decoder.decode(bytes);
20510
+ const stat = fs12.statSync(file.value);
20511
+ const cached = tamperCache.get(file.value);
20512
+ if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
20513
+ return cached.breaks;
20550
20514
  } catch {
20551
- return null;
20515
+ return [];
20552
20516
  }
20517
+ const read = readLedger(cwd, { integrity: true });
20518
+ return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
20553
20519
  }
20554
- function cosmeticChange(file, before, after) {
20555
- const left = textOf(before);
20556
- const right = textOf(after);
20557
- if (left === null || right === null)
20558
- return false;
20559
- if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
20560
- return false;
20561
- const a = substanceOf(file, left);
20562
- return a !== null && a === substanceOf(file, right);
20520
+ function tamperedUnblock(cwd) {
20521
+ const file = ledgerPath(cwd);
20522
+ const where = file.ok ? file.value : "the ledger file";
20523
+ return `workit ledger verify-integrity # lists the edited or missing rows; if the edit is understood, move ${where} aside (mv ${where} ${where}.tampered-<date>) and record the verdicts again`;
20563
20524
  }
20564
-
20565
- // packages/workit-core/src/core/policy/lane.ts
20566
- var LANE_THRESHOLDS = {
20567
- selfMaxLines: 100,
20568
- selfMaxFiles: 5,
20569
- fullMinLines: 500
20570
- };
20571
- var RISK_TOKENS = new Set([
20572
- "auth",
20573
- "authn",
20574
- "authz",
20575
- "oauth",
20576
- "security",
20577
- "permission",
20578
- "permissions",
20579
- "secret",
20580
- "secrets",
20581
- "crypto",
20582
- "gate",
20583
- "gates",
20584
- "policy",
20585
- "policies",
20586
- "hook",
20587
- "hooks",
20588
- "husky",
20589
- "lefthook",
20590
- "release",
20591
- "releases",
20592
- "migration",
20593
- "migrations",
20594
- "migrate",
20595
- "workflows"
20596
- ]);
20597
- var RISK_FILES = new Set([
20598
- ".gitlab-ci.yml",
20599
- "Jenkinsfile",
20600
- "azure-pipelines.yml",
20601
- ".pre-commit-config.yaml",
20602
- ".workit/risk-paths"
20603
- ]);
20604
- var RISK_NAMES = new Set([".gitattributes"]);
20605
- var RISK_PATHS_FILE = ".workit/risk-paths";
20606
- var LOCKFILES = new Set([
20607
- "bun.lock",
20608
- "bun.lockb",
20609
- "package-lock.json",
20610
- "npm-shrinkwrap.json",
20611
- "yarn.lock",
20612
- "pnpm-lock.yaml",
20613
- "Cargo.lock",
20614
- "go.sum",
20615
- "poetry.lock",
20616
- "uv.lock",
20617
- "Pipfile.lock",
20618
- "Gemfile.lock",
20619
- "composer.lock"
20620
- ]);
20621
- var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc|txt)$/iu;
20622
- var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
20623
- var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
20624
- var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__|spec|specs|fixtures)\/|\.(?:test|spec)\.[^/]+$/iu;
20625
- var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
20626
- var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
20627
- function kindOf(file) {
20628
- if (file.binary)
20629
- return "behavior";
20630
- if (file.generated)
20631
- return "generated";
20632
- if (LOCKFILES.has(basename(file.path)))
20633
- return "lockfile";
20634
- if (isDoc(file.path))
20635
- return "doc";
20636
- if (file.cosmetic)
20637
- return "cosmetic";
20638
- return TEST_PATH.test(file.path) ? "test" : "behavior";
20525
+ var tamperedWarning = (cwd, breaks) => `the ledger was edited, or a row was removed or moved (${breaks.slice(0, 3).map((row) => `${row.reason} at row ${row.seq}`).join(", ")}${breaks.length > 3 ? ", …" : ""}): no verdict is accepted until it is repaired. ${tamperedUnblock(cwd)}`;
20526
+ function verdictBasis(verdict, current) {
20527
+ if (verdict.head && verdict.head === current.head)
20528
+ return "fresh";
20529
+ if (verdict.patchId && verdict.patchId === current.patchId && verdict.diffHash && verdict.diffHash === current.diffHash)
20530
+ return "carried";
20531
+ return "stale";
20532
+ }
20533
+ var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
20534
+ function trailerSessions(cwd, base, head) {
20535
+ if (!base || !head || !safeRef2(base) || !safeRef2(head))
20536
+ return [];
20537
+ const out = git4(cwd, [
20538
+ "log",
20539
+ "--format=%(trailers:key=Workit-Session,valueonly,separator=%x00)",
20540
+ `${base}..${head}`
20541
+ ]);
20542
+ return (out ?? "").split(/[\0\n]/u).map((value) => value.trim()).filter(Boolean);
20639
20543
  }
20640
- var globCache = new Map;
20641
- function globMatch(pattern, file) {
20642
- let regex = globCache.get(pattern);
20643
- if (!regex) {
20644
- let source = "";
20645
- for (let index = 0;index < pattern.length; index += 1) {
20646
- const char = pattern[index];
20647
- if (char === "*" && pattern[index + 1] === "*") {
20648
- source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
20649
- index += pattern[index + 2] === "/" ? 2 : 1;
20650
- } else if (char === "*")
20651
- source += "[^/]*";
20652
- else if (char === "?")
20653
- source += "[^/]";
20654
- else
20655
- source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
20544
+ var RENAMED = /^branch: renamed refs\/heads\/(.+) to refs\/heads\/(.+)$/iu;
20545
+ function branchScope(cwd, branch) {
20546
+ const renamedAt = new Map;
20547
+ let since = Number.NEGATIVE_INFINITY;
20548
+ if (safeRef2(branch)) {
20549
+ const log = git4(cwd, [
20550
+ "log",
20551
+ "-g",
20552
+ "--date=unix",
20553
+ "--format=%gd%x09%gs",
20554
+ `refs/heads/${branch}`,
20555
+ "--"
20556
+ ]);
20557
+ for (const line of (log ?? "").split(`
20558
+ `)) {
20559
+ const [selector = "", subject = ""] = line.split("\t");
20560
+ const seconds = Number(/@\{(\d+)\}$/u.exec(selector)?.[1]);
20561
+ if (!Number.isFinite(seconds))
20562
+ continue;
20563
+ since = seconds * 1000;
20564
+ const match = RENAMED.exec(subject);
20565
+ if (!match || match[1] === branch)
20566
+ continue;
20567
+ const until = (seconds + 1) * 1000;
20568
+ renamedAt.set(match[1], Math.max(renamedAt.get(match[1]) ?? 0, until));
20656
20569
  }
20657
- regex = new RegExp(`^${source}$`, "u");
20658
- globCache.set(pattern, regex);
20659
20570
  }
20660
- return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
20571
+ return (row) => {
20572
+ if (row.branch === branch)
20573
+ return true;
20574
+ const until = row.branch === null ? undefined : renamedAt.get(row.branch);
20575
+ if (until === undefined)
20576
+ return false;
20577
+ const at = Date.parse(row.at);
20578
+ return at >= since && at <= until;
20579
+ };
20661
20580
  }
20662
- function isRiskPath(file, extra = []) {
20663
- if (extra.some((pattern) => globMatch(pattern, file)))
20664
- return true;
20665
- if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
20581
+ var renameAwareBranch = (scopeOf, branch) => {
20582
+ return (target) => {
20583
+ const own = typeof branch === "string" ? branch : null;
20584
+ const theirs = typeof target.branch === "string" ? target.branch : null;
20585
+ if (own === theirs)
20586
+ return true;
20587
+ if (own === null || theirs === null || typeof target.at !== "string")
20588
+ return false;
20589
+ return scopeOf(own)({ branch: theirs, at: target.at });
20590
+ };
20591
+ };
20592
+ function authorSessions(cwd, rows, branch, range = { base: null, head: null }, scope = branchScope(cwd, branch)) {
20593
+ const sessions = new Set;
20594
+ for (const row of rows) {
20595
+ if (!scope(row) || row.type !== "commit.recorded" && row.type !== "task.opened")
20596
+ continue;
20597
+ const session = str2(row.session) ?? row.actor.session;
20598
+ if (session)
20599
+ sessions.add(session);
20600
+ }
20601
+ for (const session of trailerSessions(cwd, range.base, range.head))
20602
+ sessions.add(session);
20603
+ return sessions;
20604
+ }
20605
+ var derivesFrom = (derived, bare) => !bare.includes(":") && derived.endsWith(`:${bare}`);
20606
+ var authoredBy = (authors, session) => {
20607
+ if (authors.has(session))
20666
20608
  return true;
20667
- if (TEST_PATH.test(file) || isDoc(file))
20668
- return false;
20669
- return file.split("/").map((segment, index, all) => index === all.length - 1 ? segment.replace(/\.[^.]+$/u, "") : segment).some((segment) => segment.toLowerCase().split(/[-_.]/u).some((token) => RISK_TOKENS.has(token)));
20609
+ for (const author of authors)
20610
+ if (derivesFrom(author, session) || derivesFrom(session, author))
20611
+ return true;
20612
+ return false;
20613
+ };
20614
+ var TRUST_REASONS = new Set([
20615
+ "unsigned",
20616
+ "hash_mismatch",
20617
+ "broken_link",
20618
+ "duplicate_id",
20619
+ "unissued_session",
20620
+ "unbound_session"
20621
+ ]);
20622
+ var SHELL_VOUCHED_HOSTS = new Set([
20623
+ "cli",
20624
+ "opencode",
20625
+ "pi",
20626
+ "codex_cli",
20627
+ "codex_desktop"
20628
+ ]);
20629
+ function sessionTrust(cwd, row) {
20630
+ const session = row.actor.session;
20631
+ if (!session || row.self === true)
20632
+ return null;
20633
+ const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
20634
+ const host = row.actor.host;
20635
+ const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
20636
+ if (!issuedOnly)
20637
+ return null;
20638
+ const binding = issuedBinding(cwd, session);
20639
+ if (binding === null)
20640
+ return "unissued_session";
20641
+ return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
20670
20642
  }
20671
- var parseRiskPaths = (text) => text.split(`
20672
- `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
20673
- function computeLane(input) {
20674
- const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
20675
- const risky = new Set(riskFiles);
20676
- const kinds = input.files.map((file) => ({
20677
- file,
20678
- kind: risky.has(file.path) ? "behavior" : kindOf(file)
20679
- }));
20680
- const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
20681
- const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
20682
- const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
20683
- const result = (lane, reasons) => ({
20684
- lane,
20685
- reasons,
20686
- lines,
20687
- files: behavior.length,
20688
- riskFiles
20643
+ function trustReasons(cwd, row) {
20644
+ if (row.integrity === "unverified")
20645
+ return [row.integrityReason ?? "unsigned"];
20646
+ if (row.integrity !== "verified")
20647
+ return ["unsigned"];
20648
+ const session = sessionTrust(cwd, row);
20649
+ return session ? [session] : [];
20650
+ }
20651
+ function withIntegrity(cwd, rows) {
20652
+ if (rows.every((row) => row.integrity !== undefined))
20653
+ return rows;
20654
+ const read = readLedger(cwd, { integrity: true });
20655
+ const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
20656
+ return rows.map((row) => {
20657
+ if (row.integrity !== undefined)
20658
+ return row;
20659
+ const fresh = bySeq.get(row.seq);
20660
+ return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
20689
20661
  });
20690
- const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
20691
- if (input.riskTier === "high")
20692
- return result("full", ["the task judgment records high risk"]);
20693
- if (riskFiles.length)
20694
- return result("full", [
20695
- `touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
20696
- ]);
20697
- if (!behavior.length)
20698
- return result("none", [
20699
- input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
20700
- ]);
20701
- if (lines >= fullMinLines)
20702
- return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
20703
- if (lines > selfMaxLines)
20704
- return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
20705
- if (behavior.length > selfMaxFiles)
20706
- return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
20707
- if (opaque.length)
20708
- return result("focused", [
20709
- `a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
20710
- ]);
20711
- return result("self", [
20712
- `small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
20713
- ]);
20714
20662
  }
20715
- function untrackedStat(cwd, file) {
20716
- try {
20717
- const bytes = readFileSync4(path16.join(cwd, file));
20718
- if (bytes.includes(0))
20719
- return { path: file, added: 0, deleted: 0, binary: true };
20720
- const text = bytes.toString("utf8");
20721
- if (hasOpaqueLineBreak(text))
20722
- return { path: file, added: 0, deleted: 0, binary: true };
20723
- const lines = text.split(`
20724
- `).length - (text.endsWith(`
20725
- `) ? 1 : 0);
20726
- return { path: file, added: lines, deleted: 0 };
20727
- } catch {
20728
- return { path: file, added: 0, deleted: 0, binary: true };
20663
+ var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
20664
+ var independenceReasons = (row, authors) => {
20665
+ const reasons = [];
20666
+ if (!row.actor.session)
20667
+ reasons.push("no_session");
20668
+ else if (authoredBy(authors, row.actor.session))
20669
+ reasons.push("author_session");
20670
+ if (row.self === true && !reasons.includes("no_session"))
20671
+ reasons.push("self");
20672
+ return reasons;
20673
+ };
20674
+ function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
20675
+ let effective = null;
20676
+ for (const row of rows) {
20677
+ if (!effective) {
20678
+ effective = row;
20679
+ continue;
20680
+ }
20681
+ if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
20682
+ continue;
20683
+ const rowIndependent = reasonsOf(row).length === 0;
20684
+ const effIndependent = reasonsOf(effective).length === 0;
20685
+ if (effIndependent && !rowIndependent && isCurrent(effective))
20686
+ continue;
20687
+ if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
20688
+ continue;
20689
+ effective = row;
20729
20690
  }
20691
+ return effective;
20730
20692
  }
20731
- var REGULAR = new Set(["100644", "100755"]);
20732
- var ABSENT = /^0+$/u;
20733
- function cosmeticFiles(cwd, fork, head) {
20734
- const raw = diffRaw(cwd, fork, head);
20735
- if (!raw)
20736
- return null;
20737
- const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
20738
- const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
20739
- if (!blobs)
20740
- return null;
20741
- const out = new Set;
20742
- const opaque = new Set;
20743
- for (const entry of candidates) {
20744
- const before = blobs.get(entry.oldBlob);
20745
- let after = blobs.get(entry.newBlob);
20746
- if (!after && head === null && ABSENT.test(entry.newBlob))
20747
- try {
20748
- after = readFileSync4(path16.join(cwd, entry.path));
20749
- } catch {
20750
- after = undefined;
20693
+ var restackExact = new WeakMap;
20694
+ function restackCarries(exactIn, rows, branch, from, to) {
20695
+ if (!from || !to)
20696
+ return false;
20697
+ const exact = (row, fromHead, head) => {
20698
+ if (exactIn === null)
20699
+ return true;
20700
+ const cwd = exactIn;
20701
+ let known = restackExact.get(row);
20702
+ if (known === undefined) {
20703
+ known = typeof row.fromBase === "string" && typeof row.toBase === "string" && sameOwnChange(cwd, {
20704
+ from: fromHead,
20705
+ to: head,
20706
+ fromBase: row.fromBase,
20707
+ toBase: row.toBase
20708
+ });
20709
+ restackExact.set(row, known);
20710
+ }
20711
+ return known;
20712
+ };
20713
+ const edges = new Map;
20714
+ for (const row of rows)
20715
+ if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head && exact(row, row.fromHead, row.head))
20716
+ edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
20717
+ const seen = new Set([from]);
20718
+ const queue = [from];
20719
+ while (queue.length) {
20720
+ const next = queue.shift();
20721
+ if (next === to)
20722
+ return true;
20723
+ for (const target of edges.get(next) ?? [])
20724
+ if (!seen.has(target)) {
20725
+ seen.add(target);
20726
+ queue.push(target);
20751
20727
  }
20752
- if (before && after && cosmeticChange(entry.path, before, after))
20753
- out.add(entry.path);
20754
- else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
20755
- opaque.add(entry.path);
20756
20728
  }
20757
- return { cosmetic: out, opaque };
20729
+ return false;
20758
20730
  }
20759
- function generatedAt(cwd, fork, paths) {
20760
- const files = treeFilesNamed(cwd, fork, ".gitattributes");
20761
- const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
20762
- if (!files || !blobs)
20763
- return new Set;
20764
- const texts = new Map;
20765
- for (const [file, id] of files) {
20766
- const bytes = blobs.get(id);
20767
- if (bytes)
20768
- texts.set(file, bytes.toString("utf8"));
20731
+ function checkVerdicts(cwd, branch, ledgerRows) {
20732
+ const rows = withIntegrity(cwd, ledgerRows);
20733
+ const head = branchHead(cwd, branch);
20734
+ const fallbackBase = defaultBase(cwd);
20735
+ const scope = branchScope(cwd, branch);
20736
+ const keyCache = new Map;
20737
+ const keysAt = (base, at) => {
20738
+ if (!base || !at)
20739
+ return { patchId: null, diffHash: null };
20740
+ const cacheKey = `${base}\x00${at}`;
20741
+ let key = keyCache.get(cacheKey);
20742
+ if (!key) {
20743
+ key = { patchId: patchId(cwd, base, at), diffHash: diffHash(cwd, base, at) };
20744
+ keyCache.set(cacheKey, key);
20745
+ }
20746
+ return key;
20747
+ };
20748
+ const keyFor = (base) => keysAt(base, head);
20749
+ const driftCache = new Map;
20750
+ const driftOf = (row) => {
20751
+ if (driftCache.has(row))
20752
+ return driftCache.get(row) ?? null;
20753
+ let drift = null;
20754
+ const toBase = row.base ?? fallbackBase;
20755
+ const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
20756
+ const fromBase = row.baseSha ?? toBaseSha;
20757
+ if (row.head && head && row.head !== head && toBaseSha && fromBase && !untrusted(reasonsOf(row)))
20758
+ drift = verdictDrift(cwd, {
20759
+ from: row.head,
20760
+ to: head,
20761
+ fromBase,
20762
+ toBase: toBaseSha,
20763
+ kind: typeof row.kind === "string" ? row.kind : "review"
20764
+ });
20765
+ driftCache.set(row, drift);
20766
+ return drift;
20767
+ };
20768
+ const exactCache = new Map;
20769
+ const exactCarry = (row) => {
20770
+ if (FAILING.has(String(row.result)))
20771
+ return true;
20772
+ let exact = exactCache.get(row);
20773
+ if (exact === undefined) {
20774
+ const toBase = row.base ?? fallbackBase;
20775
+ const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
20776
+ const fromBase = row.baseSha ?? toBaseSha;
20777
+ exact = Boolean(row.head && head && toBaseSha && fromBase && sameOwnChange(cwd, { from: row.head, to: head, fromBase, toBase: toBaseSha }));
20778
+ exactCache.set(row, exact);
20779
+ }
20780
+ return exact;
20781
+ };
20782
+ const keyBasis = (row) => {
20783
+ const basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
20784
+ return basis === "carried" && !exactCarry(row) ? "stale" : basis;
20785
+ };
20786
+ const restacked = (row) => restackCarries(FAILING.has(String(row.result)) ? null : cwd, rows, branch, row.head, head);
20787
+ const carryOf = (row) => {
20788
+ if (keyBasis(row) !== "stale")
20789
+ return null;
20790
+ if (restacked(row))
20791
+ return null;
20792
+ const drift = driftOf(row);
20793
+ return drift?.carries ? { rule: drift.rule, since: drift.since, files: drift.files } : null;
20794
+ };
20795
+ const basisOf = (row) => {
20796
+ const basis = keyBasis(row);
20797
+ if (basis !== "stale")
20798
+ return basis;
20799
+ return restacked(row) || carryOf(row) ? "carried" : basis;
20800
+ };
20801
+ const sameCode = (failed, row) => {
20802
+ if (failed.head && failed.head === row.head)
20803
+ return true;
20804
+ const base = failed.base ?? fallbackBase;
20805
+ const stored = (row.base ?? fallbackBase) === base && row.baseSha !== null && row.baseSha === failed.baseSha;
20806
+ const keys = stored ? { patchId: row.patchId, diffHash: row.diffHash } : keysAt(base, row.head);
20807
+ return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(null, rows, branch, failed.head, row.head);
20808
+ };
20809
+ const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
20810
+ const failureAuthorCache = new Map;
20811
+ const failureAuthors = (row) => {
20812
+ let set = failureAuthorCache.get(row.seq);
20813
+ if (!set) {
20814
+ set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
20815
+ failureAuthorCache.set(row.seq, set);
20816
+ }
20817
+ return set;
20818
+ };
20819
+ const reasonCache = new Map;
20820
+ const reasonsOf = (row) => {
20821
+ let reasons = reasonCache.get(row);
20822
+ if (!reasons) {
20823
+ const failing = FAILING.has(String(row.result));
20824
+ reasons = [
20825
+ ...trustReasons(cwd, row),
20826
+ ...independenceReasons(row, failing ? failureAuthors(row) : authors)
20827
+ ];
20828
+ reasonCache.set(row, reasons);
20829
+ }
20830
+ return reasons;
20831
+ };
20832
+ const retired = new Set;
20833
+ for (const row of rows)
20834
+ if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
20835
+ retired.add(row.supersedes);
20836
+ const byKind = new Map;
20837
+ for (const row of rows)
20838
+ if (isLiveVerdict(row, retired) && scope(row)) {
20839
+ const kind = typeof row.kind === "string" ? row.kind : "review";
20840
+ byKind.set(kind, [...byKind.get(kind) ?? [], row]);
20841
+ }
20842
+ const verdicts = [];
20843
+ const warnings = [];
20844
+ for (const [kind, list] of byKind) {
20845
+ const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
20846
+ if (!row)
20847
+ continue;
20848
+ const basis = basisOf(row);
20849
+ const current = basis !== "stale";
20850
+ const independence = reasonsOf(row);
20851
+ const reasons = [
20852
+ ...current ? [] : ["stale"],
20853
+ ...PASSING.has(String(row.result)) ? [] : ["not_passing"],
20854
+ ...independence
20855
+ ];
20856
+ verdicts.push({
20857
+ kind,
20858
+ basis,
20859
+ current,
20860
+ independent: independence.length === 0,
20861
+ accepted: reasons.length === 0,
20862
+ reasons,
20863
+ carry: basis === "carried" ? carryOf(row) : null,
20864
+ verdict: row
20865
+ });
20866
+ if (reasons.length === 0) {
20867
+ for (const later of list)
20868
+ if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
20869
+ warnings.push(`the author's own (self) ${String(later.result)} ${kind} verdict ${later.id} by ${later.actor.session ?? "no session"} on the current code is newer than the accepted pass ${row.id}: read it before merging`);
20870
+ }
20769
20871
  }
20770
- return generatedByAttributes(texts, paths);
20771
- }
20772
- var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
20773
- function laneFiles(cwd, fork, head) {
20774
- const stats = diffNumstat(cwd, fork, head);
20775
- const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
20776
- if (!stats || !cosmetic)
20777
- return null;
20778
- const files = [
20779
- ...stats.map((entry) => ({
20780
- ...entry,
20781
- ...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
20782
- cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
20783
- })),
20784
- ...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
20785
- ];
20786
- const generated = generatedAt(cwd, fork, files.map((entry) => entry.path));
20787
- return files.map((entry) => ({
20788
- ...entry,
20789
- generated: generated.has(entry.path)
20790
- }));
20791
- }
20792
- function branchLane(cwd, options) {
20793
- const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
20794
- const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
20795
- const head = options.workingTree ? null : options.head;
20796
- const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
20797
- const files = fork ? laneFiles(cwd, fork, head) : null;
20798
- const riskTier = options.judgment?.riskTier ?? null;
20799
- if (!fork || !files) {
20800
- const high = riskTier === "high";
20801
- return {
20802
- lane: high ? "full" : "focused",
20803
- reasons: [
20804
- `the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
20805
- ...high ? ["the task judgment records high risk"] : []
20806
- ],
20807
- lines: 0,
20808
- files: 0,
20809
- riskFiles: [],
20810
- base,
20811
- head: options.head
20812
- };
20872
+ for (const row of rows)
20873
+ if (row.integrity === "unverified" && scope(row))
20874
+ warnings.push(`ledger row ${row.id} (${row.type}) on ${row.branch ?? branch} is unverified: the workit CLI did not write it or it changed afterwards (workit ledger verify-integrity)`);
20875
+ const tampered = ledgerTampering(cwd);
20876
+ if (tampered.length) {
20877
+ warnings.push(tamperedWarning(cwd, tampered));
20878
+ for (const entry of verdicts) {
20879
+ entry.accepted = false;
20880
+ entry.reasons = [...entry.reasons, "ledger_tampered"];
20881
+ }
20813
20882
  }
20814
- const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
20883
+ verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
20884
+ const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
20885
+ const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
20886
+ const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
20887
+ const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
20888
+ accepted: false,
20889
+ verdict: failing?.verdict ?? null,
20890
+ reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
20891
+ };
20892
+ const latestSelf = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && (STRONG_RESULTS.has(String(entry.verdict.result)) || FAILING.has(String(entry.verdict.result))));
20893
+ const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
20894
+ const carryEntry = verdicts.find((entry) => entry.verdict === accepted.verdict && entry.accepted) ?? newestCurrent;
20895
+ const lastPass = accepted.accepted || failing || tampered.length ? undefined : verdicts.findLast((entry) => !entry.current && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
20896
+ const drift = lastPass ? driftOf(lastPass.verdict) : null;
20897
+ const reverify = drift && !drift.carries && drift.focused && drift.files ? { scope: "focused", since: drift.since, files: drift.files } : null;
20815
20898
  return {
20816
- ...computeLane({
20817
- files,
20818
- riskTier,
20819
- riskPaths: [...repoRisk, ...options.riskPaths ?? []]
20820
- }),
20821
- base,
20822
- head: options.head
20899
+ branch,
20900
+ head,
20901
+ review: accepted.accepted ? "verified" : selfEntry ? "self-reviewed" : "unreviewed",
20902
+ selfVerdict: accepted.accepted ? null : selfEntry?.verdict ?? null,
20903
+ base: newestCurrent?.verdict.base ?? fallbackBase,
20904
+ patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
20905
+ current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
20906
+ carry: carryEntry?.carry ?? null,
20907
+ reverify,
20908
+ accepted,
20909
+ verdicts,
20910
+ authors: [...authors].toSorted(),
20911
+ warnings
20823
20912
  };
20824
20913
  }
20825
- var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
20914
+ var OBSERVED_ONLY = new Set([
20915
+ "check",
20916
+ "merge.unverified",
20917
+ "policy.judged",
20918
+ ...PR_ROW_TYPES
20919
+ ]);
20826
20920
 
20827
20921
  // packages/workit-core/src/core/policy/derive.ts
20828
20922
  var RULES = {
@@ -20977,7 +21071,7 @@ var productDecision = (task, ledger) => {
20977
21071
  };
20978
21072
  };
20979
21073
  var planRecorded = (task, root) => {
20980
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(path17.resolve(root, ref.path)));
21074
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(path16.resolve(root, ref.path)));
20981
21075
  if (cited)
20982
21076
  return {
20983
21077
  met: true,
@@ -21044,14 +21138,14 @@ function derivedRuleStatus(task, requirement, root, ledger) {
21044
21138
 
21045
21139
  // packages/workit-core/src/core/task-evaluation.ts
21046
21140
  var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
21047
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path18.sep}`);
21048
- var relative = (root, candidate) => path18.relative(root, candidate).split(path18.sep).join("/") || ".";
21141
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`);
21142
+ var relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".";
21049
21143
  var canonicalPath = (value) => {
21050
21144
  if (typeof value !== "string" || !value)
21051
21145
  return null;
21052
21146
  if (value.includes("\\") || value.split("/").includes(".."))
21053
21147
  return null;
21054
- const normalized = path18.posix.normalize(value);
21148
+ const normalized = path17.posix.normalize(value);
21055
21149
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
21056
21150
  return null;
21057
21151
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -21087,7 +21181,7 @@ var compareCodeUnits2 = (left, right) => {
21087
21181
  return left.length - right.length;
21088
21182
  };
21089
21183
  var gitPaths = (root) => {
21090
- const result = spawnSync11("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
21184
+ const result = spawnSync10("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
21091
21185
  cwd: root,
21092
21186
  encoding: "buffer"
21093
21187
  });
@@ -21099,21 +21193,21 @@ var gitPaths = (root) => {
21099
21193
  git: !/not a git repository/i.test(stderr)
21100
21194
  };
21101
21195
  }
21102
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")) : [];
21103
- const stagedDeleted = spawnSync11("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
21196
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")) : [];
21197
+ const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
21104
21198
  if (stagedDeleted.status !== 0) {
21105
21199
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
21106
21200
  if (!/not a git repository/i.test(stderr))
21107
21201
  return { paths, uncertain: true, git: true };
21108
21202
  } else if (stagedDeleted.stdout) {
21109
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")));
21203
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")));
21110
21204
  }
21111
21205
  return { paths: [...new Set(paths)], uncertain: false, git: true };
21112
21206
  };
21113
21207
  var walk = (root, directory, output) => {
21114
21208
  let names;
21115
21209
  try {
21116
- names = fs14.readdirSync(directory);
21210
+ names = fs13.readdirSync(directory);
21117
21211
  } catch {
21118
21212
  return true;
21119
21213
  }
@@ -21121,10 +21215,10 @@ var walk = (root, directory, output) => {
21121
21215
  for (const name of names) {
21122
21216
  if (name === ".git" || name === ".workit")
21123
21217
  continue;
21124
- const target = path18.join(directory, name);
21218
+ const target = path17.join(directory, name);
21125
21219
  let stat;
21126
21220
  try {
21127
- stat = fs14.lstatSync(target);
21221
+ stat = fs13.lstatSync(target);
21128
21222
  } catch {
21129
21223
  uncertain = true;
21130
21224
  continue;
@@ -21139,14 +21233,14 @@ var walk = (root, directory, output) => {
21139
21233
  var scopeRoots = (root, scope) => {
21140
21234
  const roots = [];
21141
21235
  for (const item of scope.paths.length ? scope.paths : ["."]) {
21142
- const target = path18.resolve(root, item);
21236
+ const target = path17.resolve(root, item);
21143
21237
  if (!inside(root, target))
21144
21238
  return failure2("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
21145
21239
  let ancestor = target;
21146
- while (!fs14.existsSync(ancestor) && ancestor !== root)
21147
- ancestor = path18.dirname(ancestor);
21240
+ while (!fs13.existsSync(ancestor) && ancestor !== root)
21241
+ ancestor = path17.dirname(ancestor);
21148
21242
  try {
21149
- if (!inside(root, fs14.realpathSync(ancestor)))
21243
+ if (!inside(root, fs13.realpathSync(ancestor)))
21150
21244
  return failure2("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
21151
21245
  } catch {
21152
21246
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -21158,7 +21252,7 @@ var scopeRoots = (root, scope) => {
21158
21252
  function captureCandidate(root, scope, environment = []) {
21159
21253
  let checkout;
21160
21254
  try {
21161
- checkout = fs14.realpathSync(root);
21255
+ checkout = fs13.realpathSync(root);
21162
21256
  } catch {
21163
21257
  return failure2("invalid_input", "candidate root does not exist");
21164
21258
  }
@@ -21176,11 +21270,11 @@ function captureCandidate(root, scope, environment = []) {
21176
21270
  if (item === ".git" || item === ".workit")
21177
21271
  continue;
21178
21272
  names.add(item);
21179
- if (!fs14.existsSync(target))
21273
+ if (!fs13.existsSync(target))
21180
21274
  continue;
21181
21275
  let stat;
21182
21276
  try {
21183
- stat = fs14.lstatSync(target);
21277
+ stat = fs13.lstatSync(target);
21184
21278
  } catch {
21185
21279
  uncertain = true;
21186
21280
  continue;
@@ -21196,17 +21290,17 @@ function captureCandidate(root, scope, environment = []) {
21196
21290
  for (const item of [...names].toSorted()) {
21197
21291
  if (!scopeMatches(item, normalizedScope))
21198
21292
  continue;
21199
- const target = path18.join(checkout, item);
21293
+ const target = path17.join(checkout, item);
21200
21294
  try {
21201
- const stat = fs14.lstatSync(target);
21295
+ const stat = fs13.lstatSync(target);
21202
21296
  if (stat.isSymbolicLink()) {
21203
- const link = fs14.readlinkSync(target);
21297
+ const link = fs13.readlinkSync(target);
21204
21298
  files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
21205
21299
  } else if (stat.isFile()) {
21206
21300
  files.push({
21207
21301
  path: item,
21208
21302
  kind: "file",
21209
- digest: digestBytes(fs14.readFileSync(target)),
21303
+ digest: digestBytes(fs13.readFileSync(target)),
21210
21304
  executable: (stat.mode & 73) !== 0
21211
21305
  });
21212
21306
  } else if (!stat.isDirectory()) {
@@ -21231,7 +21325,7 @@ function captureCandidate(root, scope, environment = []) {
21231
21325
  const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
21232
21326
  if (values.some(({ value }) => value === null))
21233
21327
  completeness = "uncertain";
21234
- const headResult = spawnSync11("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
21328
+ const headResult = spawnSync10("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
21235
21329
  if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
21236
21330
  completeness = "uncertain";
21237
21331
  const head = headResult.status === 0 ? headResult.stdout.trim() : null;
@@ -21371,7 +21465,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
21371
21465
  var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
21372
21466
  let root;
21373
21467
  try {
21374
- root = fs14.realpathSync(checkoutRoot);
21468
+ root = fs13.realpathSync(checkoutRoot);
21375
21469
  } catch {
21376
21470
  return failure2("permission_denied", "checkout root is unavailable");
21377
21471
  }
@@ -21380,23 +21474,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
21380
21474
  continue;
21381
21475
  if (!reference.digest)
21382
21476
  return failure2("invalid_input", "document references require a byte digest");
21383
- const target = path18.resolve(root, reference.path);
21384
- if (target !== root && !target.startsWith(`${root}${path18.sep}`))
21477
+ const target = path17.resolve(root, reference.path);
21478
+ if (target !== root && !target.startsWith(`${root}${path17.sep}`))
21385
21479
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
21386
21480
  fields: [
21387
21481
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
21388
21482
  ]
21389
21483
  });
21390
21484
  try {
21391
- const below = path18.relative(root, target);
21485
+ const below = path17.relative(root, target);
21392
21486
  let current = root;
21393
- for (const segment of below.split(path18.sep)) {
21487
+ for (const segment of below.split(path17.sep)) {
21394
21488
  if (!segment)
21395
21489
  continue;
21396
- current = path18.join(current, segment);
21397
- const stat = fs14.lstatSync(current);
21398
- const real = fs14.realpathSync(current);
21399
- if (real !== root && !real.startsWith(`${root}${path18.sep}`))
21490
+ current = path17.join(current, segment);
21491
+ const stat = fs13.lstatSync(current);
21492
+ const real = fs13.realpathSync(current);
21493
+ if (real !== root && !real.startsWith(`${root}${path17.sep}`))
21400
21494
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
21401
21495
  fields: [
21402
21496
  {
@@ -21412,7 +21506,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
21412
21506
  if (current !== target && !stat.isDirectory())
21413
21507
  return failure2("invalid_input", "document reference ancestor is not a directory");
21414
21508
  }
21415
- if (digestBytes(fs14.readFileSync(target)) !== reference.digest)
21509
+ if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
21416
21510
  return failure2("permission_denied", "decision document bytes have changed");
21417
21511
  } catch {
21418
21512
  return failure2("invalid_input", "decision document is unavailable");
@@ -22255,7 +22349,7 @@ var judgeInputSummary = (raw) => {
22255
22349
 
22256
22350
  // packages/workit-core/src/autonomy.ts
22257
22351
  import os4 from "node:os";
22258
- import path19 from "node:path";
22352
+ import path18 from "node:path";
22259
22353
  var accountHome = () => {
22260
22354
  try {
22261
22355
  return os4.userInfo().homedir;
@@ -22264,10 +22358,10 @@ var accountHome = () => {
22264
22358
  }
22265
22359
  };
22266
22360
  var grantsHome = { resolve: accountHome };
22267
- var grantsDir = () => path19.join(grantsHome.resolve(), ".config", "workit");
22361
+ var grantsDir = () => path18.join(grantsHome.resolve(), ".config", "workit");
22268
22362
  var grantsOverride = () => {
22269
- const configured = path19.resolve(resolveConfigDir());
22270
- return configured === path19.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path19.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
22363
+ const configured = path18.resolve(resolveConfigDir());
22364
+ return configured === path18.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path18.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
22271
22365
  };
22272
22366
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
22273
22367
  var DEFAULT_GRANTS = Object.freeze({
@@ -24076,9 +24170,9 @@ class WorkitCore {
24076
24170
  }
24077
24171
  }
24078
24172
  // packages/workit-core/src/core/repo-context.ts
24079
- import { spawnSync as spawnSync12 } from "node:child_process";
24173
+ import { spawnSync as spawnSync11 } from "node:child_process";
24080
24174
  import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "node:fs";
24081
- import path20 from "node:path";
24175
+ import path19 from "node:path";
24082
24176
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
24083
24177
  try {
24084
24178
  gitRevisionParts(value);
@@ -24105,7 +24199,7 @@ var invalidRange = (cwd) => ({
24105
24199
  cwd
24106
24200
  });
24107
24201
  var runGit = (cwd, args) => {
24108
- const result = spawnSync12("git", args, { cwd, encoding: "utf8" });
24202
+ const result = spawnSync11("git", args, { cwd, encoding: "utf8" });
24109
24203
  return {
24110
24204
  stdout: (result.stdout ?? "").trimEnd(),
24111
24205
  stderr: (result.stderr ?? "").trimEnd(),
@@ -24212,7 +24306,7 @@ var findPrTemplate = (cwd) => {
24212
24306
  ]) {
24213
24307
  const parts = rel.split("/");
24214
24308
  const base = parts[parts.length - 1];
24215
- const dir = parts.length > 1 ? path20.join(cwd, ...parts.slice(0, -1)) : cwd;
24309
+ const dir = parts.length > 1 ? path19.join(cwd, ...parts.slice(0, -1)) : cwd;
24216
24310
  const actual = probeCaseInsensitive(dir, base);
24217
24311
  if (actual)
24218
24312
  return [...parts.slice(0, -1), actual].join("/");
@@ -24243,7 +24337,7 @@ var diffStatForRange = (cwd, range) => {
24243
24337
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
24244
24338
  };
24245
24339
  var packageScripts = (cwd, keys) => {
24246
- const pkgPath = path20.join(cwd, "package.json");
24340
+ const pkgPath = path19.join(cwd, "package.json");
24247
24341
  if (!existsSync7(pkgPath))
24248
24342
  return [];
24249
24343
  try {
@@ -24269,9 +24363,9 @@ var documentationFiles = (cwd) => {
24269
24363
  if (entry.isDirectory()) {
24270
24364
  if (excluded.has(entry.name))
24271
24365
  continue;
24272
- walk(path20.join(dir, entry.name), depth + 1);
24366
+ walk(path19.join(dir, entry.name), depth + 1);
24273
24367
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
24274
- matches.push("./" + path20.relative(cwd, path20.join(dir, entry.name)).replaceAll("\\", "/"));
24368
+ matches.push("./" + path19.relative(cwd, path19.join(dir, entry.name)).replaceAll("\\", "/"));
24275
24369
  }
24276
24370
  }
24277
24371
  };
@@ -24287,7 +24381,7 @@ var readTrimmed2 = (file, maxLines) => {
24287
24381
  `);
24288
24382
  };
24289
24383
  function prReadyContext(root, range) {
24290
- const cwd = path20.resolve(repoRoot(root));
24384
+ const cwd = path19.resolve(repoRoot(root));
24291
24385
  if (range !== undefined && !isResolvableContextRange(cwd, range))
24292
24386
  return invalidRange(cwd);
24293
24387
  let stdout = "";
@@ -24345,7 +24439,7 @@ ${status.stderr}` : "");
24345
24439
  stdout += `template_path: ${template}
24346
24440
 
24347
24441
  `;
24348
- stdout += readTrimmed2(path20.join(cwd, template), 220);
24442
+ stdout += readTrimmed2(path19.join(cwd, template), 220);
24349
24443
  } else {
24350
24444
  stdout += `template_path: none
24351
24445
 
@@ -24355,7 +24449,7 @@ ${status.stderr}` : "");
24355
24449
  stdout += `
24356
24450
  `;
24357
24451
  stdout += printSection("Recent Validation Signals");
24358
- if (existsSync7(path20.join(cwd, "package.json"))) {
24452
+ if (existsSync7(path19.join(cwd, "package.json"))) {
24359
24453
  stdout += `package.json detected. Common scripts:
24360
24454
  `;
24361
24455
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -24364,7 +24458,7 @@ ${status.stderr}` : "");
24364
24458
  `) + `
24365
24459
  `;
24366
24460
  }
24367
- if (existsSync7(path20.join(cwd, "Cargo.toml")) || existsSync7(path20.join(cwd, "src-tauri/Cargo.toml"))) {
24461
+ if (existsSync7(path19.join(cwd, "Cargo.toml")) || existsSync7(path19.join(cwd, "src-tauri/Cargo.toml"))) {
24368
24462
  stdout += `Rust project detected.
24369
24463
  `;
24370
24464
  }
@@ -24397,7 +24491,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
24397
24491
  - Apply with the host-native editor, preserving the repository changelog conventions.
24398
24492
  - If Unreleased already has duplicate category headings, normalize_only first.`;
24399
24493
  function changelogContext(root, range) {
24400
- const cwd = path20.resolve(repoRoot(root));
24494
+ const cwd = path19.resolve(repoRoot(root));
24401
24495
  if (range !== undefined && !isResolvableContextRange(cwd, range))
24402
24496
  return invalidRange(cwd);
24403
24497
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -24415,7 +24509,7 @@ function changelogContext(root, range) {
24415
24509
  stdout += CHANGELOG_RULES + `
24416
24510
  `;
24417
24511
  stdout += printSection("Existing CHANGELOG.md");
24418
- const changelogPath = path20.join(cwd, "CHANGELOG.md");
24512
+ const changelogPath = path19.join(cwd, "CHANGELOG.md");
24419
24513
  if (existsSync7(changelogPath)) {
24420
24514
  stdout += readTrimmed2(changelogPath, 260) + `
24421
24515
  `;
@@ -24435,7 +24529,7 @@ function changelogContext(root, range) {
24435
24529
  return { stdout, stderr: "", exitCode: 0, cwd };
24436
24530
  }
24437
24531
  function docsRefreshContext(root, range) {
24438
- const cwd = path20.resolve(repoRoot(root));
24532
+ const cwd = path19.resolve(repoRoot(root));
24439
24533
  if (range !== undefined && !isResolvableContextRange(cwd, range))
24440
24534
  return invalidRange(cwd);
24441
24535
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -24457,7 +24551,7 @@ function docsRefreshContext(root, range) {
24457
24551
  `) + `
24458
24552
  `;
24459
24553
  stdout += printSection("README Preview");
24460
- const readme = path20.join(cwd, "README.md");
24554
+ const readme = path19.join(cwd, "README.md");
24461
24555
  if (existsSync7(readme)) {
24462
24556
  stdout += readTrimmed2(readme, 220) + `
24463
24557
  `;
@@ -24466,7 +24560,7 @@ function docsRefreshContext(root, range) {
24466
24560
  `;
24467
24561
  }
24468
24562
  stdout += printSection("Package Scripts");
24469
- const pkgPath = path20.join(cwd, "package.json");
24563
+ const pkgPath = path19.join(cwd, "package.json");
24470
24564
  if (existsSync7(pkgPath)) {
24471
24565
  try {
24472
24566
  const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
@@ -24480,7 +24574,7 @@ function docsRefreshContext(root, range) {
24480
24574
  return { stdout, stderr: "", exitCode: 0, cwd };
24481
24575
  }
24482
24576
  function releaseNotesContext(root, rangeOrTag) {
24483
- const cwd = path20.resolve(repoRoot(root));
24577
+ const cwd = path19.resolve(repoRoot(root));
24484
24578
  if (!rangeOrTag) {
24485
24579
  return { stdout: "", stderr: `ERROR: release tag or range required
24486
24580
  `, exitCode: 1, cwd };
@@ -24518,7 +24612,7 @@ function releaseNotesContext(root, rangeOrTag) {
24518
24612
  `;
24519
24613
  stdout += printSection("Existing Release Files");
24520
24614
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
24521
- if (existsSync7(path20.join(cwd, rel)))
24615
+ if (existsSync7(path19.join(cwd, rel)))
24522
24616
  stdout += rel + `
24523
24617
  `;
24524
24618
  }
@@ -24547,6 +24641,46 @@ function releaseNotesContext(root, rangeOrTag) {
24547
24641
 
24548
24642
  // packages/workit-core/src/core/tracker-issues.ts
24549
24643
  import { spawnSync as spawnSync13 } from "node:child_process";
24644
+
24645
+ // packages/workit-core/src/core/pr-create.ts
24646
+ import fs14 from "node:fs";
24647
+ import path20 from "node:path";
24648
+ import { spawnSync as spawnSync12 } from "node:child_process";
24649
+ // packages/workit-core/src/forge/pr-body.ts
24650
+ function parseGhIssue(value) {
24651
+ const m = /issues\/(\d+)/.exec(value);
24652
+ return m ? m[1] : String(value).trim().replace(/^#/, "");
24653
+ }
24654
+
24655
+ // packages/workit-core/src/core/pr-create.ts
24656
+ function whichOnPath(tool) {
24657
+ const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
24658
+ for (const dir of (process.env.PATH ?? "").split(path20.delimiter)) {
24659
+ if (!dir)
24660
+ continue;
24661
+ for (const name of names) {
24662
+ const candidate = path20.join(dir, name);
24663
+ try {
24664
+ fs14.accessSync(candidate, fs14.constants.X_OK);
24665
+ return candidate;
24666
+ } catch {}
24667
+ }
24668
+ }
24669
+ return null;
24670
+ }
24671
+ var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
24672
+ var pushRemote = (cwd) => {
24673
+ const result = spawnSync12("git", ["remote", "get-url", "--push", "--all", "origin"], {
24674
+ cwd,
24675
+ encoding: "utf8"
24676
+ });
24677
+ if (result.status !== 0)
24678
+ return null;
24679
+ const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
24680
+ return urls.length === 1 ? urls[0] : null;
24681
+ };
24682
+
24683
+ // packages/workit-core/src/core/tracker-issues.ts
24550
24684
  var repoPathFromRemote = (remote) => {
24551
24685
  let rest = (remote || "").trim().replace(/\/+$/, "");
24552
24686
  if (!rest)
@@ -27810,6 +27944,25 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
27810
27944
  var signals = new Map;
27811
27945
  var cache = new Map;
27812
27946
  var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
27947
+ // packages/workit-core/src/hooks/delivery-verbs.ts
27948
+ var WORKIT_ALLOWED_VERBS = [
27949
+ "git",
27950
+ "pr",
27951
+ "ci",
27952
+ "stack",
27953
+ "release",
27954
+ "task",
27955
+ "ledger",
27956
+ "evidence",
27957
+ "finding",
27958
+ "decision",
27959
+ "worker",
27960
+ "verify-delivery",
27961
+ "policy",
27962
+ "doctor",
27963
+ "help"
27964
+ ];
27965
+ var ALLOWED = new Set(WORKIT_ALLOWED_VERBS);
27813
27966
  // packages/workit-core/src/core/heavy.ts
27814
27967
  var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
27815
27968
  var sub = (...names) => new Set(names);
@@ -28018,6 +28171,10 @@ var WRITE_TOOLS = new Set([
28018
28171
  // packages/workit-core/src/hooks/standing.ts
28019
28172
  var sinceStart = new Map;
28020
28173
  // packages/workit-core/src/hooks/cwd-normalize.ts
28174
+ var DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
28175
+ var COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
28176
+ var GLOBALS_FIRST = new RegExp(String.raw`^(\s*workit)((?:\s+(?:--json|--cwd(?:=|\s+)(?:${DIR})))+)\s+([a-z][a-z-]*)(?=\s|$)`, "u");
28177
+ var VERBS = new Set(WORKIT_ALLOWED_VERBS);
28021
28178
  var FILTERS = new Set(["tail", "head", "grep"]);
28022
28179
 
28023
28180
  // packages/workit-core/src/hooks/worktree-confine.ts