@akagilnc/pi-workflow-roles 0.1.5212 → 0.1.5235

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.
@@ -35,11 +35,11 @@ function describeCaughtError(error) {
35
35
  function isStationChildOfficerDialogue(role, env) {
36
36
  return env.stationChild === true && isOfficerReviewSeat(role);
37
37
  }
38
- import { mintEngineDetourInvocationScope, readInvocationSelectedHost, withEngineDetourInvocationScope, } from "../engine-detour-usage.js";
39
- import { projectHostTransitionPriorNative } from "../host-transition-prior-native.js";
38
+ import { mintEngineDetourInvocationScope, withEngineDetourInvocationScope, } from "../engine-detour-usage.js";
40
39
  import { clearCurrentCourt, clearTypedProviderHttpObservation, describeErrorIdentity, markRunRunning, readCurrentCourt, recordCurrentCourt, renderResumeCommand, } from "./run-lifecycle.js";
41
40
  import { processCancelDiagnostic, processCancelSignalName, } from "./process-cancel.js";
42
41
  import { homeFromRunDirectory } from "../activation-ledger-topology.js";
42
+ import { recordRunStart } from "../host-session-record.js";
43
43
  import { attemptProducedFreshSubmission, classifyPostAdmissionFailure, controlledFailureInputFromResolution, exitCodeForTerminalOutcome, explicitInternalKnownFailureClassificationInput, formatCliDiagnostic, formatErrorCauseDetail, formatTerminalResult, inspectJudgeSession, isLawfulTypedTerminalOutcome, presentFailureTerminal, publishedFailureErrorPath, presentStructuralRejection, resolveAuditedRunnerFailureResolution, resolveControlledFailureResumeObservation, settleFailureTerminalResult, settleHostEndedNoReceipt, attachRecordedSubmissions, rewritePublishedFailureErrorPath, } from "./settlement.js";
44
44
  import {} from "./terminal.js";
45
45
  import { ensureRealArtifactsDirectory, persistReturnedRunState, runWithAutoResumeLoop, TurnDispatchedFailure, } from "./auto-resume.js";
@@ -407,45 +407,11 @@ export async function dispatchPostAdmissionTurn(input) {
407
407
  try {
408
408
  // #987: do not pre-block or classify host dispatch from AK credential
409
409
  // catalog facts. Per-invocation runner/host evidence owns provider identity.
410
- // #617 DK-4: capture previous invocation host before markRunRunning overwrites it.
411
- // Single authority projectHostTransitionPriorNative classifies the prior native volume.
412
- // Same-run resume (#637) keeps host identity on the run's invocation page.
413
- let previousHost;
414
- const liveHost = env.host;
415
- const principalCoordinates = admitted.principal === undefined
416
- ? undefined
417
- : env.principalAuthority.decode(admitted.principal);
418
- let hostTransition;
419
- try {
420
- previousHost = readInvocationSelectedHost(admitted.runDirectory);
421
- hostTransition =
422
- previousHost !== undefined && liveHost !== undefined && principalCoordinates !== undefined
423
- ? await projectHostTransitionPriorNative({
424
- previousHost,
425
- liveHost,
426
- piSessionFile: principalCoordinates.sessionFile,
427
- })
428
- : undefined;
429
- }
430
- catch (error) {
431
- // prior-native IO is on the public one-shot path — controlled failure, not bare throw.
432
- return {
433
- ...(await presentControlledFailure(admitted, withEngineDetourInvocationScope({
434
- timedOut: false,
435
- code: null,
436
- stderr: "",
437
- thrown: error,
438
- }, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState)),
439
- ...deferredPersist,
440
- };
441
- }
442
410
  await clearTypedProviderHttpObservation(admitted.runDirectory);
443
411
  // The authoritative host write (markRunRunning) is delayed to just before
444
412
  // executeTurn — not merely past beforeDispatch (#840 r9 判词 class 2). Any
445
- // pre-turn retry (beforeDispatch, dossier projection, continuation
446
- // assembly) must still see the prior invocation host on its next attempt;
447
- // committing env.host earlier would make readInvocationSelectedHost read back the
448
- // new host on the retry and silently drop hostTransition.
413
+ // pre-turn retry (beforeDispatch, continuation assembly) must not commit a host
414
+ // identity until the turn is ready.
449
415
  // Failures settle the run (presentControlledFailure); they must not leave it
450
416
  // permanently running. Call-local auto-resume retries this hook until it
451
417
  // succeeds; only an exhausted nested station child skips the parent loop
@@ -487,13 +453,10 @@ export async function dispatchPostAdmissionTurn(input) {
487
453
  if (env.stationChild !== undefined) {
488
454
  turnRequest = { ...turnRequest, stationChild: env.stationChild };
489
455
  }
490
- if (hostTransition !== undefined) {
491
- turnRequest = { ...turnRequest, hostTransition };
492
- }
493
456
  // Selected host axis rides the shared Host envelope for in-turn tools
494
457
  // (detour usage ledger) — never a pre-spawn invocation.json reread.
495
- if (typeof liveHost === "string" && liveHost.trim() !== "") {
496
- turnRequest = { ...turnRequest, host: liveHost.trim() };
458
+ if (typeof env.host === "string" && env.host.trim() !== "") {
459
+ turnRequest = { ...turnRequest, host: env.host.trim() };
497
460
  }
498
461
  // 0081 non-dialogue face: freeze pointer section under run/attachments/.
499
462
  // Seat consumes via loadCaseDossierReadingMaterial → existing agent-start
@@ -514,6 +477,7 @@ export async function dispatchPostAdmissionTurn(input) {
514
477
  // auto-resume re-enters this dispatch and must reuse the same scope.
515
478
  let result;
516
479
  try {
480
+ recordRunStart(admitted.runDirectory);
517
481
  result = await env.roleTurnHost.executeTurn(turnRequest);
518
482
  }
519
483
  catch (error) {
@@ -1,11 +1,5 @@
1
- import { createHash, randomUUID } from "node:crypto";
2
- import {
3
- appendFileSync,
4
- existsSync,
5
- readFileSync,
6
- unlinkSync,
7
- writeFileSync
8
- } from "node:fs";
1
+ import { randomUUID } from "node:crypto";
2
+ import { appendFileSync } from "node:fs";
9
3
  import { dirname, join } from "node:path";
10
4
  import { resolveBookKeyFromGit } from "./activation-ledger-git.js";
11
5
  import {
@@ -19,48 +13,12 @@ import {
19
13
  import {
20
14
  SitianInfrastructureError
21
15
  } from "./sitian-contracts.js";
22
- function isRecord(value) {
23
- return typeof value === "object" && value !== null && !Array.isArray(value);
24
- }
25
- function errorCodeOf(error) {
26
- return error.code;
27
- }
28
- function identityClaimPath(recordFile, identity) {
29
- const digest = createHash("sha256").update(identity, "utf8").digest("hex");
30
- return `${recordFile}.id-${digest}`;
31
- }
32
- function createExclusiveFile(path, contents) {
33
- writeFileSync(path, contents, { encoding: "utf8", flag: "wx" });
34
- }
35
- function sealTornTail(recordFile) {
36
- if (!existsSync(recordFile)) return;
37
- const buffer = readFileSync(recordFile);
38
- if (buffer.length > 0 && buffer[buffer.length - 1] !== 10) {
39
- appendFileSync(recordFile, "\n", "utf8");
40
- }
41
- }
42
16
  function appendSitianRecordBlock(input, block) {
43
17
  if (block === "") return;
44
18
  try {
45
19
  const { sessionDir, recordFile, ledgerHome } = resolveSitianRecordPath(input);
46
20
  ensureRealDirectoryTree(ledgerHome, sessionDir);
47
- appendFileSync(recordFile, "", "utf8");
48
- sealTornTail(recordFile);
49
- const identityOf = (line) => {
50
- try {
51
- const row = JSON.parse(line);
52
- return isRecord(row) && typeof row.identity === "string" ? row.identity : void 0;
53
- } catch {
54
- return void 0;
55
- }
56
- };
57
- const identities = new Set(readFileSync(recordFile, "utf8").split("\n").map(identityOf).filter((id) => id !== void 0));
58
- for (const line of block.match(/[^\n]*\n|[^\n]+$/g) ?? []) {
59
- const identity = identityOf(line);
60
- if (identity !== void 0 && identities.has(identity)) continue;
61
- appendFileSync(recordFile, line, "utf8");
62
- if (identity !== void 0) identities.add(identity);
63
- }
21
+ appendFileSync(recordFile, block, "utf8");
64
22
  } catch (error) {
65
23
  if (error instanceof SitianInfrastructureError) throw error;
66
24
  throw new SitianInfrastructureError(
@@ -69,106 +27,6 @@ function appendSitianRecordBlock(input, block) {
69
27
  );
70
28
  }
71
29
  }
72
- function findIdentityPointer(recordFile, identity, kind, level) {
73
- if (!existsSync(recordFile)) return void 0;
74
- const text = readFileSync(recordFile, "utf8");
75
- for (const line of text.split("\n")) {
76
- const trimmed = line.trim();
77
- if (!trimmed) continue;
78
- try {
79
- const parsed = JSON.parse(trimmed);
80
- if (isRecord(parsed) && parsed.identity === identity) {
81
- return { identity, recordFile, kind, level };
82
- }
83
- } catch {
84
- }
85
- }
86
- return void 0;
87
- }
88
- function unlinkAbsentOk(path) {
89
- try {
90
- unlinkSync(path);
91
- return void 0;
92
- } catch (error) {
93
- if (errorCodeOf(error) === "ENOENT") return void 0;
94
- return error instanceof Error ? error : new Error(errorText(error));
95
- }
96
- }
97
- function appendWithIdentityClaim(recordFile, record, row) {
98
- sealTornTail(recordFile);
99
- const claimPath = identityClaimPath(recordFile, record.identity);
100
- const existing = findIdentityPointer(
101
- recordFile,
102
- record.identity,
103
- record.kind,
104
- record.level
105
- );
106
- if (existing !== void 0) {
107
- unlinkAbsentOk(claimPath);
108
- return existing;
109
- }
110
- try {
111
- createExclusiveFile(claimPath, "");
112
- } catch (error) {
113
- if (errorCodeOf(error) !== "EEXIST") throw error;
114
- sealTornTail(recordFile);
115
- const published = findIdentityPointer(
116
- recordFile,
117
- record.identity,
118
- record.kind,
119
- record.level
120
- );
121
- if (published !== void 0) {
122
- unlinkAbsentOk(claimPath);
123
- return published;
124
- }
125
- throw new SitianInfrastructureError(
126
- `Sitian identity claim at ${claimPath} already exists for identity ${record.identity}`,
127
- { cause: error }
128
- );
129
- }
130
- let primaryFailure;
131
- let result;
132
- let cleanupFailure;
133
- try {
134
- sealTornTail(recordFile);
135
- const raced = findIdentityPointer(
136
- recordFile,
137
- record.identity,
138
- record.kind,
139
- record.level
140
- );
141
- if (raced !== void 0) {
142
- result = raced;
143
- } else {
144
- appendFileSync(recordFile, row, "utf8");
145
- result = {
146
- identity: record.identity,
147
- recordFile,
148
- kind: record.kind,
149
- level: record.level
150
- };
151
- }
152
- } catch (error) {
153
- primaryFailure = error;
154
- } finally {
155
- cleanupFailure = unlinkAbsentOk(claimPath);
156
- }
157
- if (primaryFailure !== void 0) {
158
- if (primaryFailure instanceof SitianInfrastructureError) throw primaryFailure;
159
- throw new SitianInfrastructureError(
160
- `Sitian appender persistence failure: ${errorText(primaryFailure)}`,
161
- { cause: primaryFailure }
162
- );
163
- }
164
- if (cleanupFailure !== void 0) {
165
- throw new SitianInfrastructureError(
166
- `Sitian identity claim at ${claimPath} could not be released after append for identity ${record.identity}: ${errorText(cleanupFailure)}`,
167
- { cause: cleanupFailure }
168
- );
169
- }
170
- return result;
171
- }
172
30
  const S4_SUBMISSION_LEDGER_KINDS = /* @__PURE__ */ new Set([
173
31
  "candidate",
174
32
  "roundContext",
@@ -249,7 +107,13 @@ function appendSitianRecord(input) {
249
107
  };
250
108
  const row = `${JSON.stringify(record)}
251
109
  `;
252
- return appendWithIdentityClaim(recordFile, record, row);
110
+ appendFileSync(recordFile, row, "utf8");
111
+ return {
112
+ identity: record.identity,
113
+ recordFile,
114
+ kind: record.kind,
115
+ level: record.level
116
+ };
253
117
  } catch (error) {
254
118
  if (error instanceof SitianInfrastructureError) throw error;
255
119
  throw new SitianInfrastructureError(
@@ -1,10 +1,9 @@
1
1
  /**
2
- * 起居录(ticket-provenance)typed 形状 —— ADR 0075「2026-09-14 修订」/ #901 / #918。
2
+ * 起居录(ticket-provenance)typed 形状 —— ADR 0075 / ADR 0081 / #1090。
3
3
  *
4
- * 一册=一个追加式 records.jsonl;逻辑册子头与对话由读取时折叠不可变投影提交得到。
5
- * 既有「首行册子头+裸对话行」snapshot 向后可读,后续提交不回写旧行。
6
- * sessions 为 prior ∪ 各轮累计并集(遗漏不删除),已结转正文不因历史源重读而改。
7
- * 交卷 `sessions` 输入语义仍是「本轮对话边界」;累计并集是机械合并,不是角色交什么。
4
+ * 一册=一个追加式 records.jsonl。每轮交卷的 `sessions`=本轮对话边界;
5
+ * 成录接缝按该边界从宿主卷投影并追加,不回读已成录历史去重或折叠。
6
+ * 既有「首行册子头+裸对话行」snapshot 仍可读;后续提交不回写旧行。
8
7
  */
9
8
  /** Sitian kind for per-ticket court diary volumes. */
10
9
  export const TICKET_PROVENANCE_KIND = "ticket-provenance";
@@ -21,19 +20,6 @@ function positiveInteger(value) {
21
20
  }
22
21
  return undefined;
23
22
  }
24
- function nonNegativeInteger(value) {
25
- if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0)
26
- return value;
27
- if (typeof value === "string" && /^(0|[1-9][0-9]*)$/.test(value)) {
28
- const parsed = Number(value);
29
- if (Number.isSafeInteger(parsed))
30
- return parsed;
31
- }
32
- return undefined;
33
- }
34
- function projectSpeaker(value) {
35
- return value === "owner" || value === "runner" ? value : undefined;
36
- }
37
23
  function projectBound(value) {
38
24
  if (!isRecord(value))
39
25
  return undefined;
@@ -102,27 +88,3 @@ export function projectTicketProvenanceHeader(value) {
102
88
  sessions,
103
89
  };
104
90
  }
105
- /** 读回一条对话行。 */
106
- export function projectTicketProvenanceLine(value) {
107
- if (!isRecord(value))
108
- return undefined;
109
- const speaker = projectSpeaker(value.speaker);
110
- const s = nonNegativeInteger(value.s);
111
- if (speaker === undefined || s === undefined)
112
- return undefined;
113
- if (typeof value.text !== "string")
114
- return undefined;
115
- const sourcePosition = nonNegativeInteger(value.sourcePosition);
116
- const sourceIdentity = typeof value.sourceIdentity === "string" && value.sourceIdentity !== ""
117
- ? value.sourceIdentity
118
- : undefined;
119
- const id = typeof value.id === "string" && value.id !== "" ? value.id : undefined;
120
- return {
121
- speaker,
122
- s,
123
- ...(sourcePosition === undefined ? {} : { sourcePosition }),
124
- ...(sourceIdentity === undefined ? {} : { sourceIdentity }),
125
- ...(id === undefined ? {} : { id }),
126
- text: value.text,
127
- };
128
- }