@akagilnc/pi-workflow-roles 0.1.2120 → 0.1.2124

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.2120",
3
+ "version": "0.1.2124",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -412,19 +412,17 @@ export type ParseMergerArgvResult = {
412
412
  };
413
413
 
414
414
  /**
415
- * #336/#337/#338 taishi public argv — four faces on one registration seam.
416
- * - issue (default): ticket N and/or project-root P
415
+ * #336/#337/#338/#399 taishi public argv — three live faces on one registration seam.
416
+ * - issue (default): bare whole-book or --ticket N (cwd git common-dir)
417
417
  * - sweep (#337): optional positional `sweep` and/or --attach paths;
418
418
  * sweep payload rides exactly one typed JSON attachment (not argv/stdin)
419
419
  * - cohort: two labeled issue-number groups
420
- * - model-groups: one or more project-root scope keys
420
+ * --project-root deleted; --model-groups public face disabled (library kernel retained).
421
421
  */
422
422
  export type ParseTaishiIssueArgv = {
423
423
  readonly query: "issue";
424
424
  /** Caller ticket / issue number face (#176 numbering space). */
425
425
  readonly ticket?: number;
426
- /** Direct projectRoot mechanical key (ADR 0068). */
427
- readonly projectRoot?: string;
428
426
  };
429
427
 
430
428
  export type ParseTaishiSweepArgv = {
@@ -444,16 +442,10 @@ export type ParseTaishiCohortArgv = {
444
442
  ];
445
443
  };
446
444
 
447
- export type ParseTaishiModelGroupsArgv = {
448
- readonly query: "model-groups";
449
- readonly projectRoots: readonly string[];
450
- };
451
-
452
445
  export type ParseTaishiArgvResult =
453
446
  | ParseTaishiIssueArgv
454
447
  | ParseTaishiSweepArgv
455
- | ParseTaishiCohortArgv
456
- | ParseTaishiModelGroupsArgv;
448
+ | ParseTaishiCohortArgv;
457
449
 
458
450
  /** Honest activation-class failure while deriving the active-merge envelope. */
459
451
  export class MergerEnvelopeDerivationError extends Error {
@@ -2271,13 +2263,6 @@ export function parseTaishiArgv(args: readonly string[]): ParseTaishiArgvResult
2271
2263
  pushValue("ticket", taken.value);
2272
2264
  continue;
2273
2265
  }
2274
- if (taken.def.id === "project-root") {
2275
- pushValue(
2276
- "project-root",
2277
- requireOptionPath(taken.def.canonical, taken.value),
2278
- );
2279
- continue;
2280
- }
2281
2266
  if (taken.def.id === "attach") {
2282
2267
  pushValue(
2283
2268
  "attach",
@@ -2308,6 +2293,20 @@ export function parseTaishiArgv(args: readonly string[]): ParseTaishiArgvResult
2308
2293
  throw new CliUsageError(`unknown taishi option: ${taken.def.canonical}`);
2309
2294
  }
2310
2295
  const token = tokens.shift()!;
2296
+ // #399: deleted --project-root; disabled --model-groups public face.
2297
+ if (isRejectedPublicSpelling("taishi", token)) {
2298
+ if (token === "--project-root" || token.startsWith("--project-root=")) {
2299
+ throw new CliUsageError(
2300
+ "taishi no longer accepts --project-root (deleted); use bare call for whole book or --ticket N (cwd git common-dir selects the book)",
2301
+ );
2302
+ }
2303
+ if (token === "--model-groups" || token.startsWith("--model-groups=")) {
2304
+ throw new CliUsageError(
2305
+ "taishi --model-groups public CLI face is disabled; input face is being redesigned for multi-issue comparison (see follow-up ticket)",
2306
+ );
2307
+ }
2308
+ throw new CliUsageError(`unknown taishi option: ${token}`);
2309
+ }
2311
2310
  if (token.startsWith("-") && token !== "-") {
2312
2311
  throw new CliUsageError(`unknown taishi option: ${token}`);
2313
2312
  }
@@ -2351,13 +2350,6 @@ export function parseTaishiArgv(args: readonly string[]): ParseTaishiArgvResult
2351
2350
  };
