@akagilnc/pi-workflow-roles 0.1.5797 → 0.1.5820

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 (80) hide show
  1. package/dist/acp-host/production-host.js +464 -355
  2. package/dist/acp-host/role-turn-host.js +484 -0
  3. package/dist/activation-ledger-session.js +105 -0
  4. package/dist/activation-ledger.js +8 -0
  5. package/dist/activation-trace.js +33 -0
  6. package/dist/audit-escalation.js +7 -0
  7. package/dist/audited-run-identity.js +21 -0
  8. package/dist/auditor-role.js +7 -0
  9. package/dist/book-topology-migration-placement.js +404 -0
  10. package/dist/book-topology-migration.js +125 -0
  11. package/dist/book-topology-migrators-t11.js +677 -0
  12. package/dist/book-topology-mixed-volume-migrators.js +322 -0
  13. package/dist/book-topology-partition-migrators.js +23 -0
  14. package/dist/book-topology-record-class-migrators.js +671 -0
  15. package/dist/book-topology-runs-migrator.js +272 -0
  16. package/dist/collector-role.js +109 -0
  17. package/dist/compliance-transport.js +5 -3
  18. package/dist/countersign-role.js +17 -0
  19. package/dist/diarist-role.js +8 -0
  20. package/dist/diarist.js +15 -0
  21. package/dist/doctor-role.js +80 -0
  22. package/dist/engine-detour-tool.js +51 -37
  23. package/dist/engine-detour.js +22 -11
  24. package/dist/factory-board.js +1478 -0
  25. package/dist/fixer-bash-seatbelt.js +13 -0
  26. package/dist/gatekeeper-role.js +1 -2
  27. package/dist/gh-api-runner.js +81 -0
  28. package/dist/gleaner-left-role.js +8 -0
  29. package/dist/headless-host/production-host.js +464 -355
  30. package/dist/headless-host/role-turn-host.js +691 -0
  31. package/dist/human-format.js +67 -0
  32. package/dist/inspector-role.js +12 -0
  33. package/dist/judge-recording-anti-forge.js +39 -0
  34. package/dist/judge-role.js +21 -3
  35. package/dist/merger-role.js +57 -0
  36. package/dist/migrate-book-topology.js +0 -2
  37. package/dist/navigator-role.js +6 -0
  38. package/dist/navigator-work-context.js +72 -0
  39. package/dist/notary-role.js +113 -0
  40. package/dist/oauth-keepalive.js +149 -0
  41. package/dist/package-contracts/terminating-infrastructure.js +0 -35
  42. package/dist/pi/adapter.js +272 -0
  43. package/dist/public-cli/cli.js +9 -3
  44. package/dist/public-cli/instruction-seat-run.js +163 -44
  45. package/dist/public-cli/main.js +241 -123
  46. package/dist/report-ticket-tool.js +24 -13
  47. package/dist/reviewer-role.js +33 -0
  48. package/dist/role-envelope.js +707 -0
  49. package/dist/role-runtime-dependencies.js +57 -0
  50. package/dist/role-runtime.js +1476 -0
  51. package/dist/secretariat-role.js +5 -0
  52. package/dist/stderr-jsonl.js +26 -0
  53. package/dist/submission-correctable-error.js +2 -6
  54. package/dist/submission-errors.js +0 -30
  55. package/dist/submission-gate.js +61 -54
  56. package/dist/submission-ledger.js +105 -98
  57. package/dist/ticket-snapshot.js +486 -0
  58. package/dist/ticket-trajectory.js +843 -0
  59. package/dist/worker-role.js +184 -0
  60. package/dist/worker-submission-gates.js +333 -0
  61. package/package.json +1 -1
  62. package/resources/engine-dispatch.md +6 -6
  63. package/resources/engines/agy.md +1 -1
  64. package/src/compliance-transport.ts +7 -3
  65. package/src/engine-detour-tool.ts +88 -57
  66. package/src/engine-detour.ts +31 -14
  67. package/src/gatekeeper-role.ts +1 -15
  68. package/src/host-contracts.ts +9 -12
  69. package/src/judge-role.ts +30 -7
  70. package/src/package-contracts/terminating-infrastructure.ts +8 -81
  71. package/src/pi/adapter.ts +0 -1
  72. package/src/public-cli/cli.ts +21 -14
  73. package/src/public-cli/instruction-seat-run.ts +227 -44
  74. package/src/report-ticket-tool.ts +27 -13
  75. package/src/role-envelope.ts +35 -35
  76. package/src/role-runtime.ts +63 -56
  77. package/src/submission-correctable-error.ts +1 -6
  78. package/src/submission-errors.ts +0 -34
  79. package/src/submission-gate.ts +71 -56
  80. package/src/submission-ledger.ts +109 -104
