@akagilnc/pi-workflow-roles 0.1.2120 → 0.1.2124

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.
@@ -15597,7 +15597,6 @@ function resolveTaishiMode(presentOptionIds) {
15597
15597
  }
15598
15598
  if (selected.size === 0) return TAISHI_DEFAULT_MODE;
15599
15599
  if (selected.has("cohort")) return "cohort";
15600
- if (selected.has("model-groups")) return "model-groups";
15601
15600
  if (selected.has("sweep")) return "sweep";
15602
15601
  if (selected.has("issue")) return "issue";
15603
15602
  return TAISHI_DEFAULT_MODE;
@@ -15653,12 +15652,6 @@ function evaluateTaishiModeOptionContract(mode, counts) {
15653
15652
  message: "usage: ak-role taishi --cohort --group-a-label <L> --group-a-issues <N[,N...]> --group-b-label <L> --group-b-issues <N[,N...]"
15654
15653
  };
15655
15654
  }
15656
- if (mode === "model-groups") {
15657
- return {
15658
- ok: false,
15659
- message: "usage: ak-role taishi --model-groups --project-root <P> [--project-root <P> ...]"
15660
- };
15661
- }
15662
15655
  return {
15663
15656
  ok: false,
15664
15657
  message: `usage: ak-role taishi ${mode} requires ${missingRequired.map((def) => def.canonical).join(" ")}`
@@ -15671,7 +15664,7 @@ function evaluateTaishiModeOptionContract(mode, counts) {
15671
15664
  if (mode === "issue") {
15672
15665
  return {
15673
15666
  ok: false,
15674
- message: "usage: ak-role taishi ((--ticket <N> | --project-root <P>) | [sweep] --attach <sweep.json> | --cohort ... | --model-groups ...)"
15667
+ message: "usage: ak-role taishi ([--ticket <N>] | [sweep] --attach <sweep.json> | --cohort ...)"
15675
15668
  };
15676
15669
  }
15677
15670
  const flags = rule.optionIds.map((id) => byId.get(id)?.canonical ?? id).join(" | ");
@@ -15850,9 +15843,7 @@ var init_option_definitions = __esm({
15850
15843
  "src/public-cli/option-definitions.ts"() {
15851
15844
  "use strict";
15852
15845
  init_cli_errors();
15853
- TAISHI_REQUIRE_ANY_OF = [
15854
- { mode: "issue", optionIds: ["ticket", "project-root"] }
15855
- ];
15846
+ TAISHI_REQUIRE_ANY_OF = [];
15856
15847
  TAISHI_DEFAULT_MODE = "issue";
15857
15848
  REJECTED_PUBLIC_SPELLINGS = [
15858
15849
  {
@@ -15870,6 +15861,22 @@ var init_option_definitions = __esm({
15870
15861
  en: "Merger input packet is assembled internally; not a public flag.",
15871
15862
  zh: "\u6821\u4E66\u90CE input packet \u7531\u5185\u90E8\u88C5\u914D\uFF0C\u4E0D\u662F\u516C\u5F00\u65D7\u6807\u3002"
15872
15863
  }
15864
+ },
15865
+ {
15866
+ owner: "taishi",
15867
+ spellings: ["--project-root"],
15868
+ reason: {
15869
+ en: "Deleted (#399). taishi no longer accepts --project-root; use bare call for whole book or --ticket N (cwd git common-dir selects the book).",
15870
+ zh: "\u5DF2\u5220\u9664\uFF08#399\uFF09\u3002taishi \u4E0D\u518D\u63A5\u53D7 --project-root\uFF1B\u88F8\u8C03\u7528=\u6574\u7C3F\uFF0C\u6216 --ticket N\uFF08cwd git common-dir \u5B9A\u7C3F\uFF09\u3002"
15871
+ }
15872
+ },
15873
+ {
15874
+ owner: "taishi",
15875
+ spellings: ["--model-groups"],
15876
+ reason: {
15877
+ en: "Public CLI face disabled (#399). Input face is being redesigned for multi-issue comparison (see follow-up ticket). Library kernel retained.",
15878
+ zh: "\u516C\u5F00 CLI \u9762\u5DF2\u505C\u7528\uFF08#399\uFF09\u3002\u8F93\u5165\u9762\u6309\u591A issue \u91CD\u8BBE\u8BA1\u4E2D\uFF08\u89C1\u540E\u7EED\u7968\uFF09\u3002\u805A\u5408\u5185\u6838\u4FDD\u7559\u3002"
15879
+ }
15873
15880
  }
15874
15881
  ];
15875
15882
  GLOBAL_OPTIONS = [
@@ -16159,23 +16166,6 @@ var init_option_definitions = __esm({
16159
16166
  zh: "\u53EF\u9009 sweep \u6A21\u5F0F\u8BCD\u5143\uFF08\u81F3\u591A\u4E00\u6B21\uFF1B\u4E0D\u5F97\u5939\u5E26\u5176\u4ED6 positional\uFF09\u3002"
16160
16167
  }
16161
16168
  },
16162
- {
16163
- id: "project-root",
16164
- owner: "taishi",
16165
- canonical: "--project-root",
16166
- aliases: [],
16167
- valueMetavar: "path",
16168
- required: false,
16169
- repeatable: true,
16170
- form: "option",
16171
- modes: ["issue", "model-groups"],
16172
- requiredInModes: ["model-groups"],
16173
- maxCountByMode: { issue: 1 },
16174
- description: {
16175
- en: "Project-root scope key. Issue: at most one (with --ticket at least one of the two). Model-groups: one or more required.",
16176
- zh: "projectRoot \u8303\u56F4\u952E\u3002issue\uFF1A\u81F3\u591A\u4E00\u4E2A\uFF08\u4E0E --ticket \u81F3\u5C11\u5C45\u5176\u4E00\uFF09\u3002model-groups\uFF1A\u4E00\u4E2A\u6216\u591A\u4E2A\u4E14\u5FC5\u586B\u3002"
16177
- }
16178
- },
16179
16169
  {
16180
16170
  id: "ticket",
16181
16171
  owner: "taishi",
@@ -16187,8 +16177,8 @@ var init_option_definitions = __esm({
16187
16177
  form: "option",
16188
16178
  modes: ["issue"],
16189
16179
  description: {
16190
- en: "Ticket/issue number for issue mode (with --project-root at least one of the two).",
16191
- zh: "issue \u6A21\u5F0F\u7684\u7968\u53F7\uFF08\u4E0E --project-root \u81F3\u5C11\u5C45\u5176\u4E00\uFF09\u3002"
16180
+ en: "Ticket/issue number; live filter by invocation.ticketNumber inside the cwd book (git common-dir). Bare call = whole book. No library-index bootstrap.",
16181
+ zh: "\u7968\u53F7\uFF1B\u5728 cwd \u5019\u7C3F\uFF08git common-dir\uFF09\u5185\u6309 invocation.ticketNumber \u73B0\u53D6\u73B0\u7B97\u3002\u88F8\u8C03\u7528=\u6574\u7C3F\u3002\u4E0D\u4F9D\u8D56 library-index \u81EA\u4E3E\u3002"
16192
16182
  }
16193
16183
  },
16194
16184
  {
@@ -16219,28 +16209,10 @@ var init_option_definitions = __esm({
16219
16209
  repeatable: false,
16220
16210
  form: "option",
16221
16211
  modes: ["cohort"],
16222
- exclusiveWith: ["model-groups"],
16223
16212
  selectsMode: "cohort",
16224
16213
  description: {
16225
- en: "Select cohort mode (mutually exclusive with --model-groups).",
16226
- zh: "\u9009\u62E9 cohort \u6A21\u5F0F\uFF08\u4E0E --model-groups \u4E92\u65A5\uFF09\u3002"
16227
- }
16228
- },
16229
- {
16230
- id: "model-groups",
16231
- owner: "taishi",
16232
- canonical: "--model-groups",
16233
- aliases: [],
16234
- valueMetavar: null,
16235
- required: false,
16236
- repeatable: false,
16237
- form: "option",
16238
- modes: ["model-groups"],
16239
- exclusiveWith: ["cohort"],
16240
- selectsMode: "model-groups",
16241
- description: {
16242
- en: "Select model-groups mode (mutually exclusive with --cohort).",
16243
- zh: "\u9009\u62E9 model-groups \u6A21\u5F0F\uFF08\u4E0E --cohort \u4E92\u65A5\uFF09\u3002"
16214
+ en: "Select cohort mode.",
16215
+ zh: "\u9009\u62E9 cohort \u6A21\u5F0F\u3002"
16244
16216
  }
16245
16217
  },
16246
16218
  {
@@ -16395,14 +16367,14 @@ var init_option_definitions = __esm({
16395
16367
  },
16396
16368
  taishi: {
16397
16369
  command: "taishi",
16398
- summary: "Deterministic analysis seat (issue / sweep / cohort / model-groups).",
16370
+ summary: "Deterministic analysis seat (issue / sweep / cohort).",
16399
16371
  usage: [
16400
- "ak-role taishi (--ticket <N> | --project-root <P>) [options]",
16372
+ "ak-role taishi [--ticket <N>]",
16401
16373
  "ak-role taishi [sweep] --attach <path>",
16402
- "ak-role taishi --cohort --group-a-label <L> --group-a-issues <N[,N...]> --group-b-label <L> --group-b-issues <N[,N...]>",
16403
- "ak-role taishi --model-groups --project-root <P> [--project-root <P> ...]"
16374
+ "ak-role taishi --cohort --group-a-label <L> --group-a-issues <N[,N...]> --group-b-label <L> --group-b-issues <N[,N...]>"
16404
16375
  ],
16405
16376
  examples: [
16377
+ "ak-role taishi",
16406
16378
  "ak-role taishi --ticket 125",
16407
16379
  "ak-role taishi sweep --attach ./sweep.json"
16408
16380
  ]
@@ -17877,13 +17849,6 @@ function parseTaishiArgv(args) {
17877
17849
  pushValue("ticket", taken.value);
17878
17850
  continue;
17879
17851
  }
17880
- if (taken.def.id === "project-root") {
17881
- pushValue(
17882
- "project-root",
17883
- requireOptionPath(taken.def.canonical, taken.value)
17884
- );
17885
- continue;
17886
- }
17887
17852
  if (taken.def.id === "attach") {
17888
17853
  pushValue(
17889
17854
  "attach",
@@ -17912,6 +17877,19 @@ function parseTaishiArgv(args) {
17912
17877
  throw new CliUsageError(`unknown taishi option: ${taken.def.canonical}`);
17913
17878
  }
17914
17879
  const token = tokens.shift();
17880
+ if (isRejectedPublicSpelling("taishi", token)) {
17881
+ if (token === "--project-root" || token.startsWith("--project-root=")) {
17882
+ throw new CliUsageError(
17883
+ "taishi no longer accepts --project-root (deleted); use bare call for whole book or --ticket N (cwd git common-dir selects the book)"
17884
+ );
17885
+ }
17886
+ if (token === "--model-groups" || token.startsWith("--model-groups=")) {
17887
+ throw new CliUsageError(
17888
+ "taishi --model-groups public CLI face is disabled; input face is being redesigned for multi-issue comparison (see follow-up ticket)"
17889
+ );
17890
+ }
17891
+ throw new CliUsageError(`unknown taishi option: ${token}`);
17892
+ }
17915
17893
  if (token.startsWith("-") && token !== "-") {
17916
17894
  throw new CliUsageError(`unknown taishi option: ${token}`);
17917
17895
  }
@@ -17951,12 +17929,6 @@ function parseTaishiArgv(args) {
17951
17929
  ]
17952
17930
  };
17953
17931
  }
17954
- if (mode === "model-groups") {
17955
- return {
17956
- query: "model-groups",
17957
- projectRoots: valueLists.get("project-root") ?? []
17958
- };
17959
- }
17960
17932
  if (mode === "sweep") {
17961
17933
  return {
17962
17934
  query: "sweep",
@@ -17964,11 +17936,9 @@ function parseTaishiArgv(args) {
17964
17936
  };
17965
17937
  }
17966
17938
  const ticketRaw = valueLists.get("ticket")?.[0];
17967
- const projectRoot = valueLists.get("project-root")?.[0];
17968
17939
  return {
17969
17940
  query: "issue",
17970
- ...ticketRaw === void 0 ? {} : { ticket: parseTaishiTicketNumber(ticketRaw) },
17971
- ...projectRoot === void 0 ? {} : { projectRoot }
17941
+ ...ticketRaw === void 0 ? {} : { ticket: parseTaishiTicketNumber(ticketRaw) }
17972
17942
  };
17973
17943
  }
17974
17944
  var MergerEnvelopeDerivationError, DOCTOR_ISSUE_NUMBER_PATTERN, DOCTOR_CASE_RUNS_PATH_PATTERN, TAISHI_TICKET_NUMBER_PATTERN;
@@ -25064,6 +25034,7 @@ function taishiLibraryIndexPath(ledgerHome) {
25064
25034
  }
25065
25035
  function rowFromIssueMetricsPage(page) {
25066
25036
  return {
25037
+ bookKey: page.bookKey,
25067
25038
  projectRoot: page.projectRoot,
25068
25039
  // exactOptionalPropertyTypes: only materialize when page carries it.
25069
25040
  ...page.issueNumber === void 0 ? {} : { issueNumber: page.issueNumber },
@@ -25075,6 +25046,8 @@ function rowFromIssueMetricsPage(page) {
25075
25046
  }
25076
25047
  function sortRows(rows) {
25077
25048
  return [...rows].sort((a, b) => {
25049
+ const byBook = a.bookKey.localeCompare(b.bookKey);
25050
+ if (byBook !== 0) return byBook;
25078
25051
  const byRoot = a.projectRoot.localeCompare(b.projectRoot);
25079
25052
  if (byRoot !== 0) return byRoot;
25080
25053
  const aNum = a.issueNumber;
@@ -25095,23 +25068,31 @@ function findTaishiLibraryIndexRow(index, issueNumber) {
25095
25068
  if (index === void 0) return void 0;
25096
25069
  return index.rows.find((row) => row.issueNumber === issueNumber);
25097
25070
  }
25071
+ function indexRowKey(row) {
25072
+ return `${row.bookKey}\0${row.projectRoot}`;
25073
+ }
25074
+ function issueBookKey(bookKey, issueNumber) {
25075
+ return `${bookKey}\0${issueNumber}`;
25076
+ }
25098
25077
  function upsertTaishiLibraryIndexRows(existing, upserts) {
25099
- const byRoot = /* @__PURE__ */ new Map();
25100
- const rootByIssue = /* @__PURE__ */ new Map();
25078
+ const byKey = /* @__PURE__ */ new Map();
25079
+ const keyByIssue = /* @__PURE__ */ new Map();
25101
25080
  const ingest = (row) => {
25081
+ const key = indexRowKey(row);
25102
25082
  if (row.issueNumber !== void 0) {
25103
- const priorRoot = rootByIssue.get(row.issueNumber);
25104
- if (priorRoot !== void 0 && priorRoot !== row.projectRoot) {
25105
- byRoot.delete(priorRoot);
25083
+ const issueKey = issueBookKey(row.bookKey, row.issueNumber);
25084
+ const priorKey = keyByIssue.get(issueKey);
25085
+ if (priorKey !== void 0 && priorKey !== key) {
25086
+ byKey.delete(priorKey);
25106
25087
  }
25107
25088
  }
25108
- const prior = byRoot.get(row.projectRoot);
25089
+ const prior = byKey.get(key);
25109
25090
  if (prior !== void 0 && prior.issueNumber !== void 0 && prior.issueNumber !== row.issueNumber) {
25110
- rootByIssue.delete(prior.issueNumber);
25091
+ keyByIssue.delete(issueBookKey(prior.bookKey, prior.issueNumber));
25111
25092
  }
25112
- byRoot.set(row.projectRoot, row);
25093
+ byKey.set(key, row);
25113
25094
  if (row.issueNumber !== void 0) {
25114
- rootByIssue.set(row.issueNumber, row.projectRoot);
25095
+ keyByIssue.set(issueBookKey(row.bookKey, row.issueNumber), key);
25115
25096
  }
25116
25097
  };
25117
25098
  if (existing !== void 0) {
@@ -25122,7 +25103,7 @@ function upsertTaishiLibraryIndexRows(existing, upserts) {
25122
25103
  for (const row of upserts) {
25123
25104
  ingest(row);
25124
25105
  }
25125
- return buildTaishiLibraryIndexPage([...byRoot.values()]);
25106
+ return buildTaishiLibraryIndexPage([...byKey.values()]);
25126
25107
  }
25127
25108
  async function readTaishiLibraryIndexPage(ledgerHome) {
25128
25109
  const path = taishiLibraryIndexPath(ledgerHome);
@@ -25233,11 +25214,13 @@ async function aggregateGroup(index, input, ensureIssuePage) {
25233
25214
  }
25234
25215
  const page = await ensureIssuePage({
25235
25216
  projectRoot: row.projectRoot,
25236
- issueNumber
25217
+ issueNumber,
25218
+ bookKey: row.bookKey
25237
25219
  });
25238
25220
  issueEntries.push({
25239
25221
  issueNumber,
25240
25222
  status: "present",
25223
+ bookKey: row.bookKey,
25241
25224
  projectRoot: row.projectRoot
25242
25225
  });
25243
25226
  const acceptance = page.acceptanceSuccessRework;
@@ -25612,6 +25595,22 @@ function parseRunDirectoryName(name) {
25612
25595
  if (at <= 0 || at === name.length - 1) return void 0;
25613
25596
  return { runId: name.slice(0, at), role: name.slice(at + 1) };
25614
25597
  }
25598
+ function tryResolveBookKeyFromProjectRoot(projectRoot) {
25599
+ try {
25600
+ return resolveBookKeyFromGit(projectRoot);
25601
+ } catch {
25602
+ return void 0;
25603
+ }
25604
+ }
25605
+ async function listLedgerBookNames(booksRoot) {
25606
+ try {
25607
+ const entries = await readdir5(booksRoot, { withFileTypes: true });
25608
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
25609
+ } catch (error) {
25610
+ if (isMissingPathError4(error)) return [];
25611
+ throw error;
25612
+ }
25613
+ }
25615
25614
  async function readInvocationScopeFields(runDirectory) {
25616
25615
  let raw;
25617
25616
  try {
@@ -25632,14 +25631,18 @@ async function readInvocationScopeFields(runDirectory) {
25632
25631
  return { projectRoot };
25633
25632
  }
25634
25633
  function decideIssueScope(input) {
25635
- const projectRootMatch = input.runProjectRootIdentity === input.scopeProjectRootIdentity;
25636
- if (input.scopeTicketNumber !== void 0 && input.runTicketNumber !== void 0) {
25637
- if (input.runTicketNumber === input.scopeTicketNumber) {
25638
- return { inScope: true, conflict: !projectRootMatch };
25639
- }
25640
- return { inScope: false, conflict: false };
25634
+ if (input.scopeTicketNumber !== void 0) {
25635
+ return { inScope: input.runTicketNumber === input.scopeTicketNumber };
25636
+ }
25637
+ if (input.wholeBook) {
25638
+ return { inScope: true };
25639
+ }
25640
+ if (input.scopeRootIdentity !== void 0) {
25641
+ return {
25642
+ inScope: input.runProjectRootIdentity === input.scopeRootIdentity
25643
+ };
25641
25644
  }
25642
- return { inScope: projectRootMatch, conflict: false };
25645
+ return { inScope: true };
25643
25646
  }
25644
25647
  async function resolveSessionFile(runDirectory) {
25645
25648
  try {
@@ -25774,18 +25777,29 @@ async function classifyScopedRun(input) {
25774
25777
  }
25775
25778
  async function scanTaishiIssueRuns(input) {
25776
25779
  const ledgerHome = resolveActivationLedgerHome();
25777
- const scopeIdentity = physicalPathIdentity(input.projectRoot);
25778
25780
  const scopeTicketNumber = input.ticketNumber;
25779
25781
  const booksRoot = join22(ledgerHome, "books");
25782
+ let wholeBook = false;
25783
+ let scopeRootIdentity;
25780
25784
  let bookNames;
25781
- try {
25782
- const entries = await readdir5(booksRoot, { withFileTypes: true });
25783
- bookNames = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
25784
- } catch (error) {
25785
- if (isMissingPathError4(error)) {
25786
- return { runs: [], unreadable: [], scopeConflicts: [] };
25785
+ if (input.bookKey !== void 0 && input.bookKey.trim() !== "") {
25786
+ bookNames = [input.bookKey];
25787
+ wholeBook = true;
25788
+ } else if (input.projectRoot !== void 0) {
25789
+ const resolved = tryResolveBookKeyFromProjectRoot(input.projectRoot);
25790
+ if (resolved !== void 0) {
25791
+ bookNames = [resolved];
25792
+ wholeBook = true;
25793
+ } else {
25794
+ bookNames = await listLedgerBookNames(booksRoot);
25795
+ scopeRootIdentity = physicalPathIdentity(input.projectRoot);
25787
25796
  }
25788
- throw error;
25797
+ } else {
25798
+ bookNames = await listLedgerBookNames(booksRoot);
25799
+ wholeBook = true;
25800
+ }
25801
+ if (bookNames.length === 0) {
25802
+ return { runs: [], unreadable: [], scopeConflicts: [] };
25789
25803
  }
25790
25804
  const runs = [];
25791
25805
  const unreadable = [];
@@ -25814,20 +25828,13 @@ async function scanTaishiIssueRuns(input) {
25814
25828
  if (scopeFields === void 0) continue;
25815
25829
  const runProjectRootIdentity = physicalPathIdentity(scopeFields.projectRoot);
25816
25830
  const decision = decideIssueScope({
25817
- scopeProjectRootIdentity: scopeIdentity,
25818
25831
  scopeTicketNumber,
25832
+ wholeBook,
25833
+ scopeRootIdentity,
25819
25834
  runProjectRootIdentity,
25820
25835
  runTicketNumber: scopeFields.ticketNumber
25821
25836
  });
25822
25837
  if (!decision.inScope) continue;
25823
- if (decision.conflict) {
25824
- scopeConflicts.push({
25825
- runId: parsed.runId,
25826
- ticketNumber: scopeFields.ticketNumber,
25827
- projectRoot: runProjectRootIdentity,
25828
- fact: "typed-ticketNumber-over-projectRoot"
25829
- });
25830
- }
25831
25838
  const classified = await classifyScopedRun({
25832
25839
  book,
25833
25840
  runId: parsed.runId,
@@ -25848,6 +25855,7 @@ var LIVE_RUN_STATES;
25848
25855
  var init_taishi_ledger = __esm({
25849
25856
  "src/taishi-ledger.ts"() {
25850
25857
  "use strict";
25858
+ init_activation_ledger_git();
25851
25859
  init_activation_ledger_topology();
25852
25860
  init_ledger_session_read();
25853
25861
  init_run_terminal_artifacts();
@@ -26583,12 +26591,24 @@ var init_taishi_metric_family = __esm({
26583
26591
  // src/taishi-page.ts
26584
26592
  import { createHash as createHash4 } from "node:crypto";
26585
26593
  import { dirname as dirname10, join as join23 } from "node:path";
26586
- function taishiIssuePageKey(projectRoot) {
26587
- const identity = physicalPathIdentity(projectRoot);
26588
- return createHash4("sha256").update(identity).digest("hex").slice(0, 32);
26594
+ function taishiIssuePageKey(address) {
26595
+ const parts = ["book", address.bookKey];
26596
+ if (address.issueNumber !== void 0) {
26597
+ parts.push("ticket", String(address.issueNumber));
26598
+ } else if (address.scopeRootIdentity !== void 0) {
26599
+ parts.push("root", physicalPathIdentity(address.scopeRootIdentity));
26600
+ }
26601
+ return createHash4("sha256").update(parts.join("\0")).digest("hex").slice(0, 32);
26589
26602
  }
26590
- function taishiIssuePagePath(ledgerHome, projectRoot) {
26591
- return join23(ledgerHome, "taishi", "issues", `${taishiIssuePageKey(projectRoot)}.json`);
26603
+ function taishiIssuePagePath(ledgerHome, address) {
26604
+ return join23(ledgerHome, "taishi", "issues", `${taishiIssuePageKey(address)}.json`);
26605
+ }
26606
+ function taishiIssuePageAddressFromPage(page) {
26607
+ return {
26608
+ bookKey: page.bookKey,
26609
+ ...page.issueNumber === void 0 ? {} : { issueNumber: page.issueNumber },
26610
+ ...page.scopeRootIdentity === void 0 ? {} : { scopeRootIdentity: page.scopeRootIdentity }
26611
+ };
26592
26612
  }
26593
26613
  function sortLegs(legs) {
26594
26614
  return [...legs].sort((a, b) => {
@@ -26677,9 +26697,11 @@ async function buildTaishiIssueMetricsPage(input) {
26677
26697
  const envelope = {
26678
26698
  kind: "taishi-issue-metrics",
26679
26699
  mode: "issue",
26700
+ bookKey: input.bookKey,
26680
26701
  projectRoot,
26681
26702
  // exactOptionalPropertyTypes: only materialize when caller supplied it.
26682
26703
  ...input.issueNumber === void 0 ? {} : { issueNumber: input.issueNumber },
26704
+ ...input.scopeRootIdentity === void 0 ? {} : { scopeRootIdentity: physicalPathIdentity(input.scopeRootIdentity) },
26683
26705
  legs,
26684
26706
  unreadable,
26685
26707
  unreadableCount: unreadable.length,
@@ -26697,7 +26719,7 @@ async function buildTaishiIssueMetricsPage(input) {
26697
26719
  return { ...envelope, ...sections };
26698
26720
  }
26699
26721
  async function writeTaishiIssueMetricsPage(ledgerHome, page) {
26700
- const path = taishiIssuePagePath(ledgerHome, page.projectRoot);
26722
+ const path = taishiIssuePagePath(ledgerHome, taishiIssuePageAddressFromPage(page));
26701
26723
  ensureRealDirectoryTree(ledgerHome, dirname10(path));
26702
26724
  assertLedgerFileInsideHome(path, ledgerHome);
26703
26725
  await writeFileAtomically(path, `${JSON.stringify(page, null, 2)}
@@ -26720,27 +26742,51 @@ function isMissingPathError5(error) {
26720
26742
  return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
26721
26743
  }
26722
26744
  function cachedPageMatchesRequestedScope(page, input) {
26745
+ if (page.bookKey !== input.bookKey) return false;
26723
26746
  const requestedTicket = input.ticketNumber ?? input.issueNumber;
26724
26747
  if (requestedTicket === void 0) {
26725
26748
  return page.issueNumber === void 0;
26726
26749
  }
26727
26750
  return page.issueNumber === requestedTicket;
26728
26751
  }
26752
+ function tryResolveBookKey(projectRoot) {
26753
+ try {
26754
+ return resolveBookKeyFromGit(projectRoot);
26755
+ } catch {
26756
+ return void 0;
26757
+ }
26758
+ }
26759
+ function resolveIssueBookKey(input) {
26760
+ if (input.bookKey !== void 0 && input.bookKey.trim() !== "") {
26761
+ return input.bookKey;
26762
+ }
26763
+ const fromGit = tryResolveBookKey(input.projectRoot);
26764
+ if (fromGit !== void 0) return fromGit;
26765
+ return `root:${physicalPathIdentity(input.projectRoot)}`;
26766
+ }
26729
26767
  async function readOrComputeTaishiIssuePage(input) {
26730
26768
  const ledgerHome = resolveActivationLedgerHome();
26731
26769
  const projectRoot = physicalPathIdentity(input.projectRoot);
26732
- const pagePath = taishiIssuePagePath(ledgerHome, projectRoot);
26770
+ const bookKey = resolveIssueBookKey(input);
26771
+ const issueNumber = input.ticketNumber ?? input.issueNumber;
26772
+ const pagePath = taishiIssuePagePath(ledgerHome, {
26773
+ bookKey,
26774
+ ...issueNumber === void 0 ? {} : { issueNumber },
26775
+ // Sweep/legacy path-narrow pages (no ticket, no explicit CLI book-only scope).
26776
+ ...issueNumber === void 0 && input.bookKey === void 0 ? { scopeRootIdentity: projectRoot } : {}
26777
+ });
26733
26778
  try {
26734
26779
  const raw = await readFile14(pagePath, "utf8");
26735
26780
  const page = JSON.parse(raw);
26736
- if (cachedPageMatchesRequestedScope(page, input)) {
26781
+ if (cachedPageMatchesRequestedScope(page, { bookKey, ...input })) {
26737
26782
  return { mode: "issue", page, pagePath };
26738
26783
  }
26739
26784
  } catch (error) {
26740
26785
  if (!isMissingPathError5(error)) {
26741
26786
  throw new TaishiIssueComputeError({
26787
+ bookKey,
26742
26788
  projectRoot,
26743
- ...input.issueNumber === void 0 ? {} : { issueNumber: input.issueNumber },
26789
+ ...issueNumber === void 0 ? {} : { issueNumber },
26744
26790
  cause: error
26745
26791
  });
26746
26792
  }
@@ -26750,8 +26796,9 @@ async function readOrComputeTaishiIssuePage(input) {
26750
26796
  } catch (error) {
26751
26797
  if (error instanceof TaishiIssueComputeError) throw error;
26752
26798
  throw new TaishiIssueComputeError({
26799
+ bookKey,
26753
26800
  projectRoot,
26754
- ...input.issueNumber === void 0 ? {} : { issueNumber: input.issueNumber },
26801
+ ...issueNumber === void 0 ? {} : { issueNumber },
26755
26802
  cause: error
26756
26803
  });
26757
26804
  }
@@ -26761,28 +26808,26 @@ async function runTaishiIssueMode(input, precomputedScan) {
26761
26808
  const ledgerHome = resolveActivationLedgerHome();
26762
26809
  const projectRoot = input.projectRoot;
26763
26810
  const ticketNumber = "ticketNumber" in input ? input.ticketNumber : void 0;
26764
- const scan = precomputedScan ?? (ticketNumber === void 0 ? await scanTaishiIssueRuns({ projectRoot }) : await scanTaishiIssueRuns({ projectRoot, ticketNumber }));
26811
+ const inputBookKey = "bookKey" in input && typeof input.bookKey === "string" && input.bookKey.trim() !== "" ? input.bookKey : void 0;
26812
+ const scan = precomputedScan ?? (inputBookKey !== void 0 ? await scanTaishiIssueRuns({
26813
+ bookKey: inputBookKey,
26814
+ ...ticketNumber === void 0 ? {} : { ticketNumber }
26815
+ }) : ticketNumber === void 0 ? await scanTaishiIssueRuns({ projectRoot }) : await scanTaishiIssueRuns({ projectRoot, ticketNumber }));
26765
26816
  const issueNumber = "issueNumber" in input ? input.issueNumber : void 0;
26766
- const conflictingProjectRoot = "conflictingProjectRoot" in input ? input.conflictingProjectRoot : void 0;
26767
- const scopeConflicts = [...scan.scopeConflicts];
26768
- if (conflictingProjectRoot !== void 0 && ticketNumber !== void 0) {
26769
- const losingRoot = physicalPathIdentity(conflictingProjectRoot);
26770
- const winningRoot = physicalPathIdentity(projectRoot);
26771
- if (losingRoot !== winningRoot) {
26772
- scopeConflicts.push({
26773
- ticketNumber,
26774
- projectRoot: losingRoot,
26775
- fact: "typed-ticketNumber-over-projectRoot"
26776
- });
26777
- }
26778
- }
26817
+ const bookKey = resolveIssueBookKey({
26818
+ ...inputBookKey === void 0 ? {} : { bookKey: inputBookKey },
26819
+ projectRoot
26820
+ });
26821
+ const scopeRootIdentity = inputBookKey === void 0 && ticketNumber === void 0 && issueNumber === void 0 ? physicalPathIdentity(projectRoot) : void 0;
26779
26822
  const page = await buildTaishiIssueMetricsPage({
26823
+ bookKey,
26780
26824
  projectRoot,
26781
26825
  runs: scan.runs,
26782
26826
  unreadable: scan.unreadable,
26783
- scopeConflicts,
26827
+ scopeConflicts: scan.scopeConflicts,
26784
26828
  ...input.changedLines === void 0 ? {} : { changedLines: input.changedLines },
26785
- ...issueNumber === void 0 ? {} : { issueNumber }
26829
+ ...issueNumber === void 0 ? {} : { issueNumber },
26830
+ ...scopeRootIdentity === void 0 ? {} : { scopeRootIdentity }
26786
26831
  });
26787
26832
  const pagePath = await writeTaishiIssueMetricsPage(ledgerHome, page);
26788
26833
  if (issueNumber !== void 0) {
@@ -26818,19 +26863,23 @@ async function runTaishiModelGroupsMode(input) {
26818
26863
  const scan = await scanTaishiIssueRuns({ projectRoot });
26819
26864
  runs.push(...scan.runs);
26820
26865
  unreadable.push(...scan.unreadable);
26821
- const pagePath = taishiIssuePagePath(ledgerHome, projectRoot);
26866
+ const bookKey = resolveIssueBookKey({ projectRoot });
26867
+ const pagePath = taishiIssuePagePath(ledgerHome, {
26868
+ bookKey,
26869
+ scopeRootIdentity: projectRoot
26870
+ });
26822
26871
  try {
26823
26872
  const raw = await readFile14(pagePath, "utf8");
26824
26873
  JSON.parse(raw);
26825
26874
  } catch (error) {
26826
26875
  if (!isMissingPathError5(error)) {
26827
- throw new TaishiIssueComputeError({ projectRoot, cause: error });
26876
+ throw new TaishiIssueComputeError({ bookKey, projectRoot, cause: error });
26828
26877
  }
26829
26878
  try {
26830
26879
  await runTaishiIssueMode({ mode: "issue", projectRoot }, scan);
26831
26880
  } catch (computeError) {
26832
26881
  if (computeError instanceof TaishiIssueComputeError) throw computeError;
26833
- throw new TaishiIssueComputeError({ projectRoot, cause: computeError });
26882
+ throw new TaishiIssueComputeError({ bookKey, projectRoot, cause: computeError });
26834
26883
  }
26835
26884
  }
26836
26885
  }
@@ -26848,11 +26897,13 @@ async function runTaishi(input) {
26848
26897
  }
26849
26898
  if (input.mode === "cohort") {
26850
26899
  const ledgerHome = resolveActivationLedgerHome();
26851
- return runTaishiCohortMode(ledgerHome, input, async ({ projectRoot, issueNumber }) => {
26900
+ return runTaishiCohortMode(ledgerHome, input, async ({ projectRoot, issueNumber, bookKey }) => {
26901
+ const realBookKey = bookKey !== void 0 && !bookKey.startsWith("root:") ? bookKey : void 0;
26852
26902
  const ensured = await readOrComputeTaishiIssuePage({
26853
26903
  mode: "issue",
26854
26904
  projectRoot,
26855
- issueNumber
26905
+ issueNumber,
26906
+ ...realBookKey === void 0 ? {} : { bookKey: realBookKey }
26856
26907
  });
26857
26908
  return ensured.page;
26858
26909
  });
@@ -26872,19 +26923,22 @@ var init_taishi_entry = __esm({
26872
26923
  init_taishi_ledger();
26873
26924
  init_taishi_index();
26874
26925
  init_taishi_model_groups();
26926
+ init_activation_ledger_git();
26875
26927
  init_taishi_page();
26876
26928
  TaishiIssueComputeError = class extends Error {
26877
26929
  code = "taishi-issue-compute-failed";
26930
+ bookKey;
26878
26931
  projectRoot;
26879
26932
  issueNumber;
26880
26933
  constructor(input) {
26881
26934
  const root = physicalPathIdentity(input.projectRoot);
26882
26935
  const causeText = input.cause instanceof Error ? input.cause.message || input.cause.name : String(input.cause);
26883
- const issueFace = input.issueNumber === void 0 ? `projectRoot ${root}` : `issue ${input.issueNumber} (projectRoot ${root})`;
26936
+ const issueFace = input.issueNumber === void 0 ? `book ${input.bookKey} (projectRoot ${root})` : `issue ${input.issueNumber} book ${input.bookKey} (projectRoot ${root})`;
26884
26937
  super(`taishi compute failed for ${issueFace}: ${causeText}`, {
26885
26938
  cause: input.cause
26886
26939
  });
26887
26940
  this.name = "TaishiIssueComputeError";
26941
+ this.bookKey = input.bookKey;
26888
26942
  this.projectRoot = root;
26889
26943
  if (input.issueNumber !== void 0) {
26890
26944
  this.issueNumber = input.issueNumber;
@@ -26915,34 +26969,37 @@ var init_taishi_entry = __esm({
26915
26969
  // src/public-cli/taishi-run.ts
26916
26970
  import { readFile as readFile15 } from "node:fs/promises";
26917
26971
  import { isAbsolute as isAbsolute5, resolve as resolve7 } from "node:path";
26918
- async function buildTaishiIssueModeInputFromPublicArgv(parsed, ledgerHome) {
26972
+ function resolveTaishiIssueBookKeyFromCwd(cwd = process.cwd()) {
26973
+ try {
26974
+ return resolveBookKeyFromGit(cwd);
26975
+ } catch (error) {
26976
+ if (error instanceof ActivationGitRepositoryRequiredError) {
26977
+ throw new CliUsageError(
26978
+ "taishi issue query requires a git repository cwd (book = git common-dir); run inside a repository (bare = whole book, or --ticket N)",
26979
+ { cause: error }
26980
+ );
26981
+ }
26982
+ throw error;
26983
+ }
26984
+ }
26985
+ async function buildTaishiIssueModeInputFromPublicArgv(parsed, _ledgerHome) {
26986
+ const cwd = process.cwd();
26987
+ const bookKey = resolveTaishiIssueBookKeyFromCwd(cwd);
26988
+ const projectRoot = physicalPathIdentity(cwd);
26919
26989
  const ticket = parsed.ticket;
26920
- const directRoot = parsed.projectRoot;
26921
26990
  if (ticket === void 0) {
26922
26991
  return {
26923
26992
  mode: "issue",
26924
- projectRoot: directRoot
26993
+ bookKey,
26994
+ projectRoot
26925
26995
  };
26926
26996
  }
26927
- const index = await readTaishiLibraryIndexPage(ledgerHome);
26928
- const row = findTaishiLibraryIndexRow(index, ticket);
26929
- let projectRoot;
26930
- if (row !== void 0) {
26931
- projectRoot = row.projectRoot;
26932
- } else if (directRoot !== void 0) {
26933
- projectRoot = directRoot;
26934
- } else {
26935
- throw new CliUsageError(
26936
- `taishi library index has no row for ticket ${ticket}`
26937
- );
26938
- }
26939
- const dualParamConflict = row !== void 0 && directRoot !== void 0 && physicalPathIdentity(directRoot) !== physicalPathIdentity(projectRoot);
26940
26997
  return {
26941
26998
  mode: "issue",
26999
+ bookKey,
26942
27000
  projectRoot,
26943
27001
  ticketNumber: ticket,
26944
- issueNumber: ticket,
26945
- ...dualParamConflict ? { conflictingProjectRoot: directRoot } : {}
27002
+ issueNumber: ticket
26946
27003
  };
26947
27004
  }
26948
27005
  function parseTaishiSweepModeInputFromJsonValue(value) {
@@ -27007,15 +27064,6 @@ async function runPublicTaishi(argv, _env, io, parseTaishiArgv2) {
27007
27064
  groups: parsed.groups
27008
27065
  });
27009
27066
  io.stdout(`${JSON.stringify(result3, null, 2)}
27010
- `);
27011
- return { exitCode: 0 };
27012
- }
27013
- if (parsed.query === "model-groups") {
27014
- const result3 = await runTaishi({
27015
- mode: "model-groups",
27016
- projectRoots: parsed.projectRoots
27017
- });
27018
- io.stdout(`${JSON.stringify(result3, null, 2)}
27019
27067
  `);
27020
27068
  return { exitCode: 0 };
27021
27069
  }
@@ -27037,6 +27085,7 @@ async function runPublicTaishi(argv, _env, io, parseTaishiArgv2) {
27037
27085
  ...code === void 0 ? {} : { identity: { code } },
27038
27086
  details: {
27039
27087
  code: error.code,
27088
+ bookKey: error.bookKey,
27040
27089
  projectRoot: error.projectRoot,
27041
27090
  ...error.issueNumber === void 0 ? {} : { issueNumber: error.issueNumber }
27042
27091
  }
@@ -27050,9 +27099,9 @@ var init_taishi_run = __esm({
27050
27099
  "src/public-cli/taishi-run.ts"() {
27051
27100
  "use strict";
27052
27101
  init_value2();
27102
+ init_activation_ledger_git();
27053
27103
  init_activation_ledger_topology();
27054
27104
  init_exact_utf8();
27055
- init_taishi_index();
27056
27105
  init_taishi_entry();
27057
27106
  init_cli_errors();
27058
27107
  init_settlement();