2352
2351
  }
2353
2352
 
2354
- if (mode === "model-groups") {
2355
- return {
2356
- query: "model-groups",
2357
- projectRoots: valueLists.get("project-root") ?? [],
2358
- };
2359
- }
2360
-
2361
2353
  if (mode === "sweep") {
2362
2354
  return {
2363
2355
  query: "sweep",
@@ -2366,12 +2358,10 @@ export function parseTaishiArgv(args: readonly string[]): ParseTaishiArgvResult
2366
2358
  }
2367
2359
 
2368
2360
  const ticketRaw = valueLists.get("ticket")?.[0];
2369
- const projectRoot = valueLists.get("project-root")?.[0];
2370
2361
  return {
2371
2362
  query: "issue",
2372
2363
  ...(ticketRaw === undefined
2373
2364
  ? {}
2374
2365
  : { ticket: parseTaishiTicketNumber(ticketRaw) }),
2375
- ...(projectRoot === undefined ? {} : { projectRoot }),
2376
2366
  };
2377
2367
  }
@@ -13,8 +13,8 @@
13
13
 
14
14
  import { CliUsageError } from "./cli-errors.ts";
15
15
 
16
- /** Taishi query faces (#336/#337/#338). */
17
- export type TaishiMode = "issue" | "sweep" | "cohort" | "model-groups";
16
+ /** Taishi public query faces (#336/#337/#338/#399). model-groups CLI face disabled (library kernel retained). */
17
+ export type TaishiMode = "issue" | "sweep" | "cohort";
18
18
 
19
19
  /** Coder/Fixer public phase tokens. */
20
20
  export type RolePhase = "plan" | "apply";
@@ -62,11 +62,10 @@ export type PublicOptionDefinition = {
62
62
  /** Modes in which this option is required (taishi conditional requiredness). */
63
63
  readonly requiredInModes?: readonly TaishiMode[];
64
64
  /**
65
- * Other option ids on the same owner that cannot co-occur
66
- * (e.g. cohort × model-groups).
65
+ * Other option ids on the same owner that cannot co-occur.
67
66
  */
68
67
  readonly exclusiveWith?: readonly string[];
69
- /** Per-mode maximum occurrences (issue `--project-root` ≤ 1). */
68
+ /** Per-mode maximum occurrences. */
70
69
  readonly maxCountByMode?: Readonly<Partial<Record<TaishiMode, number>>>;
71
70
  /**
72
71
  * When this option (or positional) is present it activates this mode.
@@ -85,10 +84,11 @@ export type TaishiRequireAnyOfRule = {
85
84
  readonly optionIds: readonly string[];
86
85
  };
87
86
 
88
- /** Issue face: ticket | project-root at least one. */
89
- export const TAISHI_REQUIRE_ANY_OF = [
90
- { mode: "issue", optionIds: ["ticket", "project-root"] },
91
- ] as const satisfies readonly TaishiRequireAnyOfRule[];
87
+ /**
88
+ * Cross-field at-least-one rules for taishi modes.
89
+ * #399: issue bare call is lawful (whole book from cwd) — no require-any-of on issue.
90
+ */
91
+ export const TAISHI_REQUIRE_ANY_OF: readonly TaishiRequireAnyOfRule[] = [];
92
92
 
93
93
  /** Residual taishi mode when no `selectsMode` option is present. */
94
94
  export const TAISHI_DEFAULT_MODE: TaishiMode = "issue";
@@ -107,7 +107,6 @@ export function resolveTaishiMode(
107
107
  }
108
108
  if (selected.size === 0) return TAISHI_DEFAULT_MODE;
109
109
  if (selected.has("cohort")) return "cohort";
110
- if (selected.has("model-groups")) return "model-groups";
111
110
  if (selected.has("sweep")) return "sweep";
112
111
  if (selected.has("issue")) return "issue";
113
112
  return TAISHI_DEFAULT_MODE;
@@ -181,13 +180,6 @@ export function evaluateTaishiModeOptionContract(
181
180
  "usage: ak-role taishi --cohort --group-a-label <L> --group-a-issues <N[,N...]> --group-b-label <L> --group-b-issues <N[,N...]",
182
181
  };
183
182
  }
