@akagilnc/pi-workflow-roles 0.1.4673 → 0.1.4698
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/dist/acp-host/production-host.js +496 -518
- package/dist/compliance-transport.js +6 -2
- package/dist/gatekeeper-role.js +6 -7
- package/dist/headless-host/production-host.js +492 -514
- package/dist/migrate-book-topology.js +8 -0
- package/dist/public-cli/countersign-run.js +63 -18
- package/dist/public-cli/main.js +488 -513
- package/dist/public-cli/settlement.js +316 -247
- package/dist/public-cli/terminal.js +36 -9
- package/dist/run-terminal-artifacts.js +71 -32
- package/package.json +1 -1
- package/souls/audit-law.md +7 -2
- package/souls/countersign.md +1 -1
- package/souls/gleaner-left.md +1 -1
- package/souls/notary.md +1 -1
- package/souls/quality-law.md +9 -0
- package/souls/reviewer.md +1 -1
- package/src/compliance-transport.ts +8 -2
- package/src/gatekeeper-role.ts +6 -6
- package/src/public-cli/countersign-run.ts +75 -26
- package/src/public-cli/secretariat-run.ts +2 -3
- package/src/public-cli/settlement.ts +380 -355
- package/src/public-cli/terminal.ts +48 -13
- package/src/role-runtime.ts +9 -11
- package/src/run-terminal-artifacts.ts +81 -28
- package/src/secretariat-role.ts +9 -4
|
@@ -78,7 +78,11 @@ export type TerminalRoleOutcome =
|
|
|
78
78
|
/** Original diagnostic identity retained for the caller. */
|
|
79
79
|
diagnostic: string;
|
|
80
80
|
decisiveFacts: Readonly<Record<string, unknown>>;
|
|
81
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Optional current-failure payloads (rare intentional face, e.g. reviewer
|
|
83
|
+
* child terminals). Run history does not live here — #836 / #953 keep it
|
|
84
|
+
* on TerminalResult.submissions so receivers can tell it from this failure.
|
|
85
|
+
*/
|
|
82
86
|
payloads?: readonly unknown[];
|
|
83
87
|
};
|
|
84
88
|
|
|
@@ -165,13 +169,31 @@ export type TerminalGateFact = {
|
|
|
165
169
|
* auto-resumes occurred during this single LLM call; it is not persisted to
|
|
166
170
|
* run-state.json and does not participate in limit decisions.
|
|
167
171
|
*/
|
|
168
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* Current-result payloads on a terminal — accepted/audit role result, or a
|
|
174
|
+
* rare intentional failure face. Run history is TerminalResult.submissions
|
|
175
|
+
* (#836 / #953), not this helper.
|
|
176
|
+
*/
|
|
169
177
|
export function roleResultPayloads(outcome: TerminalRoleOutcome): readonly unknown[] {
|
|
170
178
|
if (outcome.kind === "accepted" || outcome.kind === "audit_escalation") return outcome.payloads ?? [];
|
|
171
179
|
if (outcome.kind === "failure") return outcome.payloads ?? [];
|
|
172
180
|
return [];
|
|
173
181
|
}
|
|
174
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Prefer non-empty current-result payloads; an empty array must not shadow
|
|
185
|
+
* top-level submissions (#953 / #836). Callers that need failure history must
|
|
186
|
+
* read TerminalResult.submissions directly — do not coalesce into failure.payloads.
|
|
187
|
+
*/
|
|
188
|
+
export function coalesceSubmissionRows(
|
|
189
|
+
payloads: readonly unknown[] | undefined,
|
|
190
|
+
submissions: readonly unknown[] | undefined,
|
|
191
|
+
): readonly unknown[] {
|
|
192
|
+
if (payloads !== undefined && payloads.length > 0) return payloads;
|
|
193
|
+
if (submissions !== undefined && submissions.length > 0) return submissions;
|
|
194
|
+
return payloads ?? submissions ?? [];
|
|
195
|
+
}
|
|
196
|
+
|
|
175
197
|
export type TerminalResult = {
|
|
176
198
|
roleOutcome: TerminalRoleOutcome;
|
|
177
199
|
/** Default Reviewer parent: original child Terminals, keyed by frozen axis. */
|
|
@@ -187,8 +209,9 @@ export type TerminalResult = {
|
|
|
187
209
|
navigator: TerminalNavigatorFact;
|
|
188
210
|
artifacts: readonly TerminalArtifactRef[];
|
|
189
211
|
/**
|
|
190
|
-
*
|
|
191
|
-
*
|
|
212
|
+
* Run-scoped recorded submission history (#836). For accepted/audit this often
|
|
213
|
+
* mirrors roleOutcome.payloads; for failure it is the sole historical carrier
|
|
214
|
+
* (#953 — not copied onto failure.payloads).
|
|
192
215
|
*/
|
|
193
216
|
submissions?: readonly unknown[];
|
|
194
217
|
/**
|
|
@@ -316,16 +339,28 @@ export function formatTerminalResult(result: TerminalResult): string {
|
|
|
316
339
|
}
|
|
317
340
|
// Role-result block: original payloads, newest first for humans (#961).
|
|
318
341
|
// Typed payloads/submissions stay ledger order; only this presentation reverses.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
342
|
+
// #953: failure history is the top-level submissions carrier (#836) — present
|
|
343
|
+
// as recorded-submission, never as this-turn submission/receipt.
|
|
344
|
+
if (result.roleOutcome.kind === "failure") {
|
|
345
|
+
const recorded = result.submissions ?? [];
|
|
346
|
+
for (let i = recorded.length - 1; i >= 0; i -= 1) {
|
|
347
|
+
const payload = recorded[i]!;
|
|
348
|
+
const rendered =
|
|
349
|
+
typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
350
|
+
lines.push(`recorded-submission\t${encodeTerminalField(rendered)}`);
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
const payloads =
|
|
354
|
+
result.roleOutcome.kind === "accepted" ||
|
|
355
|
+
result.roleOutcome.kind === "audit_escalation"
|
|
356
|
+
? coalesceSubmissionRows(result.roleOutcome.payloads, result.submissions)
|
|
324
357
|
: result.submissions ?? [];
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
358
|
+
for (let i = payloads.length - 1; i >= 0; i -= 1) {
|
|
359
|
+
const payload = payloads[i]!;
|
|
360
|
+
const rendered =
|
|
361
|
+
typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
362
|
+
lines.push(`submission\t${encodeTerminalField(rendered)}`);
|
|
363
|
+
}
|
|
329
364
|
}
|
|
330
365
|
return `${lines.join("\n")}\n`;
|
|
331
366
|
}
|
package/src/role-runtime.ts
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
} from "./engine-detour.ts";
|
|
38
38
|
import { engineSessionMaterialFromOptions } from "./package-resources/engine-material.ts";
|
|
39
39
|
import { registerEngineDetourTool } from "./engine-detour-tool.ts";
|
|
40
|
+
import { readableGateItem } from "./readable-gate-item.ts";
|
|
40
41
|
import { runIdFromRunDirectory } from "./run-terminal-artifacts.ts";
|
|
41
42
|
import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_PROMPT } from "./receipt-delivery-policy.ts";
|
|
42
43
|
import type { AnyCanonicalSkillBinding } from "./canonical-skill-binding.ts";
|
|
@@ -1226,18 +1227,15 @@ export function createSecretariatRoleRuntime(
|
|
|
1226
1227
|
: { packageRoot: dependencies.packageRoot }),
|
|
1227
1228
|
});
|
|
1228
1229
|
const details = projectSecretariatSummonResult(summoned);
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
const contentText =
|
|
1232
|
-
outcomeKind === "accepted" || outcomeKind === "audit_escalation"
|
|
1233
|
-
? "给事中回执已送达中书省"
|
|
1234
|
-
: outcomeKind === "failure"
|
|
1235
|
-
? "给事中传召失败"
|
|
1236
|
-
: outcomeKind === "no_receipt"
|
|
1237
|
-
? "给事中无回执"
|
|
1238
|
-
: "给事中传召未得终局";
|
|
1230
|
+
// #953 / #775: parent-visible text from typed details via readableGateItem
|
|
1231
|
+
// sole source — payloads + receipt (latest) already carry 传话 facts.
|
|
1239
1232
|
return {
|
|
1240
|
-
content: [
|
|
1233
|
+
content: [
|
|
1234
|
+
{
|
|
1235
|
+
type: "text" as const,
|
|
1236
|
+
text: readableGateItem(details),
|
|
1237
|
+
},
|
|
1238
|
+
],
|
|
1241
1239
|
details,
|
|
1242
1240
|
};
|
|
1243
1241
|
},
|
|
@@ -170,6 +170,7 @@ export function runIdFromRunDirectory(runDirectory: string): string | undefined
|
|
|
170
170
|
* Shared parent-directory unique fallback may be adopted only when the
|
|
171
171
|
* publisher-owned body.runId equals this run directory's runId. Same-run
|
|
172
172
|
* artifactsDir / runDirectory candidates keep path ownership and skip this.
|
|
173
|
+
* expectedRunId undefined (unparseable run dir) → never bound.
|
|
173
174
|
*/
|
|
174
175
|
function presentUniqueFallbackBoundToRun(
|
|
175
176
|
body: Record<string, unknown>,
|
|
@@ -180,51 +181,103 @@ function presentUniqueFallbackBoundToRun(
|
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
184
|
+
* Sole authority for seam-owned unique error.<uuid>.json candidates.
|
|
185
|
+
* Used by both clearOpposite (settlement) and readRunTerminalArtifact — do not
|
|
186
|
+
* re-enumerate the same-run / parent unique set elsewhere.
|
|
187
|
+
* Ownership:
|
|
188
|
+
* - same-run dirs (artifacts/, runDir): path ownership — all unique names
|
|
189
|
+
* - parent runs/: only body.runId-bound faces; unparseable runId or unreadable body → none
|
|
190
|
+
*/
|
|
191
|
+
export async function listSeamOwnedUniqueErrorFacePaths(
|
|
192
|
+
runDirectory: string,
|
|
193
|
+
): Promise<readonly string[]> {
|
|
194
|
+
const artifactsDir = roleRunArtifactsDirectory(runDirectory);
|
|
195
|
+
const owned: string[] = await listUniqueErrorFallbackPaths([
|
|
196
|
+
artifactsDir,
|
|
197
|
+
runDirectory,
|
|
198
|
+
]);
|
|
199
|
+
const expectedRunId = runIdFromRunDirectory(runDirectory);
|
|
200
|
+
for (const path of await listUniqueErrorFallbackPaths([dirname(runDirectory)])) {
|
|
201
|
+
const read = await readTerminalArtifactAtPath(path, "error.json");
|
|
202
|
+
if (read === undefined || read.status !== "present") continue;
|
|
203
|
+
if (!presentUniqueFallbackBoundToRun(read.body, expectedRunId)) continue;
|
|
204
|
+
owned.push(path);
|
|
205
|
+
}
|
|
206
|
+
return owned;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
type PresentOrUnreadable = Exclude<RunTerminalArtifactRead, { status: "absent" }>;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Publish contract (#953): only failure publish continues after clearOpposite
|
|
213
|
+
* failure, so a multi-class residue (residual report/audit beside a new error
|
|
214
|
+
* face) means the current settlement is failure. Prefer failure-class faces
|
|
215
|
+
* over success/audit — never filesystem mtime (copy/restore/utimes can lie).
|
|
216
|
+
*/
|
|
217
|
+
function failureClassRank(file: RunTerminalArtifactFile): number {
|
|
218
|
+
return file === "error.json" ? 1 : 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Read the current typed terminal artifact for a run directory.
|
|
223
|
+
*
|
|
224
|
+
* Candidate set (publisher-owned faces only):
|
|
185
225
|
* 1) conventional artifacts/{report,error,audit-incomplete}.json
|
|
186
226
|
* 2) publisher fixed failure fallbacks (error.settlement.json faces)
|
|
187
|
-
* 3)
|
|
188
|
-
*
|
|
227
|
+
* 3) seam-owned unique error.<uuid>.json via listSeamOwnedUniqueErrorFacePaths
|
|
228
|
+
* (same-run path ownership + parent body.runId binding — shared with clear)
|
|
229
|
+
*
|
|
230
|
+
* Invariant: when more than one present face remains (e.g. clearOpposite failed
|
|
231
|
+
* during failure publish and a fallback was settled beside a residual report),
|
|
232
|
+
* adopt failure-class over success/audit by the publish contract — not mtime.
|
|
233
|
+
* Same-class ties keep candidate enumeration order (conventional before
|
|
234
|
+
* fallbacks before unique).
|
|
189
235
|
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
236
|
+
* Unreadable faces never outrank a present face. Parent unique unreadable
|
|
237
|
+
* files never enter the shared enumerator (cannot prove run identity).
|
|
238
|
+
* Absence of every known durable face is a valid no-receipt state.
|
|
192
239
|
*/
|
|
193
240
|
export async function readRunTerminalArtifact(
|
|
194
241
|
runDirectory: string,
|
|
195
242
|
): Promise<RunTerminalArtifactRead> {
|
|
196
243
|
const artifactsDir = roleRunArtifactsDirectory(runDirectory);
|
|
244
|
+
const present: Array<Extract<PresentOrUnreadable, { status: "present" }>> = [];
|
|
245
|
+
const unreadable: Array<Extract<PresentOrUnreadable, { status: "unreadable" }>> =
|
|
246
|
+
[];
|
|
247
|
+
|
|
248
|
+
const consider = (read: RunTerminalArtifactRead | undefined): void => {
|
|
249
|
+
if (read === undefined || read.status === "absent") return;
|
|
250
|
+
if (read.status === "present") {
|
|
251
|
+
present.push(read);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
unreadable.push(read);
|
|
255
|
+
};
|
|
256
|
+
|
|
197
257
|
for (const file of RUN_TERMINAL_ARTIFACT_FILES) {
|
|
198
|
-
|
|
199
|
-
const read = await readTerminalArtifactAtPath(path, file);
|
|
200
|
-
if (read !== undefined) return read;
|
|
258
|
+
consider(await readTerminalArtifactAtPath(join(artifactsDir, file), file));
|
|
201
259
|
}
|
|
202
260
|
|
|
203
|
-
// Publisher settled a durable failure outside the conventional error.json name.
|
|
204
261
|
for (const relative of RUN_TERMINAL_ERROR_FALLBACK_RELATIVE_PATHS) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
262
|
+
consider(
|
|
263
|
+
await readTerminalArtifactAtPath(join(runDirectory, relative), "error.json"),
|
|
264
|
+
);
|
|
208
265
|
}
|
|
209
266
|
|
|
210
|
-
//
|
|
211
|
-
for (const path of await
|
|
212
|
-
|
|
213
|
-
if (read !== undefined) return read;
|
|
267
|
+
// Unique same-run + parent-bound faces: one enumerator shared with clear.
|
|
268
|
+
for (const path of await listSeamOwnedUniqueErrorFacePaths(runDirectory)) {
|
|
269
|
+
consider(await readTerminalArtifactAtPath(path, "error.json"));
|
|
214
270
|
}
|
|
215
271
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
// Unreadable parent unique file cannot prove run identity — do not adopt.
|
|
272
|
+
if (present.length > 0) {
|
|
273
|
+
present.sort(
|
|
274
|
+
(a, b) => failureClassRank(b.file) - failureClassRank(a.file),
|
|
275
|
+
);
|
|
276
|
+
return present[0]!;
|
|
277
|
+
}
|
|
278
|
+
if (unreadable.length > 0) {
|
|
279
|
+
return unreadable[0]!;
|
|
226
280
|
}
|
|
227
|
-
|
|
228
281
|
return { status: "absent" };
|
|
229
282
|
}
|
|
230
283
|
|
package/src/secretariat-role.ts
CHANGED
|
@@ -128,6 +128,8 @@ function latestObjectPayload(
|
|
|
128
128
|
* Project nested countersign PublicSummonResult onto tool details.
|
|
129
129
|
* Authority: keep typed terminal kind + payloads/diagnostic intact (gatekeeper
|
|
130
130
|
* projectOfficerTerminal precedent / ADR 0052 / 失败诚实). No content gate.
|
|
131
|
+
* Parent-visible text uses this details object via readableGateItem directly
|
|
132
|
+
* (#953 — no second isomorphic projection).
|
|
131
133
|
*/
|
|
132
134
|
export function projectSecretariatSummonResult(
|
|
133
135
|
summoned: PublicSummonResult,
|
|
@@ -178,7 +180,11 @@ export function projectSecretariatSummonResult(
|
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
if (roleOutcome.kind === "failure") {
|
|
181
|
-
|
|
183
|
+
// #953: current failure face is diagnostic/cause/decisiveFacts only.
|
|
184
|
+
// Historical receipts ride terminal.submissions — never as receipt/payloads.
|
|
185
|
+
const submissions = Array.isArray(terminal?.submissions)
|
|
186
|
+
? terminal.submissions
|
|
187
|
+
: undefined;
|
|
182
188
|
return {
|
|
183
189
|
...base,
|
|
184
190
|
outcomeKind: "failure",
|
|
@@ -187,10 +193,9 @@ export function projectSecretariatSummonResult(
|
|
|
187
193
|
...(roleOutcome.decisiveFacts === undefined
|
|
188
194
|
? {}
|
|
189
195
|
: { decisiveFacts: roleOutcome.decisiveFacts }),
|
|
190
|
-
...(
|
|
191
|
-
...(latestObjectPayload(payloads) === undefined
|
|
196
|
+
...(submissions === undefined || submissions.length === 0
|
|
192
197
|
? {}
|
|
193
|
-
: {
|
|
198
|
+
: { submissions }),
|
|
194
199
|
};
|
|
195
200
|
}
|
|
196
201
|
|