@akagilnc/pi-workflow-roles 0.1.1941 → 0.1.2014

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.
Files changed (40) hide show
  1. package/dist/public-cli/main.js +2470 -88
  2. package/dist/reviewer-construction.js +33 -2
  3. package/dist/reviewer-dispatch.js +142 -9
  4. package/dist/reviewer-execution-ledger.js +5 -1
  5. package/dist/reviewer-pinned-git.js +139 -10
  6. package/extensions/role-runtime.ts +2 -1
  7. package/package.json +1 -1
  8. package/src/atomic-write.ts +26 -0
  9. package/src/collector-github.ts +94 -0
  10. package/src/ledger-session-read.ts +260 -0
  11. package/src/package-contracts/reviewer-output.ts +2 -0
  12. package/src/public-cli/cli.ts +24 -0
  13. package/src/public-cli/invocation.ts +352 -1
  14. package/src/public-cli/main.ts +21 -2
  15. package/src/public-cli/registry.ts +18 -1
  16. package/src/public-cli/reviewer-run.ts +13 -0
  17. package/src/public-cli/settlement.ts +13 -0
  18. package/src/public-cli/taishi-run.ts +235 -0
  19. package/src/reviewer-construction.ts +81 -3
  20. package/src/reviewer-dispatch.ts +189 -9
  21. package/src/reviewer-execution-ledger.ts +5 -1
  22. package/src/reviewer-pinned-git.ts +154 -11
  23. package/src/reviewer-role.ts +8 -1
  24. package/src/reviewer-settlement.ts +4 -0
  25. package/src/role-runtime.ts +21 -1
  26. package/src/run-terminal-artifacts.ts +231 -0
  27. package/src/taishi-cohort.ts +232 -0
  28. package/src/taishi-entry.ts +429 -0
  29. package/src/taishi-index.ts +269 -0
  30. package/src/taishi-ledger.ts +466 -0
  31. package/src/taishi-median.ts +15 -0
  32. package/src/taishi-metric-families/acceptance-success-rework.ts +346 -0
  33. package/src/taishi-metric-families/b2-frame-buckets-actions.ts +274 -0
  34. package/src/taishi-metric-families/leg-wall-clock.ts +90 -0
  35. package/src/taishi-metric-families/round-timeline.ts +201 -0
  36. package/src/taishi-metric-families.ts +36 -0
  37. package/src/taishi-metric-family.ts +41 -0
  38. package/src/taishi-model-groups.ts +198 -0
  39. package/src/taishi-page.ts +320 -0
  40. package/src/ticket-trajectory.ts +9 -62
@@ -14273,7 +14273,13 @@ function listHelpCapabilities() {
14273
14273
  defaultPhase
14274
14274
  };
14275
14275
  });
14276
- return [...support, ...roles];
14276
+ const deterministic = PUBLIC_DETERMINISTIC_COMMANDS.map(
14277
+ (name) => ({
14278
+ kind: "deterministic",
14279
+ name
14280
+ })
14281
+ );
14282
+ return [...support, ...roles, ...deterministic];
14277
14283
  }
14278
14284
  function isPublicConfigurableSeat(value) {
14279
14285
  return PUBLIC_CONFIGURABLE_SEATS.includes(value);
@@ -14281,7 +14287,7 @@ function isPublicConfigurableSeat(value) {
14281
14287
  function isPublicCliSupportCommand(value) {
14282
14288
  return PUBLIC_CLI_SUPPORT_COMMANDS.includes(value);
14283
14289
  }
14284
- var INTERNAL_ROLE_ENTRYPOINT_RELATIVE, PUBLIC_CALLABLE_ROLES, AUTOMATIC_NAVIGATOR_SEAT, PUBLIC_CONFIGURABLE_SEATS, PUBLIC_CLI_SUPPORT_COMMANDS, STARTUP_CANDIDATES;
14290
+ var INTERNAL_ROLE_ENTRYPOINT_RELATIVE, PUBLIC_CALLABLE_ROLES, AUTOMATIC_NAVIGATOR_SEAT, PUBLIC_CONFIGURABLE_SEATS, PUBLIC_CLI_SUPPORT_COMMANDS, STARTUP_CANDIDATES, PUBLIC_DETERMINISTIC_COMMANDS;
14285
14291
  var init_registry2 = __esm({
14286
14292
  "src/public-cli/registry.ts"() {
14287
14293
  "use strict";
@@ -14335,6 +14341,7 @@ var init_registry2 = __esm({
14335
14341
  { provider: "xai", model: "grok-4.5", thinking: "high" }
14336
14342
  ]
14337
14343
  };
14344
+ PUBLIC_DETERMINISTIC_COMMANDS = ["taishi"];
14338
14345
  }
14339
14346
  });
14340
14347
 
@@ -14767,6 +14774,26 @@ function ensureRealDirectoryTree(root, targetDir) {
14767
14774
  );
14768
14775
  }
14769
14776
  }
14777
+ function assertLedgerFileInsideHome(ledgerPath, ledgerHome) {
14778
+ if (!isAbsolute(ledgerHome)) {
14779
+ throw new ActivationLedgerError(`activation ledger home must be absolute: ${ledgerHome}`);
14780
+ }
14781
+ const resolvedLedger = resolve(ledgerPath);
14782
+ try {
14783
+ if (!lstatSync2(resolvedLedger).isSymbolicLink()) return;
14784
+ throw new ActivationLedgerError(
14785
+ `activation ledger file is a symbolic link: ${resolvedLedger}`
14786
+ );
14787
+ } catch (error) {
14788
+ if (errnoCode(error) !== "ENOENT") {
14789
+ if (error instanceof ActivationLedgerError) throw error;
14790
+ throw new ActivationLedgerError(
14791
+ `activation ledger failed to stat ledger file (${resolvedLedger}): ${errorText(error)}`,
14792
+ { cause: error }
14793
+ );
14794
+ }
14795
+ }
14796
+ }
14770
14797
  var ActivationLedgerError;
