@akagilnc/pi-workflow-roles 0.1.5593 → 0.1.5614

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 (58) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/acp-host/production-host.js +521 -707
  4. package/dist/compliance-transport.js +2 -5
  5. package/dist/doctor-auditor.js +1 -1
  6. package/dist/gatekeeper-role.js +1 -1
  7. package/dist/headless-host/production-host.js +505 -691
  8. package/dist/merger-contracts.js +6 -2
  9. package/dist/migrate-book-topology.js +5142 -10735
  10. package/dist/navigator-public-session.js +4 -4
  11. package/dist/navigator-work-base.js +5 -30
  12. package/dist/package-contracts/worker-output.js +1 -6
  13. package/dist/packaged-role-registry.js +9 -6
  14. package/dist/pi/role-turn-host.js +10 -1
  15. package/dist/public-cli/instruction-seat-run.js +133 -38
  16. package/dist/public-cli/invocation.js +38 -74
  17. package/dist/public-cli/main.js +18155 -18235
  18. package/dist/public-cli/option-definitions.js +21 -2
  19. package/dist/public-cli/post-admission.js +25 -24
  20. package/dist/public-cli/run-lifecycle.js +4 -43
  21. package/dist/public-cli/turn-request.js +4 -9
  22. package/dist/public-role-summons.js +5 -3
  23. package/dist/role-run-placement.js +7 -1
  24. package/dist/role-run-relocation.js +3 -4
  25. package/package.json +1 -1
  26. package/scripts/build-package.mjs +2 -4
  27. package/src/audited-run-identity.ts +30 -0
  28. package/src/compliance-transport.ts +1 -4
  29. package/src/doctor-auditor.ts +1 -1
  30. package/src/gatekeeper-role.ts +1 -1
  31. package/src/host-contracts.ts +6 -4
  32. package/src/merger-contracts.ts +6 -2
  33. package/src/merger-role.ts +11 -1
  34. package/src/navigator-attendance.ts +2 -2
  35. package/src/navigator-public-session.ts +9 -5
  36. package/src/navigator-work-base.ts +19 -34
  37. package/src/notary-role.ts +12 -21
  38. package/src/package-contracts/worker-output.ts +0 -8
  39. package/src/packaged-role-registry.ts +8 -7
  40. package/src/pi/role-turn-host.ts +11 -1
  41. package/src/public-cli/instruction-seat-run.ts +162 -42
  42. package/src/public-cli/invocation.ts +55 -93
  43. package/src/public-cli/option-definitions.ts +22 -2
  44. package/src/public-cli/post-admission.ts +30 -24
  45. package/src/public-cli/run-lifecycle.ts +4 -56
  46. package/src/public-cli/turn-request.ts +9 -8
  47. package/src/public-role-summons.ts +17 -6
  48. package/src/role-envelope.ts +4 -0
  49. package/src/role-run-placement.ts +10 -1
  50. package/src/role-run-relocation.ts +3 -4
  51. package/src/role-runtime-dependencies.ts +0 -2
  52. package/src/role-runtime.ts +54 -47
  53. package/src/worker-role.ts +11 -78
  54. package/dist/auditor-dossier-tool.js +0 -47
  55. package/dist/package-contracts/fixer-packet.js +0 -77
  56. package/packets/fixer-prerequisites.json +0 -6
  57. package/src/auditor-dossier-tool.ts +0 -63
  58. package/src/package-contracts/fixer-packet.ts +0 -91
@@ -370,8 +370,8 @@ const FIXER_OPTIONS = [
370
370
  repeatable: false,
371
371
  form: "option",
372
372
  description: {
373
- en: "JSON array of {id, requirement} prerequisite objects.",
374
- zh: "{id, requirement} 前置条件 JSON 数组路径。",
373
+ en: "Caller prerequisites path passed as-is to the role. Package does not read, copy, or validate; use an absolute path when cwd differs from --project.",
374
+ zh: "调用方前置条件文件路径原样递给衙门。包不读、不复制、不校验;调用方当前目录与 --project 不同时请给绝对路径。",
375
375
  },
376
376
  },
377
377
  ];
@@ -514,6 +514,24 @@ const NOTARY_OPTIONS = [
514
514
  },
515
515
  },
516
516
  ];
