@mstar-harness/engine 3.6.3 → 3.7.1

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/engine.js CHANGED
@@ -2095,6 +2095,25 @@ function assertSafePathComponent(value, what) {
2095
2095
  throw new Error(`${what} must be a single safe path component ([A-Za-z0-9._-]+; not "", ".", "..", or containing "/" or "\\") — got ${JSON.stringify(value)}`);
2096
2096
  }
2097
2097
  }
2098
+ function canonicalizeNearestExisting(path) {
2099
+ const abs = resolve7(path);
2100
+ let dir = abs;
2101
+ const tail = [];
2102
+ for (;; ) {
2103
+ if (existsSync6(dir)) {
2104
+ try {
2105
+ return join9(realpathSync2(dir), ...tail);
2106
+ } catch {
2107
+ return abs;
2108
+ }
2109
+ }
2110
+ const parent = dirname5(dir);
2111
+ if (parent === dir)
2112
+ return abs;
2113
+ tail.unshift(basename3(dir));
2114
+ dir = parent;
2115
+ }
2116
+ }
2098
2117
  function resolveSddDir(harnessDir, planId) {
2099
2118
  assertSafePathComponent(planId, "planId");
2100
2119
  const base = resolve7(harnessDir);
@@ -2338,8 +2357,8 @@ function hasFiles(dir) {
2338
2357
  }
2339
2358
  // src/worktree.ts
2340
2359
  import { execFileSync as execFileSync2 } from "node:child_process";
2341
- import { existsSync as existsSync7 } from "node:fs";
2342
- import { isAbsolute as isAbsolute5, resolve as resolve8 } from "node:path";
2360
+ import { existsSync as existsSync7, realpathSync as realpathSync3 } from "node:fs";
2361
+ import { isAbsolute as isAbsolute5, join as join10, resolve as resolve8 } from "node:path";
2343
2362
  var DEFAULT_PROBE_TIMEOUT_MS = 1e4;
2344
2363
  function probeTimeoutMs() {
2345
2364
  const raw = process.env.MSTAR_GIT_PROBE_TIMEOUT_MS;
@@ -2378,6 +2397,51 @@ function probeBranch(worktreePath, opts) {
2378
2397
  return { error: detail };
2379
2398
  }
2380
2399
  }
2400
+ function probeCheckout(worktreePath, opts) {
2401
+ const timeout = opts.timeoutMs ?? probeTimeoutMs();
2402
+ try {
2403
+ const stdout = execFileSync2(opts.gitPath ?? "git", ["-C", worktreePath, "rev-parse", "--git-dir"], {
2404
+ encoding: "utf8",
2405
+ stdio: ["ignore", "pipe", "pipe"],
2406
+ timeout
2407
+ });
2408
+ const raw = stdout.trim();
2409
+ if (raw === "")
2410
+ return { error: `no git dir reported at "${worktreePath}"` };
2411
+ const abs = isAbsolute5(raw) ? raw : join10(worktreePath, raw);
2412
+ return { gitDir: realpathSync3(abs) };
2413
+ } catch (err) {
2414
+ const e = err;
2415
+ if (e.killed === true || e.signal !== undefined) {
2416
+ return { error: `git probe timed out after ${timeout}ms (killed by ${e.signal ?? "SIGTERM"})` };
2417
+ }
2418
+ const detail = (e.stderr !== undefined ? e.stderr.toString().trim() : "") || e.message || "git probe failed";
2419
+ return { error: detail };
2420
+ }
2421
+ }
2422
+ function isDistinctCheckout(controlPath, candidatePath, opts = {}) {
2423
+ const control = probeCheckout(controlPath, opts);
2424
+ const candidate = probeCheckout(candidatePath, opts);
2425
+ if ("error" in control || "error" in candidate)
2426
+ return false;
2427
+ return control.gitDir !== candidate.gitDir;
2428
+ }
2429
+ function probeCheckoutRoot(path, opts = {}) {
2430
+ const timeout = opts.timeoutMs ?? probeTimeoutMs();
2431
+ try {
2432
+ const stdout = execFileSync2(opts.gitPath ?? "git", ["-C", path, "rev-parse", "--show-toplevel"], {
2433
+ encoding: "utf8",
2434
+ stdio: ["ignore", "pipe", "pipe"],
2435
+ timeout
2436
+ });
2437
+ const raw = stdout.trim();
2438
+ if (raw === "")
2439
+ return null;
2440
+ return realpathSync3(raw);
2441
+ } catch {
2442
+ return null;
2443
+ }
2444
+ }
2381
2445
  function l1PreDispatchCheck(input, opts = {}) {
2382
2446
  const violations = [];
2383
2447
  const { controlWorktreePath, leaseWorktreePath, leaseWorkingBranch, planId } = input;
@@ -2390,8 +2454,20 @@ function l1PreDispatchCheck(input, opts = {}) {
2390
2454
  if (leaseWorkingBranch.trim() === "") {
2391
2455
  violations.push(violation7("high", "worktree.l1.lease-branch-missing", `execution_lease.working_branch is empty for plan "${planId}"`, "record the lease working_branch before dispatch"));
2392
2456
  }
2393
- if (controlWorktreePath !== "" && leaseWorktreePath !== "" && resolve8(controlWorktreePath) === resolve8(leaseWorktreePath)) {
2394
- violations.push(violation7("critical", "worktree.l1.lease-equals-control", `execution_lease.worktree_path "${leaseWorktreePath}" equals metadata.control_worktree_path — the feature worktree MUST differ from the control worktree (L1 isolation; product edits never land in the control checkout)`, "use a distinct feature worktree for the plan (git worktree add <path> <branch>) and update the lease"));
2457
+ if (controlWorktreePath !== "" && leaseWorktreePath !== "") {
2458
+ if (resolve8(controlWorktreePath) === resolve8(leaseWorktreePath)) {
2459
+ violations.push(violation7("critical", "worktree.l1.lease-equals-control", `execution_lease.worktree_path "${leaseWorktreePath}" equals metadata.control_worktree_path — the feature worktree MUST differ from the control worktree (L1 isolation; product edits never land in the control checkout)`, "use a distinct feature worktree for the plan (git worktree add <path> <branch>) and update the lease"));
2460
+ } else if (existsSync7(leaseWorktreePath)) {
2461
+ const controlProbe = probeCheckout(controlWorktreePath, opts);
2462
+ const leaseProbe = probeCheckout(leaseWorktreePath, opts);
2463
+ if ("error" in controlProbe) {
2464
+ violations.push(violation7("high", "worktree.l1.checkout-probe-failed", `cannot establish the lease worktree "${leaseWorktreePath}" is a distinct Git checkout from the control worktree "${controlWorktreePath}" for plan "${planId}": ${controlProbe.error}`, "verify the control worktree path is a git checkout (integration-branch checkout)"));
2465
+ } else if ("error" in leaseProbe) {
2466
+ violations.push(violation7("high", "worktree.l1.checkout-probe-failed", `cannot establish the lease worktree "${leaseWorktreePath}" is a distinct Git checkout from the control worktree "${controlWorktreePath}" for plan "${planId}": ${leaseProbe.error}`, "verify the lease worktree path is a git checkout (git worktree add <path> <branch>)"));
2467
+ } else if (controlProbe.gitDir === leaseProbe.gitDir) {
2468
+ violations.push(violation7("critical", "worktree.l1.lease-equals-control", `execution_lease.worktree_path "${leaseWorktreePath}" is the same Git checkout as metadata.control_worktree_path "${controlWorktreePath}" — a plain subdirectory or symlink alias of the control checkout is not isolation; the feature worktree MUST be a distinct checkout`, "use a distinct feature worktree for the plan (git worktree add <path> <branch>) and update the lease"));
2469
+ }
2470
+ }
2395
2471
  }
2396
2472
  if (leaseWorktreePath !== "" && !existsSync7(leaseWorktreePath)) {
2397
2473
  violations.push(violation7("high", "worktree.l1.feature-missing", `feature worktree directory "${leaseWorktreePath}" does not exist for plan "${planId}"`, `create it before dispatch: git worktree add ${leaseWorktreePath} <working-branch>`));
@@ -2440,11 +2516,17 @@ function l2PreDispatchCheck(input, opts = {}) {
2440
2516
  });
2441
2517
  return gate(violations);
2442
2518
  }
2443
- function assertControlVsFeaturePath(controlWorktreePath, featureWorktreePath) {
2519
+ function assertControlVsFeaturePath(controlWorktreePath, featureWorktreePath, opts = {}) {
2444
2520
  const violations = [];
2445
- const samePath = controlWorktreePath === "" && featureWorktreePath === "" || controlWorktreePath !== "" && featureWorktreePath !== "" && resolve8(controlWorktreePath) === resolve8(featureWorktreePath);
2446
- if (samePath) {
2447
- violations.push(violation7("critical", "worktree.control-feature.same", `control worktree path equals feature/lease worktree path "${controlWorktreePath}" — execution_lease.worktree_path MUST differ from metadata.control_worktree_path`, "use a distinct feature worktree for the plan's product edits"));
2521
+ if (controlWorktreePath === "" && featureWorktreePath === "") {
2522
+ violations.push(violation7("critical", "worktree.control-feature.same", `control worktree path and feature/lease worktree path are both empty — execution_lease.worktree_path MUST differ from metadata.control_worktree_path`, "record a distinct feature worktree path"));
2523
+ return gate(violations);
2524
+ }
2525
+ if (controlWorktreePath === "" || featureWorktreePath === "") {
2526
+ return gate(violations);
2527
+ }
2528
+ if (!isDistinctCheckout(controlWorktreePath, featureWorktreePath, opts)) {
2529
+ violations.push(violation7("critical", "worktree.control-feature.same", `control worktree path "${controlWorktreePath}" and feature/lease worktree path "${featureWorktreePath}" are not distinct Git checkouts — a plain subdirectory or symlink alias of the control checkout is not isolation; execution_lease.worktree_path MUST be a distinct checkout`, "use a distinct feature worktree for the plan's product edits (git worktree add <path> <branch>)"));
2448
2530
  }
2449
2531
  return gate(violations);
2450
2532
  }
@@ -2489,9 +2571,10 @@ function singleReviewSnapshot(assignments) {
2489
2571
  return gate(violations);
2490
2572
  }
2491
2573
  // src/sdd.ts
2492
- import { execFileSync as execFileSync3 } from "node:child_process";
2493
- import { mkdirSync as mkdirSync6, readdirSync as readdirSync6, readFileSync as readFileSync7, realpathSync as realpathSync3, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
2494
- import { basename as basename4, dirname as dirname6, isAbsolute as isAbsolute6, join as join10, resolve as resolve9 } from "node:path";
2574
+ import { execFileSync as execFileSync3, spawn } from "node:child_process";
2575
+ import { mkdirSync as mkdirSync6, readdirSync as readdirSync6, readFileSync as readFileSync7, realpathSync as realpathSync4, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
2576
+ import { constants as osConstants } from "node:os";
2577
+ import { basename as basename4, dirname as dirname6, isAbsolute as isAbsolute6, join as join11, relative as relative3, resolve as resolve9 } from "node:path";
2495
2578
  class SddScriptError extends Error {
2496
2579
  exitCode;
2497
2580
  constructor(message, exitCode) {
@@ -2528,14 +2611,14 @@ function gitOut(cwd, args) {
2528
2611
  }
2529
2612
  }
2530
2613
  function probeHarnessWithStatus(root) {
2531
- if (isFile2(join10(root, ".mstar", "status.json")))
2532
- return join10(root, ".mstar");
2533
- if (isFile2(join10(root, ".agents", "status.json")))
2534
- return join10(root, ".agents");
2535
- if (hasWorkflowSnapshot(join10(root, ".mstar")))
2536
- return join10(root, ".mstar");
2537
- if (hasWorkflowSnapshot(join10(root, ".agents")))
2538
- return join10(root, ".agents");
2614
+ if (isFile2(join11(root, ".mstar", "status.json")))
2615
+ return join11(root, ".mstar");
2616
+ if (isFile2(join11(root, ".agents", "status.json")))
2617
+ return join11(root, ".agents");
2618
+ if (hasWorkflowSnapshot(join11(root, ".mstar")))
2619
+ return join11(root, ".mstar");
2620
+ if (hasWorkflowSnapshot(join11(root, ".agents")))
2621
+ return join11(root, ".agents");
2539
2622
  return null;
2540
2623
  }
2541
2624
  function hasWorkflowSnapshot(harnessDir) {
@@ -2543,13 +2626,13 @@ function hasWorkflowSnapshot(harnessDir) {
2543
2626
  try {
2544
2627
  workflowsDir = resolveWorkflowDir(harnessDir, { harnessDir });
2545
2628
  } catch {
2546
- workflowsDir = join10(harnessDir, "workflows");
2629
+ workflowsDir = join11(harnessDir, "workflows");
2547
2630
  }
2548
2631
  if (!isDirectory2(workflowsDir))
2549
2632
  return false;
2550
2633
  try {
2551
2634
  for (const entry of readdirSync6(workflowsDir, { withFileTypes: true })) {
2552
- if (entry.isDirectory() && isFile2(join10(workflowsDir, entry.name, "snapshot.json")))
2635
+ if (entry.isDirectory() && isFile2(join11(workflowsDir, entry.name, "snapshot.json")))
2553
2636
  return true;
2554
2637
  }
2555
2638
  } catch {
@@ -2562,14 +2645,14 @@ function isLinkedWorktree(root) {
2562
2645
  const commonRaw = gitOut(root, ["rev-parse", "--git-common-dir"]);
2563
2646
  if (gitDirRaw === null || commonRaw === null)
2564
2647
  return false;
2565
- const gitDir = isAbsolute6(gitDirRaw) ? gitDirRaw : join10(root, gitDirRaw);
2566
- const common = isAbsolute6(commonRaw) ? commonRaw : join10(root, commonRaw);
2648
+ const gitDir = isAbsolute6(gitDirRaw) ? gitDirRaw : join11(root, gitDirRaw);
2649
+ const common = isAbsolute6(commonRaw) ? commonRaw : join11(root, commonRaw);
2567
2650
  if (gitDir.includes("/.git/worktrees/") || gitDir.includes("/worktrees/"))
2568
2651
  return true;
2569
2652
  try {
2570
- const gdParent = realpathSync3(dirname6(gitDir));
2571
- const cmAbs = realpathSync3(common);
2572
- return join10(gdParent, basename4(gitDir)) !== cmAbs && gitDir !== cmAbs;
2653
+ const gdParent = realpathSync4(dirname6(gitDir));
2654
+ const cmAbs = realpathSync4(common);
2655
+ return join11(gdParent, basename4(gitDir)) !== cmAbs && gitDir !== cmAbs;
2573
2656
  } catch {
2574
2657
  return false;
2575
2658
  }
@@ -2586,10 +2669,10 @@ function sddWorkspace(planId, opts = {}) {
2586
2669
  if (!isDirectory2(controlRoot)) {
2587
2670
  throw new SddScriptError(`mstar sdd workspace: CONTROL_ROOT / MSTAR_CONTROL_ROOT is not a directory: ${controlRoot}`, 1);
2588
2671
  }
2589
- root = realpathSync3(controlRoot);
2672
+ root = realpathSync4(controlRoot);
2590
2673
  } else {
2591
2674
  const topLevel = gitOut(cwd, ["rev-parse", "--show-toplevel"]);
2592
- root = realpathSync3(topLevel ?? cwd);
2675
+ root = realpathSync4(topLevel ?? cwd);
2593
2676
  }
2594
2677
  if (!controlRoot && isLinkedWorktree(root)) {
2595
2678
  throw new SddScriptError(`mstar sdd workspace: linked worktree at ${root} has no {HARNESS_DIR}/status.json (default gitignore).
@@ -2610,25 +2693,35 @@ function sddWorkspace(planId, opts = {}) {
2610
2693
  const probed = probeHarnessWithStatus(root);
2611
2694
  if (probed) {
2612
2695
  harnessDir = probed;
2613
- } else if (isDirectory2(join10(root, ".mstar"))) {
2614
- harnessDir = join10(root, ".mstar");
2615
- } else if (isDirectory2(join10(root, ".agents"))) {
2616
- harnessDir = join10(root, ".agents");
2696
+ } else if (isDirectory2(join11(root, ".mstar"))) {
2697
+ harnessDir = join11(root, ".mstar");
2698
+ } else if (isDirectory2(join11(root, ".agents"))) {
2699
+ harnessDir = join11(root, ".agents");
2617
2700
  } else {
2618
- harnessDir = join10(root, ".mstar");
2701
+ harnessDir = join11(root, ".mstar");
2619
2702
  }
2620
2703
  }
2621
2704
  }
2622
2705
  const sddDir = resolveSddDir(harnessDir, planId);
2623
2706
  mkdirSync6(sddDir, { recursive: true });
2624
- writeFileSync4(join10(sddDir, ".gitignore"), `*
2707
+ writeFileSync4(join11(sddDir, ".gitignore"), `*
2625
2708
  `);
2626
- return realpathSync3(sddDir);
2709
+ return realpathSync4(sddDir);
2627
2710
  }
2628
2711
  function taskBrief(planFile, taskN, outFile, opts = {}) {
2629
2712
  if (!planFile || !Number.isInteger(taskN) || taskN < 1) {
2630
2713
  throw new SddScriptError("usage: mstar sdd task-brief PLAN_FILE TASK_NUMBER [OUTFILE]", 2);
2631
2714
  }
2715
+ const bound = opts.context;
2716
+ const observedCwd = opts.cwd ?? process.cwd();
2717
+ if (bound) {
2718
+ const inputPlan = canonicalizeNearestExisting(resolve9(observedCwd, planFile));
2719
+ if (inputPlan !== canonicalizeNearestExisting(bound.planFile)) {
2720
+ throwGateFail([
2721
+ contextViolation("high", "sdd.context.plan-file-mismatch", `plan file "${planFile}" does not match the bound context plan file "${bound.planFile}" — ` + "bound mode extracts only the resolved context's plan; refused before any read or write")
2722
+ ]);
2723
+ }
2724
+ }
2632
2725
  let content;
2633
2726
  try {
2634
2727
  content = readFileSync7(planFile, "utf8");
@@ -2636,15 +2729,26 @@ function taskBrief(planFile, taskN, outFile, opts = {}) {
2636
2729
  throw new SddScriptError(`no such plan file: ${planFile}`, 2);
2637
2730
  }
2638
2731
  let out;
2732
+ let mkdirAfterGate = null;
2639
2733
  if (outFile) {
2640
- out = outFile;
2734
+ out = bound ? resolve9(observedCwd, outFile) : outFile;
2735
+ } else if (bound) {
2736
+ out = join11(bound.sddDir, `task-${taskN}-brief.md`);
2737
+ mkdirAfterGate = bound.sddDir;
2641
2738
  } else {
2642
2739
  const sddDir = opts.sddDir ?? process.env.SDD_DIR;
2643
2740
  if (!sddDir) {
2644
2741
  throw new SddScriptError("mstar sdd task-brief: set SDD_DIR or pass OUTFILE (run mstar sdd workspace PLAN_ID first)", 2);
2645
2742
  }
2646
2743
  mkdirSync6(sddDir, { recursive: true });
2647
- out = join10(sddDir, `task-${taskN}-brief.md`);
2744
+ out = join11(sddDir, `task-${taskN}-brief.md`);
2745
+ }
2746
+ if (bound) {
2747
+ const gate2 = checkSddAction(bound, { kind: "artifact", cwd: observedCwd, target: out });
2748
+ if (!gate2.ok)
2749
+ throwGateFail(gate2.violations);
2750
+ if (mkdirAfterGate !== null)
2751
+ mkdirSync6(mkdirAfterGate, { recursive: true });
2648
2752
  }
2649
2753
  const records = content.endsWith(`
2650
2754
  `) ? content.split(`
@@ -2670,13 +2774,15 @@ function taskBrief(planFile, taskN, outFile, opts = {}) {
2670
2774
  if (printed.length === 0) {
2671
2775
  throw new SddScriptError(`task ${taskN} not found in ${planFile} (no heading matching Task ${taskN})`, 3);
2672
2776
  }
2673
- return out;
2777
+ return bound ? resolve9(observedCwd, out) : out;
2674
2778
  }
2675
2779
  function reviewPackage(base, head, outFile, opts = {}) {
2676
2780
  if (!base || !head) {
2677
2781
  throw new SddScriptError("usage: mstar sdd review-package BASE HEAD [OUTFILE]", 2);
2678
2782
  }
2679
- const cwd = opts.cwd ?? process.cwd();
2783
+ const bound = opts.context;
2784
+ const cwd = bound && opts.cwd === undefined ? bound.featureCwd : opts.cwd ?? process.cwd();
2785
+ const observedCwd = opts.cwd ?? process.cwd();
2680
2786
  const verifyRef = (ref, what) => {
2681
2787
  try {
2682
2788
  execFileSync3("git", ["rev-parse", "--verify", "--quiet", ref], { cwd, stdio: ["ignore", "pipe", "pipe"] });
@@ -2687,8 +2793,14 @@ function reviewPackage(base, head, outFile, opts = {}) {
2687
2793
  verifyRef(base, "BASE");
2688
2794
  verifyRef(head, "HEAD");
2689
2795
  let out;
2796
+ let mkdirAfterGate = null;
2690
2797
  if (outFile) {
2691
- out = outFile;
2798
+ out = bound ? resolve9(observedCwd, outFile) : outFile;
2799
+ } else if (bound) {
2800
+ const shortBase = gitOut(cwd, ["rev-parse", "--short", base]) ?? base;
2801
+ const shortHead = gitOut(cwd, ["rev-parse", "--short", head]) ?? head;
2802
+ out = join11(bound.sddDir, `review-${shortBase}..${shortHead}.diff`);
2803
+ mkdirAfterGate = bound.sddDir;
2692
2804
  } else {
2693
2805
  const sddDir = opts.sddDir ?? process.env.SDD_DIR;
2694
2806
  if (!sddDir) {
@@ -2697,7 +2809,14 @@ function reviewPackage(base, head, outFile, opts = {}) {
2697
2809
  mkdirSync6(sddDir, { recursive: true });
2698
2810
  const shortBase = gitOut(cwd, ["rev-parse", "--short", base]) ?? base;
2699
2811
  const shortHead = gitOut(cwd, ["rev-parse", "--short", head]) ?? head;
2700
- out = join10(sddDir, `review-${shortBase}..${shortHead}.diff`);
2812
+ out = join11(sddDir, `review-${shortBase}..${shortHead}.diff`);
2813
+ }
2814
+ if (bound) {
2815
+ const gate2 = checkSddAction(bound, { kind: "artifact", cwd: observedCwd, target: out });
2816
+ if (!gate2.ok)
2817
+ throwGateFail(gate2.violations);
2818
+ if (mkdirAfterGate !== null)
2819
+ mkdirSync6(mkdirAfterGate, { recursive: true });
2701
2820
  }
2702
2821
  const run = (args) => execFileSync3("git", args, { cwd, maxBuffer: GIT_CAPTURE_MAX_BYTES });
2703
2822
  const parts = [
@@ -2716,7 +2835,7 @@ function reviewPackage(base, head, outFile, opts = {}) {
2716
2835
  run(["diff", "-U10", `${base}..${head}`])
2717
2836
  ];
2718
2837
  writeFileSync4(out, Buffer.concat(parts));
2719
- return out;
2838
+ return bound ? resolve9(observedCwd, out) : out;
2720
2839
  }
2721
2840
  function assertBaseSha(ref, opts = {}) {
2722
2841
  if (typeof ref !== "string" || !/^[0-9a-f]{4,40}$/i.test(ref)) {
@@ -2733,7 +2852,7 @@ function assertBaseSha(ref, opts = {}) {
2733
2852
  }
2734
2853
  function taskReportExists(sddDir, taskN) {
2735
2854
  try {
2736
- const st = statSync4(join10(sddDir, `task-${taskN}-report.md`));
2855
+ const st = statSync4(join11(sddDir, `task-${taskN}-report.md`));
2737
2856
  return st.isFile() && st.size > 0;
2738
2857
  } catch {
2739
2858
  return false;
@@ -2742,7 +2861,7 @@ function taskReportExists(sddDir, taskN) {
2742
2861
  function readProgressLedger(sddDir) {
2743
2862
  let content;
2744
2863
  try {
2745
- content = readFileSync7(join10(sddDir, "progress.md"), "utf8");
2864
+ content = readFileSync7(join11(sddDir, "progress.md"), "utf8");
2746
2865
  } catch {
2747
2866
  return [];
2748
2867
  }
@@ -2774,9 +2893,535 @@ function implementerSessionStickyRules(input) {
2774
2893
  }
2775
2894
  return { resume: true, reason: `sticky resume OK: host_agent_id ${session.host_agent_id}, next task ${nextTask}` };
2776
2895
  }
2896
+ function isPlainObject6(value) {
2897
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2898
+ }
2899
+ function isInside(child, ancestor) {
2900
+ const rel = relative3(ancestor, child);
2901
+ return rel === "" || !rel.startsWith("..") && !isAbsolute6(rel);
2902
+ }
2903
+ function canonicalDir(path) {
2904
+ try {
2905
+ return statSync4(path).isDirectory() ? realpathSync4(path) : null;
2906
+ } catch {
2907
+ return null;
2908
+ }
2909
+ }
2910
+ function contextViolation(severity, code, message, fix) {
2911
+ return { ok: false, severity, code, message, fix };
2912
+ }
2913
+ function throwGateFail(violations) {
2914
+ const detail = violations.map((v) => `${v.code}: ${v.message}${v.fix ? ` (fix: ${v.fix})` : ""}`).join(`
2915
+ `);
2916
+ throw new SddScriptError(`SDD execution context rejected:
2917
+ ${detail}`, 1);
2918
+ }
2919
+ function throwUsage(message) {
2920
+ throw new SddScriptError(message, 2);
2921
+ }
2922
+ function readActiveWorkflowIds(controlHarnessRoot) {
2923
+ const statusPath = join11(controlHarnessRoot, "status.json");
2924
+ if (!isFile2(statusPath))
2925
+ return null;
2926
+ let doc;
2927
+ try {
2928
+ doc = readJson(statusPath);
2929
+ } catch {
2930
+ return null;
2931
+ }
2932
+ if (!isPlainObject6(doc) || doc.version !== 2 || !Array.isArray(doc.workflows))
2933
+ return null;
2934
+ const ids = new Set;
2935
+ for (const entry of doc.workflows) {
2936
+ if (isPlainObject6(entry) && typeof entry.id === "string")
2937
+ ids.add(entry.id);
2938
+ }
2939
+ return ids;
2940
+ }
2941
+ function findWorkflowPlanRow(controlHarnessRoot, planId) {
2942
+ let workflowsDir;
2943
+ try {
2944
+ workflowsDir = resolveWorkflowDir(controlHarnessRoot, { harnessDir: controlHarnessRoot });
2945
+ } catch {
2946
+ return { kind: "none" };
2947
+ }
2948
+ if (!isDirectory2(workflowsDir))
2949
+ return { kind: "none" };
2950
+ let workflowIds;
2951
+ try {
2952
+ workflowIds = readdirSync6(workflowsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
2953
+ } catch {
2954
+ return { kind: "none" };
2955
+ }
2956
+ const matches = [];
2957
+ for (const id of workflowIds) {
2958
+ const snapshotPath = join11(workflowsDir, id, WORKFLOW_SNAPSHOT_FILE);
2959
+ if (!isFile2(snapshotPath))
2960
+ continue;
2961
+ let doc;
2962
+ try {
2963
+ doc = readJson(snapshotPath);
2964
+ } catch {
2965
+ continue;
2966
+ }
2967
+ const plans = doc.plans;
2968
+ if (!Array.isArray(plans))
2969
+ continue;
2970
+ for (const row of plans) {
2971
+ if (isPlainObject6(row) && (row.id === planId || row.plan_id === planId)) {
2972
+ matches.push({ workflowId: id, row });
2973
+ break;
2974
+ }
2975
+ }
2976
+ }
2977
+ if (matches.length === 0)
2978
+ return { kind: "none" };
2979
+ const registeredActive = readActiveWorkflowIds(controlHarnessRoot);
2980
+ if (registeredActive === null) {
2981
+ return { kind: "row", workflowId: matches[0].workflowId, row: matches[0].row };
2982
+ }
2983
+ const active = matches.filter((m) => registeredActive.has(m.workflowId));
2984
+ if (active.length === 0)
2985
+ return { kind: "none" };
2986
+ if (active.length > 1) {
2987
+ return { kind: "ambiguous", workflowIds: active.map((m) => m.workflowId) };
2988
+ }
2989
+ return { kind: "row", workflowId: active[0].workflowId, row: active[0].row };
2990
+ }
2991
+ function resolveSddExecutionContext(input) {
2992
+ const { planId, workingBranch } = input;
2993
+ if (typeof planId !== "string" || planId.trim() === "") {
2994
+ throwUsage("SddExecutionContext.planId must be a non-empty string");
2995
+ }
2996
+ if (typeof workingBranch !== "string" || workingBranch.trim() === "") {
2997
+ throwUsage("SddExecutionContext.workingBranch must be a non-empty string");
2998
+ }
2999
+ for (const field of ["controlHarnessRoot", "featureCwd", "planFile", "sddDir"]) {
3000
+ const value = input[field];
3001
+ if (typeof value !== "string" || value.trim() === "") {
3002
+ throwUsage(`SddExecutionContext.${field} must be a non-empty string`);
3003
+ }
3004
+ if (!isAbsolute6(value)) {
3005
+ throwUsage(`SddExecutionContext.${field} must be an absolute path (A3: all paths normalized absolute); got ${JSON.stringify(value)}`);
3006
+ }
3007
+ }
3008
+ try {
3009
+ assertSafePathComponent(planId, "SddExecutionContext.planId");
3010
+ } catch (err) {
3011
+ throwUsage(`SddExecutionContext rejected: ${err.message}`);
3012
+ }
3013
+ const canonicalControlHarnessRoot = canonicalDir(input.controlHarnessRoot);
3014
+ if (canonicalControlHarnessRoot === null) {
3015
+ throwGateFail([
3016
+ contextViolation("high", "sdd.context.control-root-missing", `controlHarnessRoot "${input.controlHarnessRoot}" does not exist or is not a directory — a declared control root is authoritative and is never re-inferred from the feature cwd (A3)`)
3017
+ ]);
3018
+ }
3019
+ const stem = basename4(input.planFile).replace(/\.md$/, "");
3020
+ if (stem !== planId) {
3021
+ throwUsage(`SddExecutionContext.planFile "${input.planFile}" does not match plan "${planId}" — the plan file must be {PLAN_DIR}/<plan-id>.md under the declared control harness`);
3022
+ }
3023
+ if (!isFile2(input.planFile)) {
3024
+ throwUsage(`no such plan file: ${input.planFile}`);
3025
+ }
3026
+ const planGate = assertPlanWritingPath(input.planFile, input.controlHarnessRoot);
3027
+ if (!planGate.ok) {
3028
+ if (planGate.code === "plan-path.symlink-escape") {
3029
+ throwGateFail([planGate]);
3030
+ }
3031
+ throwUsage(`SddExecutionContext.planFile rejected: ${planGate.code}: ${planGate.message}`);
3032
+ }
3033
+ const composedSddDir = resolveSddDir(canonicalControlHarnessRoot, planId);
3034
+ const canonicalComposedSddDir = canonicalizeNearestExisting(composedSddDir);
3035
+ const canonicalSddDir = canonicalizeNearestExisting(input.sddDir);
3036
+ if (canonicalSddDir !== canonicalComposedSddDir) {
3037
+ if (!isInside(canonicalSddDir, canonicalControlHarnessRoot)) {
3038
+ throwGateFail([
3039
+ contextViolation("high", "sdd.context.sdd-dir-escape", `sddDir "${input.sddDir}" canonicalizes to "${canonicalSddDir}", outside the control harness "${canonicalControlHarnessRoot}" — symlink escape refused (environmental gate failure)`)
3040
+ ]);
3041
+ }
3042
+ throwUsage(`SddExecutionContext.sddDir "${input.sddDir}" does not match plan "${planId}" — expected the {SDD_DIR} composition ${composedSddDir}`);
3043
+ }
3044
+ const canonicalFeatureCwd = canonicalDir(input.featureCwd);
3045
+ if (canonicalFeatureCwd === null) {
3046
+ throwGateFail([
3047
+ contextViolation("high", "sdd.context.feature-cwd-missing", `featureCwd "${input.featureCwd}" does not exist or is not a directory — the feature worktree is the required cwd for product edits`, `create the feature worktree first (git worktree add ${input.featureCwd} ${workingBranch})`)
3048
+ ]);
3049
+ }
3050
+ if (isInside(canonicalControlHarnessRoot, canonicalFeatureCwd)) {
3051
+ throwGateFail([
3052
+ contextViolation("critical", "sdd.context.control-inside-feature", `controlHarnessRoot "${canonicalControlHarnessRoot}" is inside featureCwd "${canonicalFeatureCwd}" — a feature worktree's same-looking {HARNESS_DIR} is not the SSOT; the control harness must live outside the feature checkout`)
3053
+ ]);
3054
+ }
3055
+ const controlCheckout = probeCheckoutRoot(canonicalControlHarnessRoot);
3056
+ if (controlCheckout === null) {
3057
+ throwGateFail([
3058
+ contextViolation("high", "sdd.context.control-root-unresolvable", `cannot resolve the control checkout root for controlHarnessRoot "${canonicalControlHarnessRoot}" (git rev-parse --show-toplevel failed) — the control harness must live inside a git checkout`, "verify the control harness root is inside the control worktree checkout")
3059
+ ]);
3060
+ }
3061
+ if (isInside(canonicalFeatureCwd, controlCheckout) && !isDistinctCheckout(canonicalControlHarnessRoot, canonicalFeatureCwd)) {
3062
+ throwGateFail([
3063
+ contextViolation("critical", "sdd.context.feature-in-control", `featureCwd "${canonicalFeatureCwd}" is inside the control checkout "${controlCheckout}" and is not a distinct Git checkout — a plain subdirectory or symlink alias of the control checkout is not isolation; product edits never land in the control checkout (execution_lease.worktree_path MUST be a distinct checkout)`, "use a distinct feature worktree for the plan (git worktree add <path> <branch>)")
3064
+ ]);
3065
+ }
3066
+ const match = findWorkflowPlanRow(canonicalControlHarnessRoot, planId);
3067
+ if (match.kind === "ambiguous") {
3068
+ throwGateFail([
3069
+ contextViolation("high", "sdd.context.workflow-plan-ambiguous", `plan "${planId}" appears in multiple registered active workflows (${match.workflowIds.join(", ")}) — ` + "the governing execution_lease is undecidable; resolve the duplicate registration before dispatch")
3070
+ ]);
3071
+ }
3072
+ const row = match.kind === "row" ? match.row : null;
3073
+ if (row !== null && row.execution_lease !== undefined) {
3074
+ const leaseVerify = verifyPlanExecutionLease(row, planId);
3075
+ if (!leaseVerify.ok)
3076
+ throwGateFail(leaseVerify.violations);
3077
+ const lease = leaseVerify.lease;
3078
+ const l1 = l1PreDispatchCheck({
3079
+ controlWorktreePath: controlCheckout,
3080
+ leaseWorktreePath: lease.worktree_path,
3081
+ leaseWorkingBranch: lease.working_branch,
3082
+ planId
3083
+ });
3084
+ if (!l1.ok)
3085
+ throwGateFail(l1.violations);
3086
+ if (canonicalizeNearestExisting(lease.worktree_path) !== canonicalFeatureCwd) {
3087
+ throwGateFail([
3088
+ contextViolation("high", "sdd.context.lease-worktree-mismatch", `SddExecutionContext.featureCwd "${canonicalFeatureCwd}" does not match the verified execution_lease.worktree_path "${String(lease.worktree_path)}" — the context must match the verified lease (A3)`)
3089
+ ]);
3090
+ }
3091
+ if (workingBranch !== lease.working_branch) {
3092
+ throwGateFail([
3093
+ contextViolation("high", "sdd.context.lease-branch-mismatch", `SddExecutionContext.workingBranch "${workingBranch}" does not match the verified execution_lease.working_branch "${String(lease.working_branch)}" (plan "${planId}")`)
3094
+ ]);
3095
+ }
3096
+ } else if (row !== null && row.status === "InProgress") {
3097
+ throwGateFail(verifyPlanExecutionLease(row, planId).violations);
3098
+ } else {
3099
+ const branchGate = assertBranchAlignment(canonicalFeatureCwd, workingBranch);
3100
+ if (!branchGate.ok)
3101
+ throwGateFail(branchGate.violations);
3102
+ }
3103
+ return {
3104
+ planId,
3105
+ controlHarnessRoot: canonicalControlHarnessRoot,
3106
+ featureCwd: canonicalFeatureCwd,
3107
+ workingBranch,
3108
+ planFile: realpathSync4(input.planFile),
3109
+ sddDir: canonicalSddDir
3110
+ };
3111
+ }
3112
+ function checkSddAction(context, action) {
3113
+ const violations = [];
3114
+ const add = (code, message, fix) => {
3115
+ violations.push(contextViolation("high", code, message, fix));
3116
+ };
3117
+ if (action.kind !== "source" && action.kind !== "artifact" && action.kind !== "launch") {
3118
+ add("sdd.context.kind-unknown", `unknown action kind ${JSON.stringify(action.kind)} — expected "source" | "artifact" | "launch"`);
3119
+ return { ok: false, violations };
3120
+ }
3121
+ if (typeof action.cwd !== "string" || action.cwd.trim() === "") {
3122
+ add("sdd.context.cwd-missing", "action.cwd (the observed invocation cwd) is required — never an Assignment echo");
3123
+ return { ok: false, violations };
3124
+ }
3125
+ const featureReal = canonicalDir(context.featureCwd);
3126
+ const canonicalSddDir = canonicalizeNearestExisting(context.sddDir);
3127
+ const canonicalPlanFile = canonicalizeNearestExisting(context.planFile);
3128
+ const cwdResolved = resolve9(action.cwd);
3129
+ const checkTarget = (baseDir, target, kind) => {
3130
+ if (featureReal === null)
3131
+ return;
3132
+ const targetAbs = resolve9(baseDir, target);
3133
+ const canonical = canonicalizeNearestExisting(targetAbs);
3134
+ if (!isInside(canonical, featureReal)) {
3135
+ const declaredPrefix = targetAbs === featureReal || targetAbs.startsWith(`${featureReal}/`);
3136
+ if (declaredPrefix) {
3137
+ add("sdd.context.target-symlink-escape", `${kind} target "${target}" canonicalizes to "${canonical}", outside the feature worktree — symlink escape refused before mutation`);
3138
+ } else {
3139
+ add(`sdd.context.${kind}-target-outside-feature`, `${kind} target "${target}" resolves to "${targetAbs}", outside the feature worktree "${featureReal}" — refused before mutation`);
3140
+ }
3141
+ }
3142
+ };
3143
+ if (action.kind === "source") {
3144
+ const cwdReal = canonicalDir(cwdResolved);
3145
+ if (cwdReal === null) {
3146
+ add("sdd.context.cwd-missing", `observed source cwd "${action.cwd}" does not exist or is not a directory`);
3147
+ return { ok: false, violations };
3148
+ }
3149
+ if (featureReal === null || !isInside(cwdReal, featureReal)) {
3150
+ add("sdd.context.source-cwd-outside-feature", `observed source cwd "${cwdReal}" is outside the feature worktree "${context.featureCwd}" — a declared-correct context does not make a wrong-checkout write safe (A3)`, `run the source action from inside ${context.featureCwd}`);
3151
+ return { ok: false, violations };
3152
+ }
3153
+ if (action.target !== undefined)
3154
+ checkTarget(cwdReal, action.target, "source");
3155
+ } else if (action.kind === "artifact") {
3156
+ if (typeof action.target !== "string" || action.target.trim() === "") {
3157
+ add("sdd.context.target-missing", "artifact checks require the destination target");
3158
+ return { ok: false, violations };
3159
+ }
3160
+ const targetAbs = resolve9(cwdResolved, action.target);
3161
+ const canonical = canonicalizeNearestExisting(targetAbs);
3162
+ if (!isInside(canonical, canonicalSddDir) && canonical !== canonicalPlanFile) {
3163
+ const rawSddDir = resolve9(context.sddDir);
3164
+ const declaredPrefix = targetAbs === rawSddDir || targetAbs.startsWith(`${rawSddDir}/`) || targetAbs === canonicalSddDir || targetAbs.startsWith(`${canonicalSddDir}/`) || targetAbs === canonicalPlanFile;
3165
+ if (declaredPrefix) {
3166
+ add("sdd.context.artifact-symlink-escape", `artifact target "${targetAbs}" canonicalizes to "${canonical}", outside the plan's control sddDir — symlink escape refused before write`);
3167
+ } else {
3168
+ add("sdd.context.artifact-outside-plan", `artifact target "${targetAbs}" is outside the plan's control sddDir "${context.sddDir}" and is not the declared planFile "${context.planFile}" — legitimate control artifact edits stay inside the plan's artifacts; arbitrary control source edits are not allowed (A3)`);
3169
+ }
3170
+ }
3171
+ } else {
3172
+ if (featureReal === null) {
3173
+ add("sdd.context.launch-cwd-missing", `feature worktree "${context.featureCwd}" does not exist or is not a directory — cannot bind the child's starting cwd`);
3174
+ } else {
3175
+ violations.push(...assertBranchAlignment(featureReal, context.workingBranch).violations);
3176
+ }
3177
+ if (action.target !== undefined && action.target.trim() !== "" && featureReal !== null) {
3178
+ checkTarget(featureReal, action.target, "launch");
3179
+ }
3180
+ }
3181
+ return { ok: violations.length === 0, violations };
3182
+ }
3183
+ function signalExitNumber(signal) {
3184
+ return osConstants.signals[signal] ?? 0;
3185
+ }
3186
+ async function runInSddContext(context, argv) {
3187
+ if (!Array.isArray(argv) || argv.length === 0 || typeof argv[0] !== "string" || argv[0].trim() === "") {
3188
+ throwUsage("runInSddContext: argv must be [executable, ...args] with a non-empty executable — the array is passed to the child literally (no shell)");
3189
+ }
3190
+ const resolved = resolveSddExecutionContext(context);
3191
+ const gate2 = checkSddAction(resolved, { kind: "launch", cwd: process.cwd() });
3192
+ if (!gate2.ok)
3193
+ throwGateFail(gate2.violations);
3194
+ return await new Promise((settle, reject) => {
3195
+ let child;
3196
+ try {
3197
+ child = spawn(argv[0], argv.slice(1), {
3198
+ cwd: resolved.featureCwd,
3199
+ shell: false,
3200
+ stdio: "inherit"
3201
+ });
3202
+ } catch (err) {
3203
+ if (err.code === "ENOENT") {
3204
+ settle(127);
3205
+ return;
3206
+ }
3207
+ throw err;
3208
+ }
3209
+ let done = false;
3210
+ let spawnError = null;
3211
+ const forward = (signal) => {
3212
+ if (!done && child.exitCode === null && child.signalCode === null)
3213
+ child.kill(signal);
3214
+ };
3215
+ const cleanup = () => {
3216
+ process.removeListener("SIGINT", forward);
3217
+ process.removeListener("SIGTERM", forward);
3218
+ };
3219
+ process.on("SIGINT", forward);
3220
+ process.on("SIGTERM", forward);
3221
+ child.on("error", (err) => {
3222
+ spawnError = err;
3223
+ if (err.code !== "ENOENT" && !done) {
3224
+ done = true;
3225
+ cleanup();
3226
+ reject(err);
3227
+ }
3228
+ });
3229
+ child.on("close", (code, signal) => {
3230
+ if (done)
3231
+ return;
3232
+ done = true;
3233
+ cleanup();
3234
+ if (spawnError !== null) {
3235
+ if (spawnError.code === "ENOENT")
3236
+ settle(127);
3237
+ else
3238
+ reject(spawnError);
3239
+ return;
3240
+ }
3241
+ if (signal !== null)
3242
+ settle(128 + signalExitNumber(signal));
3243
+ else
3244
+ settle(code ?? 0);
3245
+ });
3246
+ });
3247
+ }
3248
+ // src/gates.ts
3249
+ import { existsSync as existsSync8, statSync as statSync5 } from "node:fs";
3250
+ import { basename as basename5, dirname as dirname7, join as join12, relative as relative4, resolve as resolve10 } from "node:path";
3251
+ var STATUS_FILE = "status.json";
3252
+ var SNAPSHOT_FILE = "snapshot.json";
3253
+ var REGISTER_FILE = "residuals.json";
3254
+ function eventTargetPaths(input) {
3255
+ if (typeof input !== "object" || input === null)
3256
+ return [];
3257
+ const record = input;
3258
+ const paths = [];
3259
+ const push = (value) => {
3260
+ if (typeof value === "string" && value.trim() !== "")
3261
+ paths.push(value);
3262
+ };
3263
+ push(record.path);
3264
+ if (Array.isArray(record.paths)) {
3265
+ for (const value of record.paths)
3266
+ push(value);
3267
+ }
3268
+ return paths;
3269
+ }
3270
+ function hasEntry(dir, name) {
3271
+ try {
3272
+ statSync5(join12(dir, name));
3273
+ return true;
3274
+ } catch {
3275
+ return false;
3276
+ }
3277
+ }
3278
+ function hasHarnessRootMarkers(dir) {
3279
+ if (!hasEntry(dir, STATUS_FILE))
3280
+ return false;
3281
+ if (hasEntry(dir, "workflows") && hasEntry(dir, "projects"))
3282
+ return true;
3283
+ try {
3284
+ return hasEntry(resolveWorkflowDir(dir, { harnessDir: dir }), "") && hasEntry(resolveProjectDir(dir, { harnessDir: dir }), "");
3285
+ } catch {
3286
+ return false;
3287
+ }
3288
+ }
3289
+ function resolveHarnessRootOf(target) {
3290
+ let dir = resolve10(target);
3291
+ for (;; ) {
3292
+ if (hasHarnessRootMarkers(dir))
3293
+ return dir;
3294
+ const parent = dirname7(dir);
3295
+ if (parent === dir)
3296
+ return null;
3297
+ dir = parent;
3298
+ }
3299
+ }
3300
+ function harnessDocKindOfTarget(targetPath) {
3301
+ if (typeof targetPath !== "string" || targetPath.trim() === "")
3302
+ return null;
3303
+ const resolved = resolve10(targetPath);
3304
+ const name = basename5(resolved);
3305
+ if (name !== STATUS_FILE && name !== SNAPSHOT_FILE && name !== REGISTER_FILE)
3306
+ return null;
3307
+ const classify = (harnessDir2) => {
3308
+ const rel = relative4(harnessDir2, resolved);
3309
+ if (name === STATUS_FILE && rel === STATUS_FILE)
3310
+ return { harnessDir: harnessDir2, kind: "status" };
3311
+ let workflowDir;
3312
+ let projectDir;
3313
+ try {
3314
+ workflowDir = resolveWorkflowDir(harnessDir2, { harnessDir: harnessDir2 });
3315
+ projectDir = resolveProjectDir(harnessDir2, { harnessDir: harnessDir2 });
3316
+ } catch {
3317
+ workflowDir = join12(harnessDir2, "workflows");
3318
+ projectDir = join12(harnessDir2, "projects");
3319
+ }
3320
+ if (name === SNAPSHOT_FILE && /^[^/]+\/snapshot\.json$/.test(relative4(workflowDir, resolved))) {
3321
+ return { harnessDir: harnessDir2, kind: "snapshot" };
3322
+ }
3323
+ if (name === REGISTER_FILE && /^[^/]+\/residuals\.json$/.test(relative4(projectDir, resolved))) {
3324
+ return { harnessDir: harnessDir2, kind: "register" };
3325
+ }
3326
+ return null;
3327
+ };
3328
+ const probeRoot = resolveHarnessRootOf(dirname7(resolved));
3329
+ const harnessDir = probeRoot ?? resolveHarnessDir(dirname7(resolved));
3330
+ if (harnessDir === null)
3331
+ return null;
3332
+ const classified = classify(harnessDir);
3333
+ if (classified !== null)
3334
+ return classified;
3335
+ if (probeRoot === null)
3336
+ return null;
3337
+ const fallbackDir = resolveHarnessDir(dirname7(resolved));
3338
+ if (fallbackDir === null || fallbackDir === probeRoot)
3339
+ return null;
3340
+ return classify(fallbackDir);
3341
+ }
3342
+ function violationLine(violation8) {
3343
+ return `[${violation8.severity}] ${violation8.code}: ${violation8.message}${violation8.fix ? ` (fix: ${violation8.fix})` : ""}`;
3344
+ }
3345
+ var MAX_STATUS_CONTENT_LENGTH = 2 * 1024 * 1024;
3346
+ function oversizedViolation(filePath) {
3347
+ return {
3348
+ ok: false,
3349
+ severity: "high",
3350
+ code: "status.oversized",
3351
+ message: `${basename5(filePath)} exceeds the ${MAX_STATUS_CONTENT_LENGTH}-byte (2 MiB) coordination-document validation budget — repair out of band or disable for this session with MSTAR_WRITE_GATE=off`
3352
+ };
3353
+ }
3354
+ function validateStatusWriteDoc(content, filePath, kind, options = {}) {
3355
+ const oversized = options.oversized ?? "pass";
3356
+ if (typeof content === "string") {
3357
+ if (content.length > MAX_STATUS_CONTENT_LENGTH) {
3358
+ return oversized === "violate" ? [oversizedViolation(filePath)] : [];
3359
+ }
3360
+ let doc2;
3361
+ try {
3362
+ doc2 = JSON.parse(content);
3363
+ } catch (error) {
3364
+ return [
3365
+ {
3366
+ ok: false,
3367
+ severity: "high",
3368
+ code: "status.invalid-json",
3369
+ message: error.message
3370
+ }
3371
+ ];
3372
+ }
3373
+ if (doc2 === null || typeof doc2 !== "object" || Array.isArray(doc2)) {
3374
+ return [
3375
+ {
3376
+ ok: false,
3377
+ severity: "high",
3378
+ code: "status.invalid-json",
3379
+ message: `${basename5(filePath)} content must be a JSON object`
3380
+ }
3381
+ ];
3382
+ }
3383
+ return validateDocByKind(doc2, kind);
3384
+ }
3385
+ if (!existsSync8(filePath))
3386
+ return [];
3387
+ try {
3388
+ if (statSync5(filePath).size > MAX_STATUS_CONTENT_LENGTH) {
3389
+ return oversized === "violate" ? [oversizedViolation(filePath)] : [];
3390
+ }
3391
+ } catch {
3392
+ return [];
3393
+ }
3394
+ if (kind === "status")
3395
+ return validateStatus(filePath).violations;
3396
+ let doc;
3397
+ try {
3398
+ doc = readJson(filePath);
3399
+ } catch (error) {
3400
+ return [
3401
+ {
3402
+ ok: false,
3403
+ severity: "high",
3404
+ code: "status.invalid-json",
3405
+ message: error.message
3406
+ }
3407
+ ];
3408
+ }
3409
+ return validateDocByKind(doc, kind);
3410
+ }
3411
+ function validateDocByKind(doc, kind) {
3412
+ if (kind === "snapshot")
3413
+ return validateWorkflowSnapshot(doc).violations;
3414
+ if (kind === "register")
3415
+ return validateProjectRegister(doc).violations;
3416
+ return validateStatus(doc).violations;
3417
+ }
3418
+ function formatStatusWriteBlockReason(violations, skillPointer) {
3419
+ return violations.map((v) => `${violationLine(v)} (${skillPointer})`).join(`
3420
+ `);
3421
+ }
2777
3422
  // src/migrate.ts
2778
3423
  import { copyFileSync, mkdirSync as mkdirSync7, readFileSync as readFileSync8, readdirSync as readdirSync7, writeFileSync as writeFileSync5 } from "node:fs";
2779
- import { dirname as dirname7, isAbsolute as isAbsolute7, join as join11, relative as relative3, resolve as resolve10, sep as sep2 } from "node:path";
3424
+ import { dirname as dirname8, isAbsolute as isAbsolute7, join as join13, relative as relative5, resolve as resolve11, sep as sep2 } from "node:path";
2780
3425
  var MIGRATE_STATUS_FILE = "status.json";
2781
3426
  var ARCHIVED_STATUS_V1_FILE = "archived/status.v1.json";
2782
3427
  var NOTES_LEDGER_FILE = "notes.jsonl";
@@ -2793,7 +3438,7 @@ var ROOT_METADATA_LIFT_KEYS = {
2793
3438
  program_roadmap: true,
2794
3439
  updated_at: true
2795
3440
  };
2796
- function isPlainObject6(value) {
3441
+ function isPlainObject7(value) {
2797
3442
  return typeof value === "object" && value !== null && !Array.isArray(value);
2798
3443
  }
2799
3444
  function dateString(value) {
@@ -2816,7 +3461,7 @@ function todayString3() {
2816
3461
  return `${now.getFullYear()}-${month}-${day}`;
2817
3462
  }
2818
3463
  function scanCompasses(harnessDir) {
2819
- const iterationsDir = join11(harnessDir, "iterations");
3464
+ const iterationsDir = join13(harnessDir, "iterations");
2820
3465
  const out = [];
2821
3466
  let entries;
2822
3467
  try {
@@ -2827,7 +3472,7 @@ function scanCompasses(harnessDir) {
2827
3472
  for (const entry of entries) {
2828
3473
  if (!entry.isDirectory())
2829
3474
  continue;
2830
- const compassPath = join11(iterationsDir, entry.name, "delivery-compass.md");
3475
+ const compassPath = join13(iterationsDir, entry.name, "delivery-compass.md");
2831
3476
  let content;
2832
3477
  try {
2833
3478
  content = readFileSync8(compassPath, "utf8");
@@ -2907,8 +3552,8 @@ function buildIterationSnapshot(compass, rowById, rootUpdatedAt) {
2907
3552
  id: compass.id,
2908
3553
  type: "iteration",
2909
3554
  status,
2910
- file: join11("workflows", compass.id, WORKFLOW_SNAPSHOT_FILE),
2911
- source: join11("iterations", compass.id, "delivery-compass.md"),
3555
+ file: join13("workflows", compass.id, WORKFLOW_SNAPSHOT_FILE),
3556
+ source: join13("iterations", compass.id, "delivery-compass.md"),
2912
3557
  data: snapshot
2913
3558
  };
2914
3559
  }
@@ -2943,7 +3588,7 @@ function buildStandaloneSnapshot(row, rootUpdatedAt, migrationNotes) {
2943
3588
  id,
2944
3589
  type: "plan",
2945
3590
  status,
2946
- file: join11("workflows", id, WORKFLOW_SNAPSHOT_FILE),
3591
+ file: join13("workflows", id, WORKFLOW_SNAPSHOT_FILE),
2947
3592
  source: "status.json plans[] row",
2948
3593
  data: snapshot
2949
3594
  };
@@ -2973,10 +3618,10 @@ function applyRootMetadataLift(snapshot, metadata, migrationNotes, activeIterati
2973
3618
  if (typeof metadata.control_worktree_path === "string" && metadata.control_worktree_path !== "") {
2974
3619
  data.control_worktree_path = metadata.control_worktree_path;
2975
3620
  }
2976
- if (isPlainObject6(metadata.integration_merge_lease)) {
3621
+ if (isPlainObject7(metadata.integration_merge_lease)) {
2977
3622
  data.integration_merge_lease = metadata.integration_merge_lease;
2978
3623
  }
2979
- const legacyMetadata = isPlainObject6(data.legacy_metadata) ? { ...data.legacy_metadata } : {};
3624
+ const legacyMetadata = isPlainObject7(data.legacy_metadata) ? { ...data.legacy_metadata } : {};
2980
3625
  for (const [key, value] of Object.entries(metadata)) {
2981
3626
  if (key === "harness_root")
2982
3627
  continue;
@@ -3032,7 +3677,7 @@ function buildRegister(residualFindings, byPlan, projectId, migratedAt) {
3032
3677
  const raw = residualFindings[planId];
3033
3678
  if (!Array.isArray(raw))
3034
3679
  continue;
3035
- const open = raw.filter((entry) => isPlainObject6(entry) && isOpenResidual(entry)).sort((a, b) => {
3680
+ const open = raw.filter((entry) => isPlainObject7(entry) && isOpenResidual(entry)).sort((a, b) => {
3036
3681
  const aId = typeof a.id === "string" ? a.id : "";
3037
3682
  const bId = typeof b.id === "string" ? b.id : "";
3038
3683
  return compareIds(aId, bId);
@@ -3051,7 +3696,7 @@ function buildRegister(residualFindings, byPlan, projectId, migratedAt) {
3051
3696
  return null;
3052
3697
  const doc = { entries };
3053
3698
  return {
3054
- file: join11("projects", projectId, PROJECT_REGISTER_FILE),
3699
+ file: join13("projects", projectId, PROJECT_REGISTER_FILE),
3055
3700
  source: "status.json residual_findings",
3056
3701
  data: doc
3057
3702
  };
@@ -3082,7 +3727,7 @@ function collectNotesFiles(snapshots) {
3082
3727
  if (lines.length === 0)
3083
3728
  continue;
3084
3729
  out.push({
3085
- file: join11(dirname7(snapshot.file), NOTES_LEDGER_FILE),
3730
+ file: join13(dirname8(snapshot.file), NOTES_LEDGER_FILE),
3086
3731
  source,
3087
3732
  lines
3088
3733
  });
@@ -3090,11 +3735,11 @@ function collectNotesFiles(snapshots) {
3090
3735
  return out;
3091
3736
  }
3092
3737
  function migrateHarnessTree(root, opts = {}) {
3093
- const harnessDir = resolve10(root);
3738
+ const harnessDir = resolve11(root);
3094
3739
  const workflowDir = resolveWorkflowDir(harnessDir, { harnessDir });
3095
3740
  const projectDir = resolveProjectDir(harnessDir, { harnessDir });
3096
3741
  const projectId = opts.projectId ?? _DEFAULT_PROJECT;
3097
- const statusPath = join11(harnessDir, MIGRATE_STATUS_FILE);
3742
+ const statusPath = join13(harnessDir, MIGRATE_STATUS_FILE);
3098
3743
  const legacy = readJson(statusPath);
3099
3744
  if (legacy.version === 2) {
3100
3745
  const updatedAt = typeof legacy.updated_at === "string" && legacy.updated_at !== "" ? legacy.updated_at : "1970-01-01";
@@ -3121,12 +3766,12 @@ function migrateHarnessTree(root, opts = {}) {
3121
3766
  if (legacy.version === undefined) {
3122
3767
  throw new Error(`refusing to migrate: no v1 status.json found at ${statusPath} (nothing to migrate)`);
3123
3768
  }
3124
- const rows = Array.isArray(legacy.plans) ? legacy.plans.filter(isPlainObject6) : [];
3769
+ const rows = Array.isArray(legacy.plans) ? legacy.plans.filter(isPlainObject7) : [];
3125
3770
  if (Array.isArray(legacy.plans)) {
3126
3771
  const unLiftable = [];
3127
3772
  const idCounts = new Map;
3128
3773
  for (const row of legacy.plans) {
3129
- if (!isPlainObject6(row) || rowIdOf(row) === null) {
3774
+ if (!isPlainObject7(row) || rowIdOf(row) === null) {
3130
3775
  unLiftable.push(row);
3131
3776
  continue;
3132
3777
  }
@@ -3142,7 +3787,7 @@ function migrateHarnessTree(root, opts = {}) {
3142
3787
  throw new Error(`refusing to migrate: ${duplicates.length} duplicate plan id(s) (${duplicates.join(", ")}) — every v1 row must land in exactly one snapshot`);
3143
3788
  }
3144
3789
  }
3145
- const metadata = isPlainObject6(legacy.metadata) ? legacy.metadata : {};
3790
+ const metadata = isPlainObject7(legacy.metadata) ? legacy.metadata : {};
3146
3791
  const rootUpdatedAt = dateString(legacy.updated_at) ?? dateString(metadata.updated_at) ?? todayString3();
3147
3792
  const migratedAt = dateString(metadata.updated_at) ?? rootUpdatedAt;
3148
3793
  const migrationNotes = [];
@@ -3179,9 +3824,9 @@ function migrateHarnessTree(root, opts = {}) {
3179
3824
  migrationNotes.push("no active iteration snapshot found — root metadata execution-policy/branch keys have no lift home and stay unmapped (visible here, not silently dropped)");
3180
3825
  }
3181
3826
  const notesFiles = collectNotesFiles(snapshots);
3182
- const residualFindings = isPlainObject6(legacy.residual_findings) ? legacy.residual_findings : {};
3827
+ const residualFindings = isPlainObject7(legacy.residual_findings) ? legacy.residual_findings : {};
3183
3828
  const register = buildRegister(residualFindings, byPlan, projectId, migratedAt);
3184
- const programRoadmap = isPlainObject6(metadata.program_roadmap) ? metadata.program_roadmap : null;
3829
+ const programRoadmap = isPlainObject7(metadata.program_roadmap) ? metadata.program_roadmap : null;
3185
3830
  let roadmap = null;
3186
3831
  if (programRoadmap) {
3187
3832
  const rawTitle = typeof programRoadmap.title === "string" && programRoadmap.title !== "" ? programRoadmap.title : "Program roadmap";
@@ -3190,7 +3835,7 @@ function migrateHarnessTree(root, opts = {}) {
3190
3835
  migrationNotes.push(`roadmap title sanitized for frontmatter (line breaks replaced with spaces): ${JSON.stringify(rawTitle)}`);
3191
3836
  }
3192
3837
  roadmap = {
3193
- file: join11("projects", projectId, PROJECT_ROADMAP_FILE),
3838
+ file: join13("projects", projectId, PROJECT_ROADMAP_FILE),
3194
3839
  source: "status.json metadata.program_roadmap",
3195
3840
  content: buildRoadmap({ ...programRoadmap, title: sanitizedTitle }, projectId, migratedAt)
3196
3841
  };
@@ -3239,19 +3884,19 @@ async function applyMigratePlan(plan) {
3239
3884
  if (plan.dryRun) {
3240
3885
  return { applied: false, message: `dry-run: ${plan.steps.length} steps planned (source → destination), zero writes` };
3241
3886
  }
3242
- const statusPath = join11(plan.root, MIGRATE_STATUS_FILE);
3887
+ const statusPath = join13(plan.root, MIGRATE_STATUS_FILE);
3243
3888
  const current = readJson(statusPath);
3244
3889
  if (current.version === 2) {
3245
3890
  return { applied: false, message: "no-op: status.json already at schema version 2 (migrated) — nothing to do" };
3246
3891
  }
3247
- const harnessRoot = resolve10(plan.root);
3248
- const workflowRoot = resolve10(plan.workflowDir);
3249
- const projectRoot = resolve10(plan.projectDir);
3892
+ const harnessRoot = resolve11(plan.root);
3893
+ const workflowRoot = resolve11(plan.workflowDir);
3894
+ const projectRoot = resolve11(plan.projectDir);
3250
3895
  if (!isAbsolute7(plan.workflowDir) || !isAbsolute7(plan.projectDir)) {
3251
3896
  throw new Error(`refusing to apply migration: plan workflowDir/projectDir must be absolute (got ${JSON.stringify(plan.workflowDir)} / ${JSON.stringify(plan.projectDir)})`);
3252
3897
  }
3253
- const workflowTargetOf = (canonicalFile) => join11(workflowRoot, relative3("workflows", canonicalFile));
3254
- const projectTargetOf = (canonicalFile) => join11(projectRoot, relative3("projects", canonicalFile));
3898
+ const workflowTargetOf = (canonicalFile) => join13(workflowRoot, relative5("workflows", canonicalFile));
3899
+ const projectTargetOf = (canonicalFile) => join13(projectRoot, relative5("projects", canonicalFile));
3255
3900
  const allDestinations = [
3256
3901
  plan.archive.file,
3257
3902
  ...plan.snapshots.map((snapshot) => snapshot.file),
@@ -3260,20 +3905,20 @@ async function applyMigratePlan(plan) {
3260
3905
  ...plan.roadmap !== null ? [plan.roadmap.file] : []
3261
3906
  ];
3262
3907
  for (const destination of allDestinations) {
3263
- const resolvedDest = resolve10(join11(plan.root, destination));
3908
+ const resolvedDest = resolve11(join13(plan.root, destination));
3264
3909
  const inside = (dir) => resolvedDest === dir || resolvedDest.startsWith(`${dir}${sep2}`);
3265
3910
  if (!inside(harnessRoot) && !inside(workflowRoot) && !inside(projectRoot)) {
3266
3911
  throw new Error(`refusing to apply migration: destination escapes the harness dir (${JSON.stringify(destination)}) — every write must stay under ${JSON.stringify(plan.root)}, the workflow dir (${JSON.stringify(plan.workflowDir)}) or the project dir (${JSON.stringify(plan.projectDir)})`);
3267
3912
  }
3268
3913
  }
3269
- mkdirSync7(join11(plan.root, dirname7(plan.archive.file)), { recursive: true });
3270
- copyFileSync(statusPath, join11(plan.root, plan.archive.file));
3914
+ mkdirSync7(join13(plan.root, dirname8(plan.archive.file)), { recursive: true });
3915
+ copyFileSync(statusPath, join13(plan.root, plan.archive.file));
3271
3916
  for (const snapshot of plan.snapshots) {
3272
- await writeWorkflowSnapshot(snapshot.data, dirname7(workflowTargetOf(snapshot.file)));
3917
+ await writeWorkflowSnapshot(snapshot.data, dirname8(workflowTargetOf(snapshot.file)));
3273
3918
  }
3274
3919
  for (const notes of plan.notesFiles) {
3275
3920
  const filePath = workflowTargetOf(notes.file);
3276
- mkdirSync7(dirname7(filePath), { recursive: true });
3921
+ mkdirSync7(dirname8(filePath), { recursive: true });
3277
3922
  const content = notes.lines.length > 0 ? `${notes.lines.join(`
3278
3923
  `)}
3279
3924
  ` : "";
@@ -3286,13 +3931,13 @@ async function applyMigratePlan(plan) {
3286
3931
  }
3287
3932
  if (Object.keys(plan.register.data.entries ?? {}).length > 0) {
3288
3933
  const filePath = projectTargetOf(plan.register.file);
3289
- mkdirSync7(dirname7(filePath), { recursive: true });
3934
+ mkdirSync7(dirname8(filePath), { recursive: true });
3290
3935
  writeJson(filePath, plan.register.data);
3291
3936
  }
3292
3937
  }
3293
3938
  if (plan.roadmap !== null) {
3294
3939
  const filePath = projectTargetOf(plan.roadmap.file);
3295
- mkdirSync7(dirname7(filePath), { recursive: true });
3940
+ mkdirSync7(dirname8(filePath), { recursive: true });
3296
3941
  writeFileSync5(filePath, plan.roadmap.content, "utf8");
3297
3942
  }
3298
3943
  const rootGate = validateStatusV2(plan.rootV2.data, { harnessDir: plan.root });
@@ -3716,8 +4361,8 @@ function completenessLevel(frontmatterText, checklist) {
3716
4361
  }
3717
4362
  // src/audit.ts
3718
4363
  import { execFileSync as execFileSync4 } from "node:child_process";
3719
- import { existsSync as existsSync8, mkdirSync as mkdirSync8, readdirSync as readdirSync8, readFileSync as readFileSync9, rmdirSync as rmdirSync2, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
3720
- import { basename as basename5, join as join12, resolve as resolve11, sep as sep3 } from "node:path";
4364
+ import { existsSync as existsSync9, mkdirSync as mkdirSync8, readdirSync as readdirSync8, readFileSync as readFileSync9, rmdirSync as rmdirSync2, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
4365
+ import { basename as basename6, join as join14, resolve as resolve12, sep as sep3 } from "node:path";
3721
4366
  function violation9(severity, code, message, fix) {
3722
4367
  return { ok: false, severity, code, message, fix };
3723
4368
  }
@@ -4018,7 +4663,7 @@ function scanSecrets(files) {
4018
4663
  unreadableFiles++;
4019
4664
  continue;
4020
4665
  }
4021
- const base = basename5(file);
4666
+ const base = basename6(file);
4022
4667
  for (const entry of NEVER_COMMIT_FILENAMES) {
4023
4668
  if (entry.re.test(base))
4024
4669
  findings.push({ file, line: 1, type: entry.type });
@@ -4075,7 +4720,7 @@ function rootLockfiles(root) {
4075
4720
  return [];
4076
4721
  }
4077
4722
  const names = new Set(LOCKFILE_NAMES);
4078
- const present = entries.filter((entry) => entry.isFile() && names.has(entry.name)).map((entry) => join12(root, entry.name));
4723
+ const present = entries.filter((entry) => entry.isFile() && names.has(entry.name)).map((entry) => join14(root, entry.name));
4079
4724
  if (present.length === 0)
4080
4725
  return [];
4081
4726
  try {
@@ -4084,7 +4729,7 @@ function rootLockfiles(root) {
4084
4729
  encoding: "utf8",
4085
4730
  stdio: ["ignore", "pipe", "ignore"]
4086
4731
  }).split("\x00").filter((f) => f !== ""));
4087
- return present.filter((p) => tracked.has(basename5(p)));
4732
+ return present.filter((p) => tracked.has(basename6(p)));
4088
4733
  } catch {
4089
4734
  return present;
4090
4735
  }
@@ -4100,7 +4745,7 @@ function supplyChainChecks(repoRoot) {
4100
4745
  findings.push({ kind: "lockfile-duplicate", file: lockfiles.map((f) => f.replace(`${repoRoot}/`, "")).join(", ") });
4101
4746
  violations.push(violation9("medium", "audit.supply.lockfile-duplicate", `multiple lockfiles at ${repoRoot}: ${lockfiles.join(", ")}`, "keep exactly one lockfile per package manager"));
4102
4747
  }
4103
- const workflowsDir = join12(repoRoot, ".github", "workflows");
4748
+ const workflowsDir = join14(repoRoot, ".github", "workflows");
4104
4749
  let wfEntries = [];
4105
4750
  try {
4106
4751
  wfEntries = readdirSync8(workflowsDir, { withFileTypes: true });
@@ -4110,7 +4755,7 @@ function supplyChainChecks(repoRoot) {
4110
4755
  for (const entry of wfEntries) {
4111
4756
  if (!entry.isFile() || !/\.(?:ya?ml)$/.test(entry.name))
4112
4757
  continue;
4113
- const wfPath = join12(workflowsDir, entry.name);
4758
+ const wfPath = join14(workflowsDir, entry.name);
4114
4759
  const relPath = `.github/workflows/${entry.name}`;
4115
4760
  let text;
4116
4761
  try {
@@ -4267,8 +4912,8 @@ function scaffoldAuditPlan(outDir, findings, options = {}) {
4267
4912
  const date = options.date ?? new Date().toISOString().slice(0, 10);
4268
4913
  const plannedAt = options.plannedAt ?? { commit: options.repoShortSha ?? "unknown", date };
4269
4914
  mkdirSync8(outDir, { recursive: true });
4270
- const existingReadme = join12(outDir, "README.md");
4271
- const carried = existsSync8(existingReadme) ? extractSecurityDispositionSections(readFileSync9(existingReadme, "utf8")) : { needsVerification: [], hardeningChecked: [] };
4915
+ const existingReadme = join14(outDir, "README.md");
4916
+ const carried = existsSync9(existingReadme) ? extractSecurityDispositionSections(readFileSync9(existingReadme, "utf8")) : { needsVerification: [], hardeningChecked: [] };
4272
4917
  const existing = readdirSync8(outDir).filter((f) => /^\d{3}-.*\.md$/.test(f));
4273
4918
  let next = existing.reduce((max, f) => Math.max(max, Number(f.slice(0, 3))), 0) + 1;
4274
4919
  const redactedFindings = findings.map(redactFinding);
@@ -4285,13 +4930,13 @@ function scaffoldAuditPlan(outDir, findings, options = {}) {
4285
4930
  }
4286
4931
  usedSlugs.add(slug);
4287
4932
  const file = `${num}-${slug}.md`;
4288
- writeFileSync6(join12(outDir, file), renderPlanFile(finding, plannedAt));
4933
+ writeFileSync6(join14(outDir, file), renderPlanFile(finding, plannedAt));
4289
4934
  written.push(file);
4290
4935
  next++;
4291
4936
  }
4292
4937
  const all = [...existing, ...written].sort();
4293
4938
  const rows = all.map((file) => {
4294
- const summary = readPlanFileSummary(join12(outDir, file));
4939
+ const summary = readPlanFileSummary(join14(outDir, file));
4295
4940
  const fields = summary.fields;
4296
4941
  return {
4297
4942
  num: file.slice(0, 3),
@@ -4325,7 +4970,7 @@ function scaffoldAuditPlan(outDir, findings, options = {}) {
4325
4970
  });
4326
4971
  const needsVerificationLines = options.needsVerification !== undefined ? options.needsVerification.map((nv) => `- ${escapeCell(redactText(nv.lead))}: ${escapeCell(redactText(nv.how))}${nv.evidence ? ` (${escapeCell(redactText(nv.evidence))})` : ""}`) : carried.needsVerification;
4327
4972
  const hardeningCheckedLines = options.hardeningChecked !== undefined ? options.hardeningChecked.map((hc) => `- ${hc.kind}: ${escapeCell(redactText(hc.text))}`) : carried.hardeningChecked;
4328
- writeFileSync6(join12(outDir, "README.md"), renderIndex({
4973
+ writeFileSync6(join14(outDir, "README.md"), renderIndex({
4329
4974
  date,
4330
4975
  repoName: options.repoName ?? "repo",
4331
4976
  repoShortSha: options.repoShortSha ?? "unknown",
@@ -4334,7 +4979,7 @@ function scaffoldAuditPlan(outDir, findings, options = {}) {
4334
4979
  needsVerification: needsVerificationLines,
4335
4980
  hardeningChecked: hardeningCheckedLines
4336
4981
  }));
4337
- return { outDir: resolve11(outDir), date, files: written, nextNumber: next };
4982
+ return { outDir: resolve12(outDir), date, files: written, nextNumber: next };
4338
4983
  }
4339
4984
  async function promoteAuditPlans(outDir, selected, options) {
4340
4985
  if (selected.length === 0) {
@@ -4343,19 +4988,19 @@ async function promoteAuditPlans(outDir, selected, options) {
4343
4988
  if (typeof options.harnessDir !== "string" || options.harnessDir.trim() === "") {
4344
4989
  throw new Error("promoteAuditPlans: options.harnessDir is required (must contain status.json + workflows/)");
4345
4990
  }
4346
- const workflowId = options.workflowId ?? basename5(resolve11(outDir));
4991
+ const workflowId = options.workflowId ?? basename6(resolve12(outDir));
4347
4992
  assertSafePathComponent(workflowId, "workflow id");
4348
- const harnessDir = resolve11(options.harnessDir);
4349
- const statusPath = join12(harnessDir, "status.json");
4350
- const workflowDir = join12(harnessDir, "workflows", workflowId);
4351
- const snapshotPath = join12(workflowDir, WORKFLOW_SNAPSHOT_FILE);
4993
+ const harnessDir = resolve12(options.harnessDir);
4994
+ const statusPath = join14(harnessDir, "status.json");
4995
+ const workflowDir = join14(harnessDir, "workflows", workflowId);
4996
+ const snapshotPath = join14(workflowDir, WORKFLOW_SNAPSHOT_FILE);
4352
4997
  const planFiles = resolveSelectedPlanFiles(outDir, selected);
4353
4998
  const indexRows = readExecutionOrderIndex(outDir);
4354
4999
  const plans = planFiles.map((planFile) => {
4355
5000
  const stem = planFile.replace(/\.md$/, "");
4356
5001
  const num = stem.slice(0, 3);
4357
5002
  const indexRow = indexRows.get(num);
4358
- const title = indexRow?.title ?? readPlanFileSummary(join12(outDir, planFile)).title;
5003
+ const title = indexRow?.title ?? readPlanFileSummary(join14(outDir, planFile)).title;
4359
5004
  return {
4360
5005
  id: stem,
4361
5006
  title,
@@ -4384,7 +5029,7 @@ async function promoteAuditPlans(outDir, selected, options) {
4384
5029
  throw new Error(`refusing to register invalid workflow entry: ${entryGate.violations.map((v) => v.message).join("; ")}`);
4385
5030
  }
4386
5031
  await withStatusWriteLock(statusPath, async () => {
4387
- if (existsSync8(snapshotPath)) {
5032
+ if (existsSync9(snapshotPath)) {
4388
5033
  throw new Error(`refusing to promote audit plans: workflow ${JSON.stringify(workflowId)} already exists ` + `(snapshot at ${snapshotPath}) — re-promote would drop its registered plan rows; ` + `remove that workflow before promoting again`);
4389
5034
  }
4390
5035
  mkdirSync8(workflowDir, { recursive: true });
@@ -4420,7 +5065,7 @@ function resolveSelectedPlanFiles(outDir, selected) {
4420
5065
  for (const id of selected) {
4421
5066
  const file = byNum.get(id) ?? byStem.get(id) ?? byStem.get(id.replace(/\.md$/, ""));
4422
5067
  if (file === undefined) {
4423
- throw new Error(`promoteAuditPlans: selected plan ${JSON.stringify(id)} does not match any NNN-*.md file in ${resolve11(outDir)}`);
5068
+ throw new Error(`promoteAuditPlans: selected plan ${JSON.stringify(id)} does not match any NNN-*.md file in ${resolve12(outDir)}`);
4424
5069
  }
4425
5070
  if (!seen.has(file)) {
4426
5071
  seen.add(file);
@@ -4430,7 +5075,7 @@ function resolveSelectedPlanFiles(outDir, selected) {
4430
5075
  return resolved;
4431
5076
  }
4432
5077
  function readExecutionOrderIndex(outDir) {
4433
- const readmePath = join12(outDir, "README.md");
5078
+ const readmePath = join14(outDir, "README.md");
4434
5079
  let text;
4435
5080
  try {
4436
5081
  text = readFileSync9(readmePath, "utf8");
@@ -4459,7 +5104,7 @@ function readExecutionOrderIndex(outDir) {
4459
5104
  return rows;
4460
5105
  }
4461
5106
  function planFileRel(outDir, planFile) {
4462
- const resolved = resolve11(outDir);
5107
+ const resolved = resolve12(outDir);
4463
5108
  const parts = resolved.split(sep3);
4464
5109
  const plansIdx = parts.lastIndexOf("plans");
4465
5110
  if (plansIdx >= 0) {
@@ -4468,8 +5113,8 @@ function planFileRel(outDir, planFile) {
4468
5113
  return planFile;
4469
5114
  }
4470
5115
  // src/compound.ts
4471
- import { existsSync as existsSync9, readdirSync as readdirSync9, readFileSync as readFileSync10 } from "node:fs";
4472
- import { basename as basename6, isAbsolute as isAbsolute8, join as join13, relative as relative4, resolve as resolve12, sep as sep4 } from "node:path";
5116
+ import { existsSync as existsSync10, readdirSync as readdirSync9, readFileSync as readFileSync10 } from "node:fs";
5117
+ import { basename as basename7, isAbsolute as isAbsolute8, join as join15, relative as relative6, resolve as resolve13, sep as sep4 } from "node:path";
4473
5118
  function violation10(severity, code, message, fix) {
4474
5119
  return { ok: false, severity, code, message, fix };
4475
5120
  }
@@ -4764,7 +5409,7 @@ function referenceExists(repoRoot, docText) {
4764
5409
  break;
4765
5410
  if (entry.isDirectory()) {
4766
5411
  if (!WALK_SKIP_DIRS.has(entry.name))
4767
- stack.push(join13(dir, entry.name));
5412
+ stack.push(join15(dir, entry.name));
4768
5413
  } else if (!entry.isSymbolicLink()) {
4769
5414
  const base = entry.name.replace(/\.(?:ts|tsx|js|jsx|mjs|cjs)$/, "");
4770
5415
  if (moduleNames.has(base))
@@ -4776,7 +5421,7 @@ function referenceExists(repoRoot, docText) {
4776
5421
  for (const { ref, isSymbol, module } of refs) {
4777
5422
  if (!isSymbol || module === undefined) {
4778
5423
  const candidate = ref.replace(LINE_SUFFIX_RE, "").replace(ANCHOR_RE, "");
4779
- if (existsSync9(resolve12(repoRoot, candidate))) {
5424
+ if (existsSync10(resolve13(repoRoot, candidate))) {
4780
5425
  checked++;
4781
5426
  } else {
4782
5427
  violations.push(violation10("medium", "compound.reference.missing-file", `referenced path \`${ref}\` does not exist under ${repoRoot} (compound-refresh Phase 2: referenced code still exists?)`, "update the doc to reference an existing path, or delete the stale reference"));
@@ -4803,11 +5448,11 @@ function collectKnowledgeDocs(dir) {
4803
5448
  for (const entry of entries) {
4804
5449
  if (entry.isSymbolicLink())
4805
5450
  continue;
4806
- const full = join13(current, entry.name);
5451
+ const full = join15(current, entry.name);
4807
5452
  if (entry.isDirectory()) {
4808
5453
  stack.push(full);
4809
5454
  } else if (entry.name.endsWith(".md") && entry.name !== "README.md" && entry.name !== "index.md") {
4810
- docs.push(relative4(dir, full).split(sep4).join("/"));
5455
+ docs.push(relative6(dir, full).split(sep4).join("/"));
4811
5456
  }
4812
5457
  }
4813
5458
  }
@@ -4823,8 +5468,8 @@ function normalizeIndexRef(cell) {
4823
5468
  }
4824
5469
  function assertIndexRows(knowledgeDir) {
4825
5470
  const violations = [];
4826
- const readmePath = join13(knowledgeDir, "README.md");
4827
- if (!existsSync9(readmePath)) {
5471
+ const readmePath = join15(knowledgeDir, "README.md");
5472
+ if (!existsSync10(readmePath)) {
4828
5473
  violations.push(violation10("medium", "compound.index.missing-readme", `missing ${readmePath} — the knowledge index is required (mstar-compound Phase 6: every doc gets a README.md row)`, "create knowledge/README.md with a Document / Source Plan / Description / Status table"));
4829
5474
  return { ok: false, violations };
4830
5475
  }
@@ -4849,19 +5494,19 @@ function assertIndexRows(knowledgeDir) {
4849
5494
  }
4850
5495
  function compoundRefreshScope(harnessDir, projectRoot) {
4851
5496
  return [
4852
- join13(harnessDir, "knowledge"),
4853
- join13(harnessDir, "knowledge", "README.md"),
4854
- join13(projectRoot, "CONCEPTS.md"),
4855
- join13(harnessDir, "status.json")
5497
+ join15(harnessDir, "knowledge"),
5498
+ join15(harnessDir, "knowledge", "README.md"),
5499
+ join15(projectRoot, "CONCEPTS.md"),
5500
+ join15(harnessDir, "status.json")
4856
5501
  ];
4857
5502
  }
4858
5503
  function isFileLikeRoot(root) {
4859
- return /^[^.]*\.[A-Za-z0-9]{1,10}$/.test(basename6(root));
5504
+ return /^[^.]*\.[A-Za-z0-9]{1,10}$/.test(basename7(root));
4860
5505
  }
4861
5506
  function scopeGuard(path, allowedRoots) {
4862
- const resolved = resolve12(path);
5507
+ const resolved = resolve13(path);
4863
5508
  for (const root of allowedRoots) {
4864
- const r = resolve12(root);
5509
+ const r = resolve13(root);
4865
5510
  if (isFileLikeRoot(r)) {
4866
5511
  if (resolved === r)
4867
5512
  return { ok: true, violations: [] };
@@ -4920,7 +5565,7 @@ function findTemporaryMarkers(fileText) {
4920
5565
  }
4921
5566
  markers.push({ line: i + 1, text, removalPath });
4922
5567
  if (removalPath === null) {
4923
- violations.push(violation11("medium", "lint.temporary.no-removal-path", `temporary marker at line ${i + 1} records no removal path (plan/status artifact reference) — record one before claiming the task complete (mstar-coding-behavior § Simplification markers)`, 'add a plan/status reference to the marker, e.g. "removal tracked in status.json" or "plan 20260808-slice2 removes this"'));
5568
+ violations.push(violation11("medium", "lint.temporary.no-removal-path", `temporary marker at line ${i + 1} records no removal path (plan/status artifact reference) — record one before claiming the task complete (mstar-coding-behavior § Simplification markers)`, 'add a plan/status reference to the marker, e.g. "removal tracked in status.json" or "plan 20991231-example-plan removes this"'));
4924
5569
  }
4925
5570
  }
4926
5571
  return { ok: violations.length === 0, violations, markers };
@@ -5109,8 +5754,8 @@ function lintStrategySections(docText) {
5109
5754
  return { ok: violations.length === 0, violations };
5110
5755
  }
5111
5756
  // src/roles.ts
5112
- import { existsSync as existsSync10 } from "node:fs";
5113
- import { join as join14 } from "node:path";
5757
+ import { existsSync as existsSync11 } from "node:fs";
5758
+ import { join as join16 } from "node:path";
5114
5759
  function violation12(severity, code, message, fix) {
5115
5760
  return { ok: false, severity, code, message, fix };
5116
5761
  }
@@ -5166,8 +5811,8 @@ function validateRoleMapping(rolesDir, options = {}) {
5166
5811
  const violations = [];
5167
5812
  const referenceById = new Map(mapping.map((m) => [m.agentId, m.reference]));
5168
5813
  for (const { agentId, reference } of mapping) {
5169
- if (!existsSync10(join14(rolesDir, reference))) {
5170
- violations.push(violation12("medium", "roles.mapping.reference.missing", `role "${agentId}" maps to ${reference} which does not exist under ${rolesDir} (mstar-roles § Role Reference Mapping)`, `create ${join14(rolesDir, reference)} or fix the mapping row`));
5814
+ if (!existsSync11(join16(rolesDir, reference))) {
5815
+ violations.push(violation12("medium", "roles.mapping.reference.missing", `role "${agentId}" maps to ${reference} which does not exist under ${rolesDir} (mstar-roles § Role Reference Mapping)`, `create ${join16(rolesDir, reference)} or fix the mapping row`));
5171
5816
  }
5172
5817
  }
5173
5818
  for (const { family, memberIds } of families) {
@@ -5219,6 +5864,14 @@ function validateRoleMapping(rolesDir, options = {}) {
5219
5864
  return { ok: violations.length === 0, violations };
5220
5865
  }
5221
5866
  var LOAD_ORDER_HEADING_RE = /^#{1,6}\s+[^\r\n]*\b(?:load[\s-]*order|first\s+action)\b[^\r\n]*$/i;
5867
+ var HUB_BOOTSTRAP_ASSERTIONS = [
5868
+ { marker: "identity-first", why: "identity boundary before any skill list" },
5869
+ { marker: "skill presets", why: "Assignment Skill presets decision field" },
5870
+ { marker: "none", why: "explicit none => identity only, no optional topic preset" },
5871
+ { marker: "standard", why: "omitted on a substantive round => standard preset" },
5872
+ { marker: "role-owned", why: "role-owned methods / evidence obligations load regardless of preset" },
5873
+ { marker: "unknown preset", why: "unknown preset / missing identity => Needs Context / Blocked, never infer PM" }
5874
+ ];
5222
5875
  function extractLoadOrderSection(text) {
5223
5876
  const lines = text.split(/\r?\n/);
5224
5877
  let start = -1;
@@ -5252,11 +5905,22 @@ function lintLoadOrder(skillTexts) {
5252
5905
  continue;
5253
5906
  const section = extractLoadOrderSection(text);
5254
5907
  if (section === null) {
5255
- violations.push(violation12("medium", "roles.loadorder.section.missing", `skill "${name}" has no Load Order / First action section — every mstar-* topic skill must declare its first read (mstar-harness-core § 加载约定; mstar-roles § Load Order (Required))`, `add a "## Load Order" section naming mstar-harness-core as the first read`));
5908
+ violations.push(violation12("medium", "roles.loadorder.section.missing", `skill "${name}" has no Load Order / First action section — every mstar-* topic skill must declare its first read (mstar-harness-core § 加载契约; mstar-roles § Load Order = single load-selection authority)`, `add a "## Load Order" section: topics name mstar-harness-core first; mstar-roles declares the Skill presets decision matrix`));
5909
+ continue;
5910
+ }
5911
+ if (name === "mstar-roles") {
5912
+ const lower = section.toLowerCase();
5913
+ const missing = HUB_BOOTSTRAP_ASSERTIONS.filter((a) => !lower.includes(a.marker.toLowerCase()));
5914
+ if (missing.length > 0) {
5915
+ violations.push(violation12("medium", "roles.loadorder.hub.bootstrap.missing", `skill "${name}" Load Order section is missing the hub bootstrap decision matrix: ${missing.map((m) => `"${m.marker}" (${m.why})`).join("; ")} (mstar-roles § Load Order = single load-selection authority)`, "declare identity-first, the Skill presets none/standard decision, role-owned methods, and the unknown-preset / missing-identity refusal in the Load Order section"));
5916
+ }
5917
+ if (!section.includes("mstar-harness-core")) {
5918
+ violations.push(violation12("medium", "roles.loadorder.core.missing", `skill "${name}" Load Order section does not point at mstar-harness-core — the hub bootstrap is exempt from core-first, but core remains the lifecycle/authorization conflict authority and global entry whenever loaded`, "keep the conditional mstar-harness-core pointer (global entry / conflict authority) in the Load Order section"));
5919
+ }
5256
5920
  continue;
5257
5921
  }
5258
5922
  if (!section.includes("mstar-harness-core")) {
5259
- violations.push(violation12("medium", "roles.loadorder.core.missing", `skill "${name}" Load Order section does not declare mstar-harness-core as its first dependency (mstar-harness-core § 加载约定: mstar-*(name mstar-harness-core)假定读者已 Read skill)`, "name mstar-harness-core first in the Load Order section"));
5923
+ violations.push(violation12("medium", "roles.loadorder.core.missing", `skill "${name}" Load Order section does not declare mstar-harness-core as its first dependency (mstar-harness-core § 加载契约: directly-invoked topics keep core as first dependency; the mstar-roles hub bootstrap is the only exception)`, "name mstar-harness-core first in the Load Order section"));
5260
5924
  }
5261
5925
  }
5262
5926
  return { ok: violations.length === 0, violations };
@@ -5338,6 +6002,16 @@ function collectHeadings(bodyText) {
5338
6002
  }
5339
6003
  return headings;
5340
6004
  }
6005
+ function classifySkillLint(skillId) {
6006
+ const id = skillId ?? "";
6007
+ if (id === "mstar-harness-core")
6008
+ return { kind: "core", mode: null };
6009
+ if (id === "mstar-skill-authoring")
6010
+ return { kind: "authoring", mode: "authoring" };
6011
+ if (id.startsWith("mstar-"))
6012
+ return { kind: "runtime", mode: "runtime" };
6013
+ return { kind: "authoring", mode: "authoring" };
6014
+ }
5341
6015
  var RUNTIME_HEADING_ALIASES = {
5342
6016
  workflow: ["process", "playbook"],
5343
6017
  "decision-rules": [
@@ -5373,7 +6047,7 @@ function resolveAssetPath(skillName, relPath, host) {
5373
6047
  }
5374
6048
  // src/prreview.ts
5375
6049
  import { readdirSync as readdirSync10 } from "node:fs";
5376
- import { isAbsolute as isAbsolute9, join as join15 } from "node:path";
6050
+ import { isAbsolute as isAbsolute9, join as join17 } from "node:path";
5377
6051
  var MERGE_CLASSES = ["must-fix", "should-fix", "nit"];
5378
6052
  var PR_VERDICTS = ["ship it", "needs fixes", "blocked"];
5379
6053
  var REVIEW_EMOJI = {
@@ -5413,7 +6087,7 @@ function computePrTally(input) {
5413
6087
  var REVIEW_SCHEMA_ID = "mstar.review/v1";
5414
6088
  var INSPECTOR_VERDICTS = ["comment", "request_changes", "approve"];
5415
6089
  var INSPECTOR_SEVERITIES = ["critical", "warning", "suggestion", "info"];
5416
- function isPlainObject7(value) {
6090
+ function isPlainObject8(value) {
5417
6091
  return typeof value === "object" && value !== null && !Array.isArray(value);
5418
6092
  }
5419
6093
  var TALLY_COUNT_KEYS = ["mustFix", "shouldFix", "nit", "unverified"];
@@ -5424,7 +6098,7 @@ function checkProvidedTallyShape(tally, violations) {
5424
6098
  if (typeof tally.scorePct !== "number" || !Number.isInteger(tally.scorePct) || tally.scorePct < 0 || tally.scorePct > 100) {
5425
6099
  violations.push(violation14("high", "review.tally-malformed", `tally.scorePct must be an integer in [0, 100] - got ${String(tally.scorePct)}`));
5426
6100
  }
5427
- if (!isPlainObject7(tally.tally)) {
6101
+ if (!isPlainObject8(tally.tally)) {
5428
6102
  violations.push(violation14("high", "review.tally-malformed", "tally.tally must be an object carrying the four class counts"));
5429
6103
  } else {
5430
6104
  for (const key of TALLY_COUNT_KEYS) {
@@ -5440,7 +6114,7 @@ function checkProvidedTallyShape(tally, violations) {
5440
6114
  }
5441
6115
  function validateMstarReviewV1(doc) {
5442
6116
  const violations = [];
5443
- if (!isPlainObject7(doc)) {
6117
+ if (!isPlainObject8(doc)) {
5444
6118
  return {
5445
6119
  ok: false,
5446
6120
  violations: [violation14("high", "review.not-object", "review document must be a JSON object")]
@@ -5467,7 +6141,7 @@ function validateMstarReviewV1(doc) {
5467
6141
  violations.push(violation14("high", "review.findings-not-array", "findings must be an array"));
5468
6142
  } else {
5469
6143
  doc.findings.forEach((finding, index) => {
5470
- if (!isPlainObject7(finding)) {
6144
+ if (!isPlainObject8(finding)) {
5471
6145
  violations.push(violation14("high", "review.invalid-finding", `findings[${index}] must be an object`));
5472
6146
  return;
5473
6147
  }
@@ -5507,7 +6181,7 @@ function validateMstarReviewV1(doc) {
5507
6181
  });
5508
6182
  }
5509
6183
  if (doc.tally !== undefined) {
5510
- if (!isPlainObject7(doc.tally)) {
6184
+ if (!isPlainObject8(doc.tally)) {
5511
6185
  violations.push(violation14("high", "review.invalid-tally", "tally must be a PrTallyResult object"));
5512
6186
  } else {
5513
6187
  checkProvidedTallyShape(doc.tally, violations);
@@ -5517,7 +6191,7 @@ function validateMstarReviewV1(doc) {
5517
6191
  }
5518
6192
  }
5519
6193
  if (doc.target !== undefined) {
5520
- if (!isPlainObject7(doc.target)) {
6194
+ if (!isPlainObject8(doc.target)) {
5521
6195
  violations.push(violation14("high", "review.invalid-target", "target must be an object"));
5522
6196
  } else {
5523
6197
  if (doc.target.owner !== undefined && typeof doc.target.owner !== "string") {
@@ -5629,7 +6303,7 @@ function prReviewReportPath(opts) {
5629
6303
  }
5630
6304
  const revision = maxRevision + 1;
5631
6305
  const name = revision === 1 ? `${finalStem}.md` : `${finalStem}-r${revision}.md`;
5632
- return join15(opts.reportsDir, name);
6306
+ return join17(opts.reportsDir, name);
5633
6307
  }
5634
6308
  var PR_TIERS = ["quick", "default", "deep"];
5635
6309
  function violation14(severity, code, message, fix) {
@@ -5946,7 +6620,7 @@ function prReviewSeatPrompt(opts) {
5946
6620
  lines.push("");
5947
6621
  lines.push("## Read first");
5948
6622
  lines.push("");
5949
- const prReviewRef = join15(skillRoot, "references", "pr-review.md");
6623
+ const prReviewRef = join17(skillRoot, "references", "pr-review.md");
5950
6624
  const sections = opts.stage === 1 ? tier === "quick" ? "Scoping, Evidence rules" : "Review pipeline, Worktree isolation, Scoping, Evidence rules" : "Merge class, Attack and vet, Evidence rules, Sizing & change shape";
5951
6625
  lines.push(`1. \`${prReviewRef}\` — read at least these sections: ${sections}.`);
5952
6626
  lines.push(`2. The review worktree: \`${worktreePath}\` — your ONLY working directory this session; read-only (no edits, no fixes, no stash, no commits, no posts).`);
@@ -5954,9 +6628,9 @@ function prReviewSeatPrompt(opts) {
5954
6628
  lines.push(`- Read the pinned diff snapshot FIRST: \`${opts.diffFile}\` — it is the review's diff basis (already computed at setup); read it before opening files.`);
5955
6629
  }
5956
6630
  if (opts.stage === 2) {
5957
- lines.push(`3. \`${join15(skillRoot, "references", "finding-format.md")}\` — the template every finding follows.`);
6631
+ lines.push(`3. \`${join17(skillRoot, "references", "finding-format.md")}\` — the template every finding follows.`);
5958
6632
  if (opts.securitySeat === true) {
5959
- lines.push(`4. \`${join15(skillRoot, "references", "security-review.md")}\` — the security lens.`);
6633
+ lines.push(`4. \`${join17(skillRoot, "references", "security-review.md")}\` — the security lens.`);
5960
6634
  }
5961
6635
  }
5962
6636
  const budget = PR_REVIEW_TIER_BUDGETS[tier];
@@ -6154,6 +6828,7 @@ export {
6154
6828
  KNOWLEDGE_REQUIRED_FIELDS,
6155
6829
  KNOWLEDGE_RESOLUTION_TYPES,
6156
6830
  KNOWLEDGE_SEVERITIES,
6831
+ MAX_STATUS_CONTENT_LENGTH,
6157
6832
  MERGE_CLASSES,
6158
6833
  MIGRATE_STATUS_FILE,
6159
6834
  MSTARC_FILE,
@@ -6198,7 +6873,10 @@ export {
6198
6873
  assertTriIdentity,
6199
6874
  assignmentHeaderRegion,
6200
6875
  canSteal,
6876
+ canonicalizeNearestExisting,
6877
+ checkSddAction,
6201
6878
  claimLease,
6879
+ classifySkillLint,
6202
6880
  closeProjectRegisterEntry,
6203
6881
  completenessLevel,
6204
6882
  composeDispatchGate,
@@ -6209,14 +6887,18 @@ export {
6209
6887
  detectHost,
6210
6888
  emitGitignoreSnippet,
6211
6889
  evaluatePhaseGate,
6890
+ eventTargetPaths,
6212
6891
  executionModeToN,
6213
6892
  findEphemeralCitations,
6214
6893
  findMstarc,
6215
6894
  findSimplifyMarkers,
6216
6895
  findTemporaryMarkers,
6217
6896
  findingsCleanupGate,
6897
+ formatStatusWriteBlockReason,
6218
6898
  getArtifactStore,
6899
+ harnessDocKindOfTarget,
6219
6900
  implementerSessionStickyRules,
6901
+ isDistinctCheckout,
6220
6902
  isReadOnlyAssignmentRole,
6221
6903
  l1PreDispatchCheck,
6222
6904
  l2PreDispatchCheck,
@@ -6245,6 +6927,7 @@ export {
6245
6927
  prReviewSeatPrompt,
6246
6928
  prReviewSizing,
6247
6929
  preflightChangeset,
6930
+ probeCheckoutRoot,
6248
6931
  promoteAuditPlans,
6249
6932
  pushCadenceProbe,
6250
6933
  readHarnessVersion,
@@ -6267,10 +6950,12 @@ export {
6267
6950
  resolveRepoEnforcement,
6268
6951
  resolveScaffoldDirs,
6269
6952
  resolveSddDir,
6953
+ resolveSddExecutionContext,
6270
6954
  resolveSkillRoot,
6271
6955
  resolveSpecsDir,
6272
6956
  resolveWorkflowDir,
6273
6957
  reviewPackage,
6958
+ runInSddContext,
6274
6959
  sameHolderResume,
6275
6960
  scaffoldAuditPlan,
6276
6961
  scaffoldHarness,
@@ -6304,9 +6989,11 @@ export {
6304
6989
  validateSchemaYaml,
6305
6990
  validateStatus,
6306
6991
  validateStatusV2,
6992
+ validateStatusWriteDoc,
6307
6993
  validateWorkflowEntry,
6308
6994
  validateWorkflowSnapshot,
6309
6995
  verifyPlanExecutionLease,
6996
+ violationLine,
6310
6997
  withStatusWriteLock,
6311
6998
  writeJson,
6312
6999
  writeWorkflowSnapshot