@brainervirus/workit-opencode 2.6.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/plugin.js CHANGED
@@ -15,12 +15,12 @@ var __export = (target, all) => {
15
15
 
16
16
  // packages/workit-opencode/src/v1/server.ts
17
17
  import { existsSync as existsSync12 } from "node:fs";
18
- import path26 from "node:path";
18
+ import path28 from "node:path";
19
19
 
20
20
  // packages/workit-core/src/core.ts
21
- import { spawnSync as spawnSync4 } from "node:child_process";
21
+ import { spawnSync as spawnSync6 } from "node:child_process";
22
22
  import { existsSync as existsSync5, realpathSync as realpathSync7 } from "node:fs";
23
- import path11 from "node:path";
23
+ import path13 from "node:path";
24
24
 
25
25
  // packages/workit-core/src/core/task-contract.ts
26
26
  import { createHash, randomUUID } from "node:crypto";
@@ -8137,7 +8137,37 @@ var candidateSchema = object({
8137
8137
  path: ["id"]
8138
8138
  });
8139
8139
  });
8140
- var evidenceSchema = object({
8140
+ var checkObservationSchema = object({
8141
+ observer: literal("workit_cli"),
8142
+ name: nonEmpty.nullable(),
8143
+ configured: boolean2(),
8144
+ argv: array(text).min(1),
8145
+ shell: boolean2(),
8146
+ cwd: nonEmpty,
8147
+ exitCode: safeInteger,
8148
+ durationMs: safeInteger.min(0),
8149
+ timedOut: boolean2(),
8150
+ head: text.nullable(),
8151
+ tree: text.nullable(),
8152
+ dirty: boolean2().nullable(),
8153
+ signal: text.nullable(),
8154
+ treeAfter: text.nullable(),
8155
+ modifiedWorktree: boolean2(),
8156
+ base: text.nullable(),
8157
+ patchId: text.nullable(),
8158
+ environment: object({
8159
+ platform: nonEmpty,
8160
+ arch: nonEmpty,
8161
+ vars: record(nonEmpty, text.nullable())
8162
+ }).strict(),
8163
+ logDigest: text.nullable(),
8164
+ logRef: text.nullable(),
8165
+ logTail: array(text).max(40),
8166
+ ledgerRowId: text.nullable(),
8167
+ attestation: object({ host: nonEmpty, session: text.nullable(), agentId: text.nullable() }).strict().nullable()
8168
+ }).strict();
8169
+ var CHECK_OBSERVATION_PATH = "evidence.*.data.observation";
8170
+ var evidenceFields = {
8141
8171
  kind: _enum(["check", "review", "investigation", "artifact"]),
8142
8172
  claim: text,
8143
8173
  requirementIds: array(digest),
@@ -8148,7 +8178,9 @@ var evidenceSchema = object({
8148
8178
  refs: array(refSchema),
8149
8179
  exitCode: safeInteger.nullable(),
8150
8180
  reviewContext: refSchema.nullable()
8151
- }).strict();
8181
+ };
8182
+ var evidenceSchema = object({ ...evidenceFields, observation: checkObservationSchema.optional() }).strict();
8183
+ var reportedEvidenceSchema = object(evidenceFields).strict();
8152
8184
  var evidenceEvaluationSchema = object({
8153
8185
  evidenceId: id,
8154
8186
  status: _enum(["passed", "failed", "missing", "skipped", "stale"]),
@@ -8480,7 +8512,7 @@ var evidenceOperations = {
8480
8512
  action: literal("record"),
8481
8513
  ...taskId,
8482
8514
  expectedRevision: revision.optional(),
8483
- evidence: evidenceSchema
8515
+ evidence: reportedEvidenceSchema
8484
8516
  })
8485
8517
  };
8486
8518
  var findingOperations = {
@@ -10980,10 +11012,10 @@ function assertProductWriteAllowed(input) {
10980
11012
  }
10981
11013
 
10982
11014
  // packages/workit-core/src/core/task-evaluation.ts
10983
- import { createHash as createHash7 } from "node:crypto";
10984
- import * as fs8 from "node:fs";
10985
- import { spawnSync as spawnSync3 } from "node:child_process";
10986
- import path10 from "node:path";
11015
+ import { createHash as createHash8 } from "node:crypto";
11016
+ import * as fs10 from "node:fs";
11017
+ import { spawnSync as spawnSync5 } from "node:child_process";
11018
+ import path12 from "node:path";
10987
11019
 
10988
11020
  // packages/workit-core/src/core/authority.ts
10989
11021
  import { createHash as createHash6 } from "node:crypto";
@@ -12749,16 +12781,346 @@ function reconcileAction(input) {
12749
12781
  var bindingCovers = scopeCovers;
12750
12782
  var verifyDecisionContent = verifyContentRefs;
12751
12783
 
12784
+ // packages/workit-core/src/check-config.ts
12785
+ import { spawnSync as spawnSync3 } from "node:child_process";
12786
+ import fs8 from "node:fs";
12787
+ import path10 from "node:path";
12788
+ var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
12789
+ var CHECKS_FILE = "workit.checks.json";
12790
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
12791
+ var SHELL_META = /[|&;<>()$`\\*?[\]{}~!#]/u;
12792
+ function splitCommand(command) {
12793
+ const argv = [];
12794
+ let current = "";
12795
+ let quote = null;
12796
+ let started = false;
12797
+ for (const char of command) {
12798
+ if (quote) {
12799
+ if (char === quote)
12800
+ quote = null;
12801
+ else
12802
+ current += char;
12803
+ continue;
12804
+ }
12805
+ if (char === '"' || char === "'") {
12806
+ quote = char;
12807
+ started = true;
12808
+ continue;
12809
+ }
12810
+ if (/\s/u.test(char)) {
12811
+ if (started)
12812
+ argv.push(current);
12813
+ current = "";
12814
+ started = false;
12815
+ continue;
12816
+ }
12817
+ if (SHELL_META.test(char))
12818
+ return new Error(`"${command}" uses shell syntax (${char}); configure an argv array instead`);
12819
+ current += char;
12820
+ started = true;
12821
+ }
12822
+ if (quote)
12823
+ return new Error(`"${command}" has an unterminated quote`);
12824
+ if (started)
12825
+ argv.push(current);
12826
+ return argv.length ? argv : new Error("empty command");
12827
+ }
12828
+ var git = (cwd, args) => {
12829
+ const run = spawnSync3("git", args, {
12830
+ cwd,
12831
+ encoding: "utf8",
12832
+ timeout: 20000,
12833
+ killSignal: "SIGKILL",
12834
+ windowsHide: true
12835
+ });
12836
+ const value = run.status === 0 ? (run.stdout ?? "").trim() : "";
12837
+ return value || null;
12838
+ };
12839
+ function checkRoot(cwd) {
12840
+ return path10.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
12841
+ }
12842
+ var packageManager = (root) => {
12843
+ const has = (file) => fs8.existsSync(path10.join(root, file));
12844
+ if (has("bun.lock") || has("bun.lockb"))
12845
+ return "bun";
12846
+ if (has("pnpm-lock.yaml"))
12847
+ return "pnpm";
12848
+ if (has("yarn.lock"))
12849
+ return "yarn";
12850
+ return "npm";
12851
+ };
12852
+ var scriptCheck = (pm, name) => {
12853
+ const argv = [pm, "run", name];
12854
+ const accepts = [argv];
12855
+ if (pm === "pnpm" || pm === "yarn" || pm === "npm" && name === "test")
12856
+ accepts.push([pm, name]);
12857
+ return { name, argv, accepts };
12858
+ };
12859
+ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
12860
+ function detectedChecks(root) {
12861
+ const read = (file) => {
12862
+ try {
12863
+ return fs8.readFileSync(path10.join(root, file), "utf8");
12864
+ } catch {
12865
+ return null;
12866
+ }
12867
+ };
12868
+ const checks = [];
12869
+ const add = (check) => {
12870
+ if (!checks.some((item) => item.name === check.name))
12871
+ checks.push(check);
12872
+ };
12873
+ const manifestText = read("package.json");
12874
+ if (manifestText !== null) {
12875
+ let manifest = null;
12876
+ try {
12877
+ manifest = JSON.parse(manifestText);
12878
+ } catch {}
12879
+ const scripts = isRecord2(manifest) && isRecord2(manifest.scripts) ? manifest.scripts : {};
12880
+ const pm = packageManager(root);
12881
+ for (const name of SCRIPT_CHECKS) {
12882
+ const script = scripts[name];
12883
+ if (typeof script === "string" && script.trim() !== "")
12884
+ add(scriptCheck(pm, name));
12885
+ }
12886
+ }
12887
+ const plain = (name, argv) => ({ name, argv, accepts: [argv] });
12888
+ if (read("go.mod") !== null)
12889
+ add(plain("test", ["go", "test", "./..."]));
12890
+ if (read("Cargo.toml") !== null)
12891
+ add(plain("test", ["cargo", "test"]));
12892
+ if (read("pytest.ini") !== null || /\bpytest\b/u.test(read("pyproject.toml") ?? ""))
12893
+ add(plain("test", ["pytest"]));
12894
+ const makefile = read("Makefile") ?? read("makefile") ?? read("GNUmakefile");
12895
+ if (makefile !== null && /^test\s*:(?!=)/mu.test(makefile))
12896
+ add(plain("test", ["make", "test"]));
12897
+ return checks;
12898
+ }
12899
+ var GATES = ["testing", "verification"];
12900
+ function loadCheckConfig(cwd) {
12901
+ const root = checkRoot(cwd);
12902
+ const file = path10.join(root, CHECKS_FILE);
12903
+ const none = (error = null) => ({
12904
+ source: error ? CHECKS_FILE : "none",
12905
+ root,
12906
+ checks: [],
12907
+ gates: {},
12908
+ error
12909
+ });
12910
+ let text = null;
12911
+ try {
12912
+ text = fs8.readFileSync(file, "utf8");
12913
+ } catch (error) {
12914
+ if (error.code !== "ENOENT")
12915
+ return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
12916
+ }
12917
+ if (text === null) {
12918
+ const checks = detectedChecks(root);
12919
+ return checks.length ? { source: "defaults", root, checks, gates: {}, error: null } : none();
12920
+ }
12921
+ let parsed;
12922
+ try {
12923
+ parsed = JSON.parse(text);
12924
+ } catch {
12925
+ return none(`${CHECKS_FILE} is not valid JSON`);
12926
+ }
12927
+ if (!isRecord2(parsed) || !isRecord2(parsed.checks))
12928
+ return none(`${CHECKS_FILE} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
12929
+ const checks = [];
12930
+ for (const [name, value] of Object.entries(parsed.checks)) {
12931
+ if (!CHECK_NAME.test(name))
12932
+ return none(`${CHECKS_FILE}: check name "${name}" must match ${CHECK_NAME.source}`);
12933
+ const argv = typeof value === "string" ? splitCommand(value) : Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && item.length > 0) ? value : new Error("expected a command string or a non-empty argv array");
12934
+ if (argv instanceof Error)
12935
+ return none(`${CHECKS_FILE}: check "${name}": ${argv.message}`);
12936
+ checks.push({ name, argv, accepts: [argv] });
12937
+ }
12938
+ const gates = {};
12939
+ if (parsed.gates !== undefined) {
12940
+ if (!isRecord2(parsed.gates))
12941
+ return none(`${CHECKS_FILE}: gates must be {"testing":"<name>"|["<name>",…]}`);
12942
+ for (const [gate, value] of Object.entries(parsed.gates)) {
12943
+ if (!GATES.includes(gate))
12944
+ return none(`${CHECKS_FILE}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
12945
+ const names = typeof value === "string" ? [value] : value;
12946
+ if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
12947
+ return none(`${CHECKS_FILE}: gates.${gate} must name configured checks`);
12948
+ gates[gate] = names;
12949
+ }
12950
+ }
12951
+ return { source: CHECKS_FILE, root, checks, gates, error: null };
12952
+ }
12953
+ var sameArgv = (left, right) => left.length === right.length && left.every((item, index) => item === right[index]);
12954
+ function matchesNamedCheck(config, name, argv, cwd) {
12955
+ if (config.error || name === null || cwd !== ".")
12956
+ return false;
12957
+ const check = config.checks.find((item) => item.name === name);
12958
+ return Boolean(check?.accepts.some((accepted) => sameArgv(accepted, argv)));
12959
+ }
12960
+ function gateCheckNames(config, dimension) {
12961
+ const explicit = config.gates[dimension];
12962
+ if (explicit)
12963
+ return explicit;
12964
+ const names = config.checks.map((check) => check.name);
12965
+ if (dimension === "testing")
12966
+ return names.includes("test") ? ["test"] : [];
12967
+ return names;
12968
+ }
12969
+ var workitOnPath;
12970
+ function workitAvailable(env = process.env) {
12971
+ if (workitOnPath !== undefined)
12972
+ return workitOnPath;
12973
+ const windows = process.platform === "win32";
12974
+ const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
12975
+ const dirs = (env.PATH ?? env.Path ?? "").split(path10.delimiter).filter(Boolean);
12976
+ workitOnPath = dirs.some((dir) => exts.some((ext) => {
12977
+ try {
12978
+ return fs8.statSync(path10.join(dir, `workit${ext}`)).isFile();
12979
+ } catch {
12980
+ return false;
12981
+ }
12982
+ }));
12983
+ return workitOnPath;
12984
+ }
12985
+ function checkCommand(args, version, onPath = workitAvailable()) {
12986
+ return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
12987
+ }
12988
+
12989
+ // packages/workit-core/src/git/rev.ts
12990
+ import { spawnSync as spawnSync4 } from "node:child_process";
12991
+ import { createHash as createHash7 } from "node:crypto";
12992
+ import fs9 from "node:fs";
12993
+ import os2 from "node:os";
12994
+ import path11 from "node:path";
12995
+ var MAX_BUFFER = 256 * 1024 * 1024;
12996
+ var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
12997
+ var git2 = (cwd, args, options = {}) => {
12998
+ const result = spawnSync4("git", args, {
12999
+ cwd,
13000
+ encoding: "utf8",
13001
+ env: options.env ?? process.env,
13002
+ input: options.input,
13003
+ maxBuffer: MAX_BUFFER,
13004
+ timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
13005
+ killSignal: "SIGKILL",
13006
+ windowsHide: true
13007
+ });
13008
+ const error = result.error;
13009
+ return {
13010
+ ok: result.status === 0,
13011
+ stdout: result.stdout ?? "",
13012
+ stderr: result.stderr ?? "",
13013
+ timedOut: error?.code === "ETIMEDOUT"
13014
+ };
13015
+ };
13016
+ var line = (run) => {
13017
+ if (!run.ok)
13018
+ return null;
13019
+ const value = run.stdout.trim();
13020
+ return value ? value : null;
13021
+ };
13022
+ function headSha(cwd) {
13023
+ return line(git2(cwd, ["rev-parse", "--verify", "-q", "HEAD^{commit}"]));
13024
+ }
13025
+ var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
13026
+ var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
13027
+ var seedIndex = (from, to) => {
13028
+ fs9.copyFileSync(from, to);
13029
+ const { atimeNs, mtimeNs } = fs9.statSync(from, { bigint: true });
13030
+ const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
13031
+ fs9.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
13032
+ };
13033
+ function worktreeTree(cwd, options = {}) {
13034
+ const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
13035
+ const indexPath = line(git2(cwd, ["rev-parse", "--path-format=absolute", "--git-path", "index"]));
13036
+ if (!top || !indexPath)
13037
+ return null;
13038
+ const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
13039
+ const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
13040
+ const scratch = fs9.mkdtempSync(path11.join(os2.tmpdir(), "workit-tree-"));
13041
+ try {
13042
+ const tempIndex = path11.join(scratch, "index");
13043
+ if (fs9.existsSync(indexPath))
13044
+ seedIndex(indexPath, tempIndex);
13045
+ const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
13046
+ const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
13047
+ env,
13048
+ timeoutMs
13049
+ });
13050
+ if (!untracked.ok)
13051
+ return null;
13052
+ const skipped = [];
13053
+ for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
13054
+ try {
13055
+ const stat = fs9.statSync(path11.join(top, file));
13056
+ if (stat.isFile() && stat.size > cap)
13057
+ skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
13058
+ } catch {}
13059
+ }
13060
+ skipped.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
13061
+ const added = skipped.length ? git2(top, ["add", "-A", "--pathspec-from-file=-", "--pathspec-file-nul"], {
13062
+ env,
13063
+ timeoutMs,
13064
+ input: [".", ...skipped.map((file) => `:(exclude,literal)${file.path}`)].join("\x00")
13065
+ }) : git2(top, ["add", "-A"], { env, timeoutMs });
13066
+ if (!added.ok)
13067
+ return null;
13068
+ const tree = line(git2(top, ["write-tree"], { env, timeoutMs }));
13069
+ if (!tree)
13070
+ return null;
13071
+ const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
13072
+ const key = skipped.length ? `sha256:${createHash7("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
13073
+ return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
13074
+ } finally {
13075
+ fs9.rmSync(scratch, { recursive: true, force: true });
13076
+ }
13077
+ }
13078
+ function worktreeSignal(cwd, options = {}) {
13079
+ const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
13080
+ if (!top)
13081
+ return null;
13082
+ const status = git2(top, [
13083
+ "status",
13084
+ "--porcelain=v1",
13085
+ "-z",
13086
+ "--untracked-files=all",
13087
+ "--no-renames",
13088
+ "--ignore-submodules=dirty"
13089
+ ], {
13090
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
13091
+ timeoutMs: options.timeoutMs ?? GIT_TIMEOUTS.worktree
13092
+ });
13093
+ if (!status.ok)
13094
+ return null;
13095
+ const hash = createHash7("sha256");
13096
+ hash.update(`${headSha(top) ?? "unborn"}\x00`);
13097
+ for (const entry of status.stdout.split("\x00")) {
13098
+ if (entry.length < 4)
13099
+ continue;
13100
+ const file = entry.slice(3);
13101
+ const absolute = path11.join(top, file);
13102
+ let stat = "-";
13103
+ try {
13104
+ const info = fs9.lstatSync(absolute, { bigint: true });
13105
+ stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
13106
+ if (info.isDirectory())
13107
+ stat += `:${headSha(absolute) ?? "-"}`;
13108
+ } catch {}
13109
+ hash.update(`${entry.slice(0, 2)}\x00${file}\x00${stat}\x00`);
13110
+ }
13111
+ return `sig:${hash.digest("hex")}`;
13112
+ }
13113
+
12752
13114
  // packages/workit-core/src/core/task-evaluation.ts
12753
- var digestBytes3 = (value) => createHash7("sha256").update(value).digest("hex");
12754
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path10.sep}`);
12755
- var relative = (root, candidate) => path10.relative(root, candidate).split(path10.sep).join("/") || ".";
13115
+ var digestBytes3 = (value) => createHash8("sha256").update(value).digest("hex");
13116
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path12.sep}`);
13117
+ var relative = (root, candidate) => path12.relative(root, candidate).split(path12.sep).join("/") || ".";
12756
13118
  var canonicalPath = (value) => {
12757
13119
  if (typeof value !== "string" || !value)
12758
13120
  return null;
12759
13121
  if (value.includes("\\") || value.split("/").includes(".."))
12760
13122
  return null;
12761
- const normalized = path10.posix.normalize(value);
13123
+ const normalized = path12.posix.normalize(value);
12762
13124
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
12763
13125
  return null;
12764
13126
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -12794,7 +13156,7 @@ var compareCodeUnits2 = (left, right) => {
12794
13156
  return left.length - right.length;
12795
13157
  };
12796
13158
  var gitPaths = (root) => {
12797
- const result = spawnSync3("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
13159
+ const result = spawnSync5("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
12798
13160
  cwd: root,
12799
13161
  encoding: "buffer"
12800
13162
  });
@@ -12806,21 +13168,21 @@ var gitPaths = (root) => {
12806
13168
  git: !/not a git repository/i.test(stderr)
12807
13169
  };
12808
13170
  }
12809
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")) : [];
12810
- const stagedDeleted = spawnSync3("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
13171
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path12.sep).join("/")) : [];
13172
+ const stagedDeleted = spawnSync5("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
12811
13173
  if (stagedDeleted.status !== 0) {
12812
13174
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
12813
13175
  if (!/not a git repository/i.test(stderr))
12814
13176
  return { paths, uncertain: true, git: true };
12815
13177
  } else if (stagedDeleted.stdout) {
12816
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")));
13178
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path12.sep).join("/")));
12817
13179
  }
12818
13180
  return { paths: [...new Set(paths)], uncertain: false, git: true };
12819
13181
  };
12820
13182
  var walk = (root, directory, output) => {
12821
13183
  let names;
12822
13184
  try {
12823
- names = fs8.readdirSync(directory);
13185
+ names = fs10.readdirSync(directory);
12824
13186
  } catch {
12825
13187
  return true;
12826
13188
  }
@@ -12828,10 +13190,10 @@ var walk = (root, directory, output) => {
12828
13190
  for (const name of names) {
12829
13191
  if (name === ".git" || name === ".workit")
12830
13192
  continue;
12831
- const target = path10.join(directory, name);
13193
+ const target = path12.join(directory, name);
12832
13194
  let stat;
12833
13195
  try {
12834
- stat = fs8.lstatSync(target);
13196
+ stat = fs10.lstatSync(target);
12835
13197
  } catch {
12836
13198
  uncertain = true;
12837
13199
  continue;
@@ -12846,14 +13208,14 @@ var walk = (root, directory, output) => {
12846
13208
  var scopeRoots = (root, scope) => {
12847
13209
  const roots = [];
12848
13210
  for (const item of scope.paths.length ? scope.paths : ["."]) {
12849
- const target = path10.resolve(root, item);
13211
+ const target = path12.resolve(root, item);
12850
13212
  if (!inside(root, target))
12851
13213
  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` }] });
12852
13214
  let ancestor = target;
12853
- while (!fs8.existsSync(ancestor) && ancestor !== root)
12854
- ancestor = path10.dirname(ancestor);
13215
+ while (!fs10.existsSync(ancestor) && ancestor !== root)
13216
+ ancestor = path12.dirname(ancestor);
12855
13217
  try {
12856
- if (!inside(root, fs8.realpathSync(ancestor)))
13218
+ if (!inside(root, fs10.realpathSync(ancestor)))
12857
13219
  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` }] });
12858
13220
  } catch {
12859
13221
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -12865,7 +13227,7 @@ var scopeRoots = (root, scope) => {
12865
13227
  function captureCandidate(root, scope, environment = []) {
12866
13228
  let checkout;
12867
13229
  try {
12868
- checkout = fs8.realpathSync(root);
13230
+ checkout = fs10.realpathSync(root);
12869
13231
  } catch {
12870
13232
  return failure2("invalid_input", "candidate root does not exist");
12871
13233
  }
@@ -12883,11 +13245,11 @@ function captureCandidate(root, scope, environment = []) {
12883
13245
  if (item === ".git" || item === ".workit")
12884
13246
  continue;
12885
13247
  names.add(item);
12886
- if (!fs8.existsSync(target))
13248
+ if (!fs10.existsSync(target))
12887
13249
  continue;
12888
13250
  let stat;
12889
13251
  try {
12890
- stat = fs8.lstatSync(target);
13252
+ stat = fs10.lstatSync(target);
12891
13253
  } catch {
12892
13254
  uncertain = true;
12893
13255
  continue;
@@ -12903,17 +13265,17 @@ function captureCandidate(root, scope, environment = []) {
12903
13265
  for (const item of [...names].toSorted()) {
12904
13266
  if (!scopeMatches(item, normalizedScope))
12905
13267
  continue;
12906
- const target = path10.join(checkout, item);
13268
+ const target = path12.join(checkout, item);
12907
13269
  try {
12908
- const stat = fs8.lstatSync(target);
13270
+ const stat = fs10.lstatSync(target);
12909
13271
  if (stat.isSymbolicLink()) {
12910
- const link = fs8.readlinkSync(target);
13272
+ const link = fs10.readlinkSync(target);
12911
13273
  files.push({ path: item, kind: "symlink", digest: digestBytes3(link), executable: null });
12912
13274
  } else if (stat.isFile()) {
12913
13275
  files.push({
12914
13276
  path: item,
12915
13277
  kind: "file",
12916
- digest: digestBytes3(fs8.readFileSync(target)),
13278
+ digest: digestBytes3(fs10.readFileSync(target)),
12917
13279
  executable: (stat.mode & 73) !== 0
12918
13280
  });
12919
13281
  } else if (!stat.isDirectory()) {
@@ -12938,7 +13300,7 @@ function captureCandidate(root, scope, environment = []) {
12938
13300
  const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
12939
13301
  if (values.some(({ value }) => value === null))
12940
13302
  completeness = "uncertain";
12941
- const headResult = spawnSync3("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
13303
+ const headResult = spawnSync5("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
12942
13304
  if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
12943
13305
  completeness = "uncertain";
12944
13306
  const head = headResult.status === 0 ? headResult.stdout.trim() : null;
@@ -12976,7 +13338,24 @@ var sameSession = (left, right) => {
12976
13338
  return left !== null && other !== null && left.host === other.host && left.handle === other.handle;
12977
13339
  };
12978
13340
  var evidenceScopes = (task, ids) => (task.policy?.requirements ?? []).filter((requirement) => ids.includes(requirement.id)).map((requirement) => requirement.scope);
12979
- function evaluateEvidence(task, candidate) {
13341
+ var memo2 = (compute) => {
13342
+ let value;
13343
+ return () => {
13344
+ if (value === undefined)
13345
+ value = compute();
13346
+ return value;
13347
+ };
13348
+ };
13349
+ var treeFreshness = (root) => ({
13350
+ mode: "tree",
13351
+ current: memo2(() => worktreeTree(root)?.key ?? null)
13352
+ });
13353
+ var signalFreshness = (root) => ({
13354
+ mode: "signal",
13355
+ current: memo2(() => worktreeSignal(root))
13356
+ });
13357
+ var isObservedCheck = (entry) => entry.data.kind === "check" && entry.data.observation?.observer === "workit_cli" && entry.provenance?.kind === "host_observed" && entry.provenance.host === "workit_cli";
13358
+ function evaluateEvidence(task, candidate, freshness) {
12980
13359
  const current = candidate ?? task.candidates.at(-1) ?? null;
12981
13360
  return task.evidence.map((entry) => {
12982
13361
  const evidence = entry.data;
@@ -12986,6 +13365,25 @@ function evaluateEvidence(task, candidate) {
12986
13365
  status: "failed",
12987
13366
  reason: "failed evidence remains historical"
12988
13367
  };
13368
+ const observation = isObservedCheck(entry) ? evidence.observation : undefined;
13369
+ if (observation && observation.tree !== null) {
13370
+ if (task.status === "closed")
13371
+ return { evidenceId: entry.id, status: evidence.result, reason: "recorded before close" };
13372
+ if (observation.modifiedWorktree)
13373
+ return {
13374
+ evidenceId: entry.id,
13375
+ status: "stale",
13376
+ reason: "the check changed the worktree while it ran; ignore or clean its outputs"
13377
+ };
13378
+ const recorded = freshness?.mode === "signal" ? observation.signal : observation.tree;
13379
+ if (recorded !== null && freshness && recorded === freshness.current())
13380
+ return { evidenceId: entry.id, status: evidence.result, reason: "worktree is unchanged" };
13381
+ return {
13382
+ evidenceId: entry.id,
13383
+ status: "stale",
13384
+ reason: freshness?.mode === "signal" ? "the worktree may have changed since the check ran (quick signal; inspect re-checks exactly)" : "the worktree changed since the check ran"
13385
+ };
13386
+ }
12989
13387
  if (evidence.kind === "check" || evidence.kind === "review") {
12990
13388
  if (!evidence.beforeCandidateId || !evidence.candidateId)
12991
13389
  return {
@@ -13069,8 +13467,45 @@ var expectedKinds = (dimension, ruleId) => {
13069
13467
  return "decision";
13070
13468
  return "worker report";
13071
13469
  };
13072
- function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller) {
13073
- const evidence = evaluateEvidence(task, candidate);
13470
+ var observedGate = (requirement) => (requirement.dimension === "testing" || requirement.dimension === "verification") && requirement.before === "close";
13471
+ function evaluateObservedGate(task, requirement, statuses, config) {
13472
+ const names = gateCheckNames(config, requirement.dimension);
13473
+ const observed = task.evidence.filter(isObservedCheck);
13474
+ const fresh = observed.filter((entry) => statuses.get(entry.id) === "passed" && entry.data.observation?.exitCode === 0);
13475
+ const named = (entry) => {
13476
+ const observation = entry.data.observation;
13477
+ return observation.configured && !observation.shell && names.includes(observation.name ?? "") && matchesNamedCheck(config, observation.name, observation.argv, observation.cwd);
13478
+ };
13479
+ const considered = observed.map((entry) => entry.id);
13480
+ const run = checkCommand(names[0] ?? "<name>", runtimeVersion());
13481
+ if (config.error)
13482
+ return {
13483
+ satisfied: [],
13484
+ considered,
13485
+ reason: `check config is invalid (${config.error}); fix it, then run ${run}`
13486
+ };
13487
+ if (!names.length)
13488
+ return {
13489
+ satisfied: [],
13490
+ considered,
13491
+ reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE} (committed, so it shows in the diff), e.g. {"checks":{"test":"<your test command>"}}, then run ${checkCommand("test", runtimeVersion())}`
13492
+ };
13493
+ const satisfied = fresh.filter(named);
13494
+ if (satisfied.length)
13495
+ return {
13496
+ satisfied: satisfied.map((entry) => entry.id),
13497
+ considered,
13498
+ reason: `a fresh passing configured check (${names.join("|")}) was observed by workit check`
13499
+ };
13500
+ const reported = task.evidence.some((entry) => entry.data.kind === "check" && !isObservedCheck(entry) && entry.data.requirementIds.includes(requirement.id));
13501
+ const reason = fresh.length ? `ad-hoc checks do not satisfy a configured gate; run ${run}` : observed.some((entry) => statuses.get(entry.id) === "stale") ? `observed checks are stale (the worktree changed); run ${run}` : observed.some((entry) => statuses.get(entry.id) === "failed") ? `the observed check failed; fix it and run ${run}` : reported ? `agent-reported checks are notes, not evidence; run ${run}` : `no CLI-observed check (needs kind:check observed by workit check); run ${run}`;
13502
+ return { satisfied: [], considered, reason };
13503
+ }
13504
+ function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller, freshness = treeFreshness(checkoutRoot)) {
13505
+ const evidence = evaluateEvidence(task, candidate, freshness);
13506
+ const statuses = new Map(evidence.map((item) => [item.evidenceId, item.status]));
13507
+ let checkConfig;
13508
+ const configFor = () => checkConfig ??= loadCheckConfig(checkoutRoot);
13074
13509
  if (task.policy && task.policy.policyVersion !== POLICY_VERSION)
13075
13510
  return task.policy.requirements.map((requirement) => ({
13076
13511
  requirementId: requirement.id,
@@ -13080,8 +13515,19 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
13080
13515
  reason: "stored policy version is unsupported; reassessment is required"
13081
13516
  }));
13082
13517
  return (task.policy?.requirements ?? []).map((requirement) => {
13518
+ const gate = observedGate(requirement) ? evaluateObservedGate(task, requirement, statuses, configFor()) : null;
13519
+ if (gate?.satisfied.length)
13520
+ return {
13521
+ requirementId: requirement.id,
13522
+ status: "satisfied",
13523
+ evidenceIds: gate.satisfied,
13524
+ decisionIds: [],
13525
+ reason: gate.reason
13526
+ };
13083
13527
  const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
13084
13528
  const passed = related.filter(({ entry, evaluation }) => {
13529
+ if (gate)
13530
+ return false;
13085
13531
  if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
13086
13532
  return false;
13087
13533
  if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
@@ -13144,6 +13590,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
13144
13590
  reason: "an applicable approved limitation permits the missing evidence"
13145
13591
  };
13146
13592
  const unavailable = capabilities.some((capability) => capability.assurance === "unavailable" && (capability.name === requirement.dimension || capability.surface === requirement.dimension));
13593
+ if (gate)
13594
+ return {
13595
+ requirementId: requirement.id,
13596
+ status: unavailable ? "unavailable" : "unsatisfied",
13597
+ evidenceIds: [...new Set([...gate.considered, ...related.map(({ entry }) => entry.id)])],
13598
+ decisionIds: [],
13599
+ reason: unavailable ? "required capability is unavailable" : gate.reason
13600
+ };
13147
13601
  if (!unavailable && related.length > 0 && !passed.length)
13148
13602
  return {
13149
13603
  requirementId: requirement.id,
@@ -13320,8 +13774,11 @@ policy, and reapplies the call, returning busy under persistent contention. A
13320
13774
  revision_conflict means a revision you passed is stale: re-read the record
13321
13775
  before deciding whether to retry.
13322
13776
  A solo edit does not need writer acquisition; use it when concurrent checkout
13323
- writers need coordination. Record only observed facts and checks. Evidence can
13324
- become stale when its bound candidate changes; reconcile findings against the
13777
+ writers need coordination. Record only observed facts and checks. Close-time
13778
+ testing and verification gates accept only a configured check the CLI ran:
13779
+ \`workit check <name>\` (\`npx -y @brainervirus/workit-cli check <name>\` when
13780
+ \`workit\` is not on PATH); a recorded check result is a note and an ad-hoc
13781
+ \`workit check -- <cmd>\` never satisfies a gate. Evidence can become stale when its bound candidate or tree changes; reconcile findings against the
13325
13782
  current candidate before recording completion.
13326
13783
 
13327
13784
  Workit validates domain policy against the actual action target, configured
@@ -13370,8 +13827,9 @@ target result; a local commit alone is not evidence of a requested remote push.
13370
13827
  Skill routing: slash aliases /wk-* load on demand. Use workit-steer for a
13371
13828
  substantial interruption or change of direction, workit-deslop when relevant
13372
13829
  to a PR-ready endpoint, workit-green-run for failing CI, workit-blast-radius
13373
- when impact is uncertain, and workit-challenge for genuinely open consequential
13374
- choices. Load workit-plan when dependencies or handoff need durable next actions.
13830
+ when impact is uncertain, workit-challenge for genuinely open consequential
13831
+ choices, workit-bdd to turn acceptance criteria into Given/When/Then tests, and
13832
+ workit-test-audit to check tests for tautologies. Load workit-plan when dependencies or handoff need durable next actions.
13375
13833
  Load the skill; never act from memory of it.
13376
13834
  `.trim();
13377
13835
 
@@ -13382,7 +13840,7 @@ function reconcileResume(view, observations = []) {
13382
13840
  const candidate = captureCandidate(view.workspace.root, view.task.intent.data.scope, []);
13383
13841
  if (!candidate.ok)
13384
13842
  return candidate;
13385
- const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
13843
+ const staleEvidenceIds = evaluateEvidence(view.task, candidate.data, treeFreshness(view.workspace.root)).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
13386
13844
  const blockers = [...view.task.progress.blockers];
13387
13845
  if (view.task.workers.some((entry) => isUncertainWorker(entry.data.state)))
13388
13846
  blockers.push({
@@ -14750,10 +15208,7 @@ class WorkitCore {
14750
15208
  if (finding.data.disposition !== "fixed")
14751
15209
  return finding;
14752
15210
  const withEntry = { ...current, candidates, evidence: [...current.evidence, entry] };
14753
- const statuses = new Map(evaluateEvidence(withEntry, currentCandidate.data).map((item) => [
14754
- item.evidenceId,
14755
- item.status
14756
- ]));
15211
+ const statuses = new Map(evaluateEvidence(withEntry, currentCandidate.data, treeFreshness(this.store.root)).map((item) => [item.evidenceId, item.status]));
14757
15212
  const verified = withEntry.evidence.some((item) => findingVerificationPasses(finding.data.candidateId, {
14758
15213
  kind: item.data.kind,
14759
15214
  candidateId: item.data.candidateId,
@@ -14768,6 +15223,71 @@ class WorkitCore {
14768
15223
  return changed;
14769
15224
  return success(changed.data.revision, null, changed.data.evidence.at(-1));
14770
15225
  }
15226
+ observeCheck(request) {
15227
+ return this.retryOmittedRevisions(request, () => this.observeCheckOnce(request));
15228
+ }
15229
+ observeCheckOnce(request) {
15230
+ const root = this.contextRootError();
15231
+ if (!root.ok)
15232
+ return root;
15233
+ if (this.context.caller.host !== "workit_cli" || (this.context.workerId ?? null) !== null)
15234
+ return failure2("permission_denied", "only the workit CLI records observed checks");
15235
+ const observation = checkObservationSchema.safeParse(request?.observation);
15236
+ if (!observation.success)
15237
+ return failure2("invalid_input", "check observation is invalid", {
15238
+ fields: observation.error.issues.map((issue) => ({
15239
+ path: issue.path.join("."),
15240
+ reason: issue.message
15241
+ }))
15242
+ });
15243
+ const task = this.store.readTask(request.taskId);
15244
+ if (!task.ok)
15245
+ return task;
15246
+ if (task.data.status === "closed")
15247
+ return failure2("invalid_transition", "closed task cannot record evidence");
15248
+ const observed = observation.data;
15249
+ let candidate = null;
15250
+ if (observed.tree === null) {
15251
+ const captured = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
15252
+ if (!captured.ok)
15253
+ return captured;
15254
+ candidate = captured.data;
15255
+ }
15256
+ const label = observed.name ?? observed.argv.join(" ");
15257
+ const data = {
15258
+ kind: "check",
15259
+ claim: `workit check ${label}`.slice(0, 500),
15260
+ requirementIds: [],
15261
+ ...candidate ? { beforeCandidateId: candidate.id, candidateId: candidate.id } : {},
15262
+ result: observed.exitCode === 0 ? "passed" : "failed",
15263
+ summary: `${observed.configured ? "configured" : "ad-hoc"} check exited ${observed.exitCode}${observed.timedOut ? " (timed out)" : ""} in ${observed.durationMs} ms`,
15264
+ refs: [],
15265
+ exitCode: observed.exitCode,
15266
+ reviewContext: null,
15267
+ observation: observed
15268
+ };
15269
+ const changed = this.store.mutateTask(task.data.id, request.expectedRevision ?? task.data.revision, (current, mutation) => {
15270
+ const candidates = candidate === null || current.candidates.some((item) => item.id === candidate.id) ? current.candidates : [...current.candidates, candidate];
15271
+ const critical = [...new Set([...current.critical ?? [], CHECK_OBSERVATION_PATH])];
15272
+ return success(mutation.revision, null, {
15273
+ ...current,
15274
+ critical,
15275
+ candidates,
15276
+ evidence: [
15277
+ ...current.evidence,
15278
+ {
15279
+ id: newId(),
15280
+ recordedAt: mutation.now,
15281
+ provenance: provenance(this.context, "host_observed"),
15282
+ data
15283
+ }
15284
+ ]
15285
+ });
15286
+ }, trustedNow(this.context));
15287
+ if (!changed.ok)
15288
+ return changed;
15289
+ return success(changed.data.revision, null, changed.data.evidence.at(-1));
15290
+ }
14771
15291
  decision(request) {
14772
15292
  return this.recordDecision(request);
14773
15293
  }
@@ -15003,7 +15523,7 @@ class WorkitCore {
15003
15523
  const current = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
15004
15524
  if (!current.ok)
15005
15525
  return current;
15006
- const evaluations = evaluateEvidence(task.data, current.data);
15526
+ const evaluations = evaluateEvidence(task.data, current.data, treeFreshness(this.store.root));
15007
15527
  const verified = evidence.some((entry) => {
15008
15528
  const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
15009
15529
  return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
@@ -15707,7 +16227,8 @@ class WorkitCore {
15707
16227
  if (!current.ok)
15708
16228
  return current;
15709
16229
  const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
15710
- const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
16230
+ const tree = treeFreshness(this.store.root);
16231
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller, tree);
15711
16232
  return success(task.revision, workspace.data.revision, {
15712
16233
  id: task.id,
15713
16234
  revision: task.revision,
@@ -15724,7 +16245,7 @@ class WorkitCore {
15724
16245
  writer: evaluationWorkspace.writer
15725
16246
  });
15726
16247
  }
15727
- compactContext(taskId) {
16248
+ compactContext(taskId, freshness) {
15728
16249
  const root = this.contextRootError();
15729
16250
  if (!root.ok)
15730
16251
  return root;
@@ -15734,12 +16255,12 @@ class WorkitCore {
15734
16255
  const helper = this.helperTaskGuard(task.data, true);
15735
16256
  if (!helper.ok)
15736
16257
  return helper;
15737
- const view = this.view(task.data, false);
16258
+ const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
15738
16259
  if (!view.ok)
15739
16260
  return view;
15740
16261
  return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
15741
16262
  }
15742
- view(task, capture = true) {
16263
+ view(task, capture = true, freshness = treeFreshness(this.store.root)) {
15743
16264
  const workspace = this.store.readWorkspace();
15744
16265
  if (!workspace.ok)
15745
16266
  return workspace;
@@ -15750,13 +16271,14 @@ class WorkitCore {
15750
16271
  if (!current.ok)
15751
16272
  return current;
15752
16273
  const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
15753
- const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
16274
+ const tree = freshness;
16275
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller, tree);
15754
16276
  return success(task.revision, workspace.data.revision, {
15755
16277
  task,
15756
16278
  workspace: evaluationWorkspace,
15757
16279
  capabilities: this.context.capabilities,
15758
16280
  requirements,
15759
- evidence: evaluateEvidence(task, current.data)
16281
+ evidence: evaluateEvidence(task, current.data, tree)
15760
16282
  });
15761
16283
  }
15762
16284
  reconcileResume(view, observations = []) {
@@ -15983,13 +16505,14 @@ class WorkitCore {
15983
16505
  return current;
15984
16506
  const withCandidate = current.data;
15985
16507
  const taskForView = withCandidate.id === task.data.candidates.at(-1)?.id ? task.data : { ...task.data, candidates: [...task.data.candidates, withCandidate] };
15986
- const requirements = evaluateRequirements(taskForView, workspace.data, this.context.capabilities, withCandidate, this.store.root, this.context.caller);
16508
+ const tree = treeFreshness(this.store.root);
16509
+ const requirements = evaluateRequirements(taskForView, workspace.data, this.context.capabilities, withCandidate, this.store.root, this.context.caller, tree);
15987
16510
  const view = {
15988
16511
  task: taskForView,
15989
16512
  workspace: workspace.data,
15990
16513
  capabilities: this.context.capabilities,
15991
16514
  requirements,
15992
- evidence: evaluateEvidence(taskForView, withCandidate)
16515
+ evidence: evaluateEvidence(taskForView, withCandidate, tree)
15993
16516
  };
15994
16517
  const closure = evaluateClosure(input.outcome, view);
15995
16518
  if (!closure.ok)
@@ -16053,22 +16576,22 @@ function resolveGitRevision(root, value) {
16053
16576
  }
16054
16577
  function resolveInside(root, candidate) {
16055
16578
  const base = realpathSync7(root);
16056
- const target = path11.resolve(base, candidate);
16057
- if (target !== base && !target.startsWith(base + path11.sep)) {
16579
+ const target = path13.resolve(base, candidate);
16580
+ if (target !== base && !target.startsWith(base + path13.sep)) {
16058
16581
  throw new Error("path must stay inside repository root");
16059
16582
  }
16060
16583
  let ancestor = target;
16061
16584
  while (!existsSync5(ancestor))
16062
- ancestor = path11.dirname(ancestor);
16585
+ ancestor = path13.dirname(ancestor);
16063
16586
  const canonicalAncestor = realpathSync7(ancestor);
16064
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path11.sep)) {
16587
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path13.sep)) {
16065
16588
  throw new Error("path must stay inside repository root");
16066
16589
  }
16067
16590
  return target;
16068
16591
  }
16069
16592
  function run2(root, executable, args, env = {}) {
16070
16593
  const cwd = realpathSync7(root);
16071
- const result = spawnSync4(executable, args, {
16594
+ const result = spawnSync6(executable, args, {
16072
16595
  cwd,
16073
16596
  encoding: "utf8",
16074
16597
  env: { ...process.env, ...env }
@@ -16096,7 +16619,9 @@ var WORKIT_METHOD_SKILLS = [
16096
16619
  "workit-diagram",
16097
16620
  "workit-mockup",
16098
16621
  "workit-green-run",
16099
- "workit-steer"
16622
+ "workit-steer",
16623
+ "workit-bdd",
16624
+ "workit-test-audit"
16100
16625
  ];
16101
16626
  var WORKIT_SKILL_ALIASES = {
16102
16627
  "wk-challenge": "workit-challenge",
@@ -16112,14 +16637,16 @@ var WORKIT_SKILL_ALIASES = {
16112
16637
  "wk-diagram": "workit-diagram",
16113
16638
  "wk-mockup": "workit-mockup",
16114
16639
  "wk-green-run": "workit-green-run",
16115
- "wk-steer": "workit-steer"
16640
+ "wk-steer": "workit-steer",
16641
+ "wk-bdd": "workit-bdd",
16642
+ "wk-test-audit": "workit-test-audit"
16116
16643
  };
16117
16644
  if (false) {}
16118
16645
 
16119
16646
  // packages/workit-core/src/core/logger.ts
16120
16647
  import { appendFileSync, mkdirSync as mkdirSync3, readdirSync as readdirSync4, unlinkSync as unlinkSync2 } from "node:fs";
16121
- import os2 from "node:os";
16122
- import path12 from "node:path";
16648
+ import os3 from "node:os";
16649
+ import path14 from "node:path";
16123
16650
  var REDACTED = "[REDACTED]";
16124
16651
  var DEFAULT_MAX_RATE = 20;
16125
16652
  var DEFAULT_RATE_WINDOW_MS = 1000;
@@ -16173,19 +16700,19 @@ var resolveStateDir = () => {
16173
16700
  const override = process.env.WORKFLOW_TOOLKIT_STATE;
16174
16701
  if (override)
16175
16702
  return override;
16176
- const home = os2.homedir();
16703
+ const home = os3.homedir();
16177
16704
  if (process.env.XDG_STATE_HOME)
16178
- return path12.join(process.env.XDG_STATE_HOME, "workit");
16179
- switch (os2.platform()) {
16705
+ return path14.join(process.env.XDG_STATE_HOME, "workit");
16706
+ switch (os3.platform()) {
16180
16707
  case "darwin":
16181
- return path12.join(home, "Library", "Application Support", "workit");
16708
+ return path14.join(home, "Library", "Application Support", "workit");
16182
16709
  case "win32":
16183
- return path12.join(process.env.LOCALAPPDATA ?? path12.join(home, "AppData", "Local"), "workit");
16710
+ return path14.join(process.env.LOCALAPPDATA ?? path14.join(home, "AppData", "Local"), "workit");
16184
16711
  default:
16185
- return path12.join(process.env.HOME ?? home, ".local", "state", "workit");
16712
+ return path14.join(process.env.HOME ?? home, ".local", "state", "workit");
16186
16713
  }
16187
16714
  };
16188
- var homePattern = new RegExp(`${escapeRegExp(os2.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
16715
+ var homePattern = new RegExp(`${escapeRegExp(os3.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
16189
16716
  var applyPatterns = (value) => {
16190
16717
  let out = value;
16191
16718
  out = out.replace(SECRET_VALUE, REDACTED);
@@ -16255,14 +16782,14 @@ var pruneOldFiles = (dir) => {
16255
16782
  const files = readdirSync4(dir).filter((name) => DAY_FILE.test(name)).toSorted().toReversed();
16256
16783
  for (const file of files.slice(RETAINED_DAYS)) {
16257
16784
  try {
16258
- unlinkSync2(path12.join(dir, file));
16785
+ unlinkSync2(path14.join(dir, file));
16259
16786
  } catch {}
16260
16787
  }
16261
16788
  } catch {}
16262
16789
  };
16263
16790
  var createLogger = (options = {}) => {
16264
16791
  const stateDir = options.stateDir ?? resolveStateDir();
16265
- const logDir = path12.join(stateDir, "logs");
16792
+ const logDir = path14.join(stateDir, "logs");
16266
16793
  const now = options.now ?? (() => new Date);
16267
16794
  const maxRate = options.maxRate ?? DEFAULT_MAX_RATE;
16268
16795
  const rateWindowMs = options.rateWindowMs ?? DEFAULT_RATE_WINDOW_MS;
@@ -16296,7 +16823,7 @@ var createLogger = (options = {}) => {
16296
16823
  const line = `${JSON.stringify(event)}
16297
16824
  `;
16298
16825
  mkdirSync3(logDir, { recursive: true, mode: 448 });
16299
- appendFileSync(path12.join(logDir, dailyFileName(date)), line, { mode: 384 });
16826
+ appendFileSync(path14.join(logDir, dailyFileName(date)), line, { mode: 384 });
16300
16827
  pruneOldFiles(logDir);
16301
16828
  } catch {
16302
16829
  try {
@@ -16307,7 +16834,7 @@ var createLogger = (options = {}) => {
16307
16834
  context: { redaction_failed: true }
16308
16835
  };
16309
16836
  mkdirSync3(logDir, { recursive: true, mode: 448 });
16310
- appendFileSync(path12.join(logDir, dailyFileName(date)), `${JSON.stringify(event)}
16837
+ appendFileSync(path14.join(logDir, dailyFileName(date)), `${JSON.stringify(event)}
16311
16838
  `, {
16312
16839
  mode: 384
16313
16840
  });
@@ -16356,47 +16883,47 @@ OpenCode Workit decisions use the native question header "Workit decision: <purp
16356
16883
 
16357
16884
  // packages/workit-opencode/src/shared/assets.ts
16358
16885
  import { existsSync as existsSync6, realpathSync as realpathSync8 } from "node:fs";
16359
- import path13 from "node:path";
16886
+ import path15 from "node:path";
16360
16887
  import { fileURLToPath as fileURLToPath2 } from "node:url";
16361
16888
  var moduleDir = () => {
16362
16889
  try {
16363
- return realpathSync8(path13.dirname(fileURLToPath2(import.meta.url)));
16890
+ return realpathSync8(path15.dirname(fileURLToPath2(import.meta.url)));
16364
16891
  } catch {
16365
- return path13.dirname(fileURLToPath2(import.meta.url));
16892
+ return path15.dirname(fileURLToPath2(import.meta.url));
16366
16893
  }
16367
16894
  };
16368
16895
  var packageRoot2 = () => {
16369
16896
  const dir = moduleDir();
16370
16897
  for (const candidate of [
16371
- path13.resolve(dir, ".."),
16372
- path13.resolve(dir, "..", ".."),
16373
- path13.resolve(dir, "..", "..", "..")
16898
+ path15.resolve(dir, ".."),
16899
+ path15.resolve(dir, "..", ".."),
16900
+ path15.resolve(dir, "..", "..", "..")
16374
16901
  ]) {
16375
- if (existsSync6(path13.join(candidate, "package.json")))
16902
+ if (existsSync6(path15.join(candidate, "package.json")))
16376
16903
  return candidate;
16377
16904
  }
16378
- return path13.resolve(dir, "..");
16905
+ return path15.resolve(dir, "..");
16379
16906
  };
16380
- var assetsRoot = () => path13.join(packageRoot2(), "assets");
16907
+ var assetsRoot = () => path15.join(packageRoot2(), "assets");
16381
16908
 
16382
16909
  // packages/workit-opencode/src/stale-sources.ts
16383
16910
  import { existsSync as existsSync7, readdirSync as readdirSync5 } from "node:fs";
16384
- import path14 from "node:path";
16911
+ import path16 from "node:path";
16385
16912
  import { fileURLToPath as fileURLToPath3 } from "node:url";
16386
16913
  var typescriptFiles = (directory) => {
16387
16914
  if (!existsSync7(directory))
16388
16915
  return [];
16389
16916
  return readdirSync5(directory, { withFileTypes: true }).flatMap((entry) => {
16390
- const file = path14.join(directory, entry.name);
16917
+ const file = path16.join(directory, entry.name);
16391
16918
  return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
16392
16919
  });
16393
16920
  };
16394
16921
  var pluginSourceFiles = [
16395
16922
  ...new Set([
16396
16923
  fileURLToPath3(import.meta.url),
16397
- ...typescriptFiles(path14.join(packageRoot2(), "src")),
16398
- path14.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
16399
- ...typescriptFiles(path14.join(packageRoot2(), "..", "workit-core", "src", "core"))
16924
+ ...typescriptFiles(path16.join(packageRoot2(), "src")),
16925
+ path16.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
16926
+ ...typescriptFiles(path16.join(packageRoot2(), "..", "workit-core", "src", "core"))
16400
16927
  ])
16401
16928
  ].filter((file) => existsSync7(file)).toSorted();
16402
16929
 
@@ -28721,11 +29248,11 @@ function tool(input) {
28721
29248
  }
28722
29249
  tool.schema = exports_external2;
28723
29250
  // packages/workit-opencode/src/shared/init-apply.ts
28724
- import path20 from "node:path";
29251
+ import path22 from "node:path";
28725
29252
 
28726
29253
  // packages/workit-core/src/core/gitignore.ts
28727
29254
  import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
28728
- import path15 from "node:path";
29255
+ import path17 from "node:path";
28729
29256
  var GITIGNORE_ENTRIES = [
28730
29257
  "# workit: SDD working state (never commit)",
28731
29258
  "docs/*/sdd/",
@@ -28745,7 +29272,7 @@ var GITIGNORE_ENTRIES = [
28745
29272
  var ensureProjectGitignore = (workspaceRoot, confirmed) => {
28746
29273
  if (!confirmed)
28747
29274
  return { ok: false, error: "confirmed: true required" };
28748
- const file = path15.join(workspaceRoot, ".gitignore");
29275
+ const file = path17.join(workspaceRoot, ".gitignore");
28749
29276
  const existing = existsSync8(file) ? readFileSync7(file, "utf8") : "";
28750
29277
  const existingLines = new Set(existing.split(`
28751
29278
  `).map((l) => l.trim()).filter(Boolean));
@@ -28773,7 +29300,7 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
28773
29300
 
28774
29301
  // packages/workit-core/src/core/hygiene.ts
28775
29302
  import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "node:fs";
28776
- import path16 from "node:path";
29303
+ import path18 from "node:path";
28777
29304
 
28778
29305
  // packages/workit-core/src/core/scripts.ts
28779
29306
  var PLUGIN_ROOT = packageRoot();
@@ -28782,23 +29309,23 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
28782
29309
 
28783
29310
  // packages/workit-core/src/core/hygiene.ts
28784
29311
  var repoRoot = assetRoot();
28785
- var templatesDir = () => path16.join(repoRoot, "templates", "hygiene");
29312
+ var templatesDir = () => path18.join(repoRoot, "templates", "hygiene");
28786
29313
  var packageJson = (root) => {
28787
- if (!existsSync9(path16.join(root, "package.json")))
29314
+ if (!existsSync9(path18.join(root, "package.json")))
28788
29315
  return null;
28789
29316
  try {
28790
- return JSON.parse(readFileSync8(path16.join(root, "package.json"), "utf8"));
29317
+ return JSON.parse(readFileSync8(path18.join(root, "package.json"), "utf8"));
28791
29318
  } catch {
28792
29319
  return null;
28793
29320
  }
28794
29321
  };
28795
29322
  var isOpenSource = (root) => {
28796
- if (existsSync9(path16.join(root, "LICENSE")))
29323
+ if (existsSync9(path18.join(root, "LICENSE")))
28797
29324
  return true;
28798
29325
  const pkg = packageJson(root);
28799
29326
  if (pkg && !pkg.private)
28800
29327
  return true;
28801
- return path16.basename(root) === "workflow-toolkit";
29328
+ return path18.basename(root) === "workflow-toolkit";
28802
29329
  };
28803
29330
  var licenseHolder = (root) => {
28804
29331
  const pkg = packageJson(root);
@@ -28821,13 +29348,13 @@ var planHygieneFiles = (root, opts = {}) => {
28821
29348
  files.push("LICENSE", "CONTRIBUTING.md");
28822
29349
  const planned = [];
28823
29350
  for (const file of files) {
28824
- if (existsSync9(path16.join(root, file)))
29351
+ if (existsSync9(path18.join(root, file)))
28825
29352
  continue;
28826
- const tpl = path16.join(templatesDir(), file);
29353
+ const tpl = path18.join(templatesDir(), file);
28827
29354
  if (!existsSync9(tpl))
28828
29355
  continue;
28829
- const content = readFileSync8(tpl, "utf8").replace(/<PROJECT>/g, path16.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
28830
- planned.push({ path: path16.join(root, file), content });
29356
+ const content = readFileSync8(tpl, "utf8").replace(/<PROJECT>/g, path18.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
29357
+ planned.push({ path: path18.join(root, file), content });
28831
29358
  }
28832
29359
  return planned;
28833
29360
  };
@@ -28837,15 +29364,15 @@ var ensureHygieneFiles = (root, opts) => {
28837
29364
  const created = [];
28838
29365
  for (const planned of planHygieneFiles(root, opts)) {
28839
29366
  writeFileSync4(planned.path, planned.content, "utf8");
28840
- created.push(path16.basename(planned.path));
29367
+ created.push(path18.basename(planned.path));
28841
29368
  }
28842
29369
  return { ok: true, created };
28843
29370
  };
28844
29371
 
28845
29372
  // packages/workit-core/src/core/init.ts
28846
- import fs10 from "node:fs";
28847
- import path19 from "node:path";
28848
- import { spawnSync as spawnSync6 } from "node:child_process";
29373
+ import fs12 from "node:fs";
29374
+ import path21 from "node:path";
29375
+ import { spawnSync as spawnSync8 } from "node:child_process";
28849
29376
 
28850
29377
  // packages/workit-core/src/core/safe-write.ts
28851
29378
  import {
@@ -28886,11 +29413,11 @@ import {
28886
29413
  statSync as statSync3,
28887
29414
  writeFileSync as writeFileSync6
28888
29415
  } from "node:fs";
28889
- import path17 from "node:path";
29416
+ import path19 from "node:path";
28890
29417
  import { isDeepStrictEqual } from "node:util";
28891
29418
 
28892
29419
  // packages/workit-core/src/core/branch-policy.ts
28893
- import { spawnSync as spawnSync5 } from "node:child_process";
29420
+ import { spawnSync as spawnSync7 } from "node:child_process";
28894
29421
  var PREFIXES = {
28895
29422
  feature: "feature/*",
28896
29423
  bugfix: "bugfix/*",
@@ -28899,7 +29426,7 @@ var PREFIXES = {
28899
29426
  };
28900
29427
  var detectBranchPolicy = (workspaceRoot) => {
28901
29428
  const branchExists = (name) => {
28902
- const r = spawnSync5("git", ["branch", "--list", name], {
29429
+ const r = spawnSync7("git", ["branch", "--list", name], {
28903
29430
  cwd: workspaceRoot,
28904
29431
  encoding: "utf8"
28905
29432
  });
@@ -29000,7 +29527,7 @@ var readFileSafe = (p) => {
29000
29527
  };
29001
29528
  function applyWorkspaceBranchPolicy(opts) {
29002
29529
  const { workspace_root, env = process.env } = opts;
29003
- const dir = path17.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
29530
+ const dir = path19.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
29004
29531
  const {
29005
29532
  status,
29006
29533
  path: wsPath,
@@ -29012,7 +29539,7 @@ function applyWorkspaceBranchPolicy(opts) {
29012
29539
  if (status === "malformed" || status === "invalid")
29013
29540
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
29014
29541
  const detection = detectBranchPolicy(workspace_root);
29015
- const name = String(env.WORKFLOW_BP_NAME ?? path17.basename(workspace_root));
29542
+ const name = String(env.WORKFLOW_BP_NAME ?? path19.basename(workspace_root));
29016
29543
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
29017
29544
  const policy = {
29018
29545
  preset: detection.preset,
@@ -29061,7 +29588,7 @@ function applyWorkspaceBranchPolicy(opts) {
29061
29588
  ok: false,
29062
29589
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
29063
29590
  };
29064
- mkdirSync5(path17.dirname(wsPath), { recursive: true });
29591
+ mkdirSync5(path19.dirname(wsPath), { recursive: true });
29065
29592
  const tmp = `${wsPath}.${process.pid}.tmp`;
29066
29593
  try {
29067
29594
  writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
@@ -29093,8 +29620,8 @@ function applyWorkspaceBranchPolicy(opts) {
29093
29620
  }
29094
29621
 
29095
29622
  // packages/workit-core/src/core/youtrack.ts
29096
- import fs9 from "node:fs";
29097
- import path18 from "node:path";
29623
+ import fs11 from "node:fs";
29624
+ import path20 from "node:path";
29098
29625
 
29099
29626
  // packages/workit-core/src/core/templates.ts
29100
29627
  var repoRoot2 = assetRoot();
@@ -29102,22 +29629,22 @@ var repoRoot2 = assetRoot();
29102
29629
  // packages/workit-core/src/core/youtrack.ts
29103
29630
  var ISSUE_RE = /^[A-Z]+-\d+$/;
29104
29631
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
29105
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(configDir(), "youtrack.json");
29106
- var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(resolveConfigDir(), "youtrack.json");
29632
+ var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(configDir(), "youtrack.json");
29633
+ var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(resolveConfigDir(), "youtrack.json");
29107
29634
  var youTrackTokenModeOk = (p) => {
29108
29635
  if (process.platform === "win32")
29109
29636
  return true;
29110
- const mode = fs9.statSync(p).mode & 511;
29637
+ const mode = fs11.statSync(p).mode & 511;
29111
29638
  return mode === 384;
29112
29639
  };
29113
29640
  function readYouTrackConfig(required) {
29114
29641
  const cfgPath = youTrackConfigPath();
29115
- if (!fs9.existsSync(cfgPath)) {
29642
+ if (!fs11.existsSync(cfgPath)) {
29116
29643
  return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
29117
29644
  }
29118
29645
  let parsed;
29119
29646
  try {
29120
- parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
29647
+ parsed = JSON.parse(fs11.readFileSync(cfgPath, "utf8"));
29121
29648
  } catch {
29122
29649
  return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
29123
29650
  }
@@ -29128,11 +29655,11 @@ function readYouTrackConfig(required) {
29128
29655
  }
29129
29656
  var readYouTrackContextConfig = () => {
29130
29657
  const cfgPath = youTrackReadOnlyConfigPath();
29131
- if (!fs9.existsSync(cfgPath))
29658
+ if (!fs11.existsSync(cfgPath))
29132
29659
  return { data: {} };
29133
29660
  let parsed;
29134
29661
  try {
29135
- parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
29662
+ parsed = JSON.parse(fs11.readFileSync(cfgPath, "utf8"));
29136
29663
  } catch {
29137
29664
  return { error: "YouTrack configuration is unavailable" };
29138
29665
  }
@@ -29171,12 +29698,12 @@ var youTrackToken = () => {
29171
29698
  if ("error" in loaded)
29172
29699
  return loaded;
29173
29700
  const tokenFile = String(loaded.config.tokenFile ?? "");
29174
- const tokenPath = tokenFile ? path18.isAbsolute(tokenFile) ? path18.resolve(tokenFile) : path18.resolve(process.cwd(), tokenFile) : "";
29175
- if (!tokenPath || !fs9.existsSync(tokenPath))
29701
+ const tokenPath = tokenFile ? path20.isAbsolute(tokenFile) ? path20.resolve(tokenFile) : path20.resolve(process.cwd(), tokenFile) : "";
29702
+ if (!tokenPath || !fs11.existsSync(tokenPath))
29176
29703
  return { error: "missing youtrack.token" };
29177
29704
  if (!youTrackTokenModeOk(tokenPath))
29178
29705
  return { error: "youtrack.token mode must be 0600" };
29179
- const token = fs9.readFileSync(tokenPath, "utf8").trim();
29706
+ const token = fs11.readFileSync(tokenPath, "utf8").trim();
29180
29707
  if (!token)
29181
29708
  return { error: "empty token file" };
29182
29709
  if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
@@ -29222,7 +29749,7 @@ function youTrackTokenCreateUrl() {
29222
29749
  const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
29223
29750
  const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
29224
29751
  const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
29225
- const tokenFile = String(config.tokenFile ?? path18.join(path18.dirname(loaded.path), "youtrack.token"));
29752
+ const tokenFile = String(config.tokenFile ?? path20.join(path20.dirname(loaded.path), "youtrack.token"));
29226
29753
  const tab = String(defaults.profileTab ?? "account-security");
29227
29754
  const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
29228
29755
  const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
@@ -29231,7 +29758,7 @@ function youTrackTokenCreateUrl() {
29231
29758
  tokenName: name,
29232
29759
  tokenDescription: desc,
29233
29760
  scopes,
29234
- tokenFile: path18.resolve(tokenFile),
29761
+ tokenFile: path20.resolve(tokenFile),
29235
29762
  createUrl,
29236
29763
  docsUrl,
29237
29764
  prefillSupported: false,
@@ -29277,9 +29804,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
29277
29804
  } catch {
29278
29805
  continue;
29279
29806
  }
29280
- if (!fs9.existsSync(full))
29807
+ if (!fs11.existsSync(full))
29281
29808
  continue;
29282
- const text = fs9.readFileSync(full, "utf8");
29809
+ const text = fs11.readFileSync(full, "utf8");
29283
29810
  const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
29284
29811
  if (m)
29285
29812
  return m[1];
@@ -29430,7 +29957,7 @@ var fetchYouTrackIssueBody = async (issue, creds = youTrackToken, request = youT
29430
29957
  var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
29431
29958
  var readJson = (p) => {
29432
29959
  try {
29433
- const parsed = JSON.parse(fs10.readFileSync(p, "utf8"));
29960
+ const parsed = JSON.parse(fs12.readFileSync(p, "utf8"));
29434
29961
  return isConfigObject(parsed) ? parsed : null;
29435
29962
  } catch {
29436
29963
  return null;
@@ -29442,7 +29969,7 @@ var resolveInitProvider = (cwd) => {
29442
29969
  return env.toLowerCase();
29443
29970
  const root = cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
29444
29971
  try {
29445
- const out = spawnSync6("git", ["remote", "get-url", "origin"], {
29972
+ const out = spawnSync8("git", ["remote", "get-url", "origin"], {
29446
29973
  cwd: root,
29447
29974
  encoding: "utf8"
29448
29975
  });
@@ -29460,7 +29987,7 @@ var youtrackJsonContent = (dir) => {
29460
29987
  const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
29461
29988
  return {
29462
29989
  baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
29463
- tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path19.join(dir, "youtrack.token"),
29990
+ tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path21.join(dir, "youtrack.token"),
29464
29991
  ...meetingIssue ? {
29465
29992
  meetingIssue,
29466
29993
  meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
@@ -29490,19 +30017,19 @@ var vcsJsonContent = () => {
29490
30017
  };
29491
30018
  function initApplyData(action, env = process.env) {
29492
30019
  const dir = String(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
29493
- fs10.mkdirSync(dir, { recursive: true });
30020
+ fs12.mkdirSync(dir, { recursive: true });
29494
30021
  switch (action) {
29495
30022
  case "youtrack_json": {
29496
- const out = path19.join(dir, "youtrack.json");
29497
- fs10.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
30023
+ const out = path21.join(dir, "youtrack.json");
30024
+ fs12.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
29498
30025
  `, "utf8");
29499
30026
  return { action, ok: true, path: out };
29500
30027
  }
29501
30028
  case "youtrack_token_placeholder": {
29502
- const p = path19.join(dir, "youtrack.token");
30029
+ const p = path21.join(dir, "youtrack.token");
29503
30030
  const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
29504
30031
  `, 384) === "preserved";
29505
- const abs = path19.resolve(p);
30032
+ const abs = path21.resolve(p);
29506
30033
  return {
29507
30034
  action,
29508
30035
  ok: true,
@@ -29514,14 +30041,14 @@ function initApplyData(action, env = process.env) {
29514
30041
  };
29515
30042
  }
29516
30043
  case "youtrack_scaffold": {
29517
- const jsonOut = path19.join(dir, "youtrack.json");
29518
- const tokenOut = path19.join(dir, "youtrack.token");
29519
- fs10.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
30044
+ const jsonOut = path21.join(dir, "youtrack.json");
30045
+ const tokenOut = path21.join(dir, "youtrack.token");
30046
+ fs12.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
29520
30047
  `, "utf8");
29521
30048
  const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
29522
30049
  `, 384) === "preserved";
29523
- const configPath = path19.resolve(jsonOut);
29524
- const tokenPath = path19.resolve(tokenOut);
30050
+ const configPath = path21.resolve(jsonOut);
30051
+ const tokenPath = path21.resolve(tokenOut);
29525
30052
  const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
29526
30053
  process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
29527
30054
  let tokenCreate = {};
@@ -29573,10 +30100,10 @@ function initApplyData(action, env = process.env) {
29573
30100
  }
29574
30101
  }
29575
30102
  case "vcs_scaffold": {
29576
- const jsonOut = path19.join(dir, "vcs.json");
29577
- fs10.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
30103
+ const jsonOut = path21.join(dir, "vcs.json");
30104
+ fs12.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
29578
30105
  `, "utf8");
29579
- const configPath = path19.resolve(jsonOut);
30106
+ const configPath = path21.resolve(jsonOut);
29580
30107
  const prev = process.env.WORKFLOW_VCS_CONFIG;
29581
30108
  process.env.WORKFLOW_VCS_CONFIG = configPath;
29582
30109
  try {
@@ -29695,7 +30222,7 @@ var executeInitApply = (args, directory, runtime) => {
29695
30222
  protectedNames: args.branch_policy_protected
29696
30223
  }, current);
29697
30224
  writeConfig(next);
29698
- return output(ok({ action: "config", path: path20.join(configDir(), "config.json"), ...next }));
30225
+ return output(ok({ action: "config", path: path22.join(configDir(), "config.json"), ...next }));
29699
30226
  }
29700
30227
  const env = Object.fromEntries(Object.entries({
29701
30228
  WORKFLOW_YT_BASE_URL: args.base_url,
@@ -29748,12 +30275,12 @@ function createRepoTools(runtime = initApplyRuntime) {
29748
30275
 
29749
30276
  // packages/workit-opencode/src/tools/context.ts
29750
30277
  import { realpathSync as realpathSync10 } from "node:fs";
29751
- import path23 from "node:path";
30278
+ import path25 from "node:path";
29752
30279
 
29753
30280
  // packages/workit-core/src/core/pr-create.ts
29754
- import fs11 from "node:fs";
29755
- import path21 from "node:path";
29756
- import { spawnSync as spawnSync7 } from "node:child_process";
30281
+ import fs13 from "node:fs";
30282
+ import path23 from "node:path";
30283
+ import { spawnSync as spawnSync9 } from "node:child_process";
29757
30284
  // packages/workit-core/src/forge/pr-body.ts
29758
30285
  function parseGhIssue(value) {
29759
30286
  const m = /issues\/(\d+)/.exec(value);
@@ -29763,13 +30290,13 @@ function parseGhIssue(value) {
29763
30290
  // packages/workit-core/src/core/pr-create.ts
29764
30291
  function whichOnPath(tool) {
29765
30292
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
29766
- for (const dir of (process.env.PATH ?? "").split(path21.delimiter)) {
30293
+ for (const dir of (process.env.PATH ?? "").split(path23.delimiter)) {
29767
30294
  if (!dir)
29768
30295
  continue;
29769
30296
  for (const name of names) {
29770
- const candidate = path21.join(dir, name);
30297
+ const candidate = path23.join(dir, name);
29771
30298
  try {
29772
- fs11.accessSync(candidate, fs11.constants.X_OK);
30299
+ fs13.accessSync(candidate, fs13.constants.X_OK);
29773
30300
  return candidate;
29774
30301
  } catch {}
29775
30302
  }
@@ -29778,7 +30305,7 @@ function whichOnPath(tool) {
29778
30305
  }
29779
30306
  var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
29780
30307
  var pushRemote = (cwd) => {
29781
- const result = spawnSync7("git", ["remote", "get-url", "--push", "--all", "origin"], {
30308
+ const result = spawnSync9("git", ["remote", "get-url", "--push", "--all", "origin"], {
29782
30309
  cwd,
29783
30310
  encoding: "utf8"
29784
30311
  });
@@ -29789,9 +30316,9 @@ var pushRemote = (cwd) => {
29789
30316
  };
29790
30317
 
29791
30318
  // packages/workit-core/src/core/repo-context.ts
29792
- import { spawnSync as spawnSync8 } from "node:child_process";
30319
+ import { spawnSync as spawnSync10 } from "node:child_process";
29793
30320
  import { existsSync as existsSync11, readFileSync as readFileSync10, readdirSync as readdirSync7 } from "node:fs";
29794
- import path22 from "node:path";
30321
+ import path24 from "node:path";
29795
30322
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
29796
30323
  try {
29797
30324
  gitRevisionParts(value);
@@ -29818,7 +30345,7 @@ var invalidRange = (cwd) => ({
29818
30345
  cwd
29819
30346
  });
29820
30347
  var runGit = (cwd, args) => {
29821
- const result = spawnSync8("git", args, { cwd, encoding: "utf8" });
30348
+ const result = spawnSync10("git", args, { cwd, encoding: "utf8" });
29822
30349
  return {
29823
30350
  stdout: (result.stdout ?? "").trimEnd(),
29824
30351
  stderr: (result.stderr ?? "").trimEnd(),
@@ -29925,7 +30452,7 @@ var findPrTemplate = (cwd) => {
29925
30452
  ]) {
29926
30453
  const parts = rel.split("/");
29927
30454
  const base = parts[parts.length - 1];
29928
- const dir = parts.length > 1 ? path22.join(cwd, ...parts.slice(0, -1)) : cwd;
30455
+ const dir = parts.length > 1 ? path24.join(cwd, ...parts.slice(0, -1)) : cwd;
29929
30456
  const actual = probeCaseInsensitive(dir, base);
29930
30457
  if (actual)
29931
30458
  return [...parts.slice(0, -1), actual].join("/");
@@ -29956,7 +30483,7 @@ var diffStatForRange = (cwd, range) => {
29956
30483
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
29957
30484
  };
29958
30485
  var packageScripts = (cwd, keys) => {
29959
- const pkgPath = path22.join(cwd, "package.json");
30486
+ const pkgPath = path24.join(cwd, "package.json");
29960
30487
  if (!existsSync11(pkgPath))
29961
30488
  return [];
29962
30489
  try {
@@ -29982,9 +30509,9 @@ var documentationFiles = (cwd) => {
29982
30509
  if (entry.isDirectory()) {
29983
30510
  if (excluded.has(entry.name))
29984
30511
  continue;
29985
- walk(path22.join(dir, entry.name), depth + 1);
30512
+ walk(path24.join(dir, entry.name), depth + 1);
29986
30513
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
29987
- matches.push("./" + path22.relative(cwd, path22.join(dir, entry.name)).replaceAll("\\", "/"));
30514
+ matches.push("./" + path24.relative(cwd, path24.join(dir, entry.name)).replaceAll("\\", "/"));
29988
30515
  }
29989
30516
  }
29990
30517
  };
@@ -30000,7 +30527,7 @@ var readTrimmed2 = (file, maxLines) => {
30000
30527
  `);
30001
30528
  };
30002
30529
  function prReadyContext(root, range) {
30003
- const cwd = path22.resolve(repoRoot3(root));
30530
+ const cwd = path24.resolve(repoRoot3(root));
30004
30531
  if (range !== undefined && !isResolvableContextRange(cwd, range))
30005
30532
  return invalidRange(cwd);
30006
30533
  let stdout = "";
@@ -30058,7 +30585,7 @@ ${status.stderr}` : "");
30058
30585
  stdout += `template_path: ${template}
30059
30586
 
30060
30587
  `;
30061
- stdout += readTrimmed2(path22.join(cwd, template), 220);
30588
+ stdout += readTrimmed2(path24.join(cwd, template), 220);
30062
30589
  } else {
30063
30590
  stdout += `template_path: none
30064
30591
 
@@ -30068,7 +30595,7 @@ ${status.stderr}` : "");
30068
30595
  stdout += `
30069
30596
  `;
30070
30597
  stdout += printSection("Recent Validation Signals");
30071
- if (existsSync11(path22.join(cwd, "package.json"))) {
30598
+ if (existsSync11(path24.join(cwd, "package.json"))) {
30072
30599
  stdout += `package.json detected. Common scripts:
30073
30600
  `;
30074
30601
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -30077,7 +30604,7 @@ ${status.stderr}` : "");
30077
30604
  `) + `
30078
30605
  `;
30079
30606
  }
30080
- if (existsSync11(path22.join(cwd, "Cargo.toml")) || existsSync11(path22.join(cwd, "src-tauri/Cargo.toml"))) {
30607
+ if (existsSync11(path24.join(cwd, "Cargo.toml")) || existsSync11(path24.join(cwd, "src-tauri/Cargo.toml"))) {
30081
30608
  stdout += `Rust project detected.
30082
30609
  `;
30083
30610
  }
@@ -30110,7 +30637,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
30110
30637
  - Apply with the host-native editor, preserving the repository changelog conventions.
30111
30638
  - If Unreleased already has duplicate category headings, normalize_only first.`;
30112
30639
  function changelogContext(root, range) {
30113
- const cwd = path22.resolve(repoRoot3(root));
30640
+ const cwd = path24.resolve(repoRoot3(root));
30114
30641
  if (range !== undefined && !isResolvableContextRange(cwd, range))
30115
30642
  return invalidRange(cwd);
30116
30643
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -30128,7 +30655,7 @@ function changelogContext(root, range) {
30128
30655
  stdout += CHANGELOG_RULES + `
30129
30656
  `;
30130
30657
  stdout += printSection("Existing CHANGELOG.md");
30131
- const changelogPath = path22.join(cwd, "CHANGELOG.md");
30658
+ const changelogPath = path24.join(cwd, "CHANGELOG.md");
30132
30659
  if (existsSync11(changelogPath)) {
30133
30660
  stdout += readTrimmed2(changelogPath, 260) + `
30134
30661
  `;
@@ -30148,7 +30675,7 @@ function changelogContext(root, range) {
30148
30675
  return { stdout, stderr: "", exitCode: 0, cwd };
30149
30676
  }
30150
30677
  function docsRefreshContext(root, range) {
30151
- const cwd = path22.resolve(repoRoot3(root));
30678
+ const cwd = path24.resolve(repoRoot3(root));
30152
30679
  if (range !== undefined && !isResolvableContextRange(cwd, range))
30153
30680
  return invalidRange(cwd);
30154
30681
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -30170,7 +30697,7 @@ function docsRefreshContext(root, range) {
30170
30697
  `) + `
30171
30698
  `;
30172
30699
  stdout += printSection("README Preview");
30173
- const readme = path22.join(cwd, "README.md");
30700
+ const readme = path24.join(cwd, "README.md");
30174
30701
  if (existsSync11(readme)) {
30175
30702
  stdout += readTrimmed2(readme, 220) + `
30176
30703
  `;
@@ -30179,7 +30706,7 @@ function docsRefreshContext(root, range) {
30179
30706
  `;
30180
30707
  }
30181
30708
  stdout += printSection("Package Scripts");
30182
- const pkgPath = path22.join(cwd, "package.json");
30709
+ const pkgPath = path24.join(cwd, "package.json");
30183
30710
  if (existsSync11(pkgPath)) {
30184
30711
  try {
30185
30712
  const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
@@ -30193,7 +30720,7 @@ function docsRefreshContext(root, range) {
30193
30720
  return { stdout, stderr: "", exitCode: 0, cwd };
30194
30721
  }
30195
30722
  function releaseNotesContext(root, rangeOrTag) {
30196
- const cwd = path22.resolve(repoRoot3(root));
30723
+ const cwd = path24.resolve(repoRoot3(root));
30197
30724
  if (!rangeOrTag) {
30198
30725
  return { stdout: "", stderr: `ERROR: release tag or range required
30199
30726
  `, exitCode: 1, cwd };
@@ -30231,7 +30758,7 @@ function releaseNotesContext(root, rangeOrTag) {
30231
30758
  `;
30232
30759
  stdout += printSection("Existing Release Files");
30233
30760
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
30234
- if (existsSync11(path22.join(cwd, rel)))
30761
+ if (existsSync11(path24.join(cwd, rel)))
30235
30762
  stdout += rel + `
30236
30763
  `;
30237
30764
  }
@@ -30259,7 +30786,7 @@ function releaseNotesContext(root, rangeOrTag) {
30259
30786
  }
30260
30787
 
30261
30788
  // packages/workit-core/src/core/tracker-issues.ts
30262
- import { spawnSync as spawnSync9 } from "node:child_process";
30789
+ import { spawnSync as spawnSync11 } from "node:child_process";
30263
30790
  var repoPathFromRemote = (remote) => {
30264
30791
  let rest = (remote || "").trim().replace(/\/+$/, "");
30265
30792
  if (!rest)
@@ -30280,7 +30807,7 @@ var defaultCli = (bin, available) => {
30280
30807
  if (!available)
30281
30808
  return null;
30282
30809
  return (args, root) => {
30283
- const out = spawnSync9(bin, args, {
30810
+ const out = spawnSync11(bin, args, {
30284
30811
  cwd: root,
30285
30812
  encoding: "utf8",
30286
30813
  env: { ...process.env, PATH: process.env.PATH ?? "" }
@@ -30363,10 +30890,6 @@ var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\
30363
30890
  var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
30364
30891
  var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
30365
30892
 
30366
- // packages/workit-core/src/git/rev.ts
30367
- var MAX_BUFFER = 256 * 1024 * 1024;
30368
- var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
30369
-
30370
30893
  // packages/workit-core/src/ledger.ts
30371
30894
  var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
30372
30895
  var FAILING = new Set(["failed", "blocked"]);
@@ -30478,7 +31001,7 @@ var createContextTool = () => tool({
30478
31001
  try {
30479
31002
  const payload = parsed.data.payload;
30480
31003
  const cwd = payload.cwd;
30481
- root = realpathSync10(cwd ? path23.resolve(context.directory, cwd) : context.directory);
31004
+ root = realpathSync10(cwd ? path25.resolve(context.directory, cwd) : context.directory);
30482
31005
  } catch {
30483
31006
  return JSON.stringify(failure2("invalid_input", "action target directory cannot be resolved", {
30484
31007
  outcome: "not_started"
@@ -30490,14 +31013,14 @@ var createContextTool = () => tool({
30490
31013
 
30491
31014
  // packages/workit-opencode/src/shared/session.ts
30492
31015
  import { realpathSync as realpathSync11 } from "node:fs";
30493
- import path24 from "node:path";
31016
+ import path26 from "node:path";
30494
31017
  var sameWorkspace = (expected, observed) => {
30495
31018
  if (typeof observed !== "string" || !observed)
30496
31019
  return false;
30497
31020
  try {
30498
31021
  return realpathSync11(expected) === realpathSync11(observed);
30499
31022
  } catch {
30500
- return path24.resolve(expected) === path24.resolve(observed);
31023
+ return path26.resolve(expected) === path26.resolve(observed);
30501
31024
  }
30502
31025
  };
30503
31026
 
@@ -30999,7 +31522,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
30999
31522
  };
31000
31523
  });
31001
31524
  // packages/workit-core/src/hooks/context.ts
31002
- import path25 from "node:path";
31525
+ import path27 from "node:path";
31003
31526
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
31004
31527
  var newestFirst = (left, right) => right.updatedAt.localeCompare(left.updatedAt);
31005
31528
  var sessionTaskEntry = (entries, session) => entries.filter((entry) => entry.status !== "closed" && boundTo(entry, session)).toSorted(newestFirst)[0] ?? null;
@@ -31024,6 +31547,19 @@ var filesKey = (files) => {
31024
31547
  return signatures.some((item) => item !== "-" && racySignature(item)) ? null : signatures.join("\x00");
31025
31548
  };
31026
31549
  var CACHE_LIMIT = 64;
31550
+ var SIGNAL_TTL_MS = 1500;
31551
+ var signals = new Map;
31552
+ var recentSignal = (root) => {
31553
+ const now = Date.now();
31554
+ const cached = signals.get(root);
31555
+ if (cached && now - cached.at < SIGNAL_TTL_MS)
31556
+ return cached.value;
31557
+ const value = worktreeSignal(root);
31558
+ signals.set(root, { at: now, value });
31559
+ if (signals.size > CACHE_LIMIT)
31560
+ signals.delete(signals.keys().next().value);
31561
+ return value;
31562
+ };
31027
31563
  var cache = new Map;
31028
31564
  function sessionCompactContext(store, session, context, selection = "session-bound") {
31029
31565
  const listed = store.listTaskIndex();
@@ -31047,18 +31583,26 @@ function entryCompactContext(store, entry, session, context) {
31047
31583
  capabilities: context.capabilities
31048
31584
  });
31049
31585
  const hit = cache.get(slot);
31050
- if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files))
31586
+ if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files) && (hit.signal === null || hit.signal === recentSignal(store.root)))
31051
31587
  return hit.value;
31052
31588
  const task = store.readTask(entry.id);
31053
31589
  if (!task.ok)
31054
31590
  return null;
31055
- const files = task.data.decisions.flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path25.resolve(store.root, ref.path)] : []));
31056
- const observed = filesKey(files);
31057
- const compact = new WorkitCore(store, context).compactContext(entry.id);
31591
+ const freshness = { mode: "signal", current: () => recentSignal(store.root) };
31592
+ const observed = task.data.evidence.some(isObservedCheck);
31593
+ const files = task.data.decisions.flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path27.resolve(store.root, ref.path)] : []));
31594
+ const observedFiles = filesKey(files);
31595
+ const compact = new WorkitCore(store, context).compactContext(entry.id, freshness);
31058
31596
  if (!compact.ok)
31059
31597
  return null;
31060
31598
  cache.delete(slot);
31061
- cache.set(slot, { key, files, filesKey: observed, value: compact.data });
31599
+ cache.set(slot, {
31600
+ key,
31601
+ files,
31602
+ filesKey: observedFiles,
31603
+ signal: observed ? freshness.current() : null,
31604
+ value: compact.data
31605
+ });
31062
31606
  if (cache.size > CACHE_LIMIT)
31063
31607
  cache.delete(cache.keys().next().value);
31064
31608
  return compact.data;
@@ -31230,7 +31774,7 @@ var loadProvenance = (logger, pkgUrl) => {
31230
31774
 
31231
31775
  // packages/workit-opencode/src/v1/server.ts
31232
31776
  var root = assetsRoot();
31233
- var skillsPath = path26.join(root, "skills");
31777
+ var skillsPath = path28.join(root, "skills");
31234
31778
  var logger = createLogger({
31235
31779
  appLog: () => {
31236
31780
  return;
@@ -31249,7 +31793,7 @@ var warnStaleSources = () => {
31249
31793
  logger.warn(EVENT.hooks, {
31250
31794
  boundary: "stale_sources",
31251
31795
  reason: "workit sources changed after plugin load; restart the session for latest behavior",
31252
- files: changed.map((file) => path26.basename(file))
31796
+ files: changed.map((file) => path28.basename(file))
31253
31797
  });
31254
31798
  } catch {}
31255
31799
  };
@@ -31279,7 +31823,7 @@ var plugin = async ({ client, directory }) => {
31279
31823
  const dispatches = new Map;
31280
31824
  try {
31281
31825
  logger.info(EVENT.initialization, { host: "opencode", plugin_root: root });
31282
- logger.info(EVENT.provenance, loadProvenance(logger, path26.join(packageRoot2(), "package.json")));
31826
+ logger.info(EVENT.provenance, loadProvenance(logger, path28.join(packageRoot2(), "package.json")));
31283
31827
  } catch (error) {
31284
31828
  logger.warn(EVENT.hooks, { boundary: "initialization", ...errorDetail(error) });
31285
31829
  }
@@ -32213,7 +32757,7 @@ var normalizeQuestionAnswers = (answers) => {
32213
32757
 
32214
32758
  // packages/workit-opencode/src/v2/registry.ts
32215
32759
  import { existsSync as existsSync13, readFileSync as readFileSync12 } from "node:fs";
32216
- import path27 from "node:path";
32760
+ import path29 from "node:path";
32217
32761
  var parseFrontmatter = (markdown) => {
32218
32762
  const match = markdown.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
32219
32763
  if (!match)
@@ -32233,7 +32777,7 @@ var parseFrontmatter = (markdown) => {
32233
32777
  };
32234
32778
  };
32235
32779
  var registerSkills = async (ctx) => {
32236
- const skillsDir = path27.join(assetsRoot(), "skills");
32780
+ const skillsDir = path29.join(assetsRoot(), "skills");
32237
32781
  if (!existsSync13(skillsDir))
32238
32782
  return new Set;
32239
32783
  const registered = new Set;
@@ -32242,7 +32786,7 @@ var registerSkills = async (ctx) => {
32242
32786
  for (const id of WORKIT_METHOD_SKILLS) {
32243
32787
  if (existing.has(id))
32244
32788
  continue;
32245
- const file = path27.join(skillsDir, id, "SKILL.md");
32789
+ const file = path29.join(skillsDir, id, "SKILL.md");
32246
32790
  if (!existsSync13(file))
32247
32791
  continue;
32248
32792
  const parsed = parseFrontmatter(readFileSync12(file, "utf8"));