14771
14798
  var init_activation_ledger_topology = __esm({
14772
14799
  "src/activation-ledger-topology.ts"() {
@@ -16769,7 +16796,260 @@ function buildMergerTransportPrompt(admitted) {
16769
16796
  }
16770
16797
  return lines.join("\n");
16771
16798
  }
16772
- var MergerEnvelopeDerivationError, DOCTOR_ISSUE_NUMBER_PATTERN, DOCTOR_CASE_RUNS_PATH_PATTERN;
16799
+ function parseTaishiTicketNumber(raw, flag = "--ticket") {
16800
+ const trimmed = raw.trim();
16801
+ if (!TAISHI_TICKET_NUMBER_PATTERN.test(trimmed)) {
16802
+ throw new CliUsageError(
16803
+ `taishi ${flag} must be a positive integer, got ${raw}`
16804
+ );
16805
+ }
16806
+ const value = Number(trimmed);
16807
+ if (!Number.isSafeInteger(value) || value < 1) {
16808
+ throw new CliUsageError(
16809
+ `taishi ${flag} must be a positive integer, got ${raw}`
16810
+ );
16811
+ }
16812
+ return value;
16813
+ }
16814
+ function parseTaishiIssueNumberList(raw, flag) {
16815
+ const trimmed = raw.trim();
16816
+ if (trimmed === "") {
16817
+ throw new CliUsageError(`${flag} requires a comma-separated positive integer list`);
16818
+ }
16819
+ const parts = trimmed.split(",").map((part) => part.trim());
16820
+ if (parts.some((part) => part === "")) {
16821
+ throw new CliUsageError(`${flag} requires a comma-separated positive integer list`);
16822
+ }
16823
+ return parts.map((part) => parseTaishiTicketNumber(part, flag));
16824
+ }
16825
+ function requireOptionValue(flag, value, what) {
16826
+ if (value === void 0 || value.trim() === "") {
16827
+ throw new CliUsageError(`${flag} requires ${what}`);
16828
+ }
16829
+ return value;
16830
+ }
16831
+ function parseTaishiArgv(args) {
16832
+ let query = "issue";
16833
+ let ticketRaw;
16834
+ const projectRoots = [];
16835
+ let groupALabel;
16836
+ let groupAIssuesRaw;
16837
+ let groupBLabel;
16838
+ let groupBIssuesRaw;
16839
+ let sweepToken = false;
16840
+ const attachmentPaths = [];
16841
+ const tokens = [...args];
16842
+ while (tokens.length > 0) {
16843
+ const token = tokens.shift();
16844
+ if (token === "--") {
16845
+ if (tokens.length > 0) {
16846
+ throw new CliUsageError(`unexpected taishi argument: ${tokens[0]}`);
16847
+ }
16848
+ break;
16849
+ }
16850
+ if (token === "--cohort") {
16851
+ if (query !== "issue") {
16852
+ throw new CliUsageError("taishi accepts only one of --cohort / --model-groups");
16853
+ }
16854
+ query = "cohort";
16855
+ continue;
16856
+ }
16857
+ if (token === "--model-groups") {
16858
+ if (query !== "issue") {
16859
+ throw new CliUsageError("taishi accepts only one of --cohort / --model-groups");
16860
+ }
16861
+ query = "model-groups";
16862
+ continue;
16863
+ }
16864
+ if (token === "--ticket") {
16865
+ const value = tokens.shift();
16866
+ if (value === void 0 || value.trim() === "") {
16867
+ throw new CliUsageError("taishi --ticket requires a positive integer");
16868
+ }
16869
+ ticketRaw = value;
16870
+ continue;
16871
+ }
16872
+ if (token.startsWith("--ticket=")) {
16873
+ ticketRaw = token.slice("--ticket=".length);
16874
+ if (ticketRaw.trim() === "") {
16875
+ throw new CliUsageError("taishi --ticket requires a positive integer");
16876
+ }
16877
+ continue;
16878
+ }
16879
+ if (token === "--project-root") {
16880
+ projectRoots.push(requireOptionPath("--project-root", tokens.shift()));
16881
+ continue;
16882
+ }
16883
+ if (token.startsWith("--project-root=")) {
16884
+ projectRoots.push(
16885
+ requireOptionPath("--project-root", token.slice("--project-root=".length))
16886
+ );
16887
+ continue;
16888
+ }
16889
+ if (token === "--group-a-label") {
16890
+ groupALabel = requireOptionValue("--group-a-label", tokens.shift(), "a label");
16891
+ continue;
16892
+ }
16893
+ if (token.startsWith("--group-a-label=")) {
16894
+ groupALabel = requireOptionValue(
16895
+ "--group-a-label",
16896
+ token.slice("--group-a-label=".length),
16897
+ "a label"
16898
+ );
16899
+ continue;
16900
+ }
16901
+ if (token === "--group-a-issues") {
16902
+ groupAIssuesRaw = requireOptionValue(
16903
+ "--group-a-issues",
16904
+ tokens.shift(),
16905
+ "a comma-separated positive integer list"
16906
+ );
16907
+ continue;
16908
+ }
16909
+ if (token.startsWith("--group-a-issues=")) {
16910
+ groupAIssuesRaw = requireOptionValue(
16911
+ "--group-a-issues",
16912
+ token.slice("--group-a-issues=".length),
16913
+ "a comma-separated positive integer list"
16914
+ );
16915
+ continue;
16916
+ }
16917
+ if (token === "--group-b-label") {
16918
+ groupBLabel = requireOptionValue("--group-b-label", tokens.shift(), "a label");
16919
+ continue;
16920
+ }
16921
+ if (token.startsWith("--group-b-label=")) {
16922
+ groupBLabel = requireOptionValue(
16923
+ "--group-b-label",
16924
+ token.slice("--group-b-label=".length),
16925
+ "a label"
16926
+ );
16927
+ continue;
16928
+ }
16929
+ if (token === "--group-b-issues") {
16930
+ groupBIssuesRaw = requireOptionValue(
16931
+ "--group-b-issues",
16932
+ tokens.shift(),
16933
+ "a comma-separated positive integer list"
16934
+ );
16935
+ continue;
16936
+ }
16937
+ if (token.startsWith("--group-b-issues=")) {
16938
+ groupBIssuesRaw = requireOptionValue(
16939
+ "--group-b-issues",
16940
+ token.slice("--group-b-issues=".length),
16941
+ "a comma-separated positive integer list"
16942
+ );
16943
+ continue;
16944
+ }
16945
+ if (token === "--attach") {
16946
+ attachmentPaths.push(requireOptionPath("--attach", tokens.shift()));
16947
+ continue;
16948
+ }
16949
+ if (token.startsWith("--attach=")) {
16950
+ attachmentPaths.push(
16951
+ requireOptionPath("--attach", token.slice("--attach=".length))
16952
+ );
16953
+ continue;
16954
+ }
16955
+ if (token.startsWith("-") && token !== "-") {
16956
+ throw new CliUsageError(`unknown taishi option: ${token}`);
16957
+ }
16958
+ if (token === "sweep") {
16959
+ if (sweepToken) {
16960
+ throw new CliUsageError("unexpected taishi argument: sweep");
16961
+ }
16962
+ sweepToken = true;
16963
+ continue;
16964
+ }
16965
+ throw new CliUsageError(`unexpected taishi argument: ${token}`);
16966
+ }
16967
+ const hasSweepFace = sweepToken || attachmentPaths.length > 0;
16968
+ const hasCohortFlags = groupALabel !== void 0 || groupAIssuesRaw !== void 0 || groupBLabel !== void 0 || groupBIssuesRaw !== void 0;
16969
+ if (query === "cohort") {
16970
+ if (groupALabel === void 0 || groupAIssuesRaw === void 0 || groupBLabel === void 0 || groupBIssuesRaw === void 0) {
16971
+ throw new CliUsageError(
16972
+ "usage: ak-role taishi --cohort --group-a-label <L> --group-a-issues <N[,N...]> --group-b-label <L> --group-b-issues <N[,N...]>"
16973
+ );
16974
+ }
16975
+ if (ticketRaw !== void 0 || projectRoots.length > 0) {
16976
+ throw new CliUsageError(
16977
+ "taishi --cohort does not accept --ticket or --project-root"
16978
+ );
16979
+ }
16980
+ if (hasSweepFace) {
16981
+ throw new CliUsageError(
16982
+ "taishi --cohort does not accept sweep --attach"
16983
+ );
16984
+ }
16985
+ return {
16986
+ query: "cohort",
16987
+ groups: [
16988
+ {
16989
+ groupLabel: groupALabel,
16990
+ issues: parseTaishiIssueNumberList(groupAIssuesRaw, "--group-a-issues")
16991
+ },
16992
+ {
16993
+ groupLabel: groupBLabel,
16994
+ issues: parseTaishiIssueNumberList(groupBIssuesRaw, "--group-b-issues")
16995
+ }
16996
+ ]
16997
+ };
16998
+ }
16999
+ if (query === "model-groups") {
17000
+ if (projectRoots.length === 0) {
17001
+ throw new CliUsageError(
17002
+ "usage: ak-role taishi --model-groups --project-root <P> [--project-root <P> ...]"
17003
+ );
17004
+ }
17005
+ if (ticketRaw !== void 0) {
17006
+ throw new CliUsageError("taishi --model-groups does not accept --ticket");
17007
+ }
17008
+ if (hasCohortFlags) {
17009
+ throw new CliUsageError("taishi --model-groups does not accept cohort group flags");
17010
+ }
17011
+ if (hasSweepFace) {
17012
+ throw new CliUsageError(
17013
+ "taishi --model-groups does not accept sweep --attach"
17014
+ );
17015
+ }
17016
+ return {
17017
+ query: "model-groups",
17018
+ projectRoots
17019
+ };
17020
+ }
17021
+ if (hasCohortFlags) {
17022
+ throw new CliUsageError("taishi issue query does not accept cohort group flags");
17023
+ }
17024
+ if (hasSweepFace) {
17025
+ if (ticketRaw !== void 0 || projectRoots.length > 0) {
17026
+ throw new CliUsageError(
17027
+ "taishi sweep --attach cannot combine with --ticket or --project-root"
17028
+ );
17029
+ }
17030
+ return {
17031
+ query: "sweep",
17032
+ attachmentPaths
17033
+ };
17034
+ }
17035
+ if (projectRoots.length > 1) {
17036
+ throw new CliUsageError(
17037
+ "taishi issue query accepts at most one --project-root (use --model-groups for many)"
17038
+ );
17039
+ }
17040
+ const projectRoot = projectRoots[0];
17041
+ if (ticketRaw === void 0 && projectRoot === void 0) {
17042
+ throw new CliUsageError(
17043
+ "usage: ak-role taishi ((--ticket <N> | --project-root <P>) | [sweep] --attach <sweep.json> | --cohort ... | --model-groups ...)"
17044
+ );
17045
+ }
17046
+ return {
17047
+ query: "issue",
17048
+ ...ticketRaw === void 0 ? {} : { ticket: parseTaishiTicketNumber(ticketRaw) },
17049
+ ...projectRoot === void 0 ? {} : { projectRoot }
17050
+ };
17051
+ }
17052
+ var MergerEnvelopeDerivationError, DOCTOR_ISSUE_NUMBER_PATTERN, DOCTOR_CASE_RUNS_PATH_PATTERN, TAISHI_TICKET_NUMBER_PATTERN;
16773
17053
  var init_invocation = __esm({
16774
17054
  "src/public-cli/invocation.ts"() {
16775
17055
  "use strict";
@@ -16796,6 +17076,7 @@ var init_invocation = __esm({
16796
17076
  };
16797
17077
  DOCTOR_ISSUE_NUMBER_PATTERN = /^[1-9]\d*$/;
16798
17078
  DOCTOR_CASE_RUNS_PATH_PATTERN = /\/\.ak-roles\/books\/[^/]+\/issues\/([1-9]\d*)\/runs$/;
17079
+ TAISHI_TICKET_NUMBER_PATTERN = /^[1-9]\d*$/;
16799
17080
  }
16800
17081
  });
16801
17082
 
@@ -18735,6 +19016,11 @@ function formatFailureStderrDiagnostic(failure) {
18735
19016
  function presentStructuralRejection(error, io) {
18736
19017
  io.stderr(formatCliDiagnostic(error.message));
18737
19018
  }
19019
+ function presentControlledFailure(failure, io) {
19020
+ io.stdout(`${JSON.stringify(failure, null, 2)}
19021
+ `);
19022
+ io.stderr(formatFailureStderrDiagnostic(failure));
19023
+ }
18738
19024
  async function inspectJudgeSession(sessionFile) {
18739
19025
  try {
18740
19026
  await readFile9(sessionFile, "utf8");
@@ -20720,6 +21006,8 @@ async function publishReviewerArtifacts(admitted, roleOutcome, sessionDirectory,
20720
21006
  baseRevision: admitted.baseRevision,
20721
21007
  authorityRefs: [...admitted.authorityRefs],
20722
21008
  ...admitted.instructionEmpty ? {} : { callerProvenance: admitted.instruction },
21009
+ // Self-fetch Spec bytes + source annotation when primary path produced material (#343).
21010
+ ...options.reviewerReceipt?.specFetchedMaterial === void 0 ? {} : { specFetchedMaterial: options.reviewerReceipt.specFetchedMaterial },
20723
21011
  attachments: admitted.attachments.map((a) => ({
20724
21012
  provenancePath: a.provenancePath,
20725
21013
  frozenPath: a.frozenPath,
@@ -21385,7 +21673,7 @@ function buildCoderResumeActivationExtraArgs(admitted, options) {
21385
21673
  RESUME_TRANSPORT_ENVELOPE
21386
21674
  ];
21387
21675
  }
21388
- async function presentControlledFailure(admitted, failureInput, io) {
21676
+ async function presentControlledFailure2(admitted, failureInput, io) {
21389
21677
  const hasThrown = Object.hasOwn(failureInput, "thrown");
21390
21678
  const resumeObservation = await resolveControlledFailureResumeObservation({
21391
21679
  runDirectory: admitted.runDirectory,
@@ -21441,7 +21729,7 @@ async function dispatchAdmittedCoder(input) {
21441
21729
  env.credentials
21442
21730
  );
21443
21731
  if (missingCredential !== void 0) {
21444
- return await presentControlledFailure(
21732
+ return await presentControlledFailure2(
21445
21733
  admitted,
21446
21734
  missingCredential,
21447
21735
  io
@@ -21472,7 +21760,7 @@ async function dispatchAdmittedCoder(input) {
21472
21760
  ...env.piRunner === void 0 ? {} : { runner: env.piRunner }
21473
21761
  });
21474
21762
  } catch (error) {
21475
- return await presentControlledFailure(
21763
+ return await presentControlledFailure2(
21476
21764
  admitted,
21477
21765
  {
21478
21766
  timedOut: false,
@@ -21497,7 +21785,7 @@ async function dispatchAdmittedCoder(input) {
21497
21785
  ...methodProvenance === void 0 ? {} : { methodProvenance }
21498
21786
  });
21499
21787
  } catch (error) {
21500
- return await presentControlledFailure(
21788
+ return await presentControlledFailure2(
21501
21789
  admitted,
21502
21790
  {
21503
21791
  timedOut: false,
@@ -21528,7 +21816,7 @@ async function dispatchAdmittedCoder(input) {
21528
21816
  credential: credentialFailure,
21529
21817
  runDirectory: admitted.runDirectory
21530
21818
  });
21531
- return await presentControlledFailure(
21819
+ return await presentControlledFailure2(
21532
21820
  admitted,
21533
21821
  {
21534
21822
  timedOut: result2.timedOut,
@@ -21583,7 +21871,7 @@ async function runPublicCoder(argv, env, io, parseCoderArgv2) {
21583
21871
  methodProvenance = material.provenance;
21584
21872
  } catch (error) {
21585
21873
  await lease.release();
21586
- return await presentControlledFailure(
21874
+ return await presentControlledFailure2(
21587
21875
  admitted,
21588
21876
  {
21589
21877
  timedOut: false,
@@ -21660,7 +21948,7 @@ async function runPublicCoderResume(argv, env, io) {
21660
21948
  methodProvenance = material.provenance;
21661
21949
  } catch (error) {
21662
21950
  await lease.release();
21663
- return await presentControlledFailure(
21951
+ return await presentControlledFailure2(
21664
21952
  admitted,
21665
21953
  {
21666
21954
  timedOut: false,
@@ -21743,7 +22031,7 @@ function buildCollectorActivationExtraArgs(admitted, options = {}) {
21743
22031
  prompt
21744
22032
  ];
21745
22033
  }
21746
- async function presentControlledFailure2(admitted, failureInput, io) {
22034
+ async function presentControlledFailure3(admitted, failureInput, io) {
21747
22035
  const hasThrown = Object.hasOwn(failureInput, "thrown");
21748
22036
  const session = !hasThrown && !failureInput.timedOut && failureInput.knownFailure === void 0 && failureInput.knownCause === void 0 ? await inspectJudgeSession(admitted.sessionFile) : void 0;
21749
22037
  const failure = classifyPostAdmissionFailure({
@@ -21774,7 +22062,7 @@ async function dispatchAdmittedCollector(input) {
21774
22062
  env.credentials
21775
22063
  );
21776
22064
  if (missingCredential !== void 0) {
21777
- return await presentControlledFailure2(
22065
+ return await presentControlledFailure3(
21778
22066
  admitted,
21779
22067
  missingCredential,
21780
22068
  io
@@ -21805,7 +22093,7 @@ async function dispatchAdmittedCollector(input) {
21805
22093
  ...env.piRunner === void 0 ? {} : { runner: env.piRunner }
21806
22094
  });
21807
22095
  } catch (error) {
21808
- return await presentControlledFailure2(
22096
+ return await presentControlledFailure3(
21809
22097
  admitted,
21810
22098
  {
21811
22099
  timedOut: false,
@@ -21828,7 +22116,7 @@ async function dispatchAdmittedCollector(input) {
21828
22116
  try {
21829
22117
  lawful = await trySettleCollectorTerminalResult(admitted);
21830
22118
  } catch (error) {
21831
- return await presentControlledFailure2(
22119
+ return await presentControlledFailure3(
21832
22120
  admitted,
21833
22121
  {
21834
22122
  timedOut: false,
@@ -21866,7 +22154,7 @@ async function dispatchAdmittedCollector(input) {
21866
22154
  credential: credentialFailure,
21867
22155
  runDirectory: admitted.runDirectory
21868
22156
  });
21869
- return await presentControlledFailure2(
22157
+ return await presentControlledFailure3(
21870
22158
  admitted,
21871
22159
  {
21872
22160
  timedOut: result2.timedOut,
@@ -21977,7 +22265,7 @@ function buildDoctorActivationExtraArgs(admitted, options = {}) {
21977
22265
  prompt
21978
22266
  ];
21979
22267
  }
21980
- async function presentControlledFailure3(admitted, failureInput, io) {
22268
+ async function presentControlledFailure4(admitted, failureInput, io) {
21981
22269
  const hasThrown = Object.hasOwn(failureInput, "thrown");
21982
22270
  const session = !hasThrown && !failureInput.timedOut && failureInput.knownFailure === void 0 ? await inspectJudgeSession(admitted.sessionFile) : void 0;
21983
22271
  const failure = classifyPostAdmissionFailure({
@@ -22005,7 +22293,7 @@ async function dispatchAdmittedDoctor(input) {
22005
22293
  env.credentials
22006
22294
  );
22007
22295
  if (missingCredential !== void 0) {
22008
- return await presentControlledFailure3(
22296
+ return await presentControlledFailure4(
22009
22297
  admitted,
22010
22298
  missingCredential,
22011
22299
  io
@@ -22035,7 +22323,7 @@ async function dispatchAdmittedDoctor(input) {
22035
22323
  ...env.piRunner === void 0 ? {} : { runner: env.piRunner }
22036
22324
  });
22037
22325
  } catch (error) {
22038
- return await presentControlledFailure3(
22326
+ return await presentControlledFailure4(
22039
22327
  admitted,
22040
22328
  {
22041
22329
  timedOut: false,
@@ -22058,7 +22346,7 @@ async function dispatchAdmittedDoctor(input) {
22058
22346
  try {
22059
22347
  lawful = await trySettleDoctorTerminalResult(admitted);
22060
22348
  } catch (error) {
22061
- return await presentControlledFailure3(
22349
+ return await presentControlledFailure4(
22062
22350
  admitted,
22063
22351
  {
22064
22352
  timedOut: false,
@@ -22103,7 +22391,7 @@ async function dispatchAdmittedDoctor(input) {
22103
22391
  credential: credentialFailure,
22104
22392
  runDirectory: admitted.runDirectory
22105
22393
  });
22106
- return await presentControlledFailure3(
22394
+ return await presentControlledFailure4(
22107
22395
  admitted,
22108
22396
  {
22109
22397
  timedOut: result2.timedOut,
@@ -22257,7 +22545,7 @@ function buildFixerResumeActivationExtraArgs(admitted, options) {
22257
22545
  RESUME_TRANSPORT_ENVELOPE
22258
22546
  ];
22259
22547
  }
22260
- async function presentControlledFailure4(admitted, failureInput, io) {
22548
+ async function presentControlledFailure5(admitted, failureInput, io) {
22261
22549
  const hasThrown = Object.hasOwn(failureInput, "thrown");
22262
22550
  const resumeObservation = await resolveControlledFailureResumeObservation({
22263
22551
  runDirectory: admitted.runDirectory,
@@ -22310,7 +22598,7 @@ async function dispatchAdmittedFixer(input) {
22310
22598
  env.credentials
22311
22599
  );
22312
22600
  if (missingCredential !== void 0) {
22313
- return await presentControlledFailure4(
22601
+ return await presentControlledFailure5(
22314
22602
  admitted,
22315
22603
  missingCredential,
22316
22604
  io
@@ -22341,7 +22629,7 @@ async function dispatchAdmittedFixer(input) {
22341
22629
  ...env.piRunner === void 0 ? {} : { runner: env.piRunner }
22342
22630
  });
22343
22631
  } catch (error) {
22344
- return await presentControlledFailure4(
22632
+ return await presentControlledFailure5(
22345
22633
  admitted,
22346
22634
  {
22347
22635
  timedOut: false,
@@ -22371,7 +22659,7 @@ async function dispatchAdmittedFixer(input) {
22371
22659
  )
22372
22660
  });
22373
22661
  } catch (error) {
22374
- return await presentControlledFailure4(
22662
+ return await presentControlledFailure5(
22375
22663
  admitted,
22376
22664
  {
22377
22665
  timedOut: false,
@@ -22416,7 +22704,7 @@ async function dispatchAdmittedFixer(input) {
22416
22704
  credential: credentialFailure,
22417
22705
  runDirectory: admitted.runDirectory
22418
22706
  });
22419
- return await presentControlledFailure4(
22707
+ return await presentControlledFailure5(
22420
22708
  admitted,
22421
22709
  {
22422
22710
  timedOut: result2.timedOut,
@@ -22470,7 +22758,7 @@ async function runPublicFixer(argv, env, io, parseFixerArgv2) {
22470
22758
  methodMaterial = await loadFixerMethodMaterial(env.packageRoot);
22471
22759
  } catch (error) {
22472
22760
  await lease.release();
22473
- return await presentControlledFailure4(
22761
+ return await presentControlledFailure5(
22474
22762
  admitted,
22475
22763
  {
22476
22764
  timedOut: false,
@@ -22540,7 +22828,7 @@ async function runPublicFixerResume(argv, env, io) {
22540
22828
  methodMaterial = await loadFixerMethodMaterial(env.packageRoot);
22541
22829
  } catch (error) {
22542
22830
  await lease.release();
22543
- return await presentControlledFailure4(
22831
+ return await presentControlledFailure5(
22544
22832
  admitted,
22545
22833
  {
22546
22834
  timedOut: false,
@@ -22636,7 +22924,7 @@ function buildJudgeResumeActivationExtraArgs(admitted, options = {}) {
22636
22924
  RESUME_TRANSPORT_ENVELOPE
22637
22925
  ];
22638
22926
  }
22639
- async function presentControlledFailure5(admitted, failureInput, io) {
22927
+ async function presentControlledFailure6(admitted, failureInput, io) {
22640
22928
  const hasThrown = Object.hasOwn(failureInput, "thrown");
22641
22929
  const resumeObservation = await resolveControlledFailureResumeObservation({
22642
22930
  runDirectory: admitted.runDirectory,
@@ -22689,7 +22977,7 @@ async function dispatchAdmittedJudge(input) {
22689
22977
  env.credentials
22690
22978
  );
22691
22979
  if (missingCredential !== void 0) {
22692
- return await presentControlledFailure5(
22980
+ return await presentControlledFailure6(
22693
22981
  admitted,
22694
22982
  missingCredential,
22695
22983
  io
@@ -22722,7 +23010,7 @@ async function dispatchAdmittedJudge(input) {
22722
23010
  ...env.piRunner === void 0 ? {} : { runner: env.piRunner }
22723
23011
  });
22724
23012
  } catch (error) {
22725
- return await presentControlledFailure5(
23013
+ return await presentControlledFailure6(
22726
23014
  admitted,
22727
23015
  {
22728
23016
  timedOut: false,
@@ -22745,7 +23033,7 @@ async function dispatchAdmittedJudge(input) {
22745
23033
  try {
22746
23034
  lawful = await trySettleJudgeTerminalResult(admitted);
22747
23035
  } catch (error) {
22748
- return await presentControlledFailure5(
23036
+ return await presentControlledFailure6(
22749
23037
  admitted,
22750
23038
  {
22751
23039
  timedOut: false,
@@ -22790,7 +23078,7 @@ async function dispatchAdmittedJudge(input) {
22790
23078
  credential: credentialFailure,
22791
23079
  runDirectory: admitted.runDirectory
22792
23080
  });
22793
- return await presentControlledFailure5(
23081
+ return await presentControlledFailure6(
22794
23082
  admitted,
22795
23083
  {
22796
23084
  timedOut: result2.timedOut,
@@ -22983,7 +23271,7 @@ function buildMergerResumeActivationExtraArgs(admitted, options) {
22983
23271
  RESUME_TRANSPORT_ENVELOPE
22984
23272
  ];
22985
23273
  }
22986
- async function presentControlledFailure6(admitted, failureInput, io) {
23274
+ async function presentControlledFailure7(admitted, failureInput, io) {
22987
23275
  const hasThrown = Object.hasOwn(failureInput, "thrown");
22988
23276
  const resumeObservation = await resolveControlledFailureResumeObservation({
22989
23277
  runDirectory: admitted.runDirectory,
@@ -23039,7 +23327,7 @@ async function dispatchAdmittedMerger(input) {
23039
23327
  env.credentials
23040
23328
  );
23041
23329
  if (missingCredential !== void 0) {
23042
- return await presentControlledFailure6(
23330
+ return await presentControlledFailure7(
23043
23331
  admitted,
23044
23332
  missingCredential,
23045
23333
  io
@@ -23070,7 +23358,7 @@ async function dispatchAdmittedMerger(input) {
23070
23358
  ...env.piRunner === void 0 ? {} : { runner: env.piRunner }
23071
23359
  });
23072
23360
  } catch (error) {
23073
- return await presentControlledFailure6(
23361
+ return await presentControlledFailure7(
23074
23362
  admitted,
23075
23363
  {
23076
23364
  timedOut: false,
@@ -23100,7 +23388,7 @@ async function dispatchAdmittedMerger(input) {
23100
23388
  )
23101
23389
  });
23102
23390
  } catch (error) {
23103
- return await presentControlledFailure6(
23391
+ return await presentControlledFailure7(
23104
23392
  admitted,
23105
23393
  {
23106
23394
  timedOut: false,
@@ -23131,7 +23419,7 @@ async function dispatchAdmittedMerger(input) {
23131
23419
  credential: credentialFailure,
23132
23420
  runDirectory: admitted.runDirectory
23133
23421
  });
23134
- return await presentControlledFailure6(
23422
+ return await presentControlledFailure7(
23135
23423
  admitted,
23136
23424
  {
23137
23425
  timedOut: result2.timedOut,
@@ -23237,7 +23525,7 @@ async function runPublicMerger(argv, env, io, parseMergerArgv2) {
23237
23525
  ...env.createRunId === void 0 ? {} : { createRunId: env.createRunId }
23238
23526
  });
23239
23527
  await markRunAdmitted(shell);
23240
- return await presentControlledFailure6(
23528
+ return await presentControlledFailure7(
23241
23529
  shell,
23242
23530
  {
23243
23531
  timedOut: false,
@@ -23268,7 +23556,7 @@ async function runPublicMerger(argv, env, io, parseMergerArgv2) {
23268
23556
  methodMaterial = await loadMergerMethodMaterial(env.packageRoot);
23269
23557
  } catch (error) {
23270
23558
  await lease.release();
23271
- return await presentControlledFailure6(
23559
+ return await presentControlledFailure7(
23272
23560
  admitted,
23273
23561
  {
23274
23562
  timedOut: false,
@@ -23339,7 +23627,7 @@ async function runPublicMergerResume(argv, env, io) {
23339
23627
  methodMaterial = await loadMergerMethodMaterial(env.packageRoot);
23340
23628
  } catch (error) {
23341
23629
  await lease.release();
23342
- return await presentControlledFailure6(
23630
+ return await presentControlledFailure7(
23343
23631
  admitted,
23344
23632
  {
23345
23633
  timedOut: false,
@@ -23399,6 +23687,9 @@ function buildModelArgs7(model) {
23399
23687
  model.thinking
23400
23688
  ];
23401
23689
  }
23690
+ function buildReviewerTicketNumberArgs(ticketNumber) {
23691
+ return ticketNumber === void 0 ? [] : ["--ak-review-ticket-number", String(ticketNumber)];
23692
+ }
23402
23693
  function buildReviewerActivationExtraArgs(admitted, options) {
23403
23694
  const prompt = buildReviewerTransportPrompt(admitted);
23404
23695
  const skillPath = resolvePackagedMethodSkillPath(
@@ -23406,6 +23697,7 @@ function buildReviewerActivationExtraArgs(admitted, options) {
23406
23697
  "code-review"
23407
23698
  );
23408
23699
  const authorityRefArgs = admitted.authorityRefs.length === 0 ? [] : ["--ak-review-authority-refs", JSON.stringify([...admitted.authorityRefs])];
23700
+ const ticketNumberArgs = buildReviewerTicketNumberArgs(admitted.ticketNumber);
23409
23701
  return [
23410
23702
  "--no-skills",
23411
23703
  "--skill",
@@ -23423,6 +23715,7 @@ function buildReviewerActivationExtraArgs(admitted, options) {
23423
23715
  "--ak-review-base",
23424
23716
  admitted.baseRevision,
23425
23717
  ...authorityRefArgs,
23718
+ ...ticketNumberArgs,
23426
23719
  "--mode",
23427
23720
  "json",
23428
23721
  ...buildModelArgs7(options.model),
@@ -23435,6 +23728,7 @@ function buildReviewerResumeActivationExtraArgs(admitted, options) {
23435
23728
  "code-review"
23436
23729
  );
23437
23730
  const authorityRefArgs = admitted.authorityRefs.length === 0 ? [] : ["--ak-review-authority-refs", JSON.stringify([...admitted.authorityRefs])];
23731
+ const ticketNumberArgs = buildReviewerTicketNumberArgs(admitted.ticketNumber);
23438
23732
  return [
23439
23733
  "--no-skills",
23440
23734
  "--skill",
@@ -23452,13 +23746,14 @@ function buildReviewerResumeActivationExtraArgs(admitted, options) {
23452
23746
  "--ak-review-base",
23453
23747
  admitted.baseRevision,
23454
23748
  ...authorityRefArgs,
23749
+ ...ticketNumberArgs,
23455
23750
  "--mode",
23456
23751
  "json",
23457
23752
  ...buildModelArgs7(options.model),
23458
23753
  RESUME_TRANSPORT_ENVELOPE
23459
23754
  ];
23460
23755
  }
23461
- async function presentControlledFailure7(admitted, failureInput, io) {
23756
+ async function presentControlledFailure8(admitted, failureInput, io) {
23462
23757
  const hasThrown = Object.hasOwn(failureInput, "thrown");
23463
23758
  const resumeObservation = await resolveControlledFailureResumeObservation({
23464
23759
  runDirectory: admitted.runDirectory,
@@ -23511,7 +23806,7 @@ async function dispatchAdmittedReviewer(input) {
23511
23806
  env.credentials
23512
23807
  );
23513
23808
  if (missingCredential !== void 0) {
23514
- return await presentControlledFailure7(
23809
+ return await presentControlledFailure8(
23515
23810
  admitted,
23516
23811
  missingCredential,
23517
23812
  io
@@ -23543,7 +23838,7 @@ async function dispatchAdmittedReviewer(input) {
23543
23838
  ...env.piRunner === void 0 ? {} : { runner: env.piRunner }
23544
23839
  });
23545
23840
  } catch (error) {
23546
- return await presentControlledFailure7(
23841
+ return await presentControlledFailure8(
23547
23842
  admitted,
23548
23843
  {
23549
23844
  timedOut: false,
@@ -23573,7 +23868,7 @@ async function dispatchAdmittedReviewer(input) {
23573
23868
  )
23574
23869
  });
23575
23870
  } catch (error) {
23576
- return await presentControlledFailure7(
23871
+ return await presentControlledFailure8(
23577
23872
  admitted,
23578
23873
  {
23579
23874
  timedOut: false,
@@ -23618,7 +23913,7 @@ async function dispatchAdmittedReviewer(input) {
23618
23913
  credential: credentialFailure,
23619
23914
  runDirectory: admitted.runDirectory
23620
23915
  });
23621
- return await presentControlledFailure7(
23916
+ return await presentControlledFailure8(
23622
23917
  admitted,
23623
23918
  {
23624
23919
  timedOut: result2.timedOut,
@@ -23672,7 +23967,7 @@ async function runPublicReviewer(argv, env, io, parseReviewerArgv2) {
23672
23967
  methodMaterial = await loadReviewerMethodMaterial(env.packageRoot);
23673
23968
  } catch (error) {
23674
23969
  await lease.release();
23675
- return await presentControlledFailure7(
23970
+ return await presentControlledFailure8(
23676
23971
  admitted,
23677
23972
  {
23678
23973
  timedOut: false,
@@ -23742,7 +24037,7 @@ async function runPublicReviewerResume(argv, env, io) {
23742
24037
  methodMaterial = await loadReviewerMethodMaterial(env.packageRoot);
23743
24038
  } catch (error) {
23744
24039
  await lease.release();
23745
- return await presentControlledFailure7(
24040
+ return await presentControlledFailure8(
23746
24041
  admitted,
23747
24042
  {
23748
24043
  timedOut: false,
@@ -23784,44 +24079,2102 @@ var init_reviewer_run = __esm({
23784
24079
  }
23785
24080
  });
23786
24081
 
23787
- // src/public-cli/cli.ts
23788
- var cli_exports = {};
23789
- __export(cli_exports, {
23790
- CliUsageError: () => CliUsageError,
23791
- PUBLIC_ROLE_ARGV: () => PUBLIC_ROLE_ARGV,
23792
- buildExplicitInternalActivationArgs: () => buildExplicitInternalActivationArgs,
23793
- helpDocument: () => helpDocument,
23794
- resolveInternalRoleEntrypoint: () => resolveInternalRoleEntrypoint,
23795
- runAkRole: () => runAkRole
23796
- });
23797
- import { realpath as realpath5 } from "node:fs/promises";
23798
- import { homedir as homedir3 } from "node:os";
23799
- import { join as join18 } from "node:path";
23800
- function takePublicGlobalFlag(argv, index) {
23801
- const token = argv[index];
23802
- if (token === void 0) return void 0;
23803
- if (token === "--help" || token === "-h") {
23804
- return { flag: "help", consume: 1 };
23805
- }
23806
- if (token === "--model") {
23807
- const value = argv[index + 1];
23808
- if (value === void 0) {
23809
- return { flag: "model", consume: 1, value: void 0 };
23810
- }
23811
- return { flag: "model", consume: 2, value };
24082
+ // src/atomic-write.ts
24083
+ import { randomUUID as randomUUID2 } from "node:crypto";
24084
+ import { rename, rm, writeFile as writeFile13 } from "node:fs/promises";
24085
+ import { dirname as dirname7, join as join18 } from "node:path";
24086
+ async function writeFileAtomically(destination, contents) {
24087
+ const parent = dirname7(destination);
24088
+ const temporary = join18(parent, `.atomic-write-${randomUUID2()}.tmp`);
24089
+ try {
24090
+ await writeFile13(temporary, contents);
24091
+ await rename(temporary, destination);
24092
+ } catch (error) {
24093
+ await rm(temporary, { force: true }).catch(() => void 0);
24094
+ throw error;
23812
24095
  }
23813
- if (token.startsWith("--model=")) {
23814
- return {
23815
- flag: "model",
23816
- consume: 1,
23817
- value: token.slice("--model=".length)
23818
- };
24096
+ }
24097
+ var init_atomic_write = __esm({
24098
+ "src/atomic-write.ts"() {
24099
+ "use strict";
23819
24100
  }
23820
- if (token === "--thinking") {
23821
- const raw = argv[index + 1];
23822
- if (raw === void 0) {
23823
- return { flag: "thinking", consume: 1, raw: void 0 };
23824
- }
24101
+ });
24102
+
24103
+ // src/taishi-index.ts
24104
+ import { open as open3, readFile as readFile10, unlink as unlink4 } from "node:fs/promises";
24105
+ import { dirname as dirname8, join as join19 } from "node:path";
24106
+ function sleep(ms) {
24107
+ return new Promise((resolve9) => {
24108
+ setTimeout(resolve9, ms);
24109
+ });
24110
+ }
24111
+ async function withTaishiLibraryIndexLock(ledgerHome, fn) {
24112
+ const indexPath = taishiLibraryIndexPath(ledgerHome);
24113
+ ensureRealDirectoryTree(ledgerHome, dirname8(indexPath));
24114
+ const lockPath = join19(dirname8(indexPath), LIBRARY_INDEX_LOCK_NAME);
24115
+ assertLedgerFileInsideHome(lockPath, ledgerHome);
24116
+ const startedAt = Date.now();
24117
+ while (true) {
24118
+ try {
24119
+ const handle = await open3(lockPath, "wx");
24120
+ try {
24121
+ await handle.writeFile(`${process.pid}
24122
+ `, "utf8");
24123
+ return await fn();
24124
+ } finally {
24125
+ await handle.close().catch(() => void 0);
24126
+ await unlink4(lockPath).catch(() => void 0);
24127
+ }
24128
+ } catch (error) {
24129
+ const code = error instanceof Error && "code" in error ? error.code : void 0;
24130
+ if (code !== "EEXIST") throw error;
24131
+ if (Date.now() - startedAt > LIBRARY_INDEX_LOCK_TIMEOUT_MS) {
24132
+ throw new Error(
24133
+ `taishi library-index lock timeout after ${LIBRARY_INDEX_LOCK_TIMEOUT_MS}ms: ${lockPath}`
24134
+ );
24135
+ }
24136
+ await sleep(LIBRARY_INDEX_LOCK_RETRY_MS);
24137
+ }
24138
+ }
24139
+ }
24140
+ function taishiLibraryIndexPath(ledgerHome) {
24141
+ return join19(ledgerHome, "taishi", "library-index.json");
24142
+ }
24143
+ function rowFromIssueMetricsPage(page) {
24144
+ return {
24145
+ projectRoot: page.projectRoot,
24146
+ // exactOptionalPropertyTypes: only materialize when page carries it.
24147
+ ...page.issueNumber === void 0 ? {} : { issueNumber: page.issueNumber },
24148
+ totalElapsedMs: page.totalElapsedMs,
24149
+ changedLines: page.changedLines,
24150
+ msPerKLines: page.msPerKLines,
24151
+ lastActivityAt: page.lastActivityAt
24152
+ };
24153
+ }
24154
+ function sortRows(rows) {
24155
+ return [...rows].sort((a, b) => {
24156
+ const byRoot = a.projectRoot.localeCompare(b.projectRoot);
24157
+ if (byRoot !== 0) return byRoot;
24158
+ const aNum = a.issueNumber;
24159
+ const bNum = b.issueNumber;
24160
+ if (aNum === void 0 && bNum === void 0) return 0;
24161
+ if (aNum === void 0) return 1;
24162
+ if (bNum === void 0) return -1;
24163
+ return aNum - bNum;
24164
+ });
24165
+ }
24166
+ function buildTaishiLibraryIndexPage(rows) {
24167
+ return {
24168
+ kind: "taishi-library-index",
24169
+ rows: sortRows(rows)
24170
+ };
24171
+ }
24172
+ function findTaishiLibraryIndexRow(index, issueNumber) {
24173
+ if (index === void 0) return void 0;
24174
+ return index.rows.find((row) => row.issueNumber === issueNumber);
24175
+ }
24176
+ function upsertTaishiLibraryIndexRows(existing, upserts) {
24177
+ const byRoot = /* @__PURE__ */ new Map();
24178
+ const rootByIssue = /* @__PURE__ */ new Map();
24179
+ const ingest = (row) => {
24180
+ if (row.issueNumber !== void 0) {
24181
+ const priorRoot = rootByIssue.get(row.issueNumber);
24182
+ if (priorRoot !== void 0 && priorRoot !== row.projectRoot) {
24183
+ byRoot.delete(priorRoot);
24184
+ }
24185
+ }
24186
+ const prior = byRoot.get(row.projectRoot);
24187
+ if (prior !== void 0 && prior.issueNumber !== void 0 && prior.issueNumber !== row.issueNumber) {
24188
+ rootByIssue.delete(prior.issueNumber);
24189
+ }
24190
+ byRoot.set(row.projectRoot, row);
24191
+ if (row.issueNumber !== void 0) {
24192
+ rootByIssue.set(row.issueNumber, row.projectRoot);
24193
+ }
24194
+ };
24195
+ if (existing !== void 0) {
24196
+ for (const row of existing.rows) {
24197
+ ingest(row);
24198
+ }
24199
+ }
24200
+ for (const row of upserts) {
24201
+ ingest(row);
24202
+ }
24203
+ return buildTaishiLibraryIndexPage([...byRoot.values()]);
24204
+ }
24205
+ async function readTaishiLibraryIndexPage(ledgerHome) {
24206
+ const path = taishiLibraryIndexPath(ledgerHome);
24207
+ let raw;
24208
+ try {
24209
+ raw = await readFile10(path, "utf8");
24210
+ } catch (error) {
24211
+ if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
24212
+ return void 0;
24213
+ }
24214
+ throw error;
24215
+ }
24216
+ return JSON.parse(raw);
24217
+ }
24218
+ async function writeTaishiLibraryIndexPage(ledgerHome, page) {
24219
+ const path = taishiLibraryIndexPath(ledgerHome);
24220
+ ensureRealDirectoryTree(ledgerHome, dirname8(path));
24221
+ assertLedgerFileInsideHome(path, ledgerHome);
24222
+ await writeFileAtomically(path, `${JSON.stringify(page, null, 2)}
24223
+ `);
24224
+ return path;
24225
+ }
24226
+ async function mergeTaishiLibraryIndexRows(ledgerHome, upserts) {
24227
+ return withTaishiLibraryIndexLock(ledgerHome, async () => {
24228
+ const existing = await readTaishiLibraryIndexPage(ledgerHome);
24229
+ const index = upsertTaishiLibraryIndexRows(existing, upserts);
24230
+ const indexPath = await writeTaishiLibraryIndexPage(ledgerHome, index);
24231
+ return { index, indexPath };
24232
+ });
24233
+ }
24234
+ var LIBRARY_INDEX_LOCK_NAME, LIBRARY_INDEX_LOCK_TIMEOUT_MS, LIBRARY_INDEX_LOCK_RETRY_MS;
24235
+ var init_taishi_index = __esm({
24236
+ "src/taishi-index.ts"() {
24237
+ "use strict";
24238
+ init_atomic_write();
24239
+ init_activation_ledger_topology();
24240
+ LIBRARY_INDEX_LOCK_NAME = ".library-index.lock";
24241
+ LIBRARY_INDEX_LOCK_TIMEOUT_MS = 3e4;
24242
+ LIBRARY_INDEX_LOCK_RETRY_MS = 15;
24243
+ }
24244
+ });
24245
+
24246
+ // src/taishi-median.ts
24247
+ function medianNumber(values) {
24248
+ if (values.length === 0) return void 0;
24249
+ const sorted = [...values].sort((a, b) => a - b);
24250
+ const mid = Math.floor(sorted.length / 2);
24251
+ if (sorted.length % 2 === 1) {
24252
+ return sorted[mid];
24253
+ }
24254
+ return (sorted[mid - 1] + sorted[mid]) / 2;
24255
+ }
24256
+ var init_taishi_median = __esm({
24257
+ "src/taishi-median.ts"() {
24258
+ "use strict";
24259
+ }
24260
+ });
24261
+
24262
+ // src/taishi-cohort.ts
24263
+ function presentMetric(value) {
24264
+ return { status: "present", value };
24265
+ }
24266
+ function rateMetric(numerator, denominator) {
24267
+ if (denominator === 0) return ABSENT;
24268
+ return presentMetric(numerator / denominator);
24269
+ }
24270
+ function optionalMedian(values) {
24271
+ const median = medianNumber(values);
24272
+ return median === void 0 ? ABSENT : presentMetric(median);
24273
+ }
24274
+ function emptyRoleAccum() {
24275
+ return {
24276
+ convergenceRounds: [],
24277
+ firstPassLaneCount: 0,
24278
+ appearanceLaneCount: 0,
24279
+ successCount: 0,
24280
+ successEligibleCount: 0
24281
+ };
24282
+ }
24283
+ function absorbRole(accum, stats) {
24284
+ accum.convergenceRounds.push(...stats.convergenceRounds);
24285
+ accum.firstPassLaneCount += stats.firstPassLaneCount;
24286
+ accum.appearanceLaneCount += stats.appearanceLaneCount;
24287
+ accum.successCount += stats.successCount;
24288
+ accum.successEligibleCount += stats.successEligibleCount;
24289
+ }
24290
+ function finishRole(role, accum) {
24291
+ return {
24292
+ role,
24293
+ convergenceRounds: accum.convergenceRounds,
24294
+ convergenceRoundsMedian: optionalMedian(accum.convergenceRounds),
24295
+ firstPassRate: rateMetric(accum.firstPassLaneCount, accum.appearanceLaneCount),
24296
+ successRate: rateMetric(accum.successCount, accum.successEligibleCount)
24297
+ };
24298
+ }
24299
+ async function aggregateGroup(index, input, ensureIssuePage) {
24300
+ const issueEntries = [];
24301
+ const roleAccums = /* @__PURE__ */ new Map();
24302
+ let reworkWallMs = 0;
24303
+ let totalWallMs = 0;
24304
+ let hasReworkSample = false;
24305
+ const legWalls = [];
24306
+ for (const issueNumber of input.issues) {
24307
+ const row = findTaishiLibraryIndexRow(index, issueNumber);
24308
+ if (row === void 0) {
24309
+ issueEntries.push({ issueNumber, status: "absent" });
24310
+ continue;
24311
+ }
24312
+ const page = await ensureIssuePage({
24313
+ projectRoot: row.projectRoot,
24314
+ issueNumber
24315
+ });
24316
+ issueEntries.push({
24317
+ issueNumber,
24318
+ status: "present",
24319
+ projectRoot: row.projectRoot
24320
+ });
24321
+ const acceptance = page.acceptanceSuccessRework;
24322
+ if (acceptance !== void 0) {
24323
+ for (const roleStats of acceptance.byRole) {
24324
+ const accum = roleAccums.get(roleStats.role) ?? emptyRoleAccum();
24325
+ absorbRole(accum, roleStats);
24326
+ roleAccums.set(roleStats.role, accum);
24327
+ }
24328
+ reworkWallMs += acceptance.rework.reworkWallMs;
24329
+ totalWallMs += acceptance.rework.totalWallMs;
24330
+ hasReworkSample = true;
24331
+ }
24332
+ const legWallClock = page.legWallClock;
24333
+ if (legWallClock !== void 0) {
24334
+ for (const leg of legWallClock.ranking) {
24335
+ legWalls.push(leg.wallMs);
24336
+ }
24337
+ }
24338
+ }
24339
+ const byRole = [...roleAccums.keys()].sort((a, b) => a.localeCompare(b)).map((role) => finishRole(role, roleAccums.get(role)));
24340
+ return {
24341
+ groupLabel: input.groupLabel,
24342
+ issues: issueEntries,
24343
+ byRole,
24344
+ reworkRatio: hasReworkSample ? rateMetric(reworkWallMs, totalWallMs) : ABSENT,
24345
+ medianWallMs: optionalMedian(legWalls)
24346
+ };
24347
+ }
24348
+ async function runTaishiCohortMode(ledgerHome, input, ensureIssuePage) {
24349
+ const index = await readTaishiLibraryIndexPage(ledgerHome);
24350
+ const group0 = await aggregateGroup(index, input.groups[0], ensureIssuePage);
24351
+ const group1 = await aggregateGroup(index, input.groups[1], ensureIssuePage);
24352
+ return {
24353
+ mode: "cohort",
24354
+ groups: [group0, group1]
24355
+ };
24356
+ }
24357
+ var ABSENT;
24358
+ var init_taishi_cohort = __esm({
24359
+ "src/taishi-cohort.ts"() {
24360
+ "use strict";
24361
+ init_taishi_index();
24362
+ init_taishi_median();
24363
+ ABSENT = { status: "absent" };
24364
+ }
24365
+ });
24366
+
24367
+ // src/ledger-session-read.ts
24368
+ import { readFile as readFile11 } from "node:fs/promises";
24369
+ function isRecord6(value) {
24370
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24371
+ }
24372
+ async function readLedgerSessionJsonl(path) {
24373
+ const text = await readFile11(path, "utf8");
24374
+ const lines = text.split("\n");
24375
+ const rows = [];
24376
+ for (let index = 0; index < lines.length; index += 1) {
24377
+ const line2 = lines[index];
24378
+ if (!line2.trim()) continue;
24379
+ let row;
24380
+ try {
24381
+ row = JSON.parse(line2);
24382
+ } catch (error) {
24383
+ if (!(error instanceof SyntaxError)) throw error;
24384
+ const completedByTerminator = index < lines.length - 1;
24385
+ if (completedByTerminator) {
24386
+ throw new LedgerSessionJsonlError(
24387
+ `malformed JSONL record in ${path} at line ${index + 1}: ${error.message}`,
24388
+ { path, line: index + 1, prefixRows: rows }
24389
+ );
24390
+ }
24391
+ break;
24392
+ }
24393
+ if (!isRecord6(row)) {
24394
+ const kind = row === null ? "null" : Array.isArray(row) ? "array" : typeof row;
24395
+ throw new LedgerSessionJsonlError(
24396
+ `complete non-object JSONL record in ${path} at line ${index + 1}: expected object, got ${kind}`,
24397
+ { path, line: index + 1, prefixRows: rows }
24398
+ );
24399
+ }
24400
+ rows.push(row);
24401
+ }
24402
+ return rows;
24403
+ }
24404
+ function extractSessionTimestampSpan(rows) {
24405
+ let startedAt;
24406
+ let endedAt;
24407
+ for (const row of rows) {
24408
+ if (typeof row.timestamp !== "string" || !row.timestamp) continue;
24409
+ if (startedAt === void 0) startedAt = row.timestamp;
24410
+ endedAt = row.timestamp;
24411
+ }
24412
+ return {
24413
+ ...startedAt !== void 0 ? { startedAt } : {},
24414
+ ...endedAt !== void 0 ? { endedAt } : {}
24415
+ };
24416
+ }
24417
+ function extractSessionModelSequence(rows) {
24418
+ const seen = /* @__PURE__ */ new Set();
24419
+ const ordered = [];
24420
+ const push = (raw) => {
24421
+ const model = raw.trim();
24422
+ if (model === "" || seen.has(model)) return;
24423
+ seen.add(model);
24424
+ ordered.push(model);
24425
+ };
24426
+ for (const row of rows) {
24427
+ if (row.type === "model_change" && typeof row.modelId === "string") {
24428
+ push(row.modelId);
24429
+ }
24430
+ const message = isRecord6(row.message) ? row.message : void 0;
24431
+ if (message?.role === "assistant" && typeof message.model === "string") {
24432
+ push(message.model);
24433
+ }
24434
+ }
24435
+ return ordered;
24436
+ }
24437
+ function bashCommandFirstLine(command) {
24438
+ const match = /^[^\r\n]*/.exec(command);
24439
+ return match?.[0] ?? "";
24440
+ }
24441
+ function extractSessionToolIntervals(rows) {
24442
+ const order = [];
24443
+ const openById = /* @__PURE__ */ new Map();
24444
+ for (const row of rows) {
24445
+ const rowTimestamp = typeof row.timestamp === "string" ? row.timestamp : void 0;
24446
+ const message = isRecord6(row.message) ? row.message : void 0;
24447
+ if (message?.role === "assistant" && Array.isArray(message.content)) {
24448
+ const callTimestamp = typeof message.timestamp === "string" && message.timestamp ? message.timestamp : rowTimestamp;
24449
+ for (const part of message.content) {
24450
+ if (!isRecord6(part) || part.type !== "toolCall") continue;
24451
+ if (typeof part.id !== "string" || part.id.length === 0) {
24452
+ throw new Error("toolCall frame missing string id");
24453
+ }
24454
+ if (typeof part.name !== "string" || part.name.length === 0) {
24455
+ throw new Error(`toolCall ${part.id} missing string name`);
24456
+ }
24457
+ if (callTimestamp === void 0 || callTimestamp.length === 0) {
24458
+ throw new Error(`toolCall ${part.id} missing timestamp`);
24459
+ }
24460
+ if (openById.has(part.id)) {
24461
+ throw new Error(`duplicate toolCall id ${part.id}`);
24462
+ }
24463
+ const args = isRecord6(part.arguments) ? part.arguments : void 0;
24464
+ const command = part.name === "bash" && args !== void 0 && typeof args.command === "string" ? bashCommandFirstLine(args.command) : void 0;
24465
+ const interval = {
24466
+ toolCallId: part.id,
24467
+ toolName: part.name,
24468
+ startedAt: callTimestamp,
24469
+ ...command !== void 0 ? { command } : {}
24470
+ };
24471
+ order.push(interval);
24472
+ openById.set(part.id, interval);
24473
+ }
24474
+ }
24475
+ if (message?.role === "toolResult") {
24476
+ if (typeof message.toolCallId !== "string" || message.toolCallId.length === 0) {
24477
+ throw new Error("toolResult frame missing string toolCallId");
24478
+ }
24479
+ const resultTimestamp = typeof message.timestamp === "string" && message.timestamp ? message.timestamp : rowTimestamp;
24480
+ if (resultTimestamp === void 0 || resultTimestamp.length === 0) {
24481
+ throw new Error(`toolResult ${message.toolCallId} missing timestamp`);
24482
+ }
24483
+ const open4 = openById.get(message.toolCallId);
24484
+ if (open4 === void 0) {
24485
+ const toolName = typeof message.toolName === "string" && message.toolName.length > 0 ? message.toolName : "unknown";
24486
+ order.push({
24487
+ toolCallId: message.toolCallId,
24488
+ toolName,
24489
+ startedAt: resultTimestamp,
24490
+ endedAt: resultTimestamp
24491
+ });
24492
+ continue;
24493
+ }
24494
+ if (open4.endedAt !== void 0) {
24495
+ throw new Error(`duplicate toolResult for toolCallId ${message.toolCallId}`);
24496
+ }
24497
+ open4.endedAt = resultTimestamp;
24498
+ }
24499
+ }
24500
+ return order.map((interval) => {
24501
+ const base = {
24502
+ toolCallId: interval.toolCallId,
24503
+ toolName: interval.toolName,
24504
+ startedAt: interval.startedAt,
24505
+ ...interval.command !== void 0 ? { command: interval.command } : {}
24506
+ };
24507
+ return interval.endedAt === void 0 ? base : { ...base, endedAt: interval.endedAt };
24508
+ });
24509
+ }
24510
+ var LedgerSessionJsonlError;
24511
+ var init_ledger_session_read = __esm({
24512
+ "src/ledger-session-read.ts"() {
24513
+ "use strict";
24514
+ LedgerSessionJsonlError = class extends Error {
24515
+ path;
24516
+ line;
24517
+ prefixRows;
24518
+ constructor(message, init) {
24519
+ super(message);
24520
+ this.name = "LedgerSessionJsonlError";
24521
+ this.path = init.path;
24522
+ this.line = init.line;
24523
+ this.prefixRows = init.prefixRows;
24524
+ }
24525
+ };
24526
+ }
24527
+ });
24528
+
24529
+ // src/run-terminal-artifacts.ts
24530
+ import { readdir as readdir4, readFile as readFile12 } from "node:fs/promises";
24531
+ import { basename as basename4, dirname as dirname9, join as join20 } from "node:path";
24532
+ function isMissingPathError3(error) {
24533
+ return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
24534
+ }
24535
+ function errorText2(error) {
24536
+ return error instanceof Error ? error.message : String(error);
24537
+ }
24538
+ function isRecord7(value) {
24539
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24540
+ }
24541
+ function readUsableTerminalArtifactBody(body) {
24542
+ if (body === null) {
24543
+ return { ok: false, reason: "terminal artifact JSON value is null" };
24544
+ }
24545
+ if (!isRecord7(body)) {
24546
+ return {
24547
+ ok: false,
24548
+ reason: `terminal artifact JSON value is not a typed object (${Array.isArray(body) ? "array" : typeof body})`
24549
+ };
24550
+ }
24551
+ if (typeof body.role !== "string" || body.role.trim() === "") {
24552
+ return {
24553
+ ok: false,
24554
+ reason: "terminal artifact missing nonblank producer-owned role field"
24555
+ };
24556
+ }
24557
+ return { ok: true, body };
24558
+ }
24559
+ async function readTerminalArtifactAtPath(path, file) {
24560
+ let raw;
24561
+ try {
24562
+ raw = await readFile12(path, "utf8");
24563
+ } catch (error) {
24564
+ if (isMissingPathError3(error)) return void 0;
24565
+ return {
24566
+ status: "unreadable",
24567
+ file,
24568
+ path,
24569
+ reason: errorText2(error)
24570
+ };
24571
+ }
24572
+ let parsed;
24573
+ try {
24574
+ parsed = JSON.parse(raw);
24575
+ } catch (error) {
24576
+ return {
24577
+ status: "unreadable",
24578
+ file,
24579
+ path,
24580
+ reason: error instanceof Error ? error.message : `terminal artifact JSON parse failed: ${String(error)}`
24581
+ };
24582
+ }
24583
+ const usable = readUsableTerminalArtifactBody(parsed);
24584
+ if (!usable.ok) {
24585
+ return {
24586
+ status: "unreadable",
24587
+ file,
24588
+ path,
24589
+ reason: usable.reason
24590
+ };
24591
+ }
24592
+ return { status: "present", file, path, body: usable.body };
24593
+ }
24594
+ async function listUniqueErrorFallbackPaths(directories) {
24595
+ const found = [];
24596
+ for (const dir of directories) {
24597
+ let names;
24598
+ try {
24599
+ names = await readdir4(dir);
24600
+ } catch (error) {
24601
+ if (isMissingPathError3(error)) continue;
24602
+ throw error;
24603
+ }
24604
+ for (const name of names.sort((a, b) => a.localeCompare(b))) {
24605
+ if (!UNIQUE_ERROR_FALLBACK_NAME.test(name)) continue;
24606
+ found.push(join20(dir, name));
24607
+ }
24608
+ }
24609
+ return found;
24610
+ }
24611
+ function runIdFromRunDirectory(runDirectory) {
24612
+ const name = basename4(runDirectory);
24613
+ const at = name.lastIndexOf("@");
24614
+ if (at <= 0 || at === name.length - 1) return void 0;
24615
+ return name.slice(0, at);
24616
+ }
24617
+ function presentUniqueFallbackBoundToRun(body, expectedRunId) {
24618
+ if (expectedRunId === void 0) return false;
24619
+ return typeof body.runId === "string" && body.runId === expectedRunId;
24620
+ }
24621
+ async function readRunTerminalArtifact(runDirectory) {
24622
+ const artifactsDir = join20(runDirectory, "artifacts");
24623
+ for (const file of RUN_TERMINAL_ARTIFACT_FILES) {
24624
+ const path = join20(artifactsDir, file);
24625
+ const read3 = await readTerminalArtifactAtPath(path, file);
24626
+ if (read3 !== void 0) return read3;
24627
+ }
24628
+ for (const relative3 of RUN_TERMINAL_ERROR_FALLBACK_RELATIVE_PATHS) {
24629
+ const path = join20(runDirectory, relative3);
24630
+ const read3 = await readTerminalArtifactAtPath(path, "error.json");
24631
+ if (read3 !== void 0) return read3;
24632
+ }
24633
+ for (const path of await listUniqueErrorFallbackPaths([artifactsDir, runDirectory])) {
24634
+ const read3 = await readTerminalArtifactAtPath(path, "error.json");
24635
+ if (read3 !== void 0) return read3;
24636
+ }
24637
+ const expectedRunId = runIdFromRunDirectory(runDirectory);
24638
+ for (const path of await listUniqueErrorFallbackPaths([dirname9(runDirectory)])) {
24639
+ const read3 = await readTerminalArtifactAtPath(path, "error.json");
24640
+ if (read3 === void 0) continue;
24641
+ if (read3.status === "present") {
24642
+ if (!presentUniqueFallbackBoundToRun(read3.body, expectedRunId)) continue;
24643
+ return read3;
24644
+ }
24645
+ }
24646
+ return { status: "absent" };
24647
+ }
24648
+ var RUN_TERMINAL_ARTIFACT_FILES, RUN_TERMINAL_ERROR_FALLBACK_RELATIVE_PATHS, UNIQUE_ERROR_FALLBACK_NAME;
24649
+ var init_run_terminal_artifacts = __esm({
24650
+ "src/run-terminal-artifacts.ts"() {
24651
+ "use strict";
24652
+ RUN_TERMINAL_ARTIFACT_FILES = [
24653
+ "report.json",
24654
+ "error.json",
24655
+ "audit-incomplete.json"
24656
+ ];
24657
+ RUN_TERMINAL_ERROR_FALLBACK_RELATIVE_PATHS = [
24658
+ "artifacts/error.settlement.json",
24659
+ "error.settlement.json"
24660
+ ];
24661
+ UNIQUE_ERROR_FALLBACK_NAME = /^error\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.json$/i;
24662
+ }
24663
+ });
24664
+
24665
+ // src/taishi-ledger.ts
24666
+ import { readdir as readdir5, readFile as readFile13 } from "node:fs/promises";
24667
+ import { join as join21 } from "node:path";
24668
+ function isMissingPathError4(error) {
24669
+ return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
24670
+ }
24671
+ function errorText3(error) {
24672
+ return error instanceof Error ? error.message : String(error);
24673
+ }
24674
+ function isRecord8(value) {
24675
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24676
+ }
24677
+ async function readExistingRunLifecycleState(runDirectory) {
24678
+ try {
24679
+ const raw = JSON.parse(
24680
+ await readFile13(join21(runDirectory, "run-state.json"), "utf8")
24681
+ );
24682
+ if (!isRecord8(raw) || typeof raw.state !== "string") return void 0;
24683
+ return raw.state;
24684
+ } catch {
24685
+ return void 0;
24686
+ }
24687
+ }
24688
+ function parseRunDirectoryName(name) {
24689
+ const at = name.lastIndexOf("@");
24690
+ if (at <= 0 || at === name.length - 1) return void 0;
24691
+ return { runId: name.slice(0, at), role: name.slice(at + 1) };
24692
+ }
24693
+ async function readInvocationScopeFields(runDirectory) {
24694
+ let raw;
24695
+ try {
24696
+ raw = await readFile13(join21(runDirectory, "invocation.json"), "utf8");
24697
+ } catch (error) {
24698
+ if (isMissingPathError4(error)) return void 0;
24699
+ throw error;
24700
+ }
24701
+ const parsed = JSON.parse(raw);
24702
+ if (!isRecord8(parsed)) return void 0;
24703
+ if (typeof parsed.projectRoot !== "string" || parsed.projectRoot.trim() === "") {
24704
+ return void 0;
24705
+ }
24706
+ const projectRoot = parsed.projectRoot;
24707
+ if (typeof parsed.ticketNumber === "number" && Number.isInteger(parsed.ticketNumber) && parsed.ticketNumber >= 1) {
24708
+ return { projectRoot, ticketNumber: parsed.ticketNumber };
24709
+ }
24710
+ return { projectRoot };
24711
+ }
24712
+ function decideIssueScope(input) {
24713
+ const projectRootMatch = input.runProjectRootIdentity === input.scopeProjectRootIdentity;
24714
+ if (input.scopeTicketNumber !== void 0 && input.runTicketNumber !== void 0) {
24715
+ if (input.runTicketNumber === input.scopeTicketNumber) {
24716
+ return { inScope: true, conflict: !projectRootMatch };
24717
+ }
24718
+ return { inScope: false, conflict: false };
24719
+ }
24720
+ return { inScope: projectRootMatch, conflict: false };
24721
+ }
24722
+ async function resolveSessionFile(runDirectory) {
24723
+ try {
24724
+ const raw = await readFile13(join21(runDirectory, "invocation.json"), "utf8");
24725
+ const parsed = JSON.parse(raw);
24726
+ if (isRecord8(parsed) && typeof parsed.sessionFile === "string" && parsed.sessionFile.trim() !== "") {
24727
+ return parsed.sessionFile;
24728
+ }
24729
+ } catch (error) {
24730
+ if (!isMissingPathError4(error)) throw error;
24731
+ }
24732
+ return join21(runDirectory, "session", "session.jsonl");
24733
+ }
24734
+ async function classifyScopedRun(input) {
24735
+ const missingSources = [];
24736
+ const reasons = [];
24737
+ let frameSpan;
24738
+ let toolIntervals;
24739
+ let terminal;
24740
+ let models = [];
24741
+ let partialFirstFrameAt = { status: "absent" };
24742
+ let partialLastFrameAt = { status: "absent" };
24743
+ const sessionFile = await resolveSessionFile(input.runDirectory);
24744
+ let rows;
24745
+ try {
24746
+ rows = await readLedgerSessionJsonl(sessionFile);
24747
+ models = extractSessionModelSequence(rows);
24748
+ const span = extractSessionTimestampSpan(rows);
24749
+ if (span.startedAt === void 0 || span.endedAt === void 0) {
24750
+ missingSources.push("session-timeline");
24751
+ reasons.push("session timeline has no usable timestamps");
24752
+ if (span.startedAt !== void 0) {
24753
+ partialFirstFrameAt = { status: "present", at: span.startedAt };
24754
+ }
24755
+ if (span.endedAt !== void 0) {
24756
+ partialLastFrameAt = { status: "present", at: span.endedAt };
24757
+ }
24758
+ } else {
24759
+ const startedMs = Date.parse(span.startedAt);
24760
+ const endedMs = Date.parse(span.endedAt);
24761
+ if (!Number.isFinite(startedMs) || !Number.isFinite(endedMs)) {
24762
+ missingSources.push("session-timeline");
24763
+ reasons.push("session timeline timestamps are not parseable instants");
24764
+ partialFirstFrameAt = { status: "present", at: span.startedAt };
24765
+ partialLastFrameAt = { status: "present", at: span.endedAt };
24766
+ } else if (endedMs < startedMs) {
24767
+ missingSources.push("session-timeline");
24768
+ reasons.push("session timeline end is earlier than start");
24769
+ partialFirstFrameAt = { status: "present", at: span.startedAt };
24770
+ partialLastFrameAt = { status: "present", at: span.endedAt };
24771
+ } else {
24772
+ frameSpan = { startedAt: span.startedAt, endedAt: span.endedAt };
24773
+ }
24774
+ }
24775
+ } catch (error) {
24776
+ missingSources.push("session-timeline");
24777
+ reasons.push(errorText3(error));
24778
+ if (error instanceof LedgerSessionJsonlError) {
24779
+ const span = extractSessionTimestampSpan(error.prefixRows);
24780
+ if (span.startedAt !== void 0) {
24781
+ partialFirstFrameAt = { status: "present", at: span.startedAt };
24782
+ }
24783
+ if (span.endedAt !== void 0) {
24784
+ partialLastFrameAt = { status: "present", at: span.endedAt };
24785
+ }
24786
+ models = extractSessionModelSequence(error.prefixRows);
24787
+ }
24788
+ }
24789
+ if (rows !== void 0 && !missingSources.includes("session-timeline")) {
24790
+ try {
24791
+ toolIntervals = extractSessionToolIntervals(rows);
24792
+ } catch (error) {
24793
+ missingSources.push("tool-association");
24794
+ reasons.push(errorText3(error));
24795
+ }
24796
+ }
24797
+ try {
24798
+ const artifact = await readRunTerminalArtifact(input.runDirectory);
24799
+ if (artifact.status === "unreadable") {
24800
+ missingSources.push("terminal-artifact");
24801
+ reasons.push(`${artifact.file}: ${artifact.reason}`);
24802
+ } else if (artifact.status === "absent") {
24803
+ const lifecycle = await readExistingRunLifecycleState(input.runDirectory);
24804
+ if (lifecycle !== void 0 && LIVE_RUN_STATES.has(lifecycle)) {
24805
+ return { kind: "live" };
24806
+ }
24807
+ terminal = { status: "absent" };
24808
+ } else {
24809
+ terminal = {
24810
+ status: "present",
24811
+ file: artifact.file,
24812
+ body: artifact.body,
24813
+ role: artifact.body.role
24814
+ };
24815
+ }
24816
+ } catch (error) {
24817
+ missingSources.push("terminal-artifact");
24818
+ reasons.push(errorText3(error));
24819
+ }
24820
+ if (missingSources.length > 0) {
24821
+ const firstFrameAt = frameSpan !== void 0 ? { status: "present", at: frameSpan.startedAt } : partialFirstFrameAt;
24822
+ const lastFrameAt = frameSpan !== void 0 ? { status: "present", at: frameSpan.endedAt } : partialLastFrameAt;
24823
+ return {
24824
+ kind: "unreadable",
24825
+ entry: {
24826
+ runId: input.runId,
24827
+ book: input.book,
24828
+ missingSources,
24829
+ reason: reasons.join("; "),
24830
+ firstFrameAt,
24831
+ lastFrameAt
24832
+ }
24833
+ };
24834
+ }
24835
+ if (frameSpan === void 0 || toolIntervals === void 0 || terminal === void 0) {
24836
+ throw new Error(
24837
+ `classifyScopedRun internal invariant: missing retained facts for ${input.runId}`
24838
+ );
24839
+ }
24840
+ return {
24841
+ kind: "readable",
24842
+ facts: {
24843
+ runId: input.runId,
24844
+ book: input.book,
24845
+ role: input.role,
24846
+ frameSpan,
24847
+ toolIntervals,
24848
+ terminal,
24849
+ models
24850
+ }
24851
+ };
24852
+ }
24853
+ async function scanTaishiIssueRuns(input) {
24854
+ const ledgerHome = resolveActivationLedgerHome();
24855
+ const scopeIdentity = physicalPathIdentity(input.projectRoot);
24856
+ const scopeTicketNumber = input.ticketNumber;
24857
+ const booksRoot = join21(ledgerHome, "books");
24858
+ let bookNames;
24859
+ try {
24860
+ const entries = await readdir5(booksRoot, { withFileTypes: true });
24861
+ bookNames = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
24862
+ } catch (error) {
24863
+ if (isMissingPathError4(error)) {
24864
+ return { runs: [], unreadable: [], scopeConflicts: [] };
24865
+ }
24866
+ throw error;
24867
+ }
24868
+ const runs = [];
24869
+ const unreadable = [];
24870
+ const scopeConflicts = [];
24871
+ for (const book of bookNames) {
24872
+ const runsDir = join21(booksRoot, book, "runs");
24873
+ let runNames;
24874
+ try {
24875
+ const entries = await readdir5(runsDir, { withFileTypes: true });
24876
+ runNames = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
24877
+ } catch (error) {
24878
+ if (isMissingPathError4(error)) continue;
24879
+ throw error;
24880
+ }
24881
+ for (const runName of runNames) {
24882
+ const parsed = parseRunDirectoryName(runName);
24883
+ if (parsed === void 0) continue;
24884
+ const runDirectory = join21(runsDir, runName);
24885
+ let scopeFields;
24886
+ try {
24887
+ scopeFields = await readInvocationScopeFields(runDirectory);
24888
+ } catch (error) {
24889
+ if (error instanceof SyntaxError) continue;
24890
+ throw error;
24891
+ }
24892
+ if (scopeFields === void 0) continue;
24893
+ const runProjectRootIdentity = physicalPathIdentity(scopeFields.projectRoot);
24894
+ const decision = decideIssueScope({
24895
+ scopeProjectRootIdentity: scopeIdentity,
24896
+ scopeTicketNumber,
24897
+ runProjectRootIdentity,
24898
+ runTicketNumber: scopeFields.ticketNumber
24899
+ });
24900
+ if (!decision.inScope) continue;
24901
+ if (decision.conflict) {
24902
+ scopeConflicts.push({
24903
+ runId: parsed.runId,
24904
+ ticketNumber: scopeFields.ticketNumber,
24905
+ projectRoot: runProjectRootIdentity,
24906
+ fact: "typed-ticketNumber-over-projectRoot"
24907
+ });
24908
+ }
24909
+ const classified = await classifyScopedRun({
24910
+ book,
24911
+ runId: parsed.runId,
24912
+ role: parsed.role,
24913
+ runDirectory
24914
+ });
24915
+ if (classified.kind === "readable") runs.push(classified.facts);
24916
+ else if (classified.kind === "unreadable") unreadable.push(classified.entry);
24917
+ }
24918
+ }
24919
+ return {
24920
+ runs,
24921
+ unreadable,
24922
+ scopeConflicts
24923
+ };
24924
+ }
24925
+ var LIVE_RUN_STATES;
24926
+ var init_taishi_ledger = __esm({
24927
+ "src/taishi-ledger.ts"() {
24928
+ "use strict";
24929
+ init_activation_ledger_topology();
24930
+ init_ledger_session_read();
24931
+ init_run_terminal_artifacts();
24932
+ LIVE_RUN_STATES = /* @__PURE__ */ new Set(["admitted", "running", "resumable"]);
24933
+ }
24934
+ });
24935
+
24936
+ // src/taishi-metric-families/acceptance-success-rework.ts
24937
+ function isRecord9(value) {
24938
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24939
+ }
24940
+ function wallMsFromSpan(span) {
24941
+ return Date.parse(span.endedAt) - Date.parse(span.startedAt);
24942
+ }
24943
+ function findCollectorGroups(body) {
24944
+ if (Array.isArray(body.groups)) return body.groups;
24945
+ const receipt = body.receipt;
24946
+ if (isRecord9(receipt) && Array.isArray(receipt.groups)) return receipt.groups;
24947
+ const outcome = body.outcome;
24948
+ if (isRecord9(outcome)) {
24949
+ const facts = outcome.decisiveFacts;
24950
+ if (isRecord9(facts) && Array.isArray(facts.groups)) return facts.groups;
24951
+ }
24952
+ return void 0;
24953
+ }
24954
+ function extractStatus(body) {
24955
+ const outcome = body.outcome;
24956
+ if (isRecord9(outcome) && typeof outcome.status === "string" && outcome.status.trim() !== "") {
24957
+ return outcome.status;
24958
+ }
24959
+ const receipt = body.receipt;
24960
+ if (isRecord9(receipt) && typeof receipt.status === "string" && receipt.status.trim() !== "") {
24961
+ return receipt.status;
24962
+ }
24963
+ if (typeof body.status === "string" && body.status.trim() !== "") {
24964
+ return body.status;
24965
+ }
24966
+ return void 0;
24967
+ }
24968
+ function mapTerminal(role, terminal) {
24969
+ if (terminal.status === "absent") {
24970
+ return {
24971
+ terminalLabel: "no-receipt",
24972
+ accepted: false,
24973
+ success: false,
24974
+ successEligible: false,
24975
+ noReceipt: true
24976
+ };
24977
+ }
24978
+ const body = terminal.body;
24979
+ if (role === "collector") {
24980
+ const groups = findCollectorGroups(body);
24981
+ if (Array.isArray(groups)) {
24982
+ return {
24983
+ terminalLabel: "groups",
24984
+ accepted: true,
24985
+ success: true,
24986
+ successEligible: true,
24987
+ noReceipt: false
24988
+ };
24989
+ }
24990
+ return {
24991
+ terminalLabel: "non-accepted",
24992
+ accepted: false,
24993
+ success: false,
24994
+ successEligible: false,
24995
+ noReceipt: false
24996
+ };
24997
+ }
24998
+ const status = extractStatus(body);
24999
+ if (status === void 0) {
25000
+ return {
25001
+ terminalLabel: "non-accepted",
25002
+ accepted: false,
25003
+ success: false,
25004
+ successEligible: false,
25005
+ noReceipt: false
25006
+ };
25007
+ }
25008
+ const acceptedSet = ACCEPTED_STATUS[role];
25009
+ if (acceptedSet === void 0 || !acceptedSet.has(status)) {
25010
+ return {
25011
+ terminalLabel: status,
25012
+ accepted: false,
25013
+ success: false,
25014
+ successEligible: false,
25015
+ noReceipt: false
25016
+ };
25017
+ }
25018
+ const plannedDuty = WORKER_ROLES.has(role) && status === "planned";
25019
+ const successSet = SUCCESS_STATUS[role] ?? /* @__PURE__ */ new Set();
25020
+ const success = !plannedDuty && successSet.has(status);
25021
+ const successEligible = !plannedDuty;
25022
+ return {
25023
+ terminalLabel: status,
25024
+ accepted: true,
25025
+ success,
25026
+ successEligible,
25027
+ noReceipt: false
25028
+ };
25029
+ }
25030
+ function projectLegs(runs) {
25031
+ const sorted = [...runs].sort((a, b) => {
25032
+ if (a.book !== b.book) return a.book.localeCompare(b.book);
25033
+ if (a.role !== b.role) return a.role.localeCompare(b.role);
25034
+ if (a.frameSpan.startedAt !== b.frameSpan.startedAt) {
25035
+ return a.frameSpan.startedAt.localeCompare(b.frameSpan.startedAt);
25036
+ }
25037
+ return a.runId.localeCompare(b.runId);
25038
+ });
25039
+ const ordinalByKey = /* @__PURE__ */ new Map();
25040
+ const legs = [];
25041
+ for (const run of sorted) {
25042
+ const key = `${run.book}\0${run.role}`;
25043
+ const ordinal = (ordinalByKey.get(key) ?? 0) + 1;
25044
+ ordinalByKey.set(key, ordinal);
25045
+ const mapped = mapTerminal(run.role, run.terminal);
25046
+ legs.push({
25047
+ runId: run.runId,
25048
+ book: run.book,
25049
+ role: run.role,
25050
+ startedAt: run.frameSpan.startedAt,
25051
+ wallMs: wallMsFromSpan(run.frameSpan),
25052
+ terminalLabel: mapped.terminalLabel,
25053
+ accepted: mapped.accepted,
25054
+ success: mapped.success,
25055
+ successEligible: mapped.successEligible,
25056
+ noReceipt: mapped.noReceipt,
25057
+ ordinalInLaneRole: ordinal,
25058
+ rework: ordinal >= 2
25059
+ });
25060
+ }
25061
+ return legs.sort((a, b) => {
25062
+ if (a.book !== b.book) return a.book.localeCompare(b.book);
25063
+ if (a.role !== b.role) return a.role.localeCompare(b.role);
25064
+ return a.runId.localeCompare(b.runId);
25065
+ });
25066
+ }
25067
+ function aggregateByRole(legs) {
25068
+ const roles = [...new Set(legs.map((leg) => leg.role))].sort((a, b) => a.localeCompare(b));
25069
+ return roles.map((role) => {
25070
+ const roleLegs = legs.filter((leg) => leg.role === role);
25071
+ const acceptedCount = roleLegs.filter((leg) => leg.accepted).length;
25072
+ const successEligibleCount = roleLegs.filter((leg) => leg.successEligible).length;
25073
+ const successCount = roleLegs.filter((leg) => leg.success).length;
25074
+ const noReceiptCount = roleLegs.filter((leg) => leg.noReceipt).length;
25075
+ const byBook = /* @__PURE__ */ new Map();
25076
+ for (const leg of roleLegs) {
25077
+ const list = byBook.get(leg.book) ?? [];
25078
+ list.push(leg);
25079
+ byBook.set(leg.book, list);
25080
+ }
25081
+ const books = [...byBook.keys()].sort((a, b) => a.localeCompare(b));
25082
+ const convergenceRounds = [];
25083
+ let firstPassLaneCount = 0;
25084
+ for (const book of books) {
25085
+ const laneLegs = [...byBook.get(book)].sort((a, b) => {
25086
+ if (a.startedAt !== b.startedAt) return a.startedAt.localeCompare(b.startedAt);
25087
+ return a.runId.localeCompare(b.runId);
25088
+ });
25089
+ convergenceRounds.push(laneLegs.length);
25090
+ const first = laneLegs[0];
25091
+ if (first.accepted) firstPassLaneCount += 1;
25092
+ }
25093
+ const appearanceLaneCount = books.length;
25094
+ return {
25095
+ role,
25096
+ acceptedCount,
25097
+ successEligibleCount,
25098
+ successCount,
25099
+ noReceiptCount,
25100
+ successRate: successEligibleCount === 0 ? void 0 : successCount / successEligibleCount,
25101
+ appearanceLaneCount,
25102
+ firstPassLaneCount,
25103
+ firstPassRate: appearanceLaneCount === 0 ? void 0 : firstPassLaneCount / appearanceLaneCount,
25104
+ convergenceRounds,
25105
+ convergenceRoundsMedian: medianNumber(convergenceRounds)
25106
+ };
25107
+ });
25108
+ }
25109
+ function reworkLens(legs) {
25110
+ let reworkWallMs = 0;
25111
+ let totalWallMs = 0;
25112
+ let reworkLegCount = 0;
25113
+ for (const leg of legs) {
25114
+ totalWallMs += leg.wallMs;
25115
+ if (leg.rework) {
25116
+ reworkWallMs += leg.wallMs;
25117
+ reworkLegCount += 1;
25118
+ }
25119
+ }
25120
+ return {
25121
+ reworkWallMs,
25122
+ totalWallMs,
25123
+ reworkRatio: totalWallMs === 0 ? void 0 : reworkWallMs / totalWallMs,
25124
+ reworkLegCount,
25125
+ totalLegCount: legs.length
25126
+ };
25127
+ }
25128
+ function buildAcceptanceSuccessReworkSection(runs) {
25129
+ if (runs.length === 0) return void 0;
25130
+ const legs = projectLegs(runs);
25131
+ return {
25132
+ kind: "taishi-acceptance-success-rework",
25133
+ legs,
25134
+ byRole: aggregateByRole(legs),
25135
+ rework: reworkLens(legs)
25136
+ };
25137
+ }
25138
+ var WORKER_ROLES, ACCEPTED_STATUS, SUCCESS_STATUS, acceptanceSuccessReworkFamily, acceptance_success_rework_default;
25139
+ var init_acceptance_success_rework = __esm({
25140
+ "src/taishi-metric-families/acceptance-success-rework.ts"() {
25141
+ "use strict";
25142
+ init_taishi_median();
25143
+ WORKER_ROLES = /* @__PURE__ */ new Set(["coder", "fixer"]);
25144
+ ACCEPTED_STATUS = {
25145
+ coder: /* @__PURE__ */ new Set(["completed", "refused", "partially_completed", "unfinished", "planned"]),
25146
+ fixer: /* @__PURE__ */ new Set(["completed", "refused", "partially_completed", "unfinished", "planned"]),
25147
+ judge: /* @__PURE__ */ new Set(["converged", "continue", "escalate"]),
25148
+ reviewer: /* @__PURE__ */ new Set(["completed", "refused"]),
25149
+ doctor: /* @__PURE__ */ new Set(["completed", "refused"]),
25150
+ merger: /* @__PURE__ */ new Set(["completed", "escalate"])
25151
+ };
25152
+ SUCCESS_STATUS = {
25153
+ coder: /* @__PURE__ */ new Set(["completed"]),
25154
+ fixer: /* @__PURE__ */ new Set(["completed"]),
25155
+ // Judge: producing any of the three verdicts completes the duty.
25156
+ judge: /* @__PURE__ */ new Set(["converged", "continue", "escalate"]),
25157
+ reviewer: /* @__PURE__ */ new Set(["completed"]),
25158
+ doctor: /* @__PURE__ */ new Set(["completed"]),
25159
+ merger: /* @__PURE__ */ new Set(["completed"])
25160
+ };
25161
+ acceptanceSuccessReworkFamily = {
25162
+ id: "acceptance-success-rework",
25163
+ contribute(input) {
25164
+ const section = buildAcceptanceSuccessReworkSection(input.runs);
25165
+ if (section === void 0) return void 0;
25166
+ return { acceptanceSuccessRework: section };
25167
+ }
25168
+ };
25169
+ acceptance_success_rework_default = acceptanceSuccessReworkFamily;
25170
+ }
25171
+ });
25172
+
25173
+ // src/taishi-model-groups.ts
25174
+ function taishiModelGroupKey(models) {
25175
+ if (models.length === 0) return void 0;
25176
+ if (models.length === 1) return models[0];
25177
+ return `mixed:${models.join("+")}`;
25178
+ }
25179
+ function rate(numerator, denominator) {
25180
+ if (denominator === 0) return void 0;
25181
+ return numerator / denominator;
25182
+ }
25183
+ function displayNameFor(rawGroupKey, combinationMapping) {
25184
+ if (combinationMapping === void 0) return rawGroupKey;
25185
+ const aliased = combinationMapping[rawGroupKey];
25186
+ return aliased === void 0 ? rawGroupKey : aliased;
25187
+ }
25188
+ function sortUnreadable(unreadable) {
25189
+ return [...unreadable].sort((a, b) => {
25190
+ if (a.book !== b.book) return a.book.localeCompare(b.book);
25191
+ return a.runId.localeCompare(b.runId);
25192
+ });
25193
+ }
25194
+ function modelIdentityAbsentEntry(run) {
25195
+ return {
25196
+ runId: run.runId,
25197
+ book: run.book,
25198
+ missingSources: ["session-model"],
25199
+ reason: "session has no usable model identity",
25200
+ firstFrameAt: { status: "present", at: run.frameSpan.startedAt },
25201
+ // Readable legs already admitted a full span — retain end edge for lastActivityAt.
25202
+ lastFrameAt: { status: "present", at: run.frameSpan.endedAt }
25203
+ };
25204
+ }
25205
+ function buildTaishiModelGroupsPage(input) {
25206
+ const groupedRuns = [];
25207
+ const modelAbsent = [];
25208
+ for (const run of input.runs) {
25209
+ const rawGroupKey = taishiModelGroupKey(run.models);
25210
+ if (rawGroupKey === void 0) {
25211
+ modelAbsent.push(modelIdentityAbsentEntry(run));
25212
+ } else {
25213
+ groupedRuns.push({ run, rawGroupKey });
25214
+ }
25215
+ }
25216
+ const acceptance = buildAcceptanceSuccessReworkSection(
25217
+ groupedRuns.map(({ run }) => run)
25218
+ );
25219
+ const legByRunId = new Map(
25220
+ (acceptance?.legs ?? []).map((leg) => [leg.runId, leg])
25221
+ );
25222
+ const byRaw = /* @__PURE__ */ new Map();
25223
+ for (const { run, rawGroupKey } of groupedRuns) {
25224
+ const leg = legByRunId.get(run.runId);
25225
+ if (leg === void 0) {
25226
+ throw new Error(
25227
+ `taishi model-groups: missing acceptance projection for run ${run.runId}`
25228
+ );
25229
+ }
25230
+ let acc = byRaw.get(rawGroupKey);
25231
+ if (acc === void 0) {
25232
+ acc = {
25233
+ acceptedCount: 0,
25234
+ successCount: 0,
25235
+ successEligibleCount: 0,
25236
+ noReceiptCount: 0,
25237
+ walls: []
25238
+ };
25239
+ byRaw.set(rawGroupKey, acc);
25240
+ }
25241
+ if (leg.accepted) acc.acceptedCount += 1;
25242
+ if (leg.success) acc.successCount += 1;
25243
+ if (leg.successEligible) acc.successEligibleCount += 1;
25244
+ if (leg.noReceipt) acc.noReceiptCount += 1;
25245
+ acc.walls.push(leg.wallMs);
25246
+ }
25247
+ const rawKeys = [...byRaw.keys()].sort((a, b) => a.localeCompare(b));
25248
+ const groups = rawKeys.map((rawGroupKey) => {
25249
+ const acc = byRaw.get(rawGroupKey);
25250
+ const legCount = acc.walls.length;
25251
+ return {
25252
+ rawGroupKey,
25253
+ displayName: displayNameFor(rawGroupKey, input.combinationMapping),
25254
+ legCount,
25255
+ acceptedCount: acc.acceptedCount,
25256
+ acceptanceRate: rate(acc.acceptedCount, legCount),
25257
+ successCount: acc.successCount,
25258
+ successEligibleCount: acc.successEligibleCount,
25259
+ successRate: rate(acc.successCount, acc.successEligibleCount),
25260
+ noReceiptCount: acc.noReceiptCount,
25261
+ noReceiptRate: rate(acc.noReceiptCount, legCount),
25262
+ wallClockMedianMs: medianNumber(acc.walls)
25263
+ };
25264
+ });
25265
+ const unreadable = sortUnreadable([...input.unreadable, ...modelAbsent]);
25266
+ return {
25267
+ kind: "taishi-model-groups",
25268
+ mode: "model-groups",
25269
+ projectRoots: [...input.projectRoots].sort((a, b) => a.localeCompare(b)),
25270
+ groups,
25271
+ legCount: groupedRuns.length,
25272
+ unreadableCount: unreadable.length,
25273
+ unreadable
25274
+ };
25275
+ }
25276
+ var init_taishi_model_groups = __esm({
25277
+ "src/taishi-model-groups.ts"() {
25278
+ "use strict";
25279
+ init_acceptance_success_rework();
25280
+ init_taishi_median();
25281
+ }
25282
+ });
25283
+
25284
+ // src/taishi-metric-families/b2-frame-buckets-actions.ts
25285
+ function timestampMs(iso) {
25286
+ const ms = Date.parse(iso);
25287
+ if (!Number.isFinite(ms)) {
25288
+ throw new Error(`unparseable timestamp: ${iso}`);
25289
+ }
25290
+ return ms;
25291
+ }
25292
+ function toIso(ms) {
25293
+ return new Date(ms).toISOString();
25294
+ }
25295
+ function closedTools(intervals) {
25296
+ const out = [];
25297
+ for (const interval of intervals) {
25298
+ if (interval.endedAt === void 0) continue;
25299
+ const startMs = timestampMs(interval.startedAt);
25300
+ const endMs = timestampMs(interval.endedAt);
25301
+ if (endMs <= startMs) continue;
25302
+ out.push({
25303
+ toolCallId: interval.toolCallId,
25304
+ toolName: interval.toolName,
25305
+ startedAt: interval.startedAt,
25306
+ endedAt: interval.endedAt,
25307
+ startMs,
25308
+ endMs,
25309
+ ...interval.command !== void 0 ? { command: interval.command } : {}
25310
+ });
25311
+ }
25312
+ return out;
25313
+ }
25314
+ function clipToolsToFrame(tools, frameStartMs, frameEndMs) {
25315
+ if (frameEndMs <= frameStartMs) return [];
25316
+ const out = [];
25317
+ for (const tool2 of tools) {
25318
+ const startMs = Math.max(tool2.startMs, frameStartMs);
25319
+ const endMs = Math.min(tool2.endMs, frameEndMs);
25320
+ if (endMs <= startMs) continue;
25321
+ out.push({
25322
+ toolCallId: tool2.toolCallId,
25323
+ toolName: tool2.toolName,
25324
+ startMs,
25325
+ endMs,
25326
+ startedAt: startMs === tool2.startMs ? tool2.startedAt : toIso(startMs),
25327
+ endedAt: endMs === tool2.endMs ? tool2.endedAt : toIso(endMs),
25328
+ ...tool2.command !== void 0 ? { command: tool2.command } : {}
25329
+ });
25330
+ }
25331
+ return out;
25332
+ }
25333
+ function mergeUnion(intervals) {
25334
+ if (intervals.length === 0) return [];
25335
+ const sorted = [...intervals].sort(
25336
+ (a, b) => a.startMs - b.startMs || a.endMs - b.endMs
25337
+ );
25338
+ const merged = [
25339
+ { startMs: sorted[0].startMs, endMs: sorted[0].endMs }
25340
+ ];
25341
+ for (let i = 1; i < sorted.length; i += 1) {
25342
+ const cur = sorted[i];
25343
+ const last = merged[merged.length - 1];
25344
+ if (cur.startMs <= last.endMs) {
25345
+ last.endMs = Math.max(last.endMs, cur.endMs);
25346
+ } else {
25347
+ merged.push({ startMs: cur.startMs, endMs: cur.endMs });
25348
+ }
25349
+ }
25350
+ return merged;
25351
+ }
25352
+ function modelMaximalIntervals(frameStartMs, frameEndMs, toolUnion) {
25353
+ if (frameEndMs <= frameStartMs) return [];
25354
+ const gaps = [];
25355
+ let cursor = frameStartMs;
25356
+ for (const interval of toolUnion) {
25357
+ if (interval.startMs > cursor) {
25358
+ gaps.push({ startMs: cursor, endMs: interval.startMs });
25359
+ }
25360
+ cursor = Math.max(cursor, interval.endMs);
25361
+ }
25362
+ if (cursor < frameEndMs) {
25363
+ gaps.push({ startMs: cursor, endMs: frameEndMs });
25364
+ }
25365
+ return gaps;
25366
+ }
25367
+ function toolAction(tool2) {
25368
+ const action = {
25369
+ kind: "tool",
25370
+ toolCallId: tool2.toolCallId,
25371
+ toolName: tool2.toolName,
25372
+ durationMs: tool2.endMs - tool2.startMs,
25373
+ startedAt: tool2.startedAt,
25374
+ endedAt: tool2.endedAt
25375
+ };
25376
+ if (tool2.toolName === "bash" && tool2.command !== void 0) {
25377
+ return {
25378
+ ...action,
25379
+ commandSummary: tool2.command
25380
+ };
25381
+ }
25382
+ return action;
25383
+ }
25384
+ function modelAction(gap) {
25385
+ return {
25386
+ kind: "model",
25387
+ durationMs: gap.endMs - gap.startMs,
25388
+ startedAt: toIso(gap.startMs),
25389
+ endedAt: toIso(gap.endMs)
25390
+ };
25391
+ }
25392
+ function sortActionsDescending(actions) {
25393
+ return [...actions].sort((a, b) => {
25394
+ if (b.durationMs !== a.durationMs) return b.durationMs - a.durationMs;
25395
+ if (a.startedAt !== b.startedAt) return a.startedAt.localeCompare(b.startedAt);
25396
+ if (a.kind !== b.kind) return a.kind.localeCompare(b.kind);
25397
+ if (a.kind === "tool" && b.kind === "tool") {
25398
+ return a.toolCallId.localeCompare(b.toolCallId);
25399
+ }
25400
+ return 0;
25401
+ });
25402
+ }
25403
+ function computeTaishiB2RunMetrics(facts) {
25404
+ const frameStartMs = timestampMs(facts.frameSpan.startedAt);
25405
+ const frameEndMs = timestampMs(facts.frameSpan.endedAt);
25406
+ const wallMs = Math.max(0, frameEndMs - frameStartMs);
25407
+ const tools = clipToolsToFrame(closedTools(facts.toolIntervals), frameStartMs, frameEndMs);
25408
+ const toolUnion = mergeUnion(tools);
25409
+ const toolBucketMs = toolUnion.reduce(
25410
+ (sum, interval) => sum + (interval.endMs - interval.startMs),
25411
+ 0
25412
+ );
25413
+ const modelBucketMs = wallMs - toolBucketMs;
25414
+ const modelGaps = modelMaximalIntervals(frameStartMs, frameEndMs, toolUnion);
25415
+ const actions = sortActionsDescending([
25416
+ ...tools.map(toolAction),
25417
+ ...modelGaps.map(modelAction)
25418
+ ]);
25419
+ const actionDurationMedianMs = medianNumber(actions.map((action) => action.durationMs));
25420
+ return {
25421
+ runId: facts.runId,
25422
+ book: facts.book,
25423
+ role: facts.role,
25424
+ wallMs,
25425
+ toolBucketMs,
25426
+ modelBucketMs,
25427
+ actions,
25428
+ actionDurationMedianMs
25429
+ };
25430
+ }
25431
+ var b2FrameBucketsActionsFamily, b2_frame_buckets_actions_default;
25432
+ var init_b2_frame_buckets_actions = __esm({
25433
+ "src/taishi-metric-families/b2-frame-buckets-actions.ts"() {
25434
+ "use strict";
25435
+ init_taishi_median();
25436
+ b2FrameBucketsActionsFamily = {
25437
+ id: "b2-frame-buckets-actions",
25438
+ contribute(input) {
25439
+ if (input.runs.length === 0) return void 0;
25440
+ const runs = [...input.runs].map(computeTaishiB2RunMetrics).sort((a, b) => {
25441
+ if (a.book !== b.book) return a.book.localeCompare(b.book);
25442
+ if (a.role !== b.role) return a.role.localeCompare(b.role);
25443
+ return a.runId.localeCompare(b.runId);
25444
+ });
25445
+ const section = {
25446
+ kind: "taishi-b2-frame-buckets-actions",
25447
+ runs
25448
+ };
25449
+ return { b2FrameBucketsActions: section };
25450
+ }
25451
+ };
25452
+ b2_frame_buckets_actions_default = b2FrameBucketsActionsFamily;
25453
+ }
25454
+ });
25455
+
25456
+ // src/taishi-metric-families/leg-wall-clock.ts
25457
+ function frameSpanWallMs(span) {
25458
+ return Date.parse(span.endedAt) - Date.parse(span.startedAt);
25459
+ }
25460
+ function projectEntry(facts) {
25461
+ return {
25462
+ runId: facts.runId,
25463
+ book: facts.book,
25464
+ role: facts.role,
25465
+ wallMs: frameSpanWallMs(facts.frameSpan)
25466
+ };
25467
+ }
25468
+ function compareRankingDesc(a, b) {
25469
+ if (b.wallMs !== a.wallMs) return b.wallMs - a.wallMs;
25470
+ if (a.book !== b.book) return a.book.localeCompare(b.book);
25471
+ if (a.role !== b.role) return a.role.localeCompare(b.role);
25472
+ return a.runId.localeCompare(b.runId);
25473
+ }
25474
+ var legWallClockFamily, leg_wall_clock_default;
25475
+ var init_leg_wall_clock = __esm({
25476
+ "src/taishi-metric-families/leg-wall-clock.ts"() {
25477
+ "use strict";
25478
+ init_taishi_median();
25479
+ legWallClockFamily = {
25480
+ id: "leg-wall-clock",
25481
+ contribute(input) {
25482
+ if (input.runs.length === 0) {
25483
+ return void 0;
25484
+ }
25485
+ const ranking = input.runs.map(projectEntry).sort(compareRankingDesc);
25486
+ const walls = ranking.map((leg) => leg.wallMs);
25487
+ const medianWallMs = medianNumber(walls);
25488
+ if (medianWallMs === void 0) {
25489
+ return void 0;
25490
+ }
25491
+ let totalElapsedMs = 0;
25492
+ for (const wallMs of walls) totalElapsedMs += wallMs;
25493
+ const section = {
25494
+ kind: "taishi-leg-wall-clock",
25495
+ ranking,
25496
+ medianWallMs,
25497
+ totalElapsedMs
25498
+ };
25499
+ return { legWallClock: section };
25500
+ }
25501
+ };
25502
+ leg_wall_clock_default = legWallClockFamily;
25503
+ }
25504
+ });
25505
+
25506
+ // src/taishi-metric-families/round-timeline.ts
25507
+ function isRecord10(value) {
25508
+ return typeof value === "object" && value !== null && !Array.isArray(value);
25509
+ }
25510
+ function wallMsFromSpan2(startedAt, endedAt) {
25511
+ return Date.parse(endedAt) - Date.parse(startedAt);
25512
+ }
25513
+ function readOutcomeStatus(body) {
25514
+ if (!isRecord10(body.outcome)) return void 0;
25515
+ const status = body.outcome.status;
25516
+ if (typeof status !== "string" || status.trim() === "") return void 0;
25517
+ return status;
25518
+ }
25519
+ function readClassCount(body) {
25520
+ if (!isRecord10(body.outcome)) return void 0;
25521
+ if (!isRecord10(body.outcome.decisiveFacts)) return void 0;
25522
+ const classCount = body.outcome.decisiveFacts.classCount;
25523
+ if (typeof classCount !== "number" || !Number.isFinite(classCount)) {
25524
+ return void 0;
25525
+ }
25526
+ return classCount;
25527
+ }
25528
+ function projectTerminal(facts) {
25529
+ if (facts.terminal.status === "absent") {
25530
+ return { kind: "death", channel: "no-receipt" };
25531
+ }
25532
+ if (facts.terminal.file === "error.json") {
25533
+ return { kind: "death", channel: "error" };
25534
+ }
25535
+ if (facts.terminal.file === "audit-incomplete.json") {
25536
+ return { kind: "death", channel: "audit-incomplete" };
25537
+ }
25538
+ const status = readOutcomeStatus(facts.terminal.body);
25539
+ const classCount = readClassCount(facts.terminal.body);
25540
+ const receiptStatus = status ?? "unparsed";
25541
+ if (classCount === void 0) {
25542
+ return { kind: "receipt", status: receiptStatus };
25543
+ }
25544
+ return { kind: "receipt", status: receiptStatus, classCount };
25545
+ }
25546
+ function projectRunRow(facts) {
25547
+ const { startedAt, endedAt } = facts.frameSpan;
25548
+ return {
25549
+ kind: "run",
25550
+ runId: facts.runId,
25551
+ book: facts.book,
25552
+ role: facts.role,
25553
+ startedAt,
25554
+ endedAt,
25555
+ wallMs: wallMsFromSpan2(startedAt, endedAt),
25556
+ terminal: projectTerminal(facts)
25557
+ };
25558
+ }
25559
+ function projectUnreadableRow(entry) {
25560
+ return {
25561
+ kind: "unreadable",
25562
+ runId: entry.runId,
25563
+ book: entry.book,
25564
+ missingSources: entry.missingSources,
25565
+ reason: entry.reason,
25566
+ firstFrameAt: entry.firstFrameAt
25567
+ };
25568
+ }
25569
+ function rowSortStartedAt(row) {
25570
+ if (row.kind === "run") return row.startedAt;
25571
+ if (row.firstFrameAt.status === "present") return row.firstFrameAt.at;
25572
+ return void 0;
25573
+ }
25574
+ function compareRows(a, b) {
25575
+ const aStart = rowSortStartedAt(a);
25576
+ const bStart = rowSortStartedAt(b);
25577
+ if (aStart === void 0 && bStart === void 0) {
25578
+ return a.runId.localeCompare(b.runId);
25579
+ }
25580
+ if (aStart === void 0) return 1;
25581
+ if (bStart === void 0) return -1;
25582
+ if (aStart !== bStart) return aStart.localeCompare(bStart);
25583
+ return a.runId.localeCompare(b.runId);
25584
+ }
25585
+ function buildLanes(runs, unreadable) {
25586
+ const byLane = /* @__PURE__ */ new Map();
25587
+ const push = (lane, row) => {
25588
+ const list = byLane.get(lane);
25589
+ if (list === void 0) byLane.set(lane, [row]);
25590
+ else list.push(row);
25591
+ };
25592
+ for (const facts of runs) {
25593
+ push(facts.book, projectRunRow(facts));
25594
+ }
25595
+ for (const entry of unreadable) {
25596
+ push(entry.book, projectUnreadableRow(entry));
25597
+ }
25598
+ return [...byLane.keys()].sort((a, b) => a.localeCompare(b)).map((lane) => ({
25599
+ lane,
25600
+ rows: [...byLane.get(lane) ?? []].sort(compareRows)
25601
+ }));
25602
+ }
25603
+ var roundTimelineFamily, round_timeline_default;
25604
+ var init_round_timeline = __esm({
25605
+ "src/taishi-metric-families/round-timeline.ts"() {
25606
+ "use strict";
25607
+ roundTimelineFamily = {
25608
+ id: "round-timeline",
25609
+ contribute(input) {
25610
+ if (input.runs.length === 0 && input.unreadable.length === 0) {
25611
+ return void 0;
25612
+ }
25613
+ const section = {
25614
+ kind: "taishi-round-timeline",
25615
+ lanes: buildLanes(input.runs, input.unreadable)
25616
+ };
25617
+ return { roundTimeline: section };
25618
+ }
25619
+ };
25620
+ round_timeline_default = roundTimelineFamily;
25621
+ }
25622
+ });
25623
+
25624
+ // src/taishi-metric-families.ts
25625
+ async function loadTaishiIssueMetricFamilies() {
25626
+ return ISSUE_METRIC_FAMILIES;
25627
+ }
25628
+ var ISSUE_METRIC_FAMILIES;
25629
+ var init_taishi_metric_families = __esm({
25630
+ "src/taishi-metric-families.ts"() {
25631
+ "use strict";
25632
+ init_acceptance_success_rework();
25633
+ init_b2_frame_buckets_actions();
25634
+ init_leg_wall_clock();
25635
+ init_round_timeline();
25636
+ ISSUE_METRIC_FAMILIES = [
25637
+ acceptance_success_rework_default,
25638
+ b2_frame_buckets_actions_default,
25639
+ leg_wall_clock_default,
25640
+ round_timeline_default
25641
+ ].sort((a, b) => a.id.localeCompare(b.id));
25642
+ }
25643
+ });
25644
+
25645
+ // src/taishi-metric-family.ts
25646
+ function composeTaishiMetricFamilySections(families, input) {
25647
+ const sections = {};
25648
+ for (const family of families) {
25649
+ const piece = family.contribute(input);
25650
+ if (piece === void 0) continue;
25651
+ Object.assign(sections, piece);
25652
+ }
25653
+ return sections;
25654
+ }
25655
+ var init_taishi_metric_family = __esm({
25656
+ "src/taishi-metric-family.ts"() {
25657
+ "use strict";
25658
+ }
25659
+ });
25660
+
25661
+ // src/taishi-page.ts
25662
+ import { createHash as createHash4 } from "node:crypto";
25663
+ import { dirname as dirname10, join as join22 } from "node:path";
25664
+ function taishiIssuePageKey(projectRoot) {
25665
+ const identity = physicalPathIdentity(projectRoot);
25666
+ return createHash4("sha256").update(identity).digest("hex").slice(0, 32);
25667
+ }
25668
+ function taishiIssuePagePath(ledgerHome, projectRoot) {
25669
+ return join22(ledgerHome, "taishi", "issues", `${taishiIssuePageKey(projectRoot)}.json`);
25670
+ }
25671
+ function sortLegs(legs) {
25672
+ return [...legs].sort((a, b) => {
25673
+ if (a.book !== b.book) return a.book.localeCompare(b.book);
25674
+ if (a.role !== b.role) return a.role.localeCompare(b.role);
25675
+ return a.runId.localeCompare(b.runId);
25676
+ });
25677
+ }
25678
+ function sortUnreadable2(unreadable) {
25679
+ return [...unreadable].sort((a, b) => {
25680
+ if (a.book !== b.book) return a.book.localeCompare(b.book);
25681
+ return a.runId.localeCompare(b.runId);
25682
+ });
25683
+ }
25684
+ function sortScopeConflicts(conflicts) {
25685
+ return [...conflicts].sort((a, b) => {
25686
+ const aRun = a.runId ?? "";
25687
+ const bRun = b.runId ?? "";
25688
+ if (aRun !== bRun) return aRun.localeCompare(bRun);
25689
+ return a.projectRoot.localeCompare(b.projectRoot);
25690
+ });
25691
+ }
25692
+ function assertTaishiChangedLinesInput(changedLines) {
25693
+ if (changedLines === void 0) return;
25694
+ if (typeof changedLines !== "number" || !Number.isFinite(changedLines) || changedLines < 0) {
25695
+ throw new Error(
25696
+ `taishi changedLines must be a finite non-negative number, got ${String(changedLines)}`
25697
+ );
25698
+ }
25699
+ }
25700
+ function normalizeTaishiChangedLines(changedLines) {
25701
+ assertTaishiChangedLinesInput(changedLines);
25702
+ if (changedLines === void 0 || changedLines === 0) {
25703
+ return { status: "absent" };
25704
+ }
25705
+ return { status: "present", value: changedLines };
25706
+ }
25707
+ function computeTaishiMsPerKLines(totalElapsedMs, changedLines) {
25708
+ if (changedLines.status === "absent") return { status: "absent" };
25709
+ return {
25710
+ status: "present",
25711
+ value: totalElapsedMs / (changedLines.value / 1e3)
25712
+ };
25713
+ }
25714
+ function frameSpanWallMs2(span) {
25715
+ return Date.parse(span.endedAt) - Date.parse(span.startedAt);
25716
+ }
25717
+ function summarizeTaishiRunEfficiency(runs, unreadable = []) {
25718
+ let totalElapsedMs = 0;
25719
+ let latestEndedAt;
25720
+ for (const run of runs) {
25721
+ totalElapsedMs += frameSpanWallMs2(run.frameSpan);
25722
+ const endedAt = run.frameSpan.endedAt;
25723
+ if (latestEndedAt === void 0 || endedAt > latestEndedAt) {
25724
+ latestEndedAt = endedAt;
25725
+ }
25726
+ }
25727
+ for (const entry of unreadable) {
25728
+ if (entry.lastFrameAt.status !== "present") continue;
25729
+ const endedAt = entry.lastFrameAt.at;
25730
+ if (latestEndedAt === void 0 || endedAt > latestEndedAt) {
25731
+ latestEndedAt = endedAt;
25732
+ }
25733
+ }
25734
+ const lastActivityAt = latestEndedAt === void 0 ? { status: "absent" } : { status: "present", at: latestEndedAt };
25735
+ return { totalElapsedMs, lastActivityAt };
25736
+ }
25737
+ async function buildTaishiIssueMetricsPage(input) {
25738
+ const families = await loadTaishiIssueMetricFamilies();
25739
+ const legs = sortLegs(
25740
+ input.runs.map((run) => ({
25741
+ runId: run.runId,
25742
+ book: run.book,
25743
+ role: run.role
25744
+ }))
25745
+ );
25746
+ const unreadable = sortUnreadable2(input.unreadable);
25747
+ const scopeConflicts = sortScopeConflicts(input.scopeConflicts ?? []);
25748
+ const projectRoot = physicalPathIdentity(input.projectRoot);
25749
+ const { totalElapsedMs, lastActivityAt } = summarizeTaishiRunEfficiency(
25750
+ input.runs,
25751
+ unreadable
25752
+ );
25753
+ const changedLines = normalizeTaishiChangedLines(input.changedLines);
25754
+ const msPerKLines = computeTaishiMsPerKLines(totalElapsedMs, changedLines);
25755
+ const envelope = {
25756
+ kind: "taishi-issue-metrics",
25757
+ mode: "issue",
25758
+ projectRoot,
25759
+ // exactOptionalPropertyTypes: only materialize when caller supplied it.
25760
+ ...input.issueNumber === void 0 ? {} : { issueNumber: input.issueNumber },
25761
+ legs,
25762
+ unreadable,
25763
+ unreadableCount: unreadable.length,
25764
+ scopeConflicts,
25765
+ totalElapsedMs,
25766
+ changedLines,
25767
+ msPerKLines,
25768
+ lastActivityAt
25769
+ };
25770
+ const sections = composeTaishiMetricFamilySections(families, {
25771
+ projectRoot,
25772
+ runs: input.runs,
25773
+ unreadable
25774
+ });
25775
+ return { ...envelope, ...sections };
25776
+ }
25777
+ async function writeTaishiIssueMetricsPage(ledgerHome, page) {
25778
+ const path = taishiIssuePagePath(ledgerHome, page.projectRoot);
25779
+ ensureRealDirectoryTree(ledgerHome, dirname10(path));
25780
+ assertLedgerFileInsideHome(path, ledgerHome);
25781
+ await writeFileAtomically(path, `${JSON.stringify(page, null, 2)}
25782
+ `);
25783
+ return path;
25784
+ }
25785
+ var init_taishi_page = __esm({
25786
+ "src/taishi-page.ts"() {
25787
+ "use strict";
25788
+ init_atomic_write();
25789
+ init_activation_ledger_topology();
25790
+ init_taishi_metric_families();
25791
+ init_taishi_metric_family();
25792
+ }
25793
+ });
25794
+
25795
+ // src/taishi-entry.ts
25796
+ import { readFile as readFile14 } from "node:fs/promises";
25797
+ function isMissingPathError5(error) {
25798
+ return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
25799
+ }
25800
+ function cachedPageMatchesRequestedScope(page, input) {
25801
+ const requestedTicket = input.ticketNumber ?? input.issueNumber;
25802
+ if (requestedTicket === void 0) {
25803
+ return page.issueNumber === void 0;
25804
+ }
25805
+ return page.issueNumber === requestedTicket;
25806
+ }
25807
+ async function readOrComputeTaishiIssuePage(input) {
25808
+ const ledgerHome = resolveActivationLedgerHome();
25809
+ const projectRoot = physicalPathIdentity(input.projectRoot);
25810
+ const pagePath = taishiIssuePagePath(ledgerHome, projectRoot);
25811
+ try {
25812
+ const raw = await readFile14(pagePath, "utf8");
25813
+ const page = JSON.parse(raw);
25814
+ if (cachedPageMatchesRequestedScope(page, input)) {
25815
+ return { mode: "issue", page, pagePath };
25816
+ }
25817
+ } catch (error) {
25818
+ if (!isMissingPathError5(error)) {
25819
+ throw new TaishiIssueComputeError({
25820
+ projectRoot,
25821
+ ...input.issueNumber === void 0 ? {} : { issueNumber: input.issueNumber },
25822
+ cause: error
25823
+ });
25824
+ }
25825
+ }
25826
+ try {
25827
+ return await runTaishiIssueMode(input);
25828
+ } catch (error) {
25829
+ if (error instanceof TaishiIssueComputeError) throw error;
25830
+ throw new TaishiIssueComputeError({
25831
+ projectRoot,
25832
+ ...input.issueNumber === void 0 ? {} : { issueNumber: input.issueNumber },
25833
+ cause: error
25834
+ });
25835
+ }
25836
+ }
25837
+ async function runTaishiIssueMode(input, precomputedScan) {
25838
+ assertTaishiChangedLinesInput(input.changedLines);
25839
+ const ledgerHome = resolveActivationLedgerHome();
25840
+ const projectRoot = input.projectRoot;
25841
+ const ticketNumber = "ticketNumber" in input ? input.ticketNumber : void 0;
25842
+ const scan = precomputedScan ?? (ticketNumber === void 0 ? await scanTaishiIssueRuns({ projectRoot }) : await scanTaishiIssueRuns({ projectRoot, ticketNumber }));
25843
+ const issueNumber = "issueNumber" in input ? input.issueNumber : void 0;
25844
+ const conflictingProjectRoot = "conflictingProjectRoot" in input ? input.conflictingProjectRoot : void 0;
25845
+ const scopeConflicts = [...scan.scopeConflicts];
25846
+ if (conflictingProjectRoot !== void 0 && ticketNumber !== void 0) {
25847
+ const losingRoot = physicalPathIdentity(conflictingProjectRoot);
25848
+ const winningRoot = physicalPathIdentity(projectRoot);
25849
+ if (losingRoot !== winningRoot) {
25850
+ scopeConflicts.push({
25851
+ ticketNumber,
25852
+ projectRoot: losingRoot,
25853
+ fact: "typed-ticketNumber-over-projectRoot"
25854
+ });
25855
+ }
25856
+ }
25857
+ const page = await buildTaishiIssueMetricsPage({
25858
+ projectRoot,
25859
+ runs: scan.runs,
25860
+ unreadable: scan.unreadable,
25861
+ scopeConflicts,
25862
+ ...input.changedLines === void 0 ? {} : { changedLines: input.changedLines },
25863
+ ...issueNumber === void 0 ? {} : { issueNumber }
25864
+ });
25865
+ const pagePath = await writeTaishiIssueMetricsPage(ledgerHome, page);
25866
+ if (issueNumber !== void 0) {
25867
+ await mergeTaishiLibraryIndexRows(ledgerHome, [
25868
+ rowFromIssueMetricsPage(page)
25869
+ ]);
25870
+ }
25871
+ return { mode: "issue", page, pagePath };
25872
+ }
25873
+ async function runTaishiSweepMode(input) {
25874
+ const ledgerHome = resolveActivationLedgerHome();
25875
+ const issuePages = [];
25876
+ for (const entry of input.mergedPullRequests) {
25877
+ issuePages.push(await runTaishiIssueMode(entry));
25878
+ }
25879
+ const upserts = issuePages.map((result2) => rowFromIssueMetricsPage(result2.page));
25880
+ const { index, indexPath } = await mergeTaishiLibraryIndexRows(ledgerHome, upserts);
25881
+ return { mode: "sweep", issuePages, index, indexPath };
25882
+ }
25883
+ async function runTaishiModelGroupsMode(input) {
25884
+ const ledgerHome = resolveActivationLedgerHome();
25885
+ const runs = [];
25886
+ const unreadable = [];
25887
+ const seen = /* @__PURE__ */ new Set();
25888
+ const projectRoots = [];
25889
+ for (const root of input.projectRoots) {
25890
+ const identity = physicalPathIdentity(root);
25891
+ if (seen.has(identity)) continue;
25892
+ seen.add(identity);
25893
+ projectRoots.push(identity);
25894
+ }
25895
+ for (const projectRoot of projectRoots) {
25896
+ const scan = await scanTaishiIssueRuns({ projectRoot });
25897
+ runs.push(...scan.runs);
25898
+ unreadable.push(...scan.unreadable);
25899
+ const pagePath = taishiIssuePagePath(ledgerHome, projectRoot);
25900
+ try {
25901
+ const raw = await readFile14(pagePath, "utf8");
25902
+ JSON.parse(raw);
25903
+ } catch (error) {
25904
+ if (!isMissingPathError5(error)) {
25905
+ throw new TaishiIssueComputeError({ projectRoot, cause: error });
25906
+ }
25907
+ try {
25908
+ await runTaishiIssueMode({ mode: "issue", projectRoot }, scan);
25909
+ } catch (computeError) {
25910
+ if (computeError instanceof TaishiIssueComputeError) throw computeError;
25911
+ throw new TaishiIssueComputeError({ projectRoot, cause: computeError });
25912
+ }
25913
+ }
25914
+ }
25915
+ const page = input.combinationMapping === void 0 ? buildTaishiModelGroupsPage({ projectRoots, runs, unreadable }) : buildTaishiModelGroupsPage({
25916
+ projectRoots,
25917
+ runs,
25918
+ unreadable,
25919
+ combinationMapping: input.combinationMapping
25920
+ });
25921
+ return { mode: "model-groups", page };
25922
+ }
25923
+ async function runTaishi(input) {
25924
+ if (input.mode === "sweep") {
25925
+ return runTaishiSweepMode(input);
25926
+ }
25927
+ if (input.mode === "cohort") {
25928
+ const ledgerHome = resolveActivationLedgerHome();
25929
+ return runTaishiCohortMode(ledgerHome, input, async ({ projectRoot, issueNumber }) => {
25930
+ const ensured = await readOrComputeTaishiIssuePage({
25931
+ mode: "issue",
25932
+ projectRoot,
25933
+ issueNumber
25934
+ });
25935
+ return ensured.page;
25936
+ });
25937
+ }
25938
+ if (input.mode === "model-groups") {
25939
+ return runTaishiModelGroupsMode(input);
25940
+ }
25941
+ return runTaishiIssueMode(input);
25942
+ }
25943
+ var TaishiIssueComputeError, taishiSweepModeInputSchema;
25944
+ var init_taishi_entry = __esm({
25945
+ "src/taishi-entry.ts"() {
25946
+ "use strict";
25947
+ init_build();
25948
+ init_activation_ledger_topology();
25949
+ init_taishi_cohort();
25950
+ init_taishi_ledger();
25951
+ init_taishi_index();
25952
+ init_taishi_model_groups();
25953
+ init_taishi_page();
25954
+ TaishiIssueComputeError = class extends Error {
25955
+ code = "taishi-issue-compute-failed";
25956
+ projectRoot;
25957
+ issueNumber;
25958
+ constructor(input) {
25959
+ const root = physicalPathIdentity(input.projectRoot);
25960
+ const causeText = input.cause instanceof Error ? input.cause.message || input.cause.name : String(input.cause);
25961
+ const issueFace = input.issueNumber === void 0 ? `projectRoot ${root}` : `issue ${input.issueNumber} (projectRoot ${root})`;
25962
+ super(`taishi compute failed for ${issueFace}: ${causeText}`, {
25963
+ cause: input.cause
25964
+ });
25965
+ this.name = "TaishiIssueComputeError";
25966
+ this.projectRoot = root;
25967
+ if (input.issueNumber !== void 0) {
25968
+ this.issueNumber = input.issueNumber;
25969
+ }
25970
+ }
25971
+ };
25972
+ taishiSweepModeInputSchema = typebox_exports.Object(
25973
+ {
25974
+ mode: typebox_exports.Literal("sweep"),
25975
+ mergedPullRequests: typebox_exports.Array(
25976
+ typebox_exports.Object(
25977
+ {
25978
+ projectRoot: typebox_exports.String(),
25979
+ /** 排除后改动行数 — omit or 0 → typed 空缺; finite ≥ 0 only. */
25980
+ changedLines: typebox_exports.Optional(
25981
+ typebox_exports.Number({ minimum: 0, maximum: Number.MAX_VALUE })
25982
+ )
25983
+ },
25984
+ { additionalProperties: false }
25985
+ )
25986
+ )
25987
+ },
25988
+ { additionalProperties: false }
25989
+ );
25990
+ }
25991
+ });
25992
+
25993
+ // src/public-cli/taishi-run.ts
25994
+ import { readFile as readFile15 } from "node:fs/promises";
25995
+ import { isAbsolute as isAbsolute5, resolve as resolve8 } from "node:path";
25996
+ async function buildTaishiIssueModeInputFromPublicArgv(parsed, ledgerHome) {
25997
+ const ticket = parsed.ticket;
25998
+ const directRoot = parsed.projectRoot;
25999
+ if (ticket === void 0) {
26000
+ return {
26001
+ mode: "issue",
26002
+ projectRoot: directRoot
26003
+ };
26004
+ }
26005
+ const index = await readTaishiLibraryIndexPage(ledgerHome);
26006
+ const row = findTaishiLibraryIndexRow(index, ticket);
26007
+ let projectRoot;
26008
+ if (row !== void 0) {
26009
+ projectRoot = row.projectRoot;
26010
+ } else if (directRoot !== void 0) {
26011
+ projectRoot = directRoot;
26012
+ } else {
26013
+ throw new CliUsageError(
26014
+ `taishi library index has no row for ticket ${ticket}`
26015
+ );
26016
+ }
26017
+ const dualParamConflict = row !== void 0 && directRoot !== void 0 && physicalPathIdentity(directRoot) !== physicalPathIdentity(projectRoot);
26018
+ return {
26019
+ mode: "issue",
26020
+ projectRoot,
26021
+ ticketNumber: ticket,
26022
+ issueNumber: ticket,
26023
+ ...dualParamConflict ? { conflictingProjectRoot: directRoot } : {}
26024
+ };
26025
+ }
26026
+ function parseTaishiSweepModeInputFromJsonValue(value) {
26027
+ if (!value_exports.Check(taishiSweepModeInputSchema, value)) {
26028
+ throw new CliUsageError(
26029
+ "taishi sweep attachment must match TaishiSweepModeInput"
26030
+ );
26031
+ }
26032
+ return value;
26033
+ }
26034
+ async function buildTaishiSweepModeInputFromAttachmentPaths(attachmentPaths) {
26035
+ if (attachmentPaths.length !== 1) {
26036
+ throw new CliUsageError(
26037
+ "taishi sweep requires exactly one --attach typed JSON attachment"
26038
+ );
26039
+ }
26040
+ const sourcePath = attachmentPaths[0];
26041
+ const absolute = isAbsolute5(sourcePath) ? sourcePath : resolve8(sourcePath);
26042
+ let bytes;
26043
+ try {
26044
+ bytes = await readFile15(absolute);
26045
+ } catch (error) {
26046
+ throw new CliUsageError(
26047
+ `taishi sweep attachment is not a readable regular file: ${sourcePath}`,
26048
+ { cause: error }
26049
+ );
26050
+ }
26051
+ let text;
26052
+ try {
26053
+ text = exactUtf8(bytes, "taishi sweep attachment");
26054
+ } catch (error) {
26055
+ const detail = error instanceof Error ? error.message : String(error);
26056
+ throw new CliUsageError(detail, { cause: error });
26057
+ }
26058
+ let parsed;
26059
+ try {
26060
+ parsed = JSON.parse(text);
26061
+ } catch (error) {
26062
+ throw new CliUsageError(
26063
+ "taishi sweep attachment is not valid JSON",
26064
+ { cause: error }
26065
+ );
26066
+ }
26067
+ return parseTaishiSweepModeInputFromJsonValue(parsed);
26068
+ }
26069
+ async function runPublicTaishi(argv, _env, io, parseTaishiArgv2) {
26070
+ try {
26071
+ const parsed = parseTaishiArgv2(argv);
26072
+ const ledgerHome = resolveActivationLedgerHome();
26073
+ if (parsed.query === "sweep") {
26074
+ const input2 = await buildTaishiSweepModeInputFromAttachmentPaths(
26075
+ parsed.attachmentPaths
26076
+ );
26077
+ const result3 = await runTaishi(input2);
26078
+ io.stdout(`${JSON.stringify(result3, null, 2)}
26079
+ `);
26080
+ return { exitCode: 0 };
26081
+ }
26082
+ if (parsed.query === "cohort") {
26083
+ const result3 = await runTaishi({
26084
+ mode: "cohort",
26085
+ groups: parsed.groups
26086
+ });
26087
+ io.stdout(`${JSON.stringify(result3, null, 2)}
26088
+ `);
26089
+ return { exitCode: 0 };
26090
+ }
26091
+ if (parsed.query === "model-groups") {
26092
+ const result3 = await runTaishi({
26093
+ mode: "model-groups",
26094
+ projectRoots: parsed.projectRoots
26095
+ });
26096
+ io.stdout(`${JSON.stringify(result3, null, 2)}
26097
+ `);
26098
+ return { exitCode: 0 };
26099
+ }
26100
+ const input = await buildTaishiIssueModeInputFromPublicArgv(parsed, ledgerHome);
26101
+ const result2 = await readOrComputeTaishiIssuePage(input);
26102
+ io.stdout(`${JSON.stringify(result2, null, 2)}
26103
+ `);
26104
+ return { exitCode: 0 };
26105
+ } catch (error) {
26106
+ if (error instanceof CliUsageError) {
26107
+ presentStructuralRejection(error, io);
26108
+ return { exitCode: 2 };
26109
+ }
26110
+ if (error instanceof TaishiIssueComputeError) {
26111
+ const code = errnoCode(error.cause);
26112
+ presentControlledFailure({
26113
+ cause: "output",
26114
+ diagnostic: error.message,
26115
+ ...code === void 0 ? {} : { identity: { code } },
26116
+ details: {
26117
+ code: error.code,
26118
+ projectRoot: error.projectRoot,
26119
+ ...error.issueNumber === void 0 ? {} : { issueNumber: error.issueNumber }
26120
+ }
26121
+ }, io);
26122
+ return { exitCode: 1 };
26123
+ }
26124
+ throw error;
26125
+ }
26126
+ }
26127
+ var init_taishi_run = __esm({
26128
+ "src/public-cli/taishi-run.ts"() {
26129
+ "use strict";
26130
+ init_value2();
26131
+ init_activation_ledger_topology();
26132
+ init_exact_utf8();
26133
+ init_taishi_index();
26134
+ init_taishi_entry();
26135
+ init_cli_errors();
26136
+ init_settlement();
26137
+ }
26138
+ });
26139
+
26140
+ // src/public-cli/cli.ts
26141
+ var cli_exports = {};
26142
+ __export(cli_exports, {
26143
+ CliUsageError: () => CliUsageError,
26144
+ PUBLIC_ROLE_ARGV: () => PUBLIC_ROLE_ARGV,
26145
+ buildExplicitInternalActivationArgs: () => buildExplicitInternalActivationArgs,
26146
+ helpDocument: () => helpDocument,
26147
+ resolveInternalRoleEntrypoint: () => resolveInternalRoleEntrypoint,
26148
+ runAkRole: () => runAkRole
26149
+ });
26150
+ import { realpath as realpath5 } from "node:fs/promises";
26151
+ import { homedir as homedir3 } from "node:os";
26152
+ import { join as join23 } from "node:path";
26153
+ function takePublicGlobalFlag(argv, index) {
26154
+ const token = argv[index];
26155
+ if (token === void 0) return void 0;
26156
+ if (token === "--help" || token === "-h") {
26157
+ return { flag: "help", consume: 1 };
26158
+ }
26159
+ if (token === "--model") {
26160
+ const value = argv[index + 1];
26161
+ if (value === void 0) {
26162
+ return { flag: "model", consume: 1, value: void 0 };
26163
+ }
26164
+ return { flag: "model", consume: 2, value };
26165
+ }
26166
+ if (token.startsWith("--model=")) {
26167
+ return {
26168
+ flag: "model",
26169
+ consume: 1,
26170
+ value: token.slice("--model=".length)
26171
+ };
26172
+ }
26173
+ if (token === "--thinking") {
26174
+ const raw = argv[index + 1];
26175
+ if (raw === void 0) {
26176
+ return { flag: "thinking", consume: 1, raw: void 0 };
26177
+ }
23825
26178
  return { flag: "thinking", consume: 2, raw };
23826
26179
  }
23827
26180
  if (token.startsWith("--thinking=")) {
@@ -23847,7 +26200,7 @@ function resolveHome(env) {
23847
26200
  return env.home ?? process.env.HOME ?? homedir3();
23848
26201
  }
23849
26202
  function resolveAgentDir(env, home) {
23850
- return env.agentDir ?? process.env.PI_CODING_AGENT_DIR ?? join18(home, ".pi", "agent");
26203
+ return env.agentDir ?? process.env.PI_CODING_AGENT_DIR ?? join23(home, ".pi", "agent");
23851
26204
  }
23852
26205
  function parseThinking(value) {
23853
26206
  if (!THINKING_LEVELS2.has(value)) {
@@ -23933,6 +26286,12 @@ function renderHelp() {
23933
26286
  lines.push(` ${cap.name} \u2014 ${phaseText}`);
23934
26287
  }
23935
26288
  }
26289
+ lines.push("", "Deterministic commands:");
26290
+ for (const cap of doc.capabilities) {
26291
+ if (cap.kind === "deterministic") {
26292
+ lines.push(` ${cap.name}`);
26293
+ }
26294
+ }
23936
26295
  lines.push(
23937
26296
  "",
23938
26297
  "Global options: --model provider/model --thinking level",
@@ -24030,6 +26389,9 @@ async function runAkRole(argv, env) {
24030
26389
  }
24031
26390
  if (match.kind === "support") {
24032
26391
  io.stdout(`command ${match.name} kind support
26392
+ `);
26393
+ } else if (match.kind === "deterministic") {
26394
+ io.stdout(`command ${match.name} kind deterministic
24033
26395
  `);
24034
26396
  } else {
24035
26397
  io.stdout(
@@ -24433,6 +26795,15 @@ async function runAkRole(argv, env) {
24433
26795
  ...result2.terminal === void 0 ? {} : { terminal: result2.terminal }
24434
26796
  };
24435
26797
  }
26798
+ if (parsed.command === "taishi") {
26799
+ const result2 = await runPublicTaishi(
26800
+ parsed.args,
26801
+ { home },
26802
+ io,
26803
+ PUBLIC_ROLE_ARGV.taishi.parse
26804
+ );
26805
+ return { exitCode: result2.exitCode };
26806
+ }
24436
26807
  throw new CliUsageError(`unknown command: ${parsed.command}`);
24437
26808
  } catch (error) {
24438
26809
  if (error instanceof CliUsageError) {
@@ -24463,6 +26834,7 @@ var init_cli = __esm({
24463
26834
  init_judge_run();
24464
26835
  init_merger_run();
24465
26836
  init_reviewer_run();
26837
+ init_taishi_run();
24466
26838
  init_run_lifecycle();
24467
26839
  init_registry2();
24468
26840
  init_settlement();
@@ -24475,7 +26847,9 @@ var init_cli = __esm({
24475
26847
  collector: { parse: parseCollectorArgv },
24476
26848
  doctor: { parse: parseDoctorArgv },
24477
26849
  merger: { parse: parseMergerArgv },
24478
- reviewer: { parse: parseReviewerArgv }
26850
+ reviewer: { parse: parseReviewerArgv },
26851
+ /** Deterministic analysis seat (#336) — argv parse only; no LLM admission. */
26852
+ taishi: { parse: parseTaishiArgv }
24479
26853
  };
24480
26854
  THINKING_LEVELS2 = /* @__PURE__ */ new Set([
24481
26855
  "off",
@@ -24490,7 +26864,8 @@ var init_cli = __esm({
24490
26864
  });
24491
26865
 
24492
26866
  // src/public-cli/main.ts
24493
- import { dirname as dirname7, join as join19 } from "node:path";
26867
+ import { existsSync as existsSync2 } from "node:fs";
26868
+ import { dirname as dirname11, join as join24 } from "node:path";
24494
26869
  import { fileURLToPath as fileURLToPath2 } from "node:url";
24495
26870
 
24496
26871
  // src/public-cli/host-pi-runtime.ts
@@ -24577,8 +26952,15 @@ function linkPackage(packageRoot2, name, targetDir) {
24577
26952
  }
24578
26953
 
24579
26954
  // src/public-cli/main.ts
24580
- var here = dirname7(fileURLToPath2(import.meta.url));
24581
- var packageRoot = join19(here, "..", "..");
26955
+ var here = dirname11(fileURLToPath2(import.meta.url));
26956
+ function resolvePackageRoot(binDir) {
26957
+ const canonical = join24(binDir, "..", "..");
26958
+ if (existsSync2(join24(canonical, "package.json"))) {
26959
+ return canonical;
26960
+ }
26961
+ return binDir;
26962
+ }
26963
+ var packageRoot = resolvePackageRoot(here);
24582
26964
  ensureHostPiRuntimeResolvable(packageRoot);
24583
26965
  var { runAkRole: runAkRole2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
24584
26966
  var result = await runAkRole2(process.argv.slice(2), { packageRoot });