@akagilnc/pi-workflow-roles 0.1.4062 → 0.1.4196
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 +2075 -1088
- package/dist/archivist-record-entry.js +204 -20
- package/dist/archivist-record-topology.js +36 -16
- package/dist/auditor-dossier-tool.js +2 -1
- package/dist/diarist-contracts.js +96 -0
- package/dist/doctor-evidence.js +3 -2
- package/dist/headless-host/production-host.js +2215 -1228
- package/dist/migrate-book-topology.js +278917 -0
- package/dist/navigator-public-session.js +22 -25
- package/dist/notary-source-run.js +7 -4
- package/dist/pi/durable-principal.js +9 -6
- package/dist/public-cli/auto-resume.js +2 -5
- package/dist/public-cli/diarist-run.js +27 -25
- package/dist/public-cli/instruction-seat-run.js +8 -11
- package/dist/public-cli/invocation.js +127 -63
- package/dist/public-cli/main.js +1507 -645
- package/dist/public-cli/option-definitions.js +2 -2
- package/dist/public-cli/post-admission.js +47 -12
- package/dist/public-cli/run-lifecycle.js +206 -58
- package/dist/public-cli/seat-ticket-binding.js +3 -6
- package/dist/public-cli/settlement.js +5 -3
- package/dist/role-run-placement.js +84 -0
- package/dist/role-run-relocation.js +358 -0
- package/dist/run-terminal-artifacts.js +2 -1
- package/dist/run-ticket-number.js +51 -32
- package/dist/sitian-appender.js +11 -26
- package/dist/submission-ledger.js +78 -19
- package/dist/ticket-provenance.js +3 -1
- package/package.json +3 -2
- package/scripts/build-package.mjs +34 -2
- package/souls/countersign.md +2 -2
- package/souls/fixer.md +3 -2
- package/src/analyst-ledger.ts +14 -15
- package/src/archivist-record-entry.ts +285 -38
- package/src/archivist-record-topology.ts +43 -18
- package/src/auditor-dossier-tool.ts +2 -1
- package/src/book-topology-migration-placement.ts +493 -0
- package/src/book-topology-migration.ts +220 -0
- package/src/book-topology-migrators-t11.ts +856 -0
- package/src/book-topology-mixed-volume-migrators.ts +470 -0
- package/src/book-topology-partition-migrators.ts +25 -0
- package/src/book-topology-record-class-migrators.ts +779 -0
- package/src/book-topology-runs-migrator.ts +244 -0
- package/src/diarist-contracts.ts +116 -0
- package/src/diarist-role.ts +6 -0
- package/src/diarist.ts +2 -0
- package/src/doctor-evidence.ts +4 -1
- package/src/doctor-role.ts +1 -1
- package/src/navigator-public-session.ts +33 -30
- package/src/notary-source-run.ts +9 -4
- package/src/pi/durable-principal.ts +9 -13
- package/src/public-cli/auto-resume.ts +2 -6
- package/src/public-cli/countersign-run.ts +182 -32
- package/src/public-cli/diarist-run.ts +34 -26
- package/src/public-cli/instruction-seat-run.ts +7 -11
- package/src/public-cli/invocation.ts +192 -58
- package/src/public-cli/option-definitions.ts +2 -2
- package/src/public-cli/post-admission.ts +73 -13
- package/src/public-cli/run-lifecycle.ts +255 -65
- package/src/public-cli/seat-ticket-binding.ts +3 -6
- package/src/public-cli/settlement.ts +6 -4
- package/src/role-run-placement.ts +128 -0
- package/src/role-run-relocation.ts +491 -0
- package/src/role-runtime.ts +11 -5
- package/src/run-terminal-artifacts.ts +3 -1
- package/src/run-ticket-number.ts +80 -20
- package/src/sitian-appender.ts +25 -28
- package/src/submission-ledger.ts +102 -19
- package/src/ticket-provenance.ts +4 -1
- package/src/ticket-trajectory.ts +22 -35
- package/src/worker-role.ts +4 -4
- package/src/worker-submission-gates.ts +6 -6
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
1
2
|
import { resolveActivationLedgerHome, tryHomeFromAkRolesPath, } from "./activation-ledger-topology.js";
|
|
2
3
|
import { courtAttemptIdFromHostContext, runDirectoryFromHostContext, } from "./host-contracts.js";
|
|
3
4
|
import { isAuditEscalationProjection } from "./audit-escalation.js";
|
|
4
5
|
import { runIdFromRunDirectory } from "./run-terminal-artifacts.js";
|
|
5
6
|
import { readSitianRecords, resolveSitianRecordPathInLedger, sitianReport } from "./sitian-facade.js";
|
|
7
|
+
import { findRunDirectoryById } from "./public-cli/run-lifecycle.js";
|
|
6
8
|
import { isCorrectableExecuteError } from "./submission-correctable-error.js";
|
|
7
9
|
import { failOnInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.js";
|
|
8
10
|
/**
|
|
@@ -33,21 +35,40 @@ function attemptIdentity(context, runId) {
|
|
|
33
35
|
return courtAttempt;
|
|
34
36
|
return context.sessionManager.getHeader?.()?.id ?? context.sessionManager.getLeafId?.() ?? `${runId}:initial`;
|
|
35
37
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the submission-ledger record file for a run.
|
|
40
|
+
* Unknown run (no sessionParent and no discoverable directory) → undefined so
|
|
41
|
+
* read APIs return their empty set at the read boundary. Write paths always
|
|
42
|
+
* supply sessionParent and keep the ownership gate.
|
|
43
|
+
*/
|
|
44
|
+
async function submissionRecordFile(cwd, runId, scope) {
|
|
45
|
+
const ledgerHome = resolveActivationLedgerHome(scope.home);
|
|
46
|
+
let sessionParent = scope.sessionParent;
|
|
47
|
+
if (sessionParent === undefined) {
|
|
48
|
+
const discoveredRun = await findRunDirectoryById(scope.home, runId);
|
|
49
|
+
if (discoveredRun === undefined)
|
|
50
|
+
return undefined;
|
|
51
|
+
sessionParent = join(discoveredRun, "session", "session.jsonl");
|
|
52
|
+
}
|
|
38
53
|
return resolveSitianRecordPathInLedger({
|
|
39
54
|
level: "event",
|
|
40
55
|
kind: "candidate",
|
|
41
56
|
subject: { runId },
|
|
42
57
|
cwd,
|
|
58
|
+
sessionParent,
|
|
43
59
|
}, ledgerHome).recordFile;
|
|
44
60
|
}
|
|
45
|
-
async function readOwnedSubmissionRecords(cwd, runId,
|
|
46
|
-
const file = submissionRecordFile(cwd, runId,
|
|
61
|
+
async function readOwnedSubmissionRecords(cwd, runId, scope = {}) {
|
|
62
|
+
const file = await submissionRecordFile(cwd, runId, scope);
|
|
63
|
+
if (file === undefined) {
|
|
64
|
+
return { file: undefined, owned: [] };
|
|
65
|
+
}
|
|
47
66
|
const { records } = await readSitianRecords(file);
|
|
48
67
|
return {
|
|
49
68
|
file,
|
|
50
|
-
owned: records.filter((record) => typeof record.subject === "object"
|
|
69
|
+
owned: records.filter((record) => typeof record.subject === "object"
|
|
70
|
+
&& record.subject !== null
|
|
71
|
+
&& record.subject.runId === runId),
|
|
51
72
|
};
|
|
52
73
|
}
|
|
53
74
|
function resolveReadScope(homeOrScope) {
|
|
@@ -86,8 +107,8 @@ function recordAttemptId(record) {
|
|
|
86
107
|
* outranking the current attempt's own real host-turn failure signal
|
|
87
108
|
* (#637 original intent, restored narrowly).
|
|
88
109
|
*/
|
|
89
|
-
export async function hasFreshAttemptSubmission(cwd, runId, attemptId,
|
|
90
|
-
const { owned } = await readOwnedSubmissionRecords(cwd, runId,
|
|
110
|
+
export async function hasFreshAttemptSubmission(cwd, runId, attemptId, homeOrScope) {
|
|
111
|
+
const { owned } = await readOwnedSubmissionRecords(cwd, runId, resolveReadScope(homeOrScope));
|
|
91
112
|
return owned.some((record) => {
|
|
92
113
|
if (recordAttemptId(record) !== attemptId)
|
|
93
114
|
return false;
|
|
@@ -245,7 +266,7 @@ function mapOwnedToSubmissionRows(owned) {
|
|
|
245
266
|
*/
|
|
246
267
|
export async function readRecordedSubmissionRows(cwd, runId, homeOrScope) {
|
|
247
268
|
const scope = resolveReadScope(homeOrScope);
|
|
248
|
-
const { owned } = await readOwnedSubmissionRecords(cwd, runId, scope
|
|
269
|
+
const { owned } = await readOwnedSubmissionRecords(cwd, runId, scope);
|
|
249
270
|
const scoped = recordsForAttempt(owned, scope.attemptId);
|
|
250
271
|
return mapOwnedToSubmissionRows(scoped);
|
|
251
272
|
}
|
|
@@ -257,7 +278,7 @@ export async function readRecordedSubmissionRows(cwd, runId, homeOrScope) {
|
|
|
257
278
|
export async function readAttemptScopedSubmissionRows(cwd, runId, attemptId, home) {
|
|
258
279
|
if (attemptId.length === 0)
|
|
259
280
|
return [];
|
|
260
|
-
const { owned } = await readOwnedSubmissionRecords(cwd, runId, home);
|
|
281
|
+
const { owned } = await readOwnedSubmissionRecords(cwd, runId, home === undefined ? {} : { home });
|
|
261
282
|
return mapOwnedToSubmissionRows(owned.filter((record) => recordAttemptId(record) === attemptId));
|
|
262
283
|
}
|
|
263
284
|
/**
|
|
@@ -274,7 +295,7 @@ export async function hasRecordedSubmission(cwd, runId, homeOrScope) {
|
|
|
274
295
|
/** Latest non-final outcome on the run ledger (settlement residual precedence). */
|
|
275
296
|
export async function readLatestSubmissionOutcome(cwd, runId, homeOrScope) {
|
|
276
297
|
const scope = resolveReadScope(homeOrScope);
|
|
277
|
-
const { owned } = await readOwnedSubmissionRecords(cwd, runId, scope
|
|
298
|
+
const { owned } = await readOwnedSubmissionRecords(cwd, runId, scope);
|
|
278
299
|
const scoped = recordsForAttempt(owned, scope.attemptId);
|
|
279
300
|
for (let index = scoped.length - 1; index >= 0; index -= 1) {
|
|
280
301
|
const record = scoped[index];
|
|
@@ -287,14 +308,33 @@ export async function readLatestSubmissionOutcome(cwd, runId, homeOrScope) {
|
|
|
287
308
|
}
|
|
288
309
|
return undefined;
|
|
289
310
|
}
|
|
290
|
-
async function restoreState(cwd, runId,
|
|
291
|
-
const
|
|
311
|
+
async function restoreState(cwd, runId, scope) {
|
|
312
|
+
const located = await readOwnedSubmissionRecords(cwd, runId, scope);
|
|
313
|
+
// Unknown-run empty set: no prior chain. Owned rows without a file are an internal bug.
|
|
314
|
+
if (located.file === undefined) {
|
|
315
|
+
if (located.owned.length !== 0) {
|
|
316
|
+
throw new Error(`submission ledger invariant: owned rows present without recordFile for run ${runId}`);
|
|
317
|
+
}
|
|
318
|
+
return { sequence: 0 };
|
|
319
|
+
}
|
|
320
|
+
const { file, owned } = located;
|
|
292
321
|
const last = owned.at(-1);
|
|
293
322
|
return {
|
|
294
|
-
...(last === undefined
|
|
323
|
+
...(last === undefined
|
|
324
|
+
? {}
|
|
325
|
+
: {
|
|
326
|
+
prior: {
|
|
327
|
+
identity: last.identity,
|
|
328
|
+
recordFile: file,
|
|
329
|
+
kind: last.kind,
|
|
330
|
+
level: last.level,
|
|
331
|
+
},
|
|
332
|
+
}),
|
|
295
333
|
sequence: owned.reduce((maximum, record) => {
|
|
296
334
|
const payload = record.payload;
|
|
297
|
-
return payload?.type === "candidate" && typeof payload.sequence === "number"
|
|
335
|
+
return payload?.type === "candidate" && typeof payload.sequence === "number"
|
|
336
|
+
? Math.max(maximum, payload.sequence)
|
|
337
|
+
: maximum;
|
|
298
338
|
}, 0),
|
|
299
339
|
};
|
|
300
340
|
}
|
|
@@ -306,22 +346,40 @@ async function restoreState(cwd, runId, home) {
|
|
|
306
346
|
*/
|
|
307
347
|
export function createSubmissionLedgerHost(host, outputTools, failInfrastructure = (error) => { throw error; }, projectClosure = () => undefined, options) {
|
|
308
348
|
const states = new Map();
|
|
349
|
+
/** Sole HostContext-derived run coordinate for restore and append (never process.env). */
|
|
350
|
+
const sessionParentFromContext = (context) => {
|
|
351
|
+
const runDirectory = runDirectoryFromHostContext(context);
|
|
352
|
+
if (runDirectory !== undefined) {
|
|
353
|
+
return join(runDirectory, "session", "session.jsonl");
|
|
354
|
+
}
|
|
355
|
+
const sessionFile = context.sessionManager.getSessionFile?.();
|
|
356
|
+
if (typeof sessionFile === "string" && sessionFile.length > 0)
|
|
357
|
+
return sessionFile;
|
|
358
|
+
const sessionDir = context.sessionManager.getSessionDir?.();
|
|
359
|
+
if (typeof sessionDir === "string" && sessionDir.length > 0) {
|
|
360
|
+
return join(sessionDir, "session.jsonl");
|
|
361
|
+
}
|
|
362
|
+
return undefined;
|
|
363
|
+
};
|
|
309
364
|
const resolveHomeFromContext = (context) => {
|
|
310
365
|
if (options?.home !== undefined)
|
|
311
366
|
return options.home;
|
|
312
|
-
const
|
|
313
|
-
return
|
|
314
|
-
? tryHomeFromAkRolesPath(sessionFile)
|
|
315
|
-
: undefined;
|
|
367
|
+
const sessionParent = sessionParentFromContext(context);
|
|
368
|
+
return sessionParent !== undefined ? tryHomeFromAkRolesPath(sessionParent) : undefined;
|
|
316
369
|
};
|
|
317
370
|
const stateFor = (context, runId) => states.get(runId) ?? (() => {
|
|
318
371
|
const home = resolveHomeFromContext(context);
|
|
319
|
-
const
|
|
372
|
+
const sessionParent = sessionParentFromContext(context);
|
|
373
|
+
const pending = restoreState(context.cwd, runId, {
|
|
374
|
+
...(home === undefined ? {} : { home }),
|
|
375
|
+
...(sessionParent === undefined ? {} : { sessionParent }),
|
|
376
|
+
});
|
|
320
377
|
states.set(runId, pending);
|
|
321
378
|
return pending;
|
|
322
379
|
})();
|
|
323
380
|
const appendFor = (state, context, runId, attemptId, event) => {
|
|
324
381
|
const home = resolveHomeFromContext(context);
|
|
382
|
+
const sessionParent = sessionParentFromContext(context);
|
|
325
383
|
const pointer = sitianReport({
|
|
326
384
|
level: "event",
|
|
327
385
|
kind: event.type,
|
|
@@ -331,6 +389,7 @@ export function createSubmissionLedgerHost(host, outputTools, failInfrastructure
|
|
|
331
389
|
source: "role-runtime",
|
|
332
390
|
cwd: context.cwd,
|
|
333
391
|
...(home !== undefined ? { home } : {}),
|
|
392
|
+
...(sessionParent === undefined ? {} : { sessionParent }),
|
|
334
393
|
});
|
|
335
394
|
state.prior = pointer;
|
|
336
395
|
return pointer;
|
|
@@ -6,10 +6,11 @@ import { createHash } from "node:crypto";
|
|
|
6
6
|
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { resolveSitianRecordPath, sitianReport, readSitianRecords, } from "./sitian-facade.js";
|
|
9
|
+
import { isSafePositiveTicketNumber } from "./run-ticket-number.js";
|
|
9
10
|
import { TICKET_PROVENANCE_HUMAN_VIEW, TICKET_PROVENANCE_KIND, projectTicketProvenanceDiagnostic, projectTicketProvenanceEntry, } from "./ticket-provenance-contracts.js";
|
|
10
11
|
/** Subject string for ticket-keyed volumes — history follows the ticket. */
|
|
11
12
|
export function ticketProvenanceSubject(ticketNumber) {
|
|
12
|
-
if (!
|
|
13
|
+
if (!isSafePositiveTicketNumber(ticketNumber)) {
|
|
13
14
|
throw new Error(`ticket-provenance subject requires a positive ticket number, got ${String(ticketNumber)}`);
|
|
14
15
|
}
|
|
15
16
|
return String(ticketNumber);
|
|
@@ -71,6 +72,7 @@ export function appendTicketProvenanceEntry(input) {
|
|
|
71
72
|
identity,
|
|
72
73
|
subject,
|
|
73
74
|
cwd: input.cwd,
|
|
75
|
+
sessionParent: input.sessionParent,
|
|
74
76
|
...(input.home === undefined ? {} : { home: input.home }),
|
|
75
77
|
host: input.host ?? "diarist",
|
|
76
78
|
source: input.source ?? "diarist",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akagilnc/pi-workflow-roles",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4196",
|
|
4
4
|
"description": "Soul-bound workflow roles for Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
"test:integration": "node --import tsx --import ./scripts/test-process-env-preload.mjs --test test/unit/**/*.test.ts test/contract/**/*.test.ts test/integration/**/*.test.ts",
|
|
48
48
|
"test:all": "node scripts/run-test-all.mjs",
|
|
49
49
|
"test:adjudication": "node --import tsx --import ./scripts/test-process-env-preload.mjs --test test/adjudication/**/*.test.ts",
|
|
50
|
-
"typecheck": "tsc --noEmit"
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
51
|
+
"migrate:book-topology": "node dist/migrate-book-topology.js"
|
|
51
52
|
},
|
|
52
53
|
"pi": {
|
|
53
54
|
"extensions": []
|
|
@@ -22,12 +22,14 @@ const entries = [
|
|
|
22
22
|
"activation-ledger-topology",
|
|
23
23
|
"activation-reconciliation",
|
|
24
24
|
"archivist-record-entry",
|
|
25
|
-
// Pure subject nest topology — cold discovery without SessionManager (#636).
|
|
26
|
-
"archivist-record-topology",
|
|
27
25
|
// Session material loaders used by published non-bundle roots.
|
|
28
26
|
"session-opening-materials",
|
|
29
27
|
// Value-import closure of published non-bundle roots (build-package-only loadable).
|
|
30
28
|
"auditor-dossier-tool",
|
|
29
|
+
// Static import of auditor-dossier-tool / placement path authority (#859).
|
|
30
|
+
"role-run-placement",
|
|
31
|
+
// Static import of role-run-placement (safe-positive ticket invariant).
|
|
32
|
+
"run-ticket-number",
|
|
31
33
|
"canonical-json",
|
|
32
34
|
"compliance-transport",
|
|
33
35
|
"countersign-contracts",
|
|
@@ -141,6 +143,35 @@ export async function buildHeadlessProductionHost(
|
|
|
141
143
|
);
|
|
142
144
|
}
|
|
143
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Published one-shot book-topology migration CLI (#852).
|
|
148
|
+
* Node-executable ESM bundle — consumers must not need dev-only tsx.
|
|
149
|
+
* createRequire banner lets transitive CJS deps reach node builtins.
|
|
150
|
+
*/
|
|
151
|
+
export async function buildMigrateBookTopology(
|
|
152
|
+
outfile = "dist/migrate-book-topology.js",
|
|
153
|
+
) {
|
|
154
|
+
await mkdir(dirname(outfile), { recursive: true });
|
|
155
|
+
await build({
|
|
156
|
+
entryPoints: ["scripts/migrate-book-topology.ts"],
|
|
157
|
+
outfile,
|
|
158
|
+
format: "esm",
|
|
159
|
+
platform: "node",
|
|
160
|
+
target: "node20",
|
|
161
|
+
bundle: true,
|
|
162
|
+
banner: {
|
|
163
|
+
js: [
|
|
164
|
+
"#!/usr/bin/env node",
|
|
165
|
+
"import { createRequire as __akCreateRequire } from 'node:module';",
|
|
166
|
+
"const require = __akCreateRequire(import.meta.url);",
|
|
167
|
+
"",
|
|
168
|
+
].join("\n"),
|
|
169
|
+
},
|
|
170
|
+
logLevel: "silent",
|
|
171
|
+
});
|
|
172
|
+
await chmod(outfile, 0o755);
|
|
173
|
+
}
|
|
174
|
+
|
|
144
175
|
export async function buildPackageArtifacts() {
|
|
145
176
|
await build({
|
|
146
177
|
entryPoints: entries.map((name) => `src/${name}.ts`),
|
|
@@ -167,6 +198,7 @@ export async function buildPackageArtifacts() {
|
|
|
167
198
|
await buildPublicAkRoleBin();
|
|
168
199
|
await buildAcpProductionHost();
|
|
169
200
|
await buildHeadlessProductionHost();
|
|
201
|
+
await buildMigrateBookTopology();
|
|
170
202
|
}
|
|
171
203
|
|
|
172
204
|
const isMain =
|
package/souls/countersign.md
CHANGED
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
1 第一法源立法(最高法):该票起居录,全局宪法,仓内治理文件;
|
|
10
10
|
|
|
11
11
|
2 第二法源立法:
|
|
12
|
-
适用 ADR——由你自行检索,按票面触及的席位、接缝、术语在 docs/adr 与 CONTEXT.md
|
|
12
|
+
适用 ADR——由你自行检索,按票面触及的席位、接缝、术语在 docs/adr 与 CONTEXT.md 查全,票面引没引都算——并逐条列出其决定;质量法、审刑院法典;
|
|
13
13
|
|
|
14
14
|
3 第二法源合法性检查:判断第二法源 以及 输入的 prompt等 对比第一法源是否有超出,不足,曲解。如有,除非有陛下原话批准,否则直接封驳 要求改正或陛下批准。
|
|
15
15
|
|
|
16
16
|
**二、相抵** 票面,相关票与在飞分支与法源有无抵触。
|
|
17
17
|
|
|
18
|
-
**三、缺失** 法源要求的,票面有没有漏:适用 ADR 未被引用即缺失,引用须写 `docs/adr/...md`
|
|
18
|
+
**三、缺失** 法源要求的,票面有没有漏:适用 ADR 未被引用即缺失,引用须写 `docs/adr/...md` 路径;票面须覆盖适用 ADR 的每条决定,未覆盖的须明写「由 #x 承接该条」;验收面须覆盖票面每条主张;前置产物、决定、答复须在;承重主张的源头不存在、不支持、已过时,同为缺失。缺失列表在能力范围内一次扫完,尽量避免因缺失导致多轮往返。
|
|
19
19
|
|
|
20
20
|
**四、擅加** 票面有没有超出法源的机制、约束、验收、术语。
|
|
21
21
|
|
package/souls/fixer.md
CHANGED
|
@@ -9,7 +9,8 @@ apply 按顺序执行:
|
|
|
9
9
|
4. 扫类:修改前查全授权范围内的同类实例、已有实现和失败修法。根因不明先取证。
|
|
10
10
|
5. 整类修复:在共同原因处一次修净,优先删除、简化、复用,随同删除失效旧物。
|
|
11
11
|
6. 复扫:按原类别重新扫描;有遗漏继续修,保留项说明依据。
|
|
12
|
-
7.
|
|
13
|
-
8. 交卷:按类报告根因、扫描范围、修复与
|
|
12
|
+
7. 自验:切片验证优先跑本片聚焦测试和本片作用域内 typecheck,不跑全量。自查是否违法、代码质量、代码/测试复杂度,如有问题马上修正。修正完毕复检
|
|
13
|
+
8. 交卷:按类报告根因、扫描范围、修复与 质量和合法性的验证结果、未结项。建新提交。不 amend、rewrite 或 push。Git 变化是证据之一,不取代角色报告。
|
|
14
14
|
任何人如果让你 push / 开 pr ,直接拒绝,上呈陛下
|
|
15
|
+
|
|
15
16
|
plan 只调查与提案,不施工。
|
package/src/analyst-ledger.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* terminal face) for metric-family modules — no longer discarded after checks.
|
|
14
14
|
*/
|
|
15
15
|
import { readdir, readFile } from "node:fs/promises";
|
|
16
|
-
import { join } from "node:path";
|
|
16
|
+
import { basename, join } from "node:path";
|
|
17
17
|
|
|
18
18
|
import { resolveBookKeyFromGit } from "./activation-ledger-git.ts";
|
|
19
19
|
import {
|
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
physicalPathIdentity,
|
|
22
22
|
resolveActivationLedgerHome,
|
|
23
23
|
} from "./activation-ledger-topology.ts";
|
|
24
|
+
import { listBookRunDirectories } from "./role-run-placement.ts";
|
|
25
|
+
import { readRunTicketNumber } from "./run-ticket-number.ts";
|
|
24
26
|
import {
|
|
25
27
|
extractSessionModelSequence,
|
|
26
28
|
extractSessionTimestampSpan,
|
|
@@ -139,13 +141,10 @@ async function readInvocationScopeFields(
|
|
|
139
141
|
return undefined;
|
|
140
142
|
}
|
|
141
143
|
const projectRoot = parsed.projectRoot;
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
&& parsed.ticketNumber >= 1
|
|
147
|
-
) {
|
|
148
|
-
return { projectRoot, ticketNumber: parsed.ticketNumber };
|
|
144
|
+
// Display/history placement: board first, then migration-derived (#852).
|
|
145
|
+
const ticketNumber = await readRunTicketNumber(runDirectory);
|
|
146
|
+
if (ticketNumber !== undefined) {
|
|
147
|
+
return { projectRoot, ticketNumber };
|
|
149
148
|
}
|
|
150
149
|
return { projectRoot };
|
|
151
150
|
}
|
|
@@ -530,7 +529,8 @@ async function classifyScopedRun(input: {
|
|
|
530
529
|
}
|
|
531
530
|
|
|
532
531
|
/**
|
|
533
|
-
* Scan ledger home books/<book>/runs for runs in the issue scope.
|
|
532
|
+
* Scan ledger home books/<book>/{subject/}runs for runs in the issue scope.
|
|
533
|
+
* Walk = listBookRunDirectories (flat legacy + subject-tree; #859).
|
|
534
534
|
* #399: scope = book × optional ticket.
|
|
535
535
|
* - bookKey set → that book only; whole-book when no ticket and no projectRoot;
|
|
536
536
|
* ticket filters alone; bookKey + projectRoot (cohort ensure, #412) narrows
|
|
@@ -598,20 +598,19 @@ export async function scanAnalystIssueRuns(input: {
|
|
|
598
598
|
const scopeConflicts: AnalystScopeConflict[] = [];
|
|
599
599
|
|
|
600
600
|
for (const book of bookNames) {
|
|
601
|
-
const
|
|
602
|
-
let
|
|
601
|
+
const bookDir = join(booksRoot, book);
|
|
602
|
+
let runDirectories: string[];
|
|
603
603
|
try {
|
|
604
|
-
|
|
605
|
-
runNames = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
604
|
+
runDirectories = await listBookRunDirectories(bookDir);
|
|
606
605
|
} catch (error) {
|
|
607
606
|
if (isMissingPathError(error)) continue;
|
|
608
607
|
throw error;
|
|
609
608
|
}
|
|
610
609
|
|
|
611
|
-
for (const
|
|
610
|
+
for (const runDirectory of runDirectories) {
|
|
611
|
+
const runName = basename(runDirectory);
|
|
612
612
|
const parsed = parseRunDirectoryName(runName);
|
|
613
613
|
if (parsed === undefined) continue;
|
|
614
|
-
const runDirectory = join(runsDir, runName);
|
|
615
614
|
|
|
616
615
|
let scopeFields: InvocationScopeFields | undefined;
|
|
617
616
|
try {
|