184
- if (mode === "model-groups") {
185
- return {
186
- ok: false,
187
- message:
188
- "usage: ak-role taishi --model-groups --project-root <P> [--project-root <P> ...]",
189
- };
190
- }
191
183
  return {
192
184
  ok: false,
193
185
  message: `usage: ak-role taishi ${mode} requires ${missingRequired
@@ -205,7 +197,7 @@ export function evaluateTaishiModeOptionContract(
205
197
  return {
206
198
  ok: false,
207
199
  message:
208
- "usage: ak-role taishi ((--ticket <N> | --project-root <P>) | [sweep] --attach <sweep.json> | --cohort ... | --model-groups ...)",
200
+ "usage: ak-role taishi ([--ticket <N>] | [sweep] --attach <sweep.json> | --cohort ...)",
209
201
  };
210
202
  }
211
203
  const flags = rule.optionIds
@@ -247,6 +239,22 @@ export const REJECTED_PUBLIC_SPELLINGS = [
247
239
  zh: "校书郎 input packet 由内部装配,不是公开旗标。",
248
240
  },
249
241
  },
242
+ {
243
+ owner: "taishi",
244
+ spellings: ["--project-root"],
245
+ reason: {
246
+ en: "Deleted (#399). taishi no longer accepts --project-root; use bare call for whole book or --ticket N (cwd git common-dir selects the book).",
247
+ zh: "已删除(#399)。taishi 不再接受 --project-root;裸调用=整簿,或 --ticket N(cwd git common-dir 定簿)。",
248
+ },
249
+ },
250
+ {
251
+ owner: "taishi",
252
+ spellings: ["--model-groups"],
253
+ reason: {
254
+ en: "Public CLI face disabled (#399). Input face is being redesigned for multi-issue comparison (see follow-up ticket). Library kernel retained.",
255
+ zh: "公开 CLI 面已停用(#399)。输入面按多 issue 重设计中(见后续票)。聚合内核保留。",
256
+ },
257
+ },
250
258
  ] as const satisfies readonly RejectedSpelling[];
251
259
 
252
260
  const GLOBAL_OPTIONS = [
@@ -311,8 +319,8 @@ const GLOBAL_OPTIONS = [
311
319
  /**
312
320
  * Immutable shared semantics for the common ledger `--project` face.
313
321
  * Role rows bind owner only — do not copy these fields per role.
314
- * Role-specific project faces (merger merge-root; taishi `--project-root`)
315
- * stay explicit below and must not use this binding.
322
+ * Role-specific project faces (merger merge-root) stay explicit below and
323
+ * must not use this binding. taishi `--project-root` is deleted (#399).
316
324
  */
317
325
  const SHARED_PROJECT_SEMANTICS = {
318
326
  id: "project",
@@ -565,23 +573,6 @@ const TAISHI_OPTIONS = [
565
573
  zh: "可选 sweep 模式词元(至多一次;不得夹带其他 positional)。",
566
574
  },
567
575
  },
568
- {
569
- id: "project-root",
570
- owner: "taishi",
571
- canonical: "--project-root",
572
- aliases: [],
573
- valueMetavar: "path",
574
- required: false,
575
- repeatable: true,
576
- form: "option",
577
- modes: ["issue", "model-groups"],
578
- requiredInModes: ["model-groups"],
579
- maxCountByMode: { issue: 1 },
580
- description: {
581
- en: "Project-root scope key. Issue: at most one (with --ticket at least one of the two). Model-groups: one or more required.",
582
- zh: "projectRoot 范围键。issue:至多一个(与 --ticket 至少居其一)。model-groups:一个或多个且必填。",
583
- },
584
- },
585
576
  {
586
577
  id: "ticket",
587
578
  owner: "taishi",
@@ -593,8 +584,8 @@ const TAISHI_OPTIONS = [
593
584
  form: "option",
594
585
  modes: ["issue"],
595
586
  description: {
596
- en: "Ticket/issue number for issue mode (with --project-root at least one of the two).",
597
- zh: "issue 模式的票号(与 --project-root 至少居其一)。",
587
+ en: "Ticket/issue number; live filter by invocation.ticketNumber inside the cwd book (git common-dir). Bare call = whole book. No library-index bootstrap.",
588
+ zh: "票号;在 cwd 候簿(git common-dir)内按 invocation.ticketNumber 现取现算。裸调用=整簿。不依赖 library-index 自举。",
598
589
  },
599
590
  },
600
591
  {
@@ -625,28 +616,10 @@ const TAISHI_OPTIONS = [
625
616
  repeatable: false,
626
617
  form: "option",
627
618
  modes: ["cohort"],
628
- exclusiveWith: ["model-groups"],
629
619
  selectsMode: "cohort",
630
620
  description: {
631
- en: "Select cohort mode (mutually exclusive with --model-groups).",
632
- zh: "选择 cohort 模式(与 --model-groups 互斥)。",
633
- },
634
- },
635
- {
636
- id: "model-groups",
637
- owner: "taishi",
638
- canonical: "--model-groups",
639
- aliases: [],
640
- valueMetavar: null,
641
- required: false,
642
- repeatable: false,
643
- form: "option",
644
- modes: ["model-groups"],
645
- exclusiveWith: ["cohort"],
646
- selectsMode: "model-groups",
647
- description: {
648
- en: "Select model-groups mode (mutually exclusive with --cohort).",
649
- zh: "选择 model-groups 模式(与 --cohort 互斥)。",
621
+ en: "Select cohort mode.",
622
+ zh: "选择 cohort 模式。",
650
623
  },
651
624
  },
652
625
  {
@@ -1097,14 +1070,14 @@ const ROLE_COMMAND_HELP = {
1097
1070
  },
1098
1071
  taishi: {
1099
1072
  command: "taishi",
1100
- summary: "Deterministic analysis seat (issue / sweep / cohort / model-groups).",
1073
+ summary: "Deterministic analysis seat (issue / sweep / cohort).",
1101
1074
  usage: [
1102
- "ak-role taishi (--ticket <N> | --project-root <P>) [options]",
1075
+ "ak-role taishi [--ticket <N>]",
1103
1076
  "ak-role taishi [sweep] --attach <path>",
1104
1077
  "ak-role taishi --cohort --group-a-label <L> --group-a-issues <N[,N...]> --group-b-label <L> --group-b-issues <N[,N...]>",
1105
- "ak-role taishi --model-groups --project-root <P> [--project-root <P> ...]",
1106
1078
  ],
1107
1079
  examples: [
1080
+ "ak-role taishi",
1108
1081
  "ak-role taishi --ticket 125",
1109
1082
  "ak-role taishi sweep --attach ./sweep.json",
1110
1083
  ],
@@ -1,11 +1,13 @@
1
1
  /**
2
- * Public taishi adapter (#336/#337/#338): argv → typed query → runTaishi family.
2
+ * Public taishi adapter (#336/#337/#338/#399): argv → typed query → runTaishi family.
3
3
  * Deterministic analysis seat — no Pi runner, no admission lease.
4
4
  * Reuses existing CLI failure envelope (CliUsageError + structural reject +
5
5
  * ControlledFailure).
6
- * Index read reuses readTaishiLibraryIndexPage / findTaishiLibraryIndexRow.
6
+ * #399: issue query = book (cwd git common-dir) × optional --ticket N.
7
+ * Bare call = whole book; --project-root deleted; --model-groups public face disabled;
8
+ * no library-index bootstrap. Library model-groups kernel retained for follow-up.
7
9
  * #337 sweep: exactly one typed JSON attachment → TaishiSweepModeInput → #329 kernel.
8
- * #338: three query faces; sync compute-if-missing; whole-compute failure →
10
+ * #338: issue/cohort (+ library model-groups); sync compute-if-missing; whole-compute failure →
9
11
  * ControlledFailure terminal (code/projectRoot/issueNumber/real cause).
10
12
  * "Unobtrusive" binds #337 merge auto-trigger only, not this user-initiated query.
11
13
  */
@@ -13,16 +15,16 @@ import { readFile } from "node:fs/promises";
13
15
  import { isAbsolute, resolve } from "node:path";
14
16
  import { Value } from "typebox/value";
15
17
 
18
+ import {
19
+ ActivationGitRepositoryRequiredError,
20
+ resolveBookKeyFromGit,
21
+ } from "../activation-ledger-git.ts";
16
22
  import {
17
23
  errnoCode,
18
24
  physicalPathIdentity,
19
25
  resolveActivationLedgerHome,
20
26
  } from "../activation-ledger-topology.ts";
21
27
  import { exactUtf8 } from "../exact-utf8.ts";
22
- import {
23
- findTaishiLibraryIndexRow,
24
- readTaishiLibraryIndexPage,
25
- } from "../taishi-index.ts";
26
28
  import {
27
29
  readOrComputeTaishiIssuePage,
28
30
  runTaishi,
@@ -44,58 +46,52 @@ export type TaishiRunEnv = {
44
46
  };
45
47
 
46
48
  /**
47
- * Build the sole library issue-mode input from public argv faces.
48
- * - ticket N → issueNumber = ticketNumber = N; projectRoot from index (or project-root fallback).
49
- * - project-root P → direct mechanical key.
50
- * - both + index hit → index projectRoot wins; when direct root differs, retain it as
51
- * conflictingProjectRoot so runTaishi records the C4 dual-param conflict fact on the page.
52
- * - both + index miss → project-root fallback.
53
- * Bare both-missing is owned by parseTaishiArgv — no second reject here.
49
+ * Resolve the issue-query book from cwd git common-dir (same owner as record layer).
50
+ * Non-git cwd fails loud — bare / --ticket both require a book identity.
51
+ */
52
+ export function resolveTaishiIssueBookKeyFromCwd(cwd: string = process.cwd()): string {
53
+ try {
54
+ return resolveBookKeyFromGit(cwd);
55
+ } catch (error) {
56
+ if (error instanceof ActivationGitRepositoryRequiredError) {
57
+ throw new CliUsageError(
58
+ "taishi issue query requires a git repository cwd (book = git common-dir); run inside a repository (bare = whole book, or --ticket N)",
59
+ { cause: error },
60
+ );
61
+ }
62
+ throw error;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Build the sole library issue-mode input from public argv faces (#399).
68
+ * - bare → whole book from cwd git common-dir
69
+ * - --ticket N → issueNumber = ticketNumber = N inside that book (strict; no index)
70
+ * - --project-root rejected at parse (deleted unconditionally)
54
71
  */
55
72
  export async function buildTaishiIssueModeInputFromPublicArgv(
56
73
  parsed: ParseTaishiIssueArgv,
57
- ledgerHome: string,
74
+ _ledgerHome: string,
58
75
  ): Promise<TaishiIssueModeInput> {
76
+ const cwd = process.cwd();
77
+ const bookKey = resolveTaishiIssueBookKeyFromCwd(cwd);
78
+ const projectRoot = physicalPathIdentity(cwd);
59
79
  const ticket = parsed.ticket;
60
- const directRoot = parsed.projectRoot;
61
80
 
62
81
  if (ticket === undefined) {
63
82
  return {
64
83
  mode: "issue",
65
- projectRoot: directRoot!,
84
+ bookKey,
85
+ projectRoot,
66
86
  };
67
87
  }
68
88
 
69
- // ticket N = issueNumber (no conversion); also the C4 typed ticket face.
70
- const index = await readTaishiLibraryIndexPage(ledgerHome);
71
- const row = findTaishiLibraryIndexRow(index, ticket);
72
-
73
- let projectRoot: string;
74
- if (row !== undefined) {
75
- // Ticket-resolved index projectRoot wins over any concurrent --project-root.
76
- projectRoot = row.projectRoot;
77
- } else if (directRoot !== undefined) {
78
- // Index miss with project-root fallback (ticket faces still set for C4).
79
- projectRoot = directRoot;
80
- } else {
81
- throw new CliUsageError(
82
- `taishi library index has no row for ticket ${ticket}`,
83
- );
84
- }
85
-
86
- // Dual-param conflict: index root won, but caller also supplied a distinct --project-root.
87
- // Carry the losing root so the metrics page records the call-face conflict fact.
88
- const dualParamConflict =
89
- row !== undefined
90
- && directRoot !== undefined
91
- && physicalPathIdentity(directRoot) !== physicalPathIdentity(projectRoot);
92
-
93
89
  return {
94
90
  mode: "issue",
91
+ bookKey,
95
92
  projectRoot,
96
93
  ticketNumber: ticket,
97
94
  issueNumber: ticket,
98
- ...(dualParamConflict ? { conflictingProjectRoot: directRoot } : {}),
99
95
  };
100
96
  }
101
97
 
@@ -164,7 +160,8 @@ export async function buildTaishiSweepModeInputFromAttachmentPaths(
164
160
 
165
161
  /**
166
162
  * Public taishi run path — parse → resolve → query → typed receipt on stdout.
167
- * Issue (#336/#338 compute-if-missing), sweep (#337), cohort/model-groups (#338).
163
+ * Issue (#336/#338 compute-if-missing), sweep (#337), cohort (#338).
164
+ * model-groups public face disabled at parse (#399); library kernel retained.
168
165
  */
169
166
  export async function runPublicTaishi(
170
167
  argv: readonly string[],
@@ -195,15 +192,6 @@ export async function runPublicTaishi(
195
192
  return { exitCode: 0 };
196
193
  }
197
194
 
198
- if (parsed.query === "model-groups") {
199
- const result = await runTaishi({
200
- mode: "model-groups",
201
- projectRoots: parsed.projectRoots,
202
- });
203
- io.stdout(`${JSON.stringify(result, null, 2)}\n`);
204
- return { exitCode: 0 };
205
- }
206
-
207
195
  // issue query — compute-if-missing (#338); sole kernel on miss.
208
196
  const input = await buildTaishiIssueModeInputFromPublicArgv(parsed, ledgerHome);
209
197
  const result = await readOrComputeTaishiIssuePage(input);
@@ -224,6 +212,7 @@ export async function runPublicTaishi(
224
212
  ...(code === undefined ? {} : { identity: { code } }),
225
213
  details: {
226
214
  code: error.code,
215
+ bookKey: error.bookKey,
227
216
  projectRoot: error.projectRoot,
228
217
  ...(error.issueNumber === undefined ? {} : { issueNumber: error.issueNumber }),
229
218
  },
@@ -32,6 +32,7 @@ import type { TaishiAcceptanceSuccessReworkSection } from "./taishi-metric-famil
32
32
  export type TaishiIssuePageEnsuring = (input: {
33
33
  readonly projectRoot: string;
34
34
  readonly issueNumber: number;
35
+ readonly bookKey?: string;
35
36
  }) => Promise<TaishiIssueMetricsPage>;
36
37
 
37
38
  /** LOC-style optional metric — never encode absence as 0 or Infinity. */
@@ -56,6 +57,7 @@ export type TaishiCohortIssueEntry =
56
57
  | {
57
58
  readonly issueNumber: number;
58
59
  readonly status: "present";
60
+ readonly bookKey: string;
59
61
  readonly projectRoot: string;
60
62
  }
61
63
  | {
@@ -170,11 +172,13 @@ async function aggregateGroup(
170
172
  const page = (await ensureIssuePage({
171
173
  projectRoot: row.projectRoot,
172
174
  issueNumber,
175
+ bookKey: row.bookKey,
173
176
  })) as TaishiCohortSourcePage;
174
177
 
175
178
  issueEntries.push({
176
179
  issueNumber,
177
180
  status: "present",
181
+ bookKey: row.bookKey,
178
182
  projectRoot: row.projectRoot,
179
183
  });
180
184