@open-tomato/rafa 0.36.0 → 0.37.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/cli.js CHANGED
@@ -2236,7 +2236,7 @@ function restoreRunningCommand(previous) {
2236
2236
  var running = null;
2237
2237
 
2238
2238
  // package.json
2239
- var name = "@open-tomato/rafa", version = "0.36.0";
2239
+ var name = "@open-tomato/rafa", version = "0.37.0";
2240
2240
  var init_package = () => {};
2241
2241
 
2242
2242
  // src/cli/version.ts
@@ -10361,6 +10361,64 @@ var init_claude2 = __esm(() => {
10361
10361
  };
10362
10362
  });
10363
10363
 
10364
+ // src/adapters/tracker/github-edit.ts
10365
+ function viewedEditable(payload, labelNames) {
10366
+ if (!isMapping(payload))
10367
+ return `${describeValue(payload)}, expected a mapping`;
10368
+ const { title: title2, body, state, author } = payload;
10369
+ if (typeof title2 !== "string")
10370
+ return `title ${describeValue(title2)}, expected a string`;
10371
+ if (typeof body !== "string")
10372
+ return `body ${describeValue(body)}, expected a string`;
10373
+ if (state !== "OPEN" && state !== "CLOSED")
10374
+ return `state ${describeValue(state)}, expected "OPEN" or "CLOSED"`;
10375
+ const labels = labelNames(payload["labels"]);
10376
+ if (labels === null)
10377
+ return "labels that are not a list of named labels";
10378
+ const login = isMapping(author) ? author["login"] : undefined;
10379
+ if (typeof login !== "string")
10380
+ return `author ${describeValue(author)}, expected a mapping holding a string login`;
10381
+ return { title: title2, body, open: state === "OPEN", labels, author: login };
10382
+ }
10383
+ function editProblem(change) {
10384
+ if (!isMapping(change))
10385
+ return `the change is ${describeValue(change)}, expected a mapping`;
10386
+ const { body, title: title2 } = change;
10387
+ if (body === undefined && title2 === undefined)
10388
+ return "the change names neither a body nor a title";
10389
+ if (body !== undefined && typeof body !== "string")
10390
+ return `body is ${describeValue(body)}, expected a string`;
10391
+ return title2 !== undefined && typeof title2 !== "string" ? `title is ${describeValue(title2)}, expected a string` : null;
10392
+ }
10393
+ function optionalFlag(name2, value) {
10394
+ return value === undefined ? [] : [name2, value];
10395
+ }
10396
+ function createGithubEditMembers(readers) {
10397
+ const { run, issueNumberOf, parseJson, labelNames } = readers;
10398
+ const editable = async (ref) => {
10399
+ const number = issueNumberOf(ref);
10400
+ const command = `gh issue view ${number}`;
10401
+ const stdout = await run(["issue", "view", number, ...optionalFlag("--repo", ref.repo), "--json", EDITABLE_FIELDS], command);
10402
+ const viewed = viewedEditable(parseJson(stdout, command), labelNames);
10403
+ if (typeof viewed === "string")
10404
+ throw new Error(`${PREFIX2}: ${command} answered ${viewed}`);
10405
+ return { ref, ...viewed };
10406
+ };
10407
+ const edit = async (ref, change) => {
10408
+ const number = issueNumberOf(ref);
10409
+ const problem = editProblem(change);
10410
+ if (problem !== null)
10411
+ throw new TypeError(`${PREFIX2}: refused an edit of issue #${number}: ${problem}`);
10412
+ const bodyFlag = change.body === undefined ? [] : ["--body-file", "-"];
10413
+ await run(["issue", "edit", number, ...optionalFlag("--repo", ref.repo), ...optionalFlag("--title", change.title), ...bodyFlag], `gh issue edit ${number}`, change.body);
10414
+ };
10415
+ return Object.freeze({ editable, edit });
10416
+ }
10417
+ var PREFIX2 = "github tracker", EDITABLE_FIELDS = "title,body,state,labels,author";
10418
+ var init_github_edit = __esm(() => {
10419
+ init_config_sections();
10420
+ });
10421
+
10364
10422
  // src/adapters/tracker/issue-values.ts
10365
10423
  var TYPE_MEMBERS, PRIORITY_MEMBERS, STATE_MEMBERS, ISSUE_TYPES, ISSUE_PRIORITIES, ISSUE_STATES;
