@brainervirus/workit-cursor 8.23.0 → 8.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6739,8 +6739,8 @@ function requirementId(input) {
6739
6739
  import { realpathSync as realpathSync4 } from "node:fs";
6740
6740
 
6741
6741
  // packages/workit-core/src/core/policy/derive.ts
6742
- import { existsSync as existsSync2 } from "node:fs";
6743
- import path10 from "node:path";
6742
+ import { existsSync as existsSync3 } from "node:fs";
6743
+ import path11 from "node:path";
6744
6744
 
6745
6745
  // packages/workit-core/src/git/rev.ts
6746
6746
  import { spawnSync } from "node:child_process";
@@ -6903,6 +6903,144 @@ function resolveRef(cwd, ref) {
6903
6903
  return null;
6904
6904
  return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
6905
6905
  }
6906
+ function originHeadRef(cwd) {
6907
+ return line(git(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]));
6908
+ }
6909
+ function ownChangeDrift(cwd, range) {
6910
+ const before = diffRaw(cwd, range.fromFork, range.from);
6911
+ const after = diffRaw(cwd, range.toFork, range.to);
6912
+ if (!before || !after)
6913
+ return null;
6914
+ const judged = new Map(before.map((entry) => [entry.path, entry]));
6915
+ const current = new Map(after.map((entry) => [entry.path, entry]));
6916
+ const same = (a, b) => a !== undefined && b !== undefined && a.oldMode === b.oldMode && a.oldBlob === b.oldBlob && a.newMode === b.newMode && a.newBlob === b.newBlob;
6917
+ return [...new Set([...judged.keys(), ...current.keys()])].filter((file) => !same(judged.get(file), current.get(file))).toSorted();
6918
+ }
6919
+ function sameOwnChange(cwd, range) {
6920
+ const fromFork = mergeBase(cwd, range.fromBase, range.from);
6921
+ const toFork = mergeBase(cwd, range.toBase, range.to);
6922
+ if (!fromFork || !toFork)
6923
+ return false;
6924
+ return ownChangeDrift(cwd, { from: range.from, to: range.to, fromFork, toFork })?.length === 0;
6925
+ }
6926
+ var LANE_DIFF = [
6927
+ "-c",
6928
+ "core.quotePath=false",
6929
+ "diff",
6930
+ "--no-color",
6931
+ "--no-ext-diff",
6932
+ "--no-textconv",
6933
+ "--no-renames"
6934
+ ];
6935
+ var laneRange = (fork, head) => {
6936
+ if (!safeArg(fork) || head !== null && !safeArg(head))
6937
+ return null;
6938
+ return head === null ? [fork] : [fork, head];
6939
+ };
6940
+ function diffNumstat(cwd, fork, head) {
6941
+ const range = laneRange(fork, head);
6942
+ if (!range)
6943
+ return null;
6944
+ const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
6945
+ if (!run.ok)
6946
+ return null;
6947
+ const out = [];
6948
+ for (const record of run.stdout.split("\x00")) {
6949
+ const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
6950
+ if (!match)
6951
+ continue;
6952
+ const binary = match[1] === "-";
6953
+ out.push({
6954
+ path: match[3],
6955
+ added: binary ? 0 : Number(match[1]),
6956
+ deleted: binary ? 0 : Number(match[2]),
6957
+ binary
6958
+ });
6959
+ }
6960
+ return out;
6961
+ }
6962
+ function diffRaw(cwd, fork, head) {
6963
+ const range = laneRange(fork, head);
6964
+ if (!range)
6965
+ return null;
6966
+ const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
6967
+ if (!run.ok)
6968
+ return null;
6969
+ const fields = run.stdout.split("\x00");
6970
+ const out = [];
6971
+ for (let index = 0;index + 1 < fields.length; index += 2) {
6972
+ const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
6973
+ if (!meta)
6974
+ return null;
6975
+ out.push({
6976
+ path: fields[index + 1],
6977
+ oldMode: meta[1],
6978
+ newMode: meta[2],
6979
+ oldBlob: meta[3],
6980
+ newBlob: meta[4]
6981
+ });
6982
+ }
6983
+ return out;
6984
+ }
6985
+ function readBlobs(cwd, ids) {
6986
+ const out = new Map;
6987
+ const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
6988
+ if (!wanted.length)
6989
+ return out;
6990
+ const run = spawnSync("git", ["cat-file", "--batch"], {
6991
+ cwd,
6992
+ input: `${wanted.join(`
6993
+ `)}
6994
+ `,
6995
+ maxBuffer: MAX_BUFFER,
6996
+ timeout: GIT_TIMEOUTS.local,
6997
+ killSignal: "SIGKILL",
6998
+ windowsHide: true
6999
+ });
7000
+ if (run.status !== 0 || !run.stdout)
7001
+ return null;
7002
+ const bytes = run.stdout;
7003
+ let at = 0;
7004
+ for (const id of wanted) {
7005
+ const end = bytes.indexOf(10, at);
7006
+ if (end < 0)
7007
+ return null;
7008
+ const header = bytes.subarray(at, end).toString("utf8").split(" ");
7009
+ at = end + 1;
7010
+ if (header[1] === "missing")
7011
+ continue;
7012
+ const size = Number(header[2]);
7013
+ if (header[0] !== id || !Number.isSafeInteger(size))
7014
+ return null;
7015
+ out.set(id, bytes.subarray(at, at + size));
7016
+ at += size + 1;
7017
+ }
7018
+ return out;
7019
+ }
7020
+ function treeFilesNamed(cwd, rev, name) {
7021
+ if (!safeArg(rev))
7022
+ return null;
7023
+ const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
7024
+ if (!run.ok)
7025
+ return null;
7026
+ const out = new Map;
7027
+ for (const record of run.stdout.split("\x00")) {
7028
+ const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
7029
+ if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
7030
+ out.set(match[2], match[1]);
7031
+ }
7032
+ return out;
7033
+ }
7034
+ function fileAt(cwd, rev, file) {
7035
+ if (!safeArg(rev))
7036
+ return null;
7037
+ const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
7038
+ return run.ok ? run.stdout : null;
7039
+ }
7040
+ function untrackedFiles(cwd) {
7041
+ const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
7042
+ return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
7043
+ }
6906
7044
  var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
6907
7045
  function currentBranch(cwd) {
6908
7046
  return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
@@ -6927,7 +7065,7 @@ function pushRemoteName(cwd, branch) {
6927
7065
  import { spawnSync as spawnSync8 } from "node:child_process";
6928
7066
  import { createHash as createHash9, randomBytes as randomBytes2 } from "node:crypto";
6929
7067
  import fs7 from "node:fs";
6930
- import path9 from "node:path";
7068
+ import path10 from "node:path";
6931
7069
 
6932
7070
  // packages/workit-core/src/core/store-lock.ts
6933
7071
  import { spawnSync as spawnSync3 } from "node:child_process";
@@ -7353,6 +7491,10 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
7353
7491
  }
7354
7492
  };
7355
7493
 
7494
+ // packages/workit-core/src/core/policy/lane.ts
7495
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
7496
+ import path6 from "node:path";
7497
+
7356
7498
  // packages/workit-core/src/core/vcs-config.ts
7357
7499
  import fs4 from "node:fs";
7358
7500
  import path5 from "node:path";
@@ -7716,7 +7858,7 @@ var workspaceConfigSchema = object({
7716
7858
  }).passthrough().optional(),
7717
7859
  autoApprove: union([boolean2(), array(nonBlank)]).optional(),
7718
7860
  defaultEndpoint: unknown().optional(),
7719
- verification: _enum(["self", "independent"]).optional(),
7861
+ verification: _enum(["proportional", "self", "independent"]).optional(),
7720
7862
  delegation: unknown().optional(),
7721
7863
  trailers: boolean2().optional(),
7722
7864
  liveSessionGuard: boolean2().optional(),
@@ -8672,10 +8814,747 @@ function vcsConfig(mode, cwd, options = {}) {
8672
8814
  return out;
8673
8815
  }
8674
8816
 
8817
+ // packages/workit-core/src/git/default-base.ts
8818
+ var safeRef = (value) => value.length > 0 && !value.startsWith("-");
8819
+ var trackTrunk = (cwd) => {
8820
+ try {
8821
+ const resolved = vcsConfig("resolve", cwd);
8822
+ if (resolved.ok === false || !resolved.releaseTrack || resolved.releaseTrack.blocking)
8823
+ return null;
8824
+ const target = String(resolved.defaultTargetBranch ?? "").trim();
8825
+ return target && safeRef(target) ? target : null;
8826
+ } catch {
8827
+ return null;
8828
+ }
8829
+ };
8830
+ function defaultBase(cwd) {
8831
+ const trunk = trackTrunk(cwd);
8832
+ if (trunk) {
8833
+ for (const candidate of [`origin/${trunk}`, trunk])
8834
+ if (resolveRef(cwd, candidate))
8835
+ return candidate;
8836
+ }
8837
+ const originHead = originHeadRef(cwd);
8838
+ if (originHead && resolveRef(cwd, originHead))
8839
+ return originHead;
8840
+ for (const candidate of [
8841
+ "origin/main",
8842
+ "origin/master",
8843
+ "main",
8844
+ "master",
8845
+ "origin/develop",
8846
+ "develop"
8847
+ ])
8848
+ if (resolveRef(cwd, candidate))
8849
+ return candidate;
8850
+ return null;
8851
+ }
8852
+
8853
+ // packages/workit-core/src/core/policy/attributes.ts
8854
+ function patternRegex(dir, pattern) {
8855
+ if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
8856
+ return null;
8857
+ const anchored = pattern.includes("/");
8858
+ const body = pattern.replace(/^\//u, "");
8859
+ let source = "";
8860
+ for (let index = 0;index < body.length; index += 1) {
8861
+ const char = body[index];
8862
+ if (char === "*" && body[index + 1] === "*") {
8863
+ const slash = body[index + 2] === "/";
8864
+ source += slash ? "(?:.*/)?" : ".*";
8865
+ index += slash ? 2 : 1;
8866
+ } else if (char === "*")
8867
+ source += "[^/]*";
8868
+ else if (char === "?")
8869
+ source += "[^/]";
8870
+ else if (char === "[") {
8871
+ const end = body.indexOf("]", index + 2);
8872
+ if (end < 0)
8873
+ return null;
8874
+ const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
8875
+ source += `[${set}]`;
8876
+ index = end;
8877
+ } else if (char === "\\" && index + 1 < body.length) {
8878
+ source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
8879
+ index += 1;
8880
+ } else
8881
+ source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
8882
+ }
8883
+ const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
8884
+ try {
8885
+ return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
8886
+ } catch {
8887
+ return null;
8888
+ }
8889
+ }
8890
+ function rulesOf(dir, text) {
8891
+ const out = [];
8892
+ for (const raw of text.split(`
8893
+ `)) {
8894
+ const line = raw.trim();
8895
+ if (!line || line.startsWith("#") || line.startsWith("[attr]"))
8896
+ continue;
8897
+ const [pattern, ...attributes] = line.split(/\s+/u);
8898
+ let generated = null;
8899
+ for (const attribute of attributes) {
8900
+ if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
8901
+ generated = true;
8902
+ else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
8903
+ generated = false;
8904
+ }
8905
+ if (generated === null)
8906
+ continue;
8907
+ const regex = patternRegex(dir, pattern);
8908
+ if (regex)
8909
+ out.push({ regex, generated });
8910
+ }
8911
+ return out;
8912
+ }
8913
+ function generatedByAttributes(files, paths) {
8914
+ const rules = [...files].map(([file, text]) => {
8915
+ const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
8916
+ return {
8917
+ depth: dir ? dir.split("/").length : 0,
8918
+ rules: rulesOf(dir, text)
8919
+ };
8920
+ }).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
8921
+ const out = new Set;
8922
+ for (const file of paths) {
8923
+ const last = rules.findLast((rule) => rule.regex.test(file));
8924
+ if (last?.generated)
8925
+ out.add(file);
8926
+ }
8927
+ return out;
8928
+ }
8929
+
8930
+ // packages/workit-core/src/core/policy/substance.ts
8931
+ var SLASH_BLOCK = [["/*", "*/"]];
8932
+ var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
8933
+ var SYNTAXES = Object.fromEntries([
8934
+ ...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
8935
+ ...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
8936
+ ext,
8937
+ { ...C_LIKE, triple: true }
8938
+ ]),
8939
+ ["go", { ...C_LIKE, rawBacktick: true }],
8940
+ ...["css", "scss", "less"].map((ext) => [
8941
+ ext,
8942
+ { line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
8943
+ ]),
8944
+ ...["py", "pyi"].map((ext) => [
8945
+ ext,
8946
+ { line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
8947
+ ]),
8948
+ ["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
8949
+ ...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
8950
+ ext,
8951
+ { line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
8952
+ ])
8953
+ ]);
8954
+ 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;
8955
+ var extensionOf = (file) => {
8956
+ const name = file.slice(file.lastIndexOf("/") + 1);
8957
+ const dot = name.lastIndexOf(".");
8958
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
8959
+ };
8960
+ var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
8961
+ var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
8962
+ var SPACE = /[ \t\r\f\v]/u;
8963
+ var WORD = /[\w$]/u;
8964
+ var NO_SPACE_AFTER = "([{,;";
8965
+ var NO_SPACE_BEFORE = ")]},;";
8966
+ var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
8967
+ var REGEX_KEYWORDS = new Set([
8968
+ "return",
8969
+ "typeof",
8970
+ "instanceof",
8971
+ "case",
8972
+ "do",
8973
+ "else",
8974
+ "in",
8975
+ "of",
8976
+ "new",
8977
+ "delete",
8978
+ "void",
8979
+ "throw",
8980
+ "yield",
8981
+ "await"
8982
+ ]);
8983
+ function substanceOf(file, text) {
8984
+ const syntax = syntaxOf(file);
8985
+ if (!syntax)
8986
+ return plainSubstance(text);
8987
+ const markup = syntax.block.some(([open]) => open === "<!--");
8988
+ if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
8989
+ return plainSubstance(text);
8990
+ return lex(syntax, text);
8991
+ }
8992
+ function plainSubstance(text) {
8993
+ return text.split(`
8994
+ `).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
8995
+ `);
8996
+ }
8997
+ function lex(syntax, text) {
8998
+ const collapse = !syntax.indented;
8999
+ let out = "";
9000
+ let lineStart = 0;
9001
+ let kept = 0;
9002
+ let pendingSpace = false;
9003
+ const frames = [{ kind: "code", braces: 0 }];
9004
+ let index = 0;
9005
+ if (text.startsWith("#!")) {
9006
+ const end = text.indexOf(`
9007
+ `);
9008
+ out = end < 0 ? text : text.slice(0, end);
9009
+ kept = out.length;
9010
+ index = end < 0 ? text.length : end;
9011
+ }
9012
+ const emitCode = (chunk) => {
9013
+ if (collapse && pendingSpace && out.length > lineStart) {
9014
+ const before = out[out.length - 1];
9015
+ if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
9016
+ out += " ";
9017
+ }
9018
+ pendingSpace = false;
9019
+ out += chunk;
9020
+ };
9021
+ const emitVerbatim = (chunk) => {
9022
+ emitCode(chunk);
9023
+ kept = out.length;
9024
+ };
9025
+ const newline = () => {
9026
+ if (!collapse) {
9027
+ let end = out.length;
9028
+ while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
9029
+ end -= 1;
9030
+ out = out.slice(0, end);
9031
+ }
9032
+ pendingSpace = false;
9033
+ if (out.length > lineStart) {
9034
+ out += `
9035
+ `;
9036
+ lineStart = out.length;
9037
+ }
9038
+ };
9039
+ const regexAllowed = () => {
9040
+ let end = out.length;
9041
+ while (end > 0 && /\s/u.test(out[end - 1]))
9042
+ end -= 1;
9043
+ if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
9044
+ return true;
9045
+ let start = end;
9046
+ while (start > 0 && WORD.test(out[start - 1]))
9047
+ start -= 1;
9048
+ return REGEX_KEYWORDS.has(out.slice(start, end));
9049
+ };
9050
+ const scanString = (quote, escapes, multiline) => {
9051
+ const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
9052
+ const fstring = syntax.fstring === true && /f/iu.test(prefix);
9053
+ let end = index + quote.length;
9054
+ while (end < text.length) {
9055
+ const char = text[end];
9056
+ if (escapes && char === "\\") {
9057
+ end += 2;
9058
+ continue;
9059
+ }
9060
+ if (text.startsWith(quote, end)) {
9061
+ if (fstring && unbalanced(text.slice(index + quote.length, end)))
9062
+ return false;
9063
+ emitVerbatim(text.slice(index, end + quote.length));
9064
+ index = end + quote.length;
9065
+ return true;
9066
+ }
9067
+ if (char === `
9068
+ ` && !multiline)
9069
+ return false;
9070
+ end += 1;
9071
+ }
9072
+ return false;
9073
+ };
9074
+ while (index < text.length) {
9075
+ const frame = frames[frames.length - 1];
9076
+ const char = text[index];
9077
+ if (frame.kind === "template") {
9078
+ if (char === "\\") {
9079
+ emitVerbatim(text.slice(index, index + 2));
9080
+ index += 2;
9081
+ } else if (char === "`") {
9082
+ emitVerbatim(char);
9083
+ frames.pop();
9084
+ index += 1;
9085
+ } else if (text.startsWith("${", index)) {
9086
+ emitVerbatim("${");
9087
+ frames.push({ kind: "code", braces: 0 });
9088
+ index += 2;
9089
+ } else {
9090
+ out += char;
9091
+ kept = out.length;
9092
+ index += 1;
9093
+ }
9094
+ continue;
9095
+ }
9096
+ if (char === `
9097
+ `) {
9098
+ newline();
9099
+ index += 1;
9100
+ continue;
9101
+ }
9102
+ if (SPACE.test(char)) {
9103
+ if (collapse)
9104
+ pendingSpace = true;
9105
+ else
9106
+ out += char;
9107
+ index += 1;
9108
+ continue;
9109
+ }
9110
+ const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
9111
+ if (lineOpener) {
9112
+ const end = text.indexOf(`
9113
+ `, index);
9114
+ const comment = text.slice(index, end < 0 ? text.length : end);
9115
+ if (DIRECTIVE.test(comment.slice(lineOpener.length)))
9116
+ emitVerbatim(comment);
9117
+ else
9118
+ pendingSpace = true;
9119
+ index = end < 0 ? text.length : end;
9120
+ continue;
9121
+ }
9122
+ const block = syntax.block.find(([open]) => text.startsWith(open, index));
9123
+ if (block) {
9124
+ const end = text.indexOf(block[1], index + block[0].length);
9125
+ if (end < 0)
9126
+ return null;
9127
+ const comment = text.slice(index, end + block[1].length);
9128
+ if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
9129
+ emitVerbatim(comment);
9130
+ else
9131
+ pendingSpace = true;
9132
+ if (comment.includes(`
9133
+ `))
9134
+ newline();
9135
+ index = end + block[1].length;
9136
+ continue;
9137
+ }
9138
+ if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
9139
+ if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
9140
+ return null;
9141
+ continue;
9142
+ }
9143
+ if (syntax.quotes.includes(char)) {
9144
+ if (!scanString(char, true, Boolean(syntax.multiline)))
9145
+ return null;
9146
+ continue;
9147
+ }
9148
+ if (syntax.literal?.includes(char)) {
9149
+ if (!scanString(char, false, false))
9150
+ return null;
9151
+ continue;
9152
+ }
9153
+ if (syntax.rawBacktick && char === "`") {
9154
+ if (!scanString("`", false, true))
9155
+ return null;
9156
+ continue;
9157
+ }
9158
+ if (syntax.js && char === "`") {
9159
+ emitVerbatim(char);
9160
+ frames.push({ kind: "template" });
9161
+ index += 1;
9162
+ continue;
9163
+ }
9164
+ if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
9165
+ return null;
9166
+ if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
9167
+ const lineEnd = text.indexOf(`
9168
+ `, index);
9169
+ if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
9170
+ return null;
9171
+ }
9172
+ if (syntax.js && char === "/" && regexAllowed()) {
9173
+ let end = index + 1;
9174
+ let inClass = false;
9175
+ while (end < text.length && (inClass || text[end] !== "/")) {
9176
+ if (text[end] === `
9177
+ `)
9178
+ return null;
9179
+ if (text[end] === "\\")
9180
+ end += 1;
9181
+ else if (text[end] === "[")
9182
+ inClass = true;
9183
+ else if (text[end] === "]")
9184
+ inClass = false;
9185
+ end += 1;
9186
+ }
9187
+ if (end >= text.length)
9188
+ return null;
9189
+ emitVerbatim(text.slice(index, end + 1));
9190
+ index = end + 1;
9191
+ continue;
9192
+ }
9193
+ if (frames.length > 1 && frame.kind === "code") {
9194
+ if (char === "{")
9195
+ frame.braces += 1;
9196
+ else if (char === "}") {
9197
+ if (frame.braces === 0) {
9198
+ emitVerbatim(char);
9199
+ frames.pop();
9200
+ index += 1;
9201
+ continue;
9202
+ }
9203
+ frame.braces -= 1;
9204
+ }
9205
+ }
9206
+ emitCode(char);
9207
+ index += 1;
9208
+ }
9209
+ if (frames.length !== 1)
9210
+ return null;
9211
+ newline();
9212
+ return out;
9213
+ }
9214
+ function unbalanced(body) {
9215
+ let depth = 0;
9216
+ for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
9217
+ if (part === "{")
9218
+ depth += 1;
9219
+ else if (part === "}")
9220
+ depth = Math.max(0, depth - 1);
9221
+ }
9222
+ return depth > 0;
9223
+ }
9224
+ function hasOpaqueLineBreak(text) {
9225
+ return /\r(?!\n)|[\u2028\u2029]/u.test(text);
9226
+ }
9227
+ var decoder = new TextDecoder("utf-8", { fatal: true });
9228
+ function textOf(bytes) {
9229
+ if (bytes.includes(0))
9230
+ return null;
9231
+ try {
9232
+ return decoder.decode(bytes);
9233
+ } catch {
9234
+ return null;
9235
+ }
9236
+ }
9237
+ function cosmeticChange(file, before, after) {
9238
+ const left = textOf(before);
9239
+ const right = textOf(after);
9240
+ if (left === null || right === null)
9241
+ return false;
9242
+ if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
9243
+ return false;
9244
+ const a = substanceOf(file, left);
9245
+ return a !== null && a === substanceOf(file, right);
9246
+ }
9247
+
9248
+ // packages/workit-core/src/core/policy/lane.ts
9249
+ var LANE_THRESHOLDS = {
9250
+ selfMaxLines: 100,
9251
+ selfMaxFiles: 5,
9252
+ fullMinLines: 500
9253
+ };
9254
+ var RISK_TOKENS = new Set([
9255
+ "auth",
9256
+ "authn",
9257
+ "authz",
9258
+ "oauth",
9259
+ "security",
9260
+ "permission",
9261
+ "permissions",
9262
+ "secret",
9263
+ "secrets",
9264
+ "crypto",
9265
+ "gate",
9266
+ "gates",
9267
+ "policy",
9268
+ "policies",
9269
+ "hook",
9270
+ "hooks",
9271
+ "husky",
9272
+ "lefthook",
9273
+ "release",
9274
+ "releases",
9275
+ "migration",
9276
+ "migrations",
9277
+ "migrate",
9278
+ "workflows"
9279
+ ]);
9280
+ var RISK_FILES = new Set([
9281
+ ".gitlab-ci.yml",
9282
+ "Jenkinsfile",
9283
+ "azure-pipelines.yml",
9284
+ ".pre-commit-config.yaml",
9285
+ ".workit/risk-paths"
9286
+ ]);
9287
+ var RISK_NAMES = new Set([".gitattributes"]);
9288
+ var RISK_PATHS_FILE = ".workit/risk-paths";
9289
+ var LOCKFILES = new Set([
9290
+ "bun.lock",
9291
+ "bun.lockb",
9292
+ "package-lock.json",
9293
+ "npm-shrinkwrap.json",
9294
+ "yarn.lock",
9295
+ "pnpm-lock.yaml",
9296
+ "Cargo.lock",
9297
+ "go.sum",
9298
+ "poetry.lock",
9299
+ "uv.lock",
9300
+ "Pipfile.lock",
9301
+ "Gemfile.lock",
9302
+ "composer.lock"
9303
+ ]);
9304
+ var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc)$/iu;
9305
+ var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
9306
+ var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
9307
+ var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__)\/|\.(?:test|spec)\.[^/]+$|_test\.[^/]+$/iu;
9308
+ var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
9309
+ var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
9310
+ function kindOf(file) {
9311
+ if (file.binary)
9312
+ return "behavior";
9313
+ if (file.generated)
9314
+ return "generated";
9315
+ if (LOCKFILES.has(basename(file.path)))
9316
+ return "lockfile";
9317
+ if (isDoc(file.path))
9318
+ return "doc";
9319
+ if (file.cosmetic)
9320
+ return "cosmetic";
9321
+ return TEST_PATH.test(file.path) ? "test" : "behavior";
9322
+ }
9323
+ var globCache = new Map;
9324
+ function globMatch(pattern, file) {
9325
+ let regex = globCache.get(pattern);
9326
+ if (!regex) {
9327
+ let source = "";
9328
+ for (let index = 0;index < pattern.length; index += 1) {
9329
+ const char = pattern[index];
9330
+ if (char === "*" && pattern[index + 1] === "*") {
9331
+ source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
9332
+ index += pattern[index + 2] === "/" ? 2 : 1;
9333
+ } else if (char === "*")
9334
+ source += "[^/]*";
9335
+ else if (char === "?")
9336
+ source += "[^/]";
9337
+ else
9338
+ source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
9339
+ }
9340
+ regex = new RegExp(`^${source}$`, "u");
9341
+ globCache.set(pattern, regex);
9342
+ }
9343
+ return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
9344
+ }
9345
+ function isRiskPath(file, extra = []) {
9346
+ if (extra.some((pattern) => globMatch(pattern, file)))
9347
+ return true;
9348
+ if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
9349
+ return true;
9350
+ if (TEST_PATH.test(file) || isDoc(file))
9351
+ return false;
9352
+ 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)));
9353
+ }
9354
+ var parseRiskPaths = (text) => text.split(`
9355
+ `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
9356
+ function computeLane(input) {
9357
+ const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
9358
+ const risky = new Set(riskFiles);
9359
+ const kinds = input.files.map((file) => ({
9360
+ file,
9361
+ kind: risky.has(file.path) ? "behavior" : kindOf(file)
9362
+ }));
9363
+ const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
9364
+ const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
9365
+ const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
9366
+ const result = (lane, reasons) => ({
9367
+ lane,
9368
+ reasons,
9369
+ lines,
9370
+ files: behavior.length,
9371
+ riskFiles
9372
+ });
9373
+ const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
9374
+ if (input.riskTier === "high")
9375
+ return result("full", ["the task judgment records high risk"]);
9376
+ if (riskFiles.length)
9377
+ return result("full", [
9378
+ `touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
9379
+ ]);
9380
+ if (!behavior.length)
9381
+ return result("none", [
9382
+ input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
9383
+ ]);
9384
+ if (lines >= fullMinLines)
9385
+ return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
9386
+ if (lines > selfMaxLines)
9387
+ return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
9388
+ if (behavior.length > selfMaxFiles)
9389
+ return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
9390
+ if (opaque.length)
9391
+ return result("focused", [
9392
+ `a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
9393
+ ]);
9394
+ return result("self", [
9395
+ `small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
9396
+ ]);
9397
+ }
9398
+ function untrackedStat(cwd, file) {
9399
+ try {
9400
+ const bytes = readFileSync3(path6.join(cwd, file));
9401
+ if (bytes.includes(0))
9402
+ return { path: file, added: 0, deleted: 0, binary: true };
9403
+ const text = bytes.toString("utf8");
9404
+ if (hasOpaqueLineBreak(text))
9405
+ return { path: file, added: 0, deleted: 0, binary: true };
9406
+ const lines = text.split(`
9407
+ `).length - (text.endsWith(`
9408
+ `) ? 1 : 0);
9409
+ return { path: file, added: lines, deleted: 0 };
9410
+ } catch {
9411
+ return { path: file, added: 0, deleted: 0, binary: true };
9412
+ }
9413
+ }
9414
+ var REGULAR = new Set(["100644", "100755"]);
9415
+ var ABSENT = /^0+$/u;
9416
+ function cosmeticFiles(cwd, fork, head) {
9417
+ const raw = diffRaw(cwd, fork, head);
9418
+ if (!raw)
9419
+ return null;
9420
+ const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
9421
+ const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
9422
+ if (!blobs)
9423
+ return null;
9424
+ const out = new Set;
9425
+ const opaque = new Set;
9426
+ for (const entry of candidates) {
9427
+ const before = blobs.get(entry.oldBlob);
9428
+ let after = blobs.get(entry.newBlob);
9429
+ if (!after && head === null && ABSENT.test(entry.newBlob))
9430
+ try {
9431
+ after = readFileSync3(path6.join(cwd, entry.path));
9432
+ } catch {
9433
+ after = undefined;
9434
+ }
9435
+ if (before && after && cosmeticChange(entry.path, before, after))
9436
+ out.add(entry.path);
9437
+ else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
9438
+ opaque.add(entry.path);
9439
+ }
9440
+ return { cosmetic: out, opaque };
9441
+ }
9442
+ function generatedAt(cwd, fork, paths) {
9443
+ const files = treeFilesNamed(cwd, fork, ".gitattributes");
9444
+ const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
9445
+ if (!files || !blobs)
9446
+ return new Set;
9447
+ const texts = new Map;
9448
+ for (const [file, id] of files) {
9449
+ const bytes = blobs.get(id);
9450
+ if (bytes)
9451
+ texts.set(file, bytes.toString("utf8"));
9452
+ }
9453
+ return generatedByAttributes(texts, paths);
9454
+ }
9455
+ var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
9456
+ function laneFiles(cwd, fork, head, attributesAt = fork) {
9457
+ const stats = diffNumstat(cwd, fork, head);
9458
+ const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
9459
+ if (!stats || !cosmetic)
9460
+ return null;
9461
+ const files = [
9462
+ ...stats.map((entry) => ({
9463
+ ...entry,
9464
+ ...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
9465
+ cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
9466
+ })),
9467
+ ...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
9468
+ ];
9469
+ const generated = generatedAt(cwd, attributesAt, files.map((entry) => entry.path));
9470
+ return files.map((entry) => ({
9471
+ ...entry,
9472
+ generated: generated.has(entry.path)
9473
+ }));
9474
+ }
9475
+ function driftFiles(cwd, range) {
9476
+ if (range.fromFork === range.toFork)
9477
+ return laneFiles(cwd, range.from, range.to, range.toFork);
9478
+ const drifted = ownChangeDrift(cwd, range);
9479
+ const stats = drifted ? diffNumstat(cwd, range.from, range.to) : null;
9480
+ if (!drifted || !stats)
9481
+ return null;
9482
+ const byPath = new Map(stats.map((entry) => [entry.path, entry]));
9483
+ const generated = generatedAt(cwd, range.toFork, drifted);
9484
+ return drifted.map((file) => ({
9485
+ path: file,
9486
+ added: byPath.get(file)?.added ?? 0,
9487
+ deleted: byPath.get(file)?.deleted ?? 0,
9488
+ ...byPath.get(file)?.binary ? { binary: true } : {},
9489
+ cosmetic: false,
9490
+ generated: generated.has(file)
9491
+ }));
9492
+ }
9493
+ function branchLane(cwd, options) {
9494
+ const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
9495
+ const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
9496
+ const head = options.workingTree ? null : options.head;
9497
+ const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
9498
+ const files = fork ? laneFiles(cwd, fork, head) : null;
9499
+ const riskTier = options.judgment?.riskTier ?? null;
9500
+ if (!fork || !files) {
9501
+ const high = riskTier === "high";
9502
+ return {
9503
+ lane: high ? "full" : "focused",
9504
+ reasons: [
9505
+ `the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
9506
+ ...high ? ["the task judgment records high risk"] : []
9507
+ ],
9508
+ lines: 0,
9509
+ files: 0,
9510
+ riskFiles: [],
9511
+ base,
9512
+ head: options.head
9513
+ };
9514
+ }
9515
+ const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
9516
+ return {
9517
+ ...computeLane({
9518
+ files,
9519
+ riskTier,
9520
+ riskPaths: [...repoRisk, ...options.riskPaths ?? []]
9521
+ }),
9522
+ base,
9523
+ head: options.head
9524
+ };
9525
+ }
9526
+ var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
9527
+
9528
+ // packages/workit-core/src/core/policy/carry.ts
9529
+ var TEST_ONLY_KINDS = new Set(["doc", "cosmetic", "generated", "test"]);
9530
+ function verdictDrift(cwd, input) {
9531
+ const since = input.from;
9532
+ const stale = (reason, files = null, focused = false) => ({
9533
+ carries: false,
9534
+ since,
9535
+ files,
9536
+ focused,
9537
+ reason
9538
+ });
9539
+ const fromFork = mergeBase(cwd, input.fromBase, input.from);
9540
+ const toFork = mergeBase(cwd, input.toBase, input.to);
9541
+ const files = fromFork && toFork ? driftFiles(cwd, { from: input.from, to: input.to, fromFork, toFork }) : null;
9542
+ if (!fromFork || !toFork || !files)
9543
+ return stale(`the change since ${since.slice(0, 12)} cannot be read`);
9544
+ const riskPaths = [fromFork, toFork].flatMap((fork) => parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? ""));
9545
+ const lane = computeLane({ files, riskPaths });
9546
+ const paths = files.map((file) => file.path);
9547
+ if (lane.lane === "none")
9548
+ return { carries: true, rule: "lane-none", since, files: paths };
9549
+ if (input.kind === "unit" && lane.lane === "self" && files.every((file) => TEST_ONLY_KINDS.has(kindOf(file))))
9550
+ return { carries: true, rule: "tests-only", since, files: paths };
9551
+ return stale(lane.reasons[0] ?? `lane ${lane.lane}`, paths, lane.lane === "self");
9552
+ }
9553
+
8675
9554
  // packages/workit-core/src/host-session.ts
