@brainervirus/workit-codex 8.23.0 → 8.24.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.
@@ -12,7 +12,7 @@ import {
12
12
  readStandingRev,
13
13
  readStandingSeen,
14
14
  writeStandingSeen
15
- } from "./workit-hook-thet0x8m.js";
15
+ } from "./workit-hook-vhz9yrcw.js";
16
16
  // packages/workit-core/src/hooks/descriptor.ts
17
17
  var support = (descriptor, axis) => {
18
18
  if (axis.startsWith("event:"))
@@ -40,7 +40,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
40
40
  // packages/workit-core/src/hooks/context.ts
41
41
  import { createHash as createHash12 } from "node:crypto";
42
42
  import fs13 from "node:fs";
43
- import path17 from "node:path";
43
+ import path18 from "node:path";
44
44
 
45
45
  // packages/workit-core/src/core/methods.ts
46
46
  var METHODS = {
@@ -115,9 +115,9 @@ Autonomy contract:
115
115
  Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
116
116
  satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
117
117
  - Author is not verifier: your own verdict is \`--self\`, shown as
118
- self-reviewed, never verified. High risk, \`verification: independent\` and
119
- merge need a verifier with its own host-derived session (a subagent's
120
- \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
118
+ self-reviewed, never verified. High risk, a focused or full lane and
119
+ merge under \`verification: self|independent\` need a verifier with its
120
+ own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
121
121
  - A local-commit endpoint does not imply PR readiness, and a local commit alone
122
122
  is not evidence of a requested remote push. Before reporting delivery,
123
123
  reconcile every requested item and observe it (\`workit verify-delivery\`).
@@ -7174,7 +7174,7 @@ function requirementId(input) {
7174
7174
  import { createHash as createHash11 } from "node:crypto";
7175
7175
  import * as fs12 from "node:fs";
7176
7176
  import { spawnSync as spawnSync9 } from "node:child_process";
7177
- import path15 from "node:path";
7177
+ import path16 from "node:path";
7178
7178
 
7179
7179
  // packages/workit-core/src/git/rev.ts
7180
7180
  import { spawnSync as spawnSync2 } from "node:child_process";
@@ -7337,6 +7337,124 @@ function resolveRef(cwd, ref) {
7337
7337
  return null;
7338
7338
  return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
7339
7339
  }
7340
+ var LANE_DIFF = [
7341
+ "-c",
7342
+ "core.quotePath=false",
7343
+ "diff",
7344
+ "--no-color",
7345
+ "--no-ext-diff",
7346
+ "--no-textconv",
7347
+ "--no-renames"
7348
+ ];
7349
+ var laneRange = (fork, head) => {
7350
+ if (!safeArg(fork) || head !== null && !safeArg(head))
7351
+ return null;
7352
+ return head === null ? [fork] : [fork, head];
7353
+ };
7354
+ function diffNumstat(cwd, fork, head) {
7355
+ const range = laneRange(fork, head);
7356
+ if (!range)
7357
+ return null;
7358
+ const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
7359
+ if (!run.ok)
7360
+ return null;
7361
+ const out = [];
7362
+ for (const record of run.stdout.split("\x00")) {
7363
+ const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
7364
+ if (!match)
7365
+ continue;
7366
+ const binary = match[1] === "-";
7367
+ out.push({
7368
+ path: match[3],
7369
+ added: binary ? 0 : Number(match[1]),
7370
+ deleted: binary ? 0 : Number(match[2]),
7371
+ binary
7372
+ });
7373
+ }
7374
+ return out;
7375
+ }
7376
+ function diffRaw(cwd, fork, head) {
7377
+ const range = laneRange(fork, head);
7378
+ if (!range)
7379
+ return null;
7380
+ const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
7381
+ if (!run.ok)
7382
+ return null;
7383
+ const fields = run.stdout.split("\x00");
7384
+ const out = [];
7385
+ for (let index = 0;index + 1 < fields.length; index += 2) {
7386
+ const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
7387
+ if (!meta)
7388
+ return null;
7389
+ out.push({
7390
+ path: fields[index + 1],
7391
+ oldMode: meta[1],
7392
+ newMode: meta[2],
7393
+ oldBlob: meta[3],
7394
+ newBlob: meta[4]
7395
+ });
7396
+ }
7397
+ return out;
7398
+ }
7399
+ function readBlobs(cwd, ids) {
7400
+ const out = new Map;
7401
+ const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
7402
+ if (!wanted.length)
7403
+ return out;
7404
+ const run = spawnSync2("git", ["cat-file", "--batch"], {
7405
+ cwd,
7406
+ input: `${wanted.join(`
7407
+ `)}
7408
+ `,
7409
+ maxBuffer: MAX_BUFFER,
7410
+ timeout: GIT_TIMEOUTS.local,
7411
+ killSignal: "SIGKILL",
7412
+ windowsHide: true
7413
+ });
7414
+ if (run.status !== 0 || !run.stdout)
7415
+ return null;
7416
+ const bytes = run.stdout;
7417
+ let at = 0;
7418
+ for (const id of wanted) {
7419
+ const end = bytes.indexOf(10, at);
7420
+ if (end < 0)
7421
+ return null;
7422
+ const header = bytes.subarray(at, end).toString("utf8").split(" ");
7423
+ at = end + 1;
7424
+ if (header[1] === "missing")
7425
+ continue;
7426
+ const size = Number(header[2]);
7427
+ if (header[0] !== id || !Number.isSafeInteger(size))
7428
+ return null;
7429
+ out.set(id, bytes.subarray(at, at + size));
7430
+ at += size + 1;
7431
+ }
7432
+ return out;
7433
+ }
7434
+ function treeFilesNamed(cwd, rev, name) {
7435
+ if (!safeArg(rev))
7436
+ return null;
7437
+ const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
7438
+ if (!run.ok)
7439
+ return null;
7440
+ const out = new Map;
7441
+ for (const record of run.stdout.split("\x00")) {
7442
+ const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
7443
+ if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
7444
+ out.set(match[2], match[1]);
7445
+ }
7446
+ return out;
7447
+ }
7448
+ function fileAt(cwd, rev, file) {
7449
+ if (!safeArg(rev))
7450
+ return null;
7451
+ const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
7452
+ return run.ok ? run.stdout : null;
7453
+ }
7454
+ function untrackedFiles(cwd) {
7455
+ const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
7456
+ return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
7457
+ }
7340
7458
  var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
7341
7459
  function currentBranch(cwd) {
7342
7460
  return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
@@ -8329,7 +8447,7 @@ var workspaceConfigSchema = object({
8329
8447
  }).passthrough().optional(),
8330
8448
  autoApprove: union([boolean2(), array(nonBlank)]).optional(),
8331
8449
  defaultEndpoint: unknown().optional(),
8332
- verification: _enum(["self", "independent"]).optional(),
8450
+ verification: _enum(["proportional", "self", "independent"]).optional(),
8333
8451
  delegation: unknown().optional(),
8334
8452
  trailers: boolean2().optional(),
8335
8453
  liveSessionGuard: boolean2().optional(),
@@ -12032,8 +12150,8 @@ class TaskStore {
12032
12150
  }
12033
12151
 
12034
12152
  // packages/workit-core/src/core/policy/derive.ts
12035
- import { existsSync as existsSync4 } from "node:fs";
12036
- import path14 from "node:path";
12153
+ import { existsSync as existsSync5 } from "node:fs";
12154
+ import path15 from "node:path";
12037
12155
 
12038
12156
  // packages/workit-core/src/ledger.ts
12039
12157
  import { spawnSync as spawnSync8 } from "node:child_process";
@@ -12880,7 +12998,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
12880
12998
  verdict: failing?.verdict ?? null,
12881
12999
  reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
12882
13000
  };
12883
- const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
13001
+ 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))));
13002
+ const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
12884
13003
  return {
12885
13004
  branch,
12886
13005
  head,
@@ -12924,12 +13043,682 @@ var OBSERVED_ONLY = new Set([
12924
13043
  ...PR_ROW_TYPES
12925
13044
  ]);
12926
13045
 
13046
+ // packages/workit-core/src/core/policy/lane.ts
13047
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
13048
+ import path14 from "node:path";
13049
+ // packages/workit-core/src/forge/redact.ts
13050
+ 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_.-]*`;
13051
+ var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
13052
+ var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
13053
+ var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
13054
+
13055
+ // packages/workit-core/src/git/ops.ts
13056
+ var COMMIT_TIMEOUT_MS = 10 * 60000;
13057
+
13058
+ // packages/workit-core/src/core/policy/attributes.ts
13059
+ function patternRegex(dir, pattern) {
13060
+ if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
13061
+ return null;
13062
+ const anchored = pattern.includes("/");
13063
+ const body = pattern.replace(/^\//u, "");
13064
+ let source = "";
13065
+ for (let index = 0;index < body.length; index += 1) {
13066
+ const char = body[index];
13067
+ if (char === "*" && body[index + 1] === "*") {
13068
+ const slash = body[index + 2] === "/";
13069
+ source += slash ? "(?:.*/)?" : ".*";
13070
+ index += slash ? 2 : 1;
13071
+ } else if (char === "*")
13072
+ source += "[^/]*";
13073
+ else if (char === "?")
13074
+ source += "[^/]";
13075
+ else if (char === "[") {
13076
+ const end = body.indexOf("]", index + 2);
13077
+ if (end < 0)
13078
+ return null;
13079
+ const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
13080
+ source += `[${set}]`;
13081
+ index = end;
13082
+ } else if (char === "\\" && index + 1 < body.length) {
13083
+ source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
13084
+ index += 1;
13085
+ } else
13086
+ source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
13087
+ }
13088
+ const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
13089
+ try {
13090
+ return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
13091
+ } catch {
13092
+ return null;
13093
+ }
13094
+ }
13095
+ function rulesOf(dir, text) {
13096
+ const out = [];
13097
+ for (const raw of text.split(`
13098
+ `)) {
13099
+ const line = raw.trim();
13100
+ if (!line || line.startsWith("#") || line.startsWith("[attr]"))
13101
+ continue;
13102
+ const [pattern, ...attributes] = line.split(/\s+/u);
13103
+ let generated = null;
13104
+ for (const attribute of attributes) {
13105
+ if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
13106
+ generated = true;
13107
+ else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
13108
+ generated = false;
13109
+ }
13110
+ if (generated === null)
13111
+ continue;
13112
+ const regex = patternRegex(dir, pattern);
13113
+ if (regex)
13114
+ out.push({ regex, generated });
13115
+ }
13116
+ return out;
13117
+ }
13118
+ function generatedByAttributes(files, paths) {
13119
+ const rules = [...files].map(([file, text]) => {
13120
+ const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
13121
+ return {
13122
+ depth: dir ? dir.split("/").length : 0,
13123
+ rules: rulesOf(dir, text)
13124
+ };
13125
+ }).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
13126
+ const out = new Set;
13127
+ for (const file of paths) {
13128
+ const last = rules.findLast((rule) => rule.regex.test(file));
13129
+ if (last?.generated)
13130
+ out.add(file);
13131
+ }
13132
+ return out;
13133
+ }
13134
+
13135
+ // packages/workit-core/src/core/policy/substance.ts
13136
+ var SLASH_BLOCK = [["/*", "*/"]];
13137
+ var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
13138
+ var SYNTAXES = Object.fromEntries([
13139
+ ...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
13140
+ ...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
13141
+ ext,
13142
+ { ...C_LIKE, triple: true }
13143
+ ]),
13144
+ ["go", { ...C_LIKE, rawBacktick: true }],
13145
+ ...["css", "scss", "less"].map((ext) => [
13146
+ ext,
13147
+ { line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
13148
+ ]),
13149
+ ...["py", "pyi"].map((ext) => [
13150
+ ext,
13151
+ { line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
13152
+ ]),
13153
+ ["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
13154
+ ...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
13155
+ ext,
13156
+ { line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
13157
+ ])
13158
+ ]);
13159
+ 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;
13160
+ var extensionOf = (file) => {
13161
+ const name = file.slice(file.lastIndexOf("/") + 1);
13162
+ const dot = name.lastIndexOf(".");
13163
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
13164
+ };
13165
+ var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
13166
+ var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
13167
+ var SPACE = /[ \t\r\f\v]/u;
13168
+ var WORD = /[\w$]/u;
13169
+ var NO_SPACE_AFTER = "([{,;";
13170
+ var NO_SPACE_BEFORE = ")]},;";
13171
+ var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
13172
+ var REGEX_KEYWORDS = new Set([
13173
+ "return",
13174
+ "typeof",
13175
+ "instanceof",
13176
+ "case",
13177
+ "do",
13178
+ "else",
13179
+ "in",
13180
+ "of",
13181
+ "new",
13182
+ "delete",
13183
+ "void",
13184
+ "throw",
13185
+ "yield",
13186
+ "await"
13187
+ ]);
13188
+ function substanceOf(file, text) {
13189
+ const syntax = syntaxOf(file);
13190
+ if (!syntax)
13191
+ return plainSubstance(text);
13192
+ const markup = syntax.block.some(([open]) => open === "<!--");
13193
+ if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
13194
+ return plainSubstance(text);
13195
+ return lex(syntax, text);
13196
+ }
13197
+ function plainSubstance(text) {
13198
+ return text.split(`
13199
+ `).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
13200
+ `);
13201
+ }
13202
+ function lex(syntax, text) {
13203
+ const collapse = !syntax.indented;
13204
+ let out = "";
13205
+ let lineStart = 0;
13206
+ let kept = 0;
13207
+ let pendingSpace = false;
13208
+ const frames = [{ kind: "code", braces: 0 }];
13209
+ let index = 0;
13210
+ if (text.startsWith("#!")) {
13211
+ const end = text.indexOf(`
13212
+ `);
13213
+ out = end < 0 ? text : text.slice(0, end);
13214
+ kept = out.length;
13215
+ index = end < 0 ? text.length : end;
13216
+ }
13217
+ const emitCode = (chunk) => {
13218
+ if (collapse && pendingSpace && out.length > lineStart) {
13219
+ const before = out[out.length - 1];
13220
+ if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
13221
+ out += " ";
13222
+ }
13223
+ pendingSpace = false;
13224
+ out += chunk;
13225
+ };
13226
+ const emitVerbatim = (chunk) => {
13227
+ emitCode(chunk);
13228
+ kept = out.length;
13229
+ };
13230
+ const newline = () => {
13231
+ if (!collapse) {
13232
+ let end = out.length;
13233
+ while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
13234
+ end -= 1;
13235
+ out = out.slice(0, end);
13236
+ }
13237
+ pendingSpace = false;
13238
+ if (out.length > lineStart) {
13239
+ out += `
13240
+ `;
13241
+ lineStart = out.length;
13242
+ }
13243
+ };
13244
+ const regexAllowed = () => {
13245
+ let end = out.length;
13246
+ while (end > 0 && /\s/u.test(out[end - 1]))
13247
+ end -= 1;
13248
+ if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
13249
+ return true;
13250
+ let start = end;
13251
+ while (start > 0 && WORD.test(out[start - 1]))
13252
+ start -= 1;
13253
+ return REGEX_KEYWORDS.has(out.slice(start, end));
13254
+ };
13255
+ const scanString = (quote, escapes, multiline) => {
13256
+ const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
13257
+ const fstring = syntax.fstring === true && /f/iu.test(prefix);
13258
+ let end = index + quote.length;
13259
+ while (end < text.length) {
13260
+ const char = text[end];
13261
+ if (escapes && char === "\\") {
13262
+ end += 2;
13263
+ continue;
13264
+ }
13265
+ if (text.startsWith(quote, end)) {
13266
+ if (fstring && unbalanced(text.slice(index + quote.length, end)))
13267
+ return false;
13268
+ emitVerbatim(text.slice(index, end + quote.length));
13269
+ index = end + quote.length;
13270
+ return true;
13271
+ }
13272
+ if (char === `
13273
+ ` && !multiline)
13274
+ return false;
13275
+ end += 1;
13276
+ }
13277
+ return false;
13278
+ };
13279
+ while (index < text.length) {
13280
+ const frame = frames[frames.length - 1];
13281
+ const char = text[index];
13282
+ if (frame.kind === "template") {
13283
+ if (char === "\\") {
13284
+ emitVerbatim(text.slice(index, index + 2));
13285
+ index += 2;
13286
+ } else if (char === "`") {
13287
+ emitVerbatim(char);
13288
+ frames.pop();
13289
+ index += 1;
13290
+ } else if (text.startsWith("${", index)) {
13291
+ emitVerbatim("${");
13292
+ frames.push({ kind: "code", braces: 0 });
13293
+ index += 2;
13294
+ } else {
13295
+ out += char;
13296
+ kept = out.length;
13297
+ index += 1;
13298
+ }
13299
+ continue;
13300
+ }
13301
+ if (char === `
13302
+ `) {
13303
+ newline();
13304
+ index += 1;
13305
+ continue;
13306
+ }
13307
+ if (SPACE.test(char)) {
13308
+ if (collapse)
13309
+ pendingSpace = true;
13310
+ else
13311
+ out += char;
13312
+ index += 1;
13313
+ continue;
13314
+ }
13315
+ const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
13316
+ if (lineOpener) {
13317
+ const end = text.indexOf(`
13318
+ `, index);
13319
+ const comment = text.slice(index, end < 0 ? text.length : end);
13320
+ if (DIRECTIVE.test(comment.slice(lineOpener.length)))
13321
+ emitVerbatim(comment);
13322
+ else
13323
+ pendingSpace = true;
13324
+ index = end < 0 ? text.length : end;
13325
+ continue;
13326
+ }
13327
+ const block = syntax.block.find(([open]) => text.startsWith(open, index));
13328
+ if (block) {
13329
+ const end = text.indexOf(block[1], index + block[0].length);
13330
+ if (end < 0)
13331
+ return null;
13332
+ const comment = text.slice(index, end + block[1].length);
13333
+ if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
13334
+ emitVerbatim(comment);
13335
+ else
13336
+ pendingSpace = true;
13337
+ if (comment.includes(`
13338
+ `))
13339
+ newline();
13340
+ index = end + block[1].length;
13341
+ continue;
13342
+ }
13343
+ if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
13344
+ if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
13345
+ return null;
13346
+ continue;
13347
+ }
13348
+ if (syntax.quotes.includes(char)) {
13349
+ if (!scanString(char, true, Boolean(syntax.multiline)))
13350
+ return null;
13351
+ continue;
13352
+ }
13353
+ if (syntax.literal?.includes(char)) {
13354
+ if (!scanString(char, false, false))
13355
+ return null;
13356
+ continue;
13357
+ }
13358
+ if (syntax.rawBacktick && char === "`") {
13359
+ if (!scanString("`", false, true))
13360
+ return null;
13361
+ continue;
13362
+ }
13363
+ if (syntax.js && char === "`") {
13364
+ emitVerbatim(char);
13365
+ frames.push({ kind: "template" });
13366
+ index += 1;
13367
+ continue;
13368
+ }
13369
+ if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
13370
+ return null;
13371
+ if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
13372
+ const lineEnd = text.indexOf(`
13373
+ `, index);
13374
+ if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
13375
+ return null;
13376
+ }
13377
+ if (syntax.js && char === "/" && regexAllowed()) {
13378
+ let end = index + 1;
13379
+ let inClass = false;
13380
+ while (end < text.length && (inClass || text[end] !== "/")) {
13381
+ if (text[end] === `
13382
+ `)
13383
+ return null;
13384
+ if (text[end] === "\\")
13385
+ end += 1;
13386
+ else if (text[end] === "[")
13387
+ inClass = true;
13388
+ else if (text[end] === "]")
13389
+ inClass = false;
13390
+ end += 1;
13391
+ }
13392
+ if (end >= text.length)
13393
+ return null;
13394
+ emitVerbatim(text.slice(index, end + 1));
13395
+ index = end + 1;
13396
+ continue;
13397
+ }
13398
+ if (frames.length > 1 && frame.kind === "code") {
13399
+ if (char === "{")
13400
+ frame.braces += 1;
13401
+ else if (char === "}") {
13402
+ if (frame.braces === 0) {
13403
+ emitVerbatim(char);
13404
+ frames.pop();
13405
+ index += 1;
13406
+ continue;
13407
+ }
13408
+ frame.braces -= 1;
13409
+ }
13410
+ }
13411
+ emitCode(char);
13412
+ index += 1;
13413
+ }
13414
+ if (frames.length !== 1)
13415
+ return null;
13416
+ newline();
13417
+ return out;
13418
+ }
13419
+ function unbalanced(body) {
13420
+ let depth = 0;
13421
+ for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
13422
+ if (part === "{")
13423
+ depth += 1;
13424
+ else if (part === "}")
13425
+ depth = Math.max(0, depth - 1);
13426
+ }
13427
+ return depth > 0;
13428
+ }
13429
+ function hasOpaqueLineBreak(text) {
13430
+ return /\r(?!\n)|[\u2028\u2029]/u.test(text);
13431
+ }
13432
+ var decoder = new TextDecoder("utf-8", { fatal: true });
13433
+ function textOf(bytes) {
13434
+ if (bytes.includes(0))
13435
+ return null;
13436
+ try {
13437
+ return decoder.decode(bytes);
13438
+ } catch {
13439
+ return null;
13440
+ }
13441
+ }
13442
+ function cosmeticChange(file, before, after) {
13443
+ const left = textOf(before);
13444
+ const right = textOf(after);
13445
+ if (left === null || right === null)
13446
+ return false;
13447
+ if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
13448
+ return false;
13449
+ const a = substanceOf(file, left);
13450
+ return a !== null && a === substanceOf(file, right);
13451
+ }
13452
+
13453
+ // packages/workit-core/src/core/policy/lane.ts
13454
+ var LANE_THRESHOLDS = {
13455
+ selfMaxLines: 100,
13456
+ selfMaxFiles: 5,
13457
+ fullMinLines: 500
13458
+ };
13459
+ var RISK_TOKENS = new Set([
13460
+ "auth",
13461
+ "authn",
13462
+ "authz",
13463
+ "oauth",
13464
+ "security",
13465
+ "permission",
13466
+ "permissions",
13467
+ "secret",
13468
+ "secrets",
13469
+ "crypto",
13470
+ "gate",
13471
+ "gates",
13472
+ "policy",
13473
+ "policies",
13474
+ "hook",
13475
+ "hooks",
13476
+ "husky",
13477
+ "lefthook",
13478
+ "release",
13479
+ "releases",
13480
+ "migration",
13481
+ "migrations",
13482
+ "migrate",
13483
+ "workflows"
13484
+ ]);
13485
+ var RISK_FILES = new Set([
13486
+ ".gitlab-ci.yml",
13487
+ "Jenkinsfile",
13488
+ "azure-pipelines.yml",
13489
+ ".pre-commit-config.yaml",
13490
+ ".workit/risk-paths"
13491
+ ]);
13492
+ var RISK_NAMES = new Set([".gitattributes"]);
13493
+ var RISK_PATHS_FILE = ".workit/risk-paths";
13494
+ var LOCKFILES = new Set([
13495
+ "bun.lock",
13496
+ "bun.lockb",
13497
+ "package-lock.json",
13498
+ "npm-shrinkwrap.json",
13499
+ "yarn.lock",
13500
+ "pnpm-lock.yaml",
13501
+ "Cargo.lock",
13502
+ "go.sum",
13503
+ "poetry.lock",
13504
+ "uv.lock",
13505
+ "Pipfile.lock",
13506
+ "Gemfile.lock",
13507
+ "composer.lock"
13508
+ ]);
13509
+ var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc|txt)$/iu;
13510
+ var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
13511
+ var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
13512
+ var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__|spec|specs|fixtures)\/|\.(?:test|spec)\.[^/]+$/iu;
13513
+ var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
13514
+ var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
13515
+ function kindOf(file) {
13516
+ if (file.binary)
13517
+ return "behavior";
13518
+ if (file.generated)
13519
+ return "generated";
13520
+ if (LOCKFILES.has(basename(file.path)))
13521
+ return "lockfile";
13522
+ if (isDoc(file.path))
13523
+ return "doc";
13524
+ if (file.cosmetic)
13525
+ return "cosmetic";
13526
+ return TEST_PATH.test(file.path) ? "test" : "behavior";
13527
+ }
13528
+ var globCache = new Map;
13529
+ function globMatch(pattern, file) {
13530
+ let regex = globCache.get(pattern);
13531
+ if (!regex) {
13532
+ let source = "";
13533
+ for (let index = 0;index < pattern.length; index += 1) {
13534
+ const char = pattern[index];
13535
+ if (char === "*" && pattern[index + 1] === "*") {
13536
+ source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
13537
+ index += pattern[index + 2] === "/" ? 2 : 1;
13538
+ } else if (char === "*")
13539
+ source += "[^/]*";
13540
+ else if (char === "?")
13541
+ source += "[^/]";
13542
+ else
13543
+ source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
13544
+ }
13545
+ regex = new RegExp(`^${source}$`, "u");
13546
+ globCache.set(pattern, regex);
13547
+ }
13548
+ return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
13549
+ }
13550
+ function isRiskPath(file, extra = []) {
13551
+ if (extra.some((pattern) => globMatch(pattern, file)))
13552
+ return true;
13553
+ if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
13554
+ return true;
13555
+ if (TEST_PATH.test(file) || isDoc(file))
13556
+ return false;
13557
+ 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)));
13558
+ }
13559
+ var parseRiskPaths = (text) => text.split(`
13560
+ `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
13561
+ function computeLane(input) {
13562
+ const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
13563
+ const risky = new Set(riskFiles);
13564
+ const kinds = input.files.map((file) => ({
13565
+ file,
13566
+ kind: risky.has(file.path) ? "behavior" : kindOf(file)
13567
+ }));
13568
+ const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
13569
+ const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
13570
+ const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
13571
+ const result = (lane, reasons) => ({
13572
+ lane,
13573
+ reasons,
13574
+ lines,
13575
+ files: behavior.length,
13576
+ riskFiles
13577
+ });
13578
+ const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
13579
+ if (input.riskTier === "high")
13580
+ return result("full", ["the task judgment records high risk"]);
13581
+ if (riskFiles.length)
13582
+ return result("full", [
13583
+ `touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
13584
+ ]);
13585
+ if (!behavior.length)
13586
+ return result("none", [
13587
+ input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
13588
+ ]);
13589
+ if (lines >= fullMinLines)
13590
+ return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
13591
+ if (lines > selfMaxLines)
13592
+ return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
13593
+ if (behavior.length > selfMaxFiles)
13594
+ return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
13595
+ if (opaque.length)
13596
+ return result("focused", [
13597
+ `a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
13598
+ ]);
13599
+ return result("self", [
13600
+ `small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
13601
+ ]);
13602
+ }
13603
+ function untrackedStat(cwd, file) {
13604
+ try {
13605
+ const bytes = readFileSync4(path14.join(cwd, file));
13606
+ if (bytes.includes(0))
13607
+ return { path: file, added: 0, deleted: 0, binary: true };
13608
+ const text = bytes.toString("utf8");
13609
+ if (hasOpaqueLineBreak(text))
13610
+ return { path: file, added: 0, deleted: 0, binary: true };
13611
+ const lines = text.split(`
13612
+ `).length - (text.endsWith(`
13613
+ `) ? 1 : 0);
13614
+ return { path: file, added: lines, deleted: 0 };
13615
+ } catch {
13616
+ return { path: file, added: 0, deleted: 0, binary: true };
13617
+ }
13618
+ }
13619
+ var REGULAR = new Set(["100644", "100755"]);
13620
+ var ABSENT = /^0+$/u;
13621
+ function cosmeticFiles(cwd, fork, head) {
13622
+ const raw = diffRaw(cwd, fork, head);
13623
+ if (!raw)
13624
+ return null;
13625
+ const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
13626
+ const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
13627
+ if (!blobs)
13628
+ return null;
13629
+ const out = new Set;
13630
+ const opaque = new Set;
13631
+ for (const entry of candidates) {
13632
+ const before = blobs.get(entry.oldBlob);
13633
+ let after = blobs.get(entry.newBlob);
13634
+ if (!after && head === null && ABSENT.test(entry.newBlob))
13635
+ try {
13636
+ after = readFileSync4(path14.join(cwd, entry.path));
13637
+ } catch {
13638
+ after = undefined;
13639
+ }
13640
+ if (before && after && cosmeticChange(entry.path, before, after))
13641
+ out.add(entry.path);
13642
+ else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
13643
+ opaque.add(entry.path);
13644
+ }
13645
+ return { cosmetic: out, opaque };
13646
+ }
13647
+ function generatedAt(cwd, fork, paths) {
13648
+ const files = treeFilesNamed(cwd, fork, ".gitattributes");
13649
+ const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
13650
+ if (!files || !blobs)
13651
+ return new Set;
13652
+ const texts = new Map;
13653
+ for (const [file, id] of files) {
13654
+ const bytes = blobs.get(id);
13655
+ if (bytes)
13656
+ texts.set(file, bytes.toString("utf8"));
13657
+ }
13658
+ return generatedByAttributes(texts, paths);
13659
+ }
13660
+ var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
13661
+ function laneFiles(cwd, fork, head) {
13662
+ const stats = diffNumstat(cwd, fork, head);
13663
+ const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
13664
+ if (!stats || !cosmetic)
13665
+ return null;
13666
+ const files = [
13667
+ ...stats.map((entry) => ({
13668
+ ...entry,
13669
+ ...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
13670
+ cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
13671
+ })),
13672
+ ...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
13673
+ ];
13674
+ const generated = generatedAt(cwd, fork, files.map((entry) => entry.path));
13675
+ return files.map((entry) => ({
13676
+ ...entry,
13677
+ generated: generated.has(entry.path)
13678
+ }));
13679
+ }
13680
+ function branchLane(cwd, options) {
13681
+ const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
13682
+ const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
13683
+ const head = options.workingTree ? null : options.head;
13684
+ const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
13685
+ const files = fork ? laneFiles(cwd, fork, head) : null;
13686
+ const riskTier = options.judgment?.riskTier ?? null;
13687
+ if (!fork || !files) {
13688
+ const high = riskTier === "high";
13689
+ return {
13690
+ lane: high ? "full" : "focused",
13691
+ reasons: [
13692
+ `the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
13693
+ ...high ? ["the task judgment records high risk"] : []
13694
+ ],
13695
+ lines: 0,
13696
+ files: 0,
13697
+ riskFiles: [],
13698
+ base,
13699
+ head: options.head
13700
+ };
13701
+ }
13702
+ const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
13703
+ return {
13704
+ ...computeLane({
13705
+ files,
13706
+ riskTier,
13707
+ riskPaths: [...repoRisk, ...options.riskPaths ?? []]
13708
+ }),
13709
+ base,
13710
+ head: options.head
13711
+ };
13712
+ }
13713
+ var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
13714
+
12927
13715
  // packages/workit-core/src/core/policy/derive.ts