10366
10424
  var init_issue_values = __esm(() => {
@@ -10397,7 +10455,7 @@ var init_issue_values = __esm(() => {
10397
10455
  // src/adapters/tracker/github.ts
10398
10456
  function createGhRunner(options) {
10399
10457
  const { cwd, command = "gh", env, timeoutMs } = options;
10400
- return async (args) => {
10458
+ return async (args, stdin) => {
10401
10459
  const executable = env === undefined ? command : Bun.which(command, { PATH: env.PATH ?? "", cwd });
10402
10460
  if (executable === null) {
10403
10461
  return { ok: false, stdout: "", stderr: notOnPath(command, cwd, env?.PATH) };
@@ -10406,7 +10464,7 @@ function createGhRunner(options) {
10406
10464
  const proc = Bun.spawn([executable, ...args], {
10407
10465
  cwd,
10408
10466
  ...env === undefined ? {} : { env: { ...env } },
10409
- stdin: "ignore",
10467
+ stdin: stdin === undefined ? "ignore" : new TextEncoder().encode(stdin),
10410
10468
  stdout: "pipe",
10411
10469
  stderr: "pipe"
10412
10470
  });
@@ -10460,7 +10518,7 @@ function isRepoName(value) {
10460
10518
  function isLabelPart(value) {
10461
10519
  return typeof value === "string" && !value.includes(",");
10462
10520
  }
10463
- function optionalFlag(name2, value) {
10521
+ function optionalFlag2(name2, value) {
10464
10522
  return value === undefined ? [] : [name2, value];
10465
10523
  }
10466
10524
  function detailOf(result) {
@@ -10484,13 +10542,13 @@ function labelsFor(draft) {
10484
10542
  }
10485
10543
  function issueNumberOfRef(ref) {
10486
10544
  if (ref.kind !== "github") {
10487
- throw new Error(`${PREFIX2}: refused a ref of kind ${describeValue(ref.kind)}; this tracker reads github refs only`);
10545
+ throw new Error(`${PREFIX3}: refused a ref of kind ${describeValue(ref.kind)}; this tracker reads github refs only`);
10488
10546
  }
10489
10547
  if (!isIssueNumber3(ref.externalId)) {
10490
- throw new Error(`${PREFIX2}: externalId ${describeValue(ref.externalId)} is not a GitHub issue number,` + " expected a positive whole number with no leading zero");
10548
+ throw new Error(`${PREFIX3}: externalId ${describeValue(ref.externalId)} is not a GitHub issue number,` + " expected a positive whole number with no leading zero");
10491
10549
  }
10492
10550
  if (ref.repo !== undefined && !isRepoName(ref.repo)) {
10493
- throw new Error(`${PREFIX2}: repo ${describeValue(ref.repo)} is not a repository, expected owner/name`);
10551
+ throw new Error(`${PREFIX3}: repo ${describeValue(ref.repo)} is not a repository, expected owner/name`);
10494
10552
  }
10495
10553
  return ref.externalId;
10496
10554
  }
@@ -10498,7 +10556,7 @@ function parseJson(stdout, command) {
10498
10556
  try {
10499
10557
  return JSON.parse(stdout);
10500
10558
  } catch (error) {
10501
- throw new Error(`${PREFIX2}: ${command} wrote output that is not JSON: ${messageOf(error)}`, {
10559
+ throw new Error(`${PREFIX3}: ${command} wrote output that is not JSON: ${messageOf(error)}`, {
10502
10560
  cause: error
10503
10561
  });
10504
10562
  }
@@ -10532,19 +10590,19 @@ function viewedIssue(stdout, number) {
10532
10590
  const payload = parseJson(stdout, command);
10533
10591
  const problem = viewProblem(payload, number);
10534
10592
  if (problem !== null)
10535
- throw new Error(`${PREFIX2}: ${command} answered ${problem}`);
10593
+ throw new Error(`${PREFIX3}: ${command} answered ${problem}`);
10536
10594
  const checked = payload;
10537
10595
  return { ...checked, labels: labelNames(checked.labels) ?? [] };
10538
10596
  }
10539
10597
  function listedRows(stdout) {
10540
10598
  const payload = parseJson(stdout, LIST_COMMAND);
10541
10599
  if (!Array.isArray(payload)) {
10542
- throw new Error(`${PREFIX2}: ${LIST_COMMAND} answered ${describeValue(payload)}, expected a list`);
10600
+ throw new Error(`${PREFIX3}: ${LIST_COMMAND} answered ${describeValue(payload)}, expected a list`);
10543
10601
  }
10544
10602
  return payload;
10545
10603
  }
10546
10604
  function rowRefusal(index, problem) {
10547
- return new Error(`${PREFIX2}: ${LIST_COMMAND} answered row ${index} ${problem}`);
10605
+ return new Error(`${PREFIX3}: ${LIST_COMMAND} answered row ${index} ${problem}`);
10548
10606
  }
10549
10607
  function listedRow(row2, index) {
10550
10608
  if (!isMapping(row2))
@@ -10617,15 +10675,15 @@ function readBackState(state) {
10617
10675
  function stateQueryRefusal(state) {
10618
10676
  const bucket = REST_STATES[state];
10619
10677
  const siblings = ISSUE_STATES.filter((other) => other !== state && REST_STATES[other] === bucket);
10620
- return `${PREFIX2}: find cannot narrow by state ${describeValue(state)}: GitHub holds an issue open or` + ` closed, and ${state} shares ${bucket} with ${siblings.join(", ")}, so a result would include` + " those too. Search without a state, then read each issue with get.";
10678
+ return `${PREFIX3}: find cannot narrow by state ${describeValue(state)}: GitHub holds an issue open or` + ` closed, and ${state} shares ${bucket} with ${siblings.join(", ")}, so a result would include` + " those too. Search without a state, then read each issue with get.";
10621
10679
  }
10622
10680
  function createGithubTracker(options) {
10623
10681
  const { gh } = options;
10624
10682
  let knownLabels = new Set;
10625
- async function run(args, command) {
10626
- const result = await gh(args);
10683
+ async function run(args, command, stdin) {
10684
+ const result = await gh(args, stdin);
10627
10685
  if (!result.ok)
10628
- throw new Error(`${PREFIX2}: ${command} failed: ${detailOf(result)}`);
10686
+ throw new Error(`${PREFIX3}: ${command} failed: ${detailOf(result)}`);
10629
10687
  return result.stdout;
10630
10688
  }
10631
10689
  async function ensureLabels(names) {
@@ -10658,7 +10716,7 @@ function createGithubTracker(options) {
10658
10716
  create: async (draft) => {
10659
10717
  const problem = draftProblem(draft);
10660
10718
  if (problem !== null)
10661
- throw new TypeError(`${PREFIX2}: refused to file an invalid draft: ${problem}`);
10719
+ throw new TypeError(`${PREFIX3}: refused to file an invalid draft: ${problem}`);
10662
10720
  const labels = labelsFor(draft);
10663
10721
  await ensureLabels(labels);
10664
10722
  const stdout = await run([
@@ -10674,20 +10732,20 @@ function createGithubTracker(options) {
10674
10732
  `).at(-1) ?? "";
10675
10733
  const number = CREATED_ISSUE_URL.exec(url)?.[1];
10676
10734
  if (number === undefined) {
10677
- throw new Error(`${PREFIX2}: gh issue create exited 0 and printed no issue URL, so the issue may exist` + ` unrecorded; it wrote ${describeValue(stdout)}`);
10735
+ throw new Error(`${PREFIX3}: gh issue create exited 0 and printed no issue URL, so the issue may exist` + ` unrecorded; it wrote ${describeValue(stdout)}`);
10678
10736
  }
10679
10737
  return { opt: draft.opt, kind: "github", externalId: number, url, module: draft.module };
10680
10738
  },
10681
10739
  get: async (ref) => {
10682
10740
  const number = issueNumberOfRef(ref);
10683
- const stdout = await run(["issue", "view", number, ...optionalFlag("--repo", ref.repo), "--json", VIEW_FIELDS], `gh issue view ${number}`);
10741
+ const stdout = await run(["issue", "view", number, ...optionalFlag2("--repo", ref.repo), "--json", VIEW_FIELDS], `gh issue view ${number}`);
10684
10742
  return issueOf2(ref, viewedIssue(stdout, number));
10685
10743
  },
10686
10744
  find: async (query) => {
10687
10745
  if (query.state !== undefined)
10688
10746
  throw new Error(stateQueryRefusal(query.state));
10689
10747
  if (query.module !== undefined && !isLabelPart(query.module)) {
10690
- throw new TypeError(`${PREFIX2}: find refused module ${describeValue(query.module)}, expected a string holding no comma`);
10748
+ throw new TypeError(`${PREFIX3}: find refused module ${describeValue(query.module)}, expected a string holding no comma`);
10691
10749
  }
10692
10750
  const moduleLabel = query.module === undefined ? undefined : `${GITHUB_LABELS.modulePrefix}${query.module}`;
10693
10751
  const typeLabel = query.type === undefined ? undefined : `${GITHUB_LABELS.typePrefix}${query.type}`;
@@ -10700,15 +10758,15 @@ function createGithubTracker(options) {
10700
10758
  LIST_FIELDS,
10701
10759
  "--limit",
10702
10760
  String(query.limit ?? DEFAULT_FIND_LIMIT),
10703
- ...optionalFlag("--label", moduleLabel),
10704
- ...optionalFlag("--label", typeLabel),
10705
- ...optionalFlag("--search", query.text)
10761
+ ...optionalFlag2("--label", moduleLabel),
10762
+ ...optionalFlag2("--label", typeLabel),
10763
+ ...optionalFlag2("--search", query.text)
10706
10764
  ], LIST_COMMAND);
10707
10765
  return listedIssues(stdout).map(refOfRow);
10708
10766
  },
10709
10767
  openIssues: async (type) => {
10710
10768
  if (!isOneOf2(ISSUE_TYPES, type)) {
10711
- throw new TypeError(`${PREFIX2}: openIssues refused type ${describeValue(type)}, expected one of: ${ISSUE_TYPES.join(", ")}`);
10769
+ throw new TypeError(`${PREFIX3}: openIssues refused type ${describeValue(type)}, expected one of: ${ISSUE_TYPES.join(", ")}`);
10712
10770
  }
10713
10771
  const stdout = await run([
10714
10772
  "issue",
@@ -10727,21 +10785,21 @@ function createGithubTracker(options) {
10727
10785
  comment: async (ref, body) => {
10728
10786
  const number = issueNumberOfRef(ref);
10729
10787
  if (typeof body !== "string") {
10730
- throw new TypeError(`${PREFIX2}: refused a comment body ${describeValue(body)}, expected a string`);
10788
+ throw new TypeError(`${PREFIX3}: refused a comment body ${describeValue(body)}, expected a string`);
10731
10789
  }
10732
- await run(["issue", "comment", number, ...optionalFlag("--repo", ref.repo), "--body", body], `gh issue comment ${number}`);
10790
+ await run(["issue", "comment", number, ...optionalFlag2("--repo", ref.repo), "--body", body], `gh issue comment ${number}`);
10733
10791
  },
10734
10792
  transition: async (ref, state) => {
10735
10793
  const number = issueNumberOfRef(ref);
10736
10794
  if (!isOneOf2(ISSUE_STATES, state)) {
10737
- throw new TypeError(`${PREFIX2}: refused state ${describeValue(state)}, expected one of: ${ISSUE_STATES.join(", ")}`);
10795
+ throw new TypeError(`${PREFIX3}: refused state ${describeValue(state)}, expected one of: ${ISSUE_STATES.join(", ")}`);
10738
10796
  }
10739
10797
  if (REST_STATES[state] === "closed") {
10740
10798
  const path = `repos/${ref.repo ?? "{owner}/{repo}"}/issues/${number}`;
10741
10799
  const reason = state === "cancelled" ? "not_planned" : "completed";
10742
10800
  await run(["api", path, "-X", "PATCH", "-f", "state=closed", "-f", `state_reason=${reason}`], `gh api PATCH ${path}`);
10743
10801
  } else {
10744
- await run(["issue", "reopen", number, ...optionalFlag("--repo", ref.repo)], `gh issue reopen ${number}`);
10802
+ await run(["issue", "reopen", number, ...optionalFlag2("--repo", ref.repo)], `gh issue reopen ${number}`);
10745
10803
  }
10746
10804
  const readBack = readBackState(state);
10747
10805
  if (readBack === state)
@@ -10749,13 +10807,15 @@ function createGithubTracker(options) {
10749
10807
  return {
10750
10808
  warning: `issue #${number} is now ${REST_STATES[state]}, but GitHub Issues without a board` + ` holds no ${state} state, so get reads it back as ${readBack}`
10751
10809
  };
10752
- }
10810
+ },
10811
+ ...createGithubEditMembers({ run, issueNumberOf: issueNumberOfRef, parseJson, labelNames })
10753
10812
  };
10754
10813
  return Object.freeze(tracker);
10755
10814
  }
10756
- var PREFIX2 = "github tracker", GITHUB_LABELS, VIEW_FIELDS = "number,title,body,state,stateReason,labels,url", LIST_COMMAND = "gh issue list", LIST_FIELDS = "number,url,labels", OPEN_LIST_FIELDS = "number,url,labels,title,body", OPEN_ISSUES_LIMIT = 1000, DEFAULT_FIND_LIMIT = 30, CANCELLED_REASONS, ISSUE_NUMBER, REPO_PART, CREATED_ISSUE_URL, REST_STATES, TIMED_OUT, DRAFT_CHECKS;
10815
+ var PREFIX3 = "github tracker", GITHUB_LABELS, VIEW_FIELDS = "number,title,body,state,stateReason,labels,url", LIST_COMMAND = "gh issue list", LIST_FIELDS = "number,url,labels", OPEN_LIST_FIELDS = "number,url,labels,title,body", OPEN_ISSUES_LIMIT = 1000, DEFAULT_FIND_LIMIT = 30, CANCELLED_REASONS, ISSUE_NUMBER, REPO_PART, CREATED_ISSUE_URL, REST_STATES, TIMED_OUT, DRAFT_CHECKS;
10757
10816
  var init_github = __esm(() => {
10758
10817
  init_config_sections();
10818
+ init_github_edit();
10759
10819
  init_issue_values();
10760
10820
  GITHUB_LABELS = Object.freeze({
10761
10821
  modulePrefix: "module:",
@@ -10882,7 +10942,7 @@ function renderLocalIssue(record) {
10882
10942
  };
10883
10943
  const problem = frontmatterProblem(frontmatter) ?? (typeof draft.body === "string" ? null : `body is ${describeValue(draft.body)}, expected a string`);
10884
10944
  if (problem !== null) {
10885
- throw new TypeError(`${PREFIX3}: refused to write an invalid issue: ${problem}`);
10945
+ throw new TypeError(`${PREFIX4}: refused to write an invalid issue: ${problem}`);
10886
10946
  }
10887
10947
  return `---
10888
10948
  ${Bun.YAML.stringify(frontmatter, null, 2)}
@@ -10893,18 +10953,18 @@ function parseLocalIssue(contents, sourcePath) {
10893
10953
  const at = sourcePath === undefined ? "" : ` at ${sourcePath}`;
10894
10954
  const match = FRONTMATTER.exec(contents);
10895
10955
  if (match === null) {
10896
- throw new Error(`${PREFIX3}: the issue${at} has no YAML frontmatter block`);
10956
+ throw new Error(`${PREFIX4}: the issue${at} has no YAML frontmatter block`);
10897
10957
  }
10898
10958
  const [, block2 = "", body = ""] = match;
10899
10959
  let meta;
10900
10960
  try {
10901
10961
  meta = Bun.YAML.parse(block2);
10902
10962
  } catch (error) {
10903
- throw new Error(`${PREFIX3}: invalid issue${at}: ${messageOf(error)}`, { cause: error });
10963
+ throw new Error(`${PREFIX4}: invalid issue${at}: ${messageOf(error)}`, { cause: error });
10904
10964
  }
10905
10965
  const problem = frontmatterProblem(meta);
10906
10966
  if (problem !== null) {
10907
- throw new Error(`${PREFIX3}: invalid issue${at}: ${problem}`);
10967
+ throw new Error(`${PREFIX4}: invalid issue${at}: ${problem}`);
10908
10968
  }
10909
10969
  const fields = meta;
10910
10970
  return {
@@ -10936,7 +10996,7 @@ async function listLocalIssues(issuesDir, warn = warnThroughActiveOutput2, reade
10936
10996
  try {
10937
10997
  contents = await readFile3(path, "utf8");
10938
10998
  } catch (error) {
10939
- throw new Error(`${PREFIX3}: could not read ${path}: ${messageOf(error)}`, { cause: error });
10999
+ throw new Error(`${PREFIX4}: could not read ${path}: ${messageOf(error)}`, { cause: error });
10940
11000
  }
10941
11001
  return { number, path, record: parseLocalIssue(contents, path) };
10942
11002
  }));
@@ -10965,11 +11025,11 @@ function refFor(number, opt) {
10965
11025
  }
10966
11026
  function issueNumberOfRef2(ref) {
10967
11027
  if (ref.kind !== "local") {
10968
- throw new Error(`${PREFIX3}: refused a ref of kind ${describeValue(ref.kind)}; this tracker reads local refs only`);
11028
+ throw new Error(`${PREFIX4}: refused a ref of kind ${describeValue(ref.kind)}; this tracker reads local refs only`);
10969
11029
  }
10970
11030
  const number = typeof ref.externalId === "string" ? parseIssueNumber(ref.externalId) : null;
10971
11031
  if (number === null) {
10972
- throw new Error(`${PREFIX3}: externalId ${describeValue(ref.externalId)} is not a local issue number,` + " expected a positive whole number with no leading zero");
11032
+ throw new Error(`${PREFIX4}: externalId ${describeValue(ref.externalId)} is not a local issue number,` + " expected a positive whole number with no leading zero");
10973
11033
  }
10974
11034
  return number;
10975
11035
  }
@@ -10984,7 +11044,7 @@ function createLocalTracker(options) {
10984
11044
  try {
10985
11045
  contents = await readFile3(path, "utf8");
10986
11046
  } catch (cause) {
10987
- throw new Error(`${PREFIX3}: no issue ${number} under ${issuesDir}`, { cause });
11047
+ throw new Error(`${PREFIX4}: no issue ${number} under ${issuesDir}`, { cause });
10988
11048
  }
10989
11049
  return { number, record: parseLocalIssue(contents, path) };
10990
11050
  }
@@ -11019,7 +11079,7 @@ function createLocalTracker(options) {
11019
11079
  throw error;
11020
11080
  }
11021
11081
  }
11022
- throw new Error(`${PREFIX3}: no issue number is left under ${issuesDir}`);
11082
+ throw new Error(`${PREFIX4}: no issue number is left under ${issuesDir}`);
11023
11083
  },
11024
11084
  get: async (ref) => {
11025
11085
  const { record } = await read(ref);
@@ -11045,11 +11105,34 @@ function createLocalTracker(options) {
11045
11105
  const { number, record } = await read(ref);
11046
11106
  await rewrite(number, { ...record, state });
11047
11107
  return {};
11108
+ },
11109
+ editable: async (ref) => {
11110
+ const { number, record } = await read(ref);
11111
+ return {
11112
+ ref: refFor(number, record.draft.opt),
11113
+ title: record.draft.title,
11114
+ body: record.draft.body,
11115
+ open: !CLOSED_STATES.has(record.state),
11116
+ labels: [],
11117
+ author: ""
11118
+ };
11119
+ },
11120
+ edit: async (ref, change) => {
11121
+ if (change.body === undefined && change.title === undefined) {
11122
+ throw new TypeError(`${PREFIX4}: refused an edit naming neither a body nor a title`);
11123
+ }
11124
+ const { number, record } = await read(ref);
11125
+ const draft = {
11126
+ ...record.draft,
11127
+ ...change.body === undefined ? {} : { body: change.body },
11128
+ ...change.title === undefined ? {} : { title: change.title }
11129
+ };
11130
+ await rewrite(number, { ...record, draft });
11048
11131
  }
11049
11132
  };
11050
11133
  return Object.freeze(tracker);
11051
11134
  }
11052
- var PREFIX3 = "local tracker", FRONTMATTER, ISSUE_NUMBER2, CLOSED_STATES, ISSUE_EXTENSION = ".md", FIELD_CHECKS;
11135
+ var PREFIX4 = "local tracker", FRONTMATTER, ISSUE_NUMBER2, CLOSED_STATES, ISSUE_EXTENSION = ".md", FIELD_CHECKS;
11053
11136
  var init_local2 = __esm(() => {
11054
11137
  init_config_sections();
11055
11138
  init_active();
@@ -13319,7 +13402,60 @@ function requireTrustedAuthor(item, reading) {
13319
13402
  return;
13320
13403
  throw new CommandExit(TRUST_REFUSAL_EXIT, trustRefusalMessage(item, reading));
13321
13404
  }
13322
- var REPO_PATH = "{owner}/{repo}", TRUSTED_PERMISSIONS, TRUST_REFUSAL_EXIT = 2, REMEDY, UNREPORTED = "the lookup wrote nothing";
13405
+ function noLogin(detail) {
13406
+ return { login: null, detail };
13407
+ }
13408
+ function createGhEditorLogin(options) {
13409
+ const { gh } = options;
13410
+ return async () => {
13411
+ const result = await gh(["api", "user"]);
13412
+ if (!result.ok)
13413
+ return noLogin(detailOf2(result, USER_COMMAND));
13414
+ let payload;
13415
+ try {
13416
+ payload = JSON.parse(result.stdout);
13417
+ } catch (error) {
13418
+ return noLogin(`${USER_COMMAND} wrote output that is not JSON: ${messageOf(error)}`);
13419
+ }
13420
+ if (!isMapping(payload)) {
13421
+ return noLogin(`${USER_COMMAND} answered ${describeValue(payload)}, expected a mapping`);
13422
+ }
13423
+ const login = payload["login"];
13424
+ return typeof login === "string" && isGitHubLogin(login) ? { login, detail: "" } : noLogin(`${USER_COMMAND} answered ${describeValue(login)} as its login, which is not a GitHub login`);
13425
+ };
13426
+ }
13427
+ async function readEditorTrust(trust, editor) {
13428
+ const read = await editor();
13429
+ if (read.login === null) {
13430
+ return { login: null, trusted: false, refusal: "ownership-unknown", reading: null, detail: read.detail };
13431
+ }
13432
+ const reading = await readBoardTrust(trust, read.login);
13433
+ return {
13434
+ login: read.login,
13435
+ trusted: reading.trusted,
13436
+ refusal: editorRefusalOf(reading),
13437
+ reading,
13438
+ detail: ""
13439
+ };
13440
+ }
13441
+ function editorRefusalOf(reading) {
13442
+ if (reading.trusted)
13443
+ return null;
13444
+ return reading.refusal === "lookup-failed" ? "ownership-unknown" : "ownership";
13445
+ }
13446
+ function editorRefusalMessage(item, reading) {
13447
+ const what = `${item.kind} #${String(item.number)}`;
13448
+ if (reading.reading !== null) {
13449
+ const because = trustRefusalClause(item.repo, reading.reading);
13450
+ return `${what} cannot be edited by ${reading.reading.login}, ${because}; a member must make the edit`;
13451
+ }
13452
+ if (reading.trusted) {
13453
+ throw new TypeError("board trust: a trusted editor has no refusal to name");
13454
+ }
13455
+ const detail = reading.detail === "" ? UNREPORTED : reading.detail;
13456
+ return `${what} cannot be edited: the account running the edit could not be read (${detail}); ` + "log gh in with `gh auth login` and run it again";
13457
+ }
13458
+ var REPO_PATH = "{owner}/{repo}", TRUSTED_PERMISSIONS, TRUST_REFUSAL_EXIT = 2, REMEDY, UNREPORTED = "the lookup wrote nothing", USER_COMMAND = "gh api user";
13323
13459
  var init_trust = __esm(() => {
13324
13460
  init_command();
13325
13461
  init_config_sections();
@@ -16070,7 +16206,7 @@ function optionalText2(value) {
16070
16206
  function requiredText2(value) {
16071
16207
  return value === null ? { reason: "missing-field", problem: "is missing" } : optionalText2(value);
16072
16208
  }
16073
- function optionalFlag2(value) {
16209
+ function optionalFlag3(value) {
16074
16210
  return value === null || typeof value === "boolean" ? null : { reason: "unstorable-field", problem: `is ${describeValue7(value)}, not a boolean` };
16075
16211
  }
16076
16212
  function scopeOf2(bug) {
@@ -16163,7 +16299,7 @@ var init_triage = __esm(() => {
16163
16299
  };
16164
16300
  OUT_OF_SCOPE_BUGS = {
16165
16301
  key: "out_of_scope_bugs",
16166
- checks: [["what", requiredText2], ["artifact", optionalText2], ["security", optionalFlag2]],
16302
+ checks: [["what", requiredText2], ["artifact", optionalText2], ["security", optionalFlag3]],
16167
16303
  insert: `
16168
16304
  INSERT INTO out_of_scope_bugs (
16169
16305
  id, session_id, plan_stub, task_line,
@@ -18462,6 +18598,9 @@ function notesFileName(issue4) {
18462
18598
  function notesPath(specsDir, issue4) {
18463
18599
  return path.join(specsDir, notesFileName(issue4));
18464
18600
  }
18601
+ function branchName(issue4, title2) {
18602
+ return `${BRANCH_PREFIX}/${planStub(issue4, title2)}`;
18603
+ }
18465
18604
  function pullRequestTitle(issue4, title2) {
18466
18605
  const id = boardId(issue4);
18467
18606
  const spoken = title2.replace(LEADING_ID, "").trim();
@@ -18544,7 +18683,7 @@ function movePreviousCopy(options) {
18544
18683
  mkdirSync9(absoluteDirectory, { recursive: true });
18545
18684
  name2 = linkUnderFreeName(source, absoluteDirectory, savedName, timestamp);
18546
18685
  } catch (error) {
18547
- throw new Error(`${PREFIX4}: ${path2} could not be moved to ${directory2}: ${messageOf(error)}`, { cause: error });
18686
+ throw new Error(`${PREFIX5}: ${path2} could not be moved to ${directory2}: ${messageOf(error)}`, { cause: error });
18548
18687
  }
18549
18688
  const moved = {
18550
18689
  path: join32(directory2, name2),
@@ -18553,7 +18692,7 @@ function movePreviousCopy(options) {
18553
18692
  try {
18554
18693
  unlinkSync(source);
18555
18694
  } catch (error) {
18556
- throw new Error(`${PREFIX4}: ${path2} was copied to ${moved.path} but could not be removed: ${messageOf(error)}`, { cause: error });
18695
+ throw new Error(`${PREFIX5}: ${path2} was copied to ${moved.path} but could not be removed: ${messageOf(error)}`, { cause: error });
18557
18696
  }
18558
18697
  return moved;
18559
18698
  }
@@ -18567,10 +18706,10 @@ function countPreviousCopies(repoRoot, specsDir) {
18567
18706
  } catch (error) {
18568
18707
  if (isMissing(error))
18569
18708
  return 0;
18570
- throw new Error(`${PREFIX4}: ${directory2} could not be read: ${messageOf(error)}`, { cause: error });
18709
+ throw new Error(`${PREFIX5}: ${directory2} could not be read: ${messageOf(error)}`, { cause: error });
18571
18710
  }
18572
18711
  }
18573
- var PREFIX4 = "board previous copy", PREVIOUS_DIR_NAME = "previous", PREVIOUS_COPY_WARN_ABOVE = 50, COPY_EXTENSION = ".md", FIRST_SUFFIX = 2;
18712
+ var PREFIX5 = "board previous copy", PREVIOUS_DIR_NAME = "previous", PREVIOUS_COPY_WARN_ABOVE = 50, COPY_EXTENSION = ".md", FIRST_SUFFIX = 2;
18574
18713
  var init_previous_copy = __esm(() => {
18575
18714
  init_config_sections();
18576
18715
  });
@@ -18584,7 +18723,7 @@ function detailOf3(result, command) {
18584
18723
  }
18585
18724
  function issueNumber2(value) {
18586
18725
  if (!Number.isSafeInteger(value) || value < 1) {
18587
- throw new TypeError(`${PREFIX5}: the read refused issue number ${describeValue(value)}, expected a positive whole number`);
18726
+ throw new TypeError(`${PREFIX6}: the read refused issue number ${describeValue(value)}, expected a positive whole number`);
18588
18727
  }
18589
18728
  return String(value);
18590
18729
  }
@@ -18619,11 +18758,11 @@ function readViewedIssue(stdout, number, command) {
18619
18758
  try {
18620
18759
  payload = JSON.parse(stdout);
18621
18760
  } catch (error) {
18622
- throw new Error(`${PREFIX5}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
18761
+ throw new Error(`${PREFIX6}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
18623
18762
  }
18624
18763
  const problem = viewProblem2(payload, number);
18625
18764
  if (problem !== null)
18626
- throw new Error(`${PREFIX5}: ${command} answered ${problem}`);
18765
+ throw new Error(`${PREFIX6}: ${command} answered ${problem}`);
18627
18766
  const checked = payload;
18628
18767
  return {
18629
18768
  number,
@@ -18641,7 +18780,7 @@ function createGhSpecIssueReader(options) {
18641
18780
  const command = `gh issue view ${number} --json ${ISSUE_VIEW_FIELDS}`;
18642
18781
  const result = await gh(["issue", "view", number, "--json", ISSUE_VIEW_FIELDS]);
18643
18782
  if (!result.ok)
18644
- throw new Error(`${PREFIX5}: ${detailOf3(result, command)}`);
18783
+ throw new Error(`${PREFIX6}: ${detailOf3(result, command)}`);
18645
18784
  return readViewedIssue(result.stdout, issue4, command);
18646
18785
  };
18647
18786
  }
@@ -18805,7 +18944,7 @@ function writeSpecSnapshot(options) {
18805
18944
  mkdirSync10(dirname19(absolute), { recursive: true });
18806
18945
  writeFileSync9(absolute, text2);
18807
18946
  } catch (error) {
18808
- throw new Error(`${PREFIX5}: the snapshot ${path2} could not be written: ${messageOf(error)}`, { cause: error });
18947
+ throw new Error(`${PREFIX6}: the snapshot ${path2} could not be written: ${messageOf(error)}`, { cause: error });
18809
18948
  }
18810
18949
  return { ...written, action, previous };
18811
18950
  }
@@ -18819,7 +18958,7 @@ function rewriteAction(existing, body, notes, notesFile, refresh) {
18819
18958
  return "notes-rebuilt";
18820
18959
  return refresh ? "refreshed" : null;
18821
18960
  }
18822
- var PREFIX5 = "board issue", ISSUE_VIEW_FIELDS = "number,title,body,state,labels,author", SPEC_LABEL = "type:spec", ISSUE_REFUSAL_EXIT = 2, LOCAL_NOTES_HEADING = "## Local notes", NOTES_SEPARATOR, HEADING, FENCE2;
18961
+ var PREFIX6 = "board issue", ISSUE_VIEW_FIELDS = "number,title,body,state,labels,author", SPEC_LABEL = "type:spec", ISSUE_REFUSAL_EXIT = 2, LOCAL_NOTES_HEADING = "## Local notes", NOTES_SEPARATOR, HEADING, FENCE2;
18823
18962
  var init_issue = __esm(() => {
18824
18963
  init_command();
18825
18964
  init_config_sections();
@@ -19168,7 +19307,7 @@ var init_none = __esm(() => {
19168
19307
 
19169
19308
  // src/pr/gh-closing-issues.ts
19170
19309
  function refuse3(command, problem) {
19171
- throw new Error(`${PREFIX6}: ${command} answered ${problem}`);
19310
+ throw new Error(`${PREFIX7}: ${command} answered ${problem}`);
19172
19311
  }
19173
19312
  function readMapping(value, command, where) {
19174
19313
  if (!isMapping(value))
@@ -19209,7 +19348,7 @@ function readClosingIssues(value, command, where) {
19209
19348
  refuse3(command, `${where} is ${describeValue(value)}, expected a list`);
19210
19349
  return value.map((reference2, index) => readClosingIssue(reference2, command, `${where}[${index}]`));
19211
19350
  }
19212
- var CLOSING_ISSUES_FIELD = "closingIssuesReferences", PREFIX6 = "gh pull requests";
19351
+ var CLOSING_ISSUES_FIELD = "closingIssuesReferences", PREFIX7 = "gh pull requests";
19213
19352
  var init_gh_closing_issues = __esm(() => {
19214
19353
  init_config_sections();
19215
19354
  });
@@ -19222,16 +19361,16 @@ function detailOf4(result, command) {
19222
19361
  return result.ok ? `${command} exited 0 and wrote nothing` : `${command} exited non-zero and wrote nothing`;
19223
19362
  }
19224
19363
  function failure2(command, result) {
19225
- return new Error(`${PREFIX7}: ${command} failed: ${detailOf4(result, command)}`);
19364
+ return new Error(`${PREFIX8}: ${command} failed: ${detailOf4(result, command)}`);
19226
19365
  }
19227
19366
  function refuse4(command, problem) {
19228
- throw new Error(`${PREFIX7}: ${command} answered ${problem}`);
19367
+ throw new Error(`${PREFIX8}: ${command} answered ${problem}`);
19229
19368
  }
19230
19369
  function parseJson2(stdout, command) {
19231
19370
  try {
19232
19371
  return JSON.parse(stdout);
19233
19372
  } catch (error) {
19234
- throw new Error(`${PREFIX7}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
19373
+ throw new Error(`${PREFIX8}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
19235
19374
  }
19236
19375
  }
19237
19376
  function readString3(value, command, where) {
@@ -19371,7 +19510,7 @@ function readWorkflowCount(stdout) {
19371
19510
  }
19372
19511
  function pullNumber(value, member) {
19373
19512
  if (!Number.isSafeInteger(value) || value < 1) {
19374
- throw new TypeError(`${PREFIX7}: ${member} refused pull request number ${describeValue(value)},` + " expected a positive whole number");
19513
+ throw new TypeError(`${PREFIX8}: ${member} refused pull request number ${describeValue(value)},` + " expected a positive whole number");
19375
19514
  }
19376
19515
  return String(value);
19377
19516
  }
@@ -19379,13 +19518,13 @@ function textArgument(value, member, what, whole) {
19379
19518
  const bad = typeof value !== "string" || value === "" || whole && !(WHOLE_NUMBER.test(value) && Number.isSafeInteger(Number(value)));
19380
19519
  if (bad) {
19381
19520
  const expected2 = whole ? "a positive whole number with no leading zero" : "a non-empty string";
19382
- throw new TypeError(`${PREFIX7}: ${member} refused ${what} ${describeValue(value)}, expected ${expected2}`);
19521
+ throw new TypeError(`${PREFIX8}: ${member} refused ${what} ${describeValue(value)}, expected ${expected2}`);
19383
19522
  }
19384
19523
  return value;
19385
19524
  }
19386
19525
  function bodyText(value, member) {
19387
19526
  if (typeof value !== "string") {
19388
- throw new TypeError(`${PREFIX7}: ${member} refused a body ${describeValue(value)}, expected a string`);
19527
+ throw new TypeError(`${PREFIX8}: ${member} refused a body ${describeValue(value)}, expected a string`);
19389
19528
  }
19390
19529
  return value;
19391
19530
  }
@@ -19483,7 +19622,7 @@ function createGhPullRequests(options) {
19483
19622
  merge: async (number, method) => {
19484
19623
  const target = pullNumber(number, "merge");
19485
19624
  if (!isMergeMethod(method)) {
19486
- throw new TypeError(`${PREFIX7}: merge refused method ${describeValue(method)},` + ` expected one of: ${MERGE_METHODS.join(", ")}`);
19625
+ throw new TypeError(`${PREFIX8}: merge refused method ${describeValue(method)},` + ` expected one of: ${MERGE_METHODS.join(", ")}`);
19487
19626
  }
19488
19627
  const command = `gh pr merge ${target} --${method}`;
19489
19628
  const result = await gh(["pr", "merge", target, `--${method}`]);
@@ -19565,7 +19704,7 @@ function ghPullRequestsIn(cwd) {
19565
19704
  function ghAuthOkIn(cwd) {
19566
19705
  return ghAuthOk(createGhRunner({ cwd }));
19567
19706
  }
19568
- var PREFIX7 = "gh pull requests", REPO_PATH2 = "{owner}/{repo}", SUMMARY_FIELDS = "author,baseRefName,headRefName,isCrossRepository,number,state,title,updatedAt,url", DETAIL_FIELDS, CHECK_FIELDS = "name,state,link", MERGED_FIELDS = "headRefName,headRefOid,mergedAt,number", FILES_FIELDS = "changedFiles,files", REVIEW_FIELDS = "reviews", WORKFLOWS_PATH, LIST_LIMIT = 30, MERGED_LIMIT = 100, FIND_LIMIT = 1, MISSING_PULL = "Could not resolve to a PullRequest", CREATED_URL, NO_CHECKS = "no checks reported on the", LOG_DROPPED = "log not found", WHOLE_NUMBER, STATES, MERGEABILITY;
19707
+ var PREFIX8 = "gh pull requests", REPO_PATH2 = "{owner}/{repo}", SUMMARY_FIELDS = "author,baseRefName,headRefName,isCrossRepository,number,state,title,updatedAt,url", DETAIL_FIELDS, CHECK_FIELDS = "name,state,link", MERGED_FIELDS = "headRefName,headRefOid,mergedAt,number", FILES_FIELDS = "changedFiles,files", REVIEW_FIELDS = "reviews", WORKFLOWS_PATH, LIST_LIMIT = 30, MERGED_LIMIT = 100, FIND_LIMIT = 1, MISSING_PULL = "Could not resolve to a PullRequest", CREATED_URL, NO_CHECKS = "no checks reported on the", LOG_DROPPED = "log not found", WHOLE_NUMBER, STATES, MERGEABILITY;
19569
19708
  var init_gh = __esm(() => {
19570
19709
  init_github();
19571
19710
  init_config_sections();
@@ -20144,24 +20283,24 @@ function parseRows(stdout, command) {
20144
20283
  try {
20145
20284
  payload = JSON.parse(stdout);
20146
20285
  } catch (error) {
20147
- throw new Error(`${PREFIX8}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
20286
+ throw new Error(`${PREFIX9}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
20148
20287
  }
20149
20288
  if (!Array.isArray(payload)) {
20150
- throw new Error(`${PREFIX8}: ${command} answered ${describeValue(payload)}, expected a list`);
20289
+ throw new Error(`${PREFIX9}: ${command} answered ${describeValue(payload)}, expected a list`);
20151
20290
  }
20152
20291
  return payload;
20153
20292
  }
20154
20293
  function rowNumber(row2, key, command, where) {
20155
20294
  const value = isMapping(row2) ? row2[key] : null;
20156
20295
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1) {
20157
- throw new Error(`${PREFIX8}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a positive whole number`);
20296
+ throw new Error(`${PREFIX9}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a positive whole number`);
20158
20297
  }
20159
20298
  return value;
20160
20299
  }
20161
20300
  function rowText(row2, key, command, where) {
20162
20301
  const value = isMapping(row2) ? row2[key] : null;
20163
20302
  if (typeof value !== "string") {
20164
- throw new Error(`${PREFIX8}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a string`);
20303
+ throw new Error(`${PREFIX9}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a string`);
20165
20304
  }
20166
20305
  return value;
20167
20306
  }
@@ -20173,7 +20312,7 @@ function createGhRoadmapSearch(options) {
20173
20312
  return async () => {
20174
20313
  const result = await gh(args);
20175
20314
  if (!result.ok)
20176
- throw new Error(`${PREFIX8}: ${detailOf5(result, command)}`);
20315
+ throw new Error(`${PREFIX9}: ${detailOf5(result, command)}`);
20177
20316
  return Object.freeze(parseRows(result.stdout, command).map((row2, index) => ({
20178
20317
  number: rowNumber(row2, "number", command, `issue ${String(index)}`),
20179
20318
  title: rowText(row2, "title", command, `issue ${String(index)}`)
@@ -20210,7 +20349,7 @@ function createGhOpenPullRequests(options) {
20210
20349
  return async () => {
20211
20350
  const result = await gh(args);
20212
20351
  if (!result.ok)
20213
- throw new Error(`${PREFIX8}: ${detailOf5(result, command)}`);
20352
+ throw new Error(`${PREFIX9}: ${detailOf5(result, command)}`);
20214
20353
  return Object.freeze(parseRows(result.stdout, command).map((row2, index) => ({
20215
20354
  number: rowNumber(row2, "number", command, `pull request ${String(index)}`),
20216
20355
  headRefName: rowText(row2, "headRefName", command, `pull request ${String(index)}`),
@@ -20332,7 +20471,7 @@ function exhaustedMessage(roadmap, skipped2) {
20332
20471
  const id = `issue #${String(roadmap)}`;
20333
20472
  return skipped2.length === 0 ? `the roadmap, ${id}, carries no "- [ ] #<n>" line; add the next spec to its task list` : `every line of the roadmap, ${id}, is done or taken (${String(skipped2.length)} of them);` + " open the next spec issue and add it to the task list";
20334
20473
  }
20335
- var PREFIX8 = "board roadmap", ROADMAP_TITLE = "Roadmap", ROADMAP_SETTING = "roadmap.issue", ROADMAP_REFUSAL_EXIT = 2, DEFAULT_REMOTE2 = "origin", OPEN_PULL_REQUEST_LIMIT = 100, PR_LIST_FIELDS = "number,headRefName,body", ISSUE_LIST_FIELDS = "number,title", TASK_LINE, FENCE3, WHY_SEPARATOR, CLOSING;
20474
+ var PREFIX9 = "board roadmap", ROADMAP_TITLE = "Roadmap", ROADMAP_SETTING = "roadmap.issue", ROADMAP_REFUSAL_EXIT = 2, DEFAULT_REMOTE2 = "origin", OPEN_PULL_REQUEST_LIMIT = 100, PR_LIST_FIELDS = "number,headRefName,body", ISSUE_LIST_FIELDS = "number,title", TASK_LINE, FENCE3, WHY_SEPARATOR, CLOSING;
20336
20475
  var init_roadmap = __esm(() => {
20337
20476
  init_command();
20338
20477
  init_config_schema();
@@ -20426,24 +20565,24 @@ function parseRows2(stdout, command) {
20426
20565
  try {
20427
20566
  payload = JSON.parse(stdout);
20428
20567
  } catch (error) {
20429
- throw new Error(`${PREFIX9}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
20568
+ throw new Error(`${PREFIX10}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
20430
20569
  }
20431
20570
  if (!Array.isArray(payload)) {
20432
- throw new Error(`${PREFIX9}: ${command} answered ${describeValue(payload)}, expected a list`);
20571
+ throw new Error(`${PREFIX10}: ${command} answered ${describeValue(payload)}, expected a list`);
20433
20572
  }
20434
20573
  return payload;
20435
20574
  }
20436
20575
  function rowText2(row2, key, command, where) {
20437
20576
  const value = isMapping(row2) ? row2[key] : null;
20438
20577
  if (typeof value !== "string") {
20439
- throw new Error(`${PREFIX9}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a string`);
20578
+ throw new Error(`${PREFIX10}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a string`);
20440
20579
  }
20441
20580
  return value;
20442
20581
  }
20443
20582
  function rowNumber2(row2, key, command, where) {
20444
20583
  const value = isMapping(row2) ? row2[key] : null;
20445
20584
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1) {
20446
- throw new Error(`${PREFIX9}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a positive whole number`);
20585
+ throw new Error(`${PREFIX10}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a positive whole number`);
20447
20586
  }
20448
20587
  return value;
20449
20588
  }
@@ -20453,7 +20592,7 @@ async function listBoardLabels(gh) {
20453
20592
  const command = `gh label list --limit ${limit} --json name`;
20454
20593
  const result = await gh(args);
20455
20594
  if (!result.ok)
20456
- throw new Error(`${PREFIX9}: ${detailOf6(result, command)}`);
20595
+ throw new Error(`${PREFIX10}: ${detailOf6(result, command)}`);
20457
20596
  return Object.freeze(parseRows2(result.stdout, command).map((row2, index) => rowText2(row2, "name", command, `label ${String(index)}`)));
20458
20597
  }
20459
20598
  function holdsLabel(held, name2) {
@@ -20505,7 +20644,7 @@ function specTemplateSource(moduleDir = MODULE_DIR) {
20505
20644
  function readSpecTemplate(moduleDir = MODULE_DIR) {
20506
20645
  const source = specTemplateSource(moduleDir);
20507
20646
  if (!existsSync29(source)) {
20508
- throw new Error(`${PREFIX9}: the spec issue template is missing: no file at ${source}`);
20647
+ throw new Error(`${PREFIX10}: the spec issue template is missing: no file at ${source}`);
20509
20648
  }
20510
20649
  return readFileSync26(source, "utf8");
20511
20650
  }
@@ -20598,7 +20737,7 @@ function roadmapRefused(why) {
20598
20737
  function rowLabels(row2, command, where) {
20599
20738
  const value = isMapping(row2) ? row2["labels"] : null;
20600
20739
  if (!Array.isArray(value)) {
20601
- throw new Error(`${PREFIX9}: ${command} answered ${where}.labels as ${describeValue(value)}, expected a list`);
20740
+ throw new Error(`${PREFIX10}: ${command} answered ${where}.labels as ${describeValue(value)}, expected a list`);
20602
20741
  }
20603
20742
  return value.map((label, index) => rowText2(label, "name", command, `${where}.labels ${String(index)}`));
20604
20743
  }
@@ -20608,7 +20747,7 @@ async function searchRoadmapIssues(gh) {
20608
20747
  const command = `gh issue list --state open --search "${search}" --json number,title,labels`;
20609
20748
  const result = await gh(args);
20610
20749
  if (!result.ok)
20611
- throw new Error(`${PREFIX9}: ${detailOf6(result, command)}`);
20750
+ throw new Error(`${PREFIX10}: ${detailOf6(result, command)}`);
20612
20751
  const wanted = ROADMAP_TITLE.toLowerCase();
20613
20752
  return Object.freeze(parseRows2(result.stdout, command).map((row2, index) => ({
20614
20753
  number: rowNumber2(row2, "number", command, `issue ${String(index)}`),
@@ -20620,12 +20759,12 @@ async function createRoadmapIssue(gh) {
20620
20759
  const command = `gh issue create --title ${ROADMAP_TITLE} --label ${ROADMAP_LABEL}`;
20621
20760
  const result = await gh(["issue", "create", "--title", ROADMAP_TITLE, "--label", ROADMAP_LABEL, "--body", roadmapIssueBody()]);
20622
20761
  if (!result.ok)
20623
- throw new Error(`${PREFIX9}: ${detailOf6(result, command)}`);
20762
+ throw new Error(`${PREFIX10}: ${detailOf6(result, command)}`);
20624
20763
  const url = result.stdout.trim().split(`
20625
20764
  `).at(-1) ?? "";
20626
20765
  const number = CREATED_ISSUE_URL2.exec(url)?.[1];
20627
20766
  if (number === undefined) {
20628
- throw new Error(`${PREFIX9}: ${command} exited 0 and printed no issue URL, so the issue may exist` + ` unrecorded; it wrote ${describeValue(result.stdout)}`);
20767
+ throw new Error(`${PREFIX10}: ${command} exited 0 and printed no issue URL, so the issue may exist` + ` unrecorded; it wrote ${describeValue(result.stdout)}`);
20629
20768
  }
20630
20769
  return Number.parseInt(number, 10);
20631
20770
  }
@@ -20704,7 +20843,7 @@ async function setUpBoard(options) {
20704
20843
  problems: Object.freeze([...roadmap.problems])
20705
20844
  });
20706
20845
  }
20707
- var PREFIX9 = "board setup", MODULE_DIR, ROADMAP_LABEL, BOARD_LABELS, LABEL_LIST_LIMIT = 100, SPEC_TEMPLATE_PATH, TEMPLATES_DIRNAME = "templates", SPEC_TEMPLATE_FILE = "spec.md", CREATED_ISSUE_URL2, ISSUE_PART_NAME;
20846
+ var PREFIX10 = "board setup", MODULE_DIR, ROADMAP_LABEL, BOARD_LABELS, LABEL_LIST_LIMIT = 100, SPEC_TEMPLATE_PATH, TEMPLATES_DIRNAME = "templates", SPEC_TEMPLATE_FILE = "spec.md", CREATED_ISSUE_URL2, ISSUE_PART_NAME;
20708
20847
  var init_setup = __esm(() => {
20709
20848
  init_github();
20710
20849
  init_stale();
@@ -20792,47 +20931,47 @@ function detailOf7(result, command) {
20792
20931
  return written === "" ? `${command} failed and wrote nothing` : `${command} failed: ${written}`;
20793
20932
  }
20794
20933
  function failure3(command, result) {
20795
- return new Error(`${PREFIX10}: ${detailOf7(result, command)}`);
20934
+ return new Error(`${PREFIX11}: ${detailOf7(result, command)}`);
20796
20935
  }
20797
20936
  function issueNumber3(value, member) {
20798
20937
  if (!Number.isSafeInteger(value) || value < 1) {
20799
- throw new TypeError(`${PREFIX10}: ${member} refused issue number ${describeValue(value)}, expected a positive whole number`);
20938
+ throw new TypeError(`${PREFIX11}: ${member} refused issue number ${describeValue(value)}, expected a positive whole number`);
20800
20939
  }
20801
20940
  return String(value);
20802
20941
  }
20803
20942
  function commentId(value) {
20804
20943
  if (typeof value !== "string" || !COMMENT_ID.test(value)) {
20805
- throw new TypeError(`${PREFIX10}: editComment refused comment id ${describeValue(value)},` + " expected a positive whole number with no leading zero");
20944
+ throw new TypeError(`${PREFIX11}: editComment refused comment id ${describeValue(value)},` + " expected a positive whole number with no leading zero");
20806
20945
  }
20807
20946
  return value;
20808
20947
  }
20809
20948
  function commentBody2(value, member) {
20810
20949
  if (typeof value !== "string") {
20811
- throw new TypeError(`${PREFIX10}: ${member} refused a body ${describeValue(value)}, expected a string`);
20950
+ throw new TypeError(`${PREFIX11}: ${member} refused a body ${describeValue(value)}, expected a string`);
20812
20951
  }
20813
20952
  return value;
20814
20953
  }
20815
20954
  function closeReason(value) {
20816
20955
  if (!CLOSE_REASONS.includes(value)) {
20817
- throw new TypeError(`${PREFIX10}: closeIssue refused reason ${describeValue(value)}, expected one of ${CLOSE_REASONS.join(", ")}`);
20956
+ throw new TypeError(`${PREFIX11}: closeIssue refused reason ${describeValue(value)}, expected one of ${CLOSE_REASONS.join(", ")}`);
20818
20957
  }
20819
20958
  return value;
20820
20959
  }
20821
20960
  function closeComment(value, member = "closeIssue") {
20822
20961
  if (commentBody2(value, member).trim() === "") {
20823
- throw new TypeError(`${PREFIX10}: ${member} refused an empty comment, every close leaves one`);
20962
+ throw new TypeError(`${PREFIX11}: ${member} refused an empty comment, every close leaves one`);
20824
20963
  }
20825
20964
  return value;
20826
20965
  }
20827
20966
  function labelName(value, member, what) {
20828
20967
  if (typeof value !== "string" || value === "" || value.startsWith("-")) {
20829
- throw new TypeError(`${PREFIX10}: ${member} refused ${what} ${describeValue(value)},` + " expected a non-empty name that does not open with a hyphen");
20968
+ throw new TypeError(`${PREFIX11}: ${member} refused ${what} ${describeValue(value)},` + " expected a non-empty name that does not open with a hyphen");
20830
20969
  }
20831
20970
  return value;
20832
20971
  }
20833
20972
  function issueTitle(value) {
20834
20973
  if (typeof value !== "string" || value.trim() === "" || /[\r\n]/u.test(value)) {
20835
- throw new TypeError(`${PREFIX10}: createIssue refused the title ${describeValue(value)}, expected one line holding some text`);
20974
+ throw new TypeError(`${PREFIX11}: createIssue refused the title ${describeValue(value)}, expected one line holding some text`);
20836
20975
  }
20837
20976
  return value;
20838
20977
  }
@@ -20841,7 +20980,7 @@ function createdIssue(stdout) {
20841
20980
  `).at(-1)?.trim() ?? "";
20842
20981
  const number = CREATED_ISSUE_URL2.exec(url)?.[1];
20843
20982
  if (number === undefined) {
20844
- throw new Error(`${PREFIX10}: gh issue create exited 0 and printed no issue URL, so the issue may exist` + ` unrecorded; it wrote ${describeValue(stdout)}`);
20983
+ throw new Error(`${PREFIX11}: gh issue create exited 0 and printed no issue URL, so the issue may exist` + ` unrecorded; it wrote ${describeValue(stdout)}`);
20845
20984
  }
20846
20985
  return Object.freeze({ number: Number(number), url });
20847
20986
  }
@@ -20849,20 +20988,20 @@ function parseJson3(stdout, command) {
20849
20988
  try {
20850
20989
  return JSON.parse(stdout);
20851
20990
  } catch (error) {
20852
- throw new Error(`${PREFIX10}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
20991
+ throw new Error(`${PREFIX11}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
20853
20992
  }
20854
20993
  }
20855
20994
  function readComment2(value, command, where) {
20856
20995
  if (!isMapping(value)) {
20857
- throw new Error(`${PREFIX10}: ${command} answered ${where} as ${describeValue(value)}, expected a mapping`);
20996
+ throw new Error(`${PREFIX11}: ${command} answered ${where} as ${describeValue(value)}, expected a mapping`);
20858
20997
  }
20859
20998
  const id = value["id"];
20860
20999
  if (typeof id !== "number" || !Number.isSafeInteger(id) || id < 1) {
20861
- throw new Error(`${PREFIX10}: ${command} answered ${where}.id as ${describeValue(id)}, expected a positive whole number`);
21000
+ throw new Error(`${PREFIX11}: ${command} answered ${where}.id as ${describeValue(id)}, expected a positive whole number`);
20862
21001
  }
20863
21002
  const body = value["body"];
20864
21003
  if (typeof body !== "string") {
20865
- throw new Error(`${PREFIX10}: ${command} answered ${where}.body as ${describeValue(body)}, expected a string`);
21004
+ throw new Error(`${PREFIX11}: ${command} answered ${where}.body as ${describeValue(body)}, expected a string`);
20866
21005
  }
20867
21006
  const user = value["user"];
20868
21007
  const login = isMapping(user) ? user["login"] : null;
@@ -20887,7 +21026,7 @@ function createGhIssueBoard(options) {
20887
21026
  const command = `gh api ${path2}`;
20888
21027
  const rows2 = parseJson3(await succeed(["api", path2], command), command);
20889
21028
  if (!Array.isArray(rows2)) {
20890
- throw new Error(`${PREFIX10}: ${command} answered ${describeValue(rows2)}, expected a list of comments`);
21029
+ throw new Error(`${PREFIX11}: ${command} answered ${describeValue(rows2)}, expected a list of comments`);
20891
21030
  }
20892
21031
  return rows2.map((row2, index) => readComment2(row2, command, `comment ${String(index)}`));
20893
21032
  },
@@ -20947,7 +21086,7 @@ function createGhIssueBoard(options) {
20947
21086
  };
20948
21087
  return Object.freeze(board);
20949
21088
  }
20950
- var PREFIX10 = "board issue", REPO_PATH3 = "{owner}/{repo}", COMMENT_ID, CLOSE_REASONS;
21089
+ var PREFIX11 = "board issue", REPO_PATH3 = "{owner}/{repo}", COMMENT_ID, CLOSE_REASONS;
20951
21090
  var init_issue_board = __esm(() => {
20952
21091
  init_config_sections();
20953
21092
  init_setup();
@@ -20987,14 +21126,14 @@ function warn(warning) {
20987
21126
  return Object.freeze({ outcome: "warning", warning });
20988
21127
  }
20989
21128
  function noBoard(issue4, stage) {
20990
- return warn(`${PREFIX11}: #${String(issue4)} was not ${stage}: the board is not gh; ${CLAIM_STANDS}`);
21129
+ return warn(`${PREFIX12}: #${String(issue4)} was not ${stage}: the board is not gh; ${CLAIM_STANDS}`);
20991
21130
  }
20992
21131
  async function attempt(issue4, stage, write) {
20993
21132
  try {
20994
21133
  await write();
20995
21134
  return WRITTEN;
20996
21135
  } catch (error) {
20997
- return warn(`${PREFIX11}: #${String(issue4)} was not ${stage}: ${messageOf(error)}; ${CLAIM_STANDS}`);
21136
+ return warn(`${PREFIX12}: #${String(issue4)} was not ${stage}: ${messageOf(error)}; ${CLAIM_STANDS}`);
20998
21137
  }
20999
21138
  }
21000
21139
  async function labelClaimed(board, issue4) {
@@ -21024,7 +21163,7 @@ async function clearStage(board, issue4, why) {
21024
21163
  async function unlabelReleased(board, issue4) {
21025
21164
  return clearStage(board, issue4, "release");
21026
21165
  }
21027
- var PREFIX11 = "claim labels", CLAIM_STANDS = "the claim stands in git", STAGE_LABELS, WRITTEN;
21166
+ var PREFIX12 = "claim labels", CLAIM_STANDS = "the claim stands in git", STAGE_LABELS, WRITTEN;
21028
21167
  var init_labels = __esm(() => {
21029
21168
  init_config_sections();
21030
21169
  init_stale();
@@ -21421,13 +21560,13 @@ function parseBoardListing(stdout, command, mode = "labels") {
21421
21560
  try {
21422
21561
  payload = JSON.parse(stdout);
21423
21562
  } catch (error) {
21424
- throw new Error(`${PREFIX12}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
21563
+ throw new Error(`${PREFIX13}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
21425
21564
  }
21426
21565
  if (!Array.isArray(payload)) {
21427
- throw new Error(`${PREFIX12}: ${command} answered ${describeValue(payload)}, expected a list`);
21566
+ throw new Error(`${PREFIX13}: ${command} answered ${describeValue(payload)}, expected a list`);
21428
21567
  }
21429
21568
  const issues = payload.map((row2, index) => {
21430
- const refusal3 = `${PREFIX12}: ${command} answered row ${index}`;
21569
+ const refusal3 = `${PREFIX13}: ${command} answered row ${index}`;
21431
21570
  const problem = rowProblem(row2);
21432
21571
  if (problem !== null)
21433
21572
  throw new Error(`${refusal3} with ${problem}`);
@@ -21447,18 +21586,18 @@ function keepListing(listing) {
21447
21586
  function createGhBoardListing(options) {
21448
21587
  const { gh, limit = BOARD_LISTING_LIMIT, mode = "labels" } = options;
21449
21588
  if (!Number.isSafeInteger(limit) || limit < 1) {
21450
- throw new TypeError(`${PREFIX12}: limit ${describeValue(limit)}, expected a positive whole number`);
21589
+ throw new TypeError(`${PREFIX13}: limit ${describeValue(limit)}, expected a positive whole number`);
21451
21590
  }
21452
21591
  const args = listingArgs(limit, mode);
21453
21592
  const command = boardListingCommand(limit, mode);
21454
21593
  return async () => {
21455
21594
  const result = await gh(args);
21456
21595
  if (!result.ok)
21457
- throw new Error(`${PREFIX12}: ${detailOf8(result, command)}`);
21596
+ throw new Error(`${PREFIX13}: ${detailOf8(result, command)}`);
21458
21597
  return parseBoardListing(result.stdout, command, mode);
21459
21598
  };
21460
21599
  }
21461
- var PREFIX12 = "board listing", BOARD_LIST_FIELDS = "number,title,body,state,stateReason,labels", nativeBoardListFields, BOARD_LISTING_LIMIT = 1000, NativeFieldProblem, ISSUE_URL;
21600
+ var PREFIX13 = "board listing", BOARD_LIST_FIELDS = "number,title,body,state,stateReason,labels", nativeBoardListFields, BOARD_LISTING_LIMIT = 1000, NativeFieldProblem, ISSUE_URL;
21462
21601
  var init_roadmap_board = __esm(() => {
21463
21602
  init_github();
21464
21603
  init_config_sections();
@@ -22039,7 +22178,7 @@ function epicOnListing(number, listing, relations) {
22039
22178
  const epic = readEpics(relations === undefined ? input : { ...input, relations }).epics.find((read) => read.number === number);
22040
22179
  const row2 = listing.find((issue4) => issue4.number === number);
22041
22180
  if (epic === undefined || row2 === undefined) {
22042
- throw new Error(`${PREFIX13}: epic #${String(number)} is not on the board listing, so its members cannot be read;` + " the listing reads the newest issues only, and an epic it leaves out is not walked as if it had none");
22181
+ throw new Error(`${PREFIX14}: epic #${String(number)} is not on the board listing, so its members cannot be read;` + " the listing reads the newest issues only, and an epic it leaves out is not walked as if it had none");
22043
22182
  }
22044
22183
  return { epic, row: row2 };
22045
22184
  }
@@ -22160,7 +22299,7 @@ function dryEpicSentence(epic) {
22160
22299
  function rowOnListing(number, listing, what) {
22161
22300
  const row2 = listing.find((issue4) => issue4.number === number);
22162
22301
  if (row2 === undefined) {
22163
- throw new Error(`${PREFIX13}: ${what} #${String(number)} is not on the board listing, so the next ${NOW_HORIZON_LABEL} epic` + " cannot be read; the listing reads the newest issues only, and a line it leaves out is not passed as if it were none");
22302
+ throw new Error(`${PREFIX14}: ${what} #${String(number)} is not on the board listing, so the next ${NOW_HORIZON_LABEL} epic` + " cannot be read; the listing reads the newest issues only, and a line it leaves out is not passed as if it were none");
22164
22303
  }
22165
22304
  return row2;
22166
22305
  }
@@ -22201,7 +22340,7 @@ async function nextNowEpic(request) {
22201
22340
  }
22202
22341
  return null;
22203
22342
  }
22204
- var PREFIX13 = "board epic walk", NOW_HORIZON_LABEL, NO_CLAIMS, UNREAD_DAY, LINE_BREAK;
22343
+ var PREFIX14 = "board epic walk", NOW_HORIZON_LABEL, NO_CLAIMS, UNREAD_DAY, LINE_BREAK;
22205
22344
  var init_epic_walk = __esm(() => {
22206
22345
  init_github();
22207
22346
  init_epic_problems();
@@ -22301,11 +22440,11 @@ function driftSentence(finding) {
22301
22440
  }
22302
22441
  function driftLines(report) {
22303
22442
  return Object.freeze([
22304
- ...report.findings.map((finding) => `${PREFIX14}: ${driftSentence(finding)}`),
22305
- ...report.notices.map((notice) => `${PREFIX14}: ${notice}`)
22443
+ ...report.findings.map((finding) => `${PREFIX15}: ${driftSentence(finding)}`),
22444
+ ...report.notices.map((notice) => `${PREFIX15}: ${notice}`)
22306
22445
  ]);
22307
22446
  }
22308
- var PREFIX14 = "claim drift";
22447
+ var PREFIX15 = "claim drift";
22309
22448
  var init_drift = __esm(() => {
22310
22449
  init_epic_walk();
22311
22450
  init_roadmap_claims();
@@ -23923,7 +24062,7 @@ function createGhBoardLister(options) {
23923
24062
  return async () => {
23924
24063
  const result = await gh(args);
23925
24064
  if (!result.ok)
23926
- throw new Error(`${PREFIX15}: ${detailOf9(result, command)}`);
24065
+ throw new Error(`${PREFIX16}: ${detailOf9(result, command)}`);
23927
24066
  return parseBoardListing(result.stdout, command, mode);
23928
24067
  };
23929
24068
  }
@@ -23951,7 +24090,7 @@ function unlabelledRoadmapMessage(numbers) {
23951
24090
  const which = numbers.length === 1 ? `open issue ${named} is titled ${ROADMAP_TITLE} but lacks` : `open issues ${named} are titled ${ROADMAP_TITLE} but lack`;
23952
24091
  return `${which} the ${ROADMAP_LABEL} label, so no reader takes it as a board while labelled boards exist;` + " add the label to make it one, or retitle or close it";
23953
24092
  }
23954
- var PREFIX15 = "board listing", BOARDS_LIST_ARGS, BOARDS_LIST_COMMAND;
24093
+ var PREFIX16 = "board listing", BOARDS_LIST_ARGS, BOARDS_LIST_COMMAND;
23955
24094
  var init_boards = __esm(() => {
23956
24095
  init_roadmap_board();
23957
24096
  init_roadmap();
@@ -24006,17 +24145,17 @@ function bodyOf2(stdout, command) {
24006
24145
  try {
24007
24146
  payload = JSON.parse(stdout);
24008
24147
  } catch (error) {
24009
- throw new Error(`${PREFIX16}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
24148
+ throw new Error(`${PREFIX17}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
24010
24149
  }
24011
24150
  const body = isMapping(payload) ? payload["body"] : null;
24012
24151
  if (typeof body !== "string") {
24013
- throw new Error(`${PREFIX16}: ${command} answered body as ${describeValue(body)}, expected a string`);
24152
+ throw new Error(`${PREFIX17}: ${command} answered body as ${describeValue(body)}, expected a string`);
24014
24153
  }
24015
24154
  return body;
24016
24155
  }
24017
24156
  function issueSegment(value, member) {
24018
24157
  if (!Number.isSafeInteger(value) || value < 1) {
24019
- throw new TypeError(`${PREFIX16}: ${member} refused issue number ${describeValue(value)}, expected a positive whole number`);
24158
+ throw new TypeError(`${PREFIX17}: ${member} refused issue number ${describeValue(value)}, expected a positive whole number`);
24020
24159
  }
24021
24160
  return String(value);
24022
24161
  }
@@ -24028,7 +24167,7 @@ function createGhRoadmapBody(options) {
24028
24167
  const path2 = pathOf(issue4, "read");
24029
24168
  const result = await gh(["api", path2]);
24030
24169
  if (!result.ok)
24031
- throw new Error(`${PREFIX16}: ${detailOf10(result, `gh api ${path2}`)}`);
24170
+ throw new Error(`${PREFIX17}: ${detailOf10(result, `gh api ${path2}`)}`);
24032
24171
  return bodyOf2(result.stdout, `gh api ${path2}`);
24033
24172
  },
24034
24173
  write: async (issue4, body) => {
@@ -24036,13 +24175,13 @@ function createGhRoadmapBody(options) {
24036
24175
  const result = await gh(["api", path2, "-X", "PATCH", "-f", `body=${body}`]);
24037
24176
  const command = `gh api ${path2} -X PATCH -f body=<body>`;
24038
24177
  if (!result.ok)
24039
- throw new Error(`${PREFIX16}: ${detailOf10(result, command)}`);
24178
+ throw new Error(`${PREFIX17}: ${detailOf10(result, command)}`);
24040
24179
  return bodyOf2(result.stdout, command);
24041
24180
  }
24042
24181
  });
24043
24182
  }
24044
24183
  function conflictProblem(issue4) {
24045
- return `${PREFIX16}: issue #${String(issue4)} answered a body other than the one that was written,` + " so somebody edited it in between";
24184
+ return `${PREFIX17}: issue #${String(issue4)} answered a body other than the one that was written,` + " so somebody edited it in between";
24046
24185
  }
24047
24186
  async function attemptTick(roadmap, issues, board, attempt2) {
24048
24187
  let edit;
@@ -24102,7 +24241,7 @@ function tickSentence(result) {
24102
24241
  return `${named(result.already)} is ticked on ${roadmap} already.`;
24103
24242
  return `${roadmap} carries no line for ${named(result.absent)}, so nothing was ticked.`;
24104
24243
  }
24105
- var PREFIX16 = "board roadmap", REPO_PATH4 = "{owner}/{repo}", TICK_ATTEMPTS = 2, UNTICKED, TICKED = "[x]", LINE_BREAK2;
24244
+ var PREFIX17 = "board roadmap", REPO_PATH4 = "{owner}/{repo}", TICK_ATTEMPTS = 2, UNTICKED, TICKED = "[x]", LINE_BREAK2;
24106
24245
  var init_roadmap_tick = __esm(() => {
24107
24246
  init_config_sections();
24108
24247
  init_roadmap();
@@ -24124,7 +24263,7 @@ function joinParts(parts) {
24124
24263
  }
24125
24264
  function checkedIssue(issue4, member) {
24126
24265
  if (!Number.isSafeInteger(issue4) || issue4 < 1) {
24127
- throw new TypeError(`${PREFIX17}: ${member} refused issue number ${describeValue(issue4)}, expected a positive whole number`);
24266
+ throw new TypeError(`${PREFIX18}: ${member} refused issue number ${describeValue(issue4)}, expected a positive whole number`);
24128
24267
  }
24129
24268
  return issue4;
24130
24269
  }
@@ -24157,7 +24296,7 @@ function lastTextIndex(parts) {
24157
24296
  function appendLine(body, issue4, why = "") {
24158
24297
  checkedIssue(issue4, "appendLine");
24159
24298
  if (LINE_BREAK3.test(why)) {
24160
- throw new TypeError(`${PREFIX17}: appendLine refused a why spanning several lines: ${describeValue(why)}`);
24299
+ throw new TypeError(`${PREFIX18}: appendLine refused a why spanning several lines: ${describeValue(why)}`);
24161
24300
  }
24162
24301
  if (lineIndicesOf(body, issue4).length > 0)
24163
24302
  return body;
@@ -24182,7 +24321,7 @@ function tickLine(body, issue4) {
24182
24321
  return tickRoadmapLines(body, [checkedIssue(issue4, "tickLine")]).body;
24183
24322
  }
24184
24323
  function unconfirmedProblem(issue4) {
24185
- return `${PREFIX17}: issue #${String(issue4)} read back without the edit that was written,` + " so somebody edited it in between";
24324
+ return `${PREFIX18}: issue #${String(issue4)} read back without the edit that was written,` + " so somebody edited it in between";
24186
24325
  }
24187
24326
  async function attemptEdit(issue4, edit, board, attempt2) {
24188
24327
  const answered = { issue: issue4, status: "nothing-to-edit", attempts: attempt2 - 1, problem: "" };
@@ -24217,7 +24356,7 @@ async function editChecklists(options) {
24217
24356
  }
24218
24357
  return Object.freeze(results);
24219
24358
  }
24220
- var PREFIX17 = "board epic checklist", BULLET, DEFAULT_BULLET = "- ", LINE_BREAK3, DEFAULT_BREAK = `
24359
+ var PREFIX18 = "board epic checklist", BULLET, DEFAULT_BULLET = "- ", LINE_BREAK3, DEFAULT_BREAK = `
24221
24360
  `;
24222
24361
  var init_epic_checklist = __esm(() => {
24223
24362
  init_config_sections();
@@ -24255,24 +24394,24 @@ async function findEpic(slug, options, output) {
24255
24394
  const result = await options.gh(args);
24256
24395
  if (!result.ok) {
24257
24396
  const written = result.stderr.trim() || result.stdout.trim() || "nothing";
24258
- output.warn(`${PREFIX18}: ${command} failed (${written}); ${id} is planned without its epic`);
24397
+ output.warn(`${PREFIX19}: ${command} failed (${written}); ${id} is planned without its epic`);
24259
24398
  return null;
24260
24399
  }
24261
24400
  let epics;
24262
24401
  try {
24263
24402
  epics = parseBoardListing(result.stdout, command);
24264
24403
  } catch (error) {
24265
- output.warn(`${PREFIX18}: ${messageOf(error)}; ${id} is planned without its epic`);
24404
+ output.warn(`${PREFIX19}: ${messageOf(error)}; ${id} is planned without its epic`);
24266
24405
  return null;
24267
24406
  }
24268
24407
  const [epic, ...others] = epics;
24269
24408
  if (epic === undefined) {
24270
- output.warn(`${PREFIX18}: ${id} carries "${label}" but no "${EPIC_TYPE_LABEL}" issue does; it is planned without an epic`);
24409
+ output.warn(`${PREFIX19}: ${id} carries "${label}" but no "${EPIC_TYPE_LABEL}" issue does; it is planned without an epic`);
24271
24410
  return null;
24272
24411
  }
24273
24412
  if (others.length > 0) {
24274
24413
  const found = numbersOf(epics.map((each) => each.number));
24275
- output.warn(`${PREFIX18}: ${id} carries "${label}" and ${String(epics.length)} epics carry it (${found}); it is planned without an epic`);
24414
+ output.warn(`${PREFIX19}: ${id} carries "${label}" and ${String(epics.length)} epics carry it (${found}); it is planned without an epic`);
24276
24415
  return null;
24277
24416
  }
24278
24417
  return Object.freeze({ number: epic.number, title: epic.title, slug, criteria: readEpicBody(epic.body).criteria });
@@ -24284,7 +24423,7 @@ function nativeEpicOn(listing, options, relations, output) {
24284
24423
  const id = `issue #${String(options.issue)}`;
24285
24424
  const row2 = listing.find((each) => each.number === options.issue);
24286
24425
  if (row2 === undefined) {
24287
- output.warn(`${PREFIX18}: the board listing holds no ${id}; it is planned without its epic`);
24426
+ output.warn(`${PREFIX19}: the board listing holds no ${id}; it is planned without its epic`);
24288
24427
  return null;
24289
24428
  }
24290
24429
  const epicOf2 = relations.read(listing).epicOf(row2);
@@ -24292,7 +24431,7 @@ function nativeEpicOn(listing, options, relations, output) {
24292
24431
  return null;
24293
24432
  if (epicOf2.kind === "unresolved") {
24294
24433
  const marks = epicOf2.marks.map((each) => each.mark).join(", ");
24295
- output.warn(`${PREFIX18}: ${id}'s parent ${marks} is no epic on the board; it is planned without an epic`);
24434
+ output.warn(`${PREFIX19}: ${id}'s parent ${marks} is no epic on the board; it is planned without an epic`);
24296
24435
  return null;
24297
24436
  }
24298
24437
  const epic = listing.find((each) => each.number === epicOf2.epic);
@@ -24305,7 +24444,7 @@ async function readNativeEpicContext(options, relations) {
24305
24444
  try {
24306
24445
  return nativeEpicOn(await nativeListing(options), options, relations, output);
24307
24446
  } catch (error) {
24308
- output.warn(`${PREFIX18}: ${messageOf(error)}; issue #${String(options.issue)} is planned without its epic`);
24447
+ output.warn(`${PREFIX19}: ${messageOf(error)}; issue #${String(options.issue)} is planned without its epic`);
24309
24448
  return null;
24310
24449
  }
24311
24450
  }
@@ -24320,12 +24459,12 @@ async function readEpicContext(options) {
24320
24459
  const output = options.output ?? activeOutput();
24321
24460
  if (others.length > 0) {
24322
24461
  const labels = slugs.map((each) => `"${EPIC_LABEL_PREFIX}${each}"`).join(", ");
24323
- output.warn(`${PREFIX18}: issue #${String(options.issue)} carries ${String(slugs.length)} epic labels (${labels}); it is planned without an epic`);
24462
+ output.warn(`${PREFIX19}: issue #${String(options.issue)} carries ${String(slugs.length)} epic labels (${labels}); it is planned without an epic`);
24324
24463
  return null;
24325
24464
  }
24326
24465
  return findEpic(slug, options, output);
24327
24466
  }
24328
- var EPIC_TYPE_LABEL = "type:epic", EPIC_CONTEXT_LIMIT = 20, PREFIX18 = "epic context";
24467
+ var EPIC_TYPE_LABEL = "type:epic", EPIC_CONTEXT_LIMIT = 20, PREFIX19 = "epic context";
24329
24468
  var init_epic_context = __esm(() => {
24330
24469
  init_active();
24331
24470
  init_config_sections();
@@ -24514,32 +24653,32 @@ function detailOf11(result, command) {
24514
24653
  async function rowsOf3(gh, args, command) {
24515
24654
  const result = await gh(args);
24516
24655
  if (!result.ok)
24517
- throw new Error(`${PREFIX19}: ${detailOf11(result, command)}`);
24656
+ throw new Error(`${PREFIX20}: ${detailOf11(result, command)}`);
24518
24657
  let parsed;
24519
24658
  try {
24520
24659
  parsed = JSON.parse(result.stdout);
24521
24660
  } catch (error) {
24522
- throw new Error(`${PREFIX19}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
24661
+ throw new Error(`${PREFIX20}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
24523
24662
  }
24524
24663
  if (!Array.isArray(parsed)) {
24525
- throw new Error(`${PREFIX19}: ${command} answered ${describeValue(parsed)}, expected a list of issues`);
24664
+ throw new Error(`${PREFIX20}: ${command} answered ${describeValue(parsed)}, expected a list of issues`);
24526
24665
  }
24527
24666
  return parsed;
24528
24667
  }
24529
24668
  function rowNumber3(row2, command, where) {
24530
24669
  if (!isMapping(row2)) {
24531
- throw new Error(`${PREFIX19}: ${command} answered ${where} as ${describeValue(row2)}, expected a mapping`);
24670
+ throw new Error(`${PREFIX20}: ${command} answered ${where} as ${describeValue(row2)}, expected a mapping`);
24532
24671
  }
24533
24672
  const number = row2["number"];
24534
24673
  if (typeof number !== "number" || !Number.isSafeInteger(number) || number < 1) {
24535
- throw new Error(`${PREFIX19}: ${command} answered ${where}.number as ${describeValue(number)}, expected a positive whole number`);
24674
+ throw new Error(`${PREFIX20}: ${command} answered ${where}.number as ${describeValue(number)}, expected a positive whole number`);
24536
24675
  }
24537
24676
  return number;
24538
24677
  }
24539
24678
  function rowBody(row2, command, where) {
24540
24679
  const body = isMapping(row2) ? row2["body"] : null;
24541
24680
  if (typeof body !== "string") {
24542
- throw new Error(`${PREFIX19}: ${command} answered ${where}.body as ${describeValue(body)}, expected a string`);
24681
+ throw new Error(`${PREFIX20}: ${command} answered ${where}.body as ${describeValue(body)}, expected a string`);
24543
24682
  }
24544
24683
  return body;
24545
24684
  }
@@ -24604,7 +24743,7 @@ function renderBlockedIssues(report) {
24604
24743
  const unchecked = report.unchecked === null ? [] : [` ${report.unchecked}`];
24605
24744
  return [BLOCKED_HEADING, ...body, ...unchecked];
24606
24745
  }
24607
- var BLOCKED_HEADING = "Blocked issues:", BLOCKED_LIST_LIMIT = 100, KNOWN_LIST_LIMIT = 500, PREFIX19 = "board blocked", BLOCKED_ARGS, BLOCKED_COMMAND, KNOWN_ARGS, KNOWN_COMMAND;
24746
+ var BLOCKED_HEADING = "Blocked issues:", BLOCKED_LIST_LIMIT = 100, KNOWN_LIST_LIMIT = 500, PREFIX20 = "board blocked", BLOCKED_ARGS, BLOCKED_COMMAND, KNOWN_ARGS, KNOWN_COMMAND;
24608
24747
  var init_doctor_blocked = __esm(() => {
24609
24748
  init_blocked();
24610
24749
  init_config_sections();
@@ -24675,11 +24814,11 @@ async function resolveTracker(options) {
24675
24814
  }
24676
24815
  attempts = [...attempts, Object.freeze({ kind, ok: false, reason: outcome.reason })];
24677
24816
  failures = [...failures, `${kind}: ${outcome.reason}`];
24678
- log(`${PREFIX20}: ${kind} unavailable: ${outcome.reason}`);
24817
+ log(`${PREFIX21}: ${kind} unavailable: ${outcome.reason}`);
24679
24818
  }
24680
- throw new Error(`${PREFIX20}: no tracker available; tried ${failures.join(FAILURE_SEPARATOR)}`);
24819
+ throw new Error(`${PREFIX21}: no tracker available; tried ${failures.join(FAILURE_SEPARATOR)}`);
24681
24820
  }
24682
- var PREFIX20 = "tracker chain", FAILURE_SEPARATOR = "; ";
24821
+ var PREFIX21 = "tracker chain", FAILURE_SEPARATOR = "; ";
24683
24822
  var init_resolve2 = __esm(() => {
24684
24823
  init_config_sections();
24685
24824
  init_active();
@@ -24825,32 +24964,32 @@ function detailOf12(result, command) {
24825
24964
  async function rowsOf4(gh, args, command) {
24826
24965
  const result = await gh(args);
24827
24966
  if (!result.ok)
24828
- throw new Error(`${PREFIX21}: ${detailOf12(result, command)}`);
24967
+ throw new Error(`${PREFIX22}: ${detailOf12(result, command)}`);
24829
24968
  let parsed;
24830
24969
  try {
24831
24970
  parsed = JSON.parse(result.stdout);
24832
24971
  } catch (error) {
24833
- throw new Error(`${PREFIX21}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
24972
+ throw new Error(`${PREFIX22}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
24834
24973
  }
24835
24974
  if (!Array.isArray(parsed)) {
24836
- throw new Error(`${PREFIX21}: ${command} answered ${describeValue(parsed)}, expected a list of issues`);
24975
+ throw new Error(`${PREFIX22}: ${command} answered ${describeValue(parsed)}, expected a list of issues`);
24837
24976
  }
24838
24977
  return parsed;
24839
24978
  }
24840
24979
  function rowNumber4(row2, command, where) {
24841
24980
  if (!isMapping(row2)) {
24842
- throw new Error(`${PREFIX21}: ${command} answered ${where} as ${describeValue(row2)}, expected a mapping`);
24981
+ throw new Error(`${PREFIX22}: ${command} answered ${where} as ${describeValue(row2)}, expected a mapping`);
24843
24982
  }
24844
24983
  const number = row2["number"];
24845
24984
  if (typeof number !== "number" || !Number.isSafeInteger(number) || number < 1) {
24846
- throw new Error(`${PREFIX21}: ${command} answered ${where}.number as ${describeValue(number)}, expected a positive whole number`);
24985
+ throw new Error(`${PREFIX22}: ${command} answered ${where}.number as ${describeValue(number)}, expected a positive whole number`);
24847
24986
  }
24848
24987
  return number;
24849
24988
  }
24850
24989
  function rowBody2(row2, command, where) {
24851
24990
  const body = isMapping(row2) ? row2["body"] : null;
24852
24991
  if (typeof body !== "string") {
24853
- throw new Error(`${PREFIX21}: ${command} answered ${where}.body as ${describeValue(body)}, expected a string`);
24992
+ throw new Error(`${PREFIX22}: ${command} answered ${where}.body as ${describeValue(body)}, expected a string`);
24854
24993
  }
24855
24994
  return body;
24856
24995
  }
@@ -25129,7 +25268,7 @@ function createIssueUnblockCommand(seams = DEFAULT_UNBLOCK_SEAMS) {
25129
25268
  };
25130
25269
  return Object.freeze(command);
25131
25270
  }
25132
- var UNBLOCK_USAGE = "rafa issue unblock [<n>] [--all]", ALL_FLAG = "all", ISSUE_NUMBER3, YES_ANSWERS2, PREFIX21 = "board unblock", BLOCKED_ARGS2, BLOCKED_COMMAND2, STATE_ARGS, STATE_COMMAND, DEFAULT_UNBLOCK_SEAMS, WARNED, unblock_default;
25271
+ var UNBLOCK_USAGE = "rafa issue unblock [<n>] [--all]", ALL_FLAG = "all", ISSUE_NUMBER3, YES_ANSWERS2, PREFIX22 = "board unblock", BLOCKED_ARGS2, BLOCKED_COMMAND2, STATE_ARGS, STATE_COMMAND, DEFAULT_UNBLOCK_SEAMS, WARNED, unblock_default;
25133
25272
  var init_unblock = __esm(() => {
25134
25273
  init_github();
25135
25274
  init_blocked();
@@ -25314,14 +25453,14 @@ function ref(issue4) {
25314
25453
  }
25315
25454
  function issueNumber4(value, member) {
25316
25455
  if (!Number.isSafeInteger(value) || value < 1) {
25317
- throw new TypeError(`${PREFIX22}: ${member} refused issue number ${describeValue(value)}, expected a positive whole number`);
25456
+ throw new TypeError(`${PREFIX23}: ${member} refused issue number ${describeValue(value)}, expected a positive whole number`);
25318
25457
  }
25319
25458
  return value;
25320
25459
  }
25321
25460
  function rowOf3(listing, issue4, member) {
25322
25461
  const row2 = listing.find((candidate) => candidate.number === issue4);
25323
25462
  if (row2 === undefined) {
25324
- throw new Error(`${PREFIX22}: ${member} refused ${ref(issue4)}, which is not on the board listing, so nothing was changed`);
25463
+ throw new Error(`${PREFIX23}: ${member} refused ${ref(issue4)}, which is not on the board listing, so nothing was changed`);
25325
25464
  }
25326
25465
  return row2;
25327
25466
  }
@@ -25331,7 +25470,7 @@ function detailOf13(result) {
25331
25470
  async function sendRelationship(gh, args, what) {
25332
25471
  const result = await gh(args);
25333
25472
  if (!result.ok)
25334
- throw new Error(`${PREFIX22}: ${what} was refused, so nothing was changed: ${detailOf13(result)}`);
25473
+ throw new Error(`${PREFIX23}: ${what} was refused, so nothing was changed: ${detailOf13(result)}`);
25335
25474
  }
25336
25475
  function labelEditArgs(issue4, removed, added) {
25337
25476
  return [
@@ -25361,7 +25500,7 @@ function ownersOf(listing, slug) {
25361
25500
  function memberRow(listing, issue4, member) {
25362
25501
  const row2 = rowOf3(listing, issueNumber4(issue4, member), member);
25363
25502
  if (row2.type === "epic") {
25364
- throw new Error(`${PREFIX22}: ${member} refused ${ref(issue4)}, which is an epic, and an epic belongs to no epic; nothing was changed`);
25503
+ throw new Error(`${PREFIX23}: ${member} refused ${ref(issue4)}, which is an epic, and an epic belongs to no epic; nothing was changed`);
25365
25504
  }
25366
25505
  return row2;
25367
25506
  }
@@ -25369,7 +25508,7 @@ function targetSlug(listing, parent) {
25369
25508
  const row2 = rowOf3(listing, issueNumber4(parent, "setParent"), "setParent");
25370
25509
  const slug = epicSlugsOf(row2.labels)[0];
25371
25510
  if (row2.type !== "epic" || slug === undefined) {
25372
- throw new Error(`${PREFIX22}: setParent refused epic ${ref(parent)}, which is not an epic carrying an ${EPIC_LABEL_PREFIX}` + " label, so it has no label to give; nothing was changed");
25511
+ throw new Error(`${PREFIX23}: setParent refused epic ${ref(parent)}, which is not an epic carrying an ${EPIC_LABEL_PREFIX}` + " label, so it has no label to give; nothing was changed");
25373
25512
  }
25374
25513
  return slug;
25375
25514
  }
@@ -25423,7 +25562,7 @@ function waitingRow(listing, change, member) {
25423
25562
  const row2 = rowOf3(listing, issueNumber4(change.issue, member), member);
25424
25563
  issueNumber4(change.blocker.number, member);
25425
25564
  if (change.blocker.repository === null && change.blocker.number === row2.number) {
25426
- throw new Error(`${PREFIX22}: ${member} refused to make ${ref(row2.number)} wait on itself; nothing was changed`);
25565
+ throw new Error(`${PREFIX23}: ${member} refused to make ${ref(row2.number)} wait on itself; nothing was changed`);
25427
25566
  }
25428
25567
  return row2;
25429
25568
  }
@@ -25455,7 +25594,7 @@ async function removeLabelsBlocker(seams, listing, change) {
25455
25594
  await sendRelationship(seams.gh, args, what);
25456
25595
  return [written(row2.number, what, "written")];
25457
25596
  }
25458
- var PREFIX22 = "board relations labels", NO_CLAIMS2, UNREAD_DAY2;
25597
+ var PREFIX23 = "board relations labels", NO_CLAIMS2, UNREAD_DAY2;
25459
25598
  var init_labels_writes = __esm(() => {
25460
25599
  init_config_sections();
25461
25600
  init_blocked();
@@ -25495,7 +25634,7 @@ function epicOf3(issue4, owners) {
25495
25634
  }
25496
25635
  function membersOf(epic, listing, epics) {
25497
25636
  if (epic.type !== "epic") {
25498
- throw new TypeError(`${PREFIX23}: membersOf refused #${String(epic.number)}, which is not typed epic, so it has no members`);
25637
+ throw new TypeError(`${PREFIX24}: membersOf refused #${String(epic.number)}, which is not typed epic, so it has no members`);
25499
25638
  }
25500
25639
  const read = epics.find((candidate) => candidate.number === epic.number) ?? epicsOf([...listing, epic]).find((candidate) => candidate.number === epic.number);
25501
25640
  const members = read?.members ?? [];
@@ -25606,7 +25745,7 @@ function createLabelsRelations(options) {
25606
25745
  };
25607
25746
  return Object.freeze(relations);
25608
25747
  }
25609
- var PREFIX23 = "board relations labels", NO_CLAIMS3, UNREAD_DAY3, FOREIGN_TOKEN, LABELS_READS;
25748
+ var PREFIX24 = "board relations labels", NO_CLAIMS3, UNREAD_DAY3, FOREIGN_TOKEN, LABELS_READS;
25610
25749
  var init_labels2 = __esm(() => {
25611
25750
  init_merge_tick();
25612
25751
  init_merge_unblock();
@@ -25874,11 +26013,11 @@ function summaryState(summary, members, claims) {
25874
26013
  }
25875
26014
  function withSubIssuesSummary(epic, row2, claims, today) {
25876
26015
  if (row2.number !== epic.number) {
25877
- throw new TypeError(`${PREFIX24}: row #${String(row2.number)} is not epic #${String(epic.number)}'s row`);
26016
+ throw new TypeError(`${PREFIX25}: row #${String(row2.number)} is not epic #${String(epic.number)}'s row`);
25878
26017
  }
25879
26018
  const summary = row2.subIssuesSummary;
25880
26019
  if (summary === undefined) {
25881
- throw new TypeError(`${PREFIX24}: epic #${String(epic.number)} was read without subIssuesSummary; ` + "board.relationships is native, so the listing must be read with the native fields");
26020
+ throw new TypeError(`${PREFIX25}: epic #${String(epic.number)} was read without subIssuesSummary; ` + "board.relationships is native, so the listing must be read with the native fields");
25882
26021
  }
25883
26022
  const progress = Object.freeze({
25884
26023
  done: summary.completed,
@@ -25895,7 +26034,7 @@ function withSubIssuesSummary(epic, row2, claims, today) {
25895
26034
  disagreement: disagreementOf(epic.number, state, epic.stored)
25896
26035
  });
25897
26036
  }
25898
- var PREFIX24 = "board epic summary";
26037
+ var PREFIX25 = "board epic summary";
25899
26038
  var init_epic_summary = __esm(() => {
25900
26039
  init_epics();
25901
26040
  });
@@ -26438,7 +26577,7 @@ function ref4(issue4) {
26438
26577
  }
26439
26578
  function issueNumber5(value, member) {
26440
26579
  if (!Number.isSafeInteger(value) || value < 1) {
26441
- throw new TypeError(`${PREFIX25}: ${member} refused issue number ${describeValue(value)}, expected a positive whole number`);
26580
+ throw new TypeError(`${PREFIX26}: ${member} refused issue number ${describeValue(value)}, expected a positive whole number`);
26442
26581
  }
26443
26582
  return value;
26444
26583
  }
@@ -26449,17 +26588,17 @@ function rowOf5(listing, issue4, member) {
26449
26588
  const number = issueNumber5(issue4, member);
26450
26589
  const row2 = listing.find((candidate) => candidate.number === number);
26451
26590
  if (row2 === undefined) {
26452
- throw new Error(`${PREFIX25}: ${member} refused ${ref4(issue4)}, which is not on the board listing, so nothing was changed`);
26591
+ throw new Error(`${PREFIX26}: ${member} refused ${ref4(issue4)}, which is not on the board listing, so nothing was changed`);
26453
26592
  }
26454
26593
  if (!Object.hasOwn(row2, "parent") || !Object.hasOwn(row2, "blockedBy")) {
26455
- throw new TypeError(`${PREFIX25}: ${member} refused ${ref4(issue4)}, which was read without parent and blockedBy; ` + "board.relationships is native, so the listing must be read with the native fields");
26594
+ throw new TypeError(`${PREFIX26}: ${member} refused ${ref4(issue4)}, which was read without parent and blockedBy; ` + "board.relationships is native, so the listing must be read with the native fields");
26456
26595
  }
26457
26596
  return { row: row2, fields: { parent: row2.parent ?? null, blockedBy: row2.blockedBy ?? { nodes: [] } } };
26458
26597
  }
26459
26598
  function memberRow2(listing, issue4, member) {
26460
26599
  const found = rowOf5(listing, issue4, member);
26461
26600
  if (found.row.type === "epic") {
26462
- throw new Error(`${PREFIX25}: ${member} refused ${ref4(issue4)}, which is an epic, and an epic belongs to no epic; nothing was changed`);
26601
+ throw new Error(`${PREFIX26}: ${member} refused ${ref4(issue4)}, which is an epic, and an epic belongs to no epic; nothing was changed`);
26463
26602
  }
26464
26603
  return found;
26465
26604
  }
@@ -26467,7 +26606,7 @@ function targetEpic(listing, parent) {
26467
26606
  const number = issueNumber5(parent, "setParent");
26468
26607
  const row2 = listing.find((candidate) => candidate.number === number);
26469
26608
  if (row2?.type !== "epic") {
26470
- throw new Error(`${PREFIX25}: setParent refused epic ${ref4(parent)}, which is not an issue typed epic on the board listing;` + " nothing was changed");
26609
+ throw new Error(`${PREFIX26}: setParent refused epic ${ref4(parent)}, which is not an issue typed epic on the board listing;` + " nothing was changed");
26471
26610
  }
26472
26611
  return row2.number;
26473
26612
  }
@@ -26477,7 +26616,7 @@ function normalBlocker(blocker, board, member) {
26477
26616
  if (repository === null || isBoard(repository, board))
26478
26617
  return { number, repository: null };
26479
26618
  if (!REPOSITORY.test(repository)) {
26480
- throw new TypeError(`${PREFIX25}: ${member} refused blocker repository "${repository}", which is not owner/name`);
26619
+ throw new TypeError(`${PREFIX26}: ${member} refused blocker repository "${repository}", which is not owner/name`);
26481
26620
  }
26482
26621
  return { number, repository };
26483
26622
  }
@@ -26499,7 +26638,7 @@ function written2(issue4, what, status) {
26499
26638
  async function send(gh, issue4, flags, what) {
26500
26639
  const result = await gh(["issue", "edit", String(issue4), ...flags]);
26501
26640
  if (!result.ok)
26502
- throw new Error(`${PREFIX25}: ${what} was refused, so nothing was changed: ${detailOf14(result)}`);
26641
+ throw new Error(`${PREFIX26}: ${what} was refused, so nothing was changed: ${detailOf14(result)}`);
26503
26642
  return written2(issue4, what, "written");
26504
26643
  }
26505
26644
  async function setNativeParent(seams, listing, change) {
@@ -26523,7 +26662,7 @@ function blockerWrite(seams, listing, change, member) {
26523
26662
  const { row: row2, fields } = rowOf5(listing, change.issue, member);
26524
26663
  const blocker = normalBlocker(change.blocker, seams.repository, member);
26525
26664
  if (blocker.repository === null && blocker.number === row2.number) {
26526
- throw new Error(`${PREFIX25}: ${member} refused to make ${ref4(row2.number)} wait on itself; nothing was changed`);
26665
+ throw new Error(`${PREFIX26}: ${member} refused to make ${ref4(row2.number)} wait on itself; nothing was changed`);
26527
26666
  }
26528
26667
  return { row: row2, fields, blocker };
26529
26668
  }
@@ -26543,7 +26682,7 @@ async function removeNativeBlocker(seams, listing, change) {
26543
26682
  }
26544
26683
  return send(seams.gh, row2.number, ["--remove-blocked-by", blockerArg(blocker)], what);
26545
26684
  }
26546
- var PREFIX25 = "board relations native", REPOSITORY, GITHUB_HOST2 = "https://github.com";
26685
+ var PREFIX26 = "board relations native", REPOSITORY, GITHUB_HOST2 = "https://github.com";
26547
26686
  var init_native_writes = __esm(() => {
26548
26687
  init_config_sections();
26549
26688
  REPOSITORY = /^[^/\s]+\/[^/\s]+$/u;
@@ -26553,7 +26692,7 @@ var init_native_writes = __esm(() => {
26553
26692
  function nativeFieldsOf(row2) {
26554
26693
  const missing2 = NATIVE_KEYS.filter((key) => !Object.hasOwn(row2, key));
26555
26694
  if (missing2.length > 0) {
26556
- throw new TypeError(`${PREFIX26}: issue #${String(row2.number)} was read without ${missing2.join(", ")}; ` + "board.relationships is native, so the listing must be read with the native fields");
26695
+ throw new TypeError(`${PREFIX27}: issue #${String(row2.number)} was read without ${missing2.join(", ")}; ` + "board.relationships is native, so the listing must be read with the native fields");
26557
26696
  }
26558
26697
  return { parent: row2.parent ?? null, blockedBy: row2.blockedBy ?? { nodes: [] }, subIssues: row2.subIssues ?? { nodes: [] } };
26559
26698
  }
@@ -26587,7 +26726,7 @@ function isChildOf(row2, epic, board) {
26587
26726
  }
26588
26727
  function membersOf3(epic, listing, board) {
26589
26728
  if (epic.type !== "epic") {
26590
- throw new TypeError(`${PREFIX26}: membersOf refused #${String(epic.number)}, which is not typed epic, so it has no members`);
26729
+ throw new TypeError(`${PREFIX27}: membersOf refused #${String(epic.number)}, which is not typed epic, so it has no members`);
26591
26730
  }
26592
26731
  const { subIssues } = nativeFieldsOf(epic);
26593
26732
  const children = listing.filter((row2) => row2.type !== "epic" && isChildOf(row2, epic.number, board));
@@ -26630,7 +26769,7 @@ function afterNativeMerge() {
26630
26769
  function createNativeRelations(options) {
26631
26770
  const { repository } = options;
26632
26771
  if (!REPOSITORY2.test(repository)) {
26633
- throw new TypeError(`${PREFIX26}: the board's repository "${repository}" is not owner/name`);
26772
+ throw new TypeError(`${PREFIX27}: the board's repository "${repository}" is not owner/name`);
26634
26773
  }
26635
26774
  const seams = { gh: options.gh, repository };
26636
26775
  const relations = {
@@ -26645,7 +26784,7 @@ function createNativeRelations(options) {
26645
26784
  };
26646
26785
  return Object.freeze(relations);
26647
26786
  }
26648
- var PREFIX26 = "board relations native", NATIVE_KEYS, REPOSITORY2;
26787
+ var PREFIX27 = "board relations native", NATIVE_KEYS, REPOSITORY2;
26649
26788
  var init_native = __esm(() => {
26650
26789
  init_roadmap_board();
26651
26790
  init_freed();
@@ -26966,7 +27105,7 @@ function readSavedCopy(absolute, path2) {
26966
27105
  return null;
26967
27106
  return { text: readFileSync31(absolute, "utf8"), savedAt: stat.mtime };
26968
27107
  } catch (error) {
26969
- throw new Error(`${PREFIX27}: the saved copy ${path2} could not be read: ${messageOf(error)}`, { cause: error });
27108
+ throw new Error(`${PREFIX28}: the saved copy ${path2} could not be read: ${messageOf(error)}`, { cause: error });
26970
27109
  }
26971
27110
  }
26972
27111
  function printChange(change, output) {
@@ -27008,7 +27147,7 @@ async function settleSpecSnapshot(options) {
27008
27147
  throw new CommandExit(ISSUE_REFUSAL_EXIT, snapshotDiffersMessage(path2, issue4.number));
27009
27148
  return write(true);
27010
27149
  }
27011
- var PREFIX27 = "board snapshot settle", DATE_LENGTH = 10;
27150
+ var PREFIX28 = "board snapshot settle", DATE_LENGTH = 10;
27012
27151
  var init_snapshot_settle = __esm(() => {
27013
27152
  init_command();
27014
27153
  init_config_sections();
@@ -27243,7 +27382,7 @@ async function walkPlaceEpic(number, listing, readings, relations) {
27243
27382
  const read = readEpics({ issues: rows2, claims: new Set, today: new Date(0), relations }).epics.find((candidate) => candidate.number === number);
27244
27383
  const row2 = rows2.find((issue4) => issue4.number === number);
27245
27384
  if (read === undefined || row2 === undefined) {
27246
- throw new Error(`${PREFIX28}: epic #${String(number)}, the current place, is not on the board listing, so its members cannot be read`);
27385
+ throw new Error(`${PREFIX29}: epic #${String(number)}, the current place, is not on the board listing, so its members cannot be read`);
27247
27386
  }
27248
27387
  const { checklist, labelOnly, lines } = epicLines(read, row2);
27249
27388
  const epic = Object.freeze({
@@ -27380,7 +27519,7 @@ async function settleBlockedPick(options) {
27380
27519
  }
27381
27520
  return { issue: plannable.line.issue };
27382
27521
  }
27383
- var PREFIX28 = "board spec source roadmap";
27522
+ var PREFIX29 = "board spec source roadmap";
27384
27523
  var init_spec_source_roadmap = __esm(() => {
27385
27524
  init_hop_record();
27386
27525
  init_position();
@@ -27479,7 +27618,7 @@ function dryRunLine(what) {
27479
27618
  }
27480
27619
  function requireRoadmapSeams(seams) {
27481
27620
  if (seams === undefined) {
27482
- throw new TypeError(`${PREFIX29}: ${NEXT_FLAG} was resolved with no roadmap seams`);
27621
+ throw new TypeError(`${PREFIX30}: ${NEXT_FLAG} was resolved with no roadmap seams`);
27483
27622
  }
27484
27623
  return seams;
27485
27624
  }
@@ -27570,7 +27709,7 @@ async function resolveSpecSource(options) {
27570
27709
  })
27571
27710
  });
27572
27711
  }
27573
- var PREFIX29 = "board spec source", SOURCE_FLAGS, SOURCE_REFUSAL_EXIT = 1, ROADMAP_REFUSAL_EXIT2 = 2;
27712
+ var PREFIX30 = "board spec source", SOURCE_FLAGS, SOURCE_REFUSAL_EXIT = 1, ROADMAP_REFUSAL_EXIT2 = 2;
27574
27713
  var init_spec_source = __esm(() => {
27575
27714
  init_active();
27576
27715
  init_ahead();
@@ -33103,7 +33242,7 @@ async function walkEpic(epic, listing, readings, picker, relations) {
33103
33242
  const read = readEpics({ issues: rows2, claims: new Set, today: new Date(0), relations }).epics.find((candidate) => candidate.number === epic);
33104
33243
  const row2 = rows2.find((issue4) => issue4.number === epic);
33105
33244
  if (read === undefined || row2 === undefined) {
33106
- throw new Error(`${PREFIX30}: epic #${String(epic)}, the current place, is not on the board listing, so its members cannot be read`);
33245
+ throw new Error(`${PREFIX31}: epic #${String(epic)}, the current place, is not on the board listing, so its members cannot be read`);
33107
33246
  }
33108
33247
  const pick = await picker(epicLines(read, row2).lines, readings, epic);
33109
33248
  return {
@@ -33245,7 +33384,7 @@ function pickerFor(followed, seams) {
33245
33384
  }
33246
33385
  return native ? nativeHopPick(followed, seams) : waitingPick(seams.blocking);
33247
33386
  }
33248
- var PREFIX30 = "rafa next", plainPick = async (lines, readings) => {
33387
+ var PREFIX31 = "rafa next", plainPick = async (lines, readings) => {
33249
33388
  const pick = await pickNextRoadmapLine(lines, readings);
33250
33389
  return { line: pick.line, passed: pick.skipped.length, waiting: [] };
33251
33390
  }, STOP, PASS2, NOTHING_TAKEN;
@@ -34952,7 +35091,7 @@ function ghNextBoard(options) {
34952
35091
  }
34953
35092
  function nextProject(context) {
34954
35093
  if (context.project === null)
34955
- throw new Error(`${PREFIX31} runs inside a project, and was handed none`);
35094
+ throw new Error(`${PREFIX32} runs inside a project, and was handed none`);
34956
35095
  return context.project;
34957
35096
  }
34958
35097
  function nextConfig(project, warn2) {
@@ -34962,7 +35101,7 @@ function nextConfig(project, warn2) {
34962
35101
  if (!(error instanceof ConfigError))
34963
35102
  throw error;
34964
35103
  throw new CommandExit(1, [
34965
- `\u274C ${PREFIX31}: the config cannot be used:`,
35104
+ `\u274C ${PREFIX32}: the config cannot be used:`,
34966
35105
  ...error.problems.map((problem) => ` ${problem}`)
34967
35106
  ].join(`
34968
35107
  `));
@@ -35009,7 +35148,7 @@ function openNextSources(context, seams = DEFAULT_NEXT_SOURCE_SEAMS, options = {
35009
35148
  roots: Object.freeze({ root: project.root, home: project.home })
35010
35149
  });
35011
35150
  }
35012
- var PREFIX31 = "rafa next", DEFAULT_BASE_BRANCH = "main", DEFAULT_NEXT_SOURCE_SEAMS;
35151
+ var PREFIX32 = "rafa next", DEFAULT_BASE_BRANCH = "main", DEFAULT_NEXT_SOURCE_SEAMS;
35013
35152
  var init_sources = __esm(() => {
35014
35153
  init_github();
35015
35154
  init_boards();
@@ -36955,7 +37094,7 @@ async function readNextState(sources) {
36955
37094
  const found = await firstAnswer(world, rows2.filter((row2) => AHEAD_OF_TREE.has(row2.id))) ?? readTreeModified(world) ?? await firstAnswer(world, rows2.filter((row2) => !AHEAD_OF_TREE.has(row2.id)));
36956
37095
  if (found !== null)
36957
37096
  return answer3(found, world.problems());
36958
- throw new Error(`${PREFIX32}: no row of the table answered, and the last row answers for every reading`);
37097
+ throw new Error(`${PREFIX33}: no row of the table answered, and the last row answers for every reading`);
36959
37098
  }
36960
37099
  async function readHomeAfterLoop(sources) {
36961
37100
  if (sources.roadmap === undefined)
@@ -36964,7 +37103,7 @@ async function readHomeAfterLoop(sources) {
36964
37103
  const found = homeAfterLoop(reading2);
36965
37104
  return found === null ? null : answer3(found, reading2.problems);
36966
37105
  }
36967
- var PREFIX32 = "rafa next", NEXT_PRECONDITIONS, ROWS, NEXT_STATES, HOP_ROWS_AHEAD_OF, ROADMAP_ROWS, NEXT_ROADMAP_STATES, AHEAD_OF_TREE;
37106
+ var PREFIX33 = "rafa next", NEXT_PRECONDITIONS, ROWS, NEXT_STATES, HOP_ROWS_AHEAD_OF, ROADMAP_ROWS, NEXT_ROADMAP_STATES, AHEAD_OF_TREE;
36968
37107
  var init_state = __esm(() => {
36969
37108
  init_blocked_line();
36970
37109
  init_blocked();
@@ -37204,7 +37343,7 @@ function passedWords(action, options) {
37204
37343
  }
37205
37344
  function needed(state, value, what) {
37206
37345
  if (value === null) {
37207
- throw new Error(`${PREFIX33}: state "${state.id}" proposes "${state.action}" and names no ${what}`);
37346
+ throw new Error(`${PREFIX34}: state "${state.id}" proposes "${state.action}" and names no ${what}`);
37208
37347
  }
37209
37348
  return String(value);
37210
37349
  }
@@ -37221,7 +37360,7 @@ function offeredCommandActions(mode) {
37221
37360
  return OFFERED_COMMAND_ACTIONS[mode];
37222
37361
  }
37223
37362
  function notOffered(state, mode) {
37224
- return new Error(`${PREFIX33}: state "${state.id}" proposes "${state.action}", which is offered in labels mode only` + ` and board.relationships is ${mode}: the tracker clears a blocker when it closes`);
37363
+ return new Error(`${PREFIX34}: state "${state.id}" proposes "${state.action}", which is offered in labels mode only` + ` and board.relationships is ${mode}: the tracker clears a blocker when it closes`);
37225
37364
  }
37226
37365
  function runsCommand(action) {
37227
37366
  return NEXT_COMMAND_ACTIONS.includes(action);
@@ -37251,13 +37390,13 @@ function actionContext(caller, command, argv) {
37251
37390
  async function runRegistered(caller, name2, argv, step) {
37252
37391
  const command = caller.registry.find(name2.subject, name2.action);
37253
37392
  if (command === undefined) {
37254
- throw new CommandExit(1, `\u274C ${PREFIX33}: the "${step}" step runs "rafa ${commandSpelling(name2)}", which is registered by nothing`);
37393
+ throw new CommandExit(1, `\u274C ${PREFIX34}: the "${step}" step runs "rafa ${commandSpelling(name2)}", which is registered by nothing`);
37255
37394
  }
37256
37395
  await command.run(actionContext(caller, command, argv));
37257
37396
  }
37258
37397
  function hopWorld(caller, state) {
37259
37398
  if (caller.project === null) {
37260
- throw new Error(`${PREFIX33}: the action "${state.action}" of state "${state.id}" runs inside a project, and was handed none`);
37399
+ throw new Error(`${PREFIX34}: the action "${state.action}" of state "${state.id}" runs inside a project, and was handed none`);
37261
37400
  }
37262
37401
  return {
37263
37402
  root: caller.project.root,
@@ -37272,7 +37411,7 @@ function hopWorld(caller, state) {
37272
37411
  };
37273
37412
  }
37274
37413
  function noStep(state) {
37275
- return new Error(`${PREFIX33}: state "${state.id}" proposes "${state.action}" and carries no ${state.action} step`);
37414
+ return new Error(`${PREFIX34}: state "${state.id}" proposes "${state.action}" and carries no ${state.action} step`);
37276
37415
  }
37277
37416
  async function runHopStep(caller, state) {
37278
37417
  const step = state.hop;
@@ -37291,11 +37430,11 @@ async function runAction(caller, state, options = {}) {
37291
37430
  }
37292
37431
  const invocation = actionInvocation(state, options);
37293
37432
  if (invocation === null) {
37294
- throw new Error(`${PREFIX33}: the action "${state.action}" of state "${state.id}" runs no registered command`);
37433
+ throw new Error(`${PREFIX34}: the action "${state.action}" of state "${state.id}" runs no registered command`);
37295
37434
  }
37296
37435
  await runRegistered(caller, ACTION_COMMANDS[invocation.action], invocation.argv, invocation.action);
37297
37436
  }
37298
- var PREFIX33 = "rafa next", NEXT_IN_PROCESS_ACTIONS, SWITCH_COMMAND, ROADMAP_WORD = "--roadmap", ROADMAP_PASSED_ACTIONS, CLAIM_AHEAD_WORD = "--claim-ahead", ACTION_COMMANDS, NEXT_COMMAND_ACTIONS, LABELS_ONLY_ACTIONS, OFFERED_COMMAND_ACTIONS;
37437
+ var PREFIX34 = "rafa next", NEXT_IN_PROCESS_ACTIONS, SWITCH_COMMAND, ROADMAP_WORD = "--roadmap", ROADMAP_PASSED_ACTIONS, CLAIM_AHEAD_WORD = "--claim-ahead", ACTION_COMMANDS, NEXT_COMMAND_ACTIONS, LABELS_ONLY_ACTIONS, OFFERED_COMMAND_ACTIONS;
37299
37438
  var init_actions = __esm(() => {
37300
37439
  init_command();
37301
37440
  init_hint();
@@ -39021,7 +39160,7 @@ function moveBlockers(readings, issue4) {
39021
39160
  }
39022
39161
  function planRelationsMove(listing2, from, to) {
39023
39162
  if (from.mode === to.mode) {
39024
- throw new TypeError(`${PREFIX34}: refused to move a board from ${from.mode} to ${to.mode}; the two modes must differ`);
39163
+ throw new TypeError(`${PREFIX35}: refused to move a board from ${from.mode} to ${to.mode}; the two modes must differ`);
39025
39164
  }
39026
39165
  const readings = { from: from.read(listing2), to: to.read(listing2), fromMode: from.mode, toMode: to.mode };
39027
39166
  const members = epicsOf2(listing2).reduce((draft, epic) => joined(draft, to.mode === "native" ? toNativeMembers(readings, epic) : toLabelsMembers(readings, epic)), EMPTY2);
@@ -39157,7 +39296,7 @@ async function writeRelationsMove(seams, plan) {
39157
39296
  const marks = await sendInOrder(plan.marks, (mark) => removeMark(seams.gh, bodies, mark));
39158
39297
  return result(writes, true, marks);
39159
39298
  }
39160
- var PREFIX34 = "board relations move", EMPTY2;
39299
+ var PREFIX35 = "board relations move", EMPTY2;
39161
39300
  var init_move = __esm(() => {
39162
39301
  init_blocked();
39163
39302
  init_epic_checklist();
@@ -51302,6 +51441,643 @@ var init_create = __esm(() => {
51302
51441
  create_default = createIssueCreateCommand();
51303
51442
  });
51304
51443
 
51444
+ // src/board/issue-edit-body.ts
51445
+ function trimmedText(text2, what) {
51446
+ const trimmed = text2.replace(TRAILING_WHITESPACE, "");
51447
+ if (trimmed.trim() === "")
51448
+ throw new RangeError(`${what} holds nothing but whitespace`);
51449
+ return trimmed;
51450
+ }
51451
+ function renderUpdateHeading(now, reason) {
51452
+ const normalised = normaliseReason(reason);
51453
+ if (normalised === "")
51454
+ throw new RangeError("the reason holds nothing but whitespace");
51455
+ return `**Updated ${localDay(now)}, ${normalised}:**`;
51456
+ }
51457
+ function renderUpdateBlock(now, reason, text2) {
51458
+ return `${renderUpdateHeading(now, reason)}
51459
+
51460
+ ${trimmedText(text2, "the added text")}`;
51461
+ }
51462
+ function separatorAfter(body) {
51463
+ if (body === "" || body.endsWith(`
51464
+
51465
+ `))
51466
+ return "";
51467
+ return body.endsWith(`
51468
+ `) ? `
51469
+ ` : `
51470
+
51471
+ `;
51472
+ }
51473
+ function appendedBody(body, block2) {
51474
+ return `${body}${separatorAfter(body)}${block2}`;
51475
+ }
51476
+ function replacedBody(text2) {
51477
+ return trimmedText(text2, "the replacing text");
51478
+ }
51479
+ function carriesUpdateBlock(body, block2) {
51480
+ const trimmed = body.replace(TRAILING_WHITESPACE, "");
51481
+ if (!trimmed.endsWith(block2))
51482
+ return false;
51483
+ const before = trimmed.slice(0, trimmed.length - block2.length);
51484
+ return before === "" || before.endsWith(`
51485
+ `);
51486
+ }
51487
+ function originalPartHolds(before, after, block2) {
51488
+ if (!after.startsWith(before))
51489
+ return false;
51490
+ const added = after.slice(before.length).replace(TRAILING_WHITESPACE, "");
51491
+ return added === `${separatorAfter(before)}${block2}`;
51492
+ }
51493
+ var TRAILING_WHITESPACE;
51494
+ var init_issue_edit_body = __esm(() => {
51495
+ init_epic_trail();
51496
+ init_epics();
51497
+ TRAILING_WHITESPACE = /\s+$/u;
51498
+ });
51499
+
51500
+ // src/board/issue-edit-state.ts
51501
+ import { readdirSync as readdirSync20, statSync as statSync28 } from "fs";
51502
+ import { join as join81, resolve as resolve36 } from "path";
51503
+ function isClaimed(labels) {
51504
+ return labels.includes(CLAIMED_LABEL) || labels.includes(IN_DEVELOPMENT_LABEL);
51505
+ }
51506
+ function refused4(refusal13, branch = null) {
51507
+ return { pass: false, refusal: refusal13, branch };
51508
+ }
51509
+ function readEditState(input) {
51510
+ if (!input.open)
51511
+ return refused4("closed");
51512
+ if (isClaimed(input.labels)) {
51513
+ const branch = branchName(input.issue, input.title);
51514
+ return input.kind === "append" && input.whileInDevelopment ? { pass: true, staleCopy: true, branch } : refused4("in-development", branch);
51515
+ }
51516
+ const planned = input.savedCopies.length > 0;
51517
+ if (input.kind === "append")
51518
+ return { pass: true, staleCopy: planned, branch: null };
51519
+ if (input.labels.includes(SPEC_READY_LABEL))
51520
+ return refused4("spec-ready");
51521
+ if (planned)
51522
+ return refused4("planned");
51523
+ return { pass: true, staleCopy: false, branch: null };
51524
+ }
51525
+ function refreshCommand(issue4) {
51526
+ return `rafa plan create --issue=${String(issue4)} --refresh`;
51527
+ }
51528
+ function staleCopyMessage(issue4, branch) {
51529
+ const n = String(issue4);
51530
+ const refresh = refreshCommand(issue4);
51531
+ if (branch === null) {
51532
+ return `Issue #${n} has a saved copy this edit makes stale: ${refresh} rebuilds it.`;
51533
+ }
51534
+ return `Issue #${n} is under way on ${branch}: the loop plans from the copy it already has, ` + `and ${refresh} rebuilds that copy.`;
51535
+ }
51536
+ function editName(kind) {
51537
+ if (kind === "append")
51538
+ return "An append to";
51539
+ return kind === "replace" ? "A replace of the body of" : "A title change on";
51540
+ }
51541
+ function appendLine2(issue4, extra) {
51542
+ return `rafa issue edit ${String(issue4)} --append-file=<file> --reason=<text>${extra}`;
51543
+ }
51544
+ function editStateRefusalMessage(input, reading2) {
51545
+ if (reading2.pass)
51546
+ throw new TypeError("issue edit state: a passing reading has no refusal to name");
51547
+ const what = `${editName(input.kind)} issue #${String(input.issue)}`;
51548
+ switch (reading2.refusal) {
51549
+ case "closed":
51550
+ return `${what} is refused: the issue is closed, and rafa edits open issues only.`;
51551
+ case "in-development": {
51552
+ const where = `a loop holds it on ${reading2.branch ?? branchName(input.issue, input.title)}`;
51553
+ return input.kind === "append" ? `${what} is refused: ${where}; add --while-in-development for an amendment the loop must not miss.` : `${what} is refused: ${where}, and a spec under way changes by append: ${appendLine2(input.issue, " --while-in-development")}`;
51554
+ }
51555
+ case "spec-ready":
51556
+ return `${what} is refused: it carries ${SPEC_READY_LABEL}, and a ready spec changes by append: ${appendLine2(input.issue, "")}`;
51557
+ case "planned":
51558
+ return `${what} is refused: it has a saved copy under specs.dir, and a planned spec changes by append: ${appendLine2(input.issue, "")}`;
51559
+ }
51560
+ }
51561
+ function isSavedCopyName(name2, issue4) {
51562
+ const prefix = `${boardId(issue4)}-`;
51563
+ return name2.startsWith(prefix) && name2.endsWith(SPEC_EXTENSION) && name2.length > prefix.length + SPEC_EXTENSION.length && name2 !== notesFileName(issue4);
51564
+ }
51565
+ function findSavedCopies(root, specsDir, issue4) {
51566
+ const dir = resolve36(root, specsDir);
51567
+ let names2;
51568
+ try {
51569
+ names2 = readdirSync20(dir);
51570
+ } catch (error) {
51571
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
51572
+ return [];
51573
+ throw error;
51574
+ }
51575
+ return names2.filter((name2) => isSavedCopyName(name2, issue4)).filter((name2) => statSync28(join81(dir, name2)).isFile()).sort().map((name2) => join81(specsDir, name2));
51576
+ }
51577
+ var init_issue_edit_state = __esm(() => {
51578
+ init_stale();
51579
+ init_naming();
51580
+ init_readiness();
51581
+ });
51582
+
51583
+ // src/board/issue-edit-text.ts
51584
+ function addedTextSource(issue4) {
51585
+ return `the text added to issue #${String(issue4)}`;
51586
+ }
51587
+ function editedBodySource(issue4) {
51588
+ return `issue #${String(issue4)} as edited`;
51589
+ }
51590
+ function completenessApplies(labels, before) {
51591
+ return hasSpecLabel(labels) && findReadinessGaps(before).length === 0;
51592
+ }
51593
+ function requireEditText(input) {
51594
+ requireNoLeak(addedTextSource(input.issue), input.added);
51595
+ if (!completenessApplies(input.labels, input.before))
51596
+ return;
51597
+ requireCompleteSpec(editedBodySource(input.issue), input.after);
51598
+ }
51599
+ var init_issue_edit_text = __esm(() => {
51600
+ init_issue();
51601
+ init_leak();
51602
+ init_readiness();
51603
+ });
51604
+
51605
+ // src/commands/issue/edit-run.ts
51606
+ import { existsSync as existsSync54, mkdirSync as mkdirSync21, writeFileSync as writeFileSync22 } from "fs";
51607
+ import { join as join82 } from "path";
51608
+ function editExitCode(outcome2) {
51609
+ return outcome2 === "conflict" ? EDIT_CONFLICT_EXIT : 0;
51610
+ }
51611
+ function renderReading(reading2) {
51612
+ return `${reading2.gate}: ${reading2.line}`;
51613
+ }
51614
+ function readSwitch3(flags, name2) {
51615
+ const value = flags[name2];
51616
+ if (value === undefined)
51617
+ return false;
51618
+ if (typeof value === "boolean")
51619
+ return value;
51620
+ if (value === "true" || value === "false")
51621
+ return value === "true";
51622
+ throw lineRefusal(`--${name2} takes no value, and read "${value}" as one`, EDIT_USAGE);
51623
+ }
51624
+ function readEditIssue(args) {
51625
+ if (args.length > 1) {
51626
+ throw lineRefusal(`Expected one issue number, got ${String(args.length)}: ${args.join(" ")}`, EDIT_USAGE);
51627
+ }
51628
+ const word = args[0];
51629
+ if (word === undefined)
51630
+ throw lineRefusal("Name the issue number to edit", EDIT_USAGE);
51631
+ if (!ISSUE_NUMBER13.test(word)) {
51632
+ throw lineRefusal(`"${word}" is no issue number, which is a whole number from 1`, EDIT_USAGE);
51633
+ }
51634
+ return Number(word);
51635
+ }
51636
+ function notTogether(first, second, why) {
51637
+ return lineRefusal(`--${first} and --${second} cannot be used together: ${why}`, EDIT_USAGE);
51638
+ }
51639
+ function readEditFlags(flags) {
51640
+ return {
51641
+ appendFile: readNonBlankFlag(flags, FLAG2.appendFile, EDIT_USAGE),
51642
+ append: readNonBlankFlag(flags, FLAG2.append, EDIT_USAGE),
51643
+ reason: readNonBlankFlag(flags, FLAG2.reason, EDIT_USAGE),
51644
+ replaceFile: readNonBlankFlag(flags, FLAG2.replaceFile, EDIT_USAGE),
51645
+ title: readNonBlankFlag(flags, FLAG2.title, EDIT_USAGE),
51646
+ whileInDevelopment: readSwitch3(flags, FLAG2.whileInDevelopment),
51647
+ dryRun: readSwitch3(flags, FLAG2.dryRun)
51648
+ };
51649
+ }
51650
+ function appendLine3(issue4, read) {
51651
+ if (read.appendFile !== undefined && read.append !== undefined) {
51652
+ throw notTogether(FLAG2.appendFile, FLAG2.append, "name the added text one way");
51653
+ }
51654
+ if (read.title !== undefined)
51655
+ throw notTogether(FLAG2.title, FLAG2.append, "a title changes under the gates of a replace");
51656
+ if (read.reason === undefined) {
51657
+ throw lineRefusal(`--${FLAG2.reason} is required with an append: --${FLAG2.reason}=<text>`, EDIT_USAGE);
51658
+ }
51659
+ const source = read.appendFile === undefined ? { from: "line", text: read.append ?? "" } : { from: "file", flag: FLAG2.appendFile, path: read.appendFile };
51660
+ return { ...lineOf3(issue4, read, "append", source), reason: read.reason, whileInDevelopment: read.whileInDevelopment };
51661
+ }
51662
+ function lineOf3(issue4, read, kind, source) {
51663
+ return { issue: issue4, kind, source, reason: null, title: read.title ?? null, whileInDevelopment: false, dryRun: read.dryRun };
51664
+ }
51665
+ function readEditLine(args, flags) {
51666
+ const issue4 = readEditIssue(args);
51667
+ const read = readEditFlags(flags);
51668
+ const appending = read.appendFile !== undefined || read.append !== undefined;
51669
+ if (appending && read.replaceFile !== undefined) {
51670
+ const appendFlag = read.appendFile === undefined ? FLAG2.append : FLAG2.appendFile;
51671
+ throw notTogether(appendFlag, FLAG2.replaceFile, "an edit either appends or replaces");
51672
+ }
51673
+ if (appending)
51674
+ return appendLine3(issue4, read);
51675
+ if (read.reason !== undefined) {
51676
+ throw lineRefusal(`--${FLAG2.reason} goes with an append only: a replace and a title change take none`, EDIT_USAGE);
51677
+ }
51678
+ if (read.whileInDevelopment) {
51679
+ throw lineRefusal(`--${FLAG2.whileInDevelopment} goes with an append only: a spec under way is never replaced or retitled`, EDIT_USAGE);
51680
+ }
51681
+ if (read.replaceFile !== undefined) {
51682
+ return lineOf3(issue4, read, "replace", { from: "file", flag: FLAG2.replaceFile, path: read.replaceFile });
51683
+ }
51684
+ if (read.title !== undefined)
51685
+ return lineOf3(issue4, read, "title", null);
51686
+ throw lineRefusal(`Name the edit: --${FLAG2.appendFile}, --${FLAG2.append}, --${FLAG2.replaceFile} or --${FLAG2.title}`, EDIT_USAGE);
51687
+ }
51688
+ async function readEditText(source, stdin) {
51689
+ if (source.from === "line")
51690
+ return source.text;
51691
+ const where = source.path === STDIN_PATH2 ? "standard input" : `"${source.path}"`;
51692
+ let text2;
51693
+ try {
51694
+ text2 = source.path === STDIN_PATH2 ? await stdin() : await Bun.file(source.path).text();
51695
+ } catch (error) {
51696
+ throw lineRefusal(`--${source.flag} cannot read ${where}: ${messageOf(error)}`, EDIT_USAGE);
51697
+ }
51698
+ if (text2.trim() === "")
51699
+ throw lineRefusal(`--${source.flag} read nothing but whitespace from ${where}`, EDIT_USAGE);
51700
+ return text2;
51701
+ }
51702
+ async function readEditRequest(line, stdin) {
51703
+ const { source, ...rest } = line;
51704
+ if (source === null)
51705
+ return { ...rest, text: null };
51706
+ return { ...rest, text: await readEditText(source, stdin) };
51707
+ }
51708
+ function requireEditingTracker(tracker) {
51709
+ if (holdsEditing(tracker))
51710
+ return tracker;
51711
+ throw new CommandExit(1, `\u274C The ${tracker.kind} tracker cannot edit an issue: it reads and writes no issue body.`);
51712
+ }
51713
+ function holdsEditing(tracker) {
51714
+ return tracker.editable !== undefined && tracker.edit !== undefined;
51715
+ }
51716
+ function planEdit(request, found, now) {
51717
+ const titled = request.title === null ? {} : { title: request.title };
51718
+ if (request.kind === "append") {
51719
+ const block2 = renderUpdateBlock(now, request.reason ?? "", request.text ?? "");
51720
+ const already = carriesUpdateBlock(found.body, block2);
51721
+ const after = already ? found.body : appendedBody(found.body, block2);
51722
+ return { added: request.text ?? "", shown: block2, block: block2, after, already, change: { body: after } };
51723
+ }
51724
+ if (request.kind === "replace") {
51725
+ const body = replacedBody(request.text ?? "");
51726
+ const added = [request.title, body].filter((part) => part !== null).join(`
51727
+
51728
+ `);
51729
+ return { added, shown: added, block: null, after: body, already: false, change: { body, ...titled } };
51730
+ }
51731
+ const title2 = request.title ?? "";
51732
+ return { added: title2, shown: title2, block: null, after: found.body, already: false, change: { title: title2 } };
51733
+ }
51734
+ function trustedLine(reading2, repo) {
51735
+ return reading2.source === "allow-list" ? `${reading2.login}, listed in board.trustedAuthors` : `${reading2.login}, who has write access to ${repo}`;
51736
+ }
51737
+ function gatesFor(request) {
51738
+ const readings = [];
51739
+ return {
51740
+ readings,
51741
+ pass: (gate, line) => {
51742
+ readings.push({ gate, line });
51743
+ },
51744
+ refuse: (refusal13, sentence) => {
51745
+ throw new EditRefusal(refusal13, sentence, [...readings], request.dryRun);
51746
+ }
51747
+ };
51748
+ }
51749
+ async function weighOwnership(options, found, gates) {
51750
+ const { request, tracker } = options;
51751
+ if (tracker.kind === ACCOUNTLESS_KIND) {
51752
+ gates.pass("editor", LOCAL_OWNERSHIP);
51753
+ gates.pass("author", LOCAL_OWNERSHIP);
51754
+ return;
51755
+ }
51756
+ const trust = options.trust();
51757
+ const item = { kind: "issue", number: request.issue, repo: trust.repo };
51758
+ const editor = await readEditorTrust(trust, options.editor);
51759
+ if (!editor.trusted || editor.reading === null) {
51760
+ gates.refuse(editor.refusal ?? "ownership-unknown", editorRefusalMessage(item, editor));
51761
+ }
51762
+ gates.pass("editor", trustedLine(editor.reading, trust.repo));
51763
+ const author = await readBoardTrust(trust, found.author);
51764
+ if (!author.trusted) {
51765
+ const refusal13 = author.refusal === "lookup-failed" ? "ownership-unknown" : "ownership";
51766
+ gates.refuse(refusal13, trustRefusalMessage(item, author));
51767
+ }
51768
+ gates.pass("author", `#${String(request.issue)} was opened by ${trustedLine(author, trust.repo)}`);
51769
+ }
51770
+ function weighText(request, found, planned, gates) {
51771
+ try {
51772
+ requireEditText({
51773
+ issue: request.issue,
51774
+ labels: found.labels,
51775
+ before: found.body,
51776
+ added: planned.added,
51777
+ after: planned.after
51778
+ });
51779
+ } catch (error) {
51780
+ if (error instanceof CommandExit)
51781
+ gates.refuse("text", error.message);
51782
+ throw error;
51783
+ }
51784
+ const complete = completenessApplies(found.labels, found.body) ? ", and the edited body still fills the spec template" : "";
51785
+ gates.pass("text", `the added text names no machine path or credential${complete}`);
51786
+ }
51787
+ function stateLine(input, reading2) {
51788
+ if (reading2.branch !== null) {
51789
+ return `a loop holds it on ${reading2.branch}; --while-in-development lets the append through`;
51790
+ }
51791
+ if (input.savedCopies.length > 0)
51792
+ return `planned, its saved copy at ${input.savedCopies.join(", ")}`;
51793
+ return input.labels.length === 0 ? "open, with no plan and no loop building from it" : `open, labelled ${input.labels.join(", ")}, with no saved copy and no loop building from it`;
51794
+ }
51795
+ function weighState(options, found, gates) {
51796
+ const { request } = options;
51797
+ const input = {
51798
+ issue: request.issue,
51799
+ title: found.title,
51800
+ open: found.open,
51801
+ labels: found.labels,
51802
+ savedCopies: findSavedCopies(options.root, options.specsDir, request.issue),
51803
+ kind: request.kind,
51804
+ whileInDevelopment: request.whileInDevelopment
51805
+ };
51806
+ const reading2 = readEditState(input);
51807
+ if (!reading2.pass)
51808
+ gates.refuse(reading2.refusal, editStateRefusalMessage(input, reading2));
51809
+ gates.pass("state", stateLine(input, reading2));
51810
+ return reading2;
51811
+ }
51812
+ function normalised(text2) {
51813
+ return text2.replace(/\r\n/gu, `
51814
+ `).replace(TRAILING_WHITESPACE2, "");
51815
+ }
51816
+ function writeHeld(found, planned, reread) {
51817
+ const { change } = planned;
51818
+ if (change.title !== undefined && reread.title !== change.title)
51819
+ return false;
51820
+ const before = found.body.replace(/\r\n/gu, `
51821
+ `);
51822
+ const after = reread.body.replace(/\r\n/gu, `
51823
+ `);
51824
+ if (planned.block !== null)
51825
+ return originalPartHolds(before, after, normalised(planned.block));
51826
+ return normalised(after) === normalised(change.body ?? found.body);
51827
+ }
51828
+ function conflictPaths(issue4, stamp2, attempt5) {
51829
+ const suffix = attempt5 === 1 ? "" : `-${String(attempt5)}`;
51830
+ const name2 = (side) => join82(EDIT_SCRATCH_DIR, `${boardId(issue4)}-edit-${side}-${stamp2}${suffix}.md`);
51831
+ return { before: name2("before"), after: name2("after") };
51832
+ }
51833
+ function saveConflictBodies(root, issue4, now, before, after) {
51834
+ mkdirSync21(join82(root, EDIT_SCRATCH_DIR), { recursive: true });
51835
+ const stamp2 = fileStamp(now);
51836
+ let attempt5 = 1;
51837
+ let paths = conflictPaths(issue4, stamp2, attempt5);
51838
+ while (existsSync54(join82(root, paths.before)) || existsSync54(join82(root, paths.after))) {
51839
+ attempt5 += 1;
51840
+ paths = conflictPaths(issue4, stamp2, attempt5);
51841
+ }
51842
+ writeFileSync22(join82(root, paths.before), before, { flag: "wx" });
51843
+ writeFileSync22(join82(root, paths.after), after, { flag: "wx" });
51844
+ return paths;
51845
+ }
51846
+ function heldOutcome(request, state) {
51847
+ if (request.kind !== "append")
51848
+ return "replaced";
51849
+ return state.pass && state.staleCopy ? "stale-copy" : "appended";
51850
+ }
51851
+ function outcomeMessage(request, outcome2, branch, name2) {
51852
+ switch (outcome2) {
51853
+ case "appended":
51854
+ return `Appended an update to ${name2}; the original part is unchanged.`;
51855
+ case "stale-copy":
51856
+ return `Appended an update to ${name2}. ${staleCopyMessage(request.issue, branch)}`;
51857
+ case "already":
51858
+ return `${name2} already ends with this update; nothing was written.`;
51859
+ case "replaced": {
51860
+ if (request.text === null)
51861
+ return `Changed the title of ${name2}.`;
51862
+ return request.title === null ? `Replaced the body of ${name2}.` : `Replaced the body and the title of ${name2}.`;
51863
+ }
51864
+ case "conflict":
51865
+ throw new TypeError("issue edit: a conflict is worded with the paths it saved");
51866
+ }
51867
+ }
51868
+ function dryRunMessage(issue4, due, branch) {
51869
+ const stale = due === "stale-copy" ? ` ${staleCopyMessage(issue4, branch)}` : "";
51870
+ return `--dry-run: nothing was written; the edit would answer ${due}.${stale}`;
51871
+ }
51872
+ function conflictMessage(name2, scratch) {
51873
+ return `The edit was written, but ${name2} read back otherwise: somebody changed it around the write. ` + `The body read before is at ${scratch.before} and the body read after at ${scratch.after}; ` + "compare the two and make the edit again over the body as it stands.";
51874
+ }
51875
+ function report2(request, planned, readings, fields) {
51876
+ return Object.freeze({
51877
+ ...fields,
51878
+ issue: request.issue,
51879
+ exitCode: editExitCode(fields.outcome),
51880
+ dryRun: request.dryRun,
51881
+ added: planned.shown,
51882
+ readings,
51883
+ refresh: fields.outcome === "stale-copy" ? refreshCommand(request.issue) : null,
51884
+ scratch: null
51885
+ });
51886
+ }
51887
+ async function runIssueEdit(options) {
51888
+ const { request, tracker } = options;
51889
+ const ref15 = issueRef(tracker, String(request.issue));
51890
+ const name2 = issueName(ref15);
51891
+ const now = options.now();
51892
+ const found = await onTracker(tracker, `read ${name2}`, () => tracker.editable(ref15));
51893
+ const gates = gatesFor(request);
51894
+ await weighOwnership(options, found, gates);
51895
+ const planned = planEdit(request, found, now);
51896
+ weighText(request, found, planned, gates);
51897
+ const state = weighState(options, found, gates);
51898
+ const { branch } = state;
51899
+ function done(outcome2, written3, message) {
51900
+ return report2(request, planned, gates.readings, { outcome: outcome2, written: written3, branch, message });
51901
+ }
51902
+ const due = planned.already ? "already" : heldOutcome(request, state);
51903
+ if (request.dryRun)
51904
+ return done(due, false, dryRunMessage(request.issue, due, branch));
51905
+ if (planned.already)
51906
+ return done("already", false, outcomeMessage(request, "already", branch, name2));
51907
+ await onTracker(tracker, `edit ${name2}`, () => tracker.edit(ref15, planned.change));
51908
+ const reread = await onTracker(tracker, `read ${name2} again after the edit was written`, () => tracker.editable(ref15));
51909
+ if (writeHeld(found, planned, reread))
51910
+ return done(due, true, outcomeMessage(request, due, branch, name2));
51911
+ const scratch = saveConflictBodies(options.root, request.issue, now, found.body, reread.body);
51912
+ return Object.freeze({ ...done("conflict", true, conflictMessage(name2, scratch)), scratch });
51913
+ }
51914
+ function renderEditReport(made) {
51915
+ if (!made.dryRun)
51916
+ return [made.message];
51917
+ return [...made.readings.map(renderReading), "added:", made.added, made.message];
51918
+ }
51919
+ async function editIssue2(context, seams) {
51920
+ const line = readEditLine(context.args, context.flags);
51921
+ const request = await readEditRequest(line, seams.stdin ?? (() => Bun.stdin.text()));
51922
+ const project = issueProject(context);
51923
+ const { tracker, data } = await resolveIssueTracker(context, seams);
51924
+ const editing = requireEditingTracker(tracker);
51925
+ const config = issueSubjectConfig(project, () => {
51926
+ return;
51927
+ });
51928
+ const gh = seams.gh ?? createGhRunner({ cwd: project.root });
51929
+ const openGit = seams.openGit ?? createGitRunner;
51930
+ const made = await runIssueEdit({
51931
+ request,
51932
+ tracker: editing,
51933
+ root: project.root,
51934
+ specsDir: config.specsDir,
51935
+ trust: () => ghBoardTrust({
51936
+ gh,
51937
+ trustedAuthors: config.boardTrustedAuthors,
51938
+ repo: boardRepoLabel(openGit(project.root))
51939
+ }),
51940
+ editor: createGhEditorLogin({ gh }),
51941
+ now: seams.now ?? (() => new Date)
51942
+ });
51943
+ return Object.freeze({ ...made, tracker: data });
51944
+ }
51945
+ var EDIT_USAGE, EDIT_REFUSAL_EXIT, EDIT_CONFLICT_EXIT = 1, EDIT_SCRATCH_DIR, STDIN_PATH2 = "-", ISSUE_NUMBER13, TRAILING_WHITESPACE2, ACCOUNTLESS_KIND = "local", LOCAL_OWNERSHIP = "local: the local tracker holds no accounts, so nobody is asked about", FLAG2, EditRefusal;
51946
+ var init_edit_run = __esm(() => {
51947
+ init_github();
51948
+ init_issue_edit_body();
51949
+ init_issue_edit_state();
51950
+ init_issue_edit_text();
51951
+ init_naming();
51952
+ init_plan_spec();
51953
+ init_trust();
51954
+ init_command();
51955
+ init_config_sections();
51956
+ init_git();
51957
+ init_fix_schema2();
51958
+ init_issue_tracker();
51959
+ EDIT_USAGE = "rafa issue edit <n> (--append-file=<file> | --append=<text>) --reason=<text>" + " [--while-in-development] | (--replace-file=<file> [--title=<text>] | --title=<text>) [--dry-run]";
51960
+ EDIT_REFUSAL_EXIT = TRUST_REFUSAL_EXIT;
51961
+ EDIT_SCRATCH_DIR = join82(".rafa", "scratch");
51962
+ ISSUE_NUMBER13 = /^[1-9]\d*$/u;
51963
+ TRAILING_WHITESPACE2 = /\s+$/u;
51964
+ FLAG2 = Object.freeze({
51965
+ appendFile: "append-file",
51966
+ append: "append",
51967
+ reason: "reason",
51968
+ replaceFile: "replace-file",
51969
+ title: "title",
51970
+ dryRun: "dry-run",
51971
+ whileInDevelopment: "while-in-development"
51972
+ });
51973
+ EditRefusal = class EditRefusal extends CommandExit {
51974
+ refusal;
51975
+ readings;
51976
+ constructor(refusal13, sentence, readings, dryRun) {
51977
+ const passed2 = dryRun ? readings.map(renderReading) : [];
51978
+ super(EDIT_REFUSAL_EXIT, [...passed2, `\u274C refused ${refusal13}: ${sentence}`].join(`
51979
+ `));
51980
+ this.refusal = refusal13;
51981
+ this.readings = readings;
51982
+ }
51983
+ };
51984
+ });
51985
+
51986
+ // src/commands/issue/edit.ts
51987
+ function createIssueEditCommand(seams = DEFAULT_EDIT_SEAMS) {
51988
+ const command = {
51989
+ name: "issue edit",
51990
+ subject: "issue",
51991
+ action: "edit",
51992
+ summary: "append a dated update to an issue body, or replace its body or title, after four gates",
51993
+ description: "Edits one issue on the tracker the chain lands on, after four gates weighed in order, the first" + " refusal ending the run with nothing written and exit code 2: the account editing must have write" + " access or be in board.trustedAuthors; so must the issue's author; the text added must leak nothing," + " and a complete spec must stay complete; and the issue must be open, its labels and saved spec copies" + " allowing the edit. An append (`--append-file` or `--append`, with `--reason`) adds a block" + " `**Updated <YYYY-MM-DD>, <reason>:**` below the body, leaving the original part byte for byte; the" + " same block already closing the body answers `already`. `--replace-file` replaces the body and" + " `--title` the title. After the write the issue is read again: a body that no longer holds what was" + " written answers `conflict` with exit code 1, the bodies read before and after saved under" + " `.rafa/scratch/`. An append on an issue already planned answers `stale-copy` and names" + " `rafa plan create --issue=<n> --refresh`, which it never runs. `--dry-run` prints each gate's" + " reading and the added part and writes nothing. With `--output=json` the report and the tracker are" + " the data of the terminal result event.",
51994
+ args: [
51995
+ {
51996
+ name: "n",
51997
+ description: "The issue number on the tracker.",
51998
+ type: "string",
51999
+ required: true
52000
+ }
52001
+ ],
52002
+ flags: [
52003
+ {
52004
+ name: "append-file",
52005
+ description: "A file holding the text to append, read whole; `-` reads standard input." + " Needs --reason. Not with --append, --replace-file or --title.",
52006
+ type: "string"
52007
+ },
52008
+ {
52009
+ name: "append",
52010
+ description: "The text to append. Needs --reason. Not with --append-file, --replace-file or --title.",
52011
+ type: "string"
52012
+ },
52013
+ {
52014
+ name: "reason",
52015
+ description: "Why the update was made, written into its heading. Required with an append, refused otherwise.",
52016
+ type: "string"
52017
+ },
52018
+ {
52019
+ name: "replace-file",
52020
+ description: "A file holding the new body, read whole; `-` reads standard input. Not with an append.",
52021
+ type: "string"
52022
+ },
52023
+ {
52024
+ name: "title",
52025
+ description: "The new title, alone or beside --replace-file. Not with an append.",
52026
+ type: "string"
52027
+ },
52028
+ {
52029
+ name: "while-in-development",
52030
+ description: "Allows an append on an issue a loop has claimed, answering stale-copy. Only with an append.",
52031
+ type: "boolean"
52032
+ },
52033
+ {
52034
+ name: "dry-run",
52035
+ description: "Prints the added part and each gate's reading and writes nothing; a refusal still exits 2.",
52036
+ type: "boolean"
52037
+ }
52038
+ ],
52039
+ examples: [
52040
+ {
52041
+ cmd: 'rafa issue edit 57 --append-file=update.md --reason="scope narrowed"',
52042
+ note: "Appends what update.md holds to #57 below a dated heading naming the reason."
52043
+ },
52044
+ {
52045
+ cmd: 'rafa issue edit 57 --append="Also covers --dry-run." --reason=clarified --dry-run',
52046
+ note: "Prints each gate's reading and the block it would append, and writes nothing."
52047
+ },
52048
+ {
52049
+ cmd: 'rafa issue edit 57 --title="Faster plan show"',
52050
+ note: "Changes the title of #57 and leaves its body alone."
52051
+ },
52052
+ {
52053
+ cmd: "rafa issue edit 57 --replace-file=spec.md --output=json",
52054
+ note: "Replaces the body of #57; the report and the tracker are the data of the result event."
52055
+ }
52056
+ ],
52057
+ outputs: ["text", "json"],
52058
+ run: async (context) => {
52059
+ const made = await editIssue2(context, seams);
52060
+ if (made.exitCode !== 0)
52061
+ throw new CommandExit(made.exitCode, renderEditReport(made).join(`
52062
+ `));
52063
+ if (context.outputMode === "json") {
52064
+ context.output.result(made);
52065
+ return;
52066
+ }
52067
+ for (const line of renderEditReport(made))
52068
+ context.output.info(line);
52069
+ }
52070
+ };
52071
+ return Object.freeze(command);
52072
+ }
52073
+ var DEFAULT_EDIT_SEAMS, edit_default;
52074
+ var init_edit = __esm(() => {
52075
+ init_command();
52076
+ init_edit_run();
52077
+ DEFAULT_EDIT_SEAMS = Object.freeze({});
52078
+ edit_default = createIssueEditCommand();
52079
+ });
52080
+
51305
52081
  // src/commands/issue/move.ts
51306
52082
  async function moveIssue(context, seams) {
51307
52083
  const [id3, word] = expectTwoArguments(context.args, USAGE28);
@@ -51983,10 +52759,10 @@ var init_pr_lifecycle = __esm(() => {
51983
52759
  });
51984
52760
 
51985
52761
  // src/notices/notices.ts
51986
- import { mkdirSync as mkdirSync21, readFileSync as readFileSync62, writeFileSync as writeFileSync22 } from "fs";
51987
- import { dirname as dirname29, join as join81 } from "path";
52762
+ import { mkdirSync as mkdirSync22, readFileSync as readFileSync62, writeFileSync as writeFileSync23 } from "fs";
52763
+ import { dirname as dirname29, join as join83 } from "path";
51988
52764
  function noticesPath(home) {
51989
- return join81(home, ".rafa", "notices.json");
52765
+ return join83(home, ".rafa", "notices.json");
51990
52766
  }
51991
52767
  function isNoticeId(value) {
51992
52768
  return typeof value === "string" && NOTICE_IDS.includes(value);
@@ -52013,8 +52789,8 @@ function readDismissed(home) {
52013
52789
  function writeDismissed(home, ids) {
52014
52790
  const all = NOTICE_IDS.filter((id3) => ids.includes(id3) || readDismissed(home).includes(id3));
52015
52791
  const path4 = noticesPath(home);
52016
- mkdirSync21(dirname29(path4), { recursive: true });
52017
- writeFileSync22(path4, `${JSON.stringify({ dismissed: all }, null, 2)}
52792
+ mkdirSync22(dirname29(path4), { recursive: true });
52793
+ writeFileSync23(path4, `${JSON.stringify({ dismissed: all }, null, 2)}
52018
52794
  `);
52019
52795
  }
52020
52796
  function pendingNotices(dismissed) {
@@ -52133,9 +52909,9 @@ var init_budget = __esm(() => {
52133
52909
  });
52134
52910
 
52135
52911
  // src/start/checkout-guard.ts
52136
- import { existsSync as existsSync54, realpathSync as realpathSync14 } from "fs";
52912
+ import { existsSync as existsSync55, realpathSync as realpathSync14 } from "fs";
52137
52913
  function realDirOf(path4) {
52138
- if (!existsSync54(path4))
52914
+ if (!existsSync55(path4))
52139
52915
  return null;
52140
52916
  try {
52141
52917
  return realpathSync14(path4);
@@ -52446,17 +53222,17 @@ function truncateDescription(description, budget) {
52446
53222
  const cut2 = lastSpace > 0 ? window.slice(0, lastSpace) : window.slice(0, budget);
52447
53223
  return balanceCodeSpans(cut2.trimEnd()).replace(/[\s,;:-]+$/, "");
52448
53224
  }
52449
- function subjectDescription(normalised) {
52450
- const clause = firstClause(normalised);
52451
- const description = clause.length === 0 ? normalised : clause;
53225
+ function subjectDescription(normalised2) {
53226
+ const clause = firstClause(normalised2);
53227
+ const description = clause.length === 0 ? normalised2 : clause;
52452
53228
  return lowerFirstWord(stripTrailingPeriod(description));
52453
53229
  }
52454
53230
  function deriveCommitSubject(taskText, options = {}) {
52455
53231
  const type = inferCommitType(taskText);
52456
53232
  const scope = options.scope;
52457
53233
  const prefix = scope === undefined || scope.length === 0 ? `${type}: ` : `${type}(${scope}): `;
52458
- const normalised = normaliseTaskText(taskText);
52459
- const uncut = normalised.length === 0 ? FALLBACK_DESCRIPTION : subjectDescription(normalised);
53234
+ const normalised2 = normaliseTaskText(taskText);
53235
+ const uncut = normalised2.length === 0 ? FALLBACK_DESCRIPTION : subjectDescription(normalised2);
52460
53236
  const maxLength = options.maxLength ?? MAX_SUBJECT_LENGTH;
52461
53237
  const description = truncateDescription(uncut, maxLength - prefix.length);
52462
53238
  const truncated = description !== uncut;
@@ -52623,11 +53399,11 @@ function indentBlock(text2) {
52623
53399
  `);
52624
53400
  }
52625
53401
  function holdsOf(reading2) {
52626
- const { report: report2, issues } = reading2;
53402
+ const { report: report3, issues } = reading2;
52627
53403
  const holds2 = [];
52628
- if (report2.status === "blocked")
53404
+ if (report3.status === "blocked")
52629
53405
  holds2.push("status: blocked");
52630
- for (const blocker of report2.blockers) {
53406
+ for (const blocker of report3.blockers) {
52631
53407
  holds2.push(`blocker: ${blocker.what ?? "an entry with no usable what"}`);
52632
53408
  }
52633
53409
  for (const issue4 of issues) {
@@ -52810,10 +53586,10 @@ function openingLine(route, plan) {
52810
53586
  \uD83C\uDF3F Switching to the existing ${plan.branch}.`;
52811
53587
  }
52812
53588
  function runRoute(route, plan, git) {
52813
- const refused4 = treeRefusal(readWorkingTree(git, plan), plan);
52814
- if (refused4 !== null)
53589
+ const refused5 = treeRefusal(readWorkingTree(git, plan), plan);
53590
+ if (refused5 !== null)
52815
53591
  throw new CommandExit(1, `
52816
- ${refused4}`);
53592
+ ${refused5}`);
52817
53593
  const steps = branchSteps(route, plan);
52818
53594
  activeOutput().info(openingLine(route, plan));
52819
53595
  for (const step2 of steps) {
@@ -53031,9 +53807,9 @@ var init_claim_catch_up = __esm(() => {
53031
53807
 
53032
53808
  // src/start/worktree.ts
53033
53809
  import { realpathSync as realpathSync15 } from "fs";
53034
- import { join as join82, resolve as resolve36 } from "path";
53810
+ import { join as join84, resolve as resolve37 } from "path";
53035
53811
  function worktreePathFor(projectRoot3, worktreeDir, stub) {
53036
- return join82(worktreeDirAt(projectRoot3, worktreeDir), stub);
53812
+ return join84(worktreeDirAt(projectRoot3, worktreeDir), stub);
53037
53813
  }
53038
53814
  function addStep2(route, plan, path4) {
53039
53815
  const label = `add the worktree for ${plan.branch} at ${path4}`;
@@ -53089,7 +53865,7 @@ function canonicalPath(path4) {
53089
53865
  try {
53090
53866
  return realpathSync15(path4);
53091
53867
  } catch {
53092
- return resolve36(path4);
53868
+ return resolve37(path4);
53093
53869
  }
53094
53870
  }
53095
53871
  function readExisting(listing2, branch, path4) {
@@ -53282,7 +54058,7 @@ function none(reason, detail) {
53282
54058
  return Object.freeze({ owns: null, reason, detail });
53283
54059
  }
53284
54060
  function issueNumberOf2(issue4) {
53285
- const digits = ISSUE_NUMBER13.exec(issue4?.trim() ?? "")?.[1];
54061
+ const digits = ISSUE_NUMBER14.exec(issue4?.trim() ?? "")?.[1];
53286
54062
  if (digits === undefined)
53287
54063
  return null;
53288
54064
  const number = Number(digits);
@@ -53347,7 +54123,7 @@ async function readPlanOwns(options) {
53347
54123
  return slug;
53348
54124
  return epicOwns(issue4, slug, options.gh);
53349
54125
  }
53350
- var ISSUE_NUMBER13;
54126
+ var ISSUE_NUMBER14;
53351
54127
  var init_owns = __esm(() => {
53352
54128
  init_board_owns();
53353
54129
  init_epic_context();
@@ -53355,7 +54131,7 @@ var init_owns = __esm(() => {
53355
54131
  init_issue();
53356
54132
  init_roadmap_board();
53357
54133
  init_config_sections();
53358
- ISSUE_NUMBER13 = /^#?(\d+)$/u;
54134
+ ISSUE_NUMBER14 = /^#?(\d+)$/u;
53359
54135
  });
53360
54136
 
53361
54137
  // src/suite/unhandled.ts
@@ -53442,8 +54218,8 @@ var init_unhandled = __esm(() => {
53442
54218
  });
53443
54219
 
53444
54220
  // src/suite/run.ts
53445
- import { existsSync as existsSync55, mkdirSync as mkdirSync22, readFileSync as readFileSync63, rmSync as rmSync12, writeFileSync as writeFileSync23 } from "fs";
53446
- import { basename as basename22, dirname as dirname30, extname as extname2, isAbsolute as isAbsolute21, join as join83 } from "path";
54221
+ import { existsSync as existsSync56, mkdirSync as mkdirSync23, readFileSync as readFileSync63, rmSync as rmSync12, writeFileSync as writeFileSync24 } from "fs";
54222
+ import { basename as basename22, dirname as dirname30, extname as extname2, isAbsolute as isAbsolute21, join as join85 } from "path";
53447
54223
  function readNoTestFiles(stderr) {
53448
54224
  return NO_TEST_FILES_LINE.test(stderr);
53449
54225
  }
@@ -53460,7 +54236,7 @@ function suiteCommand(junitFile, paths, changedSince) {
53460
54236
  function outputFileFor(junitFile) {
53461
54237
  const name2 = basename22(junitFile);
53462
54238
  const stem = name2.endsWith(JUNIT_SUFFIX) ? name2.slice(0, -JUNIT_SUFFIX.length) : name2.slice(0, name2.length - extname2(name2).length);
53463
- return join83(dirname30(junitFile), `${stem}${OUTPUT_SUFFIX}`);
54239
+ return join85(dirname30(junitFile), `${stem}${OUTPUT_SUFFIX}`);
53464
54240
  }
53465
54241
  function suiteEnv(base) {
53466
54242
  return Object.fromEntries(Object.entries(base).filter(([key]) => key !== CLAUDE_CODE_ENV));
@@ -53563,7 +54339,7 @@ function parseJunitFailures(xml) {
53563
54339
  return walk.failures;
53564
54340
  }
53565
54341
  function readJunit(path4) {
53566
- if (!existsSync55(path4))
54342
+ if (!existsSync56(path4))
53567
54343
  return { junit: "missing", failures: [] };
53568
54344
  let text2;
53569
54345
  try {
@@ -53591,13 +54367,13 @@ async function runSuite(options) {
53591
54367
  }
53592
54368
  const command = suiteCommand(options.junitFile, options.paths, options.changedSince);
53593
54369
  const outputFile = outputFileFor(options.junitFile);
53594
- mkdirSync22(dirname30(options.junitFile), { recursive: true });
54370
+ mkdirSync23(dirname30(options.junitFile), { recursive: true });
53595
54371
  rmSync12(options.junitFile, { force: true });
53596
54372
  rmSync12(outputFile, { force: true });
53597
54373
  const spawn = options.spawn ?? spawnSuite;
53598
54374
  const env = suiteEnv(options.env ?? process.env);
53599
54375
  const { exitCode, stderr } = await spawn(command, { cwd: options.cwd, env });
53600
- writeFileSync23(outputFile, unhandledText(stderr));
54376
+ writeFileSync24(outputFile, unhandledText(stderr));
53601
54377
  const { summary, errors } = readSummary2(stderr);
53602
54378
  const { junit, failures } = readJunit(options.junitFile);
53603
54379
  const unhandled = parseUnhandled(stderr);
@@ -53615,12 +54391,12 @@ var init_run4 = __esm(() => {
53615
54391
  });
53616
54392
 
53617
54393
  // src/suite/scope.ts
53618
- import { existsSync as existsSync56, readdirSync as readdirSync20, readFileSync as readFileSync64 } from "fs";
53619
- import { isAbsolute as isAbsolute22, join as join84, relative as relative17, sep as sep7 } from "path";
54394
+ import { existsSync as existsSync57, readdirSync as readdirSync21, readFileSync as readFileSync64 } from "fs";
54395
+ import { isAbsolute as isAbsolute22, join as join86, relative as relative17, sep as sep7 } from "path";
53620
54396
  function sortedUnique(paths) {
53621
54397
  return [...new Set(paths)].sort((a, b) => a.localeCompare(b));
53622
54398
  }
53623
- function normalised(paths) {
54399
+ function normalised2(paths) {
53624
54400
  return paths.map(normaliseFolder).filter((path4) => path4 !== "");
53625
54401
  }
53626
54402
  function holds2(folder, path4) {
@@ -53628,24 +54404,24 @@ function holds2(folder, path4) {
53628
54404
  }
53629
54405
  function triggeredPaths(diff, triggers) {
53630
54406
  const globs = triggers.globs.map((pattern) => new Bun.Glob(pattern));
53631
- const preload = new Set(normalised(triggers.preload));
54407
+ const preload = new Set(normalised2(triggers.preload));
53632
54408
  const matches = (path4) => preload.has(path4) || globs.some((glob) => glob.match(path4));
53633
- return sortedUnique(normalised(diff).filter(matches));
54409
+ return sortedUnique(normalised2(diff).filter(matches));
53634
54410
  }
53635
54411
  function touchedFolders(diff, owns) {
53636
- const board = { number: 0, owner: null, owns: normalised(owns) };
53637
- const touched = normalised(diff).map((path4) => owningBoard(path4, [board], null)?.match ?? null);
54412
+ const board = { number: 0, owner: null, owns: normalised2(owns) };
54413
+ const touched = normalised2(diff).map((path4) => owningBoard(path4, [board], null)?.match ?? null);
53638
54414
  return sortedUnique(touched.filter((folder) => folder !== null));
53639
54415
  }
53640
54416
  function testFilesUnder(folders, testFiles) {
53641
- return sortedUnique(normalised(testFiles).filter((file) => folders.some((folder) => holds2(folder, file))));
54417
+ return sortedUnique(normalised2(testFiles).filter((file) => folders.some((folder) => holds2(folder, file))));
53642
54418
  }
53643
54419
  function integrationFiles(integration, testFiles) {
53644
54420
  const globs = integration.map((pattern) => new Bun.Glob(pattern));
53645
- return sortedUnique(normalised(testFiles).filter((file) => globs.some((glob) => glob.match(file))));
54421
+ return sortedUnique(normalised2(testFiles).filter((file) => globs.some((glob) => glob.match(file))));
53646
54422
  }
53647
54423
  function ownsFolders(owns) {
53648
- return owns === null ? [] : normalised(owns);
54424
+ return owns === null ? [] : normalised2(owns);
53649
54425
  }
53650
54426
  function taskStepScope(input) {
53651
54427
  const { declared } = input;
@@ -53696,8 +54472,8 @@ function preloadFilesOf(text2, root) {
53696
54472
  return paths.filter((path4) => path4 !== null);
53697
54473
  }
53698
54474
  function readPreloadFiles(root) {
53699
- const path4 = join84(root, BUNFIG_FILE);
53700
- if (!existsSync56(path4))
54475
+ const path4 = join86(root, BUNFIG_FILE);
54476
+ if (!existsSync57(path4))
53701
54477
  return { state: "missing", files: [] };
53702
54478
  try {
53703
54479
  return { state: "read", files: preloadFilesOf(readFileSync64(path4, "utf8"), root) };
@@ -53709,7 +54485,7 @@ function readPreloadFiles(root) {
53709
54485
  function listTestFiles(root) {
53710
54486
  const found = [];
53711
54487
  const walk = (folder) => {
53712
- for (const entry of readdirSync20(join84(root, folder), { withFileTypes: true })) {
54488
+ for (const entry of readdirSync21(join86(root, folder), { withFileTypes: true })) {
53713
54489
  const path4 = folder === "" ? entry.name : `${folder}/${entry.name}`;
53714
54490
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== DEPENDENCY_FOLDER)
53715
54491
  walk(path4);
@@ -53728,8 +54504,8 @@ var init_scope2 = __esm(() => {
53728
54504
  });
53729
54505
 
53730
54506
  // src/start/lint-step.ts
53731
- import { existsSync as existsSync57 } from "fs";
53732
- import { join as join85, relative as relative18 } from "path";
54507
+ import { existsSync as existsSync58 } from "fs";
54508
+ import { join as join87, relative as relative18 } from "path";
53733
54509
  async function runEslint(options) {
53734
54510
  const proc = Bun.spawn([...options.argv], {
53735
54511
  cwd: options.cwd,
@@ -53745,7 +54521,7 @@ async function runEslint(options) {
53745
54521
  return { exitCode, stdout, stderr };
53746
54522
  }
53747
54523
  function hasEslintConfig(checkout) {
53748
- return ESLINT_CONFIG_FILES.some((name2) => existsSync57(join85(checkout, name2)));
54524
+ return ESLINT_CONFIG_FILES.some((name2) => existsSync58(join87(checkout, name2)));
53749
54525
  }
53750
54526
  function readLintFiles(git, base) {
53751
54527
  const result2 = git(["diff", "--name-only", "-z", "--no-renames", "--diff-filter=d", base, "HEAD"]);
@@ -53867,7 +54643,7 @@ var init_lint_step = __esm(() => {
53867
54643
  });
53868
54644
 
53869
54645
  // src/start/suite-blocker.ts
53870
- import { readFileSync as readFileSync65, writeFileSync as writeFileSync24 } from "fs";
54646
+ import { readFileSync as readFileSync65, writeFileSync as writeFileSync25 } from "fs";
53871
54647
  function failingFiles(failures) {
53872
54648
  const counts = new Map;
53873
54649
  for (const failure6 of failures)
@@ -53939,7 +54715,7 @@ function blockRepairAgain(trackerPath, content, line, blocker) {
53939
54715
  const current = lines2[line] ?? "";
53940
54716
  if (current.startsWith(TICKED_PREFIX)) {
53941
54717
  const reopened = [...lines2.slice(0, line), `- [ ] ${current.slice(TICKED_PREFIX.length)}`, ...lines2.slice(line + 1)];
53942
- writeFileSync24(trackerPath, reopened.join(`
54718
+ writeFileSync25(trackerPath, reopened.join(`
53943
54719
  `), "utf8");
53944
54720
  }
53945
54721
  return writeTrackerBlocker(trackerPath, line, blocker) ? { line, inserted: false } : null;
@@ -53962,7 +54738,7 @@ var init_suite_blocker = __esm(() => {
53962
54738
  });
53963
54739
 
53964
54740
  // src/start/sweep-timing.ts
53965
- import { existsSync as existsSync58, readFileSync as readFileSync66 } from "fs";
54741
+ import { existsSync as existsSync59, readFileSync as readFileSync66 } from "fs";
53966
54742
  function fileTimeOf(attributes) {
53967
54743
  const file = attributes["file"] ?? attributes["name"];
53968
54744
  const seconds = Number(attributes["time"]);
@@ -54000,7 +54776,7 @@ function slowSweepLine(slow) {
54000
54776
  return `\uD83D\uDC22 ${slow.length} tests.alwaysRun file(s) took over ${SLOW_SWEEP_SECONDS}s in the task step: ${named3}.`;
54001
54777
  }
54002
54778
  function readFileTimes(path4) {
54003
- if (!existsSync58(path4))
54779
+ if (!existsSync59(path4))
54004
54780
  return [];
54005
54781
  try {
54006
54782
  return parseJunitFileTimes(readFileSync66(path4, "utf8")) ?? [];
@@ -54165,9 +54941,9 @@ var init_suite_stage_step = __esm(() => {
54165
54941
  });
54166
54942
 
54167
54943
  // src/start/suite-step.ts
54168
- import { existsSync as existsSync59, mkdirSync as mkdirSync23, readFileSync as readFileSync68, renameSync as renameSync11, writeFileSync as writeFileSync25 } from "fs";
54944
+ import { existsSync as existsSync60, mkdirSync as mkdirSync24, readFileSync as readFileSync68, renameSync as renameSync11, writeFileSync as writeFileSync26 } from "fs";
54169
54945
  import { constants as constants3 } from "os";
54170
- import { basename as basename23, dirname as dirname31, join as join86 } from "path";
54946
+ import { basename as basename23, dirname as dirname31, join as join88 } from "path";
54171
54947
  function seamsOf(context) {
54172
54948
  const seams = context.seams ?? {};
54173
54949
  return {
@@ -54183,15 +54959,15 @@ function seamsOf(context) {
54183
54959
  };
54184
54960
  }
54185
54961
  function junitFileFor(repoRoot, sessionId, kind) {
54186
- return join86(runsDir(repoRoot), sessionId, "suite", `${kind}.junit.xml`);
54962
+ return join88(runsDir(repoRoot), sessionId, "suite", `${kind}.junit.xml`);
54187
54963
  }
54188
54964
  function alwaysRunJunitFileFor(repoRoot, sessionId, kind = "task") {
54189
- return join86(runsDir(repoRoot), sessionId, "suite", `${kind}-always-run.junit.xml`);
54965
+ return join88(runsDir(repoRoot), sessionId, "suite", `${kind}-always-run.junit.xml`);
54190
54966
  }
54191
54967
  function stageLedgerPathFor(trackerPath) {
54192
54968
  const baselinePath = baselinePathFor(trackerPath);
54193
54969
  const name2 = basename23(baselinePath).replace(BASELINE_FILE_PREFIX, STAGE_LEDGER_PREFIX);
54194
- return join86(dirname31(baselinePath), name2);
54970
+ return join88(dirname31(baselinePath), name2);
54195
54971
  }
54196
54972
  function isLedgerEntry(value) {
54197
54973
  if (typeof value !== "object" || value === null)
@@ -54215,7 +54991,7 @@ function parseLedger(text2) {
54215
54991
  return stages;
54216
54992
  }
54217
54993
  function readStageLedger(path4) {
54218
- if (!existsSync59(path4))
54994
+ if (!existsSync60(path4))
54219
54995
  return [];
54220
54996
  let parsed;
54221
54997
  try {
@@ -54229,9 +55005,9 @@ function readStageLedger(path4) {
54229
55005
  return [];
54230
55006
  }
54231
55007
  function writeStageLedger(path4, entries) {
54232
- mkdirSync23(dirname31(path4), { recursive: true });
55008
+ mkdirSync24(dirname31(path4), { recursive: true });
54233
55009
  const temporary = `${path4}.${process.pid}.tmp`;
54234
- writeFileSync25(temporary, `${JSON.stringify({ version: STAGE_LEDGER_VERSION, stages: entries }, null, 2)}
55010
+ writeFileSync26(temporary, `${JSON.stringify({ version: STAGE_LEDGER_VERSION, stages: entries }, null, 2)}
54235
55011
  `, "utf8");
54236
55012
  renameSync11(temporary, path4);
54237
55013
  }
@@ -54645,8 +55421,8 @@ var init_pr_retarget = __esm(() => {
54645
55421
  });
54646
55422
 
54647
55423
  // src/release/prepare.ts
54648
- import { mkdirSync as mkdirSync24, readFileSync as readFileSync69, writeFileSync as writeFileSync26 } from "fs";
54649
- import { dirname as dirname32, join as join87, posix as posix5 } from "path";
55424
+ import { mkdirSync as mkdirSync25, readFileSync as readFileSync69, writeFileSync as writeFileSync27 } from "fs";
55425
+ import { dirname as dirname32, join as join89, posix as posix5 } from "path";
54650
55426
  function levelFields(reading2) {
54651
55427
  return { level: reading2.level, levelSource: reading2.source, notesLevel: reading2.notesLevel };
54652
55428
  }
@@ -54712,8 +55488,8 @@ function nameOf2(path4) {
54712
55488
  }
54713
55489
  function writeFragment(path4, resolved, text2) {
54714
55490
  try {
54715
- mkdirSync24(dirname32(resolved), { recursive: true });
54716
- writeFileSync26(resolved, text2);
55491
+ mkdirSync25(dirname32(resolved), { recursive: true });
55492
+ writeFileSync27(resolved, text2);
54717
55493
  return null;
54718
55494
  } catch (error) {
54719
55495
  return `${path4} could not be written: ${messageOf(error)}`;
@@ -54737,7 +55513,7 @@ function prepareRelease(input) {
54737
55513
  const fragment = { plan: input.plan, title: title2, level, notes: lines2.notes };
54738
55514
  const name2 = allocateFragmentName(input.plan, read.base.waiting.map((waiting) => nameOf2(waiting.path)));
54739
55515
  const path4 = posix5.join(input.settings.releaseFragments.replace(/\\/g, "/"), name2);
54740
- const resolved = join87(input.repoRoot, path4);
55516
+ const resolved = join89(input.repoRoot, path4);
54741
55517
  const before = textOf3(resolved);
54742
55518
  const after = serializeFragment(fragment);
54743
55519
  const problems = lines2.problem === null ? read.problems : [...read.problems, lines2.problem];
@@ -54769,7 +55545,7 @@ var init_prepare = __esm(() => {
54769
55545
  });
54770
55546
 
54771
55547
  // src/release/verify.ts
54772
- import { readFileSync as readFileSync70, writeFileSync as writeFileSync27 } from "fs";
55548
+ import { readFileSync as readFileSync70, writeFileSync as writeFileSync28 } from "fs";
54773
55549
  import { posix as posix6 } from "path";
54774
55550
  function textOf4(path4) {
54775
55551
  try {
@@ -54789,7 +55565,7 @@ function listOf4(names2) {
54789
55565
  }
54790
55566
  function restoreFragment(file) {
54791
55567
  try {
54792
- writeFileSync27(file.resolved, file.after);
55568
+ writeFileSync28(file.resolved, file.after);
54793
55569
  return { path: file.path, restored: true, problem: null };
54794
55570
  } catch (error) {
54795
55571
  return {
@@ -54916,13 +55692,13 @@ function announcePreparation(preparation) {
54916
55692
  }
54917
55693
  for (const problem2 of preparation.problems)
54918
55694
  out.warn(` \u26A0\uFE0F ${problem2}`);
54919
- const report2 = releaseLevelReport({
55695
+ const report3 = releaseLevelReport({
54920
55696
  level: preparation.level,
54921
55697
  source: preparation.levelSource,
54922
55698
  notesLevel: preparation.notesLevel
54923
55699
  });
54924
- if (report2 !== null)
54925
- out.warn(` \u26A0\uFE0F Level report: ${report2}`);
55700
+ if (report3 !== null)
55701
+ out.warn(` \u26A0\uFE0F Level report: ${report3}`);
54926
55702
  }
54927
55703
  function prepareReleaseStage(input, seams = {}) {
54928
55704
  const io = { ...RELEASE_STAGE_SEAMS, ...seams };
@@ -55439,13 +56215,13 @@ async function checkWrapUpAnswer(input) {
55439
56215
  const line = unpromotedLine(check, input.head);
55440
56216
  if (line === null)
55441
56217
  return check;
55442
- const report2 = await reportUnpromoted(input.pulls, input.branch, line);
55443
- if (!report2.written) {
55444
- out.warn(` This line is not in the pull request body, as ${report2.problem}: ${line}`);
55445
- } else if (report2.already) {
55446
- out.info(` Pull request #${report2.number} already names the lessons it does not carry.`);
56218
+ const report3 = await reportUnpromoted(input.pulls, input.branch, line);
56219
+ if (!report3.written) {
56220
+ out.warn(` This line is not in the pull request body, as ${report3.problem}: ${line}`);
56221
+ } else if (report3.already) {
56222
+ out.info(` Pull request #${report3.number} already names the lessons it does not carry.`);
55447
56223
  } else {
55448
- out.info(` Pull request #${report2.number} now names the lessons it does not carry.`);
56224
+ out.info(` Pull request #${report3.number} now names the lessons it does not carry.`);
55449
56225
  }
55450
56226
  return check;
55451
56227
  }
@@ -55755,8 +56531,8 @@ async function emitPullRequestEvent(place2, branch, known, lookup) {
55755
56531
  }
55756
56532
  emitLoopEvent(number === null ? { kind: "no-pr", reason: `no open pull request for ${branch}` } : { kind: "pr", number });
55757
56533
  }
55758
- function blockedDelivery(report2, retriesSpent) {
55759
- return { kind: "blocked", message: `${report2}
56534
+ function blockedDelivery(report3, retriesSpent) {
56535
+ return { kind: "blocked", message: `${report3}
55760
56536
  ${DELIVERY_BLOCKED_TAIL}`, retriesSpent };
55761
56537
  }
55762
56538
  async function readOpenPull2(seams, branch) {
@@ -55805,7 +56581,7 @@ async function openByRunner(seams, branch, retriesSpent) {
55805
56581
  }
55806
56582
  function planIssueNumber(planContent, planStub2, branch) {
55807
56583
  const declared = parsePlan(planContent).header.issue?.trim() ?? "";
55808
- const spelled = ISSUE_NUMBER14.exec(declared)?.[1] ?? RAFA_ID.exec(planStub2 ?? "")?.[1] ?? RAFA_ID.exec(branch)?.[1];
56584
+ const spelled = ISSUE_NUMBER15.exec(declared)?.[1] ?? RAFA_ID.exec(planStub2 ?? "")?.[1] ?? RAFA_ID.exec(branch)?.[1];
55809
56585
  return spelled === undefined ? null : Number(spelled);
55810
56586
  }
55811
56587
  function runnerPrInputFor(source) {
@@ -55858,7 +56634,7 @@ function deliverySeamsIn(context) {
55858
56634
  };
55859
56635
  }
55860
56636
  var NO_PROVIDER_REASON = "no pull request provider is configured", DELIVERY_BLOCKED_TAIL = " The run is recorded stopped, not done. Open the pull request by hand, or fix the step above and run again to retry the wrap-up.", DELIVERY_INTERRUPTED = `
55861
- \u26A0\uFE0F Interrupted: no further wrap-up session runs and the loop opens no pull request. Run again to retry the wrap-up.`, ISSUE_NUMBER14, RAFA_ID;
56637
+ \u26A0\uFE0F Interrupted: no further wrap-up session runs and the loop opens no pull request. Run again to retry the wrap-up.`, ISSUE_NUMBER15, RAFA_ID;
55862
56638
  var init_wrap_up_run = __esm(() => {
55863
56639
  init_active();
55864
56640
  init_device();
@@ -55875,7 +56651,7 @@ var init_wrap_up_run = __esm(() => {
55875
56651
  init_runner_pr();
55876
56652
  init_wrap_up_retry();
55877
56653
  init_wrap_up();
55878
- ISSUE_NUMBER14 = /^#?(\d+)$/;
56654
+ ISSUE_NUMBER15 = /^#?(\d+)$/;
55879
56655
  RAFA_ID = /(?:^|\/)rafa-(\d+)(?:-|$)/;
55880
56656
  });
55881
56657
 
@@ -55898,8 +56674,8 @@ function msUntil(startAt, now = new Date) {
55898
56674
  return target.getTime() - now.getTime();
55899
56675
  }
55900
56676
  function sleepFor(ms) {
55901
- return new Promise((resolve37) => {
55902
- setTimeout(resolve37, ms);
56677
+ return new Promise((resolve38) => {
56678
+ setTimeout(resolve38, ms);
55903
56679
  });
55904
56680
  }
55905
56681
  async function deferUntil(startAt, now = new Date, sleep = sleepFor) {
@@ -56845,7 +57621,7 @@ function poll(following, before, context, seams) {
56845
57621
  }
56846
57622
  return { cursor, found: null };
56847
57623
  }
56848
- function lineOf3(found, following, session, reading2) {
57624
+ function lineOf4(found, following, session, reading2) {
56849
57625
  if (found.event !== null)
56850
57626
  return eventLine(found.event);
56851
57627
  if (found.reason === "exit")
@@ -56866,7 +57642,7 @@ function resultOf6(found, following, seams) {
56866
57642
  return {
56867
57643
  reason: found.reason,
56868
57644
  exitCode: found.reason === "timeout" ? WAIT_TIMEOUT_EXIT : waitExitCode(found.reason),
56869
- line: lineOf3(found, following, session, reading2),
57645
+ line: lineOf4(found, following, session, reading2),
56870
57646
  event: found.event,
56871
57647
  session,
56872
57648
  awakeMinutes: reading2.awakeMinutes,
@@ -57181,7 +57957,7 @@ var init_ceiling = __esm(() => {
57181
57957
  });
57182
57958
 
57183
57959
  // src/next/lines.ts
57184
- function stateLine(state) {
57960
+ function stateLine2(state) {
57185
57961
  return `${STATE_MARK} ${state.reading}.`;
57186
57962
  }
57187
57963
  function proposalLine(state, invocation) {
@@ -57347,7 +58123,7 @@ async function playTurn(options, state, previous) {
57347
58123
  for (const problem2 of state.problems)
57348
58124
  warn4(problem2);
57349
58125
  const invocation = actionInvocation(state, passedOptions(roadmap));
57350
- info2(stateLine(state));
58126
+ info2(stateLine2(state));
57351
58127
  info2(proposalLine(state, invocation));
57352
58128
  const halt2 = (stop, over = {}) => ({ stop, step: stepOf3(state, invocation, over) });
57353
58129
  if (dryRun !== null)
@@ -57373,7 +58149,7 @@ async function runNextChain(options) {
57373
58149
  let steps = [];
57374
58150
  let hops = [];
57375
58151
  let previous = null;
57376
- const report2 = (stop) => Object.freeze({
58152
+ const report3 = (stop) => Object.freeze({
57377
58153
  steps: Object.freeze([...steps]),
57378
58154
  stop,
57379
58155
  ...roadmap === undefined ? {} : { hops: Object.freeze([...hops]) }
@@ -57382,7 +58158,7 @@ async function runNextChain(options) {
57382
58158
  const line = stopLine(stop, state, ceiling, dryRun, roadmap !== undefined);
57383
58159
  if (line !== null)
57384
58160
  info2(line);
57385
- return report2(stop);
58161
+ return report3(stop);
57386
58162
  };
57387
58163
  const turn = async (state, before) => {
57388
58164
  const end = await playTurn(options, state, before);
@@ -57421,8 +58197,8 @@ async function runStateAction(context, sources, state, passed2) {
57421
58197
  if (outcome2.said !== "")
57422
58198
  context.output.info(outcome2.said);
57423
58199
  }
57424
- function endOfEpic(context, sources, report2, epic) {
57425
- if (epic === null || report2.steps.at(-1)?.state !== "nothing-left")
58200
+ function endOfEpic(context, sources, report3, epic) {
58201
+ if (epic === null || report3.steps.at(-1)?.state !== "nothing-left")
57426
58202
  return;
57427
58203
  const release = sources.release();
57428
58204
  if (release.problem !== null)
@@ -57456,7 +58232,7 @@ async function runNext(context, seams) {
57456
58232
  const prompter = lazyPrompter2(openPrompter);
57457
58233
  const dry = watchDryEpic();
57458
58234
  try {
57459
- const report2 = await runNextChain({
58235
+ const report3 = await runNextChain({
57460
58236
  read: () => readNextState(dry.watch(sources.answer())),
57461
58237
  run: (state) => runStateAction(context, sources, state, {
57462
58238
  roadmap: sources.roadmap !== undefined,
@@ -57477,8 +58253,8 @@ async function runNext(context, seams) {
57477
58253
  }),
57478
58254
  ...roadmap ? { roadmap: { afterLoop: () => homeAfterLoop2(context, sources), claimAhead } } : {}
57479
58255
  });
57480
- endOfEpic(context, sources, report2, dry.last());
57481
- return report2;
58256
+ endOfEpic(context, sources, report3, dry.last());
58257
+ return report3;
57482
58258
  } finally {
57483
58259
  prompter.close();
57484
58260
  }
@@ -57517,9 +58293,9 @@ function createNextCommand(seams = DEFAULT_NEXT_SEAMS) {
57517
58293
  outputs: ["text", "json"],
57518
58294
  spends: { when: "through", what: "when the step it runs is one of the above" },
57519
58295
  run: async (context) => {
57520
- const report2 = await runNext(context, seams);
58296
+ const report3 = await runNext(context, seams);
57521
58297
  if (context.outputMode === "json")
57522
- context.output.result(report2);
58298
+ context.output.result(report3);
57523
58299
  }
57524
58300
  };
57525
58301
  return Object.freeze(command);
@@ -57578,7 +58354,7 @@ import path6 from "path";
57578
58354
  function unclaimed(issue4, cause, reason, pending = null, warnings = []) {
57579
58355
  return { outcome: "unclaimed", issue: issue4, cause, reason, pending, warnings };
57580
58356
  }
57581
- function refused4(attempt5, branch, owner, reason) {
58357
+ function refused5(attempt5, branch, owner, reason) {
57582
58358
  return { outcome: "refused", issue: attempt5.issue, branch, owner, reason };
57583
58359
  }
57584
58360
  function claimed(attempt5, via, warnings = []) {
@@ -57641,7 +58417,7 @@ function claimOnRemote(attempt5, ahead, context) {
57641
58417
  }
57642
58418
  const listed4 = git(["for-each-ref", "--format=%(refname:lstrip=3)", `refs/remotes/${REMOTE}/${BRANCH_PREFIX2}`]);
57643
58419
  if (!listed4.ok) {
57644
- return refused4(attempt5, attempt5.branch, null, `the ${REMOTE} branches of #${String(attempt5.issue)} could not be listed: ${gitSaid(listed4)}`);
58420
+ return refused5(attempt5, attempt5.branch, null, `the ${REMOTE} branches of #${String(attempt5.issue)} could not be listed: ${gitSaid(listed4)}`);
57645
58421
  }
57646
58422
  const names2 = listed4.stdout.split(`
57647
58423
  `).map((line) => line.trim());
@@ -57681,10 +58457,10 @@ function ownerOf(reading2) {
57681
58457
  }
57682
58458
  function unnamedRefusal(attempt5, reading2) {
57683
58459
  if (reading2.state === "unreadable") {
57684
- return refused4(attempt5, reading2.branch, null, `the claim on ${reading2.branch} could not be read, so #${String(attempt5.issue)} stays taken: ${reading2.reason}`);
58460
+ return refused5(attempt5, reading2.branch, null, `the claim on ${reading2.branch} could not be read, so #${String(attempt5.issue)} stays taken: ${reading2.reason}`);
57685
58461
  }
57686
58462
  if (reading2.state === "found" && reading2.ownership.state === "none") {
57687
- return refused4(attempt5, reading2.branch, null, `${reading2.branch} exists on ${REMOTE} carrying no claim commit, so #${String(attempt5.issue)} stays taken`);
58463
+ return refused5(attempt5, reading2.branch, null, `${reading2.branch} exists on ${REMOTE} carrying no claim commit, so #${String(attempt5.issue)} stays taken`);
57688
58464
  }
57689
58465
  return null;
57690
58466
  }
@@ -57704,16 +58480,16 @@ function otherBranchRefusal(attempt5, reading2, context) {
57704
58480
  return null;
57705
58481
  const { owner } = weighed;
57706
58482
  if (owner === attempt5.storeId) {
57707
- return refused4(attempt5, branch, owner, `this device (store ${owner}) already holds ${issue4} on ${branch}; plan it there with --stub=${stubOf(branch)}`);
58483
+ return refused5(attempt5, branch, owner, `this device (store ${owner}) already holds ${issue4} on ${branch}; plan it there with --stub=${stubOf(branch)}`);
57708
58484
  }
57709
- return weighed.state === "stale-claimed" ? refused4(attempt5, branch, owner, `${issue4} is claimed by store ${owner} on ${branch}, stale; take it over by planning there with --stub=${stubOf(branch)}`) : refused4(attempt5, branch, owner, `${issue4} is claimed by store ${owner} on ${branch}`);
58485
+ return weighed.state === "stale-claimed" ? refused5(attempt5, branch, owner, `${issue4} is claimed by store ${owner} on ${branch}, stale; take it over by planning there with --stub=${stubOf(branch)}`) : refused5(attempt5, branch, owner, `${issue4} is claimed by store ${owner} on ${branch}`);
57710
58486
  }
57711
58487
  function claimOwnBranch(attempt5, reading2, context) {
57712
58488
  if (reading2.state === "absent")
57713
58489
  return { kind: "fresh" };
57714
58490
  const unreadable3 = () => ({
57715
58491
  kind: "answer",
57716
- answer: refused4(attempt5, attempt5.branch, null, `the claim on ${attempt5.branch} could not be read`)
58492
+ answer: refused5(attempt5, attempt5.branch, null, `the claim on ${attempt5.branch} could not be read`)
57717
58493
  });
57718
58494
  const unnamed = unnamedRefusal(attempt5, reading2);
57719
58495
  if (unnamed !== null)
@@ -57729,7 +58505,7 @@ function claimOwnBranch(attempt5, reading2, context) {
57729
58505
  return { kind: "held" };
57730
58506
  const note = weighed.state === "stale-in-development" ? ", stale but in development, which is never taken over automatically" : "";
57731
58507
  const reason = `#${String(attempt5.issue)} is claimed by store ${weighed.owner} on ${attempt5.branch}${note}`;
57732
- return { kind: "answer", answer: refused4(attempt5, attempt5.branch, weighed.owner, reason) };
58508
+ return { kind: "answer", answer: refused5(attempt5, attempt5.branch, weighed.owner, reason) };
57733
58509
  }
57734
58510
  function tipOf(git, ref15) {
57735
58511
  const resolved = git(["rev-parse", "--verify", "--quiet", `${ref15}^{commit}`]);
@@ -57813,7 +58589,7 @@ function pushFreshClaim(attempt5, git) {
57813
58589
  return keepPending(attempt5, git, "push-failed", pushed.reason, staged.sha, staged.localTip);
57814
58590
  if (pushed.outcome === "claimed") {
57815
58591
  const owner = ownerOf(pushed.holder);
57816
- return refused4(attempt5, attempt5.branch, owner, raceReason(attempt5, "claimed", owner));
58592
+ return refused5(attempt5, attempt5.branch, owner, raceReason(attempt5, "claimed", owner));
57817
58593
  }
57818
58594
  return claimed(attempt5, "claim", warningsOf(landLocal(attempt5, staged, git)));
57819
58595
  }
@@ -57826,7 +58602,7 @@ function pushTake(attempt5, tip, git) {
57826
58602
  return keepPending(attempt5, git, "push-failed", pushed.reason, staged.sha, staged.localTip);
57827
58603
  if (pushed.outcome === "moved") {
57828
58604
  const owner = refetchedOwner(git, attempt5.branch);
57829
- return refused4(attempt5, attempt5.branch, owner, raceReason(attempt5, "moved", owner));
58605
+ return refused5(attempt5, attempt5.branch, owner, raceReason(attempt5, "moved", owner));
57830
58606
  }
57831
58607
  return claimed(attempt5, "take", warningsOf(landLocal(attempt5, staged, git)));
57832
58608
  }
@@ -57856,20 +58632,20 @@ function claimPair(home, ahead, git) {
57856
58632
  const line = `#${String(forward.issue)}, the line ahead, was not claimed`;
57857
58633
  if (ahead.move.kind === "answer") {
57858
58634
  const reason = ahead.move.answer.outcome === "refused" ? ahead.move.answer.reason : `the claim on ${forward.branch} could not be read`;
57859
- const report2 = aheadNotClaimed(forward.issue, "taken", `${line}: ${reason}`);
57860
- return neither(home, git, "ahead-taken", pairWhy(forward.issue, reason), report2);
58635
+ const report3 = aheadNotClaimed(forward.issue, "taken", `${line}: ${reason}`);
58636
+ return neither(home, git, "ahead-taken", pairWhy(forward.issue, reason), report3);
57861
58637
  }
57862
58638
  if (ahead.move.kind === "held")
57863
58639
  return withAhead(carryOut(home.attempt, home.move, git), aheadClaimed(forward, "held"));
57864
58640
  const aheadStaged = stage(forward, ahead.move, git);
57865
58641
  if (!aheadStaged.ok) {
57866
- const report2 = aheadNotClaimed(forward.issue, "failed", `${line}: ${aheadStaged.reason}`);
57867
- return neither(home, git, "commit-failed", pairWhy(forward.issue, aheadStaged.reason), report2);
58642
+ const report3 = aheadNotClaimed(forward.issue, "failed", `${line}: ${aheadStaged.reason}`);
58643
+ return neither(home, git, "commit-failed", pairWhy(forward.issue, aheadStaged.reason), report3);
57868
58644
  }
57869
58645
  const homeStaged = home.move.kind === "held" ? null : stage(home.attempt, home.move, git);
57870
58646
  if (homeStaged?.ok === false) {
57871
- const report2 = aheadNotClaimed(forward.issue, "not-tried", `${line}: the claim on #${String(home.attempt.issue)} could not be made`);
57872
- return withAhead(unclaimed(home.attempt.issue, "commit-failed", homeStaged.reason), report2);
58647
+ const report3 = aheadNotClaimed(forward.issue, "not-tried", `${line}: the claim on #${String(home.attempt.issue)} could not be made`);
58648
+ return withAhead(unclaimed(home.attempt.issue, "commit-failed", homeStaged.reason), report3);
57873
58649
  }
57874
58650
  return pushPair(home, homeStaged, { attempt: forward, staged: aheadStaged }, git);
57875
58651
  }
@@ -57888,16 +58664,16 @@ function pushPair(home, homeStaged, ahead, git) {
57888
58664
  }
57889
58665
  if (pushed.outcome === "refused" && pushed.branch === home.attempt.branch) {
57890
58666
  const owner = refetchedOwner(git, home.attempt.branch);
57891
- return refused4(home.attempt, home.attempt.branch, owner, raceReason(home.attempt, pushed.by, owner));
58667
+ return refused5(home.attempt, home.attempt.branch, owner, raceReason(home.attempt, pushed.by, owner));
57892
58668
  }
57893
58669
  const reason = pushed.outcome === "refused" ? raceReason(forward, pushed.by, refetchedOwner(git, forward.branch)) : pushed.reason;
57894
58670
  const cause = pushed.outcome === "refused" ? "taken" : "failed";
57895
- const report2 = aheadNotClaimed(forward.issue, cause, `${line}: ${reason}`);
58671
+ const report3 = aheadNotClaimed(forward.issue, cause, `${line}: ${reason}`);
57896
58672
  if (homeStaged === null)
57897
- return withAhead(claimed(home.attempt, "held"), report2);
58673
+ return withAhead(claimed(home.attempt, "held"), report3);
57898
58674
  const homeCause = pushed.outcome === "refused" ? "ahead-taken" : "push-failed";
57899
58675
  const why = pushed.outcome === "refused" ? pairWhy(forward.issue, reason) : reason;
57900
- return withAhead(keepPending(home.attempt, git, homeCause, why, homeStaged.sha, homeStaged.localTip), report2);
58676
+ return withAhead(keepPending(home.attempt, git, homeCause, why, homeStaged.sha, homeStaged.localTip), report3);
57901
58677
  }
57902
58678
  var init_plan_claim = __esm(() => {
57903
58679
  init_roadmap_claims();
@@ -57953,7 +58729,7 @@ async function aheadRequestOf(spec, setting, output) {
57953
58729
  return;
57954
58730
  }
57955
58731
  }
57956
- function report2(claim, output) {
58732
+ function report3(claim, output) {
57957
58733
  if (claim.outcome === "claimed") {
57958
58734
  output.info(claimedLine(claim));
57959
58735
  } else {
@@ -57985,7 +58761,7 @@ async function resolveAndClaim(seams) {
57985
58761
  ...ahead === undefined ? {} : { ahead }
57986
58762
  });
57987
58763
  if (claim.outcome !== "refused") {
57988
- report2(claim, output);
58764
+ report3(claim, output);
57989
58765
  return { outcome: "spec", spec, gate, stub, claim };
57990
58766
  }
57991
58767
  if (spec.kind !== "next")
@@ -58514,7 +59290,7 @@ var init_create2 = __esm(() => {
58514
59290
 
58515
59291
  // src/commands/plan/risk.ts
58516
59292
  import { readFileSync as readFileSync72 } from "fs";
58517
- import { resolve as resolve37 } from "path";
59293
+ import { resolve as resolve38 } from "path";
58518
59294
  function spawningRunners2(root) {
58519
59295
  return { git: createGitRunner(root), gh: createGhRunner({ cwd: root }) };
58520
59296
  }
@@ -58524,7 +59300,7 @@ function riskPlanPath(root, planDir, typed) {
58524
59300
  return path9;
58525
59301
  if (typed !== null)
58526
59302
  throw new CommandExit(1, `\u274C Plan file not found: ${path9}`);
58527
- const lookedFor = [...new Set([resolve37(root, planDir, DEFAULT_PLAN_FILE), resolve37(root, DEFAULT_PLAN_FILE)])];
59303
+ const lookedFor = [...new Set([resolve38(root, planDir, DEFAULT_PLAN_FILE), resolve38(root, DEFAULT_PLAN_FILE)])];
58528
59304
  throw new CommandExit(1, `\u274C No plan named, and no default plan at ${lookedFor.join(" or ")}
58529
59305
  Usage: ${USAGE38}`);
58530
59306
  }
@@ -58533,8 +59309,8 @@ function riskLine(finding2) {
58533
59309
  const place2 = finding2.file === undefined ? "" : `${finding2.file}${line}: `;
58534
59310
  return `${place2}${finding2.kind}: ${finding2.text}`;
58535
59311
  }
58536
- function strictRefusal(report3) {
58537
- return `\u274C ${report3.plan}: ${plural(report3.total.high, "high finding")}; --strict refuses a plan with any`;
59312
+ function strictRefusal(report4) {
59313
+ return `\u274C ${report4.plan}: ${plural(report4.total.high, "high finding")}; --strict refuses a plan with any`;
58538
59314
  }
58539
59315
  async function readRisk(path9, project, config, context, seams) {
58540
59316
  const makeRunners = seams.runners ?? spawningRunners2;
@@ -58596,24 +59372,24 @@ function createPlanRiskCommand(seams = {}) {
58596
59372
  context.output.warn(message);
58597
59373
  });
58598
59374
  const path9 = riskPlanPath(project.root, config.planDir, typed);
58599
- const report3 = await readRisk(path9, project, config, context, seams);
58600
- const refused5 = strict && report3.total.high > 0;
59375
+ const report4 = await readRisk(path9, project, config, context, seams);
59376
+ const refused6 = strict && report4.total.high > 0;
58601
59377
  if (context.outputMode === "json") {
58602
- if (!refused5) {
58603
- context.output.result(report3);
59378
+ if (!refused6) {
59379
+ context.output.result(report4);
58604
59380
  return;
58605
59381
  }
58606
- for (const finding2 of report3.findings) {
59382
+ for (const finding2 of report4.findings) {
58607
59383
  if (finding2.level === "high")
58608
59384
  context.output.error(riskLine(finding2));
58609
59385
  }
58610
59386
  } else {
58611
- for (const line of renderRiskText(report3).split(`
59387
+ for (const line of renderRiskText(report4).split(`
58612
59388
  `))
58613
59389
  context.output.info(line);
58614
59390
  }
58615
- if (refused5)
58616
- throw new CommandExit(1, strictRefusal(report3));
59391
+ if (refused6)
59392
+ throw new CommandExit(1, strictRefusal(report4));
58617
59393
  }
58618
59394
  };
58619
59395
  return Object.freeze(command);
@@ -58632,7 +59408,7 @@ var init_risk2 = __esm(() => {
58632
59408
 
58633
59409
  // src/commands/plan/show.ts
58634
59410
  import { readFileSync as readFileSync73 } from "fs";
58635
- import { join as join88 } from "path";
59411
+ import { join as join90 } from "path";
58636
59412
  function readTrackerFlag(value) {
58637
59413
  if (value === undefined || value === false || value === "false")
58638
59414
  return false;
@@ -58648,9 +59424,9 @@ function showPlan(plans, stub, tracker) {
58648
59424
  throw new CommandExit(1, `\u274C "${stub}" is no plan stub: a stub is letters, digits, ".", "_" and "-"`);
58649
59425
  }
58650
59426
  const name2 = planFileName(stub, tracker);
58651
- const file = join88(plans.path, name2);
59427
+ const file = join90(plans.path, name2);
58652
59428
  if (!isFile(file))
58653
- throw new CommandExit(1, missingText(stub, tracker, join88(plans.label, name2)));
59429
+ throw new CommandExit(1, missingText(stub, tracker, join90(plans.label, name2)));
58654
59430
  const model = parsePlan(readFileSync73(file, "utf8"));
58655
59431
  const { header } = model;
58656
59432
  return {
@@ -58674,7 +59450,7 @@ function taskLine2(task) {
58674
59450
  return ` ${checkbox(task.status)} ${task.text}`;
58675
59451
  }
58676
59452
  function renderShownPlan(shown2, dirLabel) {
58677
- const relative21 = join88(dirLabel, planFileName(shown2.stub, shown2.tracker));
59453
+ const relative21 = join90(dirLabel, planFileName(shown2.stub, shown2.tracker));
58678
59454
  const fileLabel = shown2.tracker ? "Tracker" : "File";
58679
59455
  const lines2 = [`Plan: ${shown2.stub}`, `${fileLabel}: ${relative21}`];
58680
59456
  if (shown2.header.issue !== null)
@@ -59254,7 +60030,7 @@ function freedIssueLine(issue4) {
59254
60030
  const title2 = issue4.title.trim();
59255
60031
  return title2 === "" ? `${INDENT5}#${String(issue4.number)}` : `${INDENT5}#${String(issue4.number)} ${title2}`;
59256
60032
  }
59257
- function report3(closed, freed, problem2) {
60033
+ function report4(closed, freed, problem2) {
59258
60034
  return Object.freeze({ relationships: "native", closed, freed: Object.freeze(freed), problem: problem2 });
59259
60035
  }
59260
60036
  async function readFreed(gh, closed) {
@@ -59275,14 +60051,14 @@ async function freedAfterMerge(options) {
59275
60051
  } catch (error) {
59276
60052
  const problem2 = messageOf(error);
59277
60053
  warn4(freedProblemLine(problem2));
59278
- return report3(closed, [], problem2);
60054
+ return report4(closed, [], problem2);
59279
60055
  }
59280
60056
  if (freed.length > 0) {
59281
60057
  info2(freedHeaderLine(closed, freed.length));
59282
60058
  for (const issue4 of freed)
59283
60059
  info2(freedIssueLine(issue4));
59284
60060
  }
59285
- return report3(closed, freed, null);
60061
+ return report4(closed, freed, null);
59286
60062
  }
59287
60063
  var READING2 = "the freed-issue reading";
59288
60064
  var init_merge_freed = __esm(() => {
@@ -59348,13 +60124,13 @@ async function guardBeforeMerge(step2) {
59348
60124
  const answer5 = answerOf2(reading2);
59349
60125
  const reaction = guardReaction(reading2, settings);
59350
60126
  const lines2 = guardLines(reading2, settings.releaseVersionFile);
59351
- const report4 = { answer: answer5, reaction, lines: lines2 };
60127
+ const report5 = { answer: answer5, reaction, lines: lines2 };
59352
60128
  if (reaction === "refuse")
59353
60129
  throw guardRefusal(refusalHead(step2.detail.number, answer5), lines2);
59354
60130
  if (reaction === "ask")
59355
- return { report: report4, go: await askGuard(step2, answer5, lines2) };
60131
+ return { report: report5, go: await askGuard(step2, answer5, lines2) };
59356
60132
  printLines(step2, reaction, lines2);
59357
- return { report: report4, go: true };
60133
+ return { report: report5, go: true };
59358
60134
  }
59359
60135
  var YES_ANSWERS4;
59360
60136
  var init_merge_guard = __esm(() => {
@@ -59366,15 +60142,15 @@ var init_merge_guard = __esm(() => {
59366
60142
  });
59367
60143
 
59368
60144
  // src/commands/pr/merge-loop-worktree.ts
59369
- import { copyFileSync as copyFileSync5, mkdirSync as mkdirSync25, realpathSync as realpathSync16, statSync as statSync28 } from "fs";
59370
- import { dirname as dirname33, isAbsolute as isAbsolute24, join as join89, relative as relative21, resolve as resolve38, sep as sep8 } from "path";
60145
+ import { copyFileSync as copyFileSync5, mkdirSync as mkdirSync26, realpathSync as realpathSync16, statSync as statSync29 } from "fs";
60146
+ import { dirname as dirname33, isAbsolute as isAbsolute24, join as join91, relative as relative21, resolve as resolve39, sep as sep8 } from "path";
59371
60147
  function defaultLoopWorktreeSeams(mainCheckout) {
59372
60148
  return {
59373
60149
  git: createGitRunner(mainCheckout),
59374
60150
  gitAt: createGitRunner,
59375
- modifiedAt: (path9) => statSync28(path9, { throwIfNoEntry: false })?.mtime ?? null,
60151
+ modifiedAt: (path9) => statSync29(path9, { throwIfNoEntry: false })?.mtime ?? null,
59376
60152
  copyFile: (from, to) => {
59377
- mkdirSync25(dirname33(to), { recursive: true });
60153
+ mkdirSync26(dirname33(to), { recursive: true });
59378
60154
  copyFileSync5(from, to);
59379
60155
  },
59380
60156
  realPath: (path9) => {
@@ -59405,11 +60181,11 @@ function readLoopHolder(seams, reading2) {
59405
60181
  };
59406
60182
  }
59407
60183
  function freeLoopWorktree(seams, reading2, holder) {
59408
- const into = resolve38(reading2.mainCheckout, reading2.planDir);
60184
+ const into = resolve39(reading2.mainCheckout, reading2.planDir);
59409
60185
  const files = [];
59410
60186
  for (const name2 of planFileNames(holder.stub)) {
59411
- const from = resolve38(holder.path, reading2.planDir, name2);
59412
- const to = join89(into, name2);
60187
+ const from = resolve39(holder.path, reading2.planDir, name2);
60188
+ const to = join91(into, name2);
59413
60189
  try {
59414
60190
  files.push({ name: name2, from, to, outcome: copyPlanFile(seams, from, to) });
59415
60191
  } catch (error) {
@@ -60274,7 +61050,7 @@ function readCap(value) {
60274
61050
  if (value === undefined)
60275
61051
  return FAILED_LOG_TAIL_LINES;
60276
61052
  if (!Number.isSafeInteger(value) || value < 1) {
60277
- throw new TypeError(`${PREFIX35}: readFailedLog refused a line cap ${describeValue(value)},` + " expected a positive whole number");
61053
+ throw new TypeError(`${PREFIX36}: readFailedLog refused a line cap ${describeValue(value)},` + " expected a positive whole number");
60278
61054
  }
60279
61055
  return value;
60280
61056
  }
@@ -60290,7 +61066,7 @@ function readFailedLog(text2, options = {}) {
60290
61066
  total: lines2.length
60291
61067
  };
60292
61068
  }
60293
- var FAILED_LOG_TAIL_LINES = 40, UNKNOWN_STEP = "UNKNOWN STEP", GROUP_STEP_PREFIX = "##[group]Run ", ERROR_PREFIX2 = "##[error]", COLUMN = "\t", BOM = "\uFEFF", TIMESTAMP, COLOUR, PREFIX35 = "pr triage evidence";
61069
+ var FAILED_LOG_TAIL_LINES = 40, UNKNOWN_STEP = "UNKNOWN STEP", GROUP_STEP_PREFIX = "##[group]Run ", ERROR_PREFIX2 = "##[error]", COLUMN = "\t", BOM = "\uFEFF", TIMESTAMP, COLOUR, PREFIX36 = "pr triage evidence";
60294
61070
  var init_evidence = __esm(() => {
60295
61071
  init_config_sections();
60296
61072
  TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z /;
@@ -61236,8 +62012,8 @@ function describeAge(updatedAt, now) {
61236
62012
  return `${plural7(hours, "hour", "hours")} ago`;
61237
62013
  return `${plural7(Math.floor(age / DAY_MS2), "day", "days")} ago`;
61238
62014
  }
61239
- function triageCommand(number, resolve39 = false) {
61240
- return resolve39 ? `rafa pr triage ${number} --resolve` : `rafa pr triage ${number}`;
62015
+ function triageCommand(number, resolve40 = false) {
62016
+ return resolve40 ? `rafa pr triage ${number} --resolve` : `rafa pr triage ${number}`;
61241
62017
  }
61242
62018
  function joinNumbers(pulls) {
61243
62019
  const names2 = pulls.map((pull2) => `#${pull2.number}`);
@@ -61248,14 +62024,14 @@ function joinNumbers(pulls) {
61248
62024
  function byNumber4(pulls) {
61249
62025
  return Object.freeze([...pulls].sort((left, right) => left.number - right.number));
61250
62026
  }
61251
- function skipped2(pull2, now, resolve39) {
62027
+ function skipped2(pull2, now, resolve40) {
61252
62028
  return Object.freeze({
61253
62029
  number: pull2.number,
61254
62030
  title: pull2.title,
61255
62031
  updatedAt: pull2.updatedAt,
61256
62032
  age: describeAge(pull2.updatedAt, now),
61257
62033
  fresh: isFreshCandidate(pull2.updatedAt, now),
61258
- command: triageCommand(pull2.number, resolve39)
62034
+ command: triageCommand(pull2.number, resolve40)
61259
62035
  });
61260
62036
  }
61261
62037
  function skippedLine(one) {
@@ -61299,15 +62075,15 @@ function listReading(rest, count2) {
61299
62075
  return listing2("list", Object.freeze([]), rest, headline);
61300
62076
  }
61301
62077
  function selectTriagePullRequests(input) {
61302
- const resolve39 = input.resolve === true;
62078
+ const resolve40 = input.resolve === true;
61303
62079
  const { current, now } = input;
61304
62080
  if (current !== null) {
61305
62081
  return plain("branch", Object.freeze([current]), `assessing #${current.number}, the pull request open on ${current.headRefName}`);
61306
62082
  }
61307
62083
  const candidates = byNumber4(input.candidates);
61308
62084
  const count2 = candidates.length;
61309
- const lines2 = candidates.map((pull2) => skipped2(pull2, now, resolve39));
61310
- if (resolve39 && count2 > 1)
62085
+ const lines2 = candidates.map((pull2) => skipped2(pull2, now, resolve40));
62086
+ if (resolve40 && count2 > 1)
61311
62087
  return refusal15(lines2, count2);
61312
62088
  if (count2 === 0) {
61313
62089
  return plain("none", Object.freeze([]), "no red pull request to triage, and no pull request on the current branch");
@@ -61663,7 +62439,7 @@ var init_triage_report = __esm(() => {
61663
62439
  function budgetEntry(usd) {
61664
62440
  const spelled2 = String(usd);
61665
62441
  if (parseBudgetUsd(spelled2) === null) {
61666
- throw new TypeError(`${PREFIX36}: refused an amount of ${JSON.stringify(usd)}, expected US dollars above zero` + " with at most six whole digits and six decimal places");
62442
+ throw new TypeError(`${PREFIX37}: refused an amount of ${JSON.stringify(usd)}, expected US dollars above zero` + " with at most six whole digits and six decimal places");
61667
62443
  }
61668
62444
  return `${BUDGET_KEY}=${spelled2}`;
61669
62445
  }
@@ -61690,7 +62466,7 @@ function withTaskBudget(plan2, usd) {
61690
62466
  `).map((line) => TASK_LINE2.test(line) ? withEntry(line, entry) : line).join(`
61691
62467
  `);
61692
62468
  }
61693
- var BUDGET_KEY = "budget", TASK_LINE2, TRAILING_BLOCK, PREFIX36 = "pr triage --resolve budget";
62469
+ var BUDGET_KEY = "budget", TASK_LINE2, TRAILING_BLOCK, PREFIX37 = "pr triage --resolve budget";
61694
62470
  var init_budget2 = __esm(() => {
61695
62471
  init_declaration();
61696
62472
  TASK_LINE2 = /^- \[[^\]]*\] /;
@@ -61698,8 +62474,8 @@ var init_budget2 = __esm(() => {
61698
62474
  });
61699
62475
 
61700
62476
  // src/pr/plans/load.ts
61701
- import { existsSync as existsSync60, readFileSync as readFileSync74 } from "fs";
61702
- import { dirname as dirname34, join as join90 } from "path";
62477
+ import { existsSync as existsSync61, readFileSync as readFileSync74 } from "fs";
62478
+ import { dirname as dirname34, join as join92 } from "path";
61703
62479
  import { fileURLToPath as fileURLToPath7 } from "url";
61704
62480
  function pinnedPlanFileName(triageClass) {
61705
62481
  return `resolve-${triageClass}.md`;
@@ -61710,8 +62486,8 @@ function hasPinnedPlan(triageClass) {
61710
62486
  function pinnedPlanCandidates(moduleDir, triageClass) {
61711
62487
  const name2 = pinnedPlanFileName(triageClass);
61712
62488
  return [
61713
- join90(moduleDir, PINNED_PLANS_DIRNAME, name2),
61714
- join90(moduleDir, name2)
62489
+ join92(moduleDir, PINNED_PLANS_DIRNAME, name2),
62490
+ join92(moduleDir, name2)
61715
62491
  ];
61716
62492
  }
61717
62493
  function readPinnedPlan(triageClass, lookup = {}) {
@@ -61719,7 +62495,7 @@ function readPinnedPlan(triageClass, lookup = {}) {
61719
62495
  throw new Error(`No pinned resolve plan ships for ${triageClass}; ` + `one ships for ${PINNED_PLAN_CLASSES.join(", ")}`);
61720
62496
  }
61721
62497
  const candidates = pinnedPlanCandidates(lookup.moduleDir ?? MODULE_DIR5, triageClass);
61722
- const found = candidates.find((candidate) => existsSync60(candidate));
62498
+ const found = candidates.find((candidate) => existsSync61(candidate));
61723
62499
  if (found === undefined) {
61724
62500
  throw new Error(`The pinned resolve plan for ${triageClass} is missing: ` + `no file at ${candidates.join(" or ")}`);
61725
62501
  }
@@ -61790,7 +62566,7 @@ var init_load2 = __esm(() => {
61790
62566
  // src/pr/triage/attempts.ts
61791
62567
  function readCap2(maxAttempts) {
61792
62568
  if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1) {
61793
- throw new TypeError(`${PREFIX37}: refused a cap of ${JSON.stringify(maxAttempts)},` + " expected a positive whole number");
62569
+ throw new TypeError(`${PREFIX38}: refused a cap of ${JSON.stringify(maxAttempts)},` + " expected a positive whole number");
61794
62570
  }
61795
62571
  return maxAttempts;
61796
62572
  }
@@ -61860,7 +62636,7 @@ function readAttemptRepeat(input) {
61860
62636
  reason: `attempt ${attempts} ended as \`${outcome2.triageClass}\`` + ` ${describeStep(outcome2.step)}, ${against}`
61861
62637
  };
61862
62638
  }
61863
- var ATTEMPT_STOPS, PREFIX37 = "pr triage attempts", WHITESPACE5, NO_STEP_KEY = "";
62639
+ var ATTEMPT_STOPS, PREFIX38 = "pr triage attempts", WHITESPACE5, NO_STEP_KEY = "";
61864
62640
  var init_attempts = __esm(() => {
61865
62641
  ATTEMPT_STOPS = Object.freeze([
61866
62642
  "cap",
@@ -61870,16 +62646,16 @@ var init_attempts = __esm(() => {
61870
62646
  });
61871
62647
 
61872
62648
  // src/commands/pr/resolve-loop.ts
61873
- import { mkdirSync as mkdirSync26, writeFileSync as writeFileSync28 } from "fs";
61874
- import { join as join91 } from "path";
62649
+ import { mkdirSync as mkdirSync27, writeFileSync as writeFileSync29 } from "fs";
62650
+ import { join as join93 } from "path";
61875
62651
  function resolveRunDir(home, number, attempt5) {
61876
- return join91(home, RESOLVE_SUBDIR, `${RUN_PREFIX}${number}`, `${ATTEMPT_PREFIX}${attempt5}`);
62652
+ return join93(home, RESOLVE_SUBDIR, `${RUN_PREFIX}${number}`, `${ATTEMPT_PREFIX}${attempt5}`);
61877
62653
  }
61878
62654
  function writeResolvePlan(write) {
61879
62655
  const dir = resolveRunDir(write.home, write.number, write.attempt);
61880
- mkdirSync26(dir, { recursive: true });
61881
- const path9 = join91(dir, pinnedPlanFileName(write.triageClass));
61882
- writeFileSync28(path9, write.plan, "utf8");
62656
+ mkdirSync27(dir, { recursive: true });
62657
+ const path9 = join93(dir, pinnedPlanFileName(write.triageClass));
62658
+ writeFileSync29(path9, write.plan, "utf8");
61883
62659
  return path9;
61884
62660
  }
61885
62661
  function resolveLoopArgv(planPath) {
@@ -61921,13 +62697,13 @@ var RESOLVE_SUBDIR, RUN_PREFIX = "pr-", ATTEMPT_PREFIX = "attempt-", LOOP_WORDS,
61921
62697
  var init_resolve_loop = __esm(() => {
61922
62698
  init_config_sections();
61923
62699
  init_load2();
61924
- RESOLVE_SUBDIR = join91(".rafa", "resolve");
62700
+ RESOLVE_SUBDIR = join93(".rafa", "resolve");
61925
62701
  LOOP_WORDS = Object.freeze(["loop", "start"]);
61926
62702
  });
61927
62703
 
61928
62704
  // src/pr/triage/version-convert.ts
61929
- import { mkdirSync as mkdirSync27, readFileSync as readFileSync75, writeFileSync as writeFileSync29 } from "fs";
61930
- import { dirname as dirname35, join as join92, posix as posix9 } from "path";
62705
+ import { mkdirSync as mkdirSync28, readFileSync as readFileSync75, writeFileSync as writeFileSync30 } from "fs";
62706
+ import { dirname as dirname35, join as join94, posix as posix9 } from "path";
61931
62707
  function conversionLevel(from, to) {
61932
62708
  if (from === null)
61933
62709
  return null;
@@ -61966,7 +62742,7 @@ function fragmentNamesAt(git, commit, directory2) {
61966
62742
  return [];
61967
62743
  return listed4.stdout.split("\x00").filter((path9) => path9 !== "").map((path9) => posix9.basename(path9));
61968
62744
  }
61969
- function refused5(problem2, problems) {
62745
+ function refused6(problem2, problems) {
61970
62746
  return { outcome: "refused", problem: problem2, problems };
61971
62747
  }
61972
62748
  function fragmentTitle2(title2, plan2) {
@@ -62003,9 +62779,9 @@ function planConversion(input, problems) {
62003
62779
  const baseRef = `${input.remote ?? RELEASE_REMOTE}/${input.base}`;
62004
62780
  const headCommit = commitOf2(git, "HEAD");
62005
62781
  if (headCommit === null)
62006
- return refused5(`the worktree at ${input.worktree} has no HEAD commit`, problems);
62782
+ return refused6(`the worktree at ${input.worktree} has no HEAD commit`, problems);
62007
62783
  if (input.head !== null && headCommit !== input.head) {
62008
- return refused5(`the worktree's ${input.branch} is at ${headCommit.slice(0, SHORT_HASH_LENGTH2)}, not at the pull request's` + ` head ${input.head.slice(0, SHORT_HASH_LENGTH2)}; bring the branch up to its pull request's head and run it again`, problems);
62784
+ return refused6(`the worktree's ${input.branch} is at ${headCommit.slice(0, SHORT_HASH_LENGTH2)}, not at the pull request's` + ` head ${input.head.slice(0, SHORT_HASH_LENGTH2)}; bring the branch up to its pull request's head and run it again`, problems);
62009
62785
  }
62010
62786
  const guard = readGuard({
62011
62787
  git,
@@ -62015,7 +62791,7 @@ function planConversion(input, problems) {
62015
62791
  now: input.now
62016
62792
  });
62017
62793
  if (!guard.ok)
62018
- return refused5(`the release guard could not read ${input.branch}: ${guard.problem}`, problems);
62794
+ return refused6(`the release guard could not read ${input.branch}: ${guard.problem}`, problems);
62019
62795
  const { verdict } = guard;
62020
62796
  if (verdict.answer === "clean" || verdict.answer === "missing") {
62021
62797
  return { outcome: "unstamped", answer: verdict.answer, problems };
@@ -62023,25 +62799,25 @@ function planConversion(input, problems) {
62023
62799
  const { stamp: stamp2 } = verdict;
62024
62800
  const start2 = mergeBaseSide(git, guard.base.commit, headCommit, settings);
62025
62801
  if (start2 === null)
62026
- return refused5(`${input.branch} and ${baseRef} share no merge base`, problems);
62802
+ return refused6(`${input.branch} and ${baseRef} share no merge base`, problems);
62027
62803
  const level = conversionLevel(start2.version, stamp2.version);
62028
62804
  if (level === null) {
62029
- return refused5(`no release level reads off the stamp: ${input.branch} stamped ${stamp2.version} over` + ` ${start2.version ?? "no version"} at its merge base with ${baseRef}`, problems);
62805
+ return refused6(`no release level reads off the stamp: ${input.branch} stamped ${stamp2.version} over` + ` ${start2.version ?? "no version"} at its merge base with ${baseRef}`, problems);
62030
62806
  }
62031
62807
  if (start2.version === null)
62032
- return refused5(`the merge base declares no version in ${settings.releaseVersionFile} to restore`, problems);
62808
+ return refused6(`the merge base declares no version in ${settings.releaseVersionFile} to restore`, problems);
62033
62809
  if (start2.changelog === null) {
62034
- return refused5(`the merge base holds no ${settings.releaseChangelog}, so ${input.branch} created it and there is no text to restore`, problems);
62810
+ return refused6(`the merge base holds no ${settings.releaseChangelog}, so ${input.branch} created it and there is no text to restore`, problems);
62035
62811
  }
62036
62812
  let branchVersionText;
62037
62813
  try {
62038
- branchVersionText = readFileSync75(join92(input.worktree, settings.releaseVersionFile), "utf8");
62814
+ branchVersionText = readFileSync75(join94(input.worktree, settings.releaseVersionFile), "utf8");
62039
62815
  } catch (error) {
62040
- return refused5(`${settings.releaseVersionFile} could not be read in the worktree: ${messageOf(error)}`, problems);
62816
+ return refused6(`${settings.releaseVersionFile} could not be read in the worktree: ${messageOf(error)}`, problems);
62041
62817
  }
62042
62818
  const rewritten = replaceManifestVersion(branchVersionText, start2.version);
62043
62819
  if (rewritten === null) {
62044
- return refused5(`${input.branch}'s ${settings.releaseVersionFile} holds no version pair ${start2.version} could be written over`, problems);
62820
+ return refused6(`${input.branch}'s ${settings.releaseVersionFile} holds no version pair ${start2.version} could be written over`, problems);
62045
62821
  }
62046
62822
  return {
62047
62823
  stamp: stamp2,
@@ -62061,10 +62837,10 @@ function writeConversion(input, plan2) {
62061
62837
  [input.settings.releaseChangelog, plan2.changelogText]
62062
62838
  ];
62063
62839
  for (const [path9, text2] of writes) {
62064
- const target = join92(input.worktree, path9);
62840
+ const target = join94(input.worktree, path9);
62065
62841
  try {
62066
- mkdirSync27(dirname35(target), { recursive: true });
62067
- writeFileSync29(target, text2);
62842
+ mkdirSync28(dirname35(target), { recursive: true });
62843
+ writeFileSync30(target, text2);
62068
62844
  } catch (error) {
62069
62845
  return `${path9} could not be written in the worktree, which may hold part of the conversion: ${messageOf(error)}`;
62070
62846
  }
@@ -62083,18 +62859,18 @@ function convertStampedVersion(input) {
62083
62859
  return plan2;
62084
62860
  const written3 = writeConversion(input, plan2);
62085
62861
  if (written3 !== null)
62086
- return refused5(written3, problems);
62862
+ return refused6(written3, problems);
62087
62863
  const paths = [plan2.fragmentPath, settings.releaseVersionFile, settings.releaseChangelog].map((path9) => gitPathOf(path9));
62088
62864
  const added = git(["add", "--", ...paths]);
62089
62865
  if (!added.ok)
62090
- return refused5(`the conversion's files could not be staged, and are left in the worktree: ${gitSaid(added)}`, problems);
62866
+ return refused6(`the conversion's files could not be staged, and are left in the worktree: ${gitSaid(added)}`, problems);
62091
62867
  const subject = conversionCommitSubject(plan2.plan, plan2.stamp.version);
62092
62868
  const made = git(["commit", "--cleanup=whitespace", "-m", subject, "--", ...paths]);
62093
62869
  if (!made.ok)
62094
- return refused5(`the conversion could not be committed, and is left staged in the worktree: ${gitSaid(made)}`, problems);
62870
+ return refused6(`the conversion could not be committed, and is left staged in the worktree: ${gitSaid(made)}`, problems);
62095
62871
  const commit = commitOf2(git, "HEAD");
62096
62872
  if (commit === null)
62097
- return refused5("the conversion was committed, but HEAD names no commit to report", problems);
62873
+ return refused6("the conversion was committed, but HEAD names no commit to report", problems);
62098
62874
  return {
62099
62875
  outcome: "converted",
62100
62876
  commit,
@@ -62563,16 +63339,16 @@ async function redPullRequests(pr) {
62563
63339
  }
62564
63340
  return red;
62565
63341
  }
62566
- async function selectTargets(pr, warn4, now, resolve39) {
63342
+ async function selectTargets(pr, warn4, now, resolve40) {
62567
63343
  const branch = readBranchOrNone(pr, warn4);
62568
63344
  const current = branch === null ? null : await onProvider(`read the open pull request for the branch "${branch}"`, () => pr.pulls.findOpen(branch));
62569
63345
  if (current !== null)
62570
- return selectTriagePullRequests({ current, candidates: [], now, resolve: resolve39 });
63346
+ return selectTriagePullRequests({ current, candidates: [], now, resolve: resolve40 });
62571
63347
  return selectTriagePullRequests({
62572
63348
  current: null,
62573
63349
  candidates: await redPullRequests(pr),
62574
63350
  now,
62575
- resolve: resolve39
63351
+ resolve: resolve40
62576
63352
  });
62577
63353
  }
62578
63354
  async function detailOf16(pr, number) {
@@ -62709,7 +63485,7 @@ async function runTriage(context, seams) {
62709
63485
  }
62710
63486
  const numbers = asked === null ? (selection?.assess ?? []).map((one) => one.number) : [asked];
62711
63487
  const readings = [];
62712
- let resolve39 = null;
63488
+ let resolve40 = null;
62713
63489
  for (const number of numbers) {
62714
63490
  const assess = () => assessOne({
62715
63491
  pr,
@@ -62729,7 +63505,7 @@ async function runTriage(context, seams) {
62729
63505
  readings.push(reading2);
62730
63506
  continue;
62731
63507
  }
62732
- resolve39 = await resolvePullRequest({
63508
+ resolve40 = await resolvePullRequest({
62733
63509
  trust: {
62734
63510
  permissions,
62735
63511
  trustedAuthors: pr.trustedAuthors,
@@ -62751,16 +63527,16 @@ async function runTriage(context, seams) {
62751
63527
  ...seams.clock === undefined ? {} : { clock: seams.clock },
62752
63528
  ...seams.sleep === undefined ? {} : { sleep: seams.sleep }
62753
63529
  });
62754
- readings.push(resolve39.reading);
63530
+ readings.push(resolve40.reading);
62755
63531
  }
62756
63532
  const text2 = [
62757
63533
  ...selection === null ? [] : [selection.message],
62758
63534
  ...readings.length === 0 ? [] : [renderTriages(readings)],
62759
- ...resolve39 === null ? [] : resolve39.lines
63535
+ ...resolve40 === null ? [] : resolve40.lines
62760
63536
  ].join(`
62761
63537
 
62762
63538
  `);
62763
- return { selection, readings, resolve: resolve39, text: text2, exitCode: resolve39?.exitCode ?? 0 };
63539
+ return { selection, readings, resolve: resolve40, text: text2, exitCode: resolve40?.exitCode ?? 0 };
62764
63540
  }
62765
63541
  function createPrTriageCommand(seams = DEFAULT_TRIAGE_SEAMS) {
62766
63542
  const command = {
@@ -63188,9 +63964,9 @@ async function settleByPr(worktree, settings, pulls) {
63188
63964
  if ("problem" in branch)
63189
63965
  return failed4("unpushed", build2, false, branch.problem);
63190
63966
  if (!branch.same) {
63191
- const refused6 = pushReleaseBranch(worktree, build2, branch.current);
63192
- if (refused6 !== null)
63193
- return refused6;
63967
+ const refused7 = pushReleaseBranch(worktree, build2, branch.current);
63968
+ if (refused7 !== null)
63969
+ return refused7;
63194
63970
  }
63195
63971
  const head = branch.same && branch.current !== null ? branch.current : build2.release;
63196
63972
  const draft = {
@@ -63367,9 +64143,9 @@ var init_settle_tag = __esm(() => {
63367
64143
  });
63368
64144
 
63369
64145
  // src/release/settle-worktree.ts
63370
- import { existsSync as existsSync61, mkdtempSync as mkdtempSync3, realpathSync as realpathSync17, rmSync as rmSync13 } from "fs";
64146
+ import { existsSync as existsSync62, mkdtempSync as mkdtempSync3, realpathSync as realpathSync17, rmSync as rmSync13 } from "fs";
63371
64147
  import { tmpdir as tmpdir5 } from "os";
63372
- import { join as join93 } from "path";
64148
+ import { join as join95 } from "path";
63373
64149
  function removeScratch(git, scratch, path9) {
63374
64150
  const removed = git(["worktree", "remove", "--force", path9]);
63375
64151
  const problems = [];
@@ -63384,14 +64160,14 @@ function removeScratch(git, scratch, path9) {
63384
64160
  problems.push(`the worktree record of ${path9} could not be pruned: ${gitSaid(pruned)}`);
63385
64161
  }
63386
64162
  }
63387
- if (problems.length === 0 && existsSync61(scratch)) {
64163
+ if (problems.length === 0 && existsSync62(scratch)) {
63388
64164
  problems.push(`the scratch directory ${scratch} is still there`);
63389
64165
  }
63390
64166
  return problems.length === 0 ? null : problems.join("; ");
63391
64167
  }
63392
64168
  function makeScratch(root) {
63393
64169
  try {
63394
- return realpathSync17(mkdtempSync3(join93(root, SETTLE_SCRATCH_PREFIX)));
64170
+ return realpathSync17(mkdtempSync3(join95(root, SETTLE_SCRATCH_PREFIX)));
63395
64171
  } catch (error) {
63396
64172
  return { ok: false, problem: `no scratch directory could be made under ${root}: ${messageOf(error)}`, leftover: null };
63397
64173
  }
@@ -63413,7 +64189,7 @@ function addWorktree(options) {
63413
64189
  const scratch = makeScratch(options.scratchRoot ?? tmpdir5());
63414
64190
  if (typeof scratch !== "string")
63415
64191
  return scratch;
63416
- const path9 = join93(scratch, WORKTREE_DIRECTORY);
64192
+ const path9 = join95(scratch, WORKTREE_DIRECTORY);
63417
64193
  const added = git(["worktree", "add", "--detach", path9, commit]);
63418
64194
  if (!added.ok) {
63419
64195
  return {
@@ -63689,7 +64465,7 @@ var init_release_commit = __esm(() => {
63689
64465
 
63690
64466
  // src/commands/release/tag.ts
63691
64467
  import { readFileSync as readFileSync76 } from "fs";
63692
- import { join as join94 } from "path";
64468
+ import { join as join96 } from "path";
63693
64469
  function readBranch3(git) {
63694
64470
  const result2 = git(["rev-parse", "--abbrev-ref", "HEAD"]);
63695
64471
  if (!result2.ok) {
@@ -63709,7 +64485,7 @@ function textOf5(path9) {
63709
64485
  }
63710
64486
  }
63711
64487
  function readVersion(root, configured) {
63712
- const resolved = join94(root, configured);
64488
+ const resolved = join96(root, configured);
63713
64489
  const text2 = textOf5(resolved);
63714
64490
  if (text2 === null) {
63715
64491
  return {
@@ -63723,7 +64499,7 @@ function readVersion(root, configured) {
63723
64499
  return version2 === null ? { path: configured, text: text2, version: null, problem: `${resolved} declares no version this can read` } : { path: configured, text: text2, version: version2, problem: null };
63724
64500
  }
63725
64501
  function readNewestRelease(root, configured) {
63726
- const resolved = join94(root, configured);
64502
+ const resolved = join96(root, configured);
63727
64503
  const text2 = textOf5(resolved);
63728
64504
  if (text2 === null) {
63729
64505
  return { path: configured, version: null, problem: `the changelog could not be read at ${resolved}` };
@@ -64011,10 +64787,10 @@ var init_roadmap2 = __esm(() => {
64011
64787
  });
64012
64788
 
64013
64789
  // src/backfill/proposal-file.ts
64014
- import { readdirSync as readdirSync21, readFileSync as readFileSync77 } from "fs";
64015
- import { join as join95 } from "path";
64790
+ import { readdirSync as readdirSync22, readFileSync as readFileSync77 } from "fs";
64791
+ import { join as join97 } from "path";
64016
64792
  function proposalPath(dir, batch) {
64017
- return join95(dir, `${PROPOSAL_FILE_PREFIX}${String(batch).padStart(2, "0")}${PROPOSAL_FILE_EXTENSION}`);
64793
+ return join97(dir, `${PROPOSAL_FILE_PREFIX}${String(batch).padStart(2, "0")}${PROPOSAL_FILE_EXTENSION}`);
64018
64794
  }
64019
64795
  function isProposalFileName(name2) {
64020
64796
  return name2.startsWith(PROPOSAL_FILE_PREFIX) && name2.endsWith(PROPOSAL_FILE_EXTENSION);
@@ -64145,12 +64921,12 @@ function parseProposalFile(text2) {
64145
64921
  function readProposalFiles(dir) {
64146
64922
  let names2;
64147
64923
  try {
64148
- names2 = readdirSync21(dir);
64924
+ names2 = readdirSync22(dir);
64149
64925
  } catch {
64150
64926
  return [];
64151
64927
  }
64152
64928
  return names2.filter((name2) => isProposalFileName(name2)).sort((left, right) => left.localeCompare(right)).map((name2) => {
64153
- const path9 = join95(dir, name2);
64929
+ const path9 = join97(dir, name2);
64154
64930
  try {
64155
64931
  return { path: path9, result: parseProposalFile(readFileSync77(path9, "utf8")) };
64156
64932
  } catch (error) {
@@ -64161,7 +64937,7 @@ function readProposalFiles(dir) {
64161
64937
  var BACKFILL_PATH, PROPOSAL_FILE_PREFIX = "proposals-", PROPOSAL_FILE_EXTENSION = ".yaml", SHA256_PATTERN, PROPOSAL_STATUSES, DRAFT_FILE = "draft", REVIEWED_FILE = "reviewed", PROPOSAL_ROW_STATUSES, UNANSWERED_ROW = "unanswered", ANSWERED_ROW = "answered", PROPOSAL_NEEDS, PROPOSAL_FILE_HEADER;
64162
64938
  var init_proposal_file = __esm(() => {
64163
64939
  init_skill();
64164
- BACKFILL_PATH = join95(".rafa", "backfill");
64940
+ BACKFILL_PATH = join97(".rafa", "backfill");
64165
64941
  SHA256_PATTERN = /^[0-9a-f]{64}$/;
64166
64942
  PROPOSAL_STATUSES = ["draft", "reviewed"];
64167
64943
  PROPOSAL_ROW_STATUSES = ["answered", "unanswered"];
@@ -64183,19 +64959,19 @@ var init_proposal_file = __esm(() => {
64183
64959
  });
64184
64960
 
64185
64961
  // src/backfill/backup.ts
64186
- import { copyFileSync as copyFileSync6, existsSync as existsSync62, mkdirSync as mkdirSync28 } from "fs";
64187
- import { dirname as dirname36, isAbsolute as isAbsolute25, join as join96, relative as relative22, resolve as resolve39, sep as sep9 } from "path";
64962
+ import { copyFileSync as copyFileSync6, existsSync as existsSync63, mkdirSync as mkdirSync29 } from "fs";
64963
+ import { dirname as dirname36, isAbsolute as isAbsolute25, join as join98, relative as relative22, resolve as resolve40, sep as sep9 } from "path";
64188
64964
  function backupDirectory(base) {
64189
- return join96(base, BACKUP_PATH);
64965
+ return join98(base, BACKUP_PATH);
64190
64966
  }
64191
64967
  function isUnder(root, path9) {
64192
- const step2 = relative22(resolve39(root), resolve39(path9));
64968
+ const step2 = relative22(resolve40(root), resolve40(path9));
64193
64969
  return step2 === "" || !step2.startsWith(`..${sep9}`) && step2 !== ".." && !isAbsolute25(step2);
64194
64970
  }
64195
64971
  function backupPathFor(path9, root, backupDir) {
64196
64972
  if (!isUnder(root, path9))
64197
64973
  return null;
64198
- return join96(backupDir, relative22(resolve39(root), resolve39(path9)));
64974
+ return join98(backupDir, relative22(resolve40(root), resolve40(path9)));
64199
64975
  }
64200
64976
  function backupFile(path9, options) {
64201
64977
  if (options.repoRoot !== null && isUnder(options.repoRoot, path9)) {
@@ -64204,11 +64980,11 @@ function backupFile(path9, options) {
64204
64980
  const backup = backupPathFor(path9, options.root, options.backupDir);
64205
64981
  if (backup === null)
64206
64982
  return { path: path9, kind: "outside", backup: null };
64207
- if (!existsSync62(path9))
64983
+ if (!existsSync63(path9))
64208
64984
  return { path: path9, kind: "missing", backup: null };
64209
- if (existsSync62(backup))
64985
+ if (existsSync63(backup))
64210
64986
  return { path: path9, kind: "kept", backup };
64211
- mkdirSync28(dirname36(backup), { recursive: true });
64987
+ mkdirSync29(dirname36(backup), { recursive: true });
64212
64988
  copyFileSync6(path9, backup);
64213
64989
  return { path: path9, kind: "copied", backup };
64214
64990
  }
@@ -64232,12 +65008,12 @@ function countBackups(results) {
64232
65008
  var BACKUP_PATH, BACKUP_KINDS;
64233
65009
  var init_backup = __esm(() => {
64234
65010
  init_proposal_file();
64235
- BACKUP_PATH = join96(BACKFILL_PATH, "backup");
65011
+ BACKUP_PATH = join98(BACKFILL_PATH, "backup");
64236
65012
  BACKUP_KINDS = ["copied", "kept", "tracked", "outside", "missing"];
64237
65013
  });
64238
65014
 
64239
65015
  // src/backfill/derive.ts
64240
- import { readFileSync as readFileSync78, writeFileSync as writeFileSync30 } from "fs";
65016
+ import { readFileSync as readFileSync78, writeFileSync as writeFileSync31 } from "fs";
64241
65017
  import { basename as basename24 } from "path";
64242
65018
  function renderWhenToUse(trigger, prevents) {
64243
65019
  return `${sentenceEnded(trigger)} Prevents: ${prevents}`;
@@ -64409,7 +65185,7 @@ function applyFile(action, options) {
64409
65185
  text: null
64410
65186
  };
64411
65187
  }
64412
- writeFileSync30(action.path, action.text, "utf8");
65188
+ writeFileSync31(action.path, action.text, "utf8");
64413
65189
  const after = failureLines(checkFile(action.path, "skill", {
64414
65190
  projectRoot: options.projectRoot,
64415
65191
  pathDirs: options.pathDirs
@@ -64417,7 +65193,7 @@ function applyFile(action, options) {
64417
65193
  const added = addedFailures(action.before, after);
64418
65194
  if (added.length === 0)
64419
65195
  return action;
64420
- writeFileSync30(action.path, action.original, "utf8");
65196
+ writeFileSync31(action.path, action.original, "utf8");
64421
65197
  return {
64422
65198
  ...action,
64423
65199
  kind: "refused",
@@ -64516,8 +65292,8 @@ function summaryLine3(rows2) {
64516
65292
  const parts = DEMOTION_VERDICTS.map((verdict) => `${counts[verdict]} ${verdict}`);
64517
65293
  return `${files}: ${parts.join(", ")}.`;
64518
65294
  }
64519
- function writeDemotionReport(report4) {
64520
- const rows2 = report4.rows.map((row3) => tableLine2([
65295
+ function writeDemotionReport(report5) {
65296
+ const rows2 = report5.rows.map((row3) => tableLine2([
64521
65297
  escapeCell(row3.path),
64522
65298
  escapeCell(row3.hash),
64523
65299
  escapeCell(row3.verdict),
@@ -64526,11 +65302,11 @@ function writeDemotionReport(report4) {
64526
65302
  escapeCell(row3.overrideReason ?? EMPTY_CELL2)
64527
65303
  ]));
64528
65304
  return [
64529
- renderHeader(report4.status),
65305
+ renderHeader(report5.status),
64530
65306
  "",
64531
65307
  REPORT_TITLE,
64532
65308
  "",
64533
- summaryLine3(report4.rows),
65309
+ summaryLine3(report5.rows),
64534
65310
  "",
64535
65311
  tableLine2(DEMOTION_REPORT_COLUMNS),
64536
65312
  tableLine2(DEMOTION_REPORT_COLUMNS.map(() => "---")),
@@ -64853,8 +65629,8 @@ The files:
64853
65629
  });
64854
65630
 
64855
65631
  // src/backfill/propose.ts
64856
- import { mkdirSync as mkdirSync29, readFileSync as readFileSync80, writeFileSync as writeFileSync31 } from "fs";
64857
- import { join as join97, resolve as resolve40 } from "path";
65632
+ import { mkdirSync as mkdirSync30, readFileSync as readFileSync80, writeFileSync as writeFileSync32 } from "fs";
65633
+ import { join as join99, resolve as resolve41 } from "path";
64858
65634
  function unanswered(candidate, note) {
64859
65635
  return {
64860
65636
  path: candidate.relative,
@@ -64927,12 +65703,12 @@ async function runProposalPass(options) {
64927
65703
  const batches = batchCandidates(candidates);
64928
65704
  const written3 = [];
64929
65705
  if (batches.length > 0)
64930
- mkdirSync29(options.backfillDir, { recursive: true });
65706
+ mkdirSync30(options.backfillDir, { recursive: true });
64931
65707
  for (const [index, batch] of batches.entries()) {
64932
65708
  const number = index + 1;
64933
65709
  const file = await runProposalBatch(number, batch, options);
64934
65710
  const path9 = proposalPath(options.backfillDir, number);
64935
- writeFileSync31(path9, renderProposalFile(file), "utf8");
65711
+ writeFileSync32(path9, renderProposalFile(file), "utf8");
64936
65712
  written3.push({ path: path9, file });
64937
65713
  }
64938
65714
  return { written: written3, candidates: candidates.length };
@@ -64974,7 +65750,7 @@ function writeRow(path9, row3, original, options) {
64974
65750
  return inert2(path9, row3, "unchanged", "every field the row proposes is already what the file carries");
64975
65751
  }
64976
65752
  const before = checkLines(path9, options);
64977
- writeFileSync31(path9, writeFrontmatter(document, mergeFrontmatter(document.data, changes)), "utf8");
65753
+ writeFileSync32(path9, writeFrontmatter(document, mergeFrontmatter(document.data, changes)), "utf8");
64978
65754
  const added = addedFailures(before, checkLines(path9, options));
64979
65755
  if (added.length === 0) {
64980
65756
  return {
@@ -64986,7 +65762,7 @@ function writeRow(path9, row3, original, options) {
64986
65762
  added: []
64987
65763
  };
64988
65764
  }
64989
- writeFileSync31(path9, original, "utf8");
65765
+ writeFileSync32(path9, original, "utf8");
64990
65766
  return {
64991
65767
  path: path9,
64992
65768
  relative: row3.path,
@@ -64997,7 +65773,7 @@ function writeRow(path9, row3, original, options) {
64997
65773
  };
64998
65774
  }
64999
65775
  function applyRow(root, row3, options) {
65000
- const path9 = join97(root, ...row3.path.split("/"));
65776
+ const path9 = join99(root, ...row3.path.split("/"));
65001
65777
  if (row3.status === UNANSWERED_ROW) {
65002
65778
  return inert2(path9, row3, "unanswered", row3.note ?? "the row carries no answer");
65003
65779
  }
@@ -65020,7 +65796,7 @@ function fileRefusal(file, root) {
65020
65796
  if (file.status !== REVIEWED_FILE) {
65021
65797
  return `the proposal file is still marked ${file.status}: review it and mark it ${REVIEWED_FILE}`;
65022
65798
  }
65023
- return resolve40(file.skills) === resolve40(root) ? null : `the proposal file names the skills directory ${file.skills}, not ${root}`;
65799
+ return resolve41(file.skills) === resolve41(root) ? null : `the proposal file names the skills directory ${file.skills}, not ${root}`;
65024
65800
  }
65025
65801
  function countProposalActions(actions) {
65026
65802
  const counts = Object.fromEntries(PROPOSAL_ACTION_KINDS.map((kind) => [kind, 0]));
@@ -65034,7 +65810,7 @@ function applyProposals(files, root, options) {
65034
65810
  for (const file of files) {
65035
65811
  const refusal16 = fileRefusal(file, root);
65036
65812
  for (const row3 of file.rows) {
65037
- const action = refusal16 === null ? applyRow(root, row3, options) : inert2(join97(root, ...row3.path.split("/")), row3, "refused", refusal16);
65813
+ const action = refusal16 === null ? applyRow(root, row3, options) : inert2(join99(root, ...row3.path.split("/")), row3, "refused", refusal16);
65038
65814
  actions.push(action);
65039
65815
  if (row3.trigger !== null && action.kind !== "refused" && action.kind !== "unanswered") {
65040
65816
  triggers[row3.name] = row3.trigger;
@@ -65061,8 +65837,8 @@ var init_propose = __esm(() => {
65061
65837
  });
65062
65838
 
65063
65839
  // src/demote/select.ts
65064
- import { readdirSync as readdirSync22, readFileSync as readFileSync81, realpathSync as realpathSync18, statSync as statSync29 } from "fs";
65065
- import { basename as basename25, dirname as dirname37, join as join98 } from "path";
65840
+ import { readdirSync as readdirSync23, readFileSync as readFileSync81, realpathSync as realpathSync18, statSync as statSync30 } from "fs";
65841
+ import { basename as basename25, dirname as dirname37, join as join100 } from "path";
65066
65842
  function realOrSelf(path9) {
65067
65843
  try {
65068
65844
  return realpathSync18(path9);
@@ -65084,19 +65860,19 @@ function resolveDemotionScope(dir, home) {
65084
65860
  if (basename25(claude) !== CLAUDE_DIRECTORY)
65085
65861
  return null;
65086
65862
  const base = dirname37(claude);
65087
- const demotedDir = join98(base, DEMOTED_PATH);
65863
+ const demotedDir = join100(base, DEMOTED_PATH);
65088
65864
  return {
65089
65865
  scope: samePath(base, home) ? "user" : "project",
65090
65866
  dir,
65091
65867
  base,
65092
65868
  demotedDir,
65093
- instinctsDir: join98(base, INSTINCTS_PATH),
65094
- reportPath: join98(demotedDir, REPORT_FILE)
65869
+ instinctsDir: join100(base, INSTINCTS_PATH),
65870
+ reportPath: join100(demotedDir, REPORT_FILE)
65095
65871
  };
65096
65872
  }
65097
65873
  function entryKind2(path9) {
65098
65874
  try {
65099
- const stats = statSync29(path9);
65875
+ const stats = statSync30(path9);
65100
65876
  if (stats.isFile())
65101
65877
  return "file";
65102
65878
  if (stats.isDirectory())
@@ -65108,7 +65884,7 @@ function entryKind2(path9) {
65108
65884
  }
65109
65885
  function visibleNames2(dir) {
65110
65886
  try {
65111
- return readdirSync22(dir).filter((name2) => !name2.startsWith(".")).sort((left, right) => left.localeCompare(right));
65887
+ return readdirSync23(dir).filter((name2) => !name2.startsWith(".")).sort((left, right) => left.localeCompare(right));
65112
65888
  } catch {
65113
65889
  return [];
65114
65890
  }
@@ -65118,7 +65894,7 @@ function readSelected(absolute, path9, entries) {
65118
65894
  let mtime;
65119
65895
  try {
65120
65896
  text2 = readFileSync81(absolute, "utf8");
65121
- mtime = statSync29(absolute).mtime.toISOString();
65897
+ mtime = statSync30(absolute).mtime.toISOString();
65122
65898
  } catch {
65123
65899
  return null;
65124
65900
  }
@@ -65133,14 +65909,14 @@ function selectsLearned(text2, scope) {
65133
65909
  return origin === AUTO_EXTRACTED_ORIGIN;
65134
65910
  }
65135
65911
  function selectLearned(scope) {
65136
- const dir = join98(scope.dir, LEARNED_DIRECTORY);
65912
+ const dir = join100(scope.dir, LEARNED_DIRECTORY);
65137
65913
  if (entryKind2(dir) !== "directory")
65138
65914
  return [];
65139
65915
  const selected = [];
65140
65916
  for (const name2 of visibleNames2(dir)) {
65141
65917
  if (!name2.endsWith(MARKDOWN_EXTENSION) || name2 === MARKDOWN_EXTENSION)
65142
65918
  continue;
65143
- const absolute = join98(dir, name2);
65919
+ const absolute = join100(dir, name2);
65144
65920
  if (entryKind2(absolute) !== "file")
65145
65921
  continue;
65146
65922
  const file = readSelected(absolute, `${LEARNED_DIRECTORY}/${name2}`, undefined);
@@ -65154,10 +65930,10 @@ function selectSkillFiles(scope) {
65154
65930
  for (const name2 of visibleNames2(scope.dir)) {
65155
65931
  if (name2 === LEARNED_DIRECTORY)
65156
65932
  continue;
65157
- const skillDir = join98(scope.dir, name2);
65933
+ const skillDir = join100(scope.dir, name2);
65158
65934
  if (entryKind2(skillDir) !== "directory")
65159
65935
  continue;
65160
- const absolute = join98(skillDir, SKILL_FILE);
65936
+ const absolute = join100(skillDir, SKILL_FILE);
65161
65937
  if (entryKind2(absolute) !== "file")
65162
65938
  continue;
65163
65939
  const file = readSelected(absolute, `${name2}/${SKILL_FILE}`, visibleNames2(skillDir));
@@ -65174,14 +65950,14 @@ var init_select5 = __esm(() => {
65174
65950
  init_layout();
65175
65951
  init_classify2();
65176
65952
  init_report3();
65177
- DEMOTED_PATH = join98(".rafa", "demoted");
65178
- INSTINCTS_PATH = join98(".rafa", "instincts");
65953
+ DEMOTED_PATH = join100(".rafa", "demoted");
65954
+ INSTINCTS_PATH = join100(".rafa", "instincts");
65179
65955
  });
65180
65956
 
65181
65957
  // src/commands/skill/backfill.ts
65182
- import { statSync as statSync30 } from "fs";
65183
- import { join as join99, resolve as resolve41 } from "path";
65184
- function readSwitch3(name2, value) {
65958
+ import { statSync as statSync31 } from "fs";
65959
+ import { join as join101, resolve as resolve42 } from "path";
65960
+ function readSwitch4(name2, value) {
65185
65961
  if (value === undefined || value === false || value === "false")
65186
65962
  return false;
65187
65963
  if (value === true || value === "true")
@@ -65190,8 +65966,8 @@ function readSwitch3(name2, value) {
65190
65966
  Usage: ${BACKFILL_USAGE}`);
65191
65967
  }
65192
65968
  function readMode(flags) {
65193
- const propose = readSwitch3("propose", flags["propose"]);
65194
- const apply = readSwitch3("apply", flags["apply"]);
65969
+ const propose = readSwitch4("propose", flags["propose"]);
65970
+ const apply = readSwitch4("apply", flags["apply"]);
65195
65971
  if (propose && apply) {
65196
65972
  throw new CommandExit(1, "\u274C --propose asks the sessions and --apply writes their reviewed answers, so a run is one or the" + ` other
65197
65973
  Usage: ${BACKFILL_USAGE}`);
@@ -65210,7 +65986,7 @@ Usage: ${BACKFILL_USAGE}`);
65210
65986
  throw new CommandExit(1, `\u274C Expected one directory, and read "${extra}" as a second
65211
65987
  Usage: ${BACKFILL_USAGE}`);
65212
65988
  }
65213
- return resolve41(cwd(), dir);
65989
+ return resolve42(cwd(), dir);
65214
65990
  }
65215
65991
  function readConsumerRoot(value, cwd) {
65216
65992
  if (value === undefined || value === false)
@@ -65219,11 +65995,11 @@ function readConsumerRoot(value, cwd) {
65219
65995
  throw new CommandExit(1, `\u274C --project needs a path, and the line gave it none
65220
65996
  Usage: ${BACKFILL_USAGE}`);
65221
65997
  }
65222
- return resolve41(cwd(), value);
65998
+ return resolve42(cwd(), value);
65223
65999
  }
65224
66000
  function isDirectory4(path9) {
65225
66001
  try {
65226
- return statSync30(path9).isDirectory();
66002
+ return statSync31(path9).isDirectory();
65227
66003
  } catch {
65228
66004
  return false;
65229
66005
  }
@@ -65272,7 +66048,7 @@ function actionLines(actions) {
65272
66048
  function checkOptions(context, projectRoot3) {
65273
66049
  return { projectRoot: projectRoot3, pathDirs: pathDirectories(context.env["PATH"]) };
65274
66050
  }
65275
- function report4(context, result2, lines2) {
66051
+ function report5(context, result2, lines2) {
65276
66052
  if (context.outputMode === "json") {
65277
66053
  context.output.result(result2);
65278
66054
  return;
@@ -65293,7 +66069,7 @@ function runPlan(context, scope, backfillDir, projectRoot3) {
65293
66069
  counts: plan2.counts,
65294
66070
  candidates
65295
66071
  };
65296
- report4(context, result2, [
66072
+ report5(context, result2, [
65297
66073
  `${scope.scope} scope: ${scope.dir}`,
65298
66074
  ...actionLines(actions),
65299
66075
  derivationLine(plan2),
@@ -65327,7 +66103,7 @@ async function runPropose(context, scope, backfillDir, seams) {
65327
66103
  candidates: pass.candidates,
65328
66104
  files
65329
66105
  };
65330
- report4(context, result2, [
66106
+ report5(context, result2, [
65331
66107
  `${scope.scope} scope: ${scope.dir}`,
65332
66108
  `${String(pass.candidates)} file(s) to ask about, in ${String(files.length)} batch(es)`,
65333
66109
  ...files.map((file) => `\u2705 ${file.path}: batch ${String(file.batch)}, session exit ${String(file.exitCode)},` + ` ${String(file.rows)} row(s), ${String(file.unanswered)} unanswered`),
@@ -65347,7 +66123,7 @@ function reviewedProposals(dir) {
65347
66123
  return read2.flatMap((entry) => entry.result.file === null ? [] : [entry.result.file]);
65348
66124
  }
65349
66125
  function rowPaths(files, root) {
65350
- return files.flatMap((file) => file.status !== REVIEWED_FILE ? [] : file.rows.filter((row3) => row3.status === ANSWERED_ROW).map((row3) => join99(root, ...row3.path.split("/"))));
66126
+ return files.flatMap((file) => file.status !== REVIEWED_FILE ? [] : file.rows.filter((row3) => row3.status === ANSWERED_ROW).map((row3) => join101(root, ...row3.path.split("/"))));
65351
66127
  }
65352
66128
  function runApply(context, scope, backfillDir, projectRoot3) {
65353
66129
  const options = checkOptions(context, projectRoot3);
@@ -65382,18 +66158,18 @@ function runApply(context, scope, backfillDir, projectRoot3) {
65382
66158
  derivationLine(derived),
65383
66159
  `${String(copied)} file(s) copied under ${backupDir} before they were rewritten`
65384
66160
  ];
65385
- const refused6 = proposals.counts.refused + derived.counts.refused;
65386
- if (refused6 > 0)
65387
- throw new CommandExit(Math.min(refused6, REFUSED_CAP), lines2.join(`
66161
+ const refused7 = proposals.counts.refused + derived.counts.refused;
66162
+ if (refused7 > 0)
66163
+ throw new CommandExit(Math.min(refused7, REFUSED_CAP), lines2.join(`
65388
66164
  `));
65389
- report4(context, result2, lines2);
66165
+ report5(context, result2, lines2);
65390
66166
  }
65391
66167
  async function runBackfill(context, seams) {
65392
66168
  const mode = readMode(context.flags);
65393
66169
  const dir = readBackfillDirectory(context.args, seams.cwd);
65394
66170
  const projectRoot3 = readConsumerRoot(context.flags["project"], seams.cwd);
65395
66171
  const scope = backfillScopeOf(dir, projectOf13(context).home);
65396
- const backfillDir = join99(scope.base, BACKFILL_PATH);
66172
+ const backfillDir = join101(scope.base, BACKFILL_PATH);
65397
66173
  if (mode === "propose") {
65398
66174
  await runPropose(context, scope, backfillDir, seams);
65399
66175
  return;
@@ -65526,9 +66302,9 @@ var init_check3 = __esm(() => {
65526
66302
  });
65527
66303
 
65528
66304
  // src/demote/apply.ts
65529
- import { copyFileSync as copyFileSync7, existsSync as existsSync63, mkdirSync as mkdirSync30, mkdtempSync as mkdtempSync4, readdirSync as readdirSync23, readFileSync as readFileSync82, renameSync as renameSync12, rmSync as rmSync14, statSync as statSync31, unlinkSync as unlinkSync2, writeFileSync as writeFileSync32 } from "fs";
66305
+ import { copyFileSync as copyFileSync7, existsSync as existsSync64, mkdirSync as mkdirSync31, mkdtempSync as mkdtempSync4, readdirSync as readdirSync24, readFileSync as readFileSync82, renameSync as renameSync12, rmSync as rmSync14, statSync as statSync32, unlinkSync as unlinkSync2, writeFileSync as writeFileSync33 } from "fs";
65530
66306
  import { tmpdir as tmpdir6 } from "os";
65531
- import { dirname as dirname38, join as join100 } from "path";
66307
+ import { dirname as dirname38, join as join102 } from "path";
65532
66308
  function isLearnedPath(path9) {
65533
66309
  const segments = path9.split("/");
65534
66310
  return segments.length === 2 && segments[0] === LEARNED_DIRECTORY && (segments[1] ?? "").endsWith(MARKDOWN_EXTENSION);
@@ -65538,18 +66314,18 @@ function learnedSkillName(path9) {
65538
66314
  return base.slice(0, -MARKDOWN_EXTENSION.length);
65539
66315
  }
65540
66316
  function sourcePath(scope, path9) {
65541
- return join100(scope.dir, ...path9.split("/"));
66317
+ return join102(scope.dir, ...path9.split("/"));
65542
66318
  }
65543
66319
  function demotedPath(scope, path9) {
65544
- return join100(scope.demotedDir, ...path9.split("/"));
66320
+ return join102(scope.demotedDir, ...path9.split("/"));
65545
66321
  }
65546
66322
  function promotedPath(scope, path9) {
65547
- return join100(scope.dir, learnedSkillName(path9), SKILL_FILE);
66323
+ return join102(scope.dir, learnedSkillName(path9), SKILL_FILE);
65548
66324
  }
65549
66325
  function takenInstinctIds(dir) {
65550
66326
  let names2;
65551
66327
  try {
65552
- names2 = readdirSync23(dir);
66328
+ names2 = readdirSync24(dir);
65553
66329
  } catch {
65554
66330
  return [];
65555
66331
  }
@@ -65557,7 +66333,7 @@ function takenInstinctIds(dir) {
65557
66333
  }
65558
66334
  function readText9(path9) {
65559
66335
  try {
65560
- return statSync31(path9).isFile() ? readFileSync82(path9, "utf8") : null;
66336
+ return statSync32(path9).isFile() ? readFileSync82(path9, "utf8") : null;
65561
66337
  } catch {
65562
66338
  return null;
65563
66339
  }
@@ -65585,7 +66361,7 @@ function planKept(scope, row3, file) {
65585
66361
  return inert3(row3.path, "procedure", "kept", "procedure-shaped: left where it is, for the backfill");
65586
66362
  }
65587
66363
  const to = promotedPath(scope, row3.path);
65588
- if (existsSync63(to) || existsSync63(dirname38(to))) {
66364
+ if (existsSync64(to) || existsSync64(dirname38(to))) {
65589
66365
  return inert3(row3.path, "procedure", "refused", `kept as a skill, but ${to} is already there`);
65590
66366
  }
65591
66367
  return {
@@ -65599,10 +66375,10 @@ function planKept(scope, row3, file) {
65599
66375
  };
65600
66376
  }
65601
66377
  function checkCandidate(dir, id3, text2, pathDirs) {
65602
- const path9 = join100(dir, `${id3}${MARKDOWN_EXTENSION}`);
65603
- writeFileSync32(path9, text2, "utf8");
65604
- const report5 = checkFile(path9, "instinct", { projectRoot: null, pathDirs });
65605
- return failureLines2(report5.issues);
66378
+ const path9 = join102(dir, `${id3}${MARKDOWN_EXTENSION}`);
66379
+ writeFileSync33(path9, text2, "utf8");
66380
+ const report6 = checkFile(path9, "instinct", { projectRoot: null, pathDirs });
66381
+ return failureLines2(report6.issues);
65606
66382
  }
65607
66383
  function planDemoted(scope, row3, file, context) {
65608
66384
  const conversion = convertToInstinct(file, {
@@ -65620,11 +66396,11 @@ function planDemoted(scope, row3, file, context) {
65620
66396
  if (failures.length > 0) {
65621
66397
  return inert3(row3.path, "observation", "refused", `the record ${id3} fails the checker \u2014 ${failures.join("; ")}`);
65622
66398
  }
65623
- const destination = join100(scope.instinctsDir, `${id3}${MARKDOWN_EXTENSION}`);
66399
+ const destination = join102(scope.instinctsDir, `${id3}${MARKDOWN_EXTENSION}`);
65624
66400
  const kept = demotedPath(scope, row3.path);
65625
- if (existsSync63(destination))
66401
+ if (existsSync64(destination))
65626
66402
  return inert3(row3.path, "observation", "refused", `${destination} is already there`);
65627
- if (existsSync63(kept))
66403
+ if (existsSync64(kept))
65628
66404
  return inert3(row3.path, "observation", "refused", `${kept} is already there`);
65629
66405
  context.taken.push(id3);
65630
66406
  return {
@@ -65656,7 +66432,7 @@ function planRow(scope, row3, context) {
65656
66432
  entries: undefined,
65657
66433
  absolute,
65658
66434
  hash,
65659
- mtime: statSync31(absolute).mtime.toISOString()
66435
+ mtime: statSync32(absolute).mtime.toISOString()
65660
66436
  };
65661
66437
  return verdict === "procedure" ? planKept(scope, row3, file) : planDemoted(scope, row3, file, context);
65662
66438
  }
@@ -65666,18 +66442,18 @@ function countActions(actions) {
65666
66442
  counts[action.kind] += 1;
65667
66443
  return counts;
65668
66444
  }
65669
- function planDemotion(report5, scope, seams = DEFAULT_APPLY_SEAMS) {
65670
- const temp = mkdtempSync4(join100(tmpdir6(), "rafa-demote-"));
66445
+ function planDemotion(report6, scope, seams = DEFAULT_APPLY_SEAMS) {
66446
+ const temp = mkdtempSync4(join102(tmpdir6(), "rafa-demote-"));
65671
66447
  const taken = [...takenInstinctIds(scope.instinctsDir)];
65672
66448
  try {
65673
- const actions = report5.rows.map((row3) => planRow(scope, row3, { temp, taken, seams }));
66449
+ const actions = report6.rows.map((row3) => planRow(scope, row3, { temp, taken, seams }));
65674
66450
  return { scope, actions, counts: countActions(actions) };
65675
66451
  } finally {
65676
66452
  rmSync14(temp, { recursive: true, force: true });
65677
66453
  }
65678
66454
  }
65679
66455
  function moveFile(move3) {
65680
- mkdirSync30(dirname38(move3.to), { recursive: true });
66456
+ mkdirSync31(dirname38(move3.to), { recursive: true });
65681
66457
  try {
65682
66458
  renameSync12(move3.from, move3.to);
65683
66459
  } catch {
@@ -65688,8 +66464,8 @@ function moveFile(move3) {
65688
66464
  function applyDemotion(plan2) {
65689
66465
  for (const action of plan2.actions) {
65690
66466
  if (action.write !== null) {
65691
- mkdirSync30(dirname38(action.write.path), { recursive: true });
65692
- writeFileSync32(action.write.path, action.write.text, "utf8");
66467
+ mkdirSync31(dirname38(action.write.path), { recursive: true });
66468
+ writeFileSync33(action.write.path, action.write.text, "utf8");
65693
66469
  }
65694
66470
  if (action.move !== null)
65695
66471
  moveFile(action.move);
@@ -65753,8 +66529,8 @@ var init_draft = __esm(() => {
65753
66529
  });
65754
66530
 
65755
66531
  // src/commands/skill/demote.ts
65756
- import { existsSync as existsSync64, mkdirSync as mkdirSync31, readFileSync as readFileSync83, statSync as statSync32, writeFileSync as writeFileSync33 } from "fs";
65757
- import { dirname as dirname39, resolve as resolve42 } from "path";
66532
+ import { existsSync as existsSync65, mkdirSync as mkdirSync32, readFileSync as readFileSync83, statSync as statSync33, writeFileSync as writeFileSync34 } from "fs";
66533
+ import { dirname as dirname39, resolve as resolve43 } from "path";
65758
66534
  function readApply(value) {
65759
66535
  if (value === undefined || value === false || value === "false")
65760
66536
  return false;
@@ -65773,11 +66549,11 @@ Usage: ${DEMOTE_USAGE}`);
65773
66549
  throw new CommandExit(1, `\u274C Expected one directory, and read "${extra}" as a second
65774
66550
  Usage: ${DEMOTE_USAGE}`);
65775
66551
  }
65776
- return resolve42(cwd(), dir);
66552
+ return resolve43(cwd(), dir);
65777
66553
  }
65778
66554
  function isDirectory5(path9) {
65779
66555
  try {
65780
- return statSync32(path9).isDirectory();
66556
+ return statSync33(path9).isDirectory();
65781
66557
  } catch {
65782
66558
  return false;
65783
66559
  }
@@ -65799,13 +66575,13 @@ function projectOf14(context) {
65799
66575
  throw new Error("rafa skill demote runs inside a project, and was handed none");
65800
66576
  return context.project;
65801
66577
  }
65802
- function verdictLine2(report5) {
65803
- const counts = countVerdicts(report5.rows);
66578
+ function verdictLine2(report6) {
66579
+ const counts = countVerdicts(report6.rows);
65804
66580
  const parts = DEMOTION_VERDICTS.map((verdict) => `${String(counts[verdict])} ${verdict}`);
65805
- return `${String(report5.rows.length)} file(s): ${parts.join(", ")}`;
66581
+ return `${String(report6.rows.length)} file(s): ${parts.join(", ")}`;
65806
66582
  }
65807
66583
  function previousReport(path9, context) {
65808
- if (!existsSync64(path9))
66584
+ if (!existsSync65(path9))
65809
66585
  return null;
65810
66586
  const parsed = parseDemotionReport(readFileSync83(path9, "utf8"));
65811
66587
  if (parsed.report !== null)
@@ -65815,17 +66591,17 @@ function previousReport(path9, context) {
65815
66591
  }
65816
66592
  function runWrite(context, scope) {
65817
66593
  const files = selectSources(scope);
65818
- const report5 = buildDemotionReport(files, previousReport(scope.reportPath, context));
65819
- mkdirSync31(dirname39(scope.reportPath), { recursive: true });
65820
- writeFileSync33(scope.reportPath, writeDemotionReport(report5), "utf8");
66594
+ const report6 = buildDemotionReport(files, previousReport(scope.reportPath, context));
66595
+ mkdirSync32(dirname39(scope.reportPath), { recursive: true });
66596
+ writeFileSync34(scope.reportPath, writeDemotionReport(report6), "utf8");
65821
66597
  const result2 = {
65822
66598
  applied: false,
65823
66599
  scope: scope.scope,
65824
66600
  dir: scope.dir,
65825
66601
  reportPath: scope.reportPath,
65826
- status: report5.status,
65827
- files: report5.rows.length,
65828
- counts: countVerdicts(report5.rows)
66602
+ status: report6.status,
66603
+ files: report6.rows.length,
66604
+ counts: countVerdicts(report6.rows)
65829
66605
  };
65830
66606
  if (context.outputMode === "json") {
65831
66607
  context.output.result(result2);
@@ -65833,14 +66609,14 @@ function runWrite(context, scope) {
65833
66609
  }
65834
66610
  for (const line of [
65835
66611
  `${scope.scope} scope: ${scope.dir}`,
65836
- verdictLine2(report5),
65837
- `Report written to ${scope.reportPath} (status: ${report5.status}); nothing was moved`,
66612
+ verdictLine2(report6),
66613
+ `Report written to ${scope.reportPath} (status: ${report6.status}); nothing was moved`,
65838
66614
  `Review each row, then mark the report ${REVIEWED_STATUS} and re-run with --apply`
65839
66615
  ])
65840
66616
  context.output.info(line);
65841
66617
  }
65842
66618
  function reviewedReport(scope) {
65843
- if (!existsSync64(scope.reportPath)) {
66619
+ if (!existsSync65(scope.reportPath)) {
65844
66620
  throw new CommandExit(1, `\u274C No report at ${scope.reportPath}; run "rafa skill demote ${scope.dir}" first, then review it`);
65845
66621
  }
65846
66622
  const parsed = parseDemotionReport(readFileSync83(scope.reportPath, "utf8"));
@@ -65870,8 +66646,8 @@ function renderApply(result2) {
65870
66646
  return [...rows2, `${String(result2.actions.length)} row(s) of ${result2.reportPath}: ${parts.join(", ")}`];
65871
66647
  }
65872
66648
  function runApply2(context, scope) {
65873
- const report5 = reviewedReport(scope);
65874
- const plan2 = planDemotion(report5, scope, {
66649
+ const report6 = reviewedReport(scope);
66650
+ const plan2 = planDemotion(report6, scope, {
65875
66651
  now: () => new Date().toISOString(),
65876
66652
  pathDirs: pathDirectories(context.env["PATH"])
65877
66653
  });
@@ -66244,8 +67020,8 @@ var init_render = __esm(() => {
66244
67020
 
66245
67021
  // src/status/blocked-count.ts
66246
67022
  async function labelsCount(options) {
66247
- const report5 = await options.blockedIssues(options.gh);
66248
- return report5.problem === null ? { count: report5.readings.length, notes: [] } : { count: null, notes: [`the issues labelled ${SPEC_BLOCKED_LABEL} could not be read: ${report5.problem}`] };
67023
+ const report6 = await options.blockedIssues(options.gh);
67024
+ return report6.problem === null ? { count: report6.readings.length, notes: [] } : { count: null, notes: [`the issues labelled ${SPEC_BLOCKED_LABEL} could not be read: ${report6.problem}`] };
66249
67025
  }
66250
67026
  async function nativeCount(relations, listing3) {
66251
67027
  try {
@@ -66379,7 +67155,7 @@ var init_waiting = __esm(() => {
66379
67155
  });
66380
67156
 
66381
67157
  // src/status/sections.ts
66382
- import { existsSync as existsSync65 } from "fs";
67158
+ import { existsSync as existsSync66 } from "fs";
66383
67159
  function isIdleWorktree(row3) {
66384
67160
  return !row3.blockers.some((blocker) => blocker.kind === "recent");
66385
67161
  }
@@ -66425,9 +67201,9 @@ function boundedRunner(open, root, deadline) {
66425
67201
  }
66426
67202
  async function networkSection(what, deadline, work) {
66427
67203
  let timer;
66428
- const expired = new Promise((resolve43) => {
67204
+ const expired = new Promise((resolve44) => {
66429
67205
  timer = setTimeout(() => {
66430
- resolve43(unread4(`the ${what} was not read within the ${String(deadline.ms)}ms network deadline`));
67206
+ resolve44(unread4(`the ${what} was not read within the ${String(deadline.ms)}ms network deadline`));
66431
67207
  }, Math.max(0, deadline.left()));
66432
67208
  });
66433
67209
  try {
@@ -66517,7 +67293,7 @@ function nextOfPlace(current, epic, walk) {
66517
67293
  return epic?.members.some((member) => member.number === line.issue) === true ? line.issue : null;
66518
67294
  }
66519
67295
  function hasBoards(root, listing3) {
66520
- if (existsSync65(positionFilePath(root)))
67296
+ if (existsSync66(positionFilePath(root)))
66521
67297
  return true;
66522
67298
  return listing3.some((issue5) => issue5.state === "OPEN" && issue5.labels.includes(ROADMAP_LABEL));
66523
67299
  }
@@ -66755,10 +67531,10 @@ var init_board = __esm(() => {
66755
67531
  });
66756
67532
 
66757
67533
  // src/project/lock.ts
66758
- import { existsSync as existsSync66, lstatSync as lstatSync3, readFileSync as readFileSync84, writeFileSync as writeFileSync34 } from "fs";
66759
- import { join as join101 } from "path";
67534
+ import { existsSync as existsSync67, lstatSync as lstatSync3, readFileSync as readFileSync84, writeFileSync as writeFileSync35 } from "fs";
67535
+ import { join as join103 } from "path";
66760
67536
  function lockPath(root) {
66761
- return join101(root, LOCK_FILE);
67537
+ return join103(root, LOCK_FILE);
66762
67538
  }
66763
67539
  function projectLockText(version2) {
66764
67540
  const lock = { lockfileVersion: LOCKFILE_VERSION, rafa: version2 };
@@ -66791,7 +67567,7 @@ function lockOf(value) {
66791
67567
  }
66792
67568
  function readProjectLock(root) {
66793
67569
  const path9 = lockPath(root);
66794
- if (!existsSync66(path9)) {
67570
+ if (!existsSync67(path9)) {
66795
67571
  if (isLink(path9))
66796
67572
  refuse17("is a link to nothing");
66797
67573
  return null;
@@ -66811,7 +67587,7 @@ function readProjectLock(root) {
66811
67587
  return lockOf(value);
66812
67588
  }
66813
67589
  function writeProjectLock(root, version2) {
66814
- writeFileSync34(lockPath(root), projectLockText(version2), "utf8");
67590
+ writeFileSync35(lockPath(root), projectLockText(version2), "utf8");
66815
67591
  }
66816
67592
  var LOCK_FILE = "rafa.lock", LOCKFILE_VERSION = 1, ProjectLockError;
66817
67593
  var init_lock = __esm(() => {
@@ -66823,10 +67599,10 @@ var init_lock = __esm(() => {
66823
67599
  });
66824
67600
 
66825
67601
  // src/project/update-current.ts
66826
- import { existsSync as existsSync67 } from "fs";
66827
- import { join as join102 } from "path";
67602
+ import { existsSync as existsSync68 } from "fs";
67603
+ import { join as join104 } from "path";
66828
67604
  function missingFolders(root) {
66829
- return PROJECT_TREE.map((name2) => join102(SCOPE_DIR, name2)).filter((relative24) => !existsSync67(join102(root, relative24)));
67605
+ return PROJECT_TREE.map((name2) => join104(SCOPE_DIR, name2)).filter((relative24) => !existsSync68(join104(root, relative24)));
66830
67606
  }
66831
67607
  async function readBoard3(access) {
66832
67608
  if (access.gh === null)
@@ -66841,10 +67617,10 @@ async function readBoard3(access) {
66841
67617
  function lockChangeOf(range) {
66842
67618
  if (range.kind === "adopt")
66843
67619
  return "created";
66844
- return range.kind === "patch" ? "updated" : "unchanged";
67620
+ return range.kind === "same" ? "unchanged" : "updated";
66845
67621
  }
66846
67622
  function fromOf(range) {
66847
- if (range.kind === "patch")
67623
+ if (range.kind === "patch" || range.kind === "minor")
66848
67624
  return range.from;
66849
67625
  if (range.kind === "same")
66850
67626
  return range.to;
@@ -66868,7 +67644,7 @@ function planChanges(plan2) {
66868
67644
  return plan2.folders.length > 0 || labels > 0 || plan2.deprecations.length > 0 || plan2.lock !== "unchanged";
66869
67645
  }
66870
67646
  function foldersNowThere(plan2) {
66871
- return plan2.folders.filter((relative24) => existsSync67(join102(plan2.root, relative24)));
67647
+ return plan2.folders.filter((relative24) => existsSync68(join104(plan2.root, relative24)));
66872
67648
  }
66873
67649
  function writeFolders(plan2) {
66874
67650
  if (plan2.folders.length === 0)
@@ -66915,27 +67691,29 @@ var init_update_current = __esm(() => {
66915
67691
  });
66916
67692
 
66917
67693
  // src/project/update-range.ts
66918
- function refused6(reason, message) {
67694
+ function refused7(reason, message) {
66919
67695
  return { kind: "refused", reason, message };
66920
67696
  }
66921
67697
  function readCurrentRange(recorded, installed) {
66922
67698
  const to = parseSemanticVersion(installed);
66923
67699
  if (to === null)
66924
- return refused6("unreadable", `the installed rafa answers "${installed}", which is no version`);
67700
+ return refused7("unreadable", `the installed rafa answers "${installed}", which is no version`);
66925
67701
  if (recorded === null)
66926
67702
  return { kind: "adopt", to: installed };
66927
67703
  const from = parseSemanticVersion(recorded);
66928
67704
  if (from === null)
66929
- return refused6("unreadable", `the project records rafa "${recorded}", which is no version`);
67705
+ return refused7("unreadable", `the project records rafa "${recorded}", which is no version`);
66930
67706
  const order = compareVersions(to, from);
66931
67707
  if (order < 0) {
66932
- return refused6("older", `the installed rafa ${installed} is older than the project's ${recorded}, and no downgrade runs before 1.0.0`);
67708
+ return refused7("older", `the installed rafa ${installed} is older than the project's ${recorded}, and no downgrade runs before 1.0.0`);
66933
67709
  }
66934
67710
  if (to.major !== from.major) {
66935
- return refused6("newer-major", `the installed rafa ${installed} is a newer major than the project's ${recorded}; that move is rafa update latest`);
67711
+ return refused7("newer-major", `the installed rafa ${installed} is a newer major than the project's ${recorded}; that move is rafa update latest`);
66936
67712
  }
67713
+ if (to.minor !== from.minor && to.major === 0)
67714
+ return { kind: "minor", from: recorded, to: installed };
66937
67715
  if (to.minor !== from.minor) {
66938
- return refused6("newer-minor", `the installed rafa ${installed} is a newer minor than the project's ${recorded}; that move is rafa update next`);
67716
+ return refused7("newer-minor", `the installed rafa ${installed} is a newer minor than the project's ${recorded}; that move is rafa update next`);
66939
67717
  }
66940
67718
  return order === 0 ? { kind: "same", to: installed } : { kind: "patch", from: recorded, to: installed };
66941
67719
  }
@@ -66952,7 +67730,7 @@ function checkoutLockRoot(root, dir) {
66952
67730
  const top = run.stdout.toString().trim();
66953
67731
  return top !== "" && mainCheckoutOf(top) === root ? top : root;
66954
67732
  }
66955
- function readSwitch4(context, name2) {
67733
+ function readSwitch5(context, name2) {
66956
67734
  const value = context.flags[name2];
66957
67735
  if (value === undefined || value === false || value === "false")
66958
67736
  return false;
@@ -67082,10 +67860,10 @@ async function applyAndReport(plan2, gh, context) {
67082
67860
  return applied;
67083
67861
  }
67084
67862
  function refusedLabels(applied) {
67085
- const refused7 = applied.labels.filter((part) => part.outcome === "refused");
67086
- if (refused7.length === 0)
67863
+ const refused8 = applied.labels.filter((part) => part.outcome === "refused");
67864
+ if (refused8.length === 0)
67087
67865
  return null;
67088
- return new CommandExit(1, `${String(refused7.length)} board label(s) were not created; everything else was applied.` + " Run rafa update current again once gh can create them.");
67866
+ return new CommandExit(1, `${String(refused8.length)} board label(s) were not created; everything else was applied.` + " Run rafa update current again once gh can create them.");
67089
67867
  }
67090
67868
  function stopLine2(plan2, dryRun) {
67091
67869
  if (!planChanges(plan2))
@@ -67096,8 +67874,8 @@ function stopLine2(plan2, dryRun) {
67096
67874
  }
67097
67875
  async function runUpdateCurrent(context, seams) {
67098
67876
  expectNoArgument(context.args, USAGE48);
67099
- const dryRun = readSwitch4(context, "dry-run");
67100
- const yes = readSwitch4(context, "yes");
67877
+ const dryRun = readSwitch5(context, "dry-run");
67878
+ const yes = readSwitch5(context, "yes");
67101
67879
  const project = projectOf17(context);
67102
67880
  const lockRoot = seams.lockRootOf(project.root);
67103
67881
  const range = readRange2(lockRoot, seams.installed);
@@ -67133,8 +67911,8 @@ function createUpdateCurrentCommand(seams = DEFAULT_UPDATE_CURRENT_SEAMS) {
67133
67911
  name: "update current",
67134
67912
  subject: "update",
67135
67913
  action: "current",
67136
- summary: "bring this project to the installed rafa, within its patch range",
67137
- description: "Brings the project to the installed rafa when the installed version shares the major and" + ` minor of the version \`${LOCK_FILE}\` records at the project root, and is the same patch or newer.` + " A project with no lock is adopted at the installed version. It creates the missing `.rafa/` folders" + " and the missing board labels on a GitHub board, runs the deprecations step (none registered yet)," + ` and writes \`${LOCK_FILE}\`. It prints every change first; \`--dry-run\` stops there. Otherwise it` + " warns that the changes cannot be rolled back and asks once, `--yes` answering; with no terminal and" + " no `--yes` it refuses with exit code 1. A newer minor or major, or an installed rafa older than the" + " lock, refuses with exit code 1, and so does a board label `gh` would not create, once everything" + " else is applied. A lock or config that cannot be read, or a `.rafa/` path holding something else," + " exits 2 before anything is asked; a folder or the lock that cannot be written exits 2 naming the" + " step and what was already written. In a linked worktree the lock is the worktree's, on its branch." + " With `--output=json` the plan and what was applied are the data of the terminal result event.",
67914
+ summary: "bring this project to the installed rafa: a newer patch, or any newer minor below 1.0.0",
67915
+ description: "Brings the project to the installed rafa when the installed version shares the major and" + ` minor of the version \`${LOCK_FILE}\` records at the project root, and is the same patch or newer;` + " below 1.0.0 it also crosses newer minors, as `0.34.1` to `0.36.0`." + " A project with no lock is adopted at the installed version. It creates the missing `.rafa/` folders" + " and the missing board labels on a GitHub board, runs the deprecations step (none registered yet)," + ` and writes \`${LOCK_FILE}\`. It prints every change first; \`--dry-run\` stops there. Otherwise it` + " warns that the changes cannot be rolled back and asks once, `--yes` answering; with no terminal and" + " no `--yes` it refuses with exit code 1. A newer minor from 1.0.0 on, a newer major, or an installed rafa older than the" + " lock, refuses with exit code 1, and so does a board label `gh` would not create, once everything" + " else is applied. A lock or config that cannot be read, or a `.rafa/` path holding something else," + " exits 2 before anything is asked; a folder or the lock that cannot be written exits 2 naming the" + " step and what was already written. In a linked worktree the lock is the worktree's, on its branch." + " With `--output=json` the plan and what was applied are the data of the terminal result event.",
67138
67916
  args: [],
67139
67917
  flags: [
67140
67918
  { name: "dry-run", description: "Print every change and write nothing.", type: "boolean" },
@@ -67267,6 +68045,7 @@ var init_commands2 = __esm(() => {
67267
68045
  init_check2();
67268
68046
  init_comment();
67269
68047
  init_create();
68048
+ init_edit();
67270
68049
  init_list3();
67271
68050
  init_move5();
67272
68051
  init_ready();
@@ -67319,7 +68098,7 @@ var init_commands2 = __esm(() => {
67319
68098
  CORE_SUBJECTS = Object.freeze([
67320
68099
  { name: "plan", summary: "create plans from specs; list, show and validate them; read their risk and needs" },
67321
68100
  { name: "loop", summary: "start a plan; stop, pause, resume, show and list its sessions" },
67322
- { name: "issue", summary: "the tracker: list, show, create, comment on and move issues; mark one ready, unblock it and check its references" },
68101
+ { name: "issue", summary: "the tracker: list, show, create, edit, comment on and move issues; mark one ready, unblock it and check its references" },
67323
68102
  { name: "pr", summary: "the pull request of a branch: one line, in full or in the browser; list, wait on, merge and triage them" },
67324
68103
  { name: "effort", summary: "collect session and commit rows; report per plan; read and repair the store: its schema, a copy for testing, migrations, and fixes; merge or import another device's store; move an NDJSON store to SQLite" },
67325
68104
  { name: "module", summary: "list the configured modules; run an action a module provides" },
@@ -67354,6 +68133,7 @@ var init_commands2 = __esm(() => {
67354
68133
  ready_default,
67355
68134
  unblock_default,
67356
68135
  check_default2,
68136
+ edit_default,
67357
68137
  current_default,
67358
68138
  show_default6,
67359
68139
  view_default,
@@ -68040,12 +68820,12 @@ init_config_sections();
68040
68820
  init_sessions();
68041
68821
  init_scope();
68042
68822
  init_sections2();
68043
- import { mkdirSync as mkdirSync32, readFileSync as readFileSync85, renameSync as renameSync13, writeFileSync as writeFileSync35 } from "fs";
68044
- import { dirname as dirname40, join as join103 } from "path";
68823
+ import { mkdirSync as mkdirSync33, readFileSync as readFileSync85, renameSync as renameSync13, writeFileSync as writeFileSync36 } from "fs";
68824
+ import { dirname as dirname40, join as join105 } from "path";
68045
68825
  var SEEN_FILE = "status-seen.json";
68046
68826
  var SEEN_VERSION = 1;
68047
68827
  function seenFilePath(root) {
68048
- return join103(scopeAt(root).dir, SEEN_FILE);
68828
+ return join105(scopeAt(root).dir, SEEN_FILE);
68049
68829
  }
68050
68830
  function readSeenSessions(root, isAlive) {
68051
68831
  const sessions = {};
@@ -68112,9 +68892,9 @@ function readSeenFile(root) {
68112
68892
  }
68113
68893
  function writeSeenFile(root, snapshot) {
68114
68894
  const file = seenFilePath(root);
68115
- mkdirSync32(dirname40(file), { recursive: true });
68895
+ mkdirSync33(dirname40(file), { recursive: true });
68116
68896
  const temporary = `${file}.${String(process.pid)}.tmp`;
68117
- writeFileSync35(temporary, `${JSON.stringify(snapshot, null, 2)}
68897
+ writeFileSync36(temporary, `${JSON.stringify(snapshot, null, 2)}
68118
68898
  `);
68119
68899
  renameSync13(temporary, file);
68120
68900
  }