517
+ /** 台院: optional --source-run binds the audited leg; gate summons always pass it (#1166). */
518
+ const INSPECTOR_OPTIONS = [
519
+ ...projectAndAttach("inspector"),
520
+ {
521
+ id: "source-run",
522
+ owner: "inspector",
523
+ canonical: "--source-run",
524
+ aliases: [],
525
+ valueMetavar: "runId@role|path",
526
+ required: false,
527
+ repeatable: false,
528
+ form: "option",
529
+ description: {
530
+ en: "Optional audited source-run locator (runId@role or path). Gate summons always supply it; identity rides startup materials.",
531
+ zh: "可选被审腿定位符(runId@role 或路径)。闸传召必带;身份在启动材料里。",
532
+ },
533
+ },
534
+ ];
517
535
  const MERGER_OPTIONS = [
518
536
  // Merger project face: worktree whose Git materials are handed to the role.
519
537
  {
@@ -677,6 +695,7 @@ const SEAT_OPTION_ROWS = {
677
695
  merger: MERGER_OPTIONS,
678
696
  notary: NOTARY_OPTIONS,
679
697
  auditor: AUDITOR_OPTIONS,
698
+ inspector: INSPECTOR_OPTIONS,
680
699
  };
681
700
  function seatOptionRows(role) {
682
701
  return SEAT_OPTION_ROWS[role] ?? projectAndAttach(role);
@@ -10,7 +10,7 @@ import { writeFile } from "node:fs/promises";
10
10
  import { buildAutoResumeContinuationPrompt, findRunDirectoryById, readRoleRunState, RESUME_TRANSPORT_ENVELOPE, } from "./run-lifecycle.js";
11
11
  import { CliUsageError } from "./cli-errors.js";
12
12
  import { projectPublicTurnAxes } from "./turn-request.js";
13
- import { bindAdmittedTicketNumber, buildInstructionTransportPrompt, relocateAdmittedRunToTicket, } from "./invocation.js";
13
+ import { appendCallerFileFlagPaths, bindAdmittedTicketNumber, relocateAdmittedRunToTicket, } from "./invocation.js";
14
14
  import { readRecordedSubmissionRows } from "../submission-ledger.js";
15
15
  import { parseTicketNumber, readDeclaredTicketNumber } from "../run-ticket-number.js";
16
16
  import { readStoredHostSessionId } from "../session-identity.js";
@@ -49,12 +49,12 @@ function projectRelocatedTurnIdentity(request, result, admitted, relocation) {
49
49
  mutableRequest.principal = admitted.principal;
50
50
  const activation = mutableRequest.activation;
51
51
  if (isRecord(activation)) {
52
- for (const field of ["taskPath", "packetPath", "prerequisitesPath", "inputPath"]) {
52
+ for (const field of ["inputPath"]) {
53
53
  const record = activation;
54
54
  if (field in record)
55
55
  record[field] = rewrite(record[field]);
56
56
  }
57
- // #1165: activation.requestManifestPath is a caller path — do not rewrite.
57
+ // #1165/#1168: caller file-flag paths are not activation fields to rewrite.
58
58
  }
59
59
  if (result.terminal !== undefined) {
60
60
  for (const artifact of result.terminal.artifacts) {
@@ -906,8 +906,8 @@ export async function dispatchPostAdmissionTurn(input) {
906
906
  * Shared resume continuation projection (#471 / #600 / #633 / #637 / #755 / #879):
907
907
  * seat-table model/engine/timeout axes, restored correlation, and either
908
908
  * - manual resume (no same-ticket summons): caller message bytes, or
909
- * - same-ticket summons (审核循环续话): caller/peer words + optional frozen
910
- * attachment paths only — no「请重读」、no code-authored content substitute,
909
+ * - same-ticket summons (审核循环续话): caller/peer words + optional caller file-flag
910
+ * paths only — no「请重读」、no code-authored content substitute,
911
911
  * no engine handbook packaging (#750/#755/#879).
912
912
  * Caller message wins as prompt base when supplied (bytes unchanged, including
913
913
  * blank/whitespace); else summons instruction (parent payload or reask).
@@ -916,22 +916,17 @@ export async function dispatchPostAdmissionTurn(input) {
916
916
  * first when request.summons carries instruction or attachment paths.
917
917
  */
918
918
  export function resumeTurnRequestProjectionOptions(admitted, request, env, summonsPrepared) {
919
- // #879: officer dialogue content is caller/peer words only — no「请重读」,
920
- // no code-authored constant substitute, no attachment-list wrap of peer body.
921
- // Binding pointer stays on summons sourceRunPath / activation / attachments.
919
+ // #879: officer dialogue is caller/peer words; #1166 J11 merges caller file-flag
920
+ // paths when present. Gate with no file flags stays body-only (ADR 0085).
922
921
  const officerDialogue = isStationChildOfficerDialogue(admitted.role, env);
922
+ const fileFlags = summonsPrepared?.attachments ?? [];
923
923
  let prompt;
924
924
  if (request.message !== undefined) {
925
925
  if (summonsPrepared !== undefined) {
926
- // #755: same-ticket review / open-court — caller words.
927
- // #879 station-child officer: words only (paths remain on summons materials).
928
- prompt = officerDialogue
926
+ // #755: same-ticket review / open-court — caller words + caller file flags.
927
+ prompt = officerDialogue && fileFlags.length === 0
929
928
  ? request.message
930
- : buildInstructionTransportPrompt({
931
- instruction: request.message,
932
- instructionEmpty: false,
933
- attachments: summonsPrepared.attachments,
934
- });
929
+ : appendCallerFileFlagPaths(request.message, fileFlags);
935
930
  }
936
931
  else if (request.summons !== undefined) {
937
932
  // #755: same-ticket summons without prepared materials — caller words only.
@@ -942,13 +937,11 @@ export function resumeTurnRequestProjectionOptions(admitted, request, env, summo
942
937
  }
943
938
  }
944
939
  else if (summonsPrepared !== undefined) {
945
- // #879 station-child officer: instruction bytes === peer body/reask (no wrap).
946
- // Other seats keep #755 instruction + optional attachment path listing.
947
- // #1165 J3: officer dialogue is raw instruction bytes; do not gate on
948
- // instructionEmpty metadata (resume of older admitted pages).
949
- prompt = officerDialogue
950
- ? summonsPrepared.instruction
951
- : buildInstructionTransportPrompt(summonsPrepared);
940
+ const body = summonsPrepared.instruction;
941
+ // No file flags: body alone (gate peer words). With flags: merge delivery.
942
+ prompt = officerDialogue && fileFlags.length === 0
943
+ ? body
944
+ : appendCallerFileFlagPaths(body, fileFlags);
952
945
  }
953
946
  else if (request.summons !== undefined) {
954
947
  // #879: same-ticket summons with no instruction (e.g. notary source-run binding
@@ -971,6 +964,10 @@ export function resumeTurnRequestProjectionOptions(admitted, request, env, summo
971
964
  ...(env.stationChild === undefined ? {} : { stationChild: env.stationChild }),
972
965
  // #1132: one configured ceiling, already resolved by the caller (#422).
973
966
  deliveryRequestLimit: deliveryLimitFromConfig(env.autoResumeLimit),
967
+ ...(env.navigatorWorkContextPath === undefined
968
+ || env.navigatorWorkContextPath.trim() === ""
969
+ ? {}
970
+ : { navigatorWorkContextPath: env.navigatorWorkContextPath }),
974
971
  };
975
972
  }
976
973
  /** Shared new-turn and in-call auto-resume projection. Seat code supplies prompt and activation. */
@@ -986,6 +983,10 @@ export function roleTurnOptions(env, admitted, continuation) {
986
983
  ...(env.stationChild === undefined ? {} : { stationChild: env.stationChild }),
987
984
  // #1132: one configured ceiling, already resolved by the caller (#422).
988
985
  deliveryRequestLimit: deliveryLimitFromConfig(env.autoResumeLimit),
986
+ ...(env.navigatorWorkContextPath === undefined
987
+ || env.navigatorWorkContextPath.trim() === ""
988
+ ? {}
989
+ : { navigatorWorkContextPath: env.navigatorWorkContextPath }),
989
990
  };
990
991
  }
991
992
  /**
@@ -1070,7 +1071,7 @@ export async function prepareSummonsResumeMaterials(_runDirectory, summons) {
1070
1071
  * validation, turn builder, and adapters stay on the seat.
1071
1072
  *
1072
1073
  * Court handling (#637): public manual resume reads only the open court's
1073
- * settlement identity; internal re-summons may freeze and record its materials.
1074
+ * settlement identity; internal re-summons passes caller file-flag paths as-is.
1074
1075
  */
1075
1076
  export async function runPostAdmissionSeatResume(input) {
1076
1077
  let request = input.request;
@@ -631,18 +631,9 @@ export async function findRunDirectoryById(home, runId, onlyBookKey, onlyRole) {
631
631
  .filter((bookKey) => onlyBookKey === undefined || bookKey === onlyBookKey)
632
632
  .map((bookKey) => activationBookDirectory(ledgerHome, bookKey)), runId, onlyRole);
633
633
  }
634
- /** Code-owned gate inspector summons prefix (public-role-summons / #747). */
635
- export const GATE_DOSSIER_POINTER_PREFIX = "卷宗指针:";
636
- /** Parent path from a code-owned inspector 卷宗指针 instruction; else undefined. */
637
- export function parentRunPathFromGatePointerInstruction(instruction) {
638
- if (!instruction.startsWith(GATE_DOSSIER_POINTER_PREFIX))
639
- return undefined;
640
- const path = instruction.slice(GATE_DOSSIER_POINTER_PREFIX.length).trim();
641
- return path === "" ? undefined : path;
642
- }
643
634
  /**
644
- * Parent-run binding on a retained officer run (#747).
645
- * Notary/auditor: typed sourceRunPath. Inspector: exact code-owned 卷宗指针 instruction.
635
+ * Parent-run binding on a retained officer run (#747 / #1166).
636
+ * Typed sourceRunPath only — instruction bytes are never a path protocol.
646
637
  * Missing page → undefined; damage / non-ENOENT IO propagates.
647
638
  */
648
639
  export async function readRunParentPath(runDirectory) {
@@ -652,9 +643,6 @@ export async function readRunParentPath(runDirectory) {
652
643
  if (typeof record.sourceRunPath === "string" && record.sourceRunPath.trim() !== "") {
653
644
  return record.sourceRunPath;
654
645
  }
655
- if (typeof record.instruction === "string") {
656
- return parentRunPathFromGatePointerInstruction(record.instruction);
657
- }
658
646
  return undefined;
659
647
  }
660
648
  /**
@@ -766,10 +754,7 @@ async function loadResumableRunRecord(home, runId, authority) {
766
754
  let instructionEmpty = true;
767
755
  let attachments = [];
768
756
  let phase;
769
- let taskPath;
770
- let packetPath;
771
757
  let prerequisitesPath;
772
- let prerequisites;
773
758
  let baseRevision;
774
759
  let lens;
775
760
  let authorityRefs;
@@ -813,19 +798,10 @@ async function loadResumableRunRecord(home, runId, authority) {
813
798
  if (record.phase === "plan" || record.phase === "apply") {
814
799
  phase = record.phase;
815
800
  }
816
- if (typeof record.taskPath === "string" && record.taskPath.trim() !== "") {
817
- taskPath = record.taskPath;
818
- }
819
- if (typeof record.packetPath === "string" && record.packetPath.trim() !== "") {
820
- packetPath = record.packetPath;
821
- }
822
- if (typeof record.prerequisitesPath === "string" &&
823
- record.prerequisitesPath.trim() !== "") {
801
+ // #1168: opaque caller path on resume — do not trim-filter provided values.
802
+ if (typeof record.prerequisitesPath === "string") {
824
803
  prerequisitesPath = record.prerequisitesPath;
825
804
  }
826
- if (Array.isArray(record.prerequisites)) {
827
- prerequisites = record.prerequisites;
828
- }
829
805
  if (typeof record.baseRevision === "string" &&
830
806
  record.baseRevision.trim() !== "") {
831
807
  baseRevision = record.baseRevision;
@@ -974,10 +950,7 @@ async function loadResumableRunRecord(home, runId, authority) {
974
950
  instructionEmpty,
975
951
  attachments,
976
952
  ...(phase === undefined ? {} : { phase }),
977
- ...(taskPath === undefined ? {} : { taskPath }),
978
- ...(packetPath === undefined ? {} : { packetPath }),
979
953
  ...(prerequisitesPath === undefined ? {} : { prerequisitesPath }),
980
- ...(prerequisites === undefined ? {} : { prerequisites }),
981
954
  ...(baseRevision === undefined ? {} : { baseRevision }),
982
955
  ...(lens === undefined ? {} : { lens }),
983
956
  ...(authorityRefs === undefined ? {} : { authorityRefs }),
@@ -1079,10 +1052,6 @@ function admitResumedRole(loaded) {
1079
1052
  }
1080
1053
  case "worker-task": {
1081
1054
  const phase = resumedWorkerPhase(fields.phase ?? loaded.run.phase, record.phases, role, runId);
1082
- const taskPath = fields.taskPath;
1083
- if (taskPath === undefined) {
1084
- throw new CliUsageError(`role run admitted coder task path is missing: ${runId}`);
1085
- }
1086
1055
  if (fields.instruction.trim() === "") {
1087
1056
  throw new CliUsageError(`role run admitted coder task is blank: ${runId}`);
1088
1057
  }
@@ -1091,30 +1060,22 @@ function admitResumedRole(loaded) {
1091
1060
  phase,
1092
1061
  ...base,
1093
1062
  instructionEmpty: false,
1094
- taskPath,
1095
1063
  };
1096
1064
  return admitted;
1097
1065
  }
1098
1066
  case "worker-packet": {
1099
1067
  const phase = resumedWorkerPhase(fields.phase ?? loaded.run.phase, record.phases, role, runId);
1100
- const packetPath = fields.packetPath;
1101
- if (packetPath === undefined) {
1102
- throw new CliUsageError(`role run admitted fixer packet path is missing: ${runId}`);
1103
- }
1104
1068
  if (fields.instruction.trim() === "") {
1105
1069
  throw new CliUsageError(`role run admitted fixer instruction is blank: ${runId}`);
1106
1070
  }
1107
- const prerequisites = fields.prerequisites ?? Object.freeze([]);
1108
1071
  const admitted = {
1109
1072
  role: "fixer",
1110
1073
  phase,
1111
1074
  ...base,
1112
1075
  instructionEmpty: false,
1113
- packetPath,
1114
1076
  ...(fields.prerequisitesPath === undefined
1115
1077
  ? {}
1116
1078
  : { prerequisitesPath: fields.prerequisitesPath }),
1117
- prerequisites,
1118
1079
  };
1119
1080
  return admitted;
1120
1081
  }
@@ -1,7 +1,6 @@
1
1
  import { pickEngineAxis } from "../package-resources/engine-material.js";
2
2
  import { packagedRoleActivationFlags, packagedRoleMetadata, } from "../packaged-role-registry.js";
3
3
  import { installedMethodSkillPath } from "./machine-method-skills.js";
4
- import { parentRunPathFromGatePointerInstruction } from "./run-lifecycle.js";
5
4
  /**
6
5
  * Env axes shared by every public turn projection.
7
6
  * Callers add correlation, continuation, host, and station-only fields.
@@ -49,6 +48,10 @@ export function projectRoleTurnRequest(admitted, roleDetails, options) {
49
48
  ...(options.deliveryRequestLimit === undefined
50
49
  ? {}
51
50
  : { deliveryRequestLimit: options.deliveryRequestLimit }),
51
+ ...(options.navigatorWorkContextPath === undefined
52
+ || options.navigatorWorkContextPath.trim() === ""
53
+ ? {}
54
+ : { navigatorWorkContextPath: options.navigatorWorkContextPath }),
52
55
  };
53
56
  }
54
57
  /**
@@ -83,14 +86,6 @@ function activationForAdmitted(admitted) {
83
86
  const activation = { role: admitted.role };
84
87
  for (const spec of packagedRoleActivationFlags(admitted.role)) {
85
88
  let value = readAdmittedPath(admitted, spec.from ?? spec.field);
86
- if (spec.fallback === "gate-pointer") {
87
- const trimmed = typeof value === "string" ? value.trim() : "";
88
- value = trimmed !== ""
89
- ? trimmed
90
- : parentRunPathFromGatePointerInstruction(admitted.instruction);
91
- if (typeof value !== "string" || value === "")
92
- continue;
93
- }
94
89
  if (value === undefined)
95
90
  continue;
96
91
  if (spec.text === true) {
@@ -197,7 +197,8 @@ async function summonPublicRole(options) {
197
197
  ...options.boundTicketNumber === void 0 ? {} : { boundTicketNumber: options.boundTicketNumber },
198
198
  ...options.parentRunPath === void 0 || options.parentRunPath.trim() === "" ? {} : { parentRunPath: options.parentRunPath },
199
199
  ...options.correlationId === void 0 || options.correlationId.trim() === "" ? {} : { correlationId: options.correlationId },
200
- ...options.createRunId === void 0 ? {} : { createRunId: options.createRunId }
200
+ ...options.createRunId === void 0 ? {} : { createRunId: options.createRunId },
201
+ ...options.navigatorWorkContextPath === void 0 || options.navigatorWorkContextPath.trim() === "" ? {} : { navigatorWorkContextPath: options.navigatorWorkContextPath }
201
202
  }
202
203
  };
203
204
  } catch (error) {
@@ -454,7 +455,8 @@ async function summonGateOfficer(options) {
454
455
  "judge",
455
456
  "--source-run",
456
457
  options.sourceRunDirectory,
457
- `\u5377\u5B97\u6307\u9488\uFF1A${options.sourceRunDirectory}`
458
+ "--project",
459
+ options.cwd
458
460
  ],
459
461
  ...common
460
462
  });
@@ -482,7 +484,7 @@ async function summonGateOfficer(options) {
482
484
  }
483
485
  return summonPublicRole({
484
486
  role: options.officer,
485
- argv: [`\u5377\u5B97\u6307\u9488\uFF1A${options.sourceRunDirectory}`],
487
+ argv: ["--source-run", options.sourceRunDirectory, "--project", options.cwd],
486
488
  ...common
487
489
  });
488
490
  }
@@ -1,5 +1,5 @@
1
1
  import { readdir } from "node:fs/promises";
2
- import { basename, dirname, join } from "node:path";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
3
  import {
4
4
  activationBookDirectory,
5
5
  ensureRealDirectoryTree
@@ -27,6 +27,11 @@ function sessionFileOf(runDirectory) {
27
27
  function runDirectoryOfSessionFile(sessionFile) {
28
28
  return dirname(dirname(sessionFile));
29
29
  }
30
+ function auditorRunDirectory(context) {
31
+ const sessionFile = context.sessionManager?.getSessionFile?.();
32
+ if (sessionFile === void 0) return void 0;
33
+ return resolve(runDirectoryOfSessionFile(sessionFile));
34
+ }
30
35
  function runDirectoryFromSessionDirectory(sessionDirectory) {
31
36
  return dirname(sessionDirectory);
32
37
  }
@@ -107,6 +112,7 @@ function ensureRoleRunPlacement(ledgerHome, placement) {
107
112
  }
108
113
  export {
109
114
  ROLE_RUN_SESSION_FILENAME,
115
+ auditorRunDirectory,
110
116
  ensureRoleRunDirectory,
111
117
  ensureRoleRunPlacement,
112
118
  findRoleRunDirectory,
@@ -19,10 +19,9 @@ const ADMITTED_PAGE_FIELDS = [
19
19
  "admittedRequestPath", // pre-#1161 pages only
20
20
  "sessionDirectory",
21
21
  "sessionFile",
22
- "taskPath",
23
- "packetPath",
24
- "prerequisitesPath",
25
- // #1165: requestManifestPath is a caller path — never rewrite.
22
+ // #1165/#1168: caller file-flag paths (requestManifestPath, prerequisitesPath)
23
+ // stay as given. Obsolete copy fields (taskPath/packetPath) are not rewritten —
24
+ // stock volumes keep their bytes; live relocate no longer maintains them.
26
25
  "mergerInputPath",
27
26
  "sourceRunPath",
28
27
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.5593",
3
+ "version": "0.1.5614",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -32,9 +32,8 @@ const entries = [
32
32
  "archivist-record-topology",
33
33
  // Session material loaders used by published non-bundle roots.
34
34
  "session-opening-materials",
35
- // Value-import closure of published non-bundle roots (build-package-only loadable).
36
- "auditor-dossier-tool",
37
- // Static import of auditor-dossier-tool / placement path authority (#859).
35
+ // Value-import closure of published non-bundle roots (build-package-only loadable);
36
+ // placement path authority (#859).
38
37
  "role-run-placement",
39
38
  // Static import of role-run-placement (safe-positive ticket invariant).
40
39
  "run-ticket-number",
@@ -55,7 +54,6 @@ const entries = [
55
54
  "open-tool-schema",
56
55
  "package-contracts/collector-output",
57
56
  "package-contracts/fixer-output",
58
- "package-contracts/fixer-packet",
59
57
  "package-contracts/gatekeeper-output",
60
58
  "package-contracts/judge-output",
61
59
  "package-contracts/navigator-output",
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Shared audited-run identity for review-officer startup materials (#1166 / ADR 0087).
3
+ * Value form is `<runId>@<席>` only — no directory path.
4
+ */
5
+ import { basename } from "node:path";
6
+
7
+ import { formatRunLeaf, parseRunLeaf } from "./role-run-placement.ts";
8
+
9
+ export const AUDITED_RUN_IDENTITY_KIND = "audited-run-identity" as const;
10
+
11
+ export type AuditedRunIdentityMaterial = {
12
+ readonly kind: typeof AUDITED_RUN_IDENTITY_KIND;
13
+ readonly identity: string;
14
+ };
15
+
16
+ /** Identity leaf from a run directory basename; undefined when the leaf is not `runId@role`. */
17
+ export function auditedRunIdentityFromDirectory(runDirectory: string): string | undefined {
18
+ const parsed = parseRunLeaf(basename(runDirectory));
19
+ if (parsed === undefined) return undefined;
20
+ return formatRunLeaf(parsed.runId, parsed.role);
21
+ }
22
+
23
+ export function auditedRunIdentityMaterial(identity: string): AuditedRunIdentityMaterial {
24
+ return { kind: AUDITED_RUN_IDENTITY_KIND, identity };
25
+ }
26
+
27
+ /** Identity from locator fields already in hand (notary source-run). */
28
+ export function auditedRunIdentityFromParts(runId: string, role: string): string {
29
+ return formatRunLeaf(runId, role);
30
+ }
@@ -1,6 +1,6 @@
1
1
  import type { Usage } from "@earendil-works/pi-ai";
2
2
  import type { AuditorSoulRole } from "./auditor-soul.ts";
3
- import { auditorRunDirectory } from "./auditor-dossier-tool.ts";
3
+ import { auditorRunDirectory } from "./role-run-placement.ts";
4
4
  import type { HostContext } from "./host-contracts.ts";
5
5
  import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
6
6
  import type { PublicSummonResult } from "./public-role-summons.ts";
@@ -40,8 +40,6 @@ export type ComplianceDecision =
40
40
  | ComplianceReceived
41
41
  | ComplianceTransportFailure;
42
42
  /** Source-run pointer remains the binding locator; parent body rides the separate instruction. */
43
- export const AUDITOR_DOSSIER_PROMPT = "卷宗指针:" as const;
44
-
45
43
  export const COMPLIANCE_RESPONSE_ENTRY_TYPE = "ak_compliance_response" as const;
46
44
  export const AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE = "ak_auditor_parent_attempt_binding" as const;
47
45
  export const AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE = "ak_auditor_compliance_failure" as const;
@@ -215,7 +213,6 @@ export async function runComplianceAudit(options: RunComplianceAuditOptions): Pr
215
213
  auditSubject,
216
214
  "--source-run",
217
215
  sourceRunDirectory,
218
- `${AUDITOR_DOSSIER_PROMPT}${sourceRunDirectory}`,
219
216
  ],
220
217
  cwd: options.context.cwd ?? process.cwd(),
221
218
  home,
@@ -1,4 +1,4 @@
1
- import { auditorRunDirectory } from "./auditor-dossier-tool.ts";
1
+ import { auditorRunDirectory } from "./role-run-placement.ts";
2
2
  import {
3
3
  runComplianceAudit,
4
4
  type AuditorSummon,
@@ -1,7 +1,7 @@
1
1
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
  import type { HostContext } from "./host-contracts.ts";
3
3
 
4
- import { auditorRunDirectory } from "./auditor-dossier-tool.ts";
4
+ import { auditorRunDirectory } from "./role-run-placement.ts";
5
5
  import { packagedGateStageLabel } from "./packaged-role-registry.ts";
6
6
  import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
7
7
  import { GatekeeperDecisionError, receivedDiscriminator, unreadableDiscriminatorNotice } from "./submission-errors.ts";
@@ -87,13 +87,10 @@ export type RoleTurnActivation =
87
87
  | {
88
88
  readonly role: "coder";
89
89
  readonly phase: string;
90
- readonly taskPath: string;
91
90
  }
92
91
  | {
93
92
  readonly role: "fixer";
94
93
  readonly phase: string;
95
- readonly packetPath: string;
96
- readonly prerequisitesPath?: string;
97
94
  }
98
95
  | {
99
96
  readonly role: "reviewer";
@@ -203,6 +200,11 @@ export type RoleTurnRequest = {
203
200
  * number. Absent = package default. Never re-read from disk downstream.
204
201
  */
205
202
  readonly deliveryRequestLimit?: number;
203
+ /**
204
+ * Navigator nest work-context locator for startup materials (#1166).
205
+ * Request-scoped — never parent process.env across await.
206
+ */
207
+ readonly navigatorWorkContextPath?: string;
206
208
  };
207
209
 
208
210
  /** Turn result — only fields upper layers currently consume. */
@@ -261,7 +263,7 @@ export interface DurablePrincipalAuthority {
261
263
  type HostSessionManager = { getLeafEntry(): HostSessionEntry | undefined; getLeafId(): string | null | undefined; getEntries(): Iterable<HostSessionEntry>; getSessionDir(): string; getSessionFile(): string | undefined; getHeader?(): { readonly type: string; readonly id?: string } | null; setSessionFile?(path: string): void; appendCustomEntry?(customType: string, data?: unknown): unknown; };
262
264
 
263
265
  /** Context supplied by a host for one activation and its interceptable events. */
264
- export type HostContext = { cwd: string; mode: string; model: { readonly provider: string } | undefined; sessionManager: HostSessionManager; /** Per-turn admitted run directory (#879); never process-global env. */ runDirectory?: string; /** Per-turn court attempt (#879); never process-global env. */ courtAttemptId?: string; /** Public-invocation scope (#537); never process-global env. */ invocationScopeId?: string; /** Selected host axis (#537 / ADR 0082); never process-global env invent. */ host?: string; signal?: AbortSignal | undefined; ui?: { notify?(message: string, type?: "info" | "warning" | "error"): void }; transcript?(): string; abort(): void; };
266
+ export type HostContext = { cwd: string; mode: string; model: { readonly provider: string } | undefined; sessionManager: HostSessionManager; /** Per-turn admitted run directory (#879); never process-global env. */ runDirectory?: string; /** Per-turn court attempt (#879); never process-global env. */ courtAttemptId?: string; /** Public-invocation scope (#537); never process-global env. */ invocationScopeId?: string; /** Selected host axis (#537 / ADR 0082); never process-global env invent. */ host?: string; /** Navigator nest work-context locator (#1166); never process-global env. */ navigatorWorkContextPath?: string; signal?: AbortSignal | undefined; ui?: { notify?(message: string, type?: "info" | "warning" | "error"): void }; transcript?(): string; abort(): void; };
265
267
 
266
268
  /** Per-turn run directory; adapters must project any child-process identity. */
267
269
  export function runDirectoryFromHostContext(context: HostContext): string | undefined {
@@ -11,7 +11,8 @@ export const mergerInputSchema = Type.Object({
11
11
  attemptId: Type.String({ minLength: 1, description: "对账 attempt 身份(transport)" }),
12
12
  targetObjectId: Type.String({ description: "材料:target parent OID;可空" }),
13
13
  sourceObjectId: Type.String({ description: "材料:source parent OID;可空" }),
14
- materials: Type.Object({ task: materialSchema, authority: materialSchema, targetIntent: materialSchema, sourceIntent: materialSchema }),
14
+ // #1168: task/authority dispatch copies removed; git-fact intents remain.
15
+ materials: Type.Object({ targetIntent: materialSchema, sourceIntent: materialSchema }),
15
16
  expectedConflictPaths: Type.Array(Type.String(), { description: "材料:当前未合并路径;可空" }),
16
17
  resolutionScope: Type.Array(Type.String(), { description: "材料:解析范围提示;可空" }),
17
18
  authorizedChecks: Type.Array(checkSchema),
@@ -62,7 +63,10 @@ function readMaterial(value: unknown, label: string): MergerMaterial {
62
63
  export function validateMergerInput(value: unknown): MergerInput {
63
64
  if (!isRecord(value) || blank(value.attemptId)) fail("Merger input requires a non-empty attemptId for accounting");
64
65
  if (!isRecord(value.materials)) fail("Merger materials are missing");
65
- const materials = { task: readMaterial(value.materials.task, "task"), authority: readMaterial(value.materials.authority, "authority"), targetIntent: readMaterial(value.materials.targetIntent, "targetIntent"), sourceIntent: readMaterial(value.materials.sourceIntent, "sourceIntent") };
66
+ const materials = {
67
+ targetIntent: readMaterial(value.materials.targetIntent, "targetIntent"),
68
+ sourceIntent: readMaterial(value.materials.sourceIntent, "sourceIntent"),
69
+ };
66
70
  const authorizedChecks = Array.isArray(value.authorizedChecks) ? value.authorizedChecks.filter((check): check is Record<string, unknown> => isRecord(check)).map(c => ({ name: asString(c.name), argv: asStringArray(c.argv).filter(a => a.trim().length > 0) })).filter(c => c.name.trim().length > 0 && c.argv.length > 0) : [];
67
71
  return deepFreeze(structuredClone({ attemptId: (value.attemptId as string).trim(), targetObjectId: asString(value.targetObjectId), sourceObjectId: asString(value.sourceObjectId), materials, expectedConflictPaths: asStringArray(value.expectedConflictPaths), resolutionScope: asStringArray(value.resolutionScope), authorizedChecks }) as MergerInput);
68
72
  }
@@ -21,6 +21,7 @@ function materialText(input: MergerInput, key: keyof MergerInput["materials"]):
21
21
  * Merger (校书郎) role runtime.
22
22
  * Submission tool records every call (#836); host end is the final.
23
23
  * Git state / completion verification are not attendance or rejection gates (#827).
24
+ * #1168: dispatch text is the first message; assignment carries git facts only.
24
25
  */
25
26
  export function createMergerRoleRuntime(pi: RoleHost, dependencies: MergerRoleDependencies) {
26
27
  let activation: { soul: string; input: Readonly<MergerInput> } | undefined;
@@ -41,7 +42,16 @@ export function createMergerRoleRuntime(pi: RoleHost, dependencies: MergerRoleDe
41
42
  });
42
43
  pi.on("before_agent_start", (event) => {
43
44
  if (!activation) throw new Error("校书郎未激活");
44
- const admitted = { attemptId: activation.input.attemptId, targetObjectId: activation.input.targetObjectId, sourceObjectId: activation.input.sourceObjectId, task: materialText(activation.input, "task"), authority: materialText(activation.input, "authority"), targetIntent: materialText(activation.input, "targetIntent"), sourceIntent: materialText(activation.input, "sourceIntent"), expectedConflictPaths: activation.input.expectedConflictPaths, resolutionScope: activation.input.resolutionScope, authorizedChecks: activation.input.authorizedChecks };
45
+ const admitted = {
46
+ attemptId: activation.input.attemptId,
47
+ targetObjectId: activation.input.targetObjectId,
48
+ sourceObjectId: activation.input.sourceObjectId,
49
+ targetIntent: materialText(activation.input, "targetIntent"),
50
+ sourceIntent: materialText(activation.input, "sourceIntent"),
51
+ expectedConflictPaths: activation.input.expectedConflictPaths,
52
+ resolutionScope: activation.input.resolutionScope,
53
+ authorizedChecks: activation.input.authorizedChecks,
54
+ };
45
55
  return { systemPrompt: `${event.systemPrompt}\n\n<merger_soul>\n${activation.soul}\n</merger_soul>\n\n<merger_assignment>\n${JSON.stringify(admitted)}\n</merger_assignment>` };
46
56
  });
47
57
  }
@@ -229,14 +229,14 @@ export function navigatorSubjectKey(
229
229
  /**
230
230
  * Role inputs for one ad-hoc work item live below role-specific run folders.
231
231
  * The folder and filename are transport, not identity: the shared work root
232
- * keeps task.md, fix-packet.json, and other natural inputs on one subject.
232
+ * keeps natural inputs on one subject.
233
233
  */
234
234
  export function navigatorSubjectKeyForInput(subjectRoot: string, reference: string, cwd = process.cwd()): string {
235
235
  if (issueRoot(subjectRoot) !== undefined || !subjectRoot.includes("/.ak/work/")) return subjectRoot;
236
236
  const resolvedReference = resolve(cwd, reference);
237
237
  const marker = "/runs/";
238
238
  if (resolvedReference.includes(marker)) {
239
- // The work root, not task.md/fix-packet.json/etc., is the stable subject.
239
+ // The work root, not a role-input filename, is the stable subject.
240
240
  // Different roots remain isolated without inventing a filename convention.
241
241
  return subjectRoot;
242
242
  }
@@ -26,7 +26,6 @@ import type { PublicSummonResult } from "./public-role-summons.ts";
26
26
  import { CliUsageError } from "./public-cli/cli-errors.ts";
27
27
  import { isNavigatorSeat } from "./packaged-role-registry.ts";
28
28
  import {
29
- attachWorkContextPointer,
30
29
  navigatorWorkContextFile,
31
30
  } from "./navigator-work-base.ts";
32
31
 
@@ -157,19 +156,24 @@ export function createNativeNavigatorSessionFactory(deps?: {
157
156
  // (ADR 0018 / #959 — legal HostContext may omit signal).
158
157
  if (disposed) return;
159
158
  const resumeRunId = hostRunId;
159
+ // Prompt stays caller/summoner bytes (#1166). Nest work-context path is an
160
+ // internal locator for startup materials only — never folded into argv,
161
+ // never written onto shared parent process.env across await.
160
162
  const workContextPath = navigatorWorkContextFile(sessionManager.getSessionDir());
161
- const argvText = existsSync(workContextPath)
162
- ? attachWorkContextPointer(text, workContextPath)
163
- : text;
163
+ const workContextLocator =
164
+ existsSync(workContextPath) ? workContextPath : undefined;
164
165
 
165
166
  // Call contract only: forward shared-lifecycle signal; do not own AbortController here
166
167
  // (ADR 0018 / #959 — cancel ownership stays on the attendance/envelope seam).
167
168
  const baseSummon = {
168
169
  role: "navigator" as const,
169
- argv: [argvText] as const,
170
+ argv: [text] as const,
170
171
  cwd: context.cwd,
171
172
  ...(summonHome === undefined ? {} : { home: summonHome }),
172
173
  ...(context.signal === undefined ? {} : { signal: context.signal }),
174
+ ...(workContextLocator === undefined
175
+ ? {}
176
+ : { navigatorWorkContextPath: workContextLocator }),
173
177
  };
174
178
 
175
179
  // Prefer host CLI resume so prior advice stays on the host session.