12928
13716
  var RULES = {
12929
13717
  test: "check:test",
12930
13718
  self: "verdict:self",
12931
13719
  nonAuthor: "verdict:non-author",
12932
13720
  verified: "verdict:verified",
13721
+ lane: "verdict:lane",
12933
13722
  product: "decision:product",
12934
13723
  plan: "plan"
12935
13724
  };
@@ -12952,6 +13741,7 @@ var normalizedScope = (scope) => ({
12952
13741
  function deriveRequirements(judgment, scope, constraints, verification = "self") {
12953
13742
  const out = [];
12954
13743
  const base = { scope, dependentAction: null };
13744
+ const proportional = verification === "proportional";
12955
13745
  if (judgment.behaviorChange)
12956
13746
  out.push(derived2({
12957
13747
  ...base,
@@ -12962,7 +13752,17 @@ function deriveRequirements(judgment, scope, constraints, verification = "self")
12962
13752
  before: "close",
12963
13753
  acceptanceAllowed: false
12964
13754
  }));
12965
- if (judgment.behaviorChange && judgment.riskTier === "normal")
13755
+ if (proportional && judgment.riskTier === "normal")
13756
+ out.push(derived2({
13757
+ ...base,
13758
+ ruleId: RULES.lane,
13759
+ dimension: "review",
13760
+ reason: "The review lane computed from the diff decides the verdict: none needs none, self the author's own (shown as self-reviewed), focused and full a non-author one.",
13761
+ satisfaction: "Read the lane in `workit policy assess` or `workit pr status`; record what it asks: `workit ledger verdict tests-verified --self --how <what you ran>` for self, or hand focused and full to a verifier.",
13762
+ before: "close",
13763
+ acceptanceAllowed: false
13764
+ }));
13765
+ if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
12966
13766
  out.push(verification === "independent" ? derived2({
12967
13767
  ...base,
12968
13768
  ruleId: RULES.nonAuthor,
@@ -13065,7 +13865,7 @@ var productDecision = (task, ledger) => {
13065
13865
  };
13066
13866
  };
13067
13867
  var planRecorded = (task, root) => {
13068
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path14.resolve(root, ref.path)));
13868
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(path15.resolve(root, ref.path)));
13069
13869
  if (cited)
13070
13870
  return {
13071
13871
  met: true,
@@ -13079,14 +13879,30 @@ var planRecorded = (task, root) => {
13079
13879
  decisionIds: []
13080
13880
  };
13081
13881
  };
13082
- var verdict = (requirement, root, ledger) => {
13882
+ var workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) });
13883
+ var laneVerdict = (task, root, ledger) => {
13884
+ if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
13885
+ return {
13886
+ met: true,
13887
+ reason: "no branch to read a lane from, and the judgment records no behavior change",
13888
+ decisionIds: []
13889
+ };
13890
+ const lane = workingLane(task, root);
13891
+ const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
13892
+ const need = laneNeed(lane.lane);
13893
+ if (need === "none")
13894
+ return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
13895
+ const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
13896
+ return { ...status, reason: `${label}: ${status.reason}` };
13897
+ };
13898
+ var verdict = (ruleId, root, ledger) => {
13083
13899
  const branch = ledger.branch();
13084
13900
  if (!branch)
13085
13901
  return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
13086
13902
  const check = checkVerdicts(root, branch, ledger.rows());
13087
13903
  const failing = check.accepted.reasons.includes("failing_verdict");
13088
13904
  const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
13089
- const met = !failing && (requirement.ruleId === RULES.verified ? check.verdicts.find((entry) => entry.accepted && entry.kind === "live" && String(entry.verdict.result) === "verified") : requirement.ruleId === RULES.nonAuthor ? check.verdicts.find((entry) => entry.accepted && strong(entry)) : check.verdicts.find((entry) => entry.current && strong(entry)));
13905
+ const met = !failing && (ruleId === RULES.verified ? check.verdicts.find((entry) => entry.accepted && entry.kind === "live" && String(entry.verdict.result) === "verified") : ruleId === RULES.nonAuthor ? check.verdicts.find((entry) => entry.accepted && strong(entry)) : check.verdicts.find((entry) => entry.current && strong(entry)));
13090
13906
  if (met)
13091
13907
  return {
13092
13908
  met: true,
@@ -13094,7 +13910,7 @@ var verdict = (requirement, root, ledger) => {
13094
13910
  decisionIds: []
13095
13911
  };
13096
13912
  const why = failing ? "a current independent verdict fails" : check.verdicts.some((entry) => entry.current && entry.verdict.result === "type-check-only") ? "type-check-only does not prove a behavior change" : (check.accepted.reasons.join(", ") || "no_verdict").replace(/_/g, " ");
13097
- const unblock = requirement.ruleId === RULES.self ? "record your own verdict after an observed `workit check test`: `workit ledger verdict tests-verified --self --how <what you ran>` (shown as self-reviewed), or have a verifier record one" : `an independent session records \`workit ledger verdict verified${requirement.ruleId === RULES.verified ? " --kind live" : ""} --how <what it exercised>\``;
13913
+ const unblock = ruleId === RULES.self ? "record your own verdict after an observed `workit check test`: `workit ledger verdict tests-verified --self --how <what you ran>` (shown as self-reviewed), or have a verifier record one" : `an independent session records \`workit ledger verdict verified${ruleId === RULES.verified ? " --kind live" : ""} --how <what it exercised>\``;
13098
13914
  return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
13099
13915
  };
13100
13916
  function derivedRuleStatus(task, requirement, root, ledger) {
@@ -13106,7 +13922,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
13106
13922
  case RULES.self:
13107
13923
  case RULES.nonAuthor:
13108
13924
  case RULES.verified:
13109
- return verdict(requirement, root, ledger);
13925
+ return verdict(requirement.ruleId, root, ledger);
13926
+ case RULES.lane:
13927
+ return laneVerdict(task, root, ledger);
13110
13928
  default:
13111
13929
  return null;
13112
13930
  }
@@ -13125,14 +13943,14 @@ function writeBlockers(task, root) {
13125
13943
 
13126
13944
  // packages/workit-core/src/core/task-evaluation.ts
13127
13945
  var digestBytes = (value) => createHash11("sha256").update(value).digest("hex");
13128
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path15.sep}`);
13129
- var relative = (root, candidate) => path15.relative(root, candidate).split(path15.sep).join("/") || ".";
13946
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
13947
+ var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
13130
13948
  var canonicalPath = (value) => {
13131
13949
  if (typeof value !== "string" || !value)
13132
13950
  return null;
13133
13951
  if (value.includes("\\") || value.split("/").includes(".."))
13134
13952
  return null;
13135
- const normalized = path15.posix.normalize(value);
13953
+ const normalized = path16.posix.normalize(value);
13136
13954
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
13137
13955
  return null;
13138
13956
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -13180,14 +13998,14 @@ var gitPaths = (root) => {
13180
13998
  git: !/not a git repository/i.test(stderr)
13181
13999
  };
13182
14000
  }
13183
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path15.sep).join("/")) : [];
14001
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
13184
14002
  const stagedDeleted = spawnSync9("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
13185
14003
  if (stagedDeleted.status !== 0) {
13186
14004
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
13187
14005
  if (!/not a git repository/i.test(stderr))
13188
14006
  return { paths, uncertain: true, git: true };
13189
14007
  } else if (stagedDeleted.stdout) {
13190
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path15.sep).join("/")));
14008
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
13191
14009
  }
13192
14010
  return { paths: [...new Set(paths)], uncertain: false, git: true };
13193
14011
  };
@@ -13202,7 +14020,7 @@ var walk = (root, directory, output) => {
13202
14020
  for (const name of names) {
13203
14021
  if (name === ".git" || name === ".workit")
13204
14022
  continue;
13205
- const target = path15.join(directory, name);
14023
+ const target = path16.join(directory, name);
13206
14024
  let stat;
13207
14025
  try {
13208
14026
  stat = fs12.lstatSync(target);
@@ -13220,12 +14038,12 @@ var walk = (root, directory, output) => {
13220
14038
  var scopeRoots = (root, scope) => {
13221
14039
  const roots = [];
13222
14040
  for (const item of scope.paths.length ? scope.paths : ["."]) {
13223
- const target = path15.resolve(root, item);
14041
+ const target = path16.resolve(root, item);
13224
14042
  if (!inside(root, target))
13225
14043
  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` }] });