@@ -0,0 +1,322 @@
1
+ import { appendFile, cp, lstat, mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, join, relative, resolve, sep } from "node:path";
3
+ /** Historical side-branch nest name under a run session (pre-#1178). */
4
+ const WORKER_SUBMISSION_GATE_KIND = "worker-submission-gate";
5
+ import { reconcileMigrationPartition, } from "./book-topology-migration.js";
6
+ import { bookHistoricalRoots, findPlacedMigratingRun, listMigrationBookKeys, listMigrationDirents, runRefFromBoundPath, } from "./book-topology-migration-placement.js";
7
+ import { isRecord, isEnoent } from "./unknown-value.js";
8
+ const SITIAN_MIXED_VOLUME_PARTITIONS = [
9
+ "attendance",
10
+ "auditor",
11
+ "dispatch-error",
12
+ "gate",
13
+ ];
14
+ const CURRENT_SESSION_LEDGER = "current-session.json";
15
+ function posixRelative(from, to) {
16
+ return relative(from, to).split(sep).join("/");
17
+ }
18
+ function addPathCandidate(paths, seen, value) {
19
+ if (typeof value !== "string" || value.length === 0 || seen.has(value))
20
+ return;
21
+ seen.add(value);
22
+ paths.push(value);
23
+ }
24
+ /** Path fields that can carry a decidable run leaf (`.../runs/<leaf>/...`). */
25
+ function pathCandidatesFromRecord(record) {
26
+ const paths = [];
27
+ const seen = new Set();
28
+ addPathCandidate(paths, seen, record.sessionParent);
29
+ addPathCandidate(paths, seen, record.parentSession);
30
+ if (!isRecord(record.payload))
31
+ return paths;
32
+ const payload = record.payload;
33
+ addPathCandidate(paths, seen, payload.sessionParent);
34
+ addPathCandidate(paths, seen, payload.parentSession);
35
+ addPathCandidate(paths, seen, payload.sessionFile);
36
+ if (isRecord(payload.parent)) {
37
+ addPathCandidate(paths, seen, payload.parent.sessionFile);
38
+ addPathCandidate(paths, seen, payload.parent.sessionParent);
39
+ addPathCandidate(paths, seen, payload.parent.parentSession);
40
+ }
41
+ if (isRecord(payload.data)) {
42
+ addPathCandidate(paths, seen, payload.data.sessionParent);
43
+ addPathCandidate(paths, seen, payload.data.parentSession);
44
+ addPathCandidate(paths, seen, payload.data.sessionFile);
45
+ addPathCandidate(paths, seen, payload.data.file);
46
+ }
47
+ return paths;
48
+ }
49
+ function describeFsKind(entry) {
50
+ if (entry.isFile())
51
+ return "file";
52
+ if (entry.isDirectory())
53
+ return "directory";
54
+ if (entry.isSymbolicLink())
55
+ return "symbolic link";
56
+ if (entry.isFIFO())
57
+ return "FIFO";
58
+ if (entry.isSocket())
59
+ return "socket";
60
+ if (entry.isCharacterDevice())
61
+ return "character device";
62
+ if (entry.isBlockDevice())
63
+ return "block device";
64
+ return "unknown filesystem object";
65
+ }
66
+ function runRefFromRecord(record, bookRoots) {
67
+ for (const path of pathCandidatesFromRecord(record)) {
68
+ const ref = runRefFromBoundPath(path, bookRoots);
69
+ if (ref !== undefined)
70
+ return ref;
71
+ }
72
+ return undefined;
73
+ }
74
+ async function readRegularBackupFile(path) {
75
+ let info;
76
+ try {
77
+ info = await lstat(path);
78
+ }
79
+ catch (error) {
80
+ if (isEnoent(error))
81
+ return undefined;
82
+ throw error;
83
+ }
84
+ if (!info.isFile()) {
85
+ throw new Error(`cannot migrate mixed volume from ${path}: source is a ${describeFsKind(info)}, not a regular file`);
86
+ }
87
+ return readFile(path, "utf8");
88
+ }
89
+ /**
90
+ * Worker-submission-gate volumes are Pi session files. Ownership is only the
91
+ * session header's parentSession — never later message/payload path fields.
92
+ * Malformed JSONL rows are reported separately; they do not select the leaf.
93
+ */
94
+ function inspectWorkerSubmissionGateVolume(text, bookRoots) {
95
+ const malformedRows = [];
96
+ let firstRecord = true;
97
+ let leaf;
98
+ let sourceRelative;
99
+ for (const row of jsonlRows(text)) {
100
+ const isHeader = firstRecord;
101
+ firstRecord = false;
102
+ let parsed;
103
+ try {
104
+ parsed = JSON.parse(row.raw);
105
+ }
106
+ catch {
107
+ malformedRows.push({ lineNumber: row.lineNumber, raw: row.raw });
108
+ continue;
109
+ }
110
+ if (!isRecord(parsed)) {
111
+ malformedRows.push({ lineNumber: row.lineNumber, raw: row.raw });
112
+ continue;
113
+ }
114
+ // Sole attribution evidence: the first JSONL record's parentSession.
115
+ if (isHeader &&
116
+ parsed.type === "session" &&
117
+ typeof parsed.parentSession === "string") {
118
+ const ref = runRefFromBoundPath(parsed.parentSession, bookRoots);
119
+ leaf = ref?.leaf;
120
+ sourceRelative = ref?.sourceRelative;
121
+ }
122
+ }
123
+ return { leaf, sourceRelative, malformedRows };
124
+ }
125
+ function volumeDestinationFile(booksDirectory, bookKey, destRun, kind, fileName) {
126
+ if (destRun === undefined) {
127
+ return join(booksDirectory, bookKey, "unbound", kind, fileName);
128
+ }
129
+ return join(destRun.runDirectory, "session", kind, fileName);
130
+ }
131
+ async function placedRunForLeaf(cache, booksDirectory, bookKey, leaf, sourceRelative) {
132
+ const cacheKey = `${bookKey}\0${sourceRelative ?? ""}\0${leaf}`;
133
+ if (cache.has(cacheKey))
134
+ return cache.get(cacheKey);
135
+ const placed = await findPlacedMigratingRun(booksDirectory, bookKey, leaf, sourceRelative);
136
+ cache.set(cacheKey, placed);
137
+ return placed;
138
+ }
139
+ async function appendRawLine(file, raw) {
140
+ await mkdir(dirname(file), { recursive: true });
141
+ await appendFile(file, `${raw}\n`, "utf8");
142
+ }
143
+ function jsonlRows(text) {
144
+ const lines = text.split("\n");
145
+ const rows = [];
146
+ for (let index = 0; index < lines.length; index += 1) {
147
+ const raw = lines[index];
148
+ if (index === lines.length - 1 && raw.length === 0)
149
+ continue;
150
+ if (!raw.trim())
151
+ continue;
152
+ rows.push({ lineNumber: index + 1, raw });
153
+ }
154
+ return rows;
155
+ }
156
+ async function migrateCurrentSessionPointer(input) {
157
+ const sourceFile = join(input.sourceDir, CURRENT_SESSION_LEDGER);
158
+ const raw = await readRegularBackupFile(sourceFile);
159
+ if (raw === undefined)
160
+ return;
161
+ let sessionFile;
162
+ try {
163
+ const parsed = JSON.parse(raw);
164
+ if (isRecord(parsed) && typeof parsed.sessionFile === "string" && parsed.sessionFile.length > 0) {
165
+ sessionFile = parsed.sessionFile;
166
+ }
167
+ }
168
+ catch {
169
+ sessionFile = undefined;
170
+ }
171
+ let destinationSessionFile;
172
+ if (sessionFile !== undefined) {
173
+ const pointed = resolve(input.sourceDir, sessionFile);
174
+ destinationSessionFile = input.volumeDestinations.get(pointed)
175
+ ?? input.volumeDestinations.get(sessionFile);
176
+ }
177
+ if (destinationSessionFile === undefined) {
178
+ await mkdir(dirname(input.unboundPointerFile), { recursive: true });
179
+ await writeFile(input.unboundPointerFile, raw.endsWith("\n") ? raw : `${raw}\n`, "utf8");
180
+ return;
181
+ }
182
+ const pointerFile = join(dirname(destinationSessionFile), CURRENT_SESSION_LEDGER);
183
+ await mkdir(dirname(pointerFile), { recursive: true });
184
+ await writeFile(pointerFile, `${JSON.stringify({ sessionFile: destinationSessionFile })}\n`, "utf8");
185
+ }
186
+ async function migrateSitianMixedVolume(partition, context) {
187
+ const outcomes = [];
188
+ const placedCache = new Map();
189
+ const { backupBooksDirectory, booksDirectory } = context;
190
+ for (const bookKey of await listMigrationBookKeys(backupBooksDirectory)) {
191
+ const sourceDir = join(backupBooksDirectory, bookKey, partition);
192
+ const recordsFile = join(sourceDir, "records.jsonl");
193
+ const bookRoots = bookHistoricalRoots(booksDirectory, backupBooksDirectory, bookKey);
194
+ const text = await readRegularBackupFile(recordsFile);
195
+ if (text === undefined) {
196
+ await migrateCurrentSessionPointer({
197
+ sourceDir,
198
+ volumeDestinations: new Map(),
199
+ unboundPointerFile: join(booksDirectory, bookKey, "unbound", partition, CURRENT_SESSION_LEDGER),
200
+ });
201
+ continue;
202
+ }
203
+ // Line placement builds the sole volume→dest map for current-session rewrite.
204
+ const placedDests = new Set();
205
+ const sourceIdentity = posixRelative(backupBooksDirectory, recordsFile);
206
+ for (const row of jsonlRows(text)) {
207
+ const source = `${sourceIdentity}:${row.lineNumber}`;
208
+ let parsed;
209
+ try {
210
+ parsed = JSON.parse(row.raw);
211
+ }
212
+ catch {
213
+ const dest = volumeDestinationFile(booksDirectory, bookKey, undefined, partition, "records.jsonl");
214
+ await appendRawLine(dest, row.raw);
215
+ placedDests.add(dest);
216
+ outcomes.push({
217
+ disposition: "unbound",
218
+ source,
219
+ malformed: true,
220
+ malformedRaw: row.raw,
221
+ });
222
+ continue;
223
+ }
224
+ if (!isRecord(parsed)) {
225
+ const dest = volumeDestinationFile(booksDirectory, bookKey, undefined, partition, "records.jsonl");
226
+ await appendRawLine(dest, row.raw);
227
+ placedDests.add(dest);
228
+ outcomes.push({
229
+ disposition: "unbound",
230
+ source,
231
+ malformed: true,
232
+ malformedRaw: row.raw,
233
+ });
234
+ continue;
235
+ }
236
+ const ref = runRefFromRecord(parsed, bookRoots);
237
+ const destRun = ref === undefined
238
+ ? undefined
239
+ : await placedRunForLeaf(placedCache, booksDirectory, bookKey, ref.leaf, ref.sourceRelative);
240
+ const dest = volumeDestinationFile(booksDirectory, bookKey, destRun, partition, "records.jsonl");
241
+ await appendRawLine(dest, row.raw);
242
+ placedDests.add(dest);
243
+ outcomes.push({
244
+ disposition: destRun?.disposition === "placed" ? "placed" : "unbound",
245
+ source,
246
+ });
247
+ }
248
+ const volumeDestinations = new Map();
249
+ if (placedDests.size === 1) {
250
+ const onlyDest = placedDests.values().next().value;
251
+ volumeDestinations.set(resolve(recordsFile), onlyDest);
252
+ volumeDestinations.set(resolve(join(booksDirectory, bookKey, partition, "records.jsonl")), onlyDest);
253
+ }
254
+ await migrateCurrentSessionPointer({
255
+ sourceDir,
256
+ volumeDestinations,
257
+ unboundPointerFile: join(booksDirectory, bookKey, "unbound", partition, CURRENT_SESSION_LEDGER),
258
+ });
259
+ }
260
+ return reconcileMigrationPartition(partition, "lines", outcomes);
261
+ }
262
+ async function migrateWorkerSubmissionGate(context) {
263
+ const outcomes = [];
264
+ const volumeMalformedRows = [];
265
+ const placedCache = new Map();
266
+ const { backupBooksDirectory, booksDirectory } = context;
267
+ const kind = WORKER_SUBMISSION_GATE_KIND;
268
+ for (const bookKey of await listMigrationBookKeys(backupBooksDirectory)) {
269
+ const sourceDir = join(backupBooksDirectory, bookKey, kind);
270
+ const entries = await listMigrationDirents(sourceDir);
271
+ if (entries.length === 0)
272
+ continue;
273
+ const bookRoots = bookHistoricalRoots(booksDirectory, backupBooksDirectory, bookKey);
274
+ const volumes = entries
275
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl"))
276
+ .map((entry) => entry.name)
277
+ .sort();
278
+ for (const name of volumes) {
279
+ const sourcePath = join(sourceDir, name);
280
+ const source = posixRelative(backupBooksDirectory, sourcePath);
281
+ const text = await readRegularBackupFile(sourcePath);
282
+ if (text === undefined)
283
+ continue;
284
+ const inspected = inspectWorkerSubmissionGateVolume(text, bookRoots);
285
+ const destRun = inspected.leaf === undefined
286
+ ? undefined
287
+ : await placedRunForLeaf(placedCache, booksDirectory, bookKey, inspected.leaf, inspected.sourceRelative);
288
+ const dest = volumeDestinationFile(booksDirectory, bookKey, destRun, kind, name);
289
+ await mkdir(dirname(dest), { recursive: true });
290
+ await cp(sourcePath, dest, { preserveTimestamps: true });
291
+ // One outcome per volume keeps the entries closure; bad rows attach below.
292
+ outcomes.push({
293
+ disposition: destRun?.disposition === "placed" ? "placed" : "unbound",
294
+ source,
295
+ });
296
+ for (const bad of inspected.malformedRows) {
297
+ volumeMalformedRows.push({
298
+ source: `${source}:${bad.lineNumber}`,
299
+ raw: bad.raw,
300
+ });
301
+ }
302
+ }
303
+ }
304
+ const report = reconcileMigrationPartition(kind, "entries", outcomes);
305
+ return {
306
+ ...report,
307
+ // Malformed rows are evidence on the volume bytes, not extra entry units.
308
+ malformedRows: [...report.malformedRows, ...volumeMalformedRows],
309
+ };
310
+ }
311
+ const bookTopologySitianMixedVolumeMigrators = SITIAN_MIXED_VOLUME_PARTITIONS.map((partition) => ({
312
+ partition,
313
+ migrate: (context) => migrateSitianMixedVolume(partition, context),
314
+ }));
315
+ const bookTopologyWorkerSubmissionGateMigrator = {
316
+ partition: WORKER_SUBMISSION_GATE_KIND,
317
+ migrate: migrateWorkerSubmissionGate,
318
+ };
319
+ export const BOOK_TOPOLOGY_MIXED_VOLUME_MIGRATORS = [
320
+ ...bookTopologySitianMixedVolumeMigrators,
321
+ bookTopologyWorkerSubmissionGateMigrator,
322
+ ];
@@ -0,0 +1,23 @@
1
+ import { BOOK_TOPOLOGY_MIXED_VOLUME_MIGRATORS } from "./book-topology-mixed-volume-migrators.js";
2
+ import { BOOK_TOPOLOGY_T11_MIGRATORS } from "./book-topology-migrators-t11.js";
3
+ import { BOOK_TOPOLOGY_RECORD_CLASS_MIGRATORS } from "./book-topology-record-class-migrators.js";
4
+ import { bookTopologyRunsMigrator } from "./book-topology-runs-migrator.js";
5
+ /**
6
+ * One assembly point for the independently implemented legacy partitions.
7
+ * #865–#868 populate this list; the orchestrator refuses an empty migration.
8
+ * #866 contributes record-class migrators (ticket-provenance / submission-ledger /
9
+ * attempt-history / misplaced-record-class). Runs (#865) should precede run-owned
10
+ * record classes when both are assembled.
11
+ *
12
+ * T11 (#867) registration order: auditor-roles, issues, deprecated-kinds,
13
+ * deprecated-run-pages, navigator, collector-handbook, manual-archives.
14
+ * auditor-roles prefers a run already placed by T9 when that nest exists.
15
+ * deprecated-run-pages must run after the runs migrator (T9): it deletes
16
+ * matching pages from already-placed destination runs.
17
+ */
18
+ export const BOOK_TOPOLOGY_PARTITION_MIGRATORS = [
19
+ bookTopologyRunsMigrator,
20
+ ...BOOK_TOPOLOGY_RECORD_CLASS_MIGRATORS,
21
+ ...BOOK_TOPOLOGY_T11_MIGRATORS,
22
+ ...BOOK_TOPOLOGY_MIXED_VOLUME_MIGRATORS,
23
+ ];