8676
9555
  import { createHash as createHash7, randomBytes, timingSafeEqual } from "node:crypto";
8677
9556
  import fs5 from "node:fs";
8678
- import path7 from "node:path";
9557
+ import path8 from "node:path";
8679
9558
 
8680
9559
  // packages/workit-core/src/core/branch.ts
8681
9560
  import { createHash as createHash6 } from "node:crypto";
@@ -8896,7 +9775,7 @@ var isProtectedTarget = (root, name) => {
8896
9775
 
8897
9776
  // packages/workit-core/src/core/logger.ts
8898
9777
  import os3 from "node:os";
8899
- import path6 from "node:path";
9778
+ import path7 from "node:path";
8900
9779
  var SENSITIVE_WORDS = new Set([
8901
9780
  "token",
8902
9781
  "secret",
@@ -8941,14 +9820,14 @@ var resolveStateDir = () => {
8941
9820
  return override;
8942
9821
  const home = os3.homedir();
8943
9822
  if (process.env.XDG_STATE_HOME)
8944
- return path6.join(process.env.XDG_STATE_HOME, "workit");
9823
+ return path7.join(process.env.XDG_STATE_HOME, "workit");
8945
9824
  switch (os3.platform()) {
8946
9825
  case "darwin":
8947
- return path6.join(home, "Library", "Application Support", "workit");
9826
+ return path7.join(home, "Library", "Application Support", "workit");
8948
9827
  case "win32":
8949
- return path6.join(process.env.LOCALAPPDATA ?? path6.join(home, "AppData", "Local"), "workit");
9828
+ return path7.join(process.env.LOCALAPPDATA ?? path7.join(home, "AppData", "Local"), "workit");
8950
9829
  default:
8951
- return path6.join(process.env.HOME ?? home, ".local", "state", "workit");
9830
+ return path7.join(process.env.HOME ?? home, ".local", "state", "workit");
8952
9831
  }
8953
9832
  };
8954
9833
  var homePattern = new RegExp(`${escapeRegExp2(os3.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
@@ -8984,8 +9863,8 @@ function hostSessionFromEnv(env) {
8984
9863
  var ISSUED_PER_LEAD = 64;
8985
9864
  var ISSUED_SESSIONS_MARKER = "workit-issued-sessions/3";
8986
9865
  var hash = (value) => createHash7("sha256").update(value).digest("hex").slice(0, 32);
8987
- var userIssuedRoot = () => path7.join(resolveStateDir(), "issued");
8988
- var userTokenRoot = () => path7.join(resolveStateDir(), "issued-tokens");
9866
+ var userIssuedRoot = () => path8.join(resolveStateDir(), "issued");
9867
+ var userTokenRoot = () => path8.join(resolveStateDir(), "issued-tokens");
8989
9868
  function migrateRepoIssued(cwd) {
8990
9869
  if (cwd === undefined)
8991
9870
  return;
@@ -8994,7 +9873,7 @@ function migrateRepoIssued(cwd) {
8994
9873
  const location = resolveStore(cwd);
8995
9874
  if (location instanceof Error)
8996
9875
  return;
8997
- old = path7.join(location.dir, "hooks", "issued");
9876
+ old = path8.join(location.dir, "hooks", "issued");
8998
9877
  if (!fs5.existsSync(old))
8999
9878
  return;
9000
9879
  } catch {
@@ -9002,19 +9881,19 @@ function migrateRepoIssued(cwd) {
9002
9881
  }
9003
9882
  for (const lead of safeList(old)) {
9004
9883
  try {
9005
- const from = path7.join(old, lead);
9884
+ const from = path8.join(old, lead);
9006
9885
  if (!fs5.statSync(from).isDirectory())
9007
9886
  continue;
9008
- const to = path7.join(userIssuedRoot(), lead);
9887
+ const to = path8.join(userIssuedRoot(), lead);
9009
9888
  fs5.mkdirSync(to, { recursive: true, mode: 448 });
9010
9889
  for (const id of fs5.readdirSync(from)) {
9011
9890
  try {
9012
- fs5.copyFileSync(path7.join(from, id), path7.join(to, id), fs5.constants.COPYFILE_EXCL);
9891
+ fs5.copyFileSync(path8.join(from, id), path8.join(to, id), fs5.constants.COPYFILE_EXCL);
9013
9892
  } catch (error) {
9014
9893
  if (error.code !== "EEXIST")
9015
9894
  throw error;
9016
9895
  }
9017
- fs5.rmSync(path7.join(from, id), { force: true });
9896
+ fs5.rmSync(path8.join(from, id), { force: true });
9018
9897
  }
9019
9898
  removeIfEmpty(from);
9020
9899
  } catch {}
@@ -9039,13 +9918,13 @@ function recordIssuedSession(cwd, lead, issued) {
9039
9918
  try {
9040
9919
  migrateRepoIssued(cwd);
9041
9920
  const own = hash(issued);
9042
- const tokens = path7.join(userTokenRoot(), hash(lead));
9921
+ const tokens = path8.join(userTokenRoot(), hash(lead));
9043
9922
  fs5.mkdirSync(tokens, { recursive: true, mode: 448 });
9044
- fs5.writeFileSync(path7.join(tokens, own), `${tokenHash(token)}
9923
+ fs5.writeFileSync(path8.join(tokens, own), `${tokenHash(token)}
9045
9924
  `, { mode: 384, flag: "wx" });
9046
- const dir = path7.join(userIssuedRoot(), hash(lead));
9925
+ const dir = path8.join(userIssuedRoot(), hash(lead));
9047
9926
  fs5.mkdirSync(dir, { recursive: true, mode: 448 });
9048
- fs5.writeFileSync(path7.join(dir, own), `${ISSUED_SESSIONS_MARKER}
9927
+ fs5.writeFileSync(path8.join(dir, own), `${ISSUED_SESSIONS_MARKER}
9049
9928
  `, { mode: 384 });
9050
9929
  pruneIssued(dir, own, tokens);
9051
9930
  return token;
@@ -9073,10 +9952,10 @@ function pruneIssued(dir, own, tokens) {
9073
9952
  const entries = fs5.readdirSync(dir);
9074
9953
  if (entries.length <= ISSUED_PER_LEAD)
9075
9954
  return;
9076
- const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs5.statSync(path7.join(dir, name)).mtimeMs })).toSorted((a, b) => a.at - b.at);
9955
+ const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs5.statSync(path8.join(dir, name)).mtimeMs })).toSorted((a, b) => a.at - b.at);
9077
9956
  for (const { name } of byAge.slice(0, entries.length - ISSUED_PER_LEAD)) {
9078
- fs5.rmSync(path7.join(dir, name), { force: true });
9079
- fs5.rmSync(path7.join(tokens, name), { force: true });
9957
+ fs5.rmSync(path8.join(dir, name), { force: true });
9958
+ fs5.rmSync(path8.join(tokens, name), { force: true });
9080
9959
  }
9081
9960
  } catch {}
9082
9961
  }
@@ -9086,7 +9965,7 @@ var issuedRecord = (cwd, claimed) => {
9086
9965
  return null;
9087
9966
  migrateRepoIssued(cwd);
9088
9967
  try {
9089
- return fs5.readFileSync(path7.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
9968
+ return fs5.readFileSync(path8.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
9090
9969
  } catch {
9091
9970
  return null;
9092
9971
  }
@@ -9096,9 +9975,9 @@ var issuedToken = (claimed) => {
9096
9975
  const cut = claimed.lastIndexOf(":");
9097
9976
  if (cut <= 0)
9098
9977
  return { stored: null, leadBound: false };
9099
- const dir = path7.join(userTokenRoot(), hash(claimed.slice(0, cut)));
9978
+ const dir = path8.join(userTokenRoot(), hash(claimed.slice(0, cut)));
9100
9979
  try {
9101
- const text = fs5.readFileSync(path7.join(dir, hash(claimed)), "utf8");
9980
+ const text = fs5.readFileSync(path8.join(dir, hash(claimed)), "utf8");
9102
9981
  return { stored: TOKEN_HASH.exec(text)?.[1] ?? null, leadBound: true };
9103
9982
  } catch {
9104
9983
  return { stored: null, leadBound: safeList(dir).length > 0 };
@@ -9118,33 +9997,33 @@ var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
9118
9997
  // packages/workit-core/src/hooks/standing-signal.ts
9119
9998
  import { createHash as createHash8 } from "node:crypto";
9120
9999
  import fs6 from "node:fs";
9121
- import path8 from "node:path";
10000
+ import path9 from "node:path";
9122
10001
  var gitStoreDir = (cwd) => {
9123
10002
  try {
9124
- for (let dir = path8.resolve(cwd);; dir = path8.dirname(dir)) {
9125
- const dotGit = path8.join(dir, ".git");
10003
+ for (let dir = path9.resolve(cwd);; dir = path9.dirname(dir)) {
10004
+ const dotGit = path9.join(dir, ".git");
9126
10005
  if (fs6.existsSync(dotGit)) {
9127
10006
  let gitDir = dotGit;
9128
10007
  if (fs6.statSync(dotGit).isFile()) {
9129
10008
  const target = /^gitdir:\s*(.+)$/mu.exec(fs6.readFileSync(dotGit, "utf8"))?.[1]?.trim();
9130
10009
  if (!target)
9131
10010
  return null;
9132
- gitDir = path8.resolve(dir, target);
9133
- const common = path8.join(gitDir, "commondir");
10011
+ gitDir = path9.resolve(dir, target);
10012
+ const common = path9.join(gitDir, "commondir");
9134
10013
  if (fs6.existsSync(common))
9135
- gitDir = path8.resolve(gitDir, fs6.readFileSync(common, "utf8").trim());
10014
+ gitDir = path9.resolve(gitDir, fs6.readFileSync(common, "utf8").trim());
9136
10015
  }
9137
- return path8.join(gitDir, "workit");
10016
+ return path9.join(gitDir, "workit");
9138
10017
  }
9139
- if (path8.dirname(dir) === dir)
10018
+ if (path9.dirname(dir) === dir)
9140
10019
  return null;
9141
10020
  }
9142
10021
  } catch {
9143
10022
  return null;
9144
10023
  }
9145
10024
  };
9146
- var standingRevFile = (store) => path8.join(store, "standing", "rev");
9147
- var standingSeenFile = (store, session, agentId) => path8.join(store, "standing", "seen", `${createHash8("sha256").update(`${session}\x00${agentId ?? ""}`).digest("hex").slice(0, 32)}.json`);
10025
+ var standingRevFile = (store) => path9.join(store, "standing", "rev");
10026
+ var standingSeenFile = (store, session, agentId) => path9.join(store, "standing", "seen", `${createHash8("sha256").update(`${session}\x00${agentId ?? ""}`).digest("hex").slice(0, 32)}.json`);
9148
10027
  var readStandingRev = (store) => {
9149
10028
  try {
9150
10029
  return fs6.readFileSync(standingRevFile(store), "utf8").trim() || null;
@@ -9165,7 +10044,7 @@ var readStandingSeen = (file) => {
9165
10044
  };
9166
10045
  var writeStandingSeen = (file, seen) => {
9167
10046
  try {
9168
- fs6.mkdirSync(path8.dirname(file), { recursive: true });
10047
+ fs6.mkdirSync(path9.dirname(file), { recursive: true });
9169
10048
  fs6.writeFileSync(file, JSON.stringify(seen));
9170
10049
  } catch {}
9171
10050
  };
@@ -9215,52 +10094,19 @@ var git3 = (cwd, args) => {
9215
10094
  const value = run.ok ? run.stdout.trim() : "";
9216
10095
  return value ? value : null;
9217
10096
  };
9218
- var safeRef = (value) => value.length > 0 && !value.startsWith("-");
10097
+ var safeRef2 = (value) => value.length > 0 && !value.startsWith("-");
9219
10098
  function resolveCommit(cwd, ref) {
9220
- if (!safeRef(ref))
10099
+ if (!safeRef2(ref))
9221
10100
  return null;
9222
10101
  return git3(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
9223
10102
  }
9224
10103
  function branchHead(cwd, branch) {
9225
- if (!safeRef(branch))
10104
+ if (!safeRef2(branch))
9226
10105
  return null;
9227
10106
  return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
9228
10107
  }
9229
- var trackTrunk = (cwd) => {
9230
- try {
9231
- const resolved = vcsConfig("resolve", cwd);
9232
- if (resolved.ok === false || !resolved.releaseTrack || resolved.releaseTrack.blocking)
9233
- return null;
9234
- const target = String(resolved.defaultTargetBranch ?? "").trim();
9235
- return target && safeRef(target) ? target : null;
9236
- } catch {
9237
- return null;
9238
- }
9239
- };
9240
- function defaultBase(cwd) {
9241
- const trunk = trackTrunk(cwd);
9242
- if (trunk) {
9243
- for (const candidate of [`origin/${trunk}`, trunk])
9244
- if (resolveCommit(cwd, candidate))
9245
- return candidate;
9246
- }
9247
- const originHead = git3(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
9248
- if (originHead && resolveCommit(cwd, originHead))
9249
- return originHead;
9250
- for (const candidate of [
9251
- "origin/main",
9252
- "origin/master",
9253
- "main",
9254
- "master",
9255
- "origin/develop",
9256
- "develop"
9257
- ])
9258
- if (resolveCommit(cwd, candidate))
9259
- return candidate;
9260
- return null;
9261
- }
9262
10108
  function diffHash(cwd, base, head) {
9263
- if (!safeRef(base) || !safeRef(head))
10109
+ if (!safeRef2(base) || !safeRef2(head))
9264
10110
  return null;
9265
10111
  const diff = gitRun2(cwd, [
9266
10112
  "diff",
@@ -9292,14 +10138,14 @@ function diffHash(cwd, base, head) {
9292
10138
  return `sha256:${hash.digest("hex")}`;
9293
10139
  }
9294
10140
  function storeRoot(cwd) {
9295
- const location = resolveStore(path9.resolve(cwd));
10141
+ const location = resolveStore(path10.resolve(cwd));
9296
10142
  if (location instanceof Error)
9297
10143
  return err("unavailable", location.message.replace(/; fix the repository.*$/u, ""), "fix the repository (git status should work here) or run from a non-git directory");
9298
10144
  return { ok: true, value: { root: location.dir, shared: location.shared } };
9299
10145
  }
9300
10146
  function ledgerPath(cwd) {
9301
10147
  const root = storeRoot(cwd);
9302
- return root.ok ? { ok: true, value: path9.join(root.value.root, "ledger", "ledger.jsonl") } : root;
10148
+ return root.ok ? { ok: true, value: path10.join(root.value.root, "ledger", "ledger.jsonl") } : root;
9303
10149
  }
9304
10150
  var newId2 = () => `${Date.now().toString(36)}-${randomBytes2(6).toString("hex")}`;
9305
10151
  var NETWORK_FS_TYPES = new Set([
@@ -9329,13 +10175,13 @@ var LOCK_STALE_MS = 30000;
9329
10175
  var LOCK_OWNER = "owner";
9330
10176
  var lockToken = (lock) => {
9331
10177
  try {
9332
- return fs7.readFileSync(path9.join(lock, LOCK_OWNER), "utf8");
10178
+ return fs7.readFileSync(path10.join(lock, LOCK_OWNER), "utf8");
9333
10179
  } catch {
9334
10180
  return null;
9335
10181
  }
9336
10182
  };
9337
10183
  var lockAgeMs = (lock) => {
9338
- for (const target of [path9.join(lock, LOCK_OWNER), lock]) {
10184
+ for (const target of [path10.join(lock, LOCK_OWNER), lock]) {
9339
10185
  try {
9340
10186
  return Date.now() - fs7.statSync(target).mtimeMs;
9341
10187
  } catch {}
@@ -9352,7 +10198,7 @@ var ledgerLock = {
9352
10198
  throw error;
9353
10199
  }
9354
10200
  const token = `${process.pid}-${randomBytes2(8).toString("hex")}`;
9355
- fs7.writeFileSync(path9.join(lock, LOCK_OWNER), token);
10201
+ fs7.writeFileSync(path10.join(lock, LOCK_OWNER), token);
9356
10202
  return token;
9357
10203
  },
9358
10204
  release(lock, token) {
@@ -9485,8 +10331,8 @@ function appendLine(file, build, options) {
9485
10331
  }
9486
10332
  };
9487
10333
  try {
9488
- fs7.mkdirSync(path9.dirname(file), { recursive: true });
9489
- if (!options.forceLock && !onNetworkFs(path9.dirname(file)))
10334
+ fs7.mkdirSync(path10.dirname(file), { recursive: true });
10335
+ if (!options.forceLock && !onNetworkFs(path10.dirname(file)))
9490
10336
  return write();
9491
10337
  const locked = withLedgerLock(file, write);
9492
10338
  return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
@@ -9720,7 +10566,7 @@ function verdictBasis(verdict, current) {
9720
10566
  }
9721
10567
  var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
9722
10568
  function trailerSessions(cwd, base, head) {
9723
- if (!base || !head || !safeRef(base) || !safeRef(head))
10569
+ if (!base || !head || !safeRef2(base) || !safeRef2(head))
9724
10570
  return [];
9725
10571
  const out = git3(cwd, [
9726
10572
  "log",
@@ -9733,7 +10579,7 @@ var RENAMED = /^branch: renamed refs\/heads\/(.+) to refs\/heads\/(.+)$/iu;
9733
10579
  function branchScope(cwd, branch) {
9734
10580
  const renamedAt = new Map;
9735
10581
  let since = Number.NEGATIVE_INFINITY;
9736
- if (safeRef(branch)) {
10582
+ if (safeRef2(branch)) {
9737
10583
  const log = git3(cwd, [
9738
10584
  "log",
9739
10585
  "-g",
@@ -9878,12 +10724,29 @@ function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
9878
10724
  }
9879
10725
  return effective;
9880
10726
  }
9881
- function restackCarries(rows, branch, from, to) {
10727
+ var restackExact = new WeakMap;
10728
+ function restackCarries(exactIn, rows, branch, from, to) {
9882
10729
  if (!from || !to)
9883
10730
  return false;
10731
+ const exact = (row, fromHead, head) => {
10732
+ if (exactIn === null)
10733
+ return true;
10734
+ const cwd = exactIn;
10735
+ let known = restackExact.get(row);
10736
+ if (known === undefined) {
10737
+ known = typeof row.fromBase === "string" && typeof row.toBase === "string" && sameOwnChange(cwd, {
10738
+ from: fromHead,
10739
+ to: head,
10740
+ fromBase: row.fromBase,
10741
+ toBase: row.toBase
10742
+ });
10743
+ restackExact.set(row, known);
10744
+ }
10745
+ return known;
10746
+ };
9884
10747
  const edges = new Map;
9885
10748
  for (const row of rows)
9886
- if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
10749
+ if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head && exact(row, row.fromHead, row.head))
9887
10750
  edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
9888
10751
  const seen = new Set([from]);
9889
10752
  const queue = [from];
@@ -9917,9 +10780,57 @@ function checkVerdicts(cwd, branch, ledgerRows) {
9917
10780
  return key;
9918
10781
  };
9919
10782
  const keyFor = (base) => keysAt(base, head);
9920
- const basisOf = (row) => {
10783
+ const driftCache = new Map;
10784
+ const driftOf = (row) => {
10785
+ if (driftCache.has(row))
10786
+ return driftCache.get(row) ?? null;
10787
+ let drift = null;
10788
+ const toBase = row.base ?? fallbackBase;
10789
+ const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
10790
+ const fromBase = row.baseSha ?? toBaseSha;
10791
+ if (row.head && head && row.head !== head && toBaseSha && fromBase && !untrusted(reasonsOf(row)))
10792
+ drift = verdictDrift(cwd, {
10793
+ from: row.head,
10794
+ to: head,
10795
+ fromBase,
10796
+ toBase: toBaseSha,
10797
+ kind: typeof row.kind === "string" ? row.kind : "review"
10798
+ });
10799
+ driftCache.set(row, drift);
10800
+ return drift;
10801
+ };
10802
+ const exactCache = new Map;
10803
+ const exactCarry = (row) => {
10804
+ if (FAILING.has(String(row.result)))
10805
+ return true;
10806
+ let exact = exactCache.get(row);
10807
+ if (exact === undefined) {
10808
+ const toBase = row.base ?? fallbackBase;
10809
+ const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
10810
+ const fromBase = row.baseSha ?? toBaseSha;
10811
+ exact = Boolean(row.head && head && toBaseSha && fromBase && sameOwnChange(cwd, { from: row.head, to: head, fromBase, toBase: toBaseSha }));
10812
+ exactCache.set(row, exact);
10813
+ }
10814
+ return exact;
10815
+ };
10816
+ const keyBasis = (row) => {
9921
10817
  const basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
9922
- return basis === "stale" && restackCarries(rows, branch, row.head, head) ? "carried" : basis;
10818
+ return basis === "carried" && !exactCarry(row) ? "stale" : basis;
10819
+ };
10820
+ const restacked = (row) => restackCarries(FAILING.has(String(row.result)) ? null : cwd, rows, branch, row.head, head);
10821
+ const carryOf = (row) => {
10822
+ if (keyBasis(row) !== "stale")
10823
+ return null;
10824
+ if (restacked(row))
10825
+ return null;
10826
+ const drift = driftOf(row);
10827
+ return drift?.carries ? { rule: drift.rule, since: drift.since, files: drift.files } : null;
10828
+ };
10829
+ const basisOf = (row) => {
10830
+ const basis = keyBasis(row);
10831
+ if (basis !== "stale")
10832
+ return basis;
10833
+ return restacked(row) || carryOf(row) ? "carried" : basis;
9923
10834
  };
9924
10835
  const sameCode = (failed, row) => {
9925
10836
  if (failed.head && failed.head === row.head)
@@ -9927,7 +10838,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
9927
10838
  const base = failed.base ?? fallbackBase;
9928
10839
  const stored = (row.base ?? fallbackBase) === base && row.baseSha !== null && row.baseSha === failed.baseSha;
9929
10840
  const keys = stored ? { patchId: row.patchId, diffHash: row.diffHash } : keysAt(base, row.head);
9930
- return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
10841
+ return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(null, rows, branch, failed.head, row.head);
9931
10842
  };
9932
10843
  const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
9933
10844
  const failureAuthorCache = new Map;
@@ -9983,6 +10894,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
9983
10894
  independent: independence.length === 0,
9984
10895
  accepted: reasons.length === 0,
9985
10896
  reasons,
10897
+ carry: basis === "carried" ? carryOf(row) : null,
9986
10898
  verdict: row
9987
10899
  });
9988
10900
  if (reasons.length === 0) {
@@ -10011,7 +10923,12 @@ function checkVerdicts(cwd, branch, ledgerRows) {
10011
10923
  verdict: failing?.verdict ?? null,
10012
10924
  reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
10013
10925
  };
10014
- const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
10926
+ 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))));
10927
+ const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
10928
+ const carryEntry = verdicts.find((entry) => entry.verdict === accepted.verdict && entry.accepted) ?? newestCurrent;
10929
+ const lastPass = accepted.accepted || failing || tampered.length ? undefined : verdicts.findLast((entry) => !entry.current && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
10930
+ const drift = lastPass ? driftOf(lastPass.verdict) : null;
10931
+ const reverify = drift && !drift.carries && drift.focused && drift.files ? { scope: "focused", since: drift.since, files: drift.files } : null;
10015
10932
  return {
10016
10933
  branch,
10017
10934
  head,
@@ -10020,6 +10937,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
10020
10937
  base: newestCurrent?.verdict.base ?? fallbackBase,
10021
10938
  patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
10022
10939
  current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
10940
+ carry: carryEntry?.carry ?? null,
10941
+ reverify,
10023
10942
  accepted,
10024
10943
  verdicts,
10025
10944
  authors: [...authors].toSorted(),
@@ -10061,6 +10980,7 @@ var RULES = {
10061
10980
  self: "verdict:self",
10062
10981
  nonAuthor: "verdict:non-author",
10063
10982
  verified: "verdict:verified",
10983
+ lane: "verdict:lane",
10064
10984
  product: "decision:product",
10065
10985
  plan: "plan"
10066
10986
  };
@@ -10083,6 +11003,7 @@ var normalizedScope = (scope) => ({
10083
11003
  function deriveRequirements(judgment, scope, constraints, verification = "self") {
10084
11004
  const out = [];
10085
11005
  const base = { scope, dependentAction: null };
11006
+ const proportional = verification === "proportional";
10086
11007
  if (judgment.behaviorChange)
10087
11008
  out.push(derived2({
10088
11009
  ...base,
@@ -10093,7 +11014,17 @@ function deriveRequirements(judgment, scope, constraints, verification = "self")
10093
11014
  before: "close",
10094
11015
  acceptanceAllowed: false
10095
11016
  }));
10096
- if (judgment.behaviorChange && judgment.riskTier === "normal")
11017
+ if (proportional && judgment.riskTier === "normal")
11018
+ out.push(derived2({
11019
+ ...base,
11020
+ ruleId: RULES.lane,
11021
+ dimension: "review",
11022
+ 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.",
11023
+ 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.",
11024
+ before: "close",
11025
+ acceptanceAllowed: false
11026
+ }));
11027
+ if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
10097
11028
  out.push(verification === "independent" ? derived2({
10098
11029
  ...base,
10099
11030
  ruleId: RULES.nonAuthor,
@@ -10196,7 +11127,7 @@ var productDecision = (task, ledger) => {
10196
11127
  };
10197
11128
  };
10198
11129
  var planRecorded = (task, root) => {
10199
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync2(path10.resolve(root, ref.path)));
11130
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync3(path11.resolve(root, ref.path)));
10200
11131
  if (cited)
10201
11132
  return {
10202
11133
  met: true,
@@ -10210,14 +11141,30 @@ var planRecorded = (task, root) => {
10210
11141
  decisionIds: []
10211
11142
  };
10212
11143
  };
10213
- var verdict = (requirement, root, ledger) => {
11144
+ var workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) });
11145
+ var laneVerdict = (task, root, ledger) => {
11146
+ if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
11147
+ return {
11148
+ met: true,
11149
+ reason: "no branch to read a lane from, and the judgment records no behavior change",
11150
+ decisionIds: []
11151
+ };
11152
+ const lane = workingLane(task, root);
11153
+ const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
11154
+ const need = laneNeed(lane.lane);
11155
+ if (need === "none")
11156
+ return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
11157
+ const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
11158
+ return { ...status, reason: `${label}: ${status.reason}` };
11159
+ };
11160
+ var verdict = (ruleId, root, ledger) => {
10214
11161
  const branch = ledger.branch();
10215
11162
  if (!branch)
10216
11163
  return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
10217
11164
  const check = checkVerdicts(root, branch, ledger.rows());
10218
11165
  const failing = check.accepted.reasons.includes("failing_verdict");
10219
11166
  const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
10220
- 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)));
11167
+ 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)));
10221
11168
  if (met)
10222
11169
  return {
10223
11170
  met: true,
@@ -10225,7 +11172,7 @@ var verdict = (requirement, root, ledger) => {
10225
11172
  decisionIds: []
10226
11173
  };
10227
11174
  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, " ");
10228
- 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>\``;
11175
+ 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>\``;
10229
11176
  return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
10230
11177
  };
10231
11178
  function derivedRuleStatus(task, requirement, root, ledger) {
@@ -10237,7 +11184,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
10237
11184
  case RULES.self:
10238
11185
  case RULES.nonAuthor:
10239
11186
  case RULES.verified:
10240
- return verdict(requirement, root, ledger);
11187
+ return verdict(requirement.ruleId, root, ledger);
11188
+ case RULES.lane:
11189
+ return laneVerdict(task, root, ledger);
10241
11190
  default:
10242
11191
  return null;
10243
11192
  }
@@ -10812,7 +11761,7 @@ var judgeInputSummary = (raw) => {
10812
11761
 
10813
11762
  // packages/workit-core/src/autonomy.ts
10814
11763
  import os4 from "node:os";
10815
- import path11 from "node:path";
11764
+ import path12 from "node:path";
10816
11765
  var accountHome = () => {
10817
11766
  try {
10818
11767
  return os4.userInfo().homedir;
@@ -10821,10 +11770,10 @@ var accountHome = () => {
10821
11770
  }
10822
11771
  };
10823
11772
  var grantsHome = { resolve: accountHome };
10824
- var grantsDir = () => path11.join(grantsHome.resolve(), ".config", "workit");
11773
+ var grantsDir = () => path12.join(grantsHome.resolve(), ".config", "workit");
10825
11774
  var grantsOverride = () => {
10826
- const configured = path11.resolve(resolveConfigDir());
10827
- return configured === path11.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 ${path11.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
11775
+ const configured = path12.resolve(resolveConfigDir());
11776
+ return configured === path12.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 ${path12.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
10828
11777
  };
10829
11778
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
10830
11779
  var DEFAULT_GRANTS = Object.freeze({
@@ -10877,8 +11826,16 @@ function endpointView(entry, context) {
10877
11826
  ...issue ? { endpointIssue: issue } : {}
10878
11827
  };
10879
11828
  }
10880
- var DEFAULT_VERIFICATION = "self";
10881
- var verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self";
11829
+ var VERIFICATION_MODES = [
11830
+ "proportional",
11831
+ "self",
11832
+ "independent"
11833
+ ];
11834
+ var DEFAULT_VERIFICATION = "proportional";
11835
+ var verificationOf = (entry) => {
11836
+ const value = entry?.verification;
11837
+ return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
11838
+ };
10882
11839
  var isGrantValue = (value) => value === true || value === false || value === "verified";
10883
11840
  var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
10884
11841
  var fromAutoApprove = (value) => {
@@ -10950,7 +11907,7 @@ var grantHint = (workspace, kind) => `ask the user to run, in their own terminal
10950
11907
  // packages/workit-core/src/core/task-store.ts
10951
11908
  import { createHash as createHash11, randomUUID as randomUUID3 } from "node:crypto";
10952
11909
  import * as fs12 from "node:fs";
10953
- import path16 from "node:path";
11910
+ import path17 from "node:path";
10954
11911
 
10955
11912
  // node_modules/@openclaw/fs-safe/dist/errors.js
10956
11913
  var OPERATIONAL_CODES = new Set([
@@ -11001,7 +11958,7 @@ function sameFileIdentity(left, right, platform = process.platform) {
11001
11958
  // node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
11002
11959
  import { randomBytes as randomBytes3 } from "node:crypto";
11003
11960
  import fsSync from "node:fs";
11004
- import path12 from "node:path";
11961
+ import path13 from "node:path";
11005
11962
 
11006
11963
  // node_modules/@openclaw/fs-safe/dist/bounded-read.js
11007
11964
  import fs8 from "node:fs";
@@ -11091,13 +12048,13 @@ ${ownershipToken}
11091
12048
  return { raw, ownershipToken };
11092
12049
  }
11093
12050
  function relativeSidecarLockPath(lockRoot, lockPath) {
11094
- const resolved = path12.resolve(lockPath);
11095
- const lexicalRelative = path12.relative(lockRoot.rootDir, resolved);
11096
- const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path12.sep}`) && !path12.isAbsolute(lexicalRelative) ? lexicalRelative : path12.relative(lockRoot.rootReal, resolved);
11097
- if (!relative || relative === ".." || relative.startsWith(`..${path12.sep}`) || path12.isAbsolute(relative)) {
12051
+ const resolved = path13.resolve(lockPath);
12052
+ const lexicalRelative = path13.relative(lockRoot.rootDir, resolved);
12053
+ const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path13.sep}`) && !path13.isAbsolute(lexicalRelative) ? lexicalRelative : path13.relative(lockRoot.rootReal, resolved);
12054
+ if (!relative || relative === ".." || relative.startsWith(`..${path13.sep}`) || path13.isAbsolute(relative)) {
11098
12055
  throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
11099
12056
  }
11100
- return relative.split(path12.sep).join(path12.posix.sep);
12057
+ return relative.split(path13.sep).join(path13.posix.sep);
11101
12058
  }
11102
12059
  function parseSidecarLockPayload(raw, parser) {
11103
12060
  if (parser) {
@@ -11244,7 +12201,7 @@ function getFsSafeLockConfig() {
11244
12201
 
11245
12202
  // node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
11246
12203
  import fs10 from "node:fs";
11247
- import path13 from "node:path";
12204
+ import path14 from "node:path";
11248
12205
 
11249
12206
  // node_modules/@openclaw/fs-safe/dist/timing.js
11250
12207
  function sleepSync(ms) {
@@ -11335,26 +12292,26 @@ function canonicalLockParentSync(parent) {
11335
12292
  return process.platform === "win32" ? fs10.realpathSync.native(parent) : fs10.realpathSync(parent);
11336
12293
  }
11337
12294
  function normalizeTargetPath(targetPath) {
11338
- const resolved = path13.resolve(targetPath);
11339
- fs10.mkdirSync(path13.dirname(resolved), { recursive: true });
12295
+ const resolved = path14.resolve(targetPath);
12296
+ fs10.mkdirSync(path14.dirname(resolved), { recursive: true });
11340
12297
  try {
11341
- return path13.join(canonicalLockParentSync(path13.dirname(resolved)), path13.basename(resolved));
12298
+ return path14.join(canonicalLockParentSync(path14.dirname(resolved)), path14.basename(resolved));
11342
12299
  } catch {
11343
12300
  return resolved;
11344
12301
  }
11345
12302
  }
11346
12303
  function boundedLockPath(lockPath, lockRoot) {
11347
- const resolved = path13.resolve(lockPath);
12304
+ const resolved = path14.resolve(lockPath);
11348
12305
  if (!lockRoot)
11349
12306
  return resolved;
11350
12307
  relativeSidecarLockPath(lockRoot, resolved);
11351
- const parent = path13.dirname(resolved);
12308
+ const parent = path14.dirname(resolved);
11352
12309
  const parentReal = canonicalLockParentSync(parent);
11353
- const parentRelative = path13.relative(lockRoot.rootReal, parentReal);
11354
- if (parentRelative === ".." || parentRelative.startsWith(`..${path13.sep}`) || path13.isAbsolute(parentRelative)) {
12310
+ const parentRelative = path14.relative(lockRoot.rootReal, parentReal);
12311
+ if (parentRelative === ".." || parentRelative.startsWith(`..${path14.sep}`) || path14.isAbsolute(parentRelative)) {
11355
12312
  throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
11356
12313
  }
11357
- return path13.join(parentReal, path13.basename(resolved));
12314
+ return path14.join(parentReal, path14.basename(resolved));
11358
12315
  }
11359
12316
  function defaultShouldReclaim(snapshot, staleMs, nowMs) {
11360
12317
  const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
@@ -11554,14 +12511,14 @@ function acquireFileLockSync(targetPath, options) {
11554
12511
  }
11555
12512
  }
11556
12513
  // packages/workit-core/src/core/package-root.ts
11557
- import { existsSync as existsSync3 } from "node:fs";
11558
- import path14 from "node:path";
12514
+ import { existsSync as existsSync4 } from "node:fs";
12515
+ import path15 from "node:path";
11559
12516
  import { fileURLToPath } from "node:url";
11560
12517
  var packageRoot = () => {
11561
- let dir = path14.dirname(fileURLToPath(import.meta.url));
12518
+ let dir = path15.dirname(fileURLToPath(import.meta.url));
11562
12519
  while (true) {
11563
- const parent = path14.dirname(dir);
11564
- if (existsSync3(path14.join(dir, "package.json")) || parent === dir)
12520
+ const parent = path15.dirname(dir);
12521
+ if (existsSync4(path15.join(dir, "package.json")) || parent === dir)
11565
12522
  return dir;
11566
12523
  dir = parent;
11567
12524
  }
@@ -11570,7 +12527,7 @@ var packageRoot = () => {
11570
12527
  // packages/workit-core/src/store/event-log.ts
11571
12528
  import { randomUUID as randomUUID2 } from "node:crypto";
11572
12529
  import fs11 from "node:fs";
11573
- import path15 from "node:path";
12530
+ import path16 from "node:path";
11574
12531
  var EVENT_VERSION = 1;
11575
12532
 
11576
12533
  class LogDamage extends Error {
@@ -11681,7 +12638,7 @@ function appendEvent(file, event, validEnd) {
11681
12638
  fs11.closeSync(fd);
11682
12639
  }
11683
12640
  if (created)
11684
- fsyncDirectory(path15.dirname(file));
12641
+ fsyncDirectory(path16.dirname(file));
11685
12642
  return validEnd;
11686
12643
  }
11687
12644
  function replaceFile(file, bytes, durable) {
@@ -11697,7 +12654,7 @@ function replaceFile(file, bytes, durable) {
11697
12654
  }
11698
12655
  renameWithRetry(temporary, file);
11699
12656
  if (durable)
11700
- fsyncDirectory(path15.dirname(file));
12657
+ fsyncDirectory(path16.dirname(file));
11701
12658
  } catch (error) {
11702
12659
  try {
11703
12660
  fs11.unlinkSync(temporary);
@@ -12040,7 +12997,7 @@ var cachedRuntimeVersion = null;
12040
12997
  var runtimeVersion = () => {
12041
12998
  if (cachedRuntimeVersion === null) {
12042
12999
  try {
12043
- const pkg = JSON.parse(fs12.readFileSync(path16.join(packageRoot(), "package.json"), "utf8"));
13000
+ const pkg = JSON.parse(fs12.readFileSync(path17.join(packageRoot(), "package.json"), "utf8"));
12044
13001
  cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
12045
13002
  } catch {
12046
13003
  cachedRuntimeVersion = "0.0.0";
@@ -12073,7 +13030,7 @@ var readText = (file) => {
12073
13030
  };
12074
13031
  var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
12075
13032
  var branchStart = (location, branch, aliases) => {
12076
- const first = readText(path16.join(path16.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
13033
+ const first = readText(path17.join(path17.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
12077
13034
  `, 1)[0];
12078
13035
  const created = REFLOG_LINE.exec(first);
12079
13036
  const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
@@ -12083,7 +13040,7 @@ var branchStart = (location, branch, aliases) => {
12083
13040
  return start;
12084
13041
  if (!location.git)
12085
13042
  return null;
12086
- for (const line of readText(path16.join(location.git.gitDir, "logs", "HEAD")).split(`
13043
+ for (const line of readText(path17.join(location.git.gitDir, "logs", "HEAD")).split(`
12087
13044
  `)) {
12088
13045
  const entry = REFLOG_LINE.exec(line);
12089
13046
  const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
@@ -12093,14 +13050,14 @@ var branchStart = (location, branch, aliases) => {
12093
13050
  }
12094
13051
  return null;
12095
13052
  };
12096
- var refExists = (common, ref) => fs12.existsSync(path16.join(common, ref)) || readText(path16.join(common, "packed-refs")).includes(` ${ref}
13053
+ var refExists = (common, ref) => fs12.existsSync(path17.join(common, ref)) || readText(path17.join(common, "packed-refs")).includes(` ${ref}
12097
13054
  `);
12098
13055
  var origins = new Map;
12099
13056
  var sameDirectoryIdentity = (left, right) => {
12100
- if (!path16.isAbsolute(left) || !path16.isAbsolute(right))
13057
+ if (!path17.isAbsolute(left) || !path17.isAbsolute(right))
12101
13058
  return false;
12102
- const normalizedLeft = path16.resolve(left);
12103
- const normalizedRight = path16.resolve(right);
13059
+ const normalizedLeft = path17.resolve(left);
13060
+ const normalizedRight = path17.resolve(right);
12104
13061
  try {
12105
13062
  const a = fs12.statSync(normalizedLeft, { bigint: true });
12106
13063
  const b = fs12.statSync(normalizedRight, { bigint: true });
@@ -12146,7 +13103,7 @@ class TaskStore {
12146
13103
  migrationChecked = false;
12147
13104
  compactDue = new Set;
12148
13105
  constructor(root, options = {}) {
12149
- const resolved = path16.resolve(root);
13106
+ const resolved = path17.resolve(root);
12150
13107
  this.origin = fs12.existsSync(resolved) ? fs12.realpathSync(resolved) : resolved;
12151
13108
  this.located = resolveStore(this.origin);
12152
13109
  this.root = checkoutRootOf(this.origin, this.located);
@@ -12155,7 +13112,7 @@ class TaskStore {
12155
13112
  this.migrateOnRead = options.migrateOnRead;
12156
13113
  }
12157
13114
  location() {
12158
- if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs12.existsSync(path16.join(this.root, ".git"))) {
13115
+ if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs12.existsSync(path17.join(this.root, ".git"))) {
12159
13116
  this.located = undefined;
12160
13117
  this.migrationChecked = false;
12161
13118
  }
@@ -12311,7 +13268,7 @@ class TaskStore {
12311
13268
  const moved = this.bindLocked(again.data.id, key, input.now);
12312
13269
  return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
12313
13270
  }
12314
- const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path16.basename(this.root) || this.root;
13271
+ const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path17.basename(this.root) || this.root;
12315
13272
  const intent = {
12316
13273
  objective: input.objective?.trim() || `Work on ${label}`,
12317
13274
  scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
@@ -12377,14 +13334,14 @@ class TaskStore {
12377
13334
  const branch = key.ok ? key.data.branch : null;
12378
13335
  if (!branch || !location.ok)
12379
13336
  return success(null, null, null);
12380
- const common = path16.dirname(location.data.dir);
12381
- const heads = path16.join(common, "logs", "refs", "heads");
12382
- const headLog = location.data.git ? path16.join(location.data.git.gitDir, "logs", "HEAD") : "";
13337
+ const common = path17.dirname(location.data.dir);
13338
+ const heads = path17.join(common, "logs", "refs", "heads");
13339
+ const headLog = location.data.git ? path17.join(location.data.git.gitDir, "logs", "HEAD") : "";
12383
13340
  const signatures = [
12384
- path16.join(heads, branch),
13341
+ path17.join(heads, branch),
12385
13342
  headLog,
12386
13343
  heads,
12387
- path16.join(common, "packed-refs")
13344
+ path17.join(common, "packed-refs")
12388
13345
  ].map((file) => fileSignature(file) ?? "-");
12389
13346
  const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
12390
13347
  let resolved = origins.get(slot);
@@ -12396,7 +13353,7 @@ class TaskStore {
12396
13353
  aliases.push(rename.from);
12397
13354
  const start = branchStart(location.data, branch, aliases);
12398
13355
  const slash = start?.indexOf("/") ?? -1;
12399
- const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path16.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
13356
+ const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path17.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
12400
13357
  const name = start && (remote ? start.slice(slash + 1) : start);
12401
13358
  resolved = {
12402
13359
  signature: signatures.join("|"),
@@ -12634,13 +13591,13 @@ class TaskStore {
12634
13591
  if (!ready.ok)
12635
13592
  return ready;
12636
13593
  const dirs = this.paths();
12637
- const recovery = path16.join(this.root, ".workit", "recovery");
13594
+ const recovery = path17.join(this.root, ".workit", "recovery");
12638
13595
  if (fs12.existsSync(recovery)) {
12639
13596
  let files = 0;
12640
13597
  let bytes = 0;
12641
13598
  try {
12642
13599
  for (const name of fs12.readdirSync(recovery)) {
12643
- const stat = fs12.lstatSync(path16.join(recovery, name));
13600
+ const stat = fs12.lstatSync(path17.join(recovery, name));
12644
13601
  if (!stat.isFile())
12645
13602
  continue;
12646
13603
  files += 1;
@@ -12693,7 +13650,7 @@ class TaskStore {
12693
13650
  if (fs12.existsSync(dirs.blobs))
12694
13651
  for (const name of fs12.readdirSync(dirs.blobs)) {
12695
13652
  const digest = name.replace(/\.json$/, "");
12696
- const file = path16.join(dirs.blobs, name);
13653
+ const file = path17.join(dirs.blobs, name);
12697
13654
  let stat;
12698
13655
  try {
12699
13656
  stat = fs12.lstatSync(file);
@@ -12711,7 +13668,7 @@ class TaskStore {
12711
13668
  }
12712
13669
  return success(null, null, null);
12713
13670
  };
12714
- const swept = dryRun ? sweep() : this.withLock(path16.join(dirs.dir, "gc.lock"), sweep);
13671
+ const swept = dryRun ? sweep() : this.withLock(path17.join(dirs.dir, "gc.lock"), sweep);
12715
13672
  if (!swept.ok)
12716
13673
  return swept;
12717
13674
  const temporaryDirs = [
@@ -12719,7 +13676,7 @@ class TaskStore {
12719
13676
  dirs.tasks,
12720
13677
  dirs.blobs,
12721
13678
  dirs.checkout,
12722
- ...names.map((name) => path16.join(dirs.tasks, name))
13679
+ ...names.map((name) => path17.join(dirs.tasks, name))
12723
13680
  ];
12724
13681
  for (const directory of temporaryDirs) {
12725
13682
  if (!fs12.existsSync(directory))
@@ -12727,7 +13684,7 @@ class TaskStore {
12727
13684
  for (const name of fs12.readdirSync(directory)) {
12728
13685
  if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
12729
13686
  continue;
12730
- const file = path16.join(directory, name);
13687
+ const file = path17.join(directory, name);
12731
13688
  try {
12732
13689
  const stat = fs12.lstatSync(file);
12733
13690
  if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
@@ -12744,30 +13701,30 @@ class TaskStore {
12744
13701
  }
12745
13702
  paths() {
12746
13703
  const location = this.location();
12747
- const dir = location.ok ? location.data.dir : path16.join(this.root, ".workit");
12748
- const checkout = path16.join(dir, "checkouts", checkoutSlug(this.root));
13704
+ const dir = location.ok ? location.data.dir : path17.join(this.root, ".workit");
13705
+ const checkout = path17.join(dir, "checkouts", checkoutSlug(this.root));
12749
13706
  return {
12750
13707
  dir,
12751
13708
  shared: location.ok ? location.data.shared : false,
12752
- marker: path16.join(dir, "store.json"),
13709
+ marker: path17.join(dir, "store.json"),
12753
13710
  checkout,
12754
- workspace: path16.join(checkout, "workspace.json"),
12755
- checkoutLock: path16.join(checkout, "metadata.lock"),
12756
- tasks: path16.join(dir, "tasks"),
12757
- index: path16.join(dir, "task-index.json"),
12758
- blobs: path16.join(dir, "blobs", "candidates"),
12759
- legacy: path16.join(dir, "legacy", checkoutSlug(this.root), "v2"),
12760
- v2: path16.join(this.root, ".workit")
13711
+ workspace: path17.join(checkout, "workspace.json"),
13712
+ checkoutLock: path17.join(checkout, "metadata.lock"),
13713
+ tasks: path17.join(dir, "tasks"),
13714
+ index: path17.join(dir, "task-index.json"),
13715
+ blobs: path17.join(dir, "blobs", "candidates"),
13716
+ legacy: path17.join(dir, "legacy", checkoutSlug(this.root), "v2"),
13717
+ v2: path17.join(this.root, ".workit")
12761
13718
  };
12762
13719
  }
12763
13720
  eventsPath(taskId) {
12764
- return path16.join(this.paths().tasks, taskId, "events.jsonl");
13721
+ return path17.join(this.paths().tasks, taskId, "events.jsonl");
12765
13722
  }
12766
13723
  snapshotPath(taskId) {
12767
- return path16.join(this.paths().tasks, taskId, "snapshot.json");
13724
+ return path17.join(this.paths().tasks, taskId, "snapshot.json");
12768
13725
  }
12769
13726
  taskLockPath(taskId) {
12770
- return path16.join(this.paths().tasks, taskId, "lock");
13727
+ return path17.join(this.paths().tasks, taskId, "lock");
12771
13728
  }
12772
13729
  ready(write = false) {
12773
13730
  const location = this.location();
@@ -12806,13 +13763,13 @@ class TaskStore {
12806
13763
  return {
12807
13764
  read: (digest) => {
12808
13765
  try {
12809
- return JSON.parse(fs12.readFileSync(path16.join(dir, `${digest}.json`), "utf8"));
13766
+ return JSON.parse(fs12.readFileSync(path17.join(dir, `${digest}.json`), "utf8"));
12810
13767
  } catch (error) {
12811
13768
  throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
12812
13769
  }
12813
13770
  },
12814
13771
  write: (digest, value) => {
12815
- const file = path16.join(dir, `${digest}.json`);
13772
+ const file = path17.join(dir, `${digest}.json`);
12816
13773
  if (fs12.existsSync(file)) {
12817
13774
  const stamp = new Date;
12818
13775
  try {
@@ -13004,7 +13961,7 @@ class TaskStore {
13004
13961
  }
13005
13962
  }
13006
13963
  openTask(record, type, key, legacy = null) {
13007
- const dir = path16.join(this.paths().tasks, record.id);
13964
+ const dir = path17.join(this.paths().tasks, record.id);
13008
13965
  fs12.mkdirSync(dir, { recursive: true });
13009
13966
  const blobs = this.blobStore();
13010
13967
  const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
@@ -13097,7 +14054,7 @@ class TaskStore {
13097
14054
  }
13098
14055
  withKeyLock(key, operation) {
13099
14056
  const digest = createHash11("sha256").update(key).digest("hex").slice(0, 32);
13100
- return this.withLock(path16.join(this.paths().dir, "keys", `${digest}.lock`), operation);
14057
+ return this.withLock(path17.join(this.paths().dir, "keys", `${digest}.lock`), operation);
13101
14058
  }
13102
14059
  bindLocked(taskId, key, timestamp) {
13103
14060
  const current = this.readWorkspace();
@@ -13238,7 +14195,7 @@ class TaskStore {
13238
14195
  writeWorkspace(record) {
13239
14196
  const file = this.paths().workspace;
13240
14197
  try {
13241
- fs12.mkdirSync(path16.dirname(file), { recursive: true });
14198
+ fs12.mkdirSync(path17.dirname(file), { recursive: true });
13242
14199
  replaceFile(file, `${canonicalJson(record)}
13243
14200
  `, true);
13244
14201
  return success(null, null, null);
@@ -13316,23 +14273,23 @@ class TaskStore {
13316
14273
  return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
13317
14274
  }
13318
14275
  v2Dirs() {
13319
- return [...new Set([this.root, this.origin])].map((dir) => path16.join(dir, ".workit"));
14276
+ return [...new Set([this.root, this.origin])].map((dir) => path17.join(dir, ".workit"));
13320
14277
  }
13321
14278
  pendingMigrations() {
13322
14279
  return this.v2Dirs().filter((dir) => {
13323
- const raw = this.readV2Workspace(path16.join(dir, "workspace.json"));
14280
+ const raw = this.readV2Workspace(path17.join(dir, "workspace.json"));
13324
14281
  return raw !== null && raw !== TOMBSTONE;
13325
14282
  });
13326
14283
  }
13327
14284
  migrateV2(v2) {
13328
14285
  const dirs = this.paths();
13329
- const v2Workspace = path16.join(v2, "workspace.json");
13330
- const v2Tasks = path16.join(v2, "tasks");
13331
- const legacy = path16.join(dirs.dir, "legacy", checkoutSlug(path16.dirname(v2)), "v2");
14286
+ const v2Workspace = path17.join(v2, "workspace.json");
14287
+ const v2Tasks = path17.join(v2, "tasks");
14288
+ const legacy = path17.join(dirs.dir, "legacy", checkoutSlug(path17.dirname(v2)), "v2");
13332
14289
  const raw = this.readV2Workspace(v2Workspace);
13333
14290
  if (raw === null || raw === TOMBSTONE)
13334
14291
  return success(null, null, null);
13335
- return this.withLock(dirs.checkoutLock, () => this.withLock(path16.join(v2, "metadata.lock"), () => {
14292
+ return this.withLock(dirs.checkoutLock, () => this.withLock(path17.join(v2, "metadata.lock"), () => {
13336
14293
  const workspace = this.readV2Workspace(v2Workspace);
13337
14294
  if (workspace === null || workspace === TOMBSTONE)
13338
14295
  return success(null, null, null);
@@ -13341,7 +14298,7 @@ class TaskStore {
13341
14298
  return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
13342
14299
  let files = [];
13343
14300
  try {
13344
- files = fs12.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path16.join(v2Tasks, name)));
14301
+ files = fs12.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path17.join(v2Tasks, name)));
13345
14302
  } catch {}
13346
14303
  const current = this.readWorkspace();
13347
14304
  if (!current.ok)
@@ -13359,21 +14316,21 @@ class TaskStore {
13359
14316
  let skipped = 0;
13360
14317
  const done = new Map;
13361
14318
  try {
13362
- fs12.mkdirSync(path16.join(legacy, "tasks"), { recursive: true });
14319
+ fs12.mkdirSync(path17.join(legacy, "tasks"), { recursive: true });
13363
14320
  for (const name of ["workspace.json", "index.json"])
13364
- if (fs12.existsSync(path16.join(v2, name)))
13365
- fs12.copyFileSync(path16.join(v2, name), path16.join(legacy, name));
14321
+ if (fs12.existsSync(path17.join(v2, name)))
14322
+ fs12.copyFileSync(path17.join(v2, name), path17.join(legacy, name));
13366
14323
  for (const name of files) {
13367
14324
  const id = name.slice(0, -5);
13368
- const file = path16.join(v2Tasks, name);
14325
+ const file = path17.join(v2Tasks, name);
13369
14326
  const bytes = fs12.readFileSync(file, "utf8");
13370
14327
  const digest = sha256(bytes);
13371
- fs12.writeFileSync(path16.join(legacy, "tasks", name), bytes);
14328
+ fs12.writeFileSync(path17.join(legacy, "tasks", name), bytes);
13372
14329
  const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
13373
14330
  if (!parsed.ok || parsed.data.id !== id)
13374
14331
  return failure2("recovery_required", `cannot migrate 2.x task ${id}: ${parsed.ok ? "file name and record ID differ" : parsed.error}; move ${file} aside to continue`, { path: file });
13375
14332
  const record = { ...parsed.data, workspaceId: target.id };
13376
- const origin = { path: path16.join(legacy, "tasks", name), digest };
14333
+ const origin = { path: path17.join(legacy, "tasks", name), digest };
13377
14334
  if (!fs12.existsSync(this.eventsPath(id))) {
13378
14335
  this.openTask(record, "migrated.from_v2", null, origin);
13379
14336
  migrated += 1;
@@ -13409,7 +14366,7 @@ class TaskStore {
13409
14366
  try {
13410
14367
  let complete = true;
13411
14368
  for (const [name, digest] of done) {
13412
- const file = path16.join(v2Tasks, name);
14369
+ const file = path17.join(v2Tasks, name);
13413
14370
  if (sha256(fs12.readFileSync(file, "utf8")) !== digest) {
13414
14371
  complete = false;
13415
14372
  continue;
@@ -13420,7 +14377,7 @@ class TaskStore {
13420
14377
  return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
13421
14378
  path: v2
13422
14379
  });
13423
- fs12.rmSync(path16.join(v2, "index.json"), { force: true });
14380
+ fs12.rmSync(path17.join(v2, "index.json"), { force: true });
13424
14381
  this.writeTombstone(v2, target.id);
13425
14382
  } catch (error) {
13426
14383
  return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
@@ -13440,7 +14397,7 @@ class TaskStore {
13440
14397
  }
13441
14398
  adoptLocalStore() {
13442
14399
  const dirs = this.paths();
13443
- const marker = path16.join(dirs.v2, "store.json");
14400
+ const marker = path17.join(dirs.v2, "store.json");
13444
14401
  if (!dirs.shared || !fs12.existsSync(marker))
13445
14402
  return success(null, null, null);
13446
14403
  return this.withLock(dirs.checkoutLock, () => {
@@ -13450,7 +14407,7 @@ class TaskStore {
13450
14407
  try {
13451
14408
  const merge = (from, to) => {
13452
14409
  if (!fs12.existsSync(to)) {
13453
- fs12.mkdirSync(path16.dirname(to), { recursive: true });
14410
+ fs12.mkdirSync(path17.dirname(to), { recursive: true });
13454
14411
  try {
13455
14412
  fs12.renameSync(from, to);
13456
14413
  } catch {
@@ -13461,24 +14418,24 @@ class TaskStore {
13461
14418
  }
13462
14419
  if (fs12.lstatSync(from).isDirectory() && fs12.lstatSync(to).isDirectory())
13463
14420
  for (const name of fs12.readdirSync(from))
13464
- merge(path16.join(from, name), path16.join(to, name));
14421
+ merge(path17.join(from, name), path17.join(to, name));
13465
14422
  };
13466
14423
  for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
13467
- const from = path16.join(dirs.v2, sub);
14424
+ const from = path17.join(dirs.v2, sub);
13468
14425
  if (!fs12.existsSync(from))
13469
14426
  continue;
13470
14427
  if (sub === "tasks") {
13471
14428
  for (const name of fs12.readdirSync(from))
13472
- if (validId(name) && !fs12.existsSync(path16.join(dirs.dir, sub, name)))
14429
+ if (validId(name) && !fs12.existsSync(path17.join(dirs.dir, sub, name)))
13473
14430
  tasks += 1;
13474
14431
  }
13475
- merge(from, path16.join(dirs.dir, sub));
14432
+ merge(from, path17.join(dirs.dir, sub));
13476
14433
  }
13477
- const ledger = path16.join(dirs.v2, "ledger");
13478
- if (fs12.existsSync(ledger) && !fs12.existsSync(path16.join(dirs.dir, "ledger")))
13479
- merge(ledger, path16.join(dirs.dir, "ledger"));
13480
- fs12.rmSync(path16.join(dirs.v2, "task-index.json"), { force: true });
13481
- fs12.renameSync(marker, path16.join(dirs.v2, "store.moved.json"));
14434
+ const ledger = path17.join(dirs.v2, "ledger");
14435
+ if (fs12.existsSync(ledger) && !fs12.existsSync(path17.join(dirs.dir, "ledger")))
14436
+ merge(ledger, path17.join(dirs.dir, "ledger"));
14437
+ fs12.rmSync(path17.join(dirs.v2, "task-index.json"), { force: true });
14438
+ fs12.renameSync(marker, path17.join(dirs.v2, "store.moved.json"));
13482
14439
  } catch (error) {
13483
14440
  return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
13484
14441
  path: dirs.v2
@@ -13516,7 +14473,7 @@ class TaskStore {
13516
14473
  schemaVersion: SCHEMA_VERSION,
13517
14474
  id: workspaceId ?? newId(),
13518
14475
  revision: newRevision(),
13519
- root: path16.dirname(v2),
14476
+ root: path17.dirname(v2),
13520
14477
  runtime: { createdWith: version, updatedWith: version },
13521
14478
  writer: null,
13522
14479
  store: {
@@ -13528,11 +14485,11 @@ class TaskStore {
13528
14485
  critical: ["store"]
13529
14486
  };
13530
14487
  fs12.mkdirSync(v2, { recursive: true });
13531
- const ignore = path16.join(v2, ".gitignore");
14488
+ const ignore = path17.join(v2, ".gitignore");
13532
14489
  if (!fs12.existsSync(ignore))
13533
14490
  fs12.writeFileSync(ignore, `*
13534
14491
  `);
13535
- replaceFile(path16.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
14492
+ replaceFile(path17.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
13536
14493
  `, true);
13537
14494
  }
13538
14495
  stubFor(taskId) {
@@ -13556,8 +14513,8 @@ class TaskStore {
13556
14513
  }
13557
14514
  writeTombstoneIfMissing() {
13558
14515
  try {
13559
- const v2 = path16.join(this.root, ".workit");
13560
- if (fs12.existsSync(path16.join(v2, "workspace.json")))
14516
+ const v2 = path17.join(this.root, ".workit");
14517
+ if (fs12.existsSync(path17.join(v2, "workspace.json")))
13561
14518
  return;
13562
14519
  let id = null;
13563
14520
  try {
@@ -13574,7 +14531,7 @@ class TaskStore {
13574
14531
  replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
13575
14532
  `, false);
13576
14533
  if (!dirs.shared) {
13577
- const ignore = path16.join(dirs.dir, ".gitignore");
14534
+ const ignore = path17.join(dirs.dir, ".gitignore");
13578
14535
  let current = null;
13579
14536
  try {
13580
14537
  current = fs12.readFileSync(ignore, "utf8");
@@ -13634,7 +14591,7 @@ class TaskStore {
13634
14591
  withLock(lockPath, operation) {
13635
14592
  try {
13636
14593
  this.initializeStorage();
13637
- fs12.mkdirSync(path16.dirname(lockPath), { recursive: true });
14594
+ fs12.mkdirSync(path17.dirname(lockPath), { recursive: true });
13638
14595
  } catch (error) {
13639
14596
  return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
13640
14597
  path: this.paths().dir
@@ -13697,7 +14654,7 @@ class TaskStore {
13697
14654
  if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
13698
14655
  return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
13699
14656
  path: lockPath,
13700
- guidance: `Check permissions and the read-only attribute on ${path16.dirname(lockPath)}.`
14657
+ guidance: `Check permissions and the read-only attribute on ${path17.dirname(lockPath)}.`
13701
14658
  });
13702
14659
  if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
13703
14660
  let lock = null;
@@ -13724,18 +14681,18 @@ var workerBlocksTransition = (transition, state) => transition === "pause" ? fal
13724
14681
  import { createHash as createHash12 } from "node:crypto";
13725
14682
  import * as fs14 from "node:fs";
13726
14683
  import { spawnSync as spawnSync10 } from "node:child_process";
13727
- import path18 from "node:path";
14684
+ import path19 from "node:path";
13728
14685
 
13729
14686
  // packages/workit-core/src/check-config.ts
13730
14687
  import { spawnSync as spawnSync9 } from "node:child_process";
13731
14688
  import fs13 from "node:fs";
13732
- import path17 from "node:path";
14689
+ import path18 from "node:path";
13733
14690
  var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
13734
14691
  var CHECKS_FILE = ".workit/checks.json";
13735
14692
  var LEGACY_CHECKS_FILE = "workit.checks.json";
13736
14693
  function checksConfigFile(root) {
13737
- const current = path17.join(root, CHECKS_FILE);
13738
- const legacy = path17.join(root, LEGACY_CHECKS_FILE);
14694
+ const current = path18.join(root, CHECKS_FILE);
14695
+ const legacy = path18.join(root, LEGACY_CHECKS_FILE);
13739
14696
  return !fs13.existsSync(current) && fs13.existsSync(legacy) ? legacy : current;
13740
14697
  }
13741
14698
  function repoHeavyCommands(root) {
@@ -13798,10 +14755,10 @@ var git4 = (cwd, args) => {
13798
14755
  return value || null;
13799
14756
  };
13800
14757
  function checkRoot(cwd) {
13801
- return path17.resolve(git4(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
14758
+ return path18.resolve(git4(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
13802
14759
  }
13803
14760
  var packageManager = (root) => {
13804
- const has = (file) => fs13.existsSync(path17.join(root, file));
14761
+ const has = (file) => fs13.existsSync(path18.join(root, file));
13805
14762
  if (has("bun.lock") || has("bun.lockb"))
13806
14763
  return "bun";
13807
14764
  if (has("pnpm-lock.yaml"))
@@ -13821,7 +14778,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
13821
14778
  function detectedChecks(root) {
13822
14779
  const read = (file) => {
13823
14780
  try {
13824
- return fs13.readFileSync(path17.join(root, file), "utf8");
14781
+ return fs13.readFileSync(path18.join(root, file), "utf8");
13825
14782
  } catch {
13826
14783
  return null;
13827
14784
  }
@@ -13861,7 +14818,7 @@ var GATES = ["testing", "verification"];
13861
14818
  function loadCheckConfig(cwd) {
13862
14819
  const root = checkRoot(cwd);
13863
14820
  const file = checksConfigFile(root);
13864
- const legacy = path17.basename(file) === LEGACY_CHECKS_FILE;
14821
+ const legacy = path18.basename(file) === LEGACY_CHECKS_FILE;
13865
14822
  const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
13866
14823
  const note = legacy ? {
13867
14824
  note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
@@ -13939,10 +14896,10 @@ function workitAvailable(env = process.env) {
13939
14896
  return workitOnPath;
13940
14897
  const windows = process.platform === "win32";
13941
14898
  const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
13942
- const dirs = (env.PATH ?? env.Path ?? "").split(path17.delimiter).filter(Boolean);
14899
+ const dirs = (env.PATH ?? env.Path ?? "").split(path18.delimiter).filter(Boolean);
13943
14900
  workitOnPath = dirs.some((dir) => exts.some((ext) => {
13944
14901
  try {
13945
- return fs13.statSync(path17.join(dir, `workit${ext}`)).isFile();
14902
+ return fs13.statSync(path18.join(dir, `workit${ext}`)).isFile();
13946
14903
  } catch {
13947
14904
  return false;
13948
14905
  }
@@ -13955,14 +14912,14 @@ function checkCommand(args, version, onPath = workitAvailable()) {
13955
14912
 
13956
14913
  // packages/workit-core/src/core/task-evaluation.ts
13957
14914
  var digestBytes = (value) => createHash12("sha256").update(value).digest("hex");
13958
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path18.sep}`);
13959
- var relative = (root, candidate) => path18.relative(root, candidate).split(path18.sep).join("/") || ".";
14915
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path19.sep}`);
14916
+ var relative = (root, candidate) => path19.relative(root, candidate).split(path19.sep).join("/") || ".";
13960
14917
  var canonicalPath = (value) => {
13961
14918
  if (typeof value !== "string" || !value)
13962
14919
  return null;
13963
14920
  if (value.includes("\\") || value.split("/").includes(".."))
13964
14921
  return null;
13965
- const normalized = path18.posix.normalize(value);
14922
+ const normalized = path19.posix.normalize(value);
13966
14923
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
13967
14924
  return null;
13968
14925
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -14010,14 +14967,14 @@ var gitPaths = (root) => {
14010
14967
  git: !/not a git repository/i.test(stderr)
14011
14968
  };
14012
14969
  }
14013
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")) : [];
14970
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path19.sep).join("/")) : [];
14014
14971
  const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
14015
14972
  if (stagedDeleted.status !== 0) {
14016
14973
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
14017
14974
  if (!/not a git repository/i.test(stderr))
14018
14975
  return { paths, uncertain: true, git: true };
14019
14976
  } else if (stagedDeleted.stdout) {
14020
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")));
14977
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path19.sep).join("/")));
14021
14978
  }
14022
14979
  return { paths: [...new Set(paths)], uncertain: false, git: true };
14023
14980
  };
@@ -14032,7 +14989,7 @@ var walk = (root, directory, output) => {
14032
14989
  for (const name of names) {
14033
14990
  if (name === ".git" || name === ".workit")
14034
14991
  continue;
14035
- const target = path18.join(directory, name);
14992
+ const target = path19.join(directory, name);
14036
14993
  let stat;
14037
14994
  try {
14038
14995
  stat = fs14.lstatSync(target);
@@ -14050,12 +15007,12 @@ var walk = (root, directory, output) => {
14050
15007
  var scopeRoots = (root, scope) => {
14051
15008
  const roots = [];
14052
15009
  for (const item of scope.paths.length ? scope.paths : ["."]) {
14053
- const target = path18.resolve(root, item);
15010
+ const target = path19.resolve(root, item);
14054
15011
  if (!inside(root, target))
14055
15012
  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` }] });
14056
15013
  let ancestor = target;
14057
15014
  while (!fs14.existsSync(ancestor) && ancestor !== root)
14058
- ancestor = path18.dirname(ancestor);
15015
+ ancestor = path19.dirname(ancestor);
14059
15016
  try {
14060
15017
  if (!inside(root, fs14.realpathSync(ancestor)))
14061
15018
  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` }] });
@@ -14107,7 +15064,7 @@ function captureCandidate(root, scope, environment = []) {
14107
15064
  for (const item of [...names].toSorted()) {
14108
15065
  if (!scopeMatches(item, normalizedScope))
14109
15066
  continue;
14110
- const target = path18.join(checkout, item);
15067
+ const target = path19.join(checkout, item);
14111
15068
  try {
14112
15069
  const stat = fs14.lstatSync(target);
14113
15070
  if (stat.isSymbolicLink()) {
@@ -14291,23 +15248,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
14291
15248
  continue;
14292
15249
  if (!reference.digest)
14293
15250
  return failure2("invalid_input", "document references require a byte digest");
14294
- const target = path18.resolve(root, reference.path);
14295
- if (target !== root && !target.startsWith(`${root}${path18.sep}`))
15251
+ const target = path19.resolve(root, reference.path);
15252
+ if (target !== root && !target.startsWith(`${root}${path19.sep}`))
14296
15253
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
14297
15254
  fields: [
14298
15255
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
14299
15256
  ]
14300
15257
  });
14301
15258
  try {
14302
- const below = path18.relative(root, target);
15259
+ const below = path19.relative(root, target);
14303
15260
  let current = root;
14304
- for (const segment of below.split(path18.sep)) {
15261
+ for (const segment of below.split(path19.sep)) {
14305
15262
  if (!segment)
14306
15263
  continue;
14307
- current = path18.join(current, segment);
15264
+ current = path19.join(current, segment);
14308
15265
  const stat = fs14.lstatSync(current);
14309
15266
  const real = fs14.realpathSync(current);
14310
- if (real !== root && !real.startsWith(`${root}${path18.sep}`))
15267
+ if (real !== root && !real.startsWith(`${root}${path19.sep}`))
14311
15268
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
14312
15269
  fields: [
14313
15270
  {
@@ -14740,9 +15697,9 @@ Autonomy contract:
14740
15697
  Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
14741
15698
  satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
14742
15699
  - Author is not verifier: your own verdict is \`--self\`, shown as
14743
- self-reviewed, never verified. High risk, \`verification: independent\` and
14744
- merge need a verifier with its own host-derived session (a subagent's
14745
- \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
15700
+ self-reviewed, never verified. High risk, a focused or full lane and
15701
+ merge under \`verification: self|independent\` need a verifier with its
15702
+ own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
14746
15703
  - A local-commit endpoint does not imply PR readiness, and a local commit alone
14747
15704
  is not evidence of a requested remote push. Before reporting delivery,
14748
15705
  reconcile every requested item and observe it (\`workit verify-delivery\`).
@@ -14750,7 +15707,7 @@ Autonomy contract:
14750
15707
  CLI first: if a step has one right answer, use the \`workit\` verb instead of
14751
15708
  hand-running git, gh or glab (\`workit git|pr|ci|check|help\`;
14752
15709
  else \`npx -y @brainervirus/workit-cli\`).
14753
- Other checkout: \`workit --cwd <dir> <verb>\` (Codex: \`<verb> --cwd <dir>\`); one workit
15710
+ Other checkout: \`workit <verb> --cwd <dir> …\` (verb first: host rules match it); one workit
14754
15711
  verb per shell call, never \`cd <dir> && workit\`. zsh-safe: no \`echo =====\`
14755
15712
  (\`printf '%s\\n' ---\`), quote globs and URLs, no variable named \`status\` or \`path\`.
14756
15713
  Add Co-Authored-By via \`workit git commit --trailer "K: v" -- <paths>\`, never
@@ -14762,7 +15719,7 @@ authorizes an effect. Judge work once with
14762
15719
  \`workit policy assess --judge risk= behavior= product-choice= plan=\`.
14763
15720
  Across repositories, bind items to checkout, branch and endpoint, resolve
14764
15721
  competing targets before a mutation, and reconcile an uncertain
14765
- external effect before a retry.
15722
+ effect before a retry.
14766
15723
 
14767
15724
  Mid-task: never silently drop or resume an objective (workit-continue).
14768
15725
 
@@ -16608,7 +17565,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
16608
17565
  // packages/workit-core/src/hooks/context.ts
16609
17566
  import { createHash as createHash13 } from "node:crypto";
16610
17567
  import fs15 from "node:fs";
16611
- import path19 from "node:path";
17568
+ import path20 from "node:path";
16612
17569
  var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
16613
17570
  var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
16614
17571
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
@@ -16693,7 +17650,7 @@ function entryCompactContext(store, entry, session, context) {
16693
17650
  return null;
16694
17651
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
16695
17652
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
16696
- const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path19.resolve(store.root, ref.path)] : []));
17653
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path20.resolve(store.root, ref.path)] : []));
16697
17654
  const observedFiles = filesKey(files);
16698
17655
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
16699
17656
  if (!compact.ok || !compact.data)
@@ -16771,14 +17728,14 @@ var turnMarker = (input) => {
16771
17728
  if (location instanceof Error)
16772
17729
  return null;
16773
17730
  const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
16774
- return path19.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
17731
+ return path20.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
16775
17732
  } catch {
16776
17733
  return null;
16777
17734
  }
16778
17735
  };
16779
17736
  var writeTurnDigest = (file, digest) => {
16780
17737
  try {
16781
- fs15.mkdirSync(path19.dirname(file), { recursive: true });
17738
+ fs15.mkdirSync(path20.dirname(file), { recursive: true });
16782
17739
  fs15.writeFileSync(file, `${JSON.stringify({ digest })}
16783
17740
  `);
16784
17741
  } catch {}
@@ -16793,7 +17750,7 @@ var pruneTurnDigests = (dir, now) => {
16793
17750
  for (const name of fs15.readdirSync(dir)) {
16794
17751
  if (!name.startsWith("turn-"))
16795
17752
  continue;
16796
- const file = path19.join(dir, name);
17753
+ const file = path20.join(dir, name);
16797
17754
  try {
16798
17755
  if (now - fs15.statSync(file).mtimeMs > TURN_TTL_MS)
16799
17756
  fs15.rmSync(file, { force: true });
@@ -16823,7 +17780,7 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
16823
17780
  const file = turnMarker(input);
16824
17781
  if (!file)
16825
17782
  return;
16826
- pruneTurnDigests(path19.dirname(file), Date.now());
17783
+ pruneTurnDigests(path20.dirname(file), Date.now());
16827
17784
  if (text)
16828
17785
  writeTurnDigest(file, digestOf(text));
16829
17786
  else
@@ -16831,20 +17788,20 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
16831
17788
  };
16832
17789
  // packages/workit-core/src/core/route-intent.ts
16833
17790
  import { spawnSync as spawnSync12 } from "node:child_process";
16834
- import { existsSync as existsSync6 } from "node:fs";
16835
- import path22 from "node:path";
17791
+ import { existsSync as existsSync7 } from "node:fs";
17792
+ import path23 from "node:path";
16836
17793
 
16837
17794
  // packages/workit-core/src/hooks/raw-git.ts
16838
17795
  import { spawnSync as spawnSync11 } from "node:child_process";
16839
17796
  import { createHash as createHash15 } from "node:crypto";
16840
17797
  import fs17 from "node:fs";
16841
17798
  import os5 from "node:os";
16842
- import path21 from "node:path";
17799
+ import path22 from "node:path";
16843
17800
 
16844
17801
  // packages/workit-core/src/hooks/session-marker.ts
16845
17802
  import { createHash as createHash14 } from "node:crypto";
16846
17803
  import fs16 from "node:fs";
16847
- import path20 from "node:path";
17804
+ import path21 from "node:path";
16848
17805
  var sessionMarkerFile = (input, session, store) => {
16849
17806
  let dir = store;
16850
17807
  if (dir === undefined) {
@@ -16854,7 +17811,7 @@ var sessionMarkerFile = (input, session, store) => {
16854
17811
  dir = location.dir;
16855
17812
  }
16856
17813
  const name = createHash14("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
16857
- return path20.join(dir, "hooks", `skills-${name}.json`);
17814
+ return path21.join(dir, "hooks", `skills-${name}.json`);
16858
17815
  };
16859
17816
  var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
16860
17817
  var EMPTY = { nudged: [], loaded: [], raw: [], stops: [], judges: [] };
@@ -16881,7 +17838,7 @@ var parseMarker = (text) => {
16881
17838
  };
16882
17839
  var readSessionMarker = (file) => parseMarker(readRaw(file));
16883
17840
  var writeAtomic = (file, marker) => {
16884
- fs16.mkdirSync(path20.dirname(file), { recursive: true });
17841
+ fs16.mkdirSync(path21.dirname(file), { recursive: true });
16885
17842
  const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
16886
17843
  try {
16887
17844
  fs16.writeFileSync(temp, JSON.stringify(marker));
@@ -17168,6 +18125,18 @@ var substitutions = (body) => {
17168
18125
  };
17169
18126
  var PLAIN_POSIX = /[^\s'"\\`$(){},<>|;&#]+/uy;
17170
18127
  var PLAIN_POWERSHELL = /[^\s'"`(){}<>|;&#]+/uy;
18128
+ var expandsInQuotes = (body, dialect) => {
18129
+ const escape = dialect === "posix" ? "\\" : "`";
18130
+ for (let index = 0;index < body.length; index++) {
18131
+ const char = body[index];
18132
+ if (char === escape)
18133
+ index++;
18134
+ else if (char === "$" || dialect === "posix" && char === "`")
18135
+ return true;
18136
+ }
18137
+ return false;
18138
+ };
18139
+ var EXPANDING_RUN = /[*?[]/u;
17171
18140
  var arithmeticEnd = (command, start) => {
17172
18141
  const end = substitutionEnd(command, start);
17173
18142
  return command[end - 1] === ")" && end > start + 2 ? end : 0;
@@ -17186,6 +18155,7 @@ var tokenize = (command, dialect) => {
17186
18155
  let current = { words: [], redirects: [] };
17187
18156
  let word = "";
17188
18157
  let inWord = false;
18158
+ let expanding = false;
17189
18159
  let pending = null;
17190
18160
  const heredocs = [];
17191
18161
  const escape = posix ? "\\" : "`";
@@ -17200,11 +18170,16 @@ var tokenize = (command, dialect) => {
17200
18170
  heredocs.push({ delimiter: text.replace(/^-/, ""), owner: current });
17201
18171
  else if (pending === "herestring")
17202
18172
  (current.stdin ??= []).push(text);
17203
- else if (pending !== "input")
18173
+ else if (pending !== "input") {
18174
+ const at = current.words.length;
17204
18175
  current.words.push(...MARKERS.test(word) ? expandBraces(word) : [word]);
18176
+ if (expanding || !posix && word.startsWith("@"))
18177
+ (current.expands ??= []).push(...current.words.slice(at).map((_, i) => at + i));
18178
+ }
17205
18179
  pending = null;
17206
18180
  word = "";
17207
18181
  inWord = false;
18182
+ expanding = false;
17208
18183
  };
17209
18184
  const flushSegment = () => {
17210
18185
  flushWord();
@@ -17219,6 +18194,7 @@ var tokenize = (command, dialect) => {
17219
18194
  };
17220
18195
  const substitution = (inner, text) => {
17221
18196
  (current.subs ??= []).push(inner);
18197
+ expanding = true;
17222
18198
  word += text;
17223
18199
  inWord = true;
17224
18200
  };
@@ -17229,6 +18205,8 @@ var tokenize = (command, dialect) => {
17229
18205
  if (run) {
17230
18206
  word += run[0];
17231
18207
  inWord = true;
18208
+ if (EXPANDING_RUN.test(run[0]))
18209
+ expanding = true;
17232
18210
  index += run[0].length - 1;
17233
18211
  continue;
17234
18212
  }
@@ -17257,6 +18235,8 @@ var tokenize = (command, dialect) => {
17257
18235
  const body = command.slice(index + 1, end < 0 ? undefined : end);
17258
18236
  if (posix && char === '"' && /\$\(|`/.test(body))
17259
18237
  (current.subs ??= []).push(...substitutions(body));
18238
+ if (char === '"' && expandsInQuotes(body, dialect))
18239
+ expanding = true;
17260
18240
  word += unquote(body, char, dialect);
17261
18241
  inWord = true;
17262
18242
  index = end < 0 ? command.length : end;
@@ -17302,6 +18282,8 @@ var tokenize = (command, dialect) => {
17302
18282
  if (char === "(" && inWord && word.endsWith("$")) {
17303
18283
  word = word.slice(0, -1);
17304
18284
  inWord = word !== "";
18285
+ if (!inWord)
18286
+ expanding = false;
17305
18287
  }
17306
18288
  group(char === "(" ? "open" : "close");
17307
18289
  } else if (!posix && (char === "{" || char === "}")) {
@@ -17355,6 +18337,8 @@ var tokenize = (command, dialect) => {
17355
18337
  else if (posix && char === "}" && word.includes(OPEN))
17356
18338
  word += CLOSE;
17357
18339
  else {
18340
+ if (char === "$")
18341
+ expanding = true;
17358
18342
  word += char;
17359
18343
  inWord = true;
17360
18344
  }
@@ -17412,14 +18396,14 @@ var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
17412
18396
  var ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
17413
18397
  var MAX_DEPTH = 4;
17414
18398
  var MAX_EXPANSIONS = 16;
17415
- var isAbsolute = (value) => path21.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
18399
+ var isAbsolute = (value) => path22.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
17416
18400
  var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
17417
18401
  var joinDir = (base, raw) => {
17418
18402
  const next = raw.replace(HERE, ".");
17419
18403
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
17420
18404
  return;
17421
- const expanded = next === "~" || next.startsWith("~/") ? path21.join(os5.homedir(), next.slice(1)) : next;
17422
- return base === null || isAbsolute(expanded) ? expanded : path21.join(base, expanded);
18405
+ const expanded = next === "~" || next.startsWith("~/") ? path22.join(os5.homedir(), next.slice(1)) : next;
18406
+ return base === null || isAbsolute(expanded) ? expanded : path22.join(base, expanded);
17423
18407
  };
17424
18408
  var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
17425
18409
  var cdTarget = (words) => {
@@ -17799,8 +18783,17 @@ function walk2(command, dialect, depth, seen) {
17799
18783
  visit(next, undefined, hops + 1);
17800
18784
  };
17801
18785
  const wrapped = seen ? null : workitCheckTail(words);
17802
- if (wrapped)
17803
- return "script" in wrapped ? nested(wrapped.script, "posix") : runs([wrapped.argv]);
18786
+ if (wrapped) {
18787
+ const outer = dir;
18788
+ if (wrapped.cwd !== null)
18789
+ dir = joinDir(dir, wrapped.cwd);
18790
+ if ("script" in wrapped)
18791
+ nested(wrapped.script, "posix");
18792
+ else
18793
+ runs([wrapped.argv]);
18794
+ dir = outer;
18795
+ return;
18796
+ }
17804
18797
  if (CD.has(name)) {
17805
18798
  const target = cdTarget(words);
17806
18799
  dir = target === null ? os5.homedir() : joinDir(dir, target);
@@ -17901,14 +18894,14 @@ function workitCheckTail(words) {
17901
18894
  return null;
17902
18895
  } else if (!/^workit(?:\.exe|\.cmd)?$/.test(commandName(words[0])))
17903
18896
  return null;
17904
- const args = words.slice(at + 1);
18897
+ const { rest: args, cwd } = skipWorkitGlobals(words.slice(at + 1));
17905
18898
  if (args[0] !== "check" && !(args[0] === "lock" && args[1] === "run"))
17906
18899
  return null;
17907
18900
  const split = args.indexOf("--");
17908
18901
  const tail = split < 0 ? [] : args.slice(split + 1);
17909
18902
  if (!tail.length)
17910
18903
  return null;
17911
- return args.slice(0, split).includes("--shell") ? { script: tail.join(" ") } : { argv: tail };
18904
+ return args.slice(0, split).includes("--shell") ? { script: tail.join(" "), cwd } : { argv: tail, cwd };
17912
18905
  }
17913
18906
  function simpleCommand(command, dialect = "posix", depth = 0) {
17914
18907
  let segments;
@@ -17969,7 +18962,21 @@ function skipWorkitGlobals(args) {
17969
18962
  } else if (args[at] !== "--json")
17970
18963
  break;
17971
18964
  }
17972
- return { rest: args.slice(at), cwd };
18965
+ const rest = [];
18966
+ for (;at < args.length; at++) {
18967
+ const arg = args[at];
18968
+ if (arg === "--") {
18969
+ rest.push(...args.slice(at));
18970
+ break;
18971
+ }
18972
+ if (arg.startsWith("--cwd="))
18973
+ cwd = arg.slice("--cwd=".length);
18974
+ else if (arg === "--cwd" && args[at + 1] !== undefined)
18975
+ cwd = args[++at];
18976
+ else
18977
+ rest.push(arg);
18978
+ }
18979
+ return { rest, cwd };
17973
18980
  }
17974
18981
  var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
17975
18982
  var CURRENT_BRANCH = /^\$\(\s*git\s+(?:branch\s+--show-current|rev-parse\s+--abbrev-ref\s+HEAD|symbolic-ref\s+--short\s+HEAD)\s*\)$/;
@@ -18270,17 +19277,17 @@ var headSha2 = (repo) => {
18270
19277
  if (!git || git.reftable)
18271
19278
  return null;
18272
19279
  try {
18273
- const head = fs17.readFileSync(path21.join(git.gitDir, "HEAD"), "utf8").trim();
19280
+ const head = fs17.readFileSync(path22.join(git.gitDir, "HEAD"), "utf8").trim();
18274
19281
  if (/^[0-9a-f]{40,64}$/.test(head))
18275
19282
  return head;
18276
19283
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
18277
19284
  if (!ref)
18278
19285
  return null;
18279
- const common = path21.dirname(repo.location.dir);
19286
+ const common = path22.dirname(repo.location.dir);
18280
19287
  try {
18281
- return fs17.readFileSync(path21.join(common, ref), "utf8").trim() || null;
19288
+ return fs17.readFileSync(path22.join(common, ref), "utf8").trim() || null;
18282
19289
  } catch {}
18283
- const packed = fs17.readFileSync(path21.join(common, "packed-refs"), "utf8");
19290
+ const packed = fs17.readFileSync(path22.join(common, "packed-refs"), "utf8");
18284
19291
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
18285
19292
  } catch {
18286
19293
  return null;
@@ -18368,7 +19375,7 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
18368
19375
  const sub = invocation.args[0];
18369
19376
  if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
18370
19377
  return [invocation];
18371
- const dir = path21.resolve(root, invocation.dir ?? ".");
19378
+ const dir = path22.resolve(root, invocation.dir ?? ".");
18372
19379
  if (!fs17.existsSync(dir))
18373
19380
  return [invocation];
18374
19381
  const config = gitConfig(dir, invocation, cache);
@@ -18391,11 +19398,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
18391
19398
  var normalizedUrl = (url, dir) => {
18392
19399
  const trimmed = url.trim();
18393
19400
  if (/^file:\/\//i.test(trimmed))
18394
- return path21.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
19401
+ return path22.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
18395
19402
  const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
18396
19403
  const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
18397
19404
  if (!scheme && !scp)
18398
- return path21.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
19405
+ return path22.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
18399
19406
  const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
18400
19407
  return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
18401
19408
  };
@@ -18619,7 +19626,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
18619
19626
  const out = [];
18620
19627
  const repos = new Map;
18621
19628
  const repoFor = (relative) => {
18622
- const dir = path21.resolve(cwd, relative ?? ".");
19629
+ const dir = path22.resolve(cwd, relative ?? ".");
18623
19630
  if (!repos.has(dir)) {
18624
19631
  const repo = repoAt(dir);
18625
19632
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
@@ -18705,7 +19712,7 @@ var analysisDenial = (error) => ({
18705
19712
  });
18706
19713
  var NEXT_COMMAND = "next";
18707
19714
  var MARKER_TTL_SECONDS = 3600;
18708
- var markerFile = (repo, session, key) => path21.join(repo.location.dir, "hooks", `commit-${createHash15("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
19715
+ var markerFile = (repo, session, key) => path22.join(repo.location.dir, "hooks", `commit-${createHash15("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
18709
19716
  var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash15("sha256").update(command).digest("hex")}`;
18710
19717
  function noteRawCommit(input, command, key) {
18711
19718
  try {
@@ -18716,7 +19723,7 @@ function noteRawCommit(input, command, key) {
18716
19723
  if (action.kind !== "commit")
18717
19724
  continue;
18718
19725
  const file = markerFile(repo, input.session.id, key);
18719
- fs17.mkdirSync(path21.dirname(file), { recursive: true });
19726
+ fs17.mkdirSync(path22.dirname(file), { recursive: true });
18720
19727
  fs17.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
18721
19728
  `);
18722
19729
  }
@@ -18947,8 +19954,8 @@ var judgedDir = (root, dir) => {
18947
19954
  return null;
18948
19955
  if (dir === null)
18949
19956
  return root;
18950
- const resolved = path22.resolve(root, dir);
18951
- return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
19957
+ const resolved = path23.resolve(root, dir);
19958
+ return existsSync7(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
18952
19959
  };
18953
19960
  var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
18954
19961
  let judged = false;
@@ -18981,10 +19988,57 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
18981
19988
  }
18982
19989
  return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
18983
19990
  };
19991
+ // packages/workit-core/src/hooks/delivery-verbs.ts
19992
+ var WORKIT_ALLOWED_VERBS = [
19993
+ "git",
19994
+ "pr",
19995
+ "ci",
19996
+ "stack",
19997
+ "release",
19998
+ "task",
19999
+ "ledger",
20000
+ "evidence",
20001
+ "finding",
20002
+ "decision",
20003
+ "worker",
20004
+ "verify-delivery",
20005
+ "policy",
20006
+ "doctor",
20007
+ "help"
20008
+ ];
20009
+ var WORKIT_BYPASS_FLAGS = ["--unverified", "--overwrite-unintegrated"];
20010
+ var ALLOWED = new Set(WORKIT_ALLOWED_VERBS);
20011
+ var isBypass = (word) => WORKIT_BYPASS_FLAGS.some((flag) => word === flag || word.startsWith(`${flag}=`));
20012
+ var isWorkitWord = (word) => /^(?:.*[\\/])?workit(?:-cli)?$/u.test(word);
20013
+ var bypassReason = (flag) => `Workit: \`${flag}\` overrides a Workit gate, so the user decides this call (no allow rule covers it).`;
20014
+ var EXPANSION_REASON = "Workit: an expansion before `--` could become a bypass flag, so the user decides this call (no allow rule covers it).";
20015
+ var workitBypassFlag = (command, dialect = "posix") => {
20016
+ for (const segment of segmentsOf(command, dialect)) {
20017
+ if (segment.unanalyzable) {
20018
+ const text = command.replaceAll(/['"\\`]/gu, "");
20019
+ if (!/\bworkit\b/u.test(text))
20020
+ return null;
20021
+ const found = WORKIT_BYPASS_FLAGS.find((flag) => text.includes(flag));
20022
+ return found ? bypassReason(found) : null;
20023
+ }
20024
+ const start = segment.words.findIndex(isWorkitWord);
20025
+ if (start < 0)
20026
+ continue;
20027
+ const words = segment.words.slice(start + 1);
20028
+ const end = words.indexOf("--");
20029
+ const flag = (end < 0 ? words : words.slice(0, end)).find(isBypass);
20030
+ if (flag)
20031
+ return bypassReason(flag.split("=")[0] ?? flag);
20032
+ const limit = start + 1 + (end < 0 ? words.length : end);
20033
+ if (segment.expands?.some((index) => index > start && index < limit))
20034
+ return EXPANSION_REASON;
20035
+ }
20036
+ return null;
20037
+ };
18984
20038
  // packages/workit-core/src/core/heavy-rules.ts
18985
20039
  import fs18 from "node:fs";
18986
20040
  import os6 from "node:os";
18987
- import path23 from "node:path";
20041
+ import path24 from "node:path";
18988
20042
  var EMPTY2 = () => ({ deny: [], allow: [] });
18989
20043
  var readText2 = (file) => {
18990
20044
  try {
@@ -19025,19 +20079,19 @@ function parseJsonc(text) {
19025
20079
  return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
19026
20080
  }
19027
20081
  function gitTop(cwd) {
19028
- for (let dir = path23.resolve(cwd);; dir = path23.dirname(dir)) {
19029
- if (fs18.existsSync(path23.join(dir, ".git")))
20082
+ for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
20083
+ if (fs18.existsSync(path24.join(dir, ".git")))
19030
20084
  return dir;
19031
- if (path23.dirname(dir) === dir)
20085
+ if (path24.dirname(dir) === dir)
19032
20086
  return null;
19033
20087
  }
19034
20088
  }
19035
20089
  var projectDirs = (cwd) => {
19036
20090
  const top = gitTop(cwd);
19037
20091
  const dirs = [];
19038
- for (let dir = path23.resolve(cwd);; dir = path23.dirname(dir)) {
20092
+ for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
19039
20093
  dirs.push(dir);
19040
- if (!top || dir === top || path23.dirname(dir) === dir)
20094
+ if (!top || dir === top || path24.dirname(dir) === dir)
19041
20095
  return dirs;
19042
20096
  }
19043
20097
  };
@@ -19049,12 +20103,12 @@ var CLAUDE_MANAGED = {
19049
20103
  };
19050
20104
  function claudeSettingsFiles(cwd, env, platform = process.platform) {
19051
20105
  const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
19052
- const user = env.CLAUDE_CONFIG_DIR || path23.join(homeOf(env), ".claude");
20106
+ const user = env.CLAUDE_CONFIG_DIR || path24.join(homeOf(env), ".claude");
19053
20107
  return [
19054
20108
  ...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
19055
- path23.join(user, "settings.json"),
19056
- path23.join(project, ".claude", "settings.json"),
19057
- path23.join(project, ".claude", "settings.local.json")
20109
+ path24.join(user, "settings.json"),
20110
+ path24.join(project, ".claude", "settings.json"),
20111
+ path24.join(project, ".claude", "settings.local.json")
19058
20112
  ];
19059
20113
  }
19060
20114
  var bashPattern = (entry) => {
@@ -19144,10 +20198,10 @@ var opencodeConfig = (value, rules) => {
19144
20198
  function opencodeRules(cwd, env = process.env) {
19145
20199
  const rules = EMPTY2();
19146
20200
  try {
19147
- const config = env.XDG_CONFIG_HOME || path23.join(homeOf(env), ".config");
20201
+ const config = env.XDG_CONFIG_HOME || path24.join(homeOf(env), ".config");
19148
20202
  const files = [
19149
- ...OPENCODE_NAMES.map((name) => path23.join(config, "opencode", name)),
19150
- ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path23.join(dir, name), path23.join(dir, ".opencode", name)])),
20203
+ ...OPENCODE_NAMES.map((name) => path24.join(config, "opencode", name)),
20204
+ ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path24.join(dir, name), path24.join(dir, ".opencode", name)])),
19151
20205
  ...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
19152
20206
  ];
19153
20207
  for (const file of files) {
@@ -19191,9 +20245,9 @@ var codexRule = (body) => {
19191
20245
  function codexRules(cwd, env = process.env) {
19192
20246
  const rules = EMPTY2();
19193
20247
  try {
19194
- const home = env.CODEX_HOME || path23.join(homeOf(env), ".codex");
20248
+ const home = env.CODEX_HOME || path24.join(homeOf(env), ".codex");
19195
20249
  const top = gitTop(cwd) ?? cwd;
19196
- for (const dir of [path23.join(home, "rules"), path23.join(top, ".codex", "rules")]) {
20250
+ for (const dir of [path24.join(home, "rules"), path24.join(top, ".codex", "rules")]) {
19197
20251
  let files;
19198
20252
  try {
19199
20253
  files = fs18.readdirSync(dir).filter((name) => name.endsWith(".rules"));
@@ -19203,7 +20257,7 @@ function codexRules(cwd, env = process.env) {
19203
20257
  return "unreadable";
19204
20258
  }
19205
20259
  for (const name of files) {
19206
- const text = readText2(path23.join(dir, name)) ?? "";
20260
+ const text = readText2(path24.join(dir, name)) ?? "";
19207
20261
  for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
19208
20262
  const rule = codexRule(part);
19209
20263
  if (!rule)
@@ -19222,8 +20276,8 @@ function cursorRules(cwd, env = process.env) {
19222
20276
  try {
19223
20277
  const top = gitTop(cwd) ?? cwd;
19224
20278
  for (const file of [
19225
- path23.join(homeOf(env), ".cursor", "cli-config.json"),
19226
- path23.join(top, ".cursor", "cli.json")
20279
+ path24.join(homeOf(env), ".cursor", "cli-config.json"),
20280
+ path24.join(top, ".cursor", "cli.json")
19227
20281
  ]) {
19228
20282
  const text = readText2(file);
19229
20283
  if (text === null)
@@ -19299,21 +20353,21 @@ function wrapAllowed(read, words, command, options = {}) {
19299
20353
  // packages/workit-core/src/core/heavy.ts
19300
20354
  import fs19 from "node:fs";
19301
20355
  import os7 from "node:os";
19302
- import path24 from "node:path";
20356
+ import path25 from "node:path";
19303
20357
  function stateDir(env, platform = process.platform) {
19304
20358
  if (env.WORKFLOW_TOOLKIT_STATE)
19305
20359
  return env.WORKFLOW_TOOLKIT_STATE;
19306
20360
  if (env.XDG_STATE_HOME)
19307
- return path24.join(env.XDG_STATE_HOME, "workit");
20361
+ return path25.join(env.XDG_STATE_HOME, "workit");
19308
20362
  const home = env.HOME || os7.homedir();
19309
20363
  if (platform === "darwin")
19310
- return path24.join(home, "Library", "Application Support", "workit");
20364
+ return path25.join(home, "Library", "Application Support", "workit");
19311
20365
  if (platform === "win32")
19312
- return path24.join(env.LOCALAPPDATA ?? path24.join(home, "AppData", "Local"), "workit");
19313
- return path24.join(home, ".local", "state", "workit");
20366
+ return path25.join(env.LOCALAPPDATA ?? path25.join(home, "AppData", "Local"), "workit");
20367
+ return path25.join(home, ".local", "state", "workit");
19314
20368
  }
19315
20369
  var HEAVY_COMMANDS_FILE = "heavy-commands.json";
19316
- var heavyCommandsFile = (env) => path24.join(stateDir(env), HEAVY_COMMANDS_FILE);
20370
+ var heavyCommandsFile = (env) => path25.join(stateDir(env), HEAVY_COMMANDS_FILE);
19317
20371
  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;
19318
20372
  var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
19319
20373
  var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
@@ -19588,7 +20642,7 @@ function machinePatterns(env) {
19588
20642
  return isRecord5(value) ? stringList(value.patterns) : [];
19589
20643
  }
19590
20644
  function repoPatterns(cwd) {
19591
- return repoHeavyCommands(gitTop(cwd) ?? path24.resolve(cwd));
20645
+ return repoHeavyCommands(gitTop(cwd) ?? path25.resolve(cwd));
19592
20646
  }
19593
20647
  function heavyLines(lines, options) {
19594
20648
  const found = [];
@@ -19666,12 +20720,12 @@ function heavyPlan(command, options) {
19666
20720
  }
19667
20721
  function workitOnPath2(env = process.env) {
19668
20722
  const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
19669
- for (const dir of (env.PATH ?? "").split(path24.delimiter))
20723
+ for (const dir of (env.PATH ?? "").split(path25.delimiter))
19670
20724
  for (const name of names) {
19671
20725
  if (!dir)
19672
20726
  continue;
19673
20727
  try {
19674
- fs19.accessSync(path24.join(dir, name), fs19.constants.X_OK);
20728
+ fs19.accessSync(path25.join(dir, name), fs19.constants.X_OK);
19675
20729
  return true;
19676
20730
  } catch {}
19677
20731
  }
@@ -19690,20 +20744,20 @@ function leaseRewrite(command) {
19690
20744
  // packages/workit-core/src/core/heavy-words.ts
19691
20745
  import fs20 from "node:fs";
19692
20746
  import os8 from "node:os";
19693
- import path25 from "node:path";
20747
+ import path26 from "node:path";
19694
20748
  var HEAVY_WORDS = new Set(("npm npx pnpm pnpx yarn bun bunx deno corepack make gmake docker podman nerdctl " + "python python3 py uv poetry pdm pipenv hatch rye bundle rake nx " + "jest vitest mocha ava tap karma jasmine nyc c8 tsc vue-tsc svelte-check eslint stylelint " + "tslint webpack webpack-cli rollup parcel tsup vite next nuxt nuxi astro ng react-scripts " + "playwright cypress wdio testcafe turbo lerna sonar-scanner sonarless sonarless-mcp go " + "golangci-lint gotestsum staticcheck cargo mvn mvnw gradle gradlew ant sbt bazel bazelisk " + "buck2 dotnet msbuild pytest py.test tox nox mypy pyright pylint coverage maturin pip pip3 " + "rspec rails phpunit pest phpstan psalm composer mix swift xcodebuild flutter dart pod cmake " + "ninja meson ctest scons docker-compose buildah earthly").split(" "));
19695
20749
  var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
19696
20750
  var stateDir2 = (env) => {
19697
20751
  if (env.WORKFLOW_TOOLKIT_STATE)
19698
20752
  return env.WORKFLOW_TOOLKIT_STATE;
19699
20753
  if (env.XDG_STATE_HOME)
19700
- return path25.join(env.XDG_STATE_HOME, "workit");
20754
+ return path26.join(env.XDG_STATE_HOME, "workit");
19701
20755
  const home = env.HOME || os8.homedir();
19702
20756
  if (process.platform === "darwin")
19703
- return path25.join(home, "Library", "Application Support", "workit");
20757
+ return path26.join(home, "Library", "Application Support", "workit");
19704
20758
  if (process.platform === "win32")
19705
- return path25.join(env.LOCALAPPDATA ?? path25.join(home, "AppData", "Local"), "workit");
19706
- return path25.join(home, ".local", "state", "workit");
20759
+ return path26.join(env.LOCALAPPDATA ?? path26.join(home, "AppData", "Local"), "workit");
20760
+ return path26.join(home, ".local", "state", "workit");
19707
20761
  };
19708
20762
  var readJson2 = (file) => {
19709
20763
  try {
@@ -19714,15 +20768,15 @@ var readJson2 = (file) => {
19714
20768
  };
19715
20769
  var patternWords = (cwd, env) => {
19716
20770
  const patterns = [];
19717
- const machine = readJson2(path25.join(stateDir2(env), "heavy-commands.json"));
20771
+ const machine = readJson2(path26.join(stateDir2(env), "heavy-commands.json"));
19718
20772
  if (Array.isArray(machine?.patterns))
19719
20773
  patterns.push(...machine.patterns);
19720
- for (let dir = path25.resolve(cwd);; dir = path25.dirname(dir)) {
19721
- if (fs20.existsSync(path25.join(dir, ".git"))) {
20774
+ for (let dir = path26.resolve(cwd);; dir = path26.dirname(dir)) {
20775
+ if (fs20.existsSync(path26.join(dir, ".git"))) {
19722
20776
  patterns.push(...repoHeavyCommands(dir));
19723
20777
  break;
19724
20778
  }
19725
- if (path25.dirname(dir) === dir)
20779
+ if (path26.dirname(dir) === dir)
19726
20780
  break;
19727
20781
  }
19728
20782
  const words = new Set;
@@ -19792,16 +20846,16 @@ var splitDenial = (plan) => ({
19792
20846
  unblock: plan.unblock
19793
20847
  });
19794
20848
  // packages/workit-core/src/hooks/write-gate.ts
19795
- import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
20849
+ import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
19796
20850
  import os10 from "node:os";
19797
- import path27 from "node:path";
20851
+ import path28 from "node:path";
19798
20852
 
19799
20853
  // packages/workit-core/src/core/live-session.ts
19800
20854
  import { spawnSync as spawnSync13 } from "node:child_process";
19801
20855
  import { createHash as createHash16 } from "node:crypto";
19802
20856
  import fs21 from "node:fs";
19803
20857
  import os9 from "node:os";
19804
- import path26 from "node:path";
20858
+ import path27 from "node:path";
19805
20859
  var LIVE_SESSION_TTL_MS = 15 * 60000;
19806
20860
  var HEARTBEAT_EVERY_MS = 30000;
19807
20861
  var liveProbe = {
@@ -19875,15 +20929,15 @@ var liveOwner = (input, transport, env = process.env) => {
19875
20929
  };
19876
20930
  };
19877
20931
  var checkoutOf = (target) => {
19878
- let dir = path26.resolve(target);
19879
- while (!fs21.existsSync(dir) && path26.dirname(dir) !== dir)
19880
- dir = path26.dirname(dir);
20932
+ let dir = path27.resolve(target);
20933
+ while (!fs21.existsSync(dir) && path27.dirname(dir) !== dir)
20934
+ dir = path27.dirname(dir);
19881
20935
  if (fs21.existsSync(dir) && !fs21.statSync(dir).isDirectory())
19882
- dir = path26.dirname(dir);
19883
- const parts = dir.split(path26.sep);
20936
+ dir = path27.dirname(dir);
20937
+ const parts = dir.split(path27.sep);
19884
20938
  const dotGit = parts.lastIndexOf(".git");
19885
20939
  if (dotGit > 0)
19886
- dir = parts.slice(0, dotGit).join(path26.sep) || path26.sep;
20940
+ dir = parts.slice(0, dotGit).join(path27.sep) || path27.sep;
19887
20941
  const location = resolveStore(dir);
19888
20942
  if (location instanceof Error || !location.shared || !location.top)
19889
20943
  return null;
@@ -19893,9 +20947,9 @@ var checkoutOf = (target) => {
19893
20947
  } catch {}
19894
20948
  return top;
19895
20949
  };
19896
- var markerFile2 = (top) => path26.join(resolveStateDir(), "live-sessions", `${createHash16("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
20950
+ var markerFile2 = (top) => path27.join(resolveStateDir(), "live-sessions", `${createHash16("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
19897
20951
  var checkoutKey = (top, platform = process.platform) => {
19898
- let key = path26.resolve(top);
20952
+ let key = path27.resolve(top);
19899
20953
  try {
19900
20954
  key = fs21.realpathSync.native(key);
19901
20955
  } catch {}
@@ -19990,7 +21044,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
19990
21044
  checkout,
19991
21045
  heartbeatAt: now.toISOString()
19992
21046
  };
19993
- fs21.mkdirSync(path26.dirname(file), { recursive: true });
21047
+ fs21.mkdirSync(path27.dirname(file), { recursive: true });
19994
21048
  const temp = `${file}.${process.pid}.tmp`;
19995
21049
  fs21.writeFileSync(temp, JSON.stringify(marker));
19996
21050
  fs21.renameSync(temp, file);
@@ -20002,22 +21056,22 @@ var NONE3 = { kind: "none" };
20002
21056
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
20003
21057
  var canonical2 = (file) => {
20004
21058
  let rest = "";
20005
- for (let dir = path27.resolve(file);; dir = path27.dirname(dir)) {
21059
+ for (let dir = path28.resolve(file);; dir = path28.dirname(dir)) {
20006
21060
  try {
20007
- return path27.join(realpathSync5.native(dir), rest);
21061
+ return path28.join(realpathSync5.native(dir), rest);
20008
21062
  } catch {
20009
- if (path27.dirname(dir) === dir)
20010
- return path27.resolve(file);
20011
- rest = rest ? path27.join(path27.basename(dir), rest) : path27.basename(dir);
21063
+ if (path28.dirname(dir) === dir)
21064
+ return path28.resolve(file);
21065
+ rest = rest ? path28.join(path28.basename(dir), rest) : path28.basename(dir);
20012
21066
  }
20013
21067
  }
20014
21068
  };
20015
21069
  var exempt = (cwd, root, file, plans) => {
20016
21070
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
20017
- const relative = path27.relative(fold(canonical2(root)), fold(canonical2(path27.resolve(cwd, file))));
20018
- if (relative.startsWith("..") || path27.isAbsolute(relative))
21071
+ const relative = path28.relative(fold(canonical2(root)), fold(canonical2(path28.resolve(cwd, file))));
21072
+ if (relative.startsWith("..") || path28.isAbsolute(relative))
20019
21073
  return true;
20020
- const posix = relative.split(path27.sep).join("/");
21074
+ const posix = relative.split(path28.sep).join("/");
20021
21075
  const segments = posix.split("/");
20022
21076
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
20023
21077
  };
@@ -20194,10 +21248,10 @@ var linkSources = (args, at) => {
20194
21248
  const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
20195
21249
  const target = dest === undefined ? null : at(dest);
20196
21250
  const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
20197
- const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync7(target) || /[\\/]$/.test(dest ?? "") ? target : path27.dirname(target);
21251
+ const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync8(target) || /[\\/]$/.test(dest ?? "") ? target : path28.dirname(target);
20198
21252
  return sources.flatMap((source) => {
20199
- if (symbolic && base !== null && !path27.isAbsolute(homeExpanded(source)))
20200
- return [path27.resolve(base, source)];
21253
+ if (symbolic && base !== null && !path28.isAbsolute(homeExpanded(source)))
21254
+ return [path28.resolve(base, source)];
20201
21255
  const resolved = at(source);
20202
21256
  return resolved ? [resolved] : [];
20203
21257
  });
@@ -20226,7 +21280,7 @@ var shellWrites = (command, depth = 0) => {
20226
21280
  return { writes: true, targets: unknown ? null : targets };
20227
21281
  };
20228
21282
  var cache2 = new Map;
20229
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path27.resolve(root, file)) ?? "-").join("\x00");
21283
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path28.resolve(root, file)) ?? "-").join("\x00");
20230
21284
  var gateFor = (cwd) => {
20231
21285
  const store = new TaskStore(cwd);
20232
21286
  const listed = store.listTaskIndex();
@@ -20305,13 +21359,13 @@ var gitMutationDirs = (cwd, command, dialect) => {
20305
21359
  return [];
20306
21360
  if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
20307
21361
  return [];
20308
- return [path27.resolve(cwd, invocation.dir ?? ".")];
21362
+ return [path28.resolve(cwd, invocation.dir ?? ".")];
20309
21363
  });
20310
21364
  };
20311
21365
  var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
20312
21366
  var PUSHD2 = new Set(["pushd", "push-location"]);
20313
21367
  var POPD2 = new Set(["popd", "pop-location"]);
20314
- var UNKNOWN_DIR = path27.resolve(path27.parse(process.cwd()).root, ".workit-unknown-dir");
21368
+ var UNKNOWN_DIR = path28.resolve(path28.parse(process.cwd()).root, ".workit-unknown-dir");
20315
21369
  var cdInto = (dir, words, strict = false) => {
20316
21370
  const lost = strict ? UNKNOWN_DIR : undefined;
20317
21371
  const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
@@ -20320,7 +21374,7 @@ var cdInto = (dir, words, strict = false) => {
20320
21374
  return lost;
20321
21375
  if (dir === undefined)
20322
21376
  return lost;
20323
- return target === undefined ? os10.homedir() : path27.resolve(dir, target);
21377
+ return target === undefined ? os10.homedir() : path28.resolve(dir, target);
20324
21378
  };
20325
21379
  var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
20326
21380
  var homeExpanded = (file) => {
@@ -20334,7 +21388,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
20334
21388
  const groups = [];
20335
21389
  const at = (file) => {
20336
21390
  const home = homeExpanded(file);
20337
- return path27.isAbsolute(home) ? home : dir === undefined ? null : path27.resolve(dir, home);
21391
+ return path28.isAbsolute(home) ? home : dir === undefined ? null : path28.resolve(dir, home);
20338
21392
  };
20339
21393
  for (const segment of segmentsOf(command, dialect)) {
20340
21394
  if (segment.group === "open") {
@@ -20365,7 +21419,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
20365
21419
  }
20366
21420
  const inner = depth < 4 ? wrappedScript(words) ?? (strict ? shellScript2(words) : null) : null;
20367
21421
  if (inner !== null && dir !== undefined)
20368
- targets.push(...segmentTargets(dir, inner, "posix", depth + 1, strict));
21422
+ targets.push(...segmentTargets(workitRunDir(dir, words), inner, "posix", depth + 1, strict));
20369
21423
  if (strict && dir !== undefined) {
20370
21424
  const runIn = dir;
20371
21425
  for (const sub of depth < 4 ? segment.subs ?? [] : [])
@@ -20374,7 +21428,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
20374
21428
  for (const file of opaque ?? []) {
20375
21429
  const resolved = at(file);
20376
21430
  if (resolved)
20377
- targets.push(file === "." ? path27.join(resolved, DIR_TARGET) : resolved);
21431
+ targets.push(file === "." ? path28.join(resolved, DIR_TARGET) : resolved);
20378
21432
  }
20379
21433
  const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
20380
21434
  if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
@@ -20389,7 +21443,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
20389
21443
  const edits = editsOf(segment.words);
20390
21444
  if (edits === null) {
20391
21445
  if (dir !== undefined)
20392
- targets.push(path27.join(dir, DIR_TARGET));
21446
+ targets.push(path28.join(dir, DIR_TARGET));
20393
21447
  } else
20394
21448
  for (const file of edits) {
20395
21449
  const resolved = at(file);
@@ -20400,20 +21454,28 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
20400
21454
  for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
20401
21455
  const { at: runsIn, args } = workitCwd(dir, invocation);
20402
21456
  if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
20403
- targets.push(path27.join(runsIn, DIR_TARGET));
21457
+ targets.push(path28.join(runsIn, DIR_TARGET));
20404
21458
  }
20405
21459
  }
20406
21460
  return targets;
20407
21461
  };
21462
+ var workitRunDir = (dir, words) => {
21463
+ for (const invocation of workitInvocations(words.map(quoteWord2).join(" "), "posix")) {
21464
+ const { at, args } = workitCwd(dir, invocation);
21465
+ if (args[0] === "check" || args[0] === "lock" && args[1] === "run")
21466
+ return at;
21467
+ }
21468
+ return dir;
21469
+ };
20408
21470
  var workitCwd = (dir, args) => {
20409
21471
  const { rest, cwd } = skipWorkitGlobals(args);
20410
- return { at: cwd === null ? dir : path27.resolve(dir, homeExpanded(cwd)), args: rest };
21472
+ return { at: cwd === null ? dir : path28.resolve(dir, homeExpanded(cwd)), args: rest };
20411
21473
  };
20412
21474
  var shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
20413
21475
  try {
20414
21476
  return [
20415
21477
  ...segmentTargets(cwd, command, dialect, 0, strict),
20416
- ...gitMutationDirs(cwd, command, dialect).map((dir) => path27.join(dir, DIR_TARGET))
21478
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path28.join(dir, DIR_TARGET))
20417
21479
  ];
20418
21480
  } catch {
20419
21481
  return [];
@@ -20450,7 +21512,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
20450
21512
  var guarded = (targets) => {
20451
21513
  const seen = new Set;
20452
21514
  return targets.filter((target) => {
20453
- const dir = path27.dirname(target);
21515
+ const dir = path28.dirname(target);
20454
21516
  if (seen.has(dir))
20455
21517
  return false;
20456
21518
  seen.add(dir);
@@ -20471,7 +21533,7 @@ var claimAll = (owner, targets, deps) => {
20471
21533
  for (const target of guarded(targets))
20472
21534
  claimLiveSession(target, owner, deps);
20473
21535
  };
20474
- var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path27.resolve(cwd, file)) : [path27.join(cwd, DIR_TARGET)];
21536
+ var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path28.resolve(cwd, file)) : [path28.join(cwd, DIR_TARGET)];
20475
21537
  var issuedTarget = (cwd, targets) => {
20476
21538
  const state = resolveStateDir();
20477
21539
  const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
@@ -20479,7 +21541,7 @@ var issuedTarget = (cwd, targets) => {
20479
21541
  };
20480
21542
  var underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
20481
21543
  const win = platform === "win32";
20482
- const p = win ? path27.win32 : path27.posix;
21544
+ const p = win ? path28.win32 : path28.posix;
20483
21545
  const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
20484
21546
  const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
20485
21547
  const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
@@ -20570,6 +21632,24 @@ function beforeWriteGate(cwd, paths) {
20570
21632
  }
20571
21633
  // packages/workit-core/src/hooks/stop.ts
20572
21634
  import { spawnSync as spawnSync14 } from "node:child_process";
21635
+
21636
+ // packages/workit-core/src/core/policy/current-judgment.ts
21637
+ function currentJudgment(cwd) {
21638
+ try {
21639
+ const store = new TaskStore(cwd);
21640
+ const listed = store.listTaskIndex();
21641
+ const key = store.currentKey();
21642
+ if (!listed.ok || !key.ok)
21643
+ return null;
21644
+ const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
21645
+ const task = entry ? store.readTask(entry.id) : null;
21646
+ return task?.ok === true ? latestJudgment(task.data) : null;
21647
+ } catch {
21648
+ return null;
21649
+ }
21650
+ }
21651
+
21652
+ // packages/workit-core/src/hooks/stop.ts
20573
21653
  var NONE4 = { kind: "none" };
20574
21654
  var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
20575
21655
  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;
@@ -20662,29 +21742,32 @@ var redChecks = (rows, pr, head, endpoint) => {
20662
21742
  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)" : ""}.`
20663
21743
  };
20664
21744
  };
20665
- var taskJudgment = (cwd) => {
20666
- try {
20667
- const store = new TaskStore(cwd);
20668
- const listed = store.listTaskIndex();
20669
- const key = store.currentKey();
20670
- if (!listed.ok || !key.ok)
20671
- return null;
20672
- const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
20673
- const task = entry ? store.readTask(entry.id) : null;
20674
- return task?.ok === true ? latestJudgment(task.data) : null;
20675
- } catch {
20676
- return null;
20677
- }
21745
+ var prBase = (rows) => {
21746
+ const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
21747
+ return typeof seen?.base === "string" ? seen.base : null;
20678
21748
  };
20679
- var unverified = (cwd, branch, all, descriptor, verification) => {
21749
+ var unverified = (cwd, branch, all, descriptor, verification, head) => {
20680
21750
  const check = checkVerdicts(cwd, branch, all);
20681
21751
  if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
20682
21752
  return null;
20683
- const judgment = taskJudgment(cwd);
21753
+ const judgment = currentJudgment(cwd);
20684
21754
  const high = judgment?.riskTier === "high";
20685
- if (judgment && !judgment.behaviorChange && !high)
20686
- return null;
20687
- const independent = verification === "independent" || high;
21755
+ let independent;
21756
+ if (verification === "proportional") {
21757
+ const lane = branchLane(cwd, {
21758
+ base: prBase(all.filter((row) => row.branch === branch)),
21759
+ head,
21760
+ judgment
21761
+ });
21762
+ const need = laneNeed(lane.lane);
21763
+ if (need === "none")
21764
+ return null;
21765
+ independent = need === "non-author";
21766
+ } else {
21767
+ if (judgment && !judgment.behaviorChange && !high)
21768
+ return null;
21769
+ independent = verification === "independent" || high;
21770
+ }
20688
21771
  if (!independent && check.review === "self-reviewed")
20689
21772
  return null;
20690
21773
  const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
@@ -20731,7 +21814,7 @@ function stopObligation(input, descriptor) {
20731
21814
  }
20732
21815
  if (!delivered || endpoint !== "green" && endpoint !== "merged")
20733
21816
  return null;
20734
- const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
21817
+ const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
20735
21818
  return left ? { ...left, head } : null;
20736
21819
  } catch {
20737
21820
  return null;
@@ -20992,13 +22075,13 @@ ${line}` };
20992
22075
  return decision;
20993
22076
  };
20994
22077
  // packages/workit-core/src/hooks/hosts/fields.ts
20995
- import { existsSync as existsSync8, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
20996
- import path28 from "node:path";
22078
+ import { existsSync as existsSync9, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
22079
+ import path29 from "node:path";
20997
22080
  var isRecord6 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
20998
22081
  var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
20999
22082
  var optionalText = (value) => typeof value === "string" ? value : null;
21000
22083
  var existingDirectory = (value) => {
21001
- if (!nonEmpty2(value) || !path28.isAbsolute(value) || !existsSync8(value))
22084
+ if (!nonEmpty2(value) || !path29.isAbsolute(value) || !existsSync9(value))
21002
22085
  return null;
21003
22086
  try {
21004
22087
  return statSync2(value).isDirectory() ? realpathSync6(value) : null;
@@ -21066,9 +22149,9 @@ var writeTargets = (input) => {
21066
22149
  };
21067
22150
  // packages/workit-core/src/hooks/standing.ts
21068
22151
  import fs22 from "node:fs";
21069
- import path29 from "node:path";
22152
+ import path30 from "node:path";
21070
22153
  var readPlans = (store) => {
21071
- const dir = path29.join(store, "fanouts");
22154
+ const dir = path30.join(store, "fanouts");
21072
22155
  let names;
21073
22156
  try {
21074
22157
  names = fs22.readdirSync(dir).filter((name) => name.endsWith(".json"));
@@ -21078,7 +22161,7 @@ var readPlans = (store) => {
21078
22161
  const plans = [];
21079
22162
  for (const name of names) {
21080
22163
  try {
21081
- const raw = JSON.parse(fs22.readFileSync(path29.join(dir, name), "utf8"));
22164
+ const raw = JSON.parse(fs22.readFileSync(path30.join(dir, name), "utf8"));
21082
22165
  if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
21083
22166
  continue;
21084
22167
  const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
@@ -21168,8 +22251,13 @@ var standingDelta = (input) => {
21168
22251
  };
21169
22252
  var sinceStart = new Map;
21170
22253
  // packages/workit-core/src/hooks/cwd-normalize.ts
21171
- var COMPOUND = /^\s*cd\s+([A-Za-z0-9_./@%+:,=-]+|'[^'$`\\\n]*'|"[^"$`\\!\n]*")\s*&&\s*(workit(?:\s[\s\S]*)?)$/u;
22254
+ var DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
22255
+ var COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
22256
+ var GLOBALS_FIRST = new RegExp(String.raw`^(\s*workit)((?:\s+(?:--json|--cwd(?:=|\s+)(?:${DIR})))+)\s+([a-z][a-z-]*)(?=\s|$)`, "u");
22257
+ var VERB_FIRST = /^((?:--json\s+)*)([a-z][a-z-]*)(?=\s|$)/u;
22258
+ var VERBS = new Set(WORKIT_ALLOWED_VERBS);
21172
22259
  var FILTERS = new Set(["tail", "head", "grep"]);
22260
+ var clean = (s) => !s.unanalyzable && !s.group && !s.subs?.length && !s.stdin?.length && !s.redirects.length;
21173
22261
  var stdinOnly = (words) => {
21174
22262
  const [name, ...args] = words;
21175
22263
  const operands = args.filter((w) => !w.startsWith("-"));
@@ -21191,53 +22279,67 @@ var cwdRewrite = (command) => {
21191
22279
  return null;
21192
22280
  if (head.words.some((w) => w === "--cwd" || w.startsWith("--cwd=")))
21193
22281
  return null;
21194
- const clean = (s) => !s.unanalyzable && !s.group && !s.subs?.length && !s.stdin?.length && !s.redirects.length;
21195
22282
  if (!segments.every(clean))
21196
22283
  return null;
21197
22284
  if (!segments.slice(0, -1).every((s) => s.pipe))
21198
22285
  return null;
21199
22286
  if (!filters.every((s) => FILTERS.has(s.words[0] ?? "") && stdinOnly(s.words)))
21200
22287
  return null;
21201
- return `workit --cwd ${dir} ${rest.slice("workit".length).trimStart()}`.trimEnd();
22288
+ const args = rest.slice("workit".length).trimStart();
22289
+ const verb = VERB_FIRST.exec(args);
22290
+ if (!verb?.[2] || !VERBS.has(verb[2]))
22291
+ return `workit --cwd ${dir} ${args}`.trimEnd();
22292
+ const json = verb[1] ? " --json" : "";
22293
+ return `workit ${verb[2]} --cwd ${dir}${json}${args.slice(verb[0].length)}`.trimEnd();
22294
+ };
22295
+ var verbFirst = (command) => {
22296
+ const match = GLOBALS_FIRST.exec(command);
22297
+ const [whole, lead, globals, verb] = match ?? [];
22298
+ if (!whole || !verb || !VERBS.has(verb) || !globals?.includes("--cwd"))
22299
+ return null;
22300
+ const segments = segmentsOf(command);
22301
+ if (segments.length !== 1 || !segments.every(clean))
22302
+ return null;
22303
+ return `${lead} ${verb}${globals}${command.slice(whole.length)}`;
21202
22304
  };
21203
22305
 
21204
22306
  // packages/workit-core/src/hooks/worktree-confine.ts
21205
22307
  import { createHash as createHash17 } from "node:crypto";
21206
22308
  import fs23 from "node:fs";
21207
- import path30 from "node:path";
22309
+ import path31 from "node:path";
21208
22310
  var FANOUT_TOKEN_FILE = "workit-fanout-token";
21209
22311
  var NONE5 = { kind: "none" };
21210
22312
  var CASE_INSENSITIVE2 = process.platform === "darwin" || process.platform === "win32";
21211
22313
  var canonical3 = (file) => {
21212
22314
  let rest = "";
21213
- for (let dir = path30.resolve(file);; dir = path30.dirname(dir)) {
22315
+ for (let dir = path31.resolve(file);; dir = path31.dirname(dir)) {
21214
22316
  try {
21215
- return path30.join(fs23.realpathSync.native(dir), rest);
22317
+ return path31.join(fs23.realpathSync.native(dir), rest);
21216
22318
  } catch {
21217
- if (path30.dirname(dir) === dir)
21218
- return path30.resolve(file);
21219
- rest = rest ? path30.join(path30.basename(dir), rest) : path30.basename(dir);
22319
+ if (path31.dirname(dir) === dir)
22320
+ return path31.resolve(file);
22321
+ rest = rest ? path31.join(path31.basename(dir), rest) : path31.basename(dir);
21220
22322
  }
21221
22323
  }
21222
22324
  };
21223
22325
  var within = (child, parent) => {
21224
22326
  const fold = (value) => CASE_INSENSITIVE2 ? value.toLowerCase() : value;
21225
- const relative = path30.relative(fold(parent), fold(child));
21226
- return relative === "" || !relative.startsWith("..") && !path30.isAbsolute(relative);
22327
+ const relative = path31.relative(fold(parent), fold(child));
22328
+ return relative === "" || !relative.startsWith("..") && !path31.isAbsolute(relative);
21227
22329
  };
21228
22330
  var checkoutOf2 = (file) => {
21229
- for (let dir = canonical3(file);; dir = path30.dirname(dir)) {
21230
- const dotGit = path30.join(dir, ".git");
22331
+ for (let dir = canonical3(file);; dir = path31.dirname(dir)) {
22332
+ const dotGit = path31.join(dir, ".git");
21231
22333
  if (fs23.existsSync(dotGit)) {
21232
22334
  if (!fs23.statSync(dotGit).isFile())
21233
22335
  return { root: dir, workit: false };
21234
22336
  const admin = /^gitdir:\s*(.+)$/mu.exec(fs23.readFileSync(dotGit, "utf8"))?.[1]?.trim();
21235
22337
  return {
21236
22338
  root: dir,
21237
- workit: Boolean(admin) && fs23.existsSync(path30.join(path30.resolve(dir, admin), FANOUT_TOKEN_FILE))
22339
+ workit: Boolean(admin) && fs23.existsSync(path31.join(path31.resolve(dir, admin), FANOUT_TOKEN_FILE))
21238
22340
  };
21239
22341
  }
21240
- if (path30.dirname(dir) === dir)
22342
+ if (path31.dirname(dir) === dir)
21241
22343
  return null;
21242
22344
  }
21243
22345
  };
@@ -21249,8 +22351,8 @@ var workitWorktreeOf = (file) => {
21249
22351
  return null;
21250
22352
  }
21251
22353
  };
21252
- var bindingDir = () => path30.join(resolveStateDir(), "worktree-bind");
21253
- var bindingFile = (session, agentId) => path30.join(bindingDir(), `${createHash17("sha256").update(`${session}\x00${agentId}`).digest("hex").slice(0, 32)}.json`);
22354
+ var bindingDir = () => path31.join(resolveStateDir(), "worktree-bind");
22355
+ var bindingFile = (session, agentId) => path31.join(bindingDir(), `${createHash17("sha256").update(`${session}\x00${agentId}`).digest("hex").slice(0, 32)}.json`);
21254
22356
  var readBinding = (file) => {
21255
22357
  try {
21256
22358
  const value = JSON.parse(fs23.readFileSync(file, "utf8"));
@@ -21275,7 +22377,7 @@ var bind = (input, worktree) => {
21275
22377
  agentId,
21276
22378
  at: new Date().toISOString()
21277
22379
  };
21278
- fs23.mkdirSync(path30.dirname(file), { recursive: true });
22380
+ fs23.mkdirSync(path31.dirname(file), { recursive: true });
21279
22381
  fs23.writeFileSync(file, JSON.stringify(binding));
21280
22382
  try {
21281
22383
  if (inWorkitWorkspace(input.cwd))
@@ -21294,27 +22396,27 @@ var hostIsolated = (cwd, agentId) => {
21294
22396
  const checkout = checkoutOf2(cwd);
21295
22397
  if (!checkout)
21296
22398
  return false;
21297
- const dotGit = path30.join(checkout.root, ".git");
22399
+ const dotGit = path31.join(checkout.root, ".git");
21298
22400
  if (!fs23.statSync(dotGit).isFile())
21299
22401
  return false;
21300
22402
  const admin = /^gitdir:\s*(.+)$/mu.exec(fs23.readFileSync(dotGit, "utf8"))?.[1]?.trim();
21301
22403
  if (!admin)
21302
22404
  return false;
21303
- const adminDir = path30.resolve(checkout.root, admin);
21304
- const back = fs23.readFileSync(path30.join(adminDir, "gitdir"), "utf8").trim();
21305
- if (path30.relative(canonical3(path30.resolve(adminDir, back)), canonical3(dotGit)) !== "")
22405
+ const adminDir = path31.resolve(checkout.root, admin);
22406
+ const back = fs23.readFileSync(path31.join(adminDir, "gitdir"), "utf8").trim();
22407
+ if (path31.relative(canonical3(path31.resolve(adminDir, back)), canonical3(dotGit)) !== "")
21306
22408
  return false;
21307
- const common = path30.join(adminDir, "commondir");
21308
- const gitDir = fs23.existsSync(common) ? path30.resolve(adminDir, fs23.readFileSync(common, "utf8").trim()) : adminDir;
21309
- const expected = path30.join(path30.dirname(canonical3(gitDir)), ".claude", "worktrees", `agent-${agentId}`);
21310
- return path30.relative(canonical3(expected), checkout.root) === "";
22409
+ const common = path31.join(adminDir, "commondir");
22410
+ const gitDir = fs23.existsSync(common) ? path31.resolve(adminDir, fs23.readFileSync(common, "utf8").trim()) : adminDir;
22411
+ const expected = path31.join(path31.dirname(canonical3(gitDir)), ".claude", "worktrees", `agent-${agentId}`);
22412
+ return path31.relative(canonical3(expected), checkout.root) === "";
21311
22413
  } catch {
21312
22414
  return false;
21313
22415
  }
21314
22416
  };
21315
- var shown = (target) => path30.basename(target) === ".workit-live-target" ? path30.dirname(target) : target;
22417
+ var shown = (target) => path31.basename(target) === ".workit-live-target" ? path31.dirname(target) : target;
21316
22418
  var unboundDeny = (input, target) => {
21317
- 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.";
22419
+ 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 git --cwd <worktree> commit -m … -- <paths>`, `workit check --cwd <worktree> <name>`). If your brief names no worktree, stop and report that to the lead.";
21318
22420
  return {
21319
22421
  kind: "deny",
21320
22422
  reason: `workit worktree confinement: implementer ${input.session.agentId} is not working in a Workit worktree yet, and ${shown(target)} is outside every worktree \`workit fanout worktree create\` made, so it would change the lead's checkout. ${unblock}`,
@@ -21322,7 +22424,7 @@ var unboundDeny = (input, target) => {
21322
22424
  };
21323
22425
  };
21324
22426
  var outsideDeny = (input, worktree, target) => {
21325
- const unblock = `Edit only under ${worktree} and run git there, one verb per call: \`workit --cwd ${worktree} git commit -m … -- <paths>\`, \`workit --cwd ${worktree} check <name>\` (or \`git -C ${worktree} …\`); a command whose writes Workit cannot see is judged by the directory it runs in. Anything outside it is a follow-up for your report.`;
22427
+ const unblock = `Edit only under ${worktree} and run git there, one verb per call: \`workit git --cwd ${worktree} commit -m … -- <paths>\`, \`workit check --cwd ${worktree} <name>\` (or \`git -C ${worktree} …\`); a command whose writes Workit cannot see is judged by the directory it runs in. Anything outside it is a follow-up for your report.`;
21326
22428
  return {
21327
22429
  kind: "deny",
21328
22430
  reason: `workit worktree confinement: implementer ${input.session.agentId} is bound to the worktree ${worktree}, and ${shown(target)} is outside it. ${unblock}`,
@@ -21331,7 +22433,7 @@ var outsideDeny = (input, worktree, target) => {
21331
22433
  };
21332
22434
  var unknownDirDeny = (input, worktree) => {
21333
22435
  const wt = worktree ?? "<worktree>";
21334
- const unblock = `Workit cannot resolve \`cd -\`, \`cd "$X"\` or \`popd\`: write by absolute path, \`git -C ${wt}\` or \`workit --cwd ${wt}\`.`;
22436
+ const unblock = `Workit cannot resolve \`cd -\`, \`cd "$X"\` or \`popd\`: write by absolute path, \`git -C ${wt}\` or \`workit <verb> --cwd ${wt}\`.`;
21335
22437
  return {
21336
22438
  kind: "deny",
21337
22439
  reason: `workit worktree confinement: implementer ${input.session.agentId} writes after an unknown directory change. ${unblock}`,
@@ -21342,7 +22444,7 @@ function confineImplementer(input, targets) {
21342
22444
  try {
21343
22445
  if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
21344
22446
  return NONE5;
21345
- if (targets.some((target) => within(path30.resolve(target), UNKNOWN_DIR)))
22447
+ if (targets.some((target) => within(path31.resolve(target), UNKNOWN_DIR)))
21346
22448
  return unknownDirDeny(input, boundWorktree(input));
21347
22449
  const bindings = canonical3(bindingDir());
21348
22450
  const judged = targets.map((named) => ({ named, real: canonical3(named) })).filter(({ real }) => within(real, bindings) || checkoutOf2(real) !== null);
@@ -21489,7 +22591,7 @@ var subagentStartText = (input, descriptor, event) => {
21489
22591
  return `${isolatedGit(input.cwd)}
21490
22592
  ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
21491
22593
  if (role === "implementer")
21492
- return `Workit observed ${who} as an implementer: it edits and commits only in its own git worktree, the one its brief names (the lead makes it with \`workit fanout worktree create <slice>\` before spawning), never in the lead's checkout, within its brief's scope. Workit binds it to the first Workit worktree it changes: from then on an edit or a mutating git command outside that worktree is denied, and before then so is one outside every Workit worktree. Run Workit against the worktree one verb per call, never \`cd … &&\` chains or \`$VAR\` paths: \`workit --cwd <worktree> git commit -m "<message>" -- <paths>\`, \`workit --cwd <worktree> check <name>\`. The worktree is on the brief's branch once the lead made it; otherwise switch with \`workit --cwd <worktree> git branch <branch> --base <base>\` (or \`--kind <kind> --slug <slug>\`, which picks the base); branch policy hooks still deny protected or non-compliant branches, raw git included.${guide ? ` ${guide}` : " (e.g. feature/<slug>)"} Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
22594
+ return `Workit observed ${who} as an implementer: it edits and commits only in its own git worktree, the one its brief names (the lead makes it with \`workit fanout worktree create <slice>\` before spawning), never in the lead's checkout, within its brief's scope. Workit binds it to the first Workit worktree it changes: from then on an edit or a mutating git command outside that worktree is denied, and before then so is one outside every Workit worktree. Run Workit against the worktree one verb per call, never \`cd … &&\` chains or \`$VAR\` paths: \`workit git --cwd <worktree> commit -m "<message>" -- <paths>\`, \`workit check --cwd <worktree> <name>\` (verb first, so host allow rules match). The worktree is on the brief's branch once the lead made it; otherwise switch with \`workit git --cwd <worktree> branch <branch> --base <base>\` (or \`--kind <kind> --slug <slug>\`, which picks the base); branch policy hooks still deny protected or non-compliant branches, raw git included.${guide ? ` ${guide}` : " (e.g. feature/<slug>)"} Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
21493
22595
  const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
21494
22596
  if (role !== "judge" || !event.agentId)
21495
22597
  return readOnly;
@@ -21506,7 +22608,7 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
21506
22608
  var PRE_TOOL = new Set(["shell.pre", "write.pre", "tool.pre"]);
21507
22609
  function handleHook(input, deps) {
21508
22610
  const decision = decide(input, deps);
21509
- if (deps.descriptor.transport !== "hook-process" || !PRE_TOOL.has(input.event.kind) || decision.kind === "deny")
22611
+ if (deps.descriptor.transport !== "hook-process" || !PRE_TOOL.has(input.event.kind) || decision.kind === "deny" || decision.kind === "ask")
21510
22612
  return decision;
21511
22613
  const delta = standingDelta(input);
21512
22614
  if (!delta)
@@ -21573,9 +22675,12 @@ function decide(input, deps) {
21573
22675
  if (plan.kind === "rewrite" && !rewrites && canDeny)
21574
22676
  return heavyDenial(event.command, plan);
21575
22677
  const lease = plan.kind === "rewrite" && rewrites ? plan : null;
21576
- const cwdCommand = !lease && rewrites && input.host === "claude_code" ? cwdRewrite(event.command) : null;
22678
+ const cwdCommand = !lease && rewrites && input.host === "claude_code" ? cwdRewrite(event.command) ?? verbFirst(event.command) : null;
21577
22679
  const aside = plan.kind === "passthrough" ? plan.note : null;
21578
22680
  noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
22681
+ const bypass = !lease && input.host === "claude_code" ? workitBypassFlag(event.command, event.dialect) : null;
22682
+ if (bypass)
22683
+ return { kind: "ask", reason: bypass };
21579
22684
  const rewrittenTo = lease?.command ?? cwdCommand;
21580
22685
  const rewritten = (text) => rewrittenTo ? {
21581
22686
  kind: "rewrite",
@@ -21662,7 +22767,7 @@ var dispatchHook = (adapter, raw, env = process.env) => {
21662
22767
  };
21663
22768
  // packages/workit-core/src/hooks/deadline.ts
21664
22769
  import { appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
21665
- import path31 from "node:path";
22770
+ import path32 from "node:path";
21666
22771
  var HOOK_SKIP_TEXT = "workit context skipped: host under load";
21667
22772
  var HOOK_DEADLINE_MS = 8000;
21668
22773
  var HOST_DEADLINES = {
@@ -21680,7 +22785,7 @@ var hookDeadlineMs = (env, host = "", native) => {
21680
22785
  const row = HOST_DEADLINES[host];
21681
22786
  return override(env) ?? (row ? native === undefined ? Math.min(...Object.values(row)) : row[native] ?? row[""] : HOOK_DEADLINE_MS);
21682
22787
  };
21683
- var skipsFile = (stateDir) => path31.join(stateDir, HOOK_SKIPS_FILE);
22788
+ var skipsFile = (stateDir) => path32.join(stateDir, HOOK_SKIPS_FILE);
21684
22789
  var recordHookSkip = (skip, stateDir = resolveStateDir()) => {
21685
22790
  try {
21686
22791
  mkdirSync3(stateDir, { recursive: true });
@@ -22002,6 +23107,17 @@ var render = (decision, native) => {
22002
23107
  },
22003
23108
  exitCode: 0
22004
23109
  };
23110
+ if (decision.kind === "ask" && native === "PreToolUse")
23111
+ return {
23112
+ json: {
23113
+ hookSpecificOutput: {
23114
+ hookEventName: "PreToolUse",
23115
+ permissionDecision: "ask",
23116
+ permissionDecisionReason: decision.reason
23117
+ }
23118
+ },
23119
+ exitCode: 0
23120
+ };
22005
23121
  if (decision.kind === "context" && native !== null && CONTEXT_EVENTS.has(native))
22006
23122
  return {
22007
23123
  json: { hookSpecificOutput: { hookEventName: native, additionalContext: decision.text } },
@@ -22325,7 +23441,7 @@ var codexAdapter = {
22325
23441
 
22326
23442
  // packages/workit-core/src/hooks/hosts/cursor.ts
22327
23443
  import fs24 from "node:fs";
22328
- import path32 from "node:path";
23444
+ import path33 from "node:path";
22329
23445
  var undocumented = { support: "undocumented", native: null };
22330
23446
  var none = { support: "none", native: null };
22331
23447
  var CURSOR_DESCRIPTOR = {
@@ -22522,7 +23638,7 @@ var protocolEvent2 = (input) => {
22522
23638
  };
22523
23639
  var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
22524
23640
  var lastAssistantText = (file) => {
22525
- if (!file || !path32.isAbsolute(file))
23641
+ if (!file || !path33.isAbsolute(file))
22526
23642
  return null;
22527
23643
  try {
22528
23644
  const size = fs24.statSync(file).size;
@@ -22559,7 +23675,7 @@ var lastAssistantText = (file) => {
22559
23675
  return null;
22560
23676
  }
22561
23677
  };
22562
- var within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path32.sep) ? root : `${root}${path32.sep}`);
23678
+ var within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path33.sep) ? root : `${root}${path33.sep}`);
22563
23679
  var hookCwd = (input) => {
22564
23680
  const cwd = existingDirectory(input.cwd);
22565
23681
  if (cwd && input.hook_event_name === "beforeShellExecution")