13226
14044
  let ancestor = target;
13227
14045
  while (!fs12.existsSync(ancestor) && ancestor !== root)
13228
- ancestor = path15.dirname(ancestor);
14046
+ ancestor = path16.dirname(ancestor);
13229
14047
  try {
13230
14048
  if (!inside(root, fs12.realpathSync(ancestor)))
13231
14049
  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` }] });
@@ -13277,7 +14095,7 @@ function captureCandidate(root, scope, environment = []) {
13277
14095
  for (const item of [...names].toSorted()) {
13278
14096
  if (!scopeMatches(item, normalizedScope))
13279
14097
  continue;
13280
- const target = path15.join(checkout, item);
14098
+ const target = path16.join(checkout, item);
13281
14099
  try {
13282
14100
  const stat = fs12.lstatSync(target);
13283
14101
  if (stat.isSymbolicLink()) {
@@ -13461,23 +14279,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
13461
14279
  continue;
13462
14280
  if (!reference.digest)
13463
14281
  return failure2("invalid_input", "document references require a byte digest");
13464
- const target = path15.resolve(root, reference.path);
13465
- if (target !== root && !target.startsWith(`${root}${path15.sep}`))
14282
+ const target = path16.resolve(root, reference.path);
14283
+ if (target !== root && !target.startsWith(`${root}${path16.sep}`))
13466
14284
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
13467
14285
  fields: [
13468
14286
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
13469
14287
  ]
13470
14288
  });
13471
14289
  try {
13472
- const below = path15.relative(root, target);
14290
+ const below = path16.relative(root, target);
13473
14291
  let current = root;
13474
- for (const segment of below.split(path15.sep)) {
14292
+ for (const segment of below.split(path16.sep)) {
13475
14293
  if (!segment)
13476
14294
  continue;
13477
- current = path15.join(current, segment);
14295
+ current = path16.join(current, segment);
13478
14296
  const stat = fs12.lstatSync(current);
13479
14297
  const real = fs12.realpathSync(current);
13480
- if (real !== root && !real.startsWith(`${root}${path15.sep}`))
14298
+ if (real !== root && !real.startsWith(`${root}${path16.sep}`))
13481
14299
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
13482
14300
  fields: [
13483
14301
  {
@@ -14398,7 +15216,7 @@ var judgeInputSummary = (raw) => {
14398
15216
 
14399
15217
  // packages/workit-core/src/autonomy.ts
14400
15218
  import os4 from "node:os";
14401
- import path16 from "node:path";
15219
+ import path17 from "node:path";
14402
15220
  var accountHome = () => {
14403
15221
  try {
14404
15222
  return os4.userInfo().homedir;
@@ -14407,10 +15225,10 @@ var accountHome = () => {
14407
15225
  }
14408
15226
  };
14409
15227
  var grantsHome = { resolve: accountHome };
14410
- var grantsDir = () => path16.join(grantsHome.resolve(), ".config", "workit");
15228
+ var grantsDir = () => path17.join(grantsHome.resolve(), ".config", "workit");
14411
15229
  var grantsOverride = () => {
14412
- const configured = path16.resolve(resolveConfigDir());
14413
- return configured === path16.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 ${path16.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
15230
+ const configured = path17.resolve(resolveConfigDir());
15231
+ return configured === path17.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 ${path17.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
14414
15232
  };
14415
15233
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
14416
15234
  var DEFAULT_GRANTS = Object.freeze({
@@ -14463,8 +15281,16 @@ function endpointView(entry, context) {
14463
15281
  ...issue ? { endpointIssue: issue } : {}
14464
15282
  };
14465
15283
  }
14466
- var DEFAULT_VERIFICATION = "self";
14467
- var verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self";
15284
+ var VERIFICATION_MODES = [
15285
+ "proportional",
15286
+ "self",
15287
+ "independent"
15288
+ ];
15289
+ var DEFAULT_VERIFICATION = "proportional";
15290
+ var verificationOf = (entry) => {
15291
+ const value = entry?.verification;
15292
+ return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
15293
+ };
14468
15294
  var isGrantValue = (value) => value === true || value === false || value === "verified";
14469
15295
  var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
14470
15296
  var fromAutoApprove = (value) => {
@@ -16423,7 +17249,7 @@ function entryCompactContext(store, entry, session, context) {
16423
17249
  return null;
16424
17250
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
16425
17251
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
16426
- const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path17.resolve(store.root, ref.path)] : []));
17252
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path18.resolve(store.root, ref.path)] : []));
16427
17253
  const observedFiles = filesKey(files);
16428
17254
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
16429
17255
  if (!compact.ok || !compact.data)
@@ -16501,14 +17327,14 @@ var turnMarker = (input) => {
16501
17327
  if (location instanceof Error)
16502
17328
  return null;
16503
17329
  const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
16504
- return path17.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
17330
+ return path18.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
16505
17331
  } catch {
16506
17332
  return null;
16507
17333
  }
16508
17334
  };
16509
17335
  var writeTurnDigest = (file, digest) => {
16510
17336
  try {
16511
- fs13.mkdirSync(path17.dirname(file), { recursive: true });
17337
+ fs13.mkdirSync(path18.dirname(file), { recursive: true });
16512
17338
  fs13.writeFileSync(file, `${JSON.stringify({ digest })}
16513
17339
  `);
16514
17340
  } catch {}
@@ -16523,7 +17349,7 @@ var pruneTurnDigests = (dir, now) => {
16523
17349
  for (const name of fs13.readdirSync(dir)) {
16524
17350
  if (!name.startsWith("turn-"))
16525
17351
  continue;
16526
- const file = path17.join(dir, name);
17352
+ const file = path18.join(dir, name);
16527
17353
  try {
16528
17354
  if (now - fs13.statSync(file).mtimeMs > TURN_TTL_MS)
16529
17355
  fs13.rmSync(file, { force: true });
@@ -16553,7 +17379,7 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
16553
17379
  const file = turnMarker(input);
16554
17380
  if (!file)
16555
17381
  return;
16556
- pruneTurnDigests(path17.dirname(file), Date.now());
17382
+ pruneTurnDigests(path18.dirname(file), Date.now());
16557
17383
  if (text)
16558
17384
  writeTurnDigest(file, digestOf(text));
16559
17385
  else
@@ -16561,20 +17387,20 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
16561
17387
  };
16562
17388
  // packages/workit-core/src/core/route-intent.ts
16563
17389
  import { spawnSync as spawnSync11 } from "node:child_process";
16564
- import { existsSync as existsSync6 } from "node:fs";
16565
- import path20 from "node:path";
17390
+ import { existsSync as existsSync7 } from "node:fs";
17391
+ import path21 from "node:path";
16566
17392
 
16567
17393
  // packages/workit-core/src/hooks/raw-git.ts
16568
17394
  import { spawnSync as spawnSync10 } from "node:child_process";
16569
17395
  import { createHash as createHash14 } from "node:crypto";
16570
17396
  import fs15 from "node:fs";
16571
17397
  import os5 from "node:os";
16572
- import path19 from "node:path";
17398
+ import path20 from "node:path";
16573
17399
 
16574
17400
  // packages/workit-core/src/hooks/session-marker.ts
16575
17401
  import { createHash as createHash13 } from "node:crypto";
16576
17402
  import fs14 from "node:fs";
16577
- import path18 from "node:path";
17403
+ import path19 from "node:path";
16578
17404
  var sessionMarkerFile = (input, session, store) => {
16579
17405
  let dir = store;
16580
17406
  if (dir === undefined) {
@@ -16584,7 +17410,7 @@ var sessionMarkerFile = (input, session, store) => {
16584
17410
  dir = location.dir;
16585
17411
  }
16586
17412
  const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
16587
- return path18.join(dir, "hooks", `skills-${name}.json`);
17413
+ return path19.join(dir, "hooks", `skills-${name}.json`);
16588
17414
  };
16589
17415
  var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
16590
17416
  var EMPTY = { nudged: [], loaded: [], raw: [], stops: [], judges: [] };
@@ -16611,7 +17437,7 @@ var parseMarker = (text) => {
16611
17437
  };
16612
17438
  var readSessionMarker = (file) => parseMarker(readRaw(file));
16613
17439
  var writeAtomic = (file, marker) => {
16614
- fs14.mkdirSync(path18.dirname(file), { recursive: true });
17440
+ fs14.mkdirSync(path19.dirname(file), { recursive: true });
16615
17441
  const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
16616
17442
  try {
16617
17443
  fs14.writeFileSync(temp, JSON.stringify(marker));
@@ -17139,17 +17965,17 @@ var GIT_VALUE_OPTIONS = new Set([
17139
17965
  ]);
17140
17966
  var GIT_CONFIG_OPTIONS = new Set(["-c", "--config-env", "--git-dir"]);
17141
17967
  var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
17142
- var ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
17968
+ var ASSIGNMENT2 = /^([A-Za-z_]\w*)=([\s\S]*)$/;
17143
17969
  var MAX_DEPTH = 4;
17144
17970
  var MAX_EXPANSIONS = 16;
17145
- var isAbsolute = (value) => path19.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
17971
+ var isAbsolute = (value) => path20.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
17146
17972
  var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
17147
17973
  var joinDir = (base, raw) => {
17148
17974
  const next = raw.replace(HERE, ".");
17149
17975
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
17150
17976
  return;
17151
- const expanded = next === "~" || next.startsWith("~/") ? path19.join(os5.homedir(), next.slice(1)) : next;
17152
- return base === null || isAbsolute(expanded) ? expanded : path19.join(base, expanded);
17977
+ const expanded = next === "~" || next.startsWith("~/") ? path20.join(os5.homedir(), next.slice(1)) : next;
17978
+ return base === null || isAbsolute(expanded) ? expanded : path20.join(base, expanded);
17153
17979
  };
17154
17980
  var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
17155
17981
  var cdTarget = (words) => {
@@ -17166,7 +17992,7 @@ var stripPrefixes = (input) => {
17166
17992
  const first = words[0];
17167
17993
  if (first === undefined)
17168
17994
  return { words, env };
17169
- const assignment = ASSIGNMENT.exec(first);
17995
+ const assignment = ASSIGNMENT2.exec(first);
17170
17996
  if (assignment) {
17171
17997
  if (GIT_CONFIG_ENV.test(assignment[1]))
17172
17998
  env[assignment[1]] = assignment[2];
@@ -17225,10 +18051,10 @@ var assignments = (words, vars) => {
17225
18051
  while (rest[0]?.startsWith("-"))
17226
18052
  rest = rest.slice(1);
17227
18053
  }
17228
- if (rest.length === 0 || !rest.every((word) => ASSIGNMENT.test(word)))
18054
+ if (rest.length === 0 || !rest.every((word) => ASSIGNMENT2.test(word)))
17229
18055
  return null;
17230
18056
  return rest.map((word) => {
17231
- const [, name, value] = ASSIGNMENT.exec(word);
18057
+ const [, name, value] = ASSIGNMENT2.exec(word);
17232
18058
  return [name, expandWord(value, vars)];
17233
18059
  });
17234
18060
  };
@@ -17416,7 +18242,7 @@ var splitString = (line) => {
17416
18242
  return line.slice(index).join(" ").slice(15);
17417
18243
  if (/^-S./.test(word))
17418
18244
  return [word.slice(2), ...line.slice(index + 1)].join(" ");
17419
- if (!word.startsWith("-") && !ASSIGNMENT.test(word))
18245
+ if (!word.startsWith("-") && !ASSIGNMENT2.test(word))
17420
18246
  return null;
17421
18247
  }
17422
18248
  return null;
@@ -18000,17 +18826,17 @@ var headSha2 = (repo) => {
18000
18826
  if (!git || git.reftable)
18001
18827
  return null;
18002
18828
  try {
18003
- const head = fs15.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
18829
+ const head = fs15.readFileSync(path20.join(git.gitDir, "HEAD"), "utf8").trim();
18004
18830
  if (/^[0-9a-f]{40,64}$/.test(head))
18005
18831
  return head;
18006
18832
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
18007
18833
  if (!ref)
18008
18834
  return null;
18009
- const common = path19.dirname(repo.location.dir);
18835
+ const common = path20.dirname(repo.location.dir);
18010
18836
  try {
18011
- return fs15.readFileSync(path19.join(common, ref), "utf8").trim() || null;
18837
+ return fs15.readFileSync(path20.join(common, ref), "utf8").trim() || null;
18012
18838
  } catch {}
18013
- const packed = fs15.readFileSync(path19.join(common, "packed-refs"), "utf8");
18839
+ const packed = fs15.readFileSync(path20.join(common, "packed-refs"), "utf8");
18014
18840
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
18015
18841
  } catch {
18016
18842
  return null;
@@ -18098,7 +18924,7 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
18098
18924
  const sub = invocation.args[0];
18099
18925
  if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
18100
18926
  return [invocation];
18101
- const dir = path19.resolve(root, invocation.dir ?? ".");
18927
+ const dir = path20.resolve(root, invocation.dir ?? ".");
18102
18928
  if (!fs15.existsSync(dir))
18103
18929
  return [invocation];
18104
18930
  const config = gitConfig(dir, invocation, cache);
@@ -18121,11 +18947,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
18121
18947
  var normalizedUrl = (url, dir) => {
18122
18948
  const trimmed = url.trim();
18123
18949
  if (/^file:\/\//i.test(trimmed))
18124
- return path19.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
18950
+ return path20.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
18125
18951
  const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
18126
18952
  const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
18127
18953
  if (!scheme && !scp)
18128
- return path19.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
18954
+ return path20.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
18129
18955
  const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
18130
18956
  return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
18131
18957
  };
@@ -18145,7 +18971,7 @@ var rewrittenUrl = (url, config) => {
18145
18971
  }
18146
18972
  return url;
18147
18973
  };
18148
- var pushUrl = (config, remote, dir) => {
18974
+ var pushUrl2 = (config, remote, dir) => {
18149
18975
  const configured = configValue(config, `remote.${remote}.pushurl`) ?? configValue(config, `remote.${remote}.url`);
18150
18976
  return normalizedUrl(rewrittenUrl(configured ?? remote, config), dir);
18151
18977
  };
@@ -18173,7 +18999,7 @@ var toPushRemote = (repo, remote, config) => {
18173
18999
  const pushRemote = pushRemoteOf(config, repo.branch);
18174
19000
  if (pushRemote === null || pushRemote === remote)
18175
19001
  return true;
18176
- return pushUrl(config, remote, repo.dir) === pushUrl(config, pushRemote, repo.dir);
19002
+ return pushUrl2(config, remote, repo.dir) === pushUrl2(config, pushRemote, repo.dir);
18177
19003
  };
18178
19004
  var quote = (text) => `\`${text}\``;
18179
19005
  var refusal = (action, branch, dir) => {
@@ -18349,7 +19175,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
18349
19175
  const out = [];
18350
19176
  const repos = new Map;
18351
19177
  const repoFor = (relative) => {
18352
- const dir = path19.resolve(cwd, relative ?? ".");
19178
+ const dir = path20.resolve(cwd, relative ?? ".");
18353
19179
  if (!repos.has(dir)) {
18354
19180
  const repo = repoAt(dir);
18355
19181
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
@@ -18435,7 +19261,7 @@ var analysisDenial = (error) => ({
18435
19261
  });
18436
19262
  var NEXT_COMMAND = "next";
18437
19263
  var MARKER_TTL_SECONDS = 3600;
18438
- var markerFile = (repo, session, key) => path19.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
19264
+ var markerFile = (repo, session, key) => path20.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
18439
19265
  var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
18440
19266
  function noteRawCommit(input, command, key) {
18441
19267
  try {
@@ -18446,7 +19272,7 @@ function noteRawCommit(input, command, key) {
18446
19272
  if (action.kind !== "commit")
18447
19273
  continue;
18448
19274
  const file = markerFile(repo, input.session.id, key);
18449
- fs15.mkdirSync(path19.dirname(file), { recursive: true });
19275
+ fs15.mkdirSync(path20.dirname(file), { recursive: true });
18450
19276
  fs15.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
18451
19277
  `);
18452
19278
  }
@@ -18677,8 +19503,8 @@ var judgedDir = (root, dir) => {
18677
19503
  return null;
18678
19504
  if (dir === null)
18679
19505
  return root;
18680
- const resolved = path20.resolve(root, dir);
18681
- return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
19506
+ const resolved = path21.resolve(root, dir);
19507
+ return existsSync7(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
18682
19508
  };
18683
19509
  var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
18684
19510
  let judged = false;
@@ -18714,7 +19540,7 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
18714
19540
  // packages/workit-core/src/core/heavy-rules.ts
18715
19541
  import fs16 from "node:fs";
18716
19542
  import os6 from "node:os";
18717
- import path21 from "node:path";
19543
+ import path22 from "node:path";
18718
19544
  var EMPTY2 = () => ({ deny: [], allow: [] });
18719
19545
  var readText2 = (file) => {
18720
19546
  try {
@@ -18755,19 +19581,19 @@ function parseJsonc(text) {
18755
19581
  return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
18756
19582
  }
18757
19583
  function gitTop(cwd) {
18758
- for (let dir = path21.resolve(cwd);; dir = path21.dirname(dir)) {
18759
- if (fs16.existsSync(path21.join(dir, ".git")))
19584
+ for (let dir = path22.resolve(cwd);; dir = path22.dirname(dir)) {
19585
+ if (fs16.existsSync(path22.join(dir, ".git")))
18760
19586
  return dir;
18761
- if (path21.dirname(dir) === dir)
19587
+ if (path22.dirname(dir) === dir)
18762
19588
  return null;
18763
19589
  }
18764
19590
  }
18765
19591
  var projectDirs = (cwd) => {
18766
19592
  const top = gitTop(cwd);
18767
19593
  const dirs = [];
18768
- for (let dir = path21.resolve(cwd);; dir = path21.dirname(dir)) {
19594
+ for (let dir = path22.resolve(cwd);; dir = path22.dirname(dir)) {
18769
19595
  dirs.push(dir);
18770
- if (!top || dir === top || path21.dirname(dir) === dir)
19596
+ if (!top || dir === top || path22.dirname(dir) === dir)
18771
19597
  return dirs;
18772
19598
  }
18773
19599
  };
@@ -18779,12 +19605,12 @@ var CLAUDE_MANAGED = {
18779
19605
  };
18780
19606
  function claudeSettingsFiles(cwd, env, platform = process.platform) {
18781
19607
  const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
18782
- const user = env.CLAUDE_CONFIG_DIR || path21.join(homeOf(env), ".claude");
19608
+ const user = env.CLAUDE_CONFIG_DIR || path22.join(homeOf(env), ".claude");
18783
19609
  return [
18784
19610
  ...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
18785
- path21.join(user, "settings.json"),
18786
- path21.join(project, ".claude", "settings.json"),
18787
- path21.join(project, ".claude", "settings.local.json")
19611
+ path22.join(user, "settings.json"),
19612
+ path22.join(project, ".claude", "settings.json"),
19613
+ path22.join(project, ".claude", "settings.local.json")
18788
19614
  ];
18789
19615
  }
18790
19616
  var bashPattern = (entry) => {
@@ -18874,10 +19700,10 @@ var opencodeConfig = (value, rules) => {
18874
19700
  function opencodeRules(cwd, env = process.env) {
18875
19701
  const rules = EMPTY2();
18876
19702
  try {
18877
- const config = env.XDG_CONFIG_HOME || path21.join(homeOf(env), ".config");
19703
+ const config = env.XDG_CONFIG_HOME || path22.join(homeOf(env), ".config");
18878
19704
  const files = [
18879
- ...OPENCODE_NAMES.map((name) => path21.join(config, "opencode", name)),
18880
- ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path21.join(dir, name), path21.join(dir, ".opencode", name)])),
19705
+ ...OPENCODE_NAMES.map((name) => path22.join(config, "opencode", name)),
19706
+ ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path22.join(dir, name), path22.join(dir, ".opencode", name)])),
18881
19707
  ...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
18882
19708
  ];
18883
19709
  for (const file of files) {
@@ -18921,9 +19747,9 @@ var codexRule = (body) => {
18921
19747
  function codexRules(cwd, env = process.env) {
18922
19748
  const rules = EMPTY2();
18923
19749
  try {
18924
- const home = env.CODEX_HOME || path21.join(homeOf(env), ".codex");
19750
+ const home = env.CODEX_HOME || path22.join(homeOf(env), ".codex");
18925
19751
  const top = gitTop(cwd) ?? cwd;
18926
- for (const dir of [path21.join(home, "rules"), path21.join(top, ".codex", "rules")]) {
19752
+ for (const dir of [path22.join(home, "rules"), path22.join(top, ".codex", "rules")]) {
18927
19753
  let files;
18928
19754
  try {
18929
19755
  files = fs16.readdirSync(dir).filter((name) => name.endsWith(".rules"));
@@ -18933,7 +19759,7 @@ function codexRules(cwd, env = process.env) {
18933
19759
  return "unreadable";
18934
19760
  }
18935
19761
  for (const name of files) {
18936
- const text = readText2(path21.join(dir, name)) ?? "";
19762
+ const text = readText2(path22.join(dir, name)) ?? "";
18937
19763
  for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
18938
19764
  const rule = codexRule(part);
18939
19765
  if (!rule)
@@ -18952,8 +19778,8 @@ function cursorRules(cwd, env = process.env) {
18952
19778
  try {
18953
19779
  const top = gitTop(cwd) ?? cwd;
18954
19780
  for (const file of [
18955
- path21.join(homeOf(env), ".cursor", "cli-config.json"),
18956
- path21.join(top, ".cursor", "cli.json")
19781
+ path22.join(homeOf(env), ".cursor", "cli-config.json"),
19782
+ path22.join(top, ".cursor", "cli.json")
18957
19783
  ]) {
18958
19784
  const text = readText2(file);
18959
19785
  if (text === null)
@@ -19029,21 +19855,21 @@ function wrapAllowed(read, words, command, options = {}) {
19029
19855
  // packages/workit-core/src/core/heavy.ts
19030
19856
  import fs17 from "node:fs";
19031
19857
  import os7 from "node:os";
19032
- import path22 from "node:path";
19858
+ import path23 from "node:path";
19033
19859
  function stateDir(env, platform = process.platform) {
19034
19860
  if (env.WORKFLOW_TOOLKIT_STATE)
19035
19861
  return env.WORKFLOW_TOOLKIT_STATE;
19036
19862
  if (env.XDG_STATE_HOME)
19037
- return path22.join(env.XDG_STATE_HOME, "workit");
19863
+ return path23.join(env.XDG_STATE_HOME, "workit");
19038
19864
  const home = env.HOME || os7.homedir();
19039
19865
  if (platform === "darwin")
19040
- return path22.join(home, "Library", "Application Support", "workit");
19866
+ return path23.join(home, "Library", "Application Support", "workit");
19041
19867
  if (platform === "win32")
19042
- return path22.join(env.LOCALAPPDATA ?? path22.join(home, "AppData", "Local"), "workit");
19043
- return path22.join(home, ".local", "state", "workit");
19868
+ return path23.join(env.LOCALAPPDATA ?? path23.join(home, "AppData", "Local"), "workit");
19869
+ return path23.join(home, ".local", "state", "workit");
19044
19870
  }
19045
19871
  var HEAVY_COMMANDS_FILE = "heavy-commands.json";
19046
- var heavyCommandsFile = (env) => path22.join(stateDir(env), HEAVY_COMMANDS_FILE);
19872
+ var heavyCommandsFile = (env) => path23.join(stateDir(env), HEAVY_COMMANDS_FILE);
19047
19873
  var HEAVY_SCRIPT = /^(?:test|tests|t|tst|unit|integration|e2e|coverage|cov|lint|eslint|typecheck|type-check|types|tsc|check|verify|validate|build|compile|bundle|ci|sonar|cypress|playwright|jest|vitest|mocha|bench|benchmark|all)(?:$|[:_.-])/iu;
19048
19874
  var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
19049
19875
  var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
@@ -19318,7 +20144,7 @@ function machinePatterns(env) {
19318
20144
  return isRecord4(value) ? stringList(value.patterns) : [];
19319
20145
  }
19320
20146
  function repoPatterns(cwd) {
19321
- return repoHeavyCommands(gitTop(cwd) ?? path22.resolve(cwd));
20147
+ return repoHeavyCommands(gitTop(cwd) ?? path23.resolve(cwd));
19322
20148
  }
19323
20149
  function heavyLines(lines, options) {
19324
20150
  const found = [];
@@ -19396,12 +20222,12 @@ function heavyPlan(command, options) {
19396
20222
  }
19397
20223
  function workitOnPath(env = process.env) {
19398
20224
  const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
19399
- for (const dir of (env.PATH ?? "").split(path22.delimiter))
20225
+ for (const dir of (env.PATH ?? "").split(path23.delimiter))
19400
20226
  for (const name of names) {
19401
20227
  if (!dir)
19402
20228
  continue;
19403
20229
  try {
19404
- fs17.accessSync(path22.join(dir, name), fs17.constants.X_OK);
20230
+ fs17.accessSync(path23.join(dir, name), fs17.constants.X_OK);
19405
20231
  return true;
19406
20232
  } catch {}
19407
20233
  }
@@ -19461,16 +20287,16 @@ var splitDenial = (plan) => ({
19461
20287
  unblock: plan.unblock
19462
20288
  });
19463
20289
  // packages/workit-core/src/hooks/write-gate.ts
19464
- import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
20290
+ import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
19465
20291
  import os9 from "node:os";
19466
- import path24 from "node:path";
20292
+ import path25 from "node:path";
19467
20293
 
19468
20294
  // packages/workit-core/src/core/live-session.ts
19469
20295
  import { spawnSync as spawnSync12 } from "node:child_process";
19470
20296
  import { createHash as createHash15 } from "node:crypto";
19471
20297
  import fs18 from "node:fs";
19472
20298
  import os8 from "node:os";
19473
- import path23 from "node:path";
20299
+ import path24 from "node:path";
19474
20300
  var LIVE_SESSION_TTL_MS = 15 * 60000;
19475
20301
  var HEARTBEAT_EVERY_MS = 30000;
19476
20302
  var liveProbe = {
@@ -19544,15 +20370,15 @@ var liveOwner = (input, transport, env = process.env) => {
19544
20370
  };
19545
20371
  };
19546
20372
  var checkoutOf = (target) => {
19547
- let dir = path23.resolve(target);
19548
- while (!fs18.existsSync(dir) && path23.dirname(dir) !== dir)
19549
- dir = path23.dirname(dir);
20373
+ let dir = path24.resolve(target);
20374
+ while (!fs18.existsSync(dir) && path24.dirname(dir) !== dir)
20375
+ dir = path24.dirname(dir);
19550
20376
  if (fs18.existsSync(dir) && !fs18.statSync(dir).isDirectory())
19551
- dir = path23.dirname(dir);
19552
- const parts = dir.split(path23.sep);
20377
+ dir = path24.dirname(dir);
20378
+ const parts = dir.split(path24.sep);
19553
20379
  const dotGit = parts.lastIndexOf(".git");
19554
20380
  if (dotGit > 0)
19555
- dir = parts.slice(0, dotGit).join(path23.sep) || path23.sep;
20381
+ dir = parts.slice(0, dotGit).join(path24.sep) || path24.sep;
19556
20382
  const location = resolveStore(dir);
19557
20383
  if (location instanceof Error || !location.shared || !location.top)
19558
20384
  return null;
@@ -19562,9 +20388,9 @@ var checkoutOf = (target) => {
19562
20388
  } catch {}
19563
20389
  return top;
19564
20390
  };
19565
- var markerFile2 = (top) => path23.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
20391
+ var markerFile2 = (top) => path24.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
19566
20392
  var checkoutKey = (top, platform = process.platform) => {
19567
- let key = path23.resolve(top);
20393
+ let key = path24.resolve(top);
19568
20394
  try {
19569
20395
  key = fs18.realpathSync.native(key);
19570
20396
  } catch {}
@@ -19659,7 +20485,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
19659
20485
  checkout,
19660
20486
  heartbeatAt: now.toISOString()
19661
20487
  };
19662
- fs18.mkdirSync(path23.dirname(file), { recursive: true });
20488
+ fs18.mkdirSync(path24.dirname(file), { recursive: true });
19663
20489
  const temp = `${file}.${process.pid}.tmp`;
19664
20490
  fs18.writeFileSync(temp, JSON.stringify(marker));
19665
20491
  fs18.renameSync(temp, file);
@@ -19671,22 +20497,22 @@ var NONE3 = { kind: "none" };
19671
20497
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
19672
20498
  var canonical2 = (file) => {
19673
20499
  let rest = "";
19674
- for (let dir = path24.resolve(file);; dir = path24.dirname(dir)) {
20500
+ for (let dir = path25.resolve(file);; dir = path25.dirname(dir)) {
19675
20501
  try {
19676
- return path24.join(realpathSync5.native(dir), rest);
20502
+ return path25.join(realpathSync5.native(dir), rest);
19677
20503
  } catch {
19678
- if (path24.dirname(dir) === dir)
19679
- return path24.resolve(file);
19680
- rest = rest ? path24.join(path24.basename(dir), rest) : path24.basename(dir);
20504
+ if (path25.dirname(dir) === dir)
20505
+ return path25.resolve(file);
20506
+ rest = rest ? path25.join(path25.basename(dir), rest) : path25.basename(dir);
19681
20507
  }
19682
20508
  }
19683
20509
  };
19684
20510
  var exempt = (cwd, root, file, plans) => {
19685
20511
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
19686
- const relative = path24.relative(fold(canonical2(root)), fold(canonical2(path24.resolve(cwd, file))));
19687
- if (relative.startsWith("..") || path24.isAbsolute(relative))
20512
+ const relative = path25.relative(fold(canonical2(root)), fold(canonical2(path25.resolve(cwd, file))));
20513
+ if (relative.startsWith("..") || path25.isAbsolute(relative))
19688
20514
  return true;
19689
- const posix = relative.split(path24.sep).join("/");
20515
+ const posix = relative.split(path25.sep).join("/");
19690
20516
  const segments = posix.split("/");
19691
20517
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
19692
20518
  };
@@ -19863,10 +20689,10 @@ var linkSources = (args, at) => {
19863
20689
  const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
19864
20690
  const target = dest === undefined ? null : at(dest);
19865
20691
  const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
19866
- const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync7(target) || /[\\/]$/.test(dest ?? "") ? target : path24.dirname(target);
20692
+ const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync8(target) || /[\\/]$/.test(dest ?? "") ? target : path25.dirname(target);
19867
20693
  return sources.flatMap((source) => {
19868
- if (symbolic && base !== null && !path24.isAbsolute(homeExpanded(source)))
19869
- return [path24.resolve(base, source)];
20694
+ if (symbolic && base !== null && !path25.isAbsolute(homeExpanded(source)))
20695
+ return [path25.resolve(base, source)];
19870
20696
  const resolved = at(source);
19871
20697
  return resolved ? [resolved] : [];
19872
20698
  });
@@ -19895,7 +20721,7 @@ var shellWrites = (command, depth = 0) => {
19895
20721
  return { writes: true, targets: unknown ? null : targets };
19896
20722
  };
19897
20723
  var cache2 = new Map;
19898
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path24.resolve(root, file)) ?? "-").join("\x00");
20724
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path25.resolve(root, file)) ?? "-").join("\x00");
19899
20725
  var gateFor = (cwd) => {
19900
20726
  const store = new TaskStore(cwd);
19901
20727
  const listed = store.listTaskIndex();
@@ -19974,13 +20800,13 @@ var gitMutationDirs = (cwd, command, dialect) => {
19974
20800
  return [];
19975
20801
  if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
19976
20802
  return [];
19977
- return [path24.resolve(cwd, invocation.dir ?? ".")];
20803
+ return [path25.resolve(cwd, invocation.dir ?? ".")];
19978
20804
  });
19979
20805
  };
19980
20806
  var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
19981
20807
  var PUSHD2 = new Set(["pushd", "push-location"]);
19982
20808
  var POPD2 = new Set(["popd", "pop-location"]);
19983
- var UNKNOWN_DIR = path24.resolve(path24.parse(process.cwd()).root, ".workit-unknown-dir");
20809
+ var UNKNOWN_DIR = path25.resolve(path25.parse(process.cwd()).root, ".workit-unknown-dir");
19984
20810
  var cdInto = (dir, words, strict = false) => {
19985
20811
  const lost = strict ? UNKNOWN_DIR : undefined;
19986
20812
  const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
@@ -19989,7 +20815,7 @@ var cdInto = (dir, words, strict = false) => {
19989
20815
  return lost;
19990
20816
  if (dir === undefined)
19991
20817
  return lost;
19992
- return target === undefined ? os9.homedir() : path24.resolve(dir, target);
20818
+ return target === undefined ? os9.homedir() : path25.resolve(dir, target);
19993
20819
  };
19994
20820
  var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
19995
20821
  var homeExpanded = (file) => {
@@ -20003,7 +20829,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
20003
20829
  const groups = [];
20004
20830
  const at = (file) => {
20005
20831
  const home = homeExpanded(file);
20006
- return path24.isAbsolute(home) ? home : dir === undefined ? null : path24.resolve(dir, home);
20832
+ return path25.isAbsolute(home) ? home : dir === undefined ? null : path25.resolve(dir, home);
20007
20833
  };
20008
20834
  for (const segment of segmentsOf(command, dialect)) {
20009
20835
  if (segment.group === "open") {
@@ -20043,7 +20869,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
20043
20869
  for (const file of opaque ?? []) {
20044
20870
  const resolved = at(file);
20045
20871
  if (resolved)
20046
- targets.push(file === "." ? path24.join(resolved, DIR_TARGET) : resolved);
20872
+ targets.push(file === "." ? path25.join(resolved, DIR_TARGET) : resolved);
20047
20873
  }
20048
20874
  const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
20049
20875
  if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
@@ -20058,7 +20884,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
20058
20884
  const edits = editsOf(segment.words);
20059
20885
  if (edits === null) {
20060
20886
  if (dir !== undefined)
20061
- targets.push(path24.join(dir, DIR_TARGET));
20887
+ targets.push(path25.join(dir, DIR_TARGET));
20062
20888
  } else
20063
20889
  for (const file of edits) {
20064
20890
  const resolved = at(file);
@@ -20069,20 +20895,20 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
20069
20895
  for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
20070
20896
  const { at: runsIn, args } = workitCwd(dir, invocation);
20071
20897
  if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
20072
- targets.push(path24.join(runsIn, DIR_TARGET));
20898
+ targets.push(path25.join(runsIn, DIR_TARGET));
20073
20899
  }
20074
20900
  }
20075
20901
  return targets;
20076
20902
  };
20077
20903
  var workitCwd = (dir, args) => {
20078
20904
  const { rest, cwd } = skipWorkitGlobals(args);
20079
- return { at: cwd === null ? dir : path24.resolve(dir, homeExpanded(cwd)), args: rest };
20905
+ return { at: cwd === null ? dir : path25.resolve(dir, homeExpanded(cwd)), args: rest };
20080
20906
  };
20081
20907
  var shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
20082
20908
  try {
20083
20909
  return [
20084
20910
  ...segmentTargets(cwd, command, dialect, 0, strict),
20085
- ...gitMutationDirs(cwd, command, dialect).map((dir) => path24.join(dir, DIR_TARGET))
20911
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path25.join(dir, DIR_TARGET))
20086
20912
  ];
20087
20913
  } catch {
20088
20914
  return [];
@@ -20119,7 +20945,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
20119
20945
  var guarded = (targets) => {
20120
20946
  const seen = new Set;
20121
20947
  return targets.filter((target) => {
20122
- const dir = path24.dirname(target);
20948
+ const dir = path25.dirname(target);
20123
20949
  if (seen.has(dir))
20124
20950
  return false;
20125
20951
  seen.add(dir);
@@ -20140,7 +20966,7 @@ var claimAll = (owner, targets, deps) => {
20140
20966
  for (const target of guarded(targets))
20141
20967
  claimLiveSession(target, owner, deps);
20142
20968
  };
20143
- var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path24.resolve(cwd, file)) : [path24.join(cwd, DIR_TARGET)];
20969
+ var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path25.resolve(cwd, file)) : [path25.join(cwd, DIR_TARGET)];
20144
20970
  var issuedTarget = (cwd, targets) => {
20145
20971
  const state = resolveStateDir();
20146
20972
  const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
@@ -20148,7 +20974,7 @@ var issuedTarget = (cwd, targets) => {
20148
20974
  };
20149
20975
  var underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
20150
20976
  const win = platform === "win32";
20151
- const p = win ? path24.win32 : path24.posix;
20977
+ const p = win ? path25.win32 : path25.posix;
20152
20978
  const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
20153
20979
  const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
20154
20980
  const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
@@ -20239,6 +21065,24 @@ function beforeWriteGate(cwd, paths) {
20239
21065
  }
20240
21066
  // packages/workit-core/src/hooks/stop.ts
20241
21067
  import { spawnSync as spawnSync13 } from "node:child_process";
21068
+
21069
+ // packages/workit-core/src/core/policy/current-judgment.ts
21070
+ function currentJudgment(cwd) {
21071
+ try {
21072
+ const store = new TaskStore(cwd);
21073
+ const listed = store.listTaskIndex();
21074
+ const key = store.currentKey();
21075
+ if (!listed.ok || !key.ok)
21076
+ return null;
21077
+ const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
21078
+ const task = entry ? store.readTask(entry.id) : null;
21079
+ return task?.ok === true ? latestJudgment(task.data) : null;
21080
+ } catch {
21081
+ return null;
21082
+ }
21083
+ }
21084
+
21085
+ // packages/workit-core/src/hooks/stop.ts
20242
21086
  var NONE4 = { kind: "none" };
20243
21087
  var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
20244
21088
  var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
@@ -20331,29 +21175,32 @@ var redChecks = (rows, pr, head, endpoint) => {
20331
21175
  reason: `PR #${pr}'s checks were last seen ${state} at ${short(head)}, and this workspace's endpoint is \`${endpoint}\`: run \`workit ci wait --pr ${pr}\`${state === "failing" ? " after fixing the failures `workit pr status` lists (then push)" : ""}.`
20332
21176
  };
20333
21177
  };
20334
- var taskJudgment = (cwd) => {
20335
- try {
20336
- const store = new TaskStore(cwd);
20337
- const listed = store.listTaskIndex();
20338
- const key = store.currentKey();
20339
- if (!listed.ok || !key.ok)
20340
- return null;
20341
- const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
20342
- const task = entry ? store.readTask(entry.id) : null;
20343
- return task?.ok === true ? latestJudgment(task.data) : null;
20344
- } catch {
20345
- return null;
20346
- }
21178
+ var prBase = (rows) => {
21179
+ const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
21180
+ return typeof seen?.base === "string" ? seen.base : null;
20347
21181
  };
20348
- var unverified = (cwd, branch, all, descriptor, verification) => {
21182
+ var unverified = (cwd, branch, all, descriptor, verification, head) => {
20349
21183
  const check = checkVerdicts(cwd, branch, all);
20350
21184
  if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
20351
21185
  return null;
20352
- const judgment = taskJudgment(cwd);
21186
+ const judgment = currentJudgment(cwd);
20353
21187
  const high = judgment?.riskTier === "high";
20354
- if (judgment && !judgment.behaviorChange && !high)
20355
- return null;
20356
- const independent = verification === "independent" || high;
21188
+ let independent;
21189
+ if (verification === "proportional") {
21190
+ const lane = branchLane(cwd, {
21191
+ base: prBase(all.filter((row) => row.branch === branch)),
21192
+ head,
21193
+ judgment
21194
+ });
21195
+ const need = laneNeed(lane.lane);
21196
+ if (need === "none")
21197
+ return null;
21198
+ independent = need === "non-author";
21199
+ } else {
21200
+ if (judgment && !judgment.behaviorChange && !high)
21201
+ return null;
21202
+ independent = verification === "independent" || high;
21203
+ }
20357
21204
  if (!independent && check.review === "self-reviewed")
20358
21205
  return null;
20359
21206
  const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
@@ -20400,7 +21247,7 @@ function stopObligation(input, descriptor) {
20400
21247
  }
20401
21248
  if (!delivered || endpoint !== "green" && endpoint !== "merged")
20402
21249
  return null;
20403
- const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
21250
+ const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
20404
21251
  return left ? { ...left, head } : null;
20405
21252
  } catch {
20406
21253
  return null;
@@ -20661,13 +21508,13 @@ ${line}` };
20661
21508
  return decision;
20662
21509
  };
20663
21510
  // packages/workit-core/src/hooks/hosts/fields.ts
20664
- import { existsSync as existsSync8, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
20665
- import path25 from "node:path";
21511
+ import { existsSync as existsSync9, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
21512
+ import path26 from "node:path";
20666
21513
  var isRecord5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
20667
21514
  var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
20668
21515
  var optionalText = (value) => typeof value === "string" ? value : null;
20669
21516
  var existingDirectory = (value) => {
20670
- if (!nonEmpty2(value) || !path25.isAbsolute(value) || !existsSync8(value))
21517
+ if (!nonEmpty2(value) || !path26.isAbsolute(value) || !existsSync9(value))
20671
21518
  return null;
20672
21519
  try {
20673
21520
  return statSync2(value).isDirectory() ? realpathSync6(value) : null;
@@ -20735,9 +21582,9 @@ var writeTargets = (input) => {
20735
21582
  };
20736
21583
  // packages/workit-core/src/hooks/standing.ts
20737
21584
  import fs19 from "node:fs";
20738
- import path26 from "node:path";
21585
+ import path27 from "node:path";
20739
21586
  var readPlans = (store) => {
20740
- const dir = path26.join(store, "fanouts");
21587
+ const dir = path27.join(store, "fanouts");
20741
21588
  let names;
20742
21589
  try {
20743
21590
  names = fs19.readdirSync(dir).filter((name) => name.endsWith(".json"));
@@ -20747,7 +21594,7 @@ var readPlans = (store) => {
20747
21594
  const plans = [];
20748
21595
  for (const name of names) {
20749
21596
  try {
20750
- const raw = JSON.parse(fs19.readFileSync(path26.join(dir, name), "utf8"));
21597
+ const raw = JSON.parse(fs19.readFileSync(path27.join(dir, name), "utf8"));
20751
21598
  if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
20752
21599
  continue;
20753
21600
  const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
@@ -20873,40 +21720,40 @@ var cwdRewrite = (command) => {
20873
21720
  // packages/workit-core/src/hooks/worktree-confine.ts
20874
21721
  import { createHash as createHash16 } from "node:crypto";
20875
21722
  import fs20 from "node:fs";
20876
- import path27 from "node:path";
21723
+ import path28 from "node:path";
20877
21724
  var FANOUT_TOKEN_FILE = "workit-fanout-token";
20878
21725
  var NONE5 = { kind: "none" };
20879
21726
  var CASE_INSENSITIVE2 = process.platform === "darwin" || process.platform === "win32";
20880
21727
  var canonical3 = (file) => {
20881
21728
  let rest = "";
20882
- for (let dir = path27.resolve(file);; dir = path27.dirname(dir)) {
21729
+ for (let dir = path28.resolve(file);; dir = path28.dirname(dir)) {
20883
21730
  try {
20884
- return path27.join(fs20.realpathSync.native(dir), rest);
21731
+ return path28.join(fs20.realpathSync.native(dir), rest);
20885
21732
  } catch {
20886
- if (path27.dirname(dir) === dir)
20887
- return path27.resolve(file);
20888
- rest = rest ? path27.join(path27.basename(dir), rest) : path27.basename(dir);
21733
+ if (path28.dirname(dir) === dir)
21734
+ return path28.resolve(file);
21735
+ rest = rest ? path28.join(path28.basename(dir), rest) : path28.basename(dir);
20889
21736
  }
20890
21737
  }
20891
21738
  };
20892
21739
  var within = (child, parent) => {
20893
21740
  const fold = (value) => CASE_INSENSITIVE2 ? value.toLowerCase() : value;
20894
- const relative = path27.relative(fold(parent), fold(child));
20895
- return relative === "" || !relative.startsWith("..") && !path27.isAbsolute(relative);
21741
+ const relative = path28.relative(fold(parent), fold(child));
21742
+ return relative === "" || !relative.startsWith("..") && !path28.isAbsolute(relative);
20896
21743
  };
20897
21744
  var checkoutOf2 = (file) => {
20898
- for (let dir = canonical3(file);; dir = path27.dirname(dir)) {
20899
- const dotGit = path27.join(dir, ".git");
21745
+ for (let dir = canonical3(file);; dir = path28.dirname(dir)) {
21746
+ const dotGit = path28.join(dir, ".git");
20900
21747
  if (fs20.existsSync(dotGit)) {
20901
21748
  if (!fs20.statSync(dotGit).isFile())
20902
21749
  return { root: dir, workit: false };
20903
21750
  const admin = /^gitdir:\s*(.+)$/mu.exec(fs20.readFileSync(dotGit, "utf8"))?.[1]?.trim();
20904
21751
  return {
20905
21752
  root: dir,
20906
- workit: Boolean(admin) && fs20.existsSync(path27.join(path27.resolve(dir, admin), FANOUT_TOKEN_FILE))
21753
+ workit: Boolean(admin) && fs20.existsSync(path28.join(path28.resolve(dir, admin), FANOUT_TOKEN_FILE))
20907
21754
  };
20908
21755
  }
20909
- if (path27.dirname(dir) === dir)
21756
+ if (path28.dirname(dir) === dir)
20910
21757
  return null;
20911
21758
  }
20912
21759
  };
@@ -20918,8 +21765,8 @@ var workitWorktreeOf = (file) => {
20918
21765
  return null;
20919
21766
  }
20920
21767
  };
20921
- var bindingDir = () => path27.join(resolveStateDir(), "worktree-bind");
20922
- var bindingFile = (session, agentId) => path27.join(bindingDir(), `${createHash16("sha256").update(`${session}\x00${agentId}`).digest("hex").slice(0, 32)}.json`);
21768
+ var bindingDir = () => path28.join(resolveStateDir(), "worktree-bind");
21769
+ var bindingFile = (session, agentId) => path28.join(bindingDir(), `${createHash16("sha256").update(`${session}\x00${agentId}`).digest("hex").slice(0, 32)}.json`);
20923
21770
  var readBinding = (file) => {
20924
21771
  try {
20925
21772
  const value = JSON.parse(fs20.readFileSync(file, "utf8"));
@@ -20944,7 +21791,7 @@ var bind = (input, worktree) => {
20944
21791
  agentId,
20945
21792
  at: new Date().toISOString()
20946
21793
  };
20947
- fs20.mkdirSync(path27.dirname(file), { recursive: true });
21794
+ fs20.mkdirSync(path28.dirname(file), { recursive: true });
20948
21795
  fs20.writeFileSync(file, JSON.stringify(binding));
20949
21796
  try {
20950
21797
  if (inWorkitWorkspace(input.cwd))
@@ -20963,25 +21810,25 @@ var hostIsolated = (cwd, agentId) => {
20963
21810
  const checkout = checkoutOf2(cwd);
20964
21811
  if (!checkout)
20965
21812
  return false;
20966
- const dotGit = path27.join(checkout.root, ".git");
21813
+ const dotGit = path28.join(checkout.root, ".git");
20967
21814
  if (!fs20.statSync(dotGit).isFile())
20968
21815
  return false;
20969
21816
  const admin = /^gitdir:\s*(.+)$/mu.exec(fs20.readFileSync(dotGit, "utf8"))?.[1]?.trim();
20970
21817
  if (!admin)
20971
21818
  return false;
20972
- const adminDir = path27.resolve(checkout.root, admin);
20973
- const back = fs20.readFileSync(path27.join(adminDir, "gitdir"), "utf8").trim();
20974
- if (path27.relative(canonical3(path27.resolve(adminDir, back)), canonical3(dotGit)) !== "")
21819
+ const adminDir = path28.resolve(checkout.root, admin);
21820
+ const back = fs20.readFileSync(path28.join(adminDir, "gitdir"), "utf8").trim();
21821
+ if (path28.relative(canonical3(path28.resolve(adminDir, back)), canonical3(dotGit)) !== "")
20975
21822
  return false;
20976
- const common = path27.join(adminDir, "commondir");
20977
- const gitDir = fs20.existsSync(common) ? path27.resolve(adminDir, fs20.readFileSync(common, "utf8").trim()) : adminDir;
20978
- const expected = path27.join(path27.dirname(canonical3(gitDir)), ".claude", "worktrees", `agent-${agentId}`);
20979
- return path27.relative(canonical3(expected), checkout.root) === "";
21823
+ const common = path28.join(adminDir, "commondir");
21824
+ const gitDir = fs20.existsSync(common) ? path28.resolve(adminDir, fs20.readFileSync(common, "utf8").trim()) : adminDir;
21825
+ const expected = path28.join(path28.dirname(canonical3(gitDir)), ".claude", "worktrees", `agent-${agentId}`);
21826
+ return path28.relative(canonical3(expected), checkout.root) === "";
20980
21827
  } catch {
20981
21828
  return false;
20982
21829
  }
20983
21830
  };
20984
- var shown = (target) => path27.basename(target) === ".workit-live-target" ? path27.dirname(target) : target;
21831
+ var shown = (target) => path28.basename(target) === ".workit-live-target" ? path28.dirname(target) : target;
20985
21832
  var unboundDeny = (input, target) => {
20986
21833
  const unblock = "Work only in the worktree your brief names, which the lead made with `workit fanout worktree create <slice>`: edit files under it and run one verb per call against it (`workit --cwd <worktree> git commit -m … -- <paths>`, `workit --cwd <worktree> check <name>`). If your brief names no worktree, stop and report that to the lead.";
20987
21834
  return {
@@ -21011,7 +21858,7 @@ function confineImplementer(input, targets) {
21011
21858
  try {
21012
21859
  if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
21013
21860
  return NONE5;
21014
- if (targets.some((target) => within(path27.resolve(target), UNKNOWN_DIR)))
21861
+ if (targets.some((target) => within(path28.resolve(target), UNKNOWN_DIR)))
21015
21862
  return unknownDirDeny(input, boundWorktree(input));
21016
21863
  const bindings = canonical3(bindingDir());
21017
21864
  const judged = targets.map((named) => ({ named, real: canonical3(named) })).filter(({ real }) => within(real, bindings) || checkoutOf2(real) !== null);
@@ -21331,7 +22178,7 @@ var dispatchHook = (adapter, raw, env = process.env) => {
21331
22178
  };
21332
22179
  // packages/workit-core/src/hooks/deadline.ts
21333
22180
  import { appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
21334
- import path28 from "node:path";
22181
+ import path29 from "node:path";
21335
22182
  var HOOK_SKIP_TEXT = "workit context skipped: host under load";
21336
22183
  var HOOK_DEADLINE_MS = 8000;
21337
22184
  var HOST_DEADLINES = {
@@ -21349,7 +22196,7 @@ var hookDeadlineMs = (env, host = "", native) => {
21349
22196
  const row = HOST_DEADLINES[host];
21350
22197
  return override(env) ?? (row ? native === undefined ? Math.min(...Object.values(row)) : row[native] ?? row[""] : HOOK_DEADLINE_MS);
21351
22198
  };
21352
- var skipsFile = (stateDir) => path28.join(stateDir, HOOK_SKIPS_FILE);
22199
+ var skipsFile = (stateDir) => path29.join(stateDir, HOOK_SKIPS_FILE);
21353
22200
  var recordHookSkip = (skip, stateDir = resolveStateDir()) => {
21354
22201
  try {
21355
22202
  mkdirSync3(stateDir, { recursive: true });
@@ -21994,7 +22841,7 @@ var codexAdapter = {
21994
22841
 
21995
22842
  // packages/workit-core/src/hooks/hosts/cursor.ts
21996
22843
  import fs21 from "node:fs";
21997
- import path29 from "node:path";
22844
+ import path30 from "node:path";
21998
22845
  var undocumented = { support: "undocumented", native: null };
21999
22846
  var none = { support: "none", native: null };
22000
22847
  var CURSOR_DESCRIPTOR = {
@@ -22191,7 +23038,7 @@ var protocolEvent2 = (input) => {
22191
23038
  };
22192
23039
  var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
22193
23040
  var lastAssistantText = (file) => {
22194
- if (!file || !path29.isAbsolute(file))
23041
+ if (!file || !path30.isAbsolute(file))
22195
23042
  return null;
22196
23043
  try {
22197
23044
  const size = fs21.statSync(file).size;
@@ -22228,7 +23075,7 @@ var lastAssistantText = (file) => {
22228
23075
  return null;
22229
23076
  }
22230
23077
  };
22231
- var within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path29.sep) ? root : `${root}${path29.sep}`);
23078
+ var within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path30.sep) ? root : `${root}${path30.sep}`);
22232
23079
  var hookCwd = (input) => {
22233
23080
  const cwd = existingDirectory(input.cwd);
22234
23081
  if (cwd && input.hook_event_name === "beforeShellExecution")