@brainervirus/workit-claude-code 7.6.0 → 8.0.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/workit.js CHANGED
@@ -65,7 +65,7 @@ var package_default;
65
65
  var init_package = __esm(() => {
66
66
  package_default = {
67
67
  name: "@brainervirus/workit-cli",
68
- version: "7.6.0",
68
+ version: "8.0.0",
69
69
  private: false,
70
70
  description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
71
71
  keywords: [
@@ -103,9 +103,9 @@ var init_package = __esm(() => {
103
103
  },
104
104
  dependencies: {
105
105
  "@babel/parser": "7.29.9",
106
- "@brainervirus/workit-core": "^7.6.0",
107
- "@brainervirus/workit-cursor": "^7.6.0",
108
- "@brainervirus/workit-opencode": "^7.6.0",
106
+ "@brainervirus/workit-core": "^8.0.0",
107
+ "@brainervirus/workit-cursor": "^8.0.0",
108
+ "@brainervirus/workit-opencode": "^8.0.0",
109
109
  "@inkjs/ui": "2.0.0",
110
110
  ink: "7.1.1",
111
111
  react: "19.2.8"
@@ -165,7 +165,7 @@ var init_output = __esm(() => {
165
165
  };
166
166
  });
167
167
 
168
- // packages/workit-core/src/check-config.ts
168
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/check-config.ts
169
169
  import { spawnSync } from "node:child_process";
170
170
  import fs from "node:fs";
171
171
  import path from "node:path";
@@ -7974,7 +7974,7 @@ var init_zod = __esm(() => {
7974
7974
  init_external();
7975
7975
  });
7976
7976
 
7977
- // packages/workit-core/src/core/task-contract.ts
7977
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/task-contract.ts
7978
7978
  import { createHash, randomUUID } from "node:crypto";
7979
7979
  function parseOperation(family, input) {
7980
7980
  if (typeof input === "object" && input !== null && "schemaVersion" in input && input.schemaVersion !== SCHEMA_VERSION)
@@ -8812,7 +8812,7 @@ var init_task_contract = __esm(() => {
8812
8812
  compiledOperationSchemas = Object.fromEntries(OPERATION_FAMILIES.map((family) => [family, compile(operationSchemas[family])]));
8813
8813
  });
8814
8814
 
8815
- // packages/workit-core/src/core/operation-input.ts
8815
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/operation-input.ts
8816
8816
  function normalizeOperationInput(family, input, host) {
8817
8817
  if (!isObject2(input))
8818
8818
  return input;
@@ -9149,7 +9149,7 @@ var init_operation_input = __esm(() => {
9149
9149
  ]);
9150
9150
  });
9151
9151
 
9152
- // packages/workit-core/src/core/policy/judgment.ts
9152
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/policy/judgment.ts
9153
9153
  function normalizeJudgment(raw, previous) {
9154
9154
  const input = isObject3(raw) ? raw : {};
9155
9155
  const legacy = Object.keys(input).some((key) => LEGACY_KEYS.has(key));
@@ -9914,7 +9914,7 @@ var init_file_lock = __esm(() => {
9914
9914
  init_file_lock_sync();
9915
9915
  });
9916
9916
 
9917
- // packages/workit-core/src/core/package-root.ts
9917
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/package-root.ts
9918
9918
  import { existsSync } from "node:fs";
9919
9919
  import path4 from "node:path";
9920
9920
  import { fileURLToPath } from "node:url";
@@ -9933,7 +9933,7 @@ var packageRoot = () => {
9933
9933
  };
9934
9934
  var init_package_root = () => {};
9935
9935
 
9936
- // packages/workit-core/src/store/paths.ts
9936
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/store/paths.ts
9937
9937
  import { spawnSync as spawnSync2 } from "node:child_process";
9938
9938
  import { createHash as createHash2 } from "node:crypto";
9939
9939
  import fs5 from "node:fs";
@@ -10209,7 +10209,7 @@ var init_paths = __esm(() => {
10209
10209
  ];
10210
10210
  });
10211
10211
 
10212
- // packages/workit-core/src/core/store-lock.ts
10212
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/store-lock.ts
10213
10213
  import { spawnSync as spawnSync3 } from "node:child_process";
10214
10214
  import fs6 from "node:fs";
10215
10215
  import { hostname } from "node:os";
@@ -10401,7 +10401,7 @@ var init_store_lock = __esm(() => {
10401
10401
  FOREIGN_LOCK_TTL_MS = 10 * 60000;
10402
10402
  });
10403
10403
 
10404
- // packages/workit-core/src/store/event-log.ts
10404
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/store/event-log.ts
10405
10405
  import { randomUUID as randomUUID2 } from "node:crypto";
10406
10406
  import fs7 from "node:fs";
10407
10407
  import path7 from "node:path";
@@ -10556,7 +10556,7 @@ var init_event_log = __esm(() => {
10556
10556
  TRANSIENT_WINDOWS_CODES = new Set(["EPERM", "EACCES", "EBUSY"]);
10557
10557
  });
10558
10558
 
10559
- // packages/workit-core/src/store/patch.ts
10559
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/store/patch.ts
10560
10560
  function diff(before, after, path = []) {
10561
10561
  if (equal(before, after))
10562
10562
  return [];
@@ -10662,7 +10662,7 @@ var isPlainObject2 = (value) => typeof value === "object" && value !== null && !
10662
10662
  return false;
10663
10663
  });
10664
10664
 
10665
- // packages/workit-core/src/store/reduce.ts
10665
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/store/reduce.ts
10666
10666
  import { createHash as createHash3 } from "node:crypto";
10667
10667
  function reduce(state, event, blobs) {
10668
10668
  const data = event.data;
@@ -10788,7 +10788,7 @@ var init_reduce = __esm(() => {
10788
10788
  ]);
10789
10789
  });
10790
10790
 
10791
- // packages/workit-core/src/store/notes.ts
10791
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/store/notes.ts
10792
10792
  var SLOT, reportMigration = (report) => {
10793
10793
  try {
10794
10794
  const reporter = globalThis[SLOT];
@@ -10800,7 +10800,7 @@ var init_notes = __esm(() => {
10800
10800
  SLOT = Symbol.for("workit.migrationReporter");
10801
10801
  });
10802
10802
 
10803
- // packages/workit-core/src/core/task-store.ts
10803
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/task-store.ts
10804
10804
  import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
10805
10805
  import * as fs8 from "node:fs";
10806
10806
  import path8 from "node:path";
@@ -12427,13 +12427,13 @@ var init_task_store = __esm(() => {
12427
12427
  heldInProcess = new Map;
12428
12428
  });
12429
12429
 
12430
- // packages/workit-core/src/core/workers.ts
12430
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/workers.ts
12431
12431
  var UNCERTAIN_WORKER_STATES, isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state), workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
12432
12432
  var init_workers = __esm(() => {
12433
12433
  UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
12434
12434
  });
12435
12435
 
12436
- // packages/workit-core/src/git/rev.ts
12436
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/git/rev.ts
12437
12437
  import { spawnSync as spawnSync4 } from "node:child_process";
12438
12438
  import { createHash as createHash5 } from "node:crypto";
12439
12439
  import fs9 from "node:fs";
@@ -12964,7 +12964,7 @@ var init_rev = __esm(() => {
12964
12964
  };
12965
12965
  });
12966
12966
 
12967
- // packages/workit-core/src/ledger.ts
12967
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/ledger.ts
12968
12968
  import { spawnSync as spawnSync5 } from "node:child_process";
12969
12969
  import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
12970
12970
  import fs10 from "node:fs";
@@ -13426,6 +13426,70 @@ function recordRuling(context, input) {
13426
13426
  refs: input.refs ?? []
13427
13427
  }, { now: context.now });
13428
13428
  }
13429
+ function activeStanding(rows, fanout, leads = null) {
13430
+ const active = [];
13431
+ for (const row of rows) {
13432
+ if (row.fanout !== fanout || !fromLead(row, leads))
13433
+ continue;
13434
+ if (row.type === "standing" && typeof row.what === "string" && row.what.trim()) {
13435
+ if (!row.superseded)
13436
+ active.push(row);
13437
+ } else if (row.type === "standing.cleared") {
13438
+ const target = str2(row.target);
13439
+ if (target === null)
13440
+ active.length = 0;
13441
+ else {
13442
+ const index = active.findIndex((order) => order.id === target);
13443
+ if (index >= 0)
13444
+ active.splice(index, 1);
13445
+ }
13446
+ }
13447
+ }
13448
+ return active;
13449
+ }
13450
+ function leadOnly(context, lead) {
13451
+ const session = context.actor.session;
13452
+ if (lead !== null ? session === lead : !isDelegateSession(session))
13453
+ return { ok: true, value: undefined };
13454
+ return err("blocked", lead !== null ? `session ${session ?? "(none)"} is not the fanout's lead (${lead}): standing orders come from the lead` : `session ${session} is a worker or verifier: standing orders come from the lead`, lead !== null ? `the lead in a new session? workit fanout plan <plan.json> --take-lead from this session; a worker or verifier reports the order to the lead instead` : "a worker or verifier reports the order to the lead instead");
13455
+ }
13456
+ function recordStanding(context, input) {
13457
+ const what = required2(input.what, "<order>");
13458
+ if (!what.ok)
13459
+ return what;
13460
+ if (/\p{Cc}/u.test(what.value))
13461
+ return err("invalid_input", "a standing order is one line: no newlines or control characters", 'add each order separately: workit ledger standing add "<order>"');
13462
+ const lead = leadOnly(context, input.lead);
13463
+ if (!lead.ok)
13464
+ return lead;
13465
+ const link = checkSupersede(context, "standing");
13466
+ if (!link.ok)
13467
+ return link;
13468
+ return appendRow(context.cwd, {
13469
+ ...common(context, noteKey(context)),
13470
+ type: "standing",
13471
+ fanout: input.fanout,
13472
+ what: what.value
13473
+ }, { now: context.now });
13474
+ }
13475
+ function clearStanding(context, input) {
13476
+ const lead = leadOnly(context, input.lead);
13477
+ if (!lead.ok)
13478
+ return lead;
13479
+ if (input.target !== null) {
13480
+ const ledger = readLedger(context.cwd);
13481
+ if (!ledger.ok)
13482
+ return ledger;
13483
+ if (!activeStanding(ledger.value.rows, input.fanout, input.leads ?? null).some((row) => row.id === input.target))
13484
+ return err("not_found", `no standing order ${input.target} in force for fanout ${input.fanout}`, `workit ledger standing list --fanout ${input.fanout}`);
13485
+ }
13486
+ return appendRow(context.cwd, {
13487
+ ...common(context, noteKey(context)),
13488
+ type: "standing.cleared",
13489
+ fanout: input.fanout,
13490
+ target: input.target
13491
+ }, { now: context.now });
13492
+ }
13429
13493
  function recordVerdict(context, input) {
13430
13494
  if (!VERDICT_RESULTS.includes(input.result))
13431
13495
  return err("invalid_input", `unknown verdict "${input.result}"; expected ${VERDICT_RESULTS.join("|")}`);
@@ -13504,6 +13568,12 @@ function summarizeRow(row) {
13504
13568
  case "handoff":
13505
13569
  summary = `next: ${text("next")}${text("note") ? ` (note: ${text("note")})` : ""}`;
13506
13570
  break;
13571
+ case "standing":
13572
+ summary = `${text("what")} (fanout ${text("fanout")})`;
13573
+ break;
13574
+ case "standing.cleared":
13575
+ summary = `cleared ${text("target") || "every standing order"} (fanout ${text("fanout")})`;
13576
+ break;
13507
13577
  case "commit.recorded":
13508
13578
  summary = `${(row.head ?? "?").slice(0, 12)} ${text("subject")}${row.actor.session ? "" : " (no session)"}`;
13509
13579
  break;
@@ -13730,7 +13800,7 @@ var LEDGER_VERSION = 1, MAX_LINE_BYTES = 4096, VERDICT_RESULTS, PASSING, FAILING
13730
13800
  }, required2 = (value, flag) => {
13731
13801
  const text = value?.trim();
13732
13802
  return text ? { ok: true, value: text } : err("invalid_input", `${flag} is required`);
13733
- }, OBSERVED_ONLY, MAX_DIRTY_PATHS = 10, dirtyState = (cwd) => {
13803
+ }, fromLead = (row, leads) => leads === null || row.actor.session !== null && leads.includes(row.actor.session), isDelegateSession = (session) => session !== null && (/-w-[a-z0-9._-]+(?:\+try\d)?$/u.test(session) || /-v\d+$/u.test(session) || session.includes(":")), OBSERVED_ONLY, MAX_DIRTY_PATHS = 10, dirtyState = (cwd) => {
13734
13804
  const run = gitRun2(cwd, ["status", "--porcelain=v1", "-z", "--untracked-files=normal"], GIT_TIMEOUTS.worktree);
13735
13805
  if (!run.ok)
13736
13806
  return { dirty: false, files: 0, paths: [] };
@@ -13844,7 +13914,7 @@ var init_ledger = __esm(() => {
13844
13914
  OBSERVED_ONLY = new Set(["check", ...PR_ROW_TYPES]);
13845
13915
  });
13846
13916
 
13847
- // packages/workit-core/src/core/policy/derive.ts
13917
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/policy/derive.ts
13848
13918
  import { existsSync as existsSync3 } from "node:fs";
13849
13919
  import path11 from "node:path";
13850
13920
  function deriveRequirements(judgment, scope, constraints, verification = "self") {
@@ -14032,7 +14102,7 @@ var init_derive = __esm(() => {
14032
14102
  };
14033
14103
  });
14034
14104
 
14035
- // packages/workit-core/src/core/task-evaluation.ts
14105
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/task-evaluation.ts
14036
14106
  import { createHash as createHash7 } from "node:crypto";
14037
14107
  import * as fs11 from "node:fs";
14038
14108
  import { spawnSync as spawnSync6 } from "node:child_process";
@@ -14680,7 +14750,7 @@ var init_task_evaluation = __esm(() => {
14680
14750
  scopeCovers2 = scopeCovers;
14681
14751
  });
14682
14752
 
14683
- // packages/workit-core/src/core/methods.ts
14753
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/methods.ts
14684
14754
  function selectMethods(policy, capabilities) {
14685
14755
  return Object.entries(METHODS).flatMap(([id, definition]) => {
14686
14756
  const requirements = policy.requirements.filter((requirement) => methodMatches(definition, requirement));
@@ -14727,7 +14797,7 @@ var init_methods = __esm(() => {
14727
14797
  };
14728
14798
  });
14729
14799
 
14730
- // packages/workit-core/src/core/task-context.ts
14800
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/task-context.ts
14731
14801
  function reconcileResume(view, observations = []) {
14732
14802
  if (observations.length > 0)
14733
14803
  return failure2("permission_denied", "worker observations require native host verification");
@@ -14840,7 +14910,7 @@ var init_task_context = __esm(() => {
14840
14910
  privateKey = /-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?-----END [^-]*PRIVATE KEY-----/gi;
14841
14911
  });
14842
14912
 
14843
- // packages/workit-core/src/core/commit-flavors.ts
14913
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/commit-flavors.ts
14844
14914
  var CONVENTIONAL_RE, TICKET_RE, GITMOJI_RE, matchCommitFlavor = (message, flavor, pattern) => {
14845
14915
  const subject = message.split(`
14846
14916
  `, 1)[0] ?? "";
@@ -14891,10 +14961,10 @@ var init_commit_flavors = __esm(() => {
14891
14961
  DETECTABLE = ["conventional", "gitmoji", "ticket-prefix"];
14892
14962
  });
14893
14963
 
14894
- // packages/workit-core/src/core/git.ts
14964
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/git.ts
14895
14965
  var init_git = () => {};
14896
14966
 
14897
- // packages/workit-core/src/core/boundary.ts
14967
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/boundary.ts
14898
14968
  var EVENT, errorDetail = (err) => {
14899
14969
  if (err instanceof Error) {
14900
14970
  return { error_name: err.name, error: err.message };
@@ -14917,7 +14987,7 @@ var init_boundary = __esm(() => {
14917
14987
  };
14918
14988
  });
14919
14989
 
14920
- // packages/workit-core/src/core/config.ts
14990
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/config.ts
14921
14991
  import {
14922
14992
  copyFileSync as copyFileSync2,
14923
14993
  cpSync as cpSync2,
@@ -15086,7 +15156,7 @@ var init_config = __esm(() => {
15086
15156
  ];
15087
15157
  });
15088
15158
 
15089
- // packages/workit-core/src/core/workspaces.ts
15159
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/workspaces.ts
15090
15160
  import { createHash as createHash8 } from "node:crypto";
15091
15161
  import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
15092
15162
  import path14 from "node:path";
@@ -15522,7 +15592,7 @@ var init_workspaces = __esm(() => {
15522
15592
  }).passthrough();
15523
15593
  });
15524
15594
 
15525
- // packages/workit-core/src/core/release-tracks.ts
15595
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/release-tracks.ts
15526
15596
  import { createHash as createHash9 } from "node:crypto";
15527
15597
  import { spawnSync as spawnSync7 } from "node:child_process";
15528
15598
  import { readFileSync as readFileSync5 } from "node:fs";
@@ -15984,7 +16054,7 @@ var init_release_tracks = __esm(() => {
15984
16054
  memo3 = new Map;
15985
16055
  });
15986
16056
 
15987
- // packages/workit-core/src/core/vcs-config.ts
16057
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/vcs-config.ts
15988
16058
  import fs12 from "node:fs";
15989
16059
  import path15 from "node:path";
15990
16060
  import { spawnSync as spawnSync8 } from "node:child_process";
@@ -16112,7 +16182,7 @@ var init_vcs_config = __esm(() => {
16112
16182
  init_release_tracks();
16113
16183
  });
16114
16184
 
16115
- // packages/workit-core/src/core/branch.ts
16185
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/branch.ts
16116
16186
  import { execFileSync } from "node:child_process";
16117
16187
  import { createHash as createHash10 } from "node:crypto";
16118
16188
  import path16 from "node:path";
@@ -16285,12 +16355,12 @@ var init_branch = __esm(() => {
16285
16355
  init_release_tracks();
16286
16356
  });
16287
16357
 
16288
- // packages/workit-core/src/core/route-intent.ts
16358
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/route-intent.ts
16289
16359
  var init_route_intent = __esm(() => {
16290
16360
  init_branch();
16291
16361
  });
16292
16362
 
16293
- // packages/workit-core/src/core/runtime-identity.ts
16363
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/runtime-identity.ts
16294
16364
  import { createHash as createHash11 } from "node:crypto";
16295
16365
  import { readFileSync as readFileSync6 } from "node:fs";
16296
16366
  var sha256Hex = (bytes) => createHash11("sha256").update(bytes).digest("hex"), bundleHashOfFile = (file) => {
@@ -16315,7 +16385,7 @@ var init_runtime_identity = __esm(() => {
16315
16385
  ];
16316
16386
  });
16317
16387
 
16318
- // packages/workit-core/src/core/policy-resolver.ts
16388
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/policy-resolver.ts
16319
16389
  function resolvePolicy(input) {
16320
16390
  const ids = input.constraints.map((constraint) => constraint.id);
16321
16391
  if (new Set(ids).size !== ids.length)
@@ -16375,7 +16445,7 @@ var init_policy_resolver = __esm(() => {
16375
16445
  init_derive();
16376
16446
  });
16377
16447
 
16378
- // packages/workit-core/src/core/safe-write.ts
16448
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/safe-write.ts
16379
16449
  import { randomUUID as randomUUID4 } from "node:crypto";
16380
16450
  import {
16381
16451
  closeSync as closeSync2,
@@ -16453,7 +16523,7 @@ function writeFileAtomic(file, content, mode) {
16453
16523
  }
16454
16524
  var init_safe_write = () => {};
16455
16525
 
16456
- // packages/workit-core/src/autonomy.ts
16526
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/autonomy.ts
16457
16527
  import { copyFileSync as copyFileSync3, existsSync as existsSync6 } from "node:fs";
16458
16528
  import os3 from "node:os";
16459
16529
  import path18 from "node:path";
@@ -16734,7 +16804,7 @@ var init_autonomy = __esm(() => {
16734
16804
  DEFAULT_ENDPOINTS = ["commit", "pr", "green", "merged"];
16735
16805
  });
16736
16806
 
16737
- // packages/workit-core/src/core/task-engine.ts
16807
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/task-engine.ts
16738
16808
  import { realpathSync as realpathSync4 } from "node:fs";
16739
16809
 
16740
16810
  class WorkitCore {
@@ -18316,13 +18386,13 @@ var init_task_engine = __esm(() => {
18316
18386
  retryPause = new Int32Array(new SharedArrayBuffer(4));
18317
18387
  });
18318
18388
 
18319
- // packages/workit-core/src/core/repo-context.ts
18389
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/repo-context.ts
18320
18390
  var init_repo_context = __esm(() => {
18321
18391
  init_vcs_config();
18322
18392
  init_core3();
18323
18393
  });
18324
18394
 
18325
- // packages/workit-core/src/forge/pr-body.ts
18395
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/pr-body.ts
18326
18396
  function parseGhIssue(value) {
18327
18397
  const m = /issues\/(\d+)/.exec(value);
18328
18398
  return m ? m[1] : String(value).trim().replace(/^#/, "");
@@ -18383,7 +18453,7 @@ var init_pr_body = __esm(() => {
18383
18453
  init_vcs_config();
18384
18454
  });
18385
18455
 
18386
- // packages/workit-core/src/core/pr-create.ts
18456
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/pr-create.ts
18387
18457
  import path19 from "node:path";
18388
18458
  import { spawnSync as spawnSync9 } from "node:child_process";
18389
18459
  import { pathToFileURL } from "node:url";
@@ -18426,13 +18496,13 @@ var init_pr_create = __esm(() => {
18426
18496
  init_pr_body();
18427
18497
  });
18428
18498
 
18429
- // packages/workit-core/src/core/tracker-issues.ts
18499
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/tracker-issues.ts
18430
18500
  var init_tracker_issues = __esm(() => {
18431
18501
  init_pr_create();
18432
18502
  init_vcs_config();
18433
18503
  });
18434
18504
 
18435
- // packages/workit-core/src/core/templates.ts
18505
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/templates.ts
18436
18506
  var repoRoot;
18437
18507
  var init_templates = __esm(() => {
18438
18508
  init_config();
@@ -18440,7 +18510,7 @@ var init_templates = __esm(() => {
18440
18510
  repoRoot = assetRoot();
18441
18511
  });
18442
18512
 
18443
- // packages/workit-core/src/core/scripts.ts
18513
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/scripts.ts
18444
18514
  var PLUGIN_ROOT, ASSET_ROOT, resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
18445
18515
  var init_scripts = __esm(() => {
18446
18516
  init_package_root();
@@ -18448,7 +18518,7 @@ var init_scripts = __esm(() => {
18448
18518
  ASSET_ROOT = assetRoot();
18449
18519
  });
18450
18520
 
18451
- // packages/workit-core/src/core/youtrack.ts
18521
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/youtrack.ts
18452
18522
  import fs13 from "node:fs";
18453
18523
  import path20 from "node:path";
18454
18524
  function readYouTrackConfig(required) {
@@ -18920,7 +18990,7 @@ var init_youtrack = __esm(() => {
18920
18990
  };
18921
18991
  });
18922
18992
 
18923
- // packages/workit-core/src/core/context-read.ts
18993
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/context-read.ts
18924
18994
  var contextReadSchema;
18925
18995
  var init_context_read = __esm(() => {
18926
18996
  init_zod();
@@ -18951,7 +19021,7 @@ var init_context_read = __esm(() => {
18951
19021
  }).strict();
18952
19022
  });
18953
19023
 
18954
- // packages/workit-core/src/core.ts
19024
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core.ts
18955
19025
  import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
18956
19026
  import path21 from "node:path";
18957
19027
  function resolveInside(root, candidate) {
@@ -62107,7 +62177,7 @@ var init_build3 = __esm(async () => {
62107
62177
  init_types();
62108
62178
  });
62109
62179
 
62110
- // packages/workit-core/src/core/gitignore.ts
62180
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/gitignore.ts
62111
62181
  var GITIGNORE_ENTRIES;
62112
62182
  var init_gitignore = __esm(() => {
62113
62183
  GITIGNORE_ENTRIES = [
@@ -62128,7 +62198,7 @@ var init_gitignore = __esm(() => {
62128
62198
  ];
62129
62199
  });
62130
62200
 
62131
- // packages/workit-core/src/core/changelog.ts
62201
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/changelog.ts
62132
62202
  import fs16 from "node:fs";
62133
62203
  import path23 from "node:path";
62134
62204
  import { createHash as createHash12 } from "node:crypto";
@@ -62457,7 +62527,7 @@ var init_changelog = __esm(() => {
62457
62527
  HEADING_RE = /^###\s+/;
62458
62528
  });
62459
62529
 
62460
- // packages/workit-core/src/core/hygiene.ts
62530
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/hygiene.ts
62461
62531
  import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "node:fs";
62462
62532
  import path24 from "node:path";
62463
62533
  var repoRoot2, templatesDir = () => path24.join(repoRoot2, "templates", "hygiene"), packageJson = (root) => {
@@ -62662,7 +62732,7 @@ var init_registration = __esm(() => {
62662
62732
  CURSOR_HOOK_RUN_COMMAND = `npx -y --prefer-online --min-release-age=0 --package=${CURSOR_RUNTIME_PACKAGE} workit-cursor-hook`;
62663
62733
  });
62664
62734
 
62665
- // packages/workit-core/src/core/support-matrix.ts
62735
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/support-matrix.ts
62666
62736
  var SUPPORT_MATRIX;
62667
62737
  var init_support_matrix = __esm(() => {
62668
62738
  SUPPORT_MATRIX = {
@@ -63106,7 +63176,7 @@ var init_host_install = __esm(() => {
63106
63176
  CLAUDE_SCOPES = new Set(["user", "project", "local"]);
63107
63177
  });
63108
63178
 
63109
- // packages/workit-core/src/core/skill-manifests.ts
63179
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/skill-manifests.ts
63110
63180
  import { cpSync as cpSync3, existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync5, statSync as statSync3, writeFileSync as writeFileSync5 } from "node:fs";
63111
63181
  import path28 from "node:path";
63112
63182
  var WORKIT_METHOD_SKILLS, skillManifestNames = (root) => existsSync12(root) ? readdirSync5(root).filter((name) => existsSync12(path28.join(root, name, "SKILL.md"))).toSorted() : [], validateSkillManifests = (root, expected, label) => {
@@ -65369,7 +65439,7 @@ var init_setup = __esm(() => {
65369
65439
  SETUP_OVERRIDES = [...YT_OVERRIDES, ...VCS_OVERRIDES];
65370
65440
  });
65371
65441
 
65372
- // packages/workit-core/src/core/branch-policy.ts
65442
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/branch-policy.ts
65373
65443
  import { spawnSync as spawnSync12 } from "node:child_process";
65374
65444
  var PREFIXES, detectBranchPolicy = (workspaceRoot) => {
65375
65445
  const branchExists = (name) => {
@@ -65432,7 +65502,7 @@ var init_branch_policy = __esm(() => {
65432
65502
  };
65433
65503
  });
65434
65504
 
65435
- // packages/workit-core/src/core/workspace-branch-policy.ts
65505
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/workspace-branch-policy.ts
65436
65506
  import { mkdirSync as mkdirSync6, readFileSync as readFileSync15, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "node:fs";
65437
65507
  import path31 from "node:path";
65438
65508
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
@@ -68910,7 +68980,7 @@ var init_steps = __esm(async () => {
68910
68980
  ];
68911
68981
  });
68912
68982
 
68913
- // packages/workit-core/src/core/logger.ts
68983
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/logger.ts
68914
68984
  import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync7, unlinkSync as unlinkSync3 } from "node:fs";
68915
68985
  import os11 from "node:os";
68916
68986
  import path34 from "node:path";
@@ -69918,7 +69988,7 @@ var init_launch = __esm(() => {
69918
69988
  init_upgrade();
69919
69989
  });
69920
69990
 
69921
- // packages/workit-core/src/knowledge.ts
69991
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/knowledge.ts
69922
69992
  import { spawnSync as spawnSync14 } from "node:child_process";
69923
69993
  import fs17 from "node:fs";
69924
69994
  import path36 from "node:path";
@@ -70315,7 +70385,7 @@ var init_doctor2 = __esm(() => {
70315
70385
  init_task();
70316
70386
  });
70317
70387
 
70318
- // packages/workit-core/src/forge/redact.ts
70388
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/redact.ts
70319
70389
  function redactText(value) {
70320
70390
  let out = stripAnsi2(value).replace(PRIVATE_KEY, "[REDACTED PRIVATE KEY]");
70321
70391
  out = out.replace(WEBHOOK, `$1/${R}`);
@@ -70418,7 +70488,7 @@ var init_redact = __esm(() => {
70418
70488
  NOT_FIRST_ERROR = /^\s*(?:\(pass\)|\(skip\)|\(todo\)|✓|✔|ok\b|PASS\b)|^\s*\d+ fail\b|##\[error\]Process completed|^\s*0 (?:errors?|fail)/u;
70419
70489
  });
70420
70490
 
70421
- // packages/workit-core/src/checks.ts
70491
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/checks.ts
70422
70492
  import { spawn, spawnSync as spawnSync15 } from "node:child_process";
70423
70493
  import { createHash as createHash14 } from "node:crypto";
70424
70494
  import fs18 from "node:fs";
@@ -71320,10 +71390,10 @@ var init_check = __esm(() => {
71320
71390
  FINGERPRINT_VARS = ["CI", "NODE_ENV", "npm_config_script_shell", "NODE_OPTIONS", "SHELL"];
71321
71391
  });
71322
71392
 
71323
- // packages/workit-core/src/forge/types.ts
71393
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/types.ts
71324
71394
  var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
71325
71395
 
71326
- // packages/workit-core/src/git/ops.ts
71396
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/git/ops.ts
71327
71397
  import { spawnSync as spawnSync16 } from "node:child_process";
71328
71398
  function dirtEntries(cwd) {
71329
71399
  const run = git5(cwd, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
@@ -71703,7 +71773,7 @@ var init_ops = __esm(() => {
71703
71773
  SHA = /^[0-9a-f]{40,64}$/u;
71704
71774
  });
71705
71775
 
71706
- // packages/workit-core/src/forge/reruns.ts
71776
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/reruns.ts
71707
71777
  import fs19 from "node:fs";
71708
71778
  import path39 from "node:path";
71709
71779
  function rerunLogPath(cwd) {
@@ -71794,7 +71864,7 @@ var init_reruns = __esm(() => {
71794
71864
  init_rev();
71795
71865
  });
71796
71866
 
71797
- // packages/workit-core/src/forge/report.ts
71867
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/report.ts
71798
71868
  function gatingChecks(status) {
71799
71869
  const required = status.checks.filter((check) => check.required === true);
71800
71870
  const protectionNamesChecks = (status.requiredContexts?.length ?? 0) > 0;
@@ -72148,7 +72218,7 @@ var init_report = __esm(() => {
72148
72218
  GITHUB_MERGEABLE = new Set(["clean", "unstable", "has_hooks"]);
72149
72219
  });
72150
72220
 
72151
- // packages/workit-core/src/forge/pr-ops.ts
72221
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/pr-ops.ts
72152
72222
  import { spawnSync as spawnSync17 } from "node:child_process";
72153
72223
  function fillFromCommits(cwd, resolved, base) {
72154
72224
  const tracking = `refs/remotes/${resolved.baseRemote}/${base}`;
@@ -72431,7 +72501,7 @@ var init_pr_ops = __esm(() => {
72431
72501
  };
72432
72502
  });
72433
72503
 
72434
- // packages/workit-core/src/forge/exec.ts
72504
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/exec.ts
72435
72505
  import { spawnSync as spawnSync18 } from "node:child_process";
72436
72506
  import fs20 from "node:fs";
72437
72507
  import path40 from "node:path";
@@ -72549,7 +72619,7 @@ var init_exec = __esm(() => {
72549
72619
  MAX_BUFFER2 = 64 * 1024 * 1024;
72550
72620
  });
72551
72621
 
72552
- // packages/workit-core/src/forge/github.ts
72622
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/github.ts
72553
72623
  function checkFromContext(node) {
72554
72624
  const required = node.isRequired ?? null;
72555
72625
  if (node.__typename === "StatusContext") {
@@ -72848,7 +72918,7 @@ var init_github = __esm(() => {
72848
72918
  ]);
72849
72919
  });
72850
72920
 
72851
- // packages/workit-core/src/forge/gitlab.ts
72921
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/gitlab.ts
72852
72922
  function checkFromBridge(bridge, pipelineId, scope) {
72853
72923
  const status = bridge.downstream_pipeline?.status ?? bridge.status;
72854
72924
  const allow = bridge.allow_failure === true;
@@ -73136,7 +73206,7 @@ var init_gitlab = __esm(() => {
73136
73206
  UNSETTLED = new Set(["checking", "unchecked", "preparing", "approvals_syncing"]);
73137
73207
  });
73138
73208
 
73139
- // packages/workit-core/src/forge/resolve.ts
73209
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/resolve.ts
73140
73210
  import fs21 from "node:fs";
73141
73211
  function resolveForge(cwd, options = {}) {
73142
73212
  let workspace;
@@ -73256,7 +73326,7 @@ var init_resolve = __esm(() => {
73256
73326
  init_gitlab();
73257
73327
  });
73258
73328
 
73259
- // packages/workit-core/src/duration.ts
73329
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/duration.ts
73260
73330
  function parseDuration2(value) {
73261
73331
  const text = value.trim().toLowerCase();
73262
73332
  if (/^\d+(?:\.\d+)?$/u.test(text))
@@ -73315,6 +73385,14 @@ function parseFlags(argv, spec) {
73315
73385
  }
73316
73386
  return parsed;
73317
73387
  }
73388
+ function unverifiedFlag(flags) {
73389
+ const reason = flags.values.reason?.trim();
73390
+ if (!flags.booleans.has("unverified"))
73391
+ return reason === undefined ? null : "--reason is only used with --unverified";
73392
+ if (!reason)
73393
+ return "--unverified needs --reason <why the user asked for it>";
73394
+ return reason.length > MAX_REASON ? `--reason is capped at ${MAX_REASON} characters` : { reason };
73395
+ }
73318
73396
  function positiveInt(value, flag) {
73319
73397
  if (value === undefined)
73320
73398
  return null;
@@ -73398,7 +73476,7 @@ function renderStatus(doc) {
73398
73476
  lines.push(`next: ${doc.next}`);
73399
73477
  return lines;
73400
73478
  }
73401
- var forgeDeps, usage3 = (io, message, line) => emit(io, fail("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail(result.code, result.error, { data, unblock: result.unblock })), short = (sha) => sha ? sha.slice(0, 7) : "?";
73479
+ var forgeDeps, MAX_REASON = 500, usage3 = (io, message, line) => emit(io, fail("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail(result.code, result.error, { data, unblock: result.unblock })), short = (sha) => sha ? sha.slice(0, 7) : "?";
73402
73480
  var init_forge_common = __esm(() => {
73403
73481
  init_resolve();
73404
73482
  init_vcs_config();
@@ -73544,13 +73622,23 @@ async function create4(argv, io) {
73544
73622
  ...data.releaseTrack?.name ? [`release track: ${data.releaseTrack.name} (${data.releaseTrack.detail})`] : [],
73545
73623
  ...(data.releaseTrack?.warnings ?? []).map((warning) => `note: ${warning}`),
73546
73624
  `head ${data.head.slice(0, 12)} verified on the forge`,
73547
- ..."error" in data.recorded ? [`ledger: not recorded (${data.recorded.error})`] : []
73625
+ ..."error" in data.recorded ? [`ledger: not recorded (${data.recorded.error})`] : [],
73626
+ `next: ${data.next}`
73548
73627
  ]);
73549
73628
  }
73550
73629
  async function merge3(argv, io) {
73551
- const flags = parseFlags(argv, { pr: "value", method: "value", "delete-branch": "boolean" });
73630
+ const flags = parseFlags(argv, {
73631
+ pr: "value",
73632
+ method: "value",
73633
+ "delete-branch": "boolean",
73634
+ unverified: "boolean",
73635
+ reason: "value"
73636
+ });
73552
73637
  if (typeof flags === "string")
73553
73638
  return usage3(io, flags, MERGE_USAGE);
73639
+ const unverified = unverifiedFlag(flags);
73640
+ if (typeof unverified === "string")
73641
+ return usage3(io, unverified, MERGE_USAGE);
73554
73642
  if (flags.positionals.length)
73555
73643
  return usage3(io, `unexpected argument ${flags.positionals[0]}`, MERGE_USAGE);
73556
73644
  const pr = positiveInt(flags.values.pr, "--pr");
@@ -73562,7 +73650,13 @@ async function merge3(argv, io) {
73562
73650
  const connected = connect(io);
73563
73651
  if (!connected.ok)
73564
73652
  return forgeFail(io, connected);
73565
- const result = await mergePullRequest(io.cwd, connected.data, { pr, method, deleteBranch: flags.booleans.has("delete-branch"), actor: actorFromEnv(io.env) }, forgeDeps.sleep);
73653
+ const result = await mergePullRequest(io.cwd, connected.data, {
73654
+ pr,
73655
+ method,
73656
+ deleteBranch: flags.booleans.has("delete-branch"),
73657
+ actor: actorFromEnv(io.env),
73658
+ ...unverified ? { unverified } : {}
73659
+ }, forgeDeps.sleep);
73566
73660
  if (!result.ok)
73567
73661
  return emit(io, fail(result.code, result.error, {
73568
73662
  unblock: result.unblock,
@@ -73570,7 +73664,7 @@ async function merge3(argv, io) {
73570
73664
  }));
73571
73665
  return emit(io, ok(result.data), (data) => [
73572
73666
  `merged ${connected.data.forge.kind === "github" ? "PR #" : "MR !"}${data.number} (${data.method}) at head ${data.head.slice(0, 12)}${data.mergeSha ? ` -> ${data.mergeSha.slice(0, 12)}` : ""}`,
73573
- data.verdict.required ? `verdict ${data.verdict.verdictId ?? "?"} accepted for that head` : "no verdict required (workspace grants merge: true)",
73667
+ data.unverified ? `UNVERIFIED: merged without a verdict (--unverified: ${data.unverified.reason}); ledger ${data.unverified.recorded}` : `verdict ${data.verdict.verdictId ?? "?"} accepted for that head`,
73574
73668
  ...data.mergeBack.length ? [
73575
73669
  `merge back: ${data.base} is the ${data.mergeBackTrack} production branch; bring it into ${data.mergeBack.join(", ")}`
73576
73670
  ] : [],
@@ -73588,7 +73682,7 @@ async function run10(argv, io) {
73588
73682
  return merge3(rest, io);
73589
73683
  return usage3(io, sub && !sub.startsWith("-") ? `unknown pr subcommand "${sub}"` : "missing pr subcommand", USAGE4);
73590
73684
  }
73591
- var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f>] | --fill) [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch] [--json]", USAGE4 = "workit pr status|create|merge ... (workit help pr)", METHODS2;
73685
+ var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f>] | --fill) [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch] [--unverified --reason <why>] [--json]", USAGE4 = "workit pr status|create|merge ... (workit help pr)", METHODS2;
73592
73686
  var init_pr = __esm(() => {
73593
73687
  init_pr_body();
73594
73688
  init_pr_ops();
@@ -73980,7 +74074,7 @@ var init_git2 = __esm(() => {
73980
74074
  init_forge_common();
73981
74075
  });
73982
74076
 
73983
- // packages/workit-core/src/forge/verify.ts
74077
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/verify.ts
73984
74078
  import { spawnSync as spawnSync19 } from "node:child_process";
73985
74079
  function finish(cwd, report, actor) {
73986
74080
  const delivered = report.observations.length > 0 && report.observations.every((entry) => entry.ok);
@@ -74300,7 +74394,7 @@ var init_verify_delivery = __esm(() => {
74300
74394
  };
74301
74395
  });
74302
74396
 
74303
- // packages/workit-core/src/stack.ts
74397
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/stack.ts
74304
74398
  import { spawnSync as spawnSync20 } from "node:child_process";
74305
74399
  import { createHash as createHash15, randomBytes as randomBytes3 } from "node:crypto";
74306
74400
  import fs22 from "node:fs";
@@ -75631,10 +75725,15 @@ async function land(argv, io) {
75631
75725
  max: "value",
75632
75726
  method: "value",
75633
75727
  timeout: "value",
75634
- interval: "value"
75728
+ interval: "value",
75729
+ unverified: "boolean",
75730
+ reason: "value"
75635
75731
  });
75636
75732
  if (typeof flags === "string")
75637
75733
  return usage3(io, flags, LAND_USAGE);
75734
+ const unverified = unverifiedFlag(flags);
75735
+ if (typeof unverified === "string")
75736
+ return usage3(io, unverified, LAND_USAGE);
75638
75737
  if (flags.positionals.length)
75639
75738
  return usage3(io, `unexpected argument ${flags.positionals[0]}`, LAND_USAGE);
75640
75739
  const max = positiveInt(flags.values.max, "--max");
@@ -75665,7 +75764,7 @@ async function land(argv, io) {
75665
75764
  resolved: connected.data,
75666
75765
  actor: actorFromEnv(io.env),
75667
75766
  sleep: forgeDeps.sleep
75668
- }, fresh.data ?? selected.data, { dryRun: flags.booleans.has("dry-run"), max, method, timeoutMs, intervalMs });
75767
+ }, fresh.data ?? selected.data, { dryRun: flags.booleans.has("dry-run"), max, method, timeoutMs, intervalMs, unverified });
75669
75768
  });
75670
75769
  if (!result.ok)
75671
75770
  return stackFailed(io, result);
@@ -75674,7 +75773,7 @@ async function land(argv, io) {
75674
75773
  ...data.dryRun ? [
75675
75774
  data.wouldLand.length ? `would land: ${data.wouldLand.map((item) => `${label(kind, item.pr)} (${item.branch})`).join(", ")}` : "would land nothing"
75676
75775
  ] : [
75677
- data.landed.length ? `landed: ${data.landed.map((item) => `${label(kind, item.pr)} (${item.branch}) -> ${short4(item.mergeSha)}`).join(", ")}` : "landed nothing"
75776
+ data.landed.length ? `landed: ${data.landed.map((item) => `${label(kind, item.pr)} (${item.branch}) -> ${short4(item.mergeSha)}${item.unverified ? ` UNVERIFIED (ledger ${item.unverified})` : ""}`).join(", ")}` : "landed nothing"
75678
75777
  ],
75679
75778
  ...data.retargeted.length ? [`retargeted: ${data.retargeted.map((pr) => label(kind, pr)).join(", ")}`] : [],
75680
75779
  ...data.restacked.length ? [`restacked: ${data.restacked.join(", ")}`] : [],
@@ -75694,7 +75793,7 @@ async function run14(argv, io) {
75694
75793
  return land(rest, io);
75695
75794
  return usage3(io, sub && !sub.startsWith("-") ? `unknown stack subcommand "${sub}"` : "missing stack subcommand", USAGE7);
75696
75795
  }
75697
- var PLAN_USAGE = "workit stack plan [--name <n>] [--trunk <b> | --track <t>] [<bottom> … <top>] [--json]", STATUS_USAGE2 = "workit stack status [--name <n>] [--json]", SYNC_USAGE = "workit stack sync [--name <n>] [--local] [--dry-run] [--force <branch>]… [--json]", LAND_USAGE = "workit stack land [--name <n>] [--dry-run] [--max <n>] [--method squash|merge|rebase] [--timeout 20m] [--interval 30s] [--json]", USAGE7 = "workit stack plan|status|sync|land ... (workit help stack)", short4 = (sha) => sha ? sha.slice(0, 12) : "?", stackFailed = (io, result) => emit(io, fail(result.code, result.error, { unblock: result.unblock, data: result.data ?? {} })), label = (forge, pr) => pr === null ? "no PR" : `${forge === "gitlab" ? "MR !" : "PR #"}${pr}`, renderSync = (data) => [
75796
+ var PLAN_USAGE = "workit stack plan [--name <n>] [--trunk <b> | --track <t>] [<bottom> … <top>] [--json]", STATUS_USAGE2 = "workit stack status [--name <n>] [--json]", SYNC_USAGE = "workit stack sync [--name <n>] [--local] [--dry-run] [--force <branch>]… [--json]", LAND_USAGE = "workit stack land [--name <n>] [--dry-run] [--max <n>] [--method squash|merge|rebase] [--unverified --reason <why>] [--timeout 20m] [--interval 30s] [--json]", USAGE7 = "workit stack plan|status|sync|land ... (workit help stack)", short4 = (sha) => sha ? sha.slice(0, 12) : "?", stackFailed = (io, result) => emit(io, fail(result.code, result.error, { unblock: result.unblock, data: result.data ?? {} })), label = (forge, pr) => pr === null ? "no PR" : `${forge === "gitlab" ? "MR !" : "PR #"}${pr}`, renderSync = (data) => [
75698
75797
  `${data.dryRun ? "would sync" : "synced"} stack ${data.name}${data.merged.length ? `; merged: ${data.merged.map((item) => item.branch).join(", ")}` : ""}`,
75699
75798
  ...data.steps.map((step) => [
75700
75799
  ` ${step.branch} on ${step.parent}:`,
@@ -75711,8 +75810,9 @@ var init_stack2 = __esm(() => {
75711
75810
  METHODS3 = new Set(["squash", "merge", "rebase"]);
75712
75811
  });
75713
75812
 
75714
- // packages/workit-core/src/fanout.ts
75813
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/fanout.ts
75715
75814
  import { spawnSync as spawnSync21 } from "node:child_process";
75815
+ import { createHash as createHash16 } from "node:crypto";
75716
75816
  import fs23 from "node:fs";
75717
75817
  import path43 from "node:path";
75718
75818
  function expandBraces(pattern, limit = 64) {
@@ -75874,6 +75974,9 @@ function parsePlan(raw, defaults, name) {
75874
75974
  else
75875
75975
  shared.push(normalized.pattern);
75876
75976
  }
75977
+ const fanIn = raw.fanIn ?? "prs";
75978
+ if (!FAN_IN_MODES.includes(fanIn))
75979
+ problems.push(`fanIn must be one of ${FAN_IN_MODES.join(", ")}`);
75877
75980
  if (!Array.isArray(raw.slices) || raw.slices.length === 0)
75878
75981
  return { ok: false, problems: [...problems, "slices: list at least one slice"] };
75879
75982
  const slices = [];
@@ -75955,7 +76058,9 @@ function parsePlan(raw, defaults, name) {
75955
76058
  verify,
75956
76059
  forbidden,
75957
76060
  context: optional("context"),
75958
- timebox: optional("timebox")
76061
+ timebox: optional("timebox"),
76062
+ hash: "",
76063
+ hashSince: ""
75959
76064
  });
75960
76065
  });
75961
76066
  const byId = new Map(slices.map((slice) => [slice.id, slice]));
@@ -75988,8 +76093,11 @@ function parsePlan(raw, defaults, name) {
75988
76093
  v: FANOUT_VERSION,
75989
76094
  name: planName,
75990
76095
  trunk,
76096
+ fanIn,
75991
76097
  shared,
75992
76098
  slices,
76099
+ leadSession: null,
76100
+ formerLeads: [],
75993
76101
  createdAt: "",
75994
76102
  updatedAt: ""
75995
76103
  }
@@ -76175,6 +76283,18 @@ function worktreeRoot(cwd) {
76175
76283
  const main = mainCheckout(cwd);
76176
76284
  return path43.join(path43.dirname(main), `${path43.basename(main)}-wt`);
76177
76285
  }
76286
+ function sliceHash(slice) {
76287
+ const {
76288
+ worktree: _worktree,
76289
+ hash: _hash,
76290
+ hashSince: _since,
76291
+ tier: _tier,
76292
+ timebox: _timebox,
76293
+ context: _context,
76294
+ ...definition
76295
+ } = slice;
76296
+ return createHash16("sha256").update(JSON.stringify(definition)).digest("hex").slice(0, 16);
76297
+ }
76178
76298
  function fanoutsDir(cwd) {
76179
76299
  const root = storeRoot(cwd);
76180
76300
  if (!root.ok)
@@ -76215,20 +76335,29 @@ function parseStored(raw) {
76215
76335
  verify: list(item.verify),
76216
76336
  forbidden: list(item.forbidden),
76217
76337
  context: typeof item.context === "string" ? item.context : null,
76218
- timebox: typeof item.timebox === "string" ? item.timebox : null
76338
+ timebox: typeof item.timebox === "string" ? item.timebox : null,
76339
+ hash: text(item.hash),
76340
+ hashSince: text(item.hashSince) || text(raw.createdAt)
76219
76341
  });
76220
76342
  }
76343
+ for (const slice of slices)
76344
+ slice.hash ||= sliceHash(slice);
76221
76345
  const shared = globs(raw.shared);
76222
76346
  const ids = new Set(slices.map((slice) => slice.id));
76223
76347
  if (!globsValid || ids.size !== slices.length || slices.some((slice) => slice.dependsOn.some((dep) => dep === slice.id || !ids.has(dep))) || findCycle(slices))
76224
76348
  return null;
76349
+ const fanIn = raw.fanIn === "integration" ? "integration" : "prs";
76350
+ const createdAt = text(raw.createdAt);
76225
76351
  return {
76226
76352
  v: typeof raw.v === "number" ? raw.v : FANOUT_VERSION,
76227
76353
  name: raw.name,
76228
76354
  trunk: raw.trunk,
76355
+ fanIn,
76229
76356
  shared,
76230
76357
  slices,
76231
- createdAt: text(raw.createdAt),
76358
+ leadSession: typeof raw.leadSession === "string" && raw.leadSession ? raw.leadSession : null,
76359
+ formerLeads: list(raw.formerLeads),
76360
+ createdAt,
76232
76361
  updatedAt: text(raw.updatedAt)
76233
76362
  };
76234
76363
  }
@@ -76308,6 +76437,11 @@ function planFanout(cwd, input, now = new Date) {
76308
76437
  const dir = fanoutsDir(cwd);
76309
76438
  if (!dir.ok)
76310
76439
  return dir;
76440
+ const session = input.actor.session;
76441
+ if (input.takeLead && !session)
76442
+ return fanoutFail("blocked", "--take-lead needs a session: WORKIT_SESSION_ID is not set", "export WORKIT_SESSION_ID=<your session id>, then plan again with --take-lead");
76443
+ if (input.takeLead && isDelegateSession(session))
76444
+ return fanoutFail("blocked", `session ${session} is a worker or verifier id; only a lead takes the lead`, "run --take-lead from the lead's own session");
76311
76445
  const parsed = parsePlan(input.trunk && isRecord8(input.raw) ? { ...input.raw, trunk: input.trunk } : input.raw, {
76312
76446
  trunk: input.trunk ?? defaultTrunk(cwd),
76313
76447
  root: mainCheckout(cwd),
@@ -76316,6 +76450,8 @@ function planFanout(cwd, input, now = new Date) {
76316
76450
  if (!parsed.ok)
76317
76451
  return fanoutFail("invalid_input", `the plan is incomplete: ${parsed.problems[0]}${parsed.problems.length > 1 ? ` (+${parsed.problems.length - 1} more)` : ""}`, "fill every listed field, then run workit fanout plan again", { problems: parsed.problems });
76318
76452
  const plan = parsed.plan;
76453
+ if (plan.fanIn === "integration" && plan.trunk === defaultTrunk(cwd))
76454
+ return fanoutFail("invalid_input", `fanIn "integration" needs an integration branch as the trunk, not ${plan.trunk}`, `workit git branch <integration-branch> --base ${plan.trunk}, then plan with --trunk <integration-branch>`);
76319
76455
  const badBranches = plan.slices.filter((slice) => !gitRun4(cwd, ["check-ref-format", "--branch", slice.branch]).ok).map((slice) => `slice ${slice.id}: ${slice.branch} is not a valid branch name`);
76320
76456
  if (badBranches.length)
76321
76457
  return fanoutFail("invalid_input", badBranches[0], "rename the branch in the plan", {
@@ -76344,20 +76480,50 @@ function planFanout(cwd, input, now = new Date) {
76344
76480
  const existing = readFanout(cwd, plan.name);
76345
76481
  const previous = existing.ok ? existing.data : null;
76346
76482
  plan.createdAt = previous?.createdAt || now.toISOString();
76483
+ plan.leadSession = input.takeLead ? session : previous ? previous.leadSession : session;
76484
+ plan.formerLeads = [
76485
+ ...new Set([
76486
+ ...previous?.formerLeads ?? [],
76487
+ ...previous?.leadSession && previous.leadSession !== plan.leadSession ? [previous.leadSession] : []
76488
+ ])
76489
+ ].filter((former) => former !== plan.leadSession);
76347
76490
  plan.updatedAt = now.toISOString();
76491
+ for (const slice of plan.slices) {
76492
+ slice.hash = sliceHash(slice);
76493
+ const before = previous?.slices.find((old) => old.id === slice.id);
76494
+ slice.hashSince = before && before.hash === slice.hash ? before.hashSince || plan.updatedAt : plan.updatedAt;
76495
+ }
76348
76496
  const written = writeFanout(cwd, plan);
76349
76497
  if (!written.ok)
76350
76498
  return written;
76499
+ if (previous && plan.leadSession !== previous.leadSession)
76500
+ appendObserved(cwd, {
76501
+ type: "fanout.lead.changed",
76502
+ actor: input.actor,
76503
+ fanout: plan.name,
76504
+ from: previous.leadSession,
76505
+ to: plan.leadSession
76506
+ });
76351
76507
  appendObserved(cwd, {
76352
76508
  type: "fanout.planned",
76353
76509
  actor: input.actor,
76354
76510
  fanout: plan.name,
76355
76511
  trunk: plan.trunk,
76512
+ fanIn: plan.fanIn,
76356
76513
  slices: plan.slices.length,
76357
76514
  ids: plan.slices.slice(0, 20).map((slice) => slice.id)
76358
76515
  });
76359
76516
  const grouped = waves(plan.slices);
76360
76517
  const notes = [];
76518
+ const warnings = [];
76519
+ if (!input.takeLead && previous?.leadSession && session && session !== previous.leadSession)
76520
+ notes.push(`the lead stays ${previous.leadSession}; this session is ${session}. Leading from this session now? workit fanout plan <plan.json> --take-lead`);
76521
+ if (previous === null) {
76522
+ const ledger = readLedger(cwd);
76523
+ const inForce = ledger.ok ? activeStanding(ledger.value.rows, plan.name, planLeads(plan)) : [];
76524
+ if (inForce.length)
76525
+ warnings.push(`${inForce.length} standing order${inForce.length === 1 ? " is" : "s are"} already in force for fanout ${plan.name}, and every brief will carry ${inForce.length === 1 ? "it" : "them"}: ${inForce.map((row) => `"${String(row.what)}"`).join("; ")}. From an earlier run? workit ledger standing clear --fanout ${plan.name}`);
76526
+ }
76361
76527
  if (!ref)
76362
76528
  notes.push(`trunk ${plan.trunk} does not resolve here; only sample paths of each glob were compared`);
76363
76529
  grouped.forEach((wave, index) => {
@@ -76372,6 +76538,7 @@ function planFanout(cwd, input, now = new Date) {
76372
76538
  data: {
76373
76539
  name: plan.name,
76374
76540
  trunk: plan.trunk,
76541
+ fanIn: plan.fanIn,
76375
76542
  file: written.data,
76376
76543
  created: previous === null,
76377
76544
  slices: plan.slices.map(({ id, branch, base, worktree, tier, dependsOn, scope, owns }) => ({
@@ -76387,11 +76554,12 @@ function planFanout(cwd, input, now = new Date) {
76387
76554
  overlaps,
76388
76555
  waves: grouped,
76389
76556
  landingOrder: landingOrder(plan.slices),
76390
- notes
76557
+ notes,
76558
+ warnings
76391
76559
  }
76392
76560
  };
76393
76561
  }
76394
- var FANOUT_VERSION = 1, TIERS, fanoutFail = (code, error, unblock, data) => ({
76562
+ var FANOUT_VERSION = 1, TIERS, FAN_IN_MODES, fanoutFail = (code, error, unblock, data) => ({
76395
76563
  ok: false,
76396
76564
  code,
76397
76565
  error,
@@ -76431,12 +76599,13 @@ var FANOUT_VERSION = 1, TIERS, fanoutFail = (code, error, unblock, data) => ({
76431
76599
  stdout: run.stdout ?? "",
76432
76600
  stderr: run.error ? String(run.error.message) : run.stderr ?? ""
76433
76601
  };
76434
- }, nulList2 = (text) => text.split("\x00").filter(Boolean), IN_FLIGHT_CAP = 6;
76602
+ }, nulList2 = (text) => text.split("\x00").filter(Boolean), planLeads = (plan) => plan.leadSession ? [plan.leadSession, ...plan.formerLeads] : null, IN_FLIGHT_CAP = 6;
76435
76603
  var init_fanout = __esm(() => {
76436
76604
  init_rev();
76437
76605
  init_ledger();
76438
76606
  init_stack();
76439
76607
  TIERS = ["mundane", "standard", "hard"];
76608
+ FAN_IN_MODES = ["prs", "integration"];
76440
76609
  DEFAULT_SHARED = [
76441
76610
  "**/package.json",
76442
76611
  "**/{package-lock.json,npm-shrinkwrap.json,bun.lock,bun.lockb,pnpm-lock.yaml,pnpm-workspace.yaml,yarn.lock}",
@@ -76452,7 +76621,489 @@ var init_fanout = __esm(() => {
76452
76621
  ID = /^[a-z0-9][a-z0-9._-]{0,63}$/u;
76453
76622
  });
76454
76623
 
76455
- // packages/workit-core/src/fanout-landed.ts
76624
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/fanout-worktree.ts
76625
+ import { randomBytes as randomBytes4 } from "node:crypto";
76626
+ import fs24 from "node:fs";
76627
+ import path44 from "node:path";
76628
+ function rowSample(lines) {
76629
+ const out = [];
76630
+ let bytes = 0;
76631
+ for (const line of lines) {
76632
+ const entry = line.slice(0, 200);
76633
+ bytes += Buffer.byteLength(entry) + 3;
76634
+ if (bytes > DIRTY_ROW_BYTES)
76635
+ break;
76636
+ out.push(entry);
76637
+ }
76638
+ return out;
76639
+ }
76640
+ function listWorktrees(cwd) {
76641
+ const out = [];
76642
+ let current = null;
76643
+ for (const field of gitRun4(cwd, ["worktree", "list", "--porcelain", "-z"]).stdout.split("\x00")) {
76644
+ if (field.startsWith("worktree ")) {
76645
+ current = { path: field.slice(9), head: null, branch: null, locked: false };
76646
+ out.push(current);
76647
+ } else if (!current)
76648
+ continue;
76649
+ else if (field.startsWith("HEAD "))
76650
+ current.head = field.slice(5);
76651
+ else if (field.startsWith("branch "))
76652
+ current.branch = field.slice(7);
76653
+ else if (field === "locked" || field.startsWith("locked "))
76654
+ current.locked = true;
76655
+ }
76656
+ return out;
76657
+ }
76658
+ function createdRow(cwd, plan, slice, target) {
76659
+ const ledger = readLedger(cwd);
76660
+ if (!ledger.ok)
76661
+ return null;
76662
+ const atPath = (row) => row.fanout === plan.name && row.slice === slice.id && typeof row.path === "string" && samePath(row.path, target);
76663
+ const rows = ledger.value.rows;
76664
+ const row = rows.findLast((candidate) => candidate.type === "fanout.worktree.created" && atPath(candidate));
76665
+ if (!row)
76666
+ return null;
76667
+ const removedSince = rows.some((candidate) => candidate.seq > row.seq && candidate.type === "fanout.worktree.released" && candidate.outcome === "removed" && atPath(candidate));
76668
+ if (removedSince)
76669
+ return null;
76670
+ const admin = adminDir(target);
76671
+ if (!admin)
76672
+ return null;
76673
+ if (typeof row.adminToken === "string")
76674
+ return row.adminToken === admin.token && row.adminId === admin.id && row.adminIno === admin.ino ? { row, exact: true } : null;
76675
+ const made = admin.birthMs ?? admin.mtimeMs;
76676
+ if (made + CLOCK_SLACK_MS < Date.parse(row.at))
76677
+ return null;
76678
+ return { row, exact: false };
76679
+ }
76680
+ function adminDir(worktree) {
76681
+ const dir = gitRun4(worktree, ["rev-parse", "--absolute-git-dir"]);
76682
+ if (!dir.ok)
76683
+ return null;
76684
+ const file = dir.stdout.trim();
76685
+ let token = null;
76686
+ try {
76687
+ token = fs24.readFileSync(path44.join(file, TOKEN_FILE), "utf8").trim() || null;
76688
+ } catch {
76689
+ token = null;
76690
+ }
76691
+ try {
76692
+ const stat = worktreeDeps.stat(file);
76693
+ return {
76694
+ path: file,
76695
+ id: path44.basename(file),
76696
+ ino: stat.ino.toString(),
76697
+ token,
76698
+ birthMs: stat.birthtimeNs > 0n ? Number(stat.birthtimeMs) : null,
76699
+ mtimeMs: Number(stat.mtimeMs)
76700
+ };
76701
+ } catch {
76702
+ return null;
76703
+ }
76704
+ }
76705
+ function markAdminDir(worktree) {
76706
+ const admin = adminDir(worktree);
76707
+ if (!admin)
76708
+ return null;
76709
+ const token = randomBytes4(16).toString("hex");
76710
+ try {
76711
+ fs24.writeFileSync(path44.join(admin.path, TOKEN_FILE), `${token}
76712
+ `);
76713
+ } catch {
76714
+ return null;
76715
+ }
76716
+ return { ...admin, token };
76717
+ }
76718
+ function findSlice(plan, id) {
76719
+ const slice = plan.slices.find((candidate) => candidate.id === id);
76720
+ return slice ? { ok: true, data: slice } : fanoutFail("invalid_input", `${id}: not a slice of fanout ${plan.name}`, `slices: ${plan.slices.map((candidate) => candidate.id).join(", ")}`);
76721
+ }
76722
+ function excludeScratch(cwd) {
76723
+ const common = gitCommonDir(cwd);
76724
+ if (!common)
76725
+ return;
76726
+ const file = path44.join(common, "info", "exclude");
76727
+ let text = "";
76728
+ try {
76729
+ text = fs24.readFileSync(file, "utf8");
76730
+ } catch {
76731
+ text = "";
76732
+ }
76733
+ if (text.split(/\r?\n/u).includes(EXCLUDE_LINE))
76734
+ return;
76735
+ fs24.mkdirSync(path44.dirname(file), { recursive: true });
76736
+ fs24.appendFileSync(file, `${text && !text.endsWith(`
76737
+ `) ? `
76738
+ ` : ""}# workit fanout: per-worker scratch dirs
76739
+ ${EXCLUDE_LINE}
76740
+ `);
76741
+ }
76742
+ function ensureScratch(worktree) {
76743
+ const scratch = path44.join(worktree, SCRATCH_DIR);
76744
+ fs24.mkdirSync(scratch, { recursive: true });
76745
+ return scratch;
76746
+ }
76747
+ function createSliceWorktree(cwd, plan, input) {
76748
+ const found = findSlice(plan, input.slice);
76749
+ if (!found.ok)
76750
+ return found;
76751
+ const slice = found.data;
76752
+ if (!slice.worktree)
76753
+ return fanoutFail("invalid_input", `slice ${slice.id} has no worktree path`, "re-plan it with workit fanout plan");
76754
+ const target = slicePath(cwd, slice);
76755
+ const worktrees = listWorktrees(cwd);
76756
+ const localRef = `refs/heads/${slice.branch}`;
76757
+ const existing = worktrees.find((entry) => samePath(entry.path, target));
76758
+ const base = {
76759
+ fanout: plan.name,
76760
+ slice: slice.id,
76761
+ branch: slice.branch,
76762
+ base: slice.base,
76763
+ path: target
76764
+ };
76765
+ if (existing) {
76766
+ if (existing.branch !== null && existing.branch !== localRef)
76767
+ return fanoutFail("blocked", `${target} is a worktree on ${existing.branch.replace(/^refs\/heads\//u, "")}, not ${slice.branch}`, `release it first, or set slice ${slice.id}'s worktree to another path`);
76768
+ if (!createdRow(cwd, plan, slice, target))
76769
+ return fanoutFail("blocked", `${target} is a worktree that workit fanout worktree create did not make for slice ${slice.id}`, `remove it yourself (git worktree remove ${target}) once you know what it holds`);
76770
+ excludeScratch(cwd);
76771
+ return {
76772
+ ok: true,
76773
+ data: {
76774
+ ...base,
76775
+ scratch: ensureScratch(target),
76776
+ head: existing.head ?? "",
76777
+ mode: resolveRef(cwd, localRef) ? "resume" : "new",
76778
+ created: false,
76779
+ notes: ["the worktree already exists; nothing was changed"]
76780
+ }
76781
+ };
76782
+ }
76783
+ const elsewhere = worktrees.find((entry) => entry.branch === localRef);
76784
+ if (elsewhere)
76785
+ return fanoutFail("blocked", `${slice.branch} is checked out at ${elsewhere.path}: never two live workers on one branch`, `stop that worker, then workit fanout worktree release ${slice.id} (or git worktree remove ${elsewhere.path})`);
76786
+ let dirExisted = false;
76787
+ try {
76788
+ const stat = fs24.statSync(target);
76789
+ if (!stat.isDirectory() || fs24.readdirSync(target).length > 0)
76790
+ return fanoutFail("blocked", `${target} exists and is ${stat.isDirectory() ? "not empty" : "not a directory"} (not a worktree of this repository)`, `move it away, or set slice ${slice.id}'s worktree to another path`);
76791
+ dirExisted = true;
76792
+ } catch (error) {
76793
+ if (error.code !== "ENOENT")
76794
+ return fanoutFail("failed", `cannot read ${target}: ${error.message}`);
76795
+ }
76796
+ const notes = [];
76797
+ let mode = "resume";
76798
+ if (resolveRef(cwd, localRef)) {
76799
+ const added = gitRun4(cwd, ["worktree", "add", target, slice.branch], SLOW);
76800
+ if (!added.ok)
76801
+ return worktreeFailed(added.stderr);
76802
+ } else {
76803
+ const remote = pushRemoteName(cwd) ?? "origin";
76804
+ const remoteRef = `refs/remotes/${remote}/${slice.branch}`;
76805
+ if (resolveRef(cwd, remoteRef)) {
76806
+ const added = gitRun4(cwd, ["worktree", "add", "-b", slice.branch, target, remoteRef], SLOW);
76807
+ if (!added.ok)
76808
+ return worktreeFailed(added.stderr);
76809
+ notes.push(`resumed ${slice.branch} from ${remote}/${slice.branch}`);
76810
+ } else {
76811
+ mode = "new";
76812
+ const parent = plan.slices.find((other) => other.branch === slice.base);
76813
+ const from = slice.base === plan.trunk ? trunkRef(cwd, plan.trunk) : parent ? branchRef(cwd, parent.branch) : branchRef(cwd, slice.base);
76814
+ if (!from)
76815
+ return fanoutFail("not_found", `base ${slice.base} of slice ${slice.id} does not resolve here`, parent ? `start slice ${parent.id} first (its branch ${parent.branch} is the base)` : `git fetch origin ${slice.base}`);
76816
+ const added = gitRun4(cwd, ["worktree", "add", "--detach", target, from], SLOW);
76817
+ if (!added.ok)
76818
+ return worktreeFailed(added.stderr);
76819
+ const branched = gitBranch(target, { name: slice.branch, base: slice.base });
76820
+ if (!branched.ok) {
76821
+ gitRun4(cwd, ["worktree", "remove", target], SLOW);
76822
+ return fanoutFail(branched.code, `workit git branch ${slice.branch} refused: ${branched.error}`, branched.unblock ?? `fix the branch name in the plan, then workit fanout worktree create ${slice.id}`);
76823
+ }
76824
+ notes.push(...branched.data.notes);
76825
+ }
76826
+ }
76827
+ excludeScratch(cwd);
76828
+ const scratch = ensureScratch(target);
76829
+ const head = gitRun4(target, ["rev-parse", "HEAD"]).stdout.trim();
76830
+ const baseSha = mode === "new" ? head : null;
76831
+ const admin = markAdminDir(target);
76832
+ appendObserved(cwd, {
76833
+ type: "fanout.worktree.created",
76834
+ actor: input.actor,
76835
+ fanout: plan.name,
76836
+ slice: slice.id,
76837
+ branch: slice.branch,
76838
+ path: target,
76839
+ scratch,
76840
+ mode,
76841
+ head,
76842
+ dirExisted,
76843
+ planCreatedAt: plan.createdAt,
76844
+ sliceHash: slice.hash,
76845
+ ...admin ? { adminId: admin.id, adminIno: admin.ino, adminToken: admin.token } : {},
76846
+ ...baseSha ? { baseSha } : {}
76847
+ });
76848
+ return { ok: true, data: { ...base, scratch, head, mode, created: true, notes } };
76849
+ }
76850
+ function releaseSliceWorktree(cwd, plan, input) {
76851
+ const found = findSlice(plan, input.slice);
76852
+ if (!found.ok)
76853
+ return found;
76854
+ const slice = found.data;
76855
+ const target = slicePath(cwd, slice);
76856
+ const worktrees = listWorktrees(cwd);
76857
+ const index = worktrees.findIndex((entry) => samePath(entry.path, target));
76858
+ const out = {
76859
+ fanout: plan.name,
76860
+ slice: slice.id,
76861
+ branch: slice.branch,
76862
+ path: target,
76863
+ head: null,
76864
+ dirty: [],
76865
+ forced: input.force,
76866
+ removed: false,
76867
+ unpushed: null,
76868
+ notes: []
76869
+ };
76870
+ if (index < 0) {
76871
+ if (fs24.existsSync(target))
76872
+ return fanoutFail("blocked", `${target} is not a worktree of this repository; nothing was removed`, "remove it yourself once you know what it holds");
76873
+ out.notes.push(`no worktree at ${target}; nothing to release`);
76874
+ return { ok: true, data: out };
76875
+ }
76876
+ const entry = worktrees[index];
76877
+ if (index === 0)
76878
+ return fanoutFail("blocked", `${target} is the main checkout; it is never released`);
76879
+ if (inside2(cwd, entry.path))
76880
+ return fanoutFail("blocked", `this command runs inside ${entry.path}`, `run it from the main checkout: workit --cwd <main checkout> fanout worktree release ${slice.id}`);
76881
+ if (entry.branch !== null && entry.branch !== `refs/heads/${slice.branch}`)
76882
+ return fanoutFail("blocked", `${entry.path} is on ${entry.branch.replace(/^refs\/heads\//u, "")}, not slice ${slice.id}'s ${slice.branch}; nothing was removed`);
76883
+ const owned = createdRow(cwd, plan, slice, entry.path);
76884
+ if (!owned)
76885
+ return fanoutFail("blocked", `${entry.path} was not made by workit fanout worktree create for slice ${slice.id} (no ledger row); nothing was removed`, `remove it yourself (git worktree remove ${entry.path}) once you know what it holds`);
76886
+ if (input.force && !owned.exact)
76887
+ return fanoutFail("blocked", `${entry.path} was created before workit recorded worktree identity, so it cannot be told from a worktree someone re-added there; --force is refused and nothing was removed`, `release it without --force, or remove it yourself (git worktree remove ${entry.path}) once you know what it holds`);
76888
+ const made = owned.row;
76889
+ out.head = entry.head;
76890
+ const status = gitRun4(entry.path, [
76891
+ "status",
76892
+ "--porcelain=v1",
76893
+ "-z",
76894
+ "--untracked-files=all",
76895
+ "--ignore-submodules=none"
76896
+ ]);
76897
+ if (!status.ok)
76898
+ return fanoutFail("failed", `git status in ${entry.path} failed: ${status.stderr.trim().split(`
76899
+ `)[0]}`, "nothing was removed; inspect the worktree by hand");
76900
+ const entries = porcelainEntries(status.stdout);
76901
+ const inScratch = (line) => line.slice(3).startsWith(`${SCRATCH_DIR}/`);
76902
+ out.dirty = entries.filter((line) => !inScratch(line));
76903
+ out.unpushed = unpushedCount(cwd, slice.branch);
76904
+ const refused = out.dirty.length > 0 && !input.force;
76905
+ const recorded = appendObserved(cwd, {
76906
+ type: "fanout.worktree.released",
76907
+ actor: input.actor,
76908
+ fanout: plan.name,
76909
+ slice: slice.id,
76910
+ branch: slice.branch,
76911
+ path: entry.path,
76912
+ head: entry.head,
76913
+ uncommittedCount: out.dirty.length,
76914
+ uncommitted: rowSample(out.dirty),
76915
+ forced: input.force,
76916
+ outcome: refused ? "refused" : "removed"
76917
+ });
76918
+ if (!recorded.ok)
76919
+ return fanoutFail("unavailable", `cannot record the worktree state in the ledger (${recorded.error}); nothing was removed`);
76920
+ if (refused)
76921
+ return fanoutFail("blocked", `${entry.path} has ${out.dirty.length} uncommitted change${out.dirty.length === 1 ? "" : "s"} (${out.dirty.slice(0, 3).join(", ")}${out.dirty.length > 3 ? ", …" : ""}); recorded in the ledger, nothing removed`, `commit them on ${slice.branch} (workit git commit), or pass --force to drop them`, { ...out });
76922
+ const args = ["worktree", "remove", ...input.force ? ["--force"] : [], entry.path];
76923
+ let removed = gitRun4(cwd, args, SLOW);
76924
+ if (!removed.ok && entries.some(inScratch)) {
76925
+ removeScratch(entry.path);
76926
+ removed = gitRun4(cwd, args, SLOW);
76927
+ }
76928
+ if (!removed.ok)
76929
+ return fanoutFail("failed", `git worktree remove failed: ${removed.stderr.trim().split(`
76930
+ `)[0]}`, entry.locked ? `git worktree unlock ${entry.path}, then release again` : undefined, { ...out });
76931
+ out.removed = true;
76932
+ if (made.dirExisted === true)
76933
+ fs24.mkdirSync(entry.path, { recursive: true });
76934
+ if (out.unpushed)
76935
+ out.notes.push(`${slice.branch} is kept with ${out.unpushed} commit(s) not on the remote`);
76936
+ return { ok: true, data: out };
76937
+ }
76938
+ function porcelainEntries(stdout) {
76939
+ const fields = nulList2(stdout);
76940
+ const out = [];
76941
+ for (let index = 0;index < fields.length; index += 1) {
76942
+ const entry = fields[index];
76943
+ out.push(entry);
76944
+ if (entry[0] === "R" || entry[0] === "C")
76945
+ index += 1;
76946
+ }
76947
+ return out;
76948
+ }
76949
+ function unpushedCount(cwd, branch) {
76950
+ const remote = pushRemoteName(cwd) ?? "origin";
76951
+ const local = resolveRef(cwd, `refs/heads/${branch}`);
76952
+ if (!local)
76953
+ return null;
76954
+ const tracking = resolveRef(cwd, `refs/remotes/${remote}/${branch}`);
76955
+ if (!tracking)
76956
+ return null;
76957
+ const count = gitRun4(cwd, ["rev-list", "--count", `${tracking}..${local}`]);
76958
+ return count.ok ? Number(count.stdout.trim()) : null;
76959
+ }
76960
+ function removeScratch(worktree) {
76961
+ const scratch = path44.join(worktree, SCRATCH_DIR);
76962
+ let stat;
76963
+ try {
76964
+ stat = fs24.lstatSync(scratch);
76965
+ } catch {
76966
+ return;
76967
+ }
76968
+ if (stat.isSymbolicLink() || !stat.isDirectory())
76969
+ fs24.unlinkSync(scratch);
76970
+ else
76971
+ fs24.rmSync(scratch, { recursive: true, force: true, maxRetries: 2 });
76972
+ }
76973
+ var SLOW, SCRATCH_DIR = ".workit-scratch", EXCLUDE_LINE, DIRTY_ROW_BYTES = 2000, canonical2 = (target) => {
76974
+ try {
76975
+ return fs24.realpathSync.native(target);
76976
+ } catch {
76977
+ return path44.resolve(target);
76978
+ }
76979
+ }, samePath = (a, b) => {
76980
+ const left = canonical2(a);
76981
+ const right = canonical2(b);
76982
+ return process.platform === "win32" || process.platform === "darwin" ? left.toLowerCase() === right.toLowerCase() : left === right;
76983
+ }, inside2 = (child, parent) => {
76984
+ const relative = path44.relative(canonical2(parent), canonical2(child));
76985
+ return relative === "" || !relative.startsWith("..") && !path44.isAbsolute(relative);
76986
+ }, slicePath = (cwd, slice) => path44.isAbsolute(slice.worktree) ? slice.worktree : path44.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, TOKEN_FILE = "workit-fanout-token", worktreeDeps, worktreeFailed = (stderr) => fanoutFail("failed", `git worktree add failed: ${stderr.trim().split(`
76987
+ `)[0] || "unknown error"}`);
76988
+ var init_fanout_worktree = __esm(() => {
76989
+ init_ops();
76990
+ init_rev();
76991
+ init_ledger();
76992
+ init_fanout();
76993
+ SLOW = GIT_TIMEOUTS.worktree;
76994
+ EXCLUDE_LINE = `/${SCRATCH_DIR}/`;
76995
+ worktreeDeps = {
76996
+ stat: (file) => fs24.statSync(file, { bigint: true })
76997
+ };
76998
+ });
76999
+
77000
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/fanout-brief.ts
77001
+ import { createHash as createHash17 } from "node:crypto";
77002
+ function workerSession(lead, slice, attempt) {
77003
+ const tail = attempt === null ? "" : `+try${attempt}`;
77004
+ const full = `${lead}-w-${slice}${tail}`;
77005
+ if (full.length <= SESSION_MAX)
77006
+ return full;
77007
+ const digest = createHash17("sha256").update(slice).digest("hex").slice(0, 12);
77008
+ return `${lead.slice(0, SESSION_MAX - 3 - digest.length - tail.length)}-w-${digest}${tail}`;
77009
+ }
77010
+ function renderBrief(cwd, plan, input) {
77011
+ const found = findSlice(plan, input.slice);
77012
+ if (!found.ok)
77013
+ return found;
77014
+ const slice = found.data;
77015
+ const ledger = readLedger(cwd);
77016
+ if (!ledger.ok)
77017
+ return fanoutFail(ledger.code, ledger.error, ledger.unblock);
77018
+ if (!input.actor.session)
77019
+ return fanoutFail("blocked", "WORKIT_SESSION_ID is not set: worker session ids derive from the lead's", "export WORKIT_SESSION_ID=<your session id>, then render the brief again");
77020
+ const branch = input.attempt === null ? slice.branch : `${slice.branch}-try${input.attempt}`;
77021
+ const taken = plan.slices.find((other) => other.id !== slice.id && other.branch === branch);
77022
+ if (taken)
77023
+ return fanoutFail("invalid_input", `attempt branch ${branch} is slice ${taken.id}'s branch`, "pick another --attempt number, or rename one of the branches in the plan");
77024
+ const head = branchRef(cwd, branch);
77025
+ const mode = input.mode ?? (head ? "resume" : "new");
77026
+ if (mode === "resume" && !head)
77027
+ return fanoutFail("not_found", `MODE resume needs the branch ${branch}, which does not exist here`, `workit fanout brief ${slice.id} --mode new`);
77028
+ const planned = `${slicePath(cwd, slice)}${input.attempt === null ? "" : `+try${input.attempt}`}`;
77029
+ const exists = listWorktrees(cwd).some((entry) => samePath(entry.path, planned));
77030
+ excludeScratch(cwd);
77031
+ const scratch = exists ? `${planned}/${SCRATCH_DIR}` : SCRATCH_DIR;
77032
+ const scratchLine = exists ? `SCRATCH: ${scratch} (yours alone; temp files go here, never a shared path)` : `SCRATCH: ${SCRATCH_DIR}/ at your worktree root (mkdir -p it; git ignores it; never a shared path)`;
77033
+ const standing = activeStanding(ledger.value.rows, plan.name, planLeads(plan)).map((row) => ({
77034
+ id: row.id,
77035
+ what: String(row.what)
77036
+ }));
77037
+ const session = workerSession(sessionSafe(input.actor.session), slice.id, input.attempt);
77038
+ const fanIn = plan.fanIn === "integration" ? [
77039
+ `FAN-IN: integration branch ${plan.trunk}. Before you report, merge its tip into your branch`,
77040
+ ` (\`git merge --no-edit ${plan.trunk}\`), resolve conflicts inside SCOPE and re-run VERIFY, so the`,
77041
+ " lead's merge is a fast-forward. No other merge, and no rebase, retarget or force-push."
77042
+ ] : [
77043
+ "FAN-IN: one PR per slice. Never rebase, retarget, merge or force-push: the lead owns topology."
77044
+ ];
77045
+ const lines = [
77046
+ `MODE: ${mode}${input.attempt === null ? "" : ` (race attempt ${input.attempt}: the lead keeps the attempt with the best verdict)`}`,
77047
+ `GOAL: ${slice.goal}`,
77048
+ `SCOPE: ${slice.scope.join(", ")}`,
77049
+ ...slice.owns.length ? [` owns: ${slice.owns.join(", ")}`] : [],
77050
+ ` branch: ${branch} base: ${slice.base}`,
77051
+ `CONTEXT: ${slice.context ?? "none given: read the files in SCOPE and the neighbours they import"}`,
77052
+ ...mode === "resume" && head ? [
77053
+ ` resume: continue ${branch} from ${head.slice(0, 12)}; read \`git log ${slice.base}..${branch}\` and \`workit ledger list --branch ${branch}\` (the last report, verdicts, rulings)`
77054
+ ] : [],
77055
+ "ACCEPTANCE:",
77056
+ ...slice.acceptance.map((line) => ` - ${line}`),
77057
+ `VERIFY: ${slice.verify.join("; ")}`,
77058
+ `TIER: ${slice.tier}`,
77059
+ `TIMEBOX: ${slice.timebox ?? DEFAULT_TIMEBOX}; past it without a new commit you will be replaced`,
77060
+ scratchLine,
77061
+ `FORBIDDEN: ${slice.forbidden.join("; ")}`,
77062
+ ...fanIn,
77063
+ ...REPORT,
77064
+ "STANDING:",
77065
+ ...standing.length ? standing.map((order) => ` - ${order.what}`) : [" - none recorded beyond this brief"],
77066
+ ` export WORKIT_SESSION_ID=${session}`,
77067
+ ...rules(mode, branch, slice.base)
77068
+ ];
77069
+ return {
77070
+ ok: true,
77071
+ data: {
77072
+ fanout: plan.name,
77073
+ slice: slice.id,
77074
+ mode,
77075
+ attempt: input.attempt,
77076
+ branch,
77077
+ base: slice.base,
77078
+ tier: slice.tier,
77079
+ scratch,
77080
+ session,
77081
+ standing,
77082
+ text: `${lines.join(`
77083
+ `)}
77084
+ `
77085
+ }
77086
+ };
77087
+ }
77088
+ var DEFAULT_TIMEBOX = "30 minutes", REPORT, rules = (mode, branch, base) => [
77089
+ "RULES:",
77090
+ mode === "new" ? ` 1. First command: \`workit git branch ${branch} --base ${base}\` (your worktree may start on another name).` : ` 1. First command: \`git switch ${branch}\`, then continue from its head.`,
77091
+ ' 2. Decide ambiguities yourself: `workit ledger ruling "<what>" --why "<why>" --cost-if-wrong "<cost>"`; stop only for an irreversible, security-sensitive or out-of-worktree action.',
77092
+ " 3. Commit with `workit git commit -m <msg> -- <paths in SCOPE>`; push only if this brief says so.",
77093
+ " 4. Never record a verdict on your own work."
77094
+ ], sessionSafe = (text) => text.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-"), SESSION_MAX = 128;
77095
+ var init_fanout_brief = __esm(() => {
77096
+ init_ledger();
77097
+ init_fanout();
77098
+ init_fanout_worktree();
77099
+ REPORT = [
77100
+ "REPORT: branch, head SHA, files changed, each VERIFY command with its exit code,",
77101
+ " each ACCEPTANCE line met / not met, rulings you made (`workit ledger ruling`),",
77102
+ " anything out of scope as a follow-up, not a diff."
77103
+ ];
77104
+ });
77105
+
77106
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/fanout-landed.ts
76456
77107
  import { spawnSync as spawnSync22 } from "node:child_process";
76457
77108
  function forgeBreaker(resolved) {
76458
77109
  if (!resolved)
@@ -76562,9 +77213,20 @@ function trunkPatchIds(ctx, fork) {
76562
77213
  cache.set(fork, ids);
76563
77214
  return ids;
76564
77215
  }
77216
+ function inSliceRun(row, slice) {
77217
+ if (row.type === "fanout.worktree.created" && typeof row.sliceHash === "string" && slice.hash)
77218
+ return row.sliceHash === slice.hash;
77219
+ const since = Date.parse(slice.hashSince);
77220
+ return Number.isNaN(since) || Date.parse(String(row.at)) >= since;
77221
+ }
76565
77222
  function linked(rows, input, slice, head) {
76566
77223
  const since = input.since ? Date.parse(input.since) : Number.NaN;
76567
- return rows.some((row) => (Number.isNaN(since) || Date.parse(String(row.at)) >= since) && (row.type === "fanout.worktree.created" && row.fanout === input.fanout && row.slice === slice.id || head !== null && row.branch === slice.branch && row.head === head));
77224
+ const recent = (row) => Number.isNaN(since) || Date.parse(String(row.at)) >= since;
77225
+ return rows.some((row) => {
77226
+ if (row.type === "fanout.worktree.created" && row.fanout === input.fanout && row.slice === slice.id)
77227
+ return typeof row.sliceHash === "string" && input.sliceHash ? row.sliceHash === input.sliceHash : recent(row);
77228
+ return recent(row) && head !== null && row.branch === slice.branch && row.head === head;
77229
+ });
76568
77230
  }
76569
77231
  function detectLanding(ctx, slice, input) {
76570
77232
  const out = {
@@ -76637,7 +77299,7 @@ function sliceStart(cwd, rows, fanout, slice) {
76637
77299
  const reflog = branchCreatedAt(cwd, slice.branch);
76638
77300
  if (reflog)
76639
77301
  return reflog;
76640
- const row = rows.findLast((candidate) => candidate.type === "fanout.worktree.created" && candidate.fanout === fanout && candidate.slice === slice.id && typeof candidate.baseSha === "string");
77302
+ const row = rows.findLast((candidate) => candidate.type === "fanout.worktree.created" && candidate.fanout === fanout && candidate.slice === slice.id && typeof candidate.baseSha === "string" && inSliceRun(candidate, slice));
76641
77303
  return row?.baseSha ?? null;
76642
77304
  }
76643
77305
  var isAncestor2 = (cwd, a, b) => gitRun4(cwd, ["merge-base", "--is-ancestor", a, b]).ok, TRUNK_SCAN = 500;
@@ -76646,7 +77308,7 @@ var init_fanout_landed = __esm(() => {
76646
77308
  init_fanout();
76647
77309
  });
76648
77310
 
76649
- // packages/workit-core/src/fanout-check.ts
77311
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/fanout-check.ts
76650
77312
  function mergeConflicts(cwd, a, b) {
76651
77313
  const run = gitRun4(cwd, [
76652
77314
  "merge-tree",
@@ -76786,7 +77448,8 @@ function sliceLandings(cwd, plan, trunk, tips, rows, forge) {
76786
77448
  createdFrom: sliceStart(cwd, rows, plan.name, slice),
76787
77449
  parentHead,
76788
77450
  fanout: plan.name,
76789
- since: plan.createdAt || null,
77451
+ since: slice.hashSince || plan.createdAt || null,
77452
+ sliceHash: slice.hash || null,
76790
77453
  rows
76791
77454
  }));
76792
77455
  }
@@ -76903,7 +77566,7 @@ var init_fanout_check = __esm(() => {
76903
77566
  init_fanout_landed();
76904
77567
  });
76905
77568
 
76906
- // packages/workit-core/src/fanout-status.ts
77569
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/fanout-status.ts
76907
77570
  function timeboxMs(text) {
76908
77571
  if (!text || /^\s*\d+(?:\.\d+)?\s*$/u.test(text))
76909
77572
  return null;
@@ -76928,7 +77591,7 @@ function activity(cwd, plan, slice, rows) {
76928
77591
  head,
76929
77592
  commit,
76930
77593
  last: newest(commit, reflog, ...ledger),
76931
- worktreeCreated: rows.some((row) => row.type === "fanout.worktree.created" && row.fanout === plan.name && row.slice === slice.id)
77594
+ worktreeCreated: rows.some((row) => row.type === "fanout.worktree.created" && row.fanout === plan.name && row.slice === slice.id && inSliceRun(row, slice))
76932
77595
  };
76933
77596
  }
76934
77597
  function fanoutStatus(cwd, plan, options) {
@@ -77038,7 +77701,7 @@ function fanoutStatus(cwd, plan, options) {
77038
77701
  else if (row.branchExists)
77039
77702
  row.state = "active";
77040
77703
  else if (row.waitsFor.length === 0 || parent && row.waitsFor.length === 1 && row.waitsFor[0] === parent.id && out.get(parent.id)?.verdict.accepted) {
77041
- const created = rows.some((candidate) => candidate.type === "fanout.worktree.created" && candidate.fanout === plan.name && candidate.slice === id);
77704
+ const created = rows.some((candidate) => candidate.type === "fanout.worktree.created" && candidate.fanout === plan.name && candidate.slice === id && inSliceRun(candidate, byId.get(id)));
77042
77705
  row.state = created ? "active" : "not_started";
77043
77706
  if (!created)
77044
77707
  spawnable.push(id);
@@ -77090,340 +77753,6 @@ var init_fanout_status = __esm(() => {
77090
77753
  DEFAULT_STUCK_MS = 30 * 60000;
77091
77754
  });
77092
77755
 
77093
- // packages/workit-core/src/fanout-worktree.ts
77094
- import fs24 from "node:fs";
77095
- import path44 from "node:path";
77096
- function rowSample(lines) {
77097
- const out = [];
77098
- let bytes = 0;
77099
- for (const line of lines) {
77100
- const entry = line.slice(0, 200);
77101
- bytes += Buffer.byteLength(entry) + 3;
77102
- if (bytes > DIRTY_ROW_BYTES)
77103
- break;
77104
- out.push(entry);
77105
- }
77106
- return out;
77107
- }
77108
- function listWorktrees(cwd) {
77109
- const out = [];
77110
- let current = null;
77111
- for (const field of gitRun4(cwd, ["worktree", "list", "--porcelain", "-z"]).stdout.split("\x00")) {
77112
- if (field.startsWith("worktree ")) {
77113
- current = { path: field.slice(9), head: null, branch: null, locked: false };
77114
- out.push(current);
77115
- } else if (!current)
77116
- continue;
77117
- else if (field.startsWith("HEAD "))
77118
- current.head = field.slice(5);
77119
- else if (field.startsWith("branch "))
77120
- current.branch = field.slice(7);
77121
- else if (field === "locked" || field.startsWith("locked "))
77122
- current.locked = true;
77123
- }
77124
- return out;
77125
- }
77126
- function createdRow(cwd, plan, slice, target) {
77127
- const ledger = readLedger(cwd);
77128
- if (!ledger.ok)
77129
- return null;
77130
- const atPath = (row) => row.fanout === plan.name && row.slice === slice.id && typeof row.path === "string" && samePath(row.path, target);
77131
- const rows = ledger.value.rows;
77132
- const row = rows.findLast((candidate) => candidate.type === "fanout.worktree.created" && atPath(candidate));
77133
- if (!row)
77134
- return null;
77135
- const removedSince = rows.some((candidate) => candidate.seq > row.seq && candidate.type === "fanout.worktree.released" && candidate.outcome === "removed" && atPath(candidate));
77136
- if (removedSince)
77137
- return null;
77138
- const made = adminDirTime(target);
77139
- if (made === null || made + CLOCK_SLACK_MS < Date.parse(row.at))
77140
- return null;
77141
- return row;
77142
- }
77143
- function adminDirTime(worktree) {
77144
- const dir = gitRun4(worktree, ["rev-parse", "--absolute-git-dir"]);
77145
- if (!dir.ok)
77146
- return null;
77147
- try {
77148
- const stat = fs24.statSync(dir.stdout.trim());
77149
- return stat.birthtimeMs > 0 ? stat.birthtimeMs : stat.mtimeMs;
77150
- } catch {
77151
- return null;
77152
- }
77153
- }
77154
- function findSlice(plan, id) {
77155
- const slice = plan.slices.find((candidate) => candidate.id === id);
77156
- return slice ? { ok: true, data: slice } : fanoutFail("invalid_input", `${id}: not a slice of fanout ${plan.name}`, `slices: ${plan.slices.map((candidate) => candidate.id).join(", ")}`);
77157
- }
77158
- function excludeScratch(cwd) {
77159
- const common = gitCommonDir(cwd);
77160
- if (!common)
77161
- return;
77162
- const file = path44.join(common, "info", "exclude");
77163
- let text = "";
77164
- try {
77165
- text = fs24.readFileSync(file, "utf8");
77166
- } catch {
77167
- text = "";
77168
- }
77169
- if (text.split(/\r?\n/u).includes(EXCLUDE_LINE))
77170
- return;
77171
- fs24.mkdirSync(path44.dirname(file), { recursive: true });
77172
- fs24.appendFileSync(file, `${text && !text.endsWith(`
77173
- `) ? `
77174
- ` : ""}# workit fanout: per-worker scratch dirs
77175
- ${EXCLUDE_LINE}
77176
- `);
77177
- }
77178
- function ensureScratch(worktree) {
77179
- const scratch = path44.join(worktree, SCRATCH_DIR);
77180
- fs24.mkdirSync(scratch, { recursive: true });
77181
- return scratch;
77182
- }
77183
- function createSliceWorktree(cwd, plan, input) {
77184
- const found = findSlice(plan, input.slice);
77185
- if (!found.ok)
77186
- return found;
77187
- const slice = found.data;
77188
- if (!slice.worktree)
77189
- return fanoutFail("invalid_input", `slice ${slice.id} has no worktree path`, "re-plan it with workit fanout plan");
77190
- const target = slicePath(cwd, slice);
77191
- const worktrees = listWorktrees(cwd);
77192
- const localRef = `refs/heads/${slice.branch}`;
77193
- const existing = worktrees.find((entry) => samePath(entry.path, target));
77194
- const base = {
77195
- fanout: plan.name,
77196
- slice: slice.id,
77197
- branch: slice.branch,
77198
- base: slice.base,
77199
- path: target
77200
- };
77201
- if (existing) {
77202
- if (existing.branch !== null && existing.branch !== localRef)
77203
- return fanoutFail("blocked", `${target} is a worktree on ${existing.branch.replace(/^refs\/heads\//u, "")}, not ${slice.branch}`, `release it first, or set slice ${slice.id}'s worktree to another path`);
77204
- if (!createdRow(cwd, plan, slice, target))
77205
- return fanoutFail("blocked", `${target} is a worktree that workit fanout worktree create did not make for slice ${slice.id}`, `remove it yourself (git worktree remove ${target}) once you know what it holds`);
77206
- excludeScratch(cwd);
77207
- return {
77208
- ok: true,
77209
- data: {
77210
- ...base,
77211
- scratch: ensureScratch(target),
77212
- head: existing.head ?? "",
77213
- mode: resolveRef(cwd, localRef) ? "resume" : "new",
77214
- created: false,
77215
- notes: ["the worktree already exists; nothing was changed"]
77216
- }
77217
- };
77218
- }
77219
- const elsewhere = worktrees.find((entry) => entry.branch === localRef);
77220
- if (elsewhere)
77221
- return fanoutFail("blocked", `${slice.branch} is checked out at ${elsewhere.path}: never two live workers on one branch`, `stop that worker, then workit fanout worktree release ${slice.id} (or git worktree remove ${elsewhere.path})`);
77222
- let dirExisted = false;
77223
- try {
77224
- const stat = fs24.statSync(target);
77225
- if (!stat.isDirectory() || fs24.readdirSync(target).length > 0)
77226
- return fanoutFail("blocked", `${target} exists and is ${stat.isDirectory() ? "not empty" : "not a directory"} (not a worktree of this repository)`, `move it away, or set slice ${slice.id}'s worktree to another path`);
77227
- dirExisted = true;
77228
- } catch (error) {
77229
- if (error.code !== "ENOENT")
77230
- return fanoutFail("failed", `cannot read ${target}: ${error.message}`);
77231
- }
77232
- const notes = [];
77233
- let mode = "resume";
77234
- if (resolveRef(cwd, localRef)) {
77235
- const added = gitRun4(cwd, ["worktree", "add", target, slice.branch], SLOW);
77236
- if (!added.ok)
77237
- return worktreeFailed(added.stderr);
77238
- } else {
77239
- const remote = pushRemoteName(cwd) ?? "origin";
77240
- const remoteRef = `refs/remotes/${remote}/${slice.branch}`;
77241
- if (resolveRef(cwd, remoteRef)) {
77242
- const added = gitRun4(cwd, ["worktree", "add", "-b", slice.branch, target, remoteRef], SLOW);
77243
- if (!added.ok)
77244
- return worktreeFailed(added.stderr);
77245
- notes.push(`resumed ${slice.branch} from ${remote}/${slice.branch}`);
77246
- } else {
77247
- mode = "new";
77248
- const parent = plan.slices.find((other) => other.branch === slice.base);
77249
- const from = slice.base === plan.trunk ? trunkRef(cwd, plan.trunk) : parent ? branchRef(cwd, parent.branch) : branchRef(cwd, slice.base);
77250
- if (!from)
77251
- return fanoutFail("not_found", `base ${slice.base} of slice ${slice.id} does not resolve here`, parent ? `start slice ${parent.id} first (its branch ${parent.branch} is the base)` : `git fetch origin ${slice.base}`);
77252
- const added = gitRun4(cwd, ["worktree", "add", "--detach", target, from], SLOW);
77253
- if (!added.ok)
77254
- return worktreeFailed(added.stderr);
77255
- const branched = gitBranch(target, { name: slice.branch, base: slice.base });
77256
- if (!branched.ok) {
77257
- gitRun4(cwd, ["worktree", "remove", target], SLOW);
77258
- return fanoutFail(branched.code, `workit git branch ${slice.branch} refused: ${branched.error}`, branched.unblock ?? `fix the branch name in the plan, then workit fanout worktree create ${slice.id}`);
77259
- }
77260
- notes.push(...branched.data.notes);
77261
- }
77262
- }
77263
- excludeScratch(cwd);
77264
- const scratch = ensureScratch(target);
77265
- const head = gitRun4(target, ["rev-parse", "HEAD"]).stdout.trim();
77266
- const baseSha = mode === "new" ? head : null;
77267
- appendObserved(cwd, {
77268
- type: "fanout.worktree.created",
77269
- actor: input.actor,
77270
- fanout: plan.name,
77271
- slice: slice.id,
77272
- branch: slice.branch,
77273
- path: target,
77274
- scratch,
77275
- mode,
77276
- head,
77277
- dirExisted,
77278
- planCreatedAt: plan.createdAt,
77279
- ...baseSha ? { baseSha } : {}
77280
- });
77281
- return { ok: true, data: { ...base, scratch, head, mode, created: true, notes } };
77282
- }
77283
- function releaseSliceWorktree(cwd, plan, input) {
77284
- const found = findSlice(plan, input.slice);
77285
- if (!found.ok)
77286
- return found;
77287
- const slice = found.data;
77288
- const target = slicePath(cwd, slice);
77289
- const worktrees = listWorktrees(cwd);
77290
- const index = worktrees.findIndex((entry) => samePath(entry.path, target));
77291
- const out = {
77292
- fanout: plan.name,
77293
- slice: slice.id,
77294
- branch: slice.branch,
77295
- path: target,
77296
- head: null,
77297
- dirty: [],
77298
- forced: input.force,
77299
- removed: false,
77300
- unpushed: null,
77301
- notes: []
77302
- };
77303
- if (index < 0) {
77304
- if (fs24.existsSync(target))
77305
- return fanoutFail("blocked", `${target} is not a worktree of this repository; nothing was removed`, "remove it yourself once you know what it holds");
77306
- out.notes.push(`no worktree at ${target}; nothing to release`);
77307
- return { ok: true, data: out };
77308
- }
77309
- const entry = worktrees[index];
77310
- if (index === 0)
77311
- return fanoutFail("blocked", `${target} is the main checkout; it is never released`);
77312
- if (inside2(cwd, entry.path))
77313
- return fanoutFail("blocked", `this command runs inside ${entry.path}`, `run it from the main checkout: workit --cwd <main checkout> fanout worktree release ${slice.id}`);
77314
- if (entry.branch !== null && entry.branch !== `refs/heads/${slice.branch}`)
77315
- return fanoutFail("blocked", `${entry.path} is on ${entry.branch.replace(/^refs\/heads\//u, "")}, not slice ${slice.id}'s ${slice.branch}; nothing was removed`);
77316
- const made = createdRow(cwd, plan, slice, entry.path);
77317
- if (!made)
77318
- return fanoutFail("blocked", `${entry.path} was not made by workit fanout worktree create for slice ${slice.id} (no ledger row); nothing was removed`, `remove it yourself (git worktree remove ${entry.path}) once you know what it holds`);
77319
- out.head = entry.head;
77320
- const status = gitRun4(entry.path, [
77321
- "status",
77322
- "--porcelain=v1",
77323
- "-z",
77324
- "--untracked-files=all",
77325
- "--ignore-submodules=none"
77326
- ]);
77327
- if (!status.ok)
77328
- return fanoutFail("failed", `git status in ${entry.path} failed: ${status.stderr.trim().split(`
77329
- `)[0]}`, "nothing was removed; inspect the worktree by hand");
77330
- const entries = porcelainEntries(status.stdout);
77331
- const inScratch = (line) => line.slice(3).startsWith(`${SCRATCH_DIR}/`);
77332
- out.dirty = entries.filter((line) => !inScratch(line));
77333
- out.unpushed = unpushedCount(cwd, slice.branch);
77334
- const refused = out.dirty.length > 0 && !input.force;
77335
- const recorded = appendObserved(cwd, {
77336
- type: "fanout.worktree.released",
77337
- actor: input.actor,
77338
- fanout: plan.name,
77339
- slice: slice.id,
77340
- branch: slice.branch,
77341
- path: entry.path,
77342
- head: entry.head,
77343
- uncommittedCount: out.dirty.length,
77344
- uncommitted: rowSample(out.dirty),
77345
- forced: input.force,
77346
- outcome: refused ? "refused" : "removed"
77347
- });
77348
- if (!recorded.ok)
77349
- return fanoutFail("unavailable", `cannot record the worktree state in the ledger (${recorded.error}); nothing was removed`);
77350
- if (refused)
77351
- return fanoutFail("blocked", `${entry.path} has ${out.dirty.length} uncommitted change${out.dirty.length === 1 ? "" : "s"} (${out.dirty.slice(0, 3).join(", ")}${out.dirty.length > 3 ? ", …" : ""}); recorded in the ledger, nothing removed`, `commit them on ${slice.branch} (workit git commit), or pass --force to drop them`, { ...out });
77352
- const args = ["worktree", "remove", ...input.force ? ["--force"] : [], entry.path];
77353
- let removed = gitRun4(cwd, args, SLOW);
77354
- if (!removed.ok && entries.some(inScratch)) {
77355
- removeScratch(entry.path);
77356
- removed = gitRun4(cwd, args, SLOW);
77357
- }
77358
- if (!removed.ok)
77359
- return fanoutFail("failed", `git worktree remove failed: ${removed.stderr.trim().split(`
77360
- `)[0]}`, entry.locked ? `git worktree unlock ${entry.path}, then release again` : undefined, { ...out });
77361
- out.removed = true;
77362
- if (made.dirExisted === true)
77363
- fs24.mkdirSync(entry.path, { recursive: true });
77364
- if (out.unpushed)
77365
- out.notes.push(`${slice.branch} is kept with ${out.unpushed} commit(s) not on the remote`);
77366
- return { ok: true, data: out };
77367
- }
77368
- function porcelainEntries(stdout) {
77369
- const fields = nulList2(stdout);
77370
- const out = [];
77371
- for (let index = 0;index < fields.length; index += 1) {
77372
- const entry = fields[index];
77373
- out.push(entry);
77374
- if (entry[0] === "R" || entry[0] === "C")
77375
- index += 1;
77376
- }
77377
- return out;
77378
- }
77379
- function unpushedCount(cwd, branch) {
77380
- const remote = pushRemoteName(cwd) ?? "origin";
77381
- const local = resolveRef(cwd, `refs/heads/${branch}`);
77382
- if (!local)
77383
- return null;
77384
- const tracking = resolveRef(cwd, `refs/remotes/${remote}/${branch}`);
77385
- if (!tracking)
77386
- return null;
77387
- const count = gitRun4(cwd, ["rev-list", "--count", `${tracking}..${local}`]);
77388
- return count.ok ? Number(count.stdout.trim()) : null;
77389
- }
77390
- function removeScratch(worktree) {
77391
- const scratch = path44.join(worktree, SCRATCH_DIR);
77392
- let stat;
77393
- try {
77394
- stat = fs24.lstatSync(scratch);
77395
- } catch {
77396
- return;
77397
- }
77398
- if (stat.isSymbolicLink() || !stat.isDirectory())
77399
- fs24.unlinkSync(scratch);
77400
- else
77401
- fs24.rmSync(scratch, { recursive: true, force: true, maxRetries: 2 });
77402
- }
77403
- var SLOW, SCRATCH_DIR = ".workit-scratch", EXCLUDE_LINE, DIRTY_ROW_BYTES = 2000, canonical2 = (target) => {
77404
- try {
77405
- return fs24.realpathSync.native(target);
77406
- } catch {
77407
- return path44.resolve(target);
77408
- }
77409
- }, samePath = (a, b) => {
77410
- const left = canonical2(a);
77411
- const right = canonical2(b);
77412
- return process.platform === "win32" || process.platform === "darwin" ? left.toLowerCase() === right.toLowerCase() : left === right;
77413
- }, inside2 = (child, parent) => {
77414
- const relative = path44.relative(canonical2(parent), canonical2(child));
77415
- return relative === "" || !relative.startsWith("..") && !path44.isAbsolute(relative);
77416
- }, slicePath = (cwd, slice) => path44.isAbsolute(slice.worktree) ? slice.worktree : path44.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, worktreeFailed = (stderr) => fanoutFail("failed", `git worktree add failed: ${stderr.trim().split(`
77417
- `)[0] || "unknown error"}`);
77418
- var init_fanout_worktree = __esm(() => {
77419
- init_ops();
77420
- init_rev();
77421
- init_ledger();
77422
- init_fanout();
77423
- SLOW = GIT_TIMEOUTS.worktree;
77424
- EXCLUDE_LINE = `/${SCRATCH_DIR}/`;
77425
- });
77426
-
77427
77756
  // packages/workit-cli/src/verbs/fanout.ts
77428
77757
  var exports_fanout = {};
77429
77758
  __export(exports_fanout, {
@@ -77478,7 +77807,12 @@ function resolveTrunk(io, raw, flag, track) {
77478
77807
  return { trunk: derived.tracked || track !== null ? derived.trunk : null };
77479
77808
  }
77480
77809
  function plan2(argv, io) {
77481
- const flags = parseFlags(argv, { name: "value", trunk: "value", track: "value" });
77810
+ const flags = parseFlags(argv, {
77811
+ name: "value",
77812
+ trunk: "value",
77813
+ track: "value",
77814
+ "take-lead": "boolean"
77815
+ });
77482
77816
  if (typeof flags === "string")
77483
77817
  return usage3(io, flags, PLAN_USAGE2);
77484
77818
  const track = flags.values.track ?? null;
@@ -77500,7 +77834,8 @@ function plan2(argv, io) {
77500
77834
  raw,
77501
77835
  name: flags.values.name ?? null,
77502
77836
  trunk: trunk.trunk,
77503
- actor: actorFromEnv(io.env)
77837
+ actor: actorFromEnv(io.env),
77838
+ takeLead: flags.booleans.has("take-lead")
77504
77839
  });
77505
77840
  if (!result.ok) {
77506
77841
  const code = failed(io, result);
@@ -77508,15 +77843,42 @@ function plan2(argv, io) {
77508
77843
  return code;
77509
77844
  }
77510
77845
  return emit(io, ok(result.data), (data) => [
77511
- `${data.created ? "planned" : "re-planned"} fanout ${data.name} on ${data.trunk}: ${data.slices.length} slice${data.slices.length === 1 ? "" : "s"}`,
77846
+ `${data.created ? "planned" : "re-planned"} fanout ${data.name} on ${data.trunk}${data.fanIn === "integration" ? " (integration branch: one PR)" : ""}: ${data.slices.length} slice${data.slices.length === 1 ? "" : "s"}`,
77512
77847
  ...data.slices.map((slice) => ` ${slice.id} [${slice.tier}] ${slice.branch} <- ${slice.base}${slice.dependsOn.length ? ` after ${slice.dependsOn.join(", ")}` : ""}
77513
77848
  worktree ${slice.worktree}`),
77514
77849
  ...data.overlaps.map((overlap) => ` shared ${overlap.path}: ${overlap.resolution === "owner" ? `owned by ${overlap.owner}` : `serialized (${overlap.slices.join(" -> ")})`}`),
77515
77850
  `waves: ${data.waves.map((wave) => wave.join(", ")).join(" | ")}`,
77516
77851
  `landing order: ${data.landingOrder.join(", ")}`,
77517
- ...data.notes.map((note) => `note: ${note}`)
77852
+ ...data.notes.map((note) => `note: ${note}`),
77853
+ ...data.warnings.map((warning) => `warning: ${warning}`)
77518
77854
  ]);
77519
77855
  }
77856
+ function brief(argv, io) {
77857
+ const flags = parseFlags(argv, { name: "value", mode: "value", attempt: "value" });
77858
+ if (typeof flags === "string")
77859
+ return usage3(io, flags, BRIEF_USAGE);
77860
+ if (flags.positionals.length !== 1)
77861
+ return usage3(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <slice>", BRIEF_USAGE);
77862
+ const mode = flags.values.mode ?? null;
77863
+ if (mode !== null && mode !== "new" && mode !== "resume")
77864
+ return usage3(io, "--mode takes new or resume", BRIEF_USAGE);
77865
+ const rawAttempt = flags.values.attempt;
77866
+ const attempt = rawAttempt === undefined ? null : Number(rawAttempt);
77867
+ if (attempt !== null && !(Number.isInteger(attempt) && attempt >= 1 && attempt <= 9))
77868
+ return usage3(io, "--attempt takes a number from 1 to 9", BRIEF_USAGE);
77869
+ const selected = selectFanout(io.cwd, flags.values.name ?? null, currentBranch(io.cwd));
77870
+ if (!selected.ok)
77871
+ return failed(io, selected);
77872
+ const result = renderBrief(io.cwd, selected.data, {
77873
+ slice: flags.positionals[0],
77874
+ mode,
77875
+ attempt,
77876
+ actor: actorFromEnv(io.env)
77877
+ });
77878
+ if (!result.ok)
77879
+ return failed(io, result);
77880
+ return emit(io, ok(result.data), (data) => data.text.trimEnd());
77881
+ }
77520
77882
  function check2(argv, io) {
77521
77883
  const flags = parseFlags(argv, { name: "value", base: "value", offline: "boolean" });
77522
77884
  if (typeof flags === "string")
@@ -77618,6 +77980,8 @@ async function run15(argv, io) {
77618
77980
  const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
77619
77981
  if (sub === "plan")
77620
77982
  return plan2(rest, io);
77983
+ if (sub === "brief")
77984
+ return brief(rest, io);
77621
77985
  if (sub === "check")
77622
77986
  return check2(rest, io);
77623
77987
  if (sub === "status")
@@ -77626,7 +77990,7 @@ async function run15(argv, io) {
77626
77990
  return worktree(rest, io);
77627
77991
  return usage3(io, sub && !sub.startsWith("-") ? `unknown fanout subcommand "${sub}"` : "missing fanout subcommand", USAGE8);
77628
77992
  }
77629
- var PLAN_USAGE2 = "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --track <t>] [--json]", CHECK_USAGE = "workit fanout check [<slice>…] [--name <n>] [--base <ref>] [--offline] [--json]", STATUS_USAGE3 = "workit fanout status [--name <n>] [--stuck-after 30m] [--offline] [--json]", WORKTREE_USAGE = "workit fanout worktree create <slice> [--name <n>] | worktree release <slice> [--name <n>] [--force] [--json]", USAGE8 = "workit fanout plan|check|status|worktree ... (workit help fanout)", failed = (io, result) => emit(io, fail(result.code, result.error, { unblock: result.unblock, data: result.data ?? {} })), renderCheck = (data) => [
77993
+ var PLAN_USAGE2 = "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --track <t>] [--take-lead] [--json]", BRIEF_USAGE = "workit fanout brief <slice> [--name <n>] [--mode new|resume] [--attempt <n>] [--json]", CHECK_USAGE = "workit fanout check [<slice>…] [--name <n>] [--base <ref>] [--offline] [--json]", STATUS_USAGE3 = "workit fanout status [--name <n>] [--stuck-after 30m] [--offline] [--json]", WORKTREE_USAGE = "workit fanout worktree create <slice> [--name <n>] | worktree release <slice> [--name <n>] [--force] [--json]", USAGE8 = "workit fanout plan|brief|check|status|worktree ... (workit help fanout)", failed = (io, result) => emit(io, fail(result.code, result.error, { unblock: result.unblock, data: result.data ?? {} })), renderCheck = (data) => [
77630
77994
  `fanout ${data.name} against ${data.trunkRef}: ${data.ready ? "ready to land" : "blocked"}`,
77631
77995
  ...data.slices.map((slice) => slice.status === "landed" ? ` -- ${slice.id} (${slice.branch}) landed (${slice.landed?.how}${slice.landed?.pr ? ` #${slice.landed.pr}` : ""})` : ` ${slice.status === "ready" ? "ok " : "x "}${slice.id} (${slice.branch}) ${slice.changed} file${slice.changed === 1 ? "" : "s"} · verdict ${slice.verdict.accepted ? "accepted" : slice.verdict.basis}${slice.reasons.length ? `
77632
77996
  ${slice.reasons.join(`
@@ -77664,6 +78028,7 @@ var PLAN_USAGE2 = "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --
77664
78028
  ];
77665
78029
  };
77666
78030
  var init_fanout2 = __esm(() => {
78031
+ init_fanout_brief();
77667
78032
  init_fanout_check();
77668
78033
  init_fanout_status();
77669
78034
  init_fanout_worktree();
@@ -77679,7 +78044,7 @@ var exports_ledger = {};
77679
78044
  __export(exports_ledger, {
77680
78045
  run: () => run16
77681
78046
  });
77682
- import { randomBytes as randomBytes4 } from "node:crypto";
78047
+ import { randomBytes as randomBytes5 } from "node:crypto";
77683
78048
  import { parseArgs } from "node:util";
77684
78049
  function targetBranch(io, rows, branch, pr) {
77685
78050
  if (pr !== undefined) {
@@ -77725,8 +78090,10 @@ async function run16(argv, io) {
77725
78090
  const text = rest.join(" ");
77726
78091
  if (sub === undefined)
77727
78092
  return usage4(io, "missing subcommand");
77728
- if (!["decision", "ruling", "verdict", "list", "show", "check"].includes(sub))
78093
+ if (!["decision", "ruling", "verdict", "standing", "list", "show", "check"].includes(sub))
77729
78094
  return usage4(io, `unknown ledger subcommand "${sub}"`);
78095
+ if (sub === "standing")
78096
+ return standing(io, values, rest);
77730
78097
  const ledger = readLedger(io.cwd);
77731
78098
  if (!ledger.ok)
77732
78099
  return failed2(io, ledger);
@@ -77804,7 +78171,62 @@ async function run16(argv, io) {
77804
78171
  `);
77805
78172
  return fromResult(io, verdict, (row) => `recorded verdict ${row.id}: ${row.result} [${row.kind}] for ${row.branch} @ ${(row.head ?? "").slice(0, 12)} as ${actor.session ?? "no session"}${row.self ? ` (self${row.selfReason === "no_session" ? ": WORKIT_SESSION_ID unset" : ""}; never accepted)` : ""}`);
77806
78173
  }
77807
- var USAGE9 = "workit ledger decision|ruling|verdict|list|check ... (workit help ledger for the grammar)", OPTIONS, SESSION_SAFE2, ROLE_SAFE, actorFor = (io, values) => {
78174
+ function standingFanout(io, values) {
78175
+ if (values.fanout !== undefined) {
78176
+ const name = values.fanout.trim();
78177
+ if (!name)
78178
+ return new Error("--fanout needs a plan name");
78179
+ const read = readFanout(io.cwd, name);
78180
+ return { name, plan: read.ok ? read.data : null };
78181
+ }
78182
+ const selected = selectFanout(io.cwd, null, currentBranch(io.cwd));
78183
+ return selected.ok ? { name: selected.data.name, plan: selected.data } : new Error(`${selected.error}; pass --fanout <name>`);
78184
+ }
78185
+ async function standing(io, values, args) {
78186
+ const [action, ...rest] = args;
78187
+ if (action !== "add" && action !== "list" && action !== "clear")
78188
+ return emit(io, fail("invalid_input", action ? `unknown standing action "${action}"` : "missing add, list or clear", { unblock: STANDING_USAGE }));
78189
+ const target = standingFanout(io, values);
78190
+ if (target instanceof Error)
78191
+ return emit(io, fail("invalid_input", target.message, { unblock: STANDING_USAGE }));
78192
+ const fanout = target.name;
78193
+ const lead = target.plan?.leadSession ?? null;
78194
+ const leads = target.plan ? planLeads(target.plan) : null;
78195
+ if (action === "list") {
78196
+ if (rest.length)
78197
+ return usage4(io, `unexpected argument: ${rest[0]}`);
78198
+ const ledger = readLedger(io.cwd);
78199
+ if (!ledger.ok)
78200
+ return failed2(io, ledger);
78201
+ const orders = activeStanding(ledger.value.rows, fanout, leads).map((row) => ({
78202
+ id: row.id,
78203
+ at: row.at,
78204
+ what: row.what
78205
+ }));
78206
+ return emit(io, ok({ fanout, orders }), (data) => data.orders.length ? [
78207
+ `standing orders for fanout ${fanout}:`,
78208
+ ...data.orders.map((o) => ` ${o.id} ${o.what}`)
78209
+ ] : `no standing orders for fanout ${fanout}`);
78210
+ }
78211
+ const acting = actorFor(io, values);
78212
+ if (acting instanceof Error)
78213
+ return usage4(io, acting.message);
78214
+ const context = {
78215
+ cwd: io.cwd,
78216
+ actor: acting.actor,
78217
+ ...values.supersedes ? { supersedes: values.supersedes } : {}
78218
+ };
78219
+ if (action === "add" && target.plan === null)
78220
+ return emit(io, fail("not_found", `no fanout plan named ${fanout}`, {
78221
+ unblock: "workit fanout plan <plan.json> first, or name an existing plan with --fanout"
78222
+ }));
78223
+ if (action === "add")
78224
+ return fromResult(io, recordStanding(context, { fanout, lead, what: rest.join(" ") }), (row) => `recorded standing order ${row.id} for fanout ${fanout}`);
78225
+ if (rest.length > 1)
78226
+ return usage4(io, `unexpected argument: ${rest[1]}`);
78227
+ return fromResult(io, clearStanding(context, { fanout, lead, leads, target: rest[0] ?? null }), (row) => row.target ? `cleared standing order ${row.target} for fanout ${fanout}` : `cleared every standing order for fanout ${fanout}`);
78228
+ }
78229
+ var USAGE9 = "workit ledger decision|ruling|verdict|standing|list|check ... (workit help ledger for the grammar)", STANDING_USAGE = 'workit ledger standing add "<order>" | list | clear [<id>] [--fanout <name>] [--json]', OPTIONS, SESSION_SAFE2, ROLE_SAFE, actorFor = (io, values) => {
77808
78230
  const actor = actorFromEnv(io.env);
77809
78231
  if (values.session !== undefined && values.as !== undefined)
77810
78232
  return new Error("pass --session or --as, not both");
@@ -77818,7 +78240,7 @@ var USAGE9 = "workit ledger decision|ruling|verdict|list|check ... (workit help
77818
78240
  return new Error("--as takes a lowercase role, e.g. verifier");
77819
78241
  const prefix = (actor.session ?? actor.host).slice(0, 96);
77820
78242
  return {
77821
- actor: { ...actor, session: `${prefix}:${values.as}:${randomBytes4(4).toString("hex")}` },
78243
+ actor: { ...actor, session: `${prefix}:${values.as}:${randomBytes5(4).toString("hex")}` },
77822
78244
  derivedFrom: actor.session
77823
78245
  };
77824
78246
  }
@@ -77841,6 +78263,7 @@ var USAGE9 = "workit ledger decision|ruling|verdict|list|check ... (workit help
77841
78263
  };
77842
78264
  var init_ledger2 = __esm(() => {
77843
78265
  init_ledger();
78266
+ init_fanout();
77844
78267
  init_rev();
77845
78268
  init_implicit_task();
77846
78269
  init_output();
@@ -77861,6 +78284,7 @@ var init_ledger2 = __esm(() => {
77861
78284
  supersedes: { type: "string" },
77862
78285
  session: { type: "string" },
77863
78286
  as: { type: "string" },
78287
+ fanout: { type: "string" },
77864
78288
  json: { type: "boolean" }
77865
78289
  };
77866
78290
  SESSION_SAFE2 = /^[A-Za-z0-9_.:@/+-]{1,128}$/;
@@ -94561,7 +94985,7 @@ var init_knowledge2 = __esm(() => {
94561
94985
  init_output();
94562
94986
  });
94563
94987
 
94564
- // packages/workit-core/src/core/youtrack-tools.ts
94988
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/youtrack-tools.ts
94565
94989
  async function logTimeUpdate(input, operation = defaultOperations) {
94566
94990
  try {
94567
94991
  const value = await operation.logTime(input);
@@ -94610,7 +95034,7 @@ var init_youtrack_tools = __esm(() => {
94610
95034
  };
94611
95035
  });
94612
95036
 
94613
- // packages/workit-core/src/core/youtrack-writes.ts
95037
+ // packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/youtrack-writes.ts
94614
95038
  async function markerPresent(issueId, marker, where) {
94615
95039
  const creds = youTrackToken();
94616
95040
  if ("error" in creds)
@@ -95009,7 +95433,7 @@ var VERBS = [
95009
95433
  {
95010
95434
  name: "pr",
95011
95435
  group: "delivery",
95012
- usage: "workit pr status [--pr <n>] | pr create [--base <b> | --track <t>] (--title <t> | --fill) [--draft] | pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch]",
95436
+ usage: "workit pr status [--pr <n>] | pr create [--base <b> | --track <t>] (--title <t> | --fill) [--draft] | pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch] [--unverified --reason <why>]",
95013
95437
  summary: "PR/MR state with failing log tails and next action; open a SHA-verified PR; merge only when READY, verified and granted",
95014
95438
  load: () => Promise.resolve().then(() => (init_pr(), exports_pr))
95015
95439
  },
@@ -95037,22 +95461,22 @@ var VERBS = [
95037
95461
  {
95038
95462
  name: "stack",
95039
95463
  group: "delivery",
95040
- usage: "workit stack plan [<bottom> … <top>] | status | sync [--local] | land [--dry-run] [--max <n>] [--json]",
95464
+ usage: "workit stack plan [<bottom> … <top>] | status | sync [--local] | land [--dry-run] [--max <n>] [--unverified --reason <why>] [--json]",
95041
95465
  summary: "Plan, restack (lease push + retarget) and land a base-branch stack of PRs, root first",
95042
95466
  load: () => Promise.resolve().then(() => (init_stack2(), exports_stack))
95043
95467
  },
95044
95468
  {
95045
95469
  name: "fanout",
95046
95470
  group: "delivery",
95047
- usage: "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --track <t>] | fanout check [<slice>…] [--name <n>] [--base <ref>] [--offline] | fanout status [--name <n>] [--stuck-after 30m] [--offline] | fanout worktree create|release <slice> [--force] [--json]",
95048
- summary: "Register parallel slices (complete briefs, disjoint scopes, shared-file owners), watch them (STUCK, PR, CI, verdict, landed, landing order), make and release slice worktrees, and gate fan-in: out-of-scope edits, trunk and sibling merge conflicts",
95471
+ usage: "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --track <t>] [--take-lead] | fanout brief <slice> [--mode new|resume] [--attempt <n>] | fanout check [<slice>…] [--name <n>] [--base <ref>] [--offline] | fanout status [--name <n>] [--stuck-after 30m] [--offline] | fanout worktree create|release <slice> [--force] [--json]",
95472
+ summary: "Register parallel slices (complete briefs, disjoint scopes, shared-file owners), render each worker's brief with the standing orders, watch them (STUCK, PR, CI, verdict, landed, landing order), make and release slice worktrees, and gate fan-in: out-of-scope edits, trunk and sibling merge conflicts",
95049
95473
  load: () => Promise.resolve().then(() => (init_fanout2(), exports_fanout))
95050
95474
  },
95051
95475
  {
95052
95476
  name: "ledger",
95053
95477
  group: "delivery",
95054
- usage: "workit ledger decision|ruling|verdict|list|check ...",
95055
- summary: "Record and query decisions, rulings and SHA-keyed verdicts (patch-id carry-over across rebases)",
95478
+ usage: "workit ledger decision|ruling|verdict|standing|list|check ...",
95479
+ summary: "Record and query decisions, rulings, SHA-keyed verdicts (patch-id carry-over across rebases) and a fanout's standing orders",
95056
95480
  load: () => Promise.resolve().then(() => (init_ledger2(), exports_ledger))
95057
95481
  },
95058
95482
  {