@akagilnc/pi-workflow-roles 0.1.5368 → 0.1.5375
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 +719 -776
- package/dist/activation-ledger-git.js +2 -2
- package/dist/activation-ledger-topology.js +1 -7
- package/dist/analyst-entry.js +4 -8
- package/dist/analyst-gate-cycles-read.js +3 -11
- package/dist/analyst-index.js +3 -9
- package/dist/analyst-ledger.js +1 -11
- package/dist/analyst-metric-families/acceptance-success-rework.js +1 -3
- package/dist/analyst-metric-families/round-timeline.js +1 -3
- package/dist/analyst-page.js +2 -2
- package/dist/archivist-record-topology.js +2 -2
- package/dist/audit-escalation.js +2 -1
- package/dist/auditor-soul.js +2 -1
- package/dist/collector-config.js +6 -8
- package/dist/compliance-transport.js +2 -1
- package/dist/countersign-contracts.js +2 -1
- package/dist/diarist-contracts.js +3 -2
- package/dist/doctor-contracts.js +1 -3
- package/dist/doctor-evidence.js +7 -8
- package/dist/dossier-resolution.js +1 -3
- package/dist/engine-detour-usage.js +1 -3
- package/dist/gatekeeper-role.js +2 -9
- package/dist/gleaner-left-contracts.js +2 -1
- package/dist/headless-host/description.js +14 -17
- package/dist/headless-host/production-host.js +721 -780
- package/dist/host-session-record.js +3 -2
- package/dist/inspector-contracts.js +2 -1
- package/dist/ledger-session-read.js +1 -3
- package/dist/merger-contracts.js +9 -9
- package/dist/migrate-book-topology.js +348 -314
- package/dist/navigator-attendance.js +9 -11
- package/dist/navigator-invocation-identity.js +3 -2
- package/dist/navigator-public-session.js +3 -5
- package/dist/navigator-session-contracts.js +8 -10
- package/dist/navigator-work-base.js +5 -8
- package/dist/notary-contracts.js +3 -5
- package/dist/package-contracts/auditor-output.js +1 -3
- package/dist/package-contracts/fixer-packet.js +3 -6
- package/dist/package-contracts/gatekeeper-output.js +1 -3
- package/dist/package-contracts/navigator-output.js +8 -8
- package/dist/package-contracts/terminating-infrastructure.js +5 -9
- package/dist/package-contracts/terminating-tools.js +4 -3
- package/dist/public-cli/analyst-run.js +3 -2
- package/dist/public-cli/auto-resume.js +3 -8
- package/dist/public-cli/cli.js +9 -8
- package/dist/public-cli/config.js +8 -11
- package/dist/public-cli/countersign-run.js +1 -3
- package/dist/public-cli/host-providers.js +7 -14
- package/dist/public-cli/instruction-seat-run.js +4 -3
- package/dist/public-cli/invocation.js +9 -8
- package/dist/public-cli/main.js +1531 -1567
- package/dist/public-cli/post-admission.js +4 -3
- package/dist/public-cli/run-lifecycle.js +14 -26
- package/dist/public-cli/settlement.js +10 -26
- package/dist/public-role-summons.js +3 -2
- package/dist/receipt-delivery-policy.js +1 -3
- package/dist/role-run-placement.js +3 -5
- package/dist/role-run-relocation.js +12 -19
- package/dist/run-terminal-artifacts.js +1 -11
- package/dist/run-ticket-number.js +2 -4
- package/dist/session-assistant-usage.js +2 -1
- package/dist/session-dialogue.js +1 -14
- package/dist/sitian-reader.js +2 -4
- package/dist/submission-correctable-error.js +2 -1
- package/dist/submission-errors.js +2 -1
- package/dist/submission-gate.js +2 -1
- package/dist/submission-ledger.js +4 -5
- package/dist/ticket-provenance-contracts.js +1 -3
- package/dist/ticket-provenance.js +2 -1
- package/dist/typed-provider-http.js +4 -7
- package/dist/unknown-value.js +28 -0
- package/dist/user-dialogue-stdin.js +2 -1
- package/package.json +1 -1
- package/src/acp-host/role-turn-host.ts +4 -2
- package/src/activation-ledger-git.ts +3 -2
- package/src/activation-ledger-topology.ts +4 -12
- package/src/analyst-entry.ts +3 -11
- package/src/analyst-gate-cycles-read.ts +3 -15
- package/src/analyst-index.ts +3 -13
- package/src/analyst-ledger.ts +2 -16
- package/src/analyst-metric-families/acceptance-success-rework.ts +2 -4
- package/src/analyst-metric-families/round-timeline.ts +2 -4
- package/src/analyst-page.ts +2 -2
- package/src/archivist-record-topology.ts +2 -2
- package/src/audit-escalation.ts +2 -1
- package/src/auditor-soul.ts +3 -1
- package/src/book-topology-migration-placement.ts +5 -16
- package/src/book-topology-migrators-t11.ts +16 -15
- package/src/book-topology-mixed-volume-migrators.ts +3 -6
- package/src/book-topology-record-class-migrators.ts +7 -10
- package/src/book-topology-runs-migrator.ts +4 -3
- package/src/collector-config.ts +7 -9
- package/src/compliance-transport.ts +2 -1
- package/src/countersign-contracts.ts +2 -1
- package/src/diarist-contracts.ts +3 -2
- package/src/doctor-contracts.ts +3 -1
- package/src/doctor-evidence.ts +8 -7
- package/src/doctor-role.ts +4 -2
- package/src/dossier-resolution.ts +2 -4
- package/src/engine-detour-tool.ts +0 -2
- package/src/engine-detour-usage.ts +2 -4
- package/src/external-host-turn-loop.ts +3 -1
- package/src/factory-board.ts +1 -8
- package/src/gatekeeper-role.ts +2 -10
- package/src/gleaner-left-contracts.ts +3 -1
- package/src/headless-host/description.ts +15 -18
- package/src/headless-host/role-turn-host.ts +10 -9
- package/src/host-session-record.ts +4 -2
- package/src/inspector-contracts.ts +2 -1
- package/src/ledger-session-read.ts +2 -5
- package/src/merger-contracts.ts +10 -9
- package/src/navigator-attendance.ts +10 -12
- package/src/navigator-invocation-identity.ts +3 -2
- package/src/navigator-public-session.ts +4 -6
- package/src/navigator-session-contracts.ts +8 -11
- package/src/navigator-work-base.ts +5 -8
- package/src/navigator-work-context.ts +2 -1
- package/src/notary-contracts.ts +4 -6
- package/src/oauth-keepalive.ts +5 -18
- package/src/package-contracts/auditor-output.ts +2 -4
- package/src/package-contracts/fixer-packet.ts +4 -6
- package/src/package-contracts/gatekeeper-output.ts +2 -4
- package/src/package-contracts/judge-output.ts +0 -1
- package/src/package-contracts/navigator-output.ts +8 -8
- package/src/package-contracts/terminating-infrastructure.ts +6 -20
- package/src/package-contracts/terminating-tools.ts +8 -12
- package/src/pi/adapter.ts +0 -1
- package/src/pi/role-turn-host.ts +0 -1
- package/src/public-cli/analyst-run.ts +4 -2
- package/src/public-cli/auto-resume.ts +4 -11
- package/src/public-cli/cli.ts +10 -10
- package/src/public-cli/config.ts +8 -27
- package/src/public-cli/countersign-run.ts +2 -4
- package/src/public-cli/host-providers.ts +10 -27
- package/src/public-cli/instruction-seat-run.ts +5 -3
- package/src/public-cli/invocation.ts +11 -10
- package/src/public-cli/post-admission.ts +5 -3
- package/src/public-cli/run-lifecycle.ts +20 -41
- package/src/public-cli/settlement.ts +11 -33
- package/src/public-role-summons.ts +4 -3
- package/src/receipt-delivery-policy.ts +1 -4
- package/src/reviewer-role.ts +0 -1
- package/src/role-envelope.ts +5 -3
- package/src/role-run-placement.ts +4 -10
- package/src/role-run-relocation.ts +13 -23
- package/src/role-runtime.ts +9 -15
- package/src/run-terminal-artifacts.ts +2 -16
- package/src/run-ticket-number.ts +4 -10
- package/src/session-assistant-usage.ts +3 -1
- package/src/session-dialogue.ts +1 -4
- package/src/session-identity.ts +0 -1
- package/src/sitian-reader.ts +2 -4
- package/src/submission-correctable-error.ts +3 -1
- package/src/submission-errors.ts +2 -1
- package/src/submission-gate.ts +3 -1
- package/src/submission-ledger.ts +5 -6
- package/src/ticket-provenance-contracts.ts +1 -4
- package/src/ticket-provenance.ts +3 -1
- package/src/ticket-snapshot.ts +2 -4
- package/src/ticket-trajectory.ts +1 -8
- package/src/typed-provider-http.ts +5 -15
- package/src/unknown-value.ts +34 -0
- package/src/user-dialogue-stdin.ts +3 -1
- package/src/worker-role.ts +2 -5
- package/src/worker-submission-gates.ts +5 -7
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRecord } from "./unknown-value.js";
|
|
1
2
|
/**
|
|
2
3
|
* 起居录(ticket-provenance)typed 形状 —— ADR 0075 / ADR 0081 / #1090。
|
|
3
4
|
*
|
|
@@ -7,9 +8,6 @@
|
|
|
7
8
|
*/
|
|
8
9
|
/** Sitian kind for per-ticket court diary volumes. */
|
|
9
10
|
export const TICKET_PROVENANCE_KIND = "ticket-provenance";
|
|
10
|
-
function isRecord(value) {
|
|
11
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
|
-
}
|
|
13
11
|
function positiveInteger(value) {
|
|
14
12
|
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 1)
|
|
15
13
|
return value;
|
|
@@ -11,6 +11,7 @@ import { isSafePositiveTicketNumber } from "./run-ticket-number.js";
|
|
|
11
11
|
import { adaptSessionDialogue, nativeEventId } from "./session-dialogue.js";
|
|
12
12
|
import { appendSitianRecordBlock, appendSitianRecord, resolveSitianRecordPath, } from "./sitian-facade.js";
|
|
13
13
|
import { TICKET_PROVENANCE_KIND, } from "./ticket-provenance-contracts.js";
|
|
14
|
+
import { errorText } from "./unknown-value.js";
|
|
14
15
|
/**
|
|
15
16
|
* Typed input failure for diarist bounds/session path (reask, not infrastructure).
|
|
16
17
|
* Accept hook discriminates with instanceof — never Error.message prefixes.
|
|
@@ -199,7 +200,7 @@ async function projectSessionRanges(input) {
|
|
|
199
200
|
throw error;
|
|
200
201
|
const code = errnoCode(error);
|
|
201
202
|
if (code === "ENOENT" || code === "ENOTDIR" || code === "EISDIR") {
|
|
202
|
-
const detail =
|
|
203
|
+
const detail = errorText(error);
|
|
203
204
|
throw new TicketProvenanceInputError(`session unreadable: ${input.session.path} (${detail})`, { cause: error });
|
|
204
205
|
}
|
|
205
206
|
throw error;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile, unlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { isEnoent, isRecord } from "./unknown-value.js";
|
|
3
4
|
const TYPED_HTTP_FILE = "typed-provider-http.json";
|
|
4
5
|
function typedProviderHttpPath(runDirectory) {
|
|
5
6
|
return join(runDirectory, TYPED_HTTP_FILE);
|
|
@@ -8,9 +9,7 @@ async function clearTypedProviderHttpObservation(runDirectory) {
|
|
|
8
9
|
try {
|
|
9
10
|
await unlink(typedProviderHttpPath(runDirectory));
|
|
10
11
|
} catch (error) {
|
|
11
|
-
if (error
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
12
|
+
if (isEnoent(error)) return;
|
|
14
13
|
throw error;
|
|
15
14
|
}
|
|
16
15
|
}
|
|
@@ -35,13 +34,11 @@ async function readLatestTypedProviderHttpObservation(runDirectory) {
|
|
|
35
34
|
try {
|
|
36
35
|
text = await readFile(typedProviderHttpPath(runDirectory), "utf8");
|
|
37
36
|
} catch (error) {
|
|
38
|
-
if (error
|
|
39
|
-
return void 0;
|
|
40
|
-
}
|
|
37
|
+
if (isEnoent(error)) return void 0;
|
|
41
38
|
throw error;
|
|
42
39
|
}
|
|
43
40
|
const raw = JSON.parse(text);
|
|
44
|
-
if (
|
|
41
|
+
if (!isRecord(raw)) {
|
|
45
42
|
throw new Error("typed provider HTTP observation must be a JSON object");
|
|
46
43
|
}
|
|
47
44
|
const record = raw;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Guard } from "typebox/guard";
|
|
2
|
+
/**
|
|
3
|
+
* Sole readers for a plain object, a Node errno, and an Error message.
|
|
4
|
+
* Callers choose which codes count; ENOTDIR is not ENOENT.
|
|
5
|
+
* Plain objects are TypeBox `Guard.IsObjectNotArray`.
|
|
6
|
+
*/
|
|
7
|
+
export function isRecord(value) {
|
|
8
|
+
return Guard.IsObjectNotArray(value);
|
|
9
|
+
}
|
|
10
|
+
export function errnoCode(error) {
|
|
11
|
+
return error !== null && typeof error === "object" && "code" in error
|
|
12
|
+
&& typeof error.code === "string"
|
|
13
|
+
? error.code
|
|
14
|
+
: undefined;
|
|
15
|
+
}
|
|
16
|
+
/** `Error.message`, otherwise `String(value)`. */
|
|
17
|
+
export function errorText(error) {
|
|
18
|
+
return error instanceof Error ? error.message : String(error);
|
|
19
|
+
}
|
|
20
|
+
/** True only for ENOENT on an Error. ENOTDIR and every other code stay distinct. */
|
|
21
|
+
export function isEnoent(error) {
|
|
22
|
+
return error instanceof Error && errnoCode(error) === "ENOENT";
|
|
23
|
+
}
|
|
24
|
+
/** ENOENT or ENOTDIR on an Error. */
|
|
25
|
+
export function isMissingPathError(error) {
|
|
26
|
+
const code = errnoCode(error);
|
|
27
|
+
return error instanceof Error && (code === "ENOENT" || code === "ENOTDIR");
|
|
28
|
+
}
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
* Pi's piped-stdin reader trims the wrapper; the body field keeps original bytes.
|
|
4
4
|
* No size cap and no path/pointer substitute — the whole dialogue rides the envelope.
|
|
5
5
|
*/
|
|
6
|
+
import { isRecord } from "./unknown-value.js";
|
|
6
7
|
export const USER_DIALOGUE_STDIN_KIND = "ak-user-dialogue";
|
|
7
8
|
export function encodeUserDialogueStdin(body) {
|
|
8
9
|
return JSON.stringify({ kind: USER_DIALOGUE_STDIN_KIND, body });
|
|
9
10
|
}
|
|
10
11
|
function isUserDialogueEnvelope(value) {
|
|
11
|
-
if (
|
|
12
|
+
if (!isRecord(value))
|
|
12
13
|
return false;
|
|
13
14
|
const record = value;
|
|
14
15
|
return record.kind === USER_DIALOGUE_STDIN_KIND && typeof record.body === "string";
|
package/package.json
CHANGED
|
@@ -27,6 +27,8 @@ import {
|
|
|
27
27
|
} from "../prepared-role-turn.ts";
|
|
28
28
|
import { acpModelId, type AcpHostDescription } from "./description.ts";
|
|
29
29
|
|
|
30
|
+
import { isRecord } from "../unknown-value.ts";
|
|
31
|
+
|
|
30
32
|
/**
|
|
31
33
|
* #959: collect free-form agent text from ACP session/update stream.
|
|
32
34
|
* Used only when the navigator seat spoke prose without calling the output tool.
|
|
@@ -42,7 +44,7 @@ function acpAgentTextChunk(params: Readonly<Record<string, unknown>>): string |
|
|
|
42
44
|
let textFallback: unknown;
|
|
43
45
|
|
|
44
46
|
const nested = params.update;
|
|
45
|
-
if (
|
|
47
|
+
if (isRecord(nested)) {
|
|
46
48
|
const record = nested as Record<string, unknown>;
|
|
47
49
|
kind = record.sessionUpdate;
|
|
48
50
|
content = record.content;
|
|
@@ -55,7 +57,7 @@ function acpAgentTextChunk(params: Readonly<Record<string, unknown>>): string |
|
|
|
55
57
|
|
|
56
58
|
if (kind !== "agent_message_chunk" && kind !== "agent_message") return undefined;
|
|
57
59
|
if (typeof content === "string" && content.length > 0) return content;
|
|
58
|
-
if (
|
|
60
|
+
if (isRecord(content)) {
|
|
59
61
|
const record = content as Record<string, unknown>;
|
|
60
62
|
if (typeof record.text === "string" && record.text.length > 0) return record.text;
|
|
61
63
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { basename, dirname, isAbsolute, resolve } from "node:path";
|
|
3
3
|
|
|
4
|
+
import { errnoCode } from "./unknown-value.ts";
|
|
5
|
+
|
|
4
6
|
/** Git discovery env vars that must not influence book-key resolution from cwd. */
|
|
5
7
|
const GIT_DISCOVERY_ENV_KEYS = [
|
|
6
8
|
"GIT_DIR",
|
|
@@ -60,8 +62,7 @@ export class ActivationGitRepositoryRequiredError extends Error {
|
|
|
60
62
|
|
|
61
63
|
/** Spawn/OS failure identity for the git child — not a repository-status result. */
|
|
62
64
|
function isGitSpawnInfrastructureError(error: unknown): boolean {
|
|
63
|
-
|
|
64
|
-
const code = (error as { code: unknown }).code;
|
|
65
|
+
const code = errnoCode(error);
|
|
65
66
|
return code === "ENOENT" || code === "EACCES" || code === "EPERM";
|
|
66
67
|
}
|
|
67
68
|
|
|
@@ -7,6 +7,10 @@ import {
|
|
|
7
7
|
import { userInfo } from "node:os";
|
|
8
8
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
9
9
|
|
|
10
|
+
import { errnoCode, errorText } from "./unknown-value.ts";
|
|
11
|
+
|
|
12
|
+
export { errnoCode, errorText };
|
|
13
|
+
|
|
10
14
|
/**
|
|
11
15
|
* Typed activation-ledger path/fs failure. Callers discriminate with instanceof/code;
|
|
12
16
|
* never by parsing message prose. Original filesystem causes are retained.
|
|
@@ -128,18 +132,6 @@ export function physicallyContainedIn(root: string, candidate: string): boolean
|
|
|
128
132
|
return pathContainedIn(physicalPathIdentity(root), physicalPathIdentity(candidate));
|
|
129
133
|
}
|
|
130
134
|
|
|
131
|
-
export function errnoCode(error: unknown): string | undefined {
|
|
132
|
-
return error !== null && typeof error === "object" && "code" in error
|
|
133
|
-
&& typeof (error as { code: unknown }).code === "string"
|
|
134
|
-
? (error as { code: string }).code
|
|
135
|
-
: undefined;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function errorText(error: unknown): string {
|
|
139
|
-
if (!(error instanceof Error)) return String(error);
|
|
140
|
-
return error.message;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
135
|
/**
|
|
144
136
|
* Ensure the configured ledger root is a physical directory identity.
|
|
145
137
|
* lstat before create/realpath: a root symlink is never admitted, even when its
|
package/src/analyst-entry.ts
CHANGED
|
@@ -46,6 +46,8 @@ import {
|
|
|
46
46
|
type AnalystUnreadableRun,
|
|
47
47
|
} from "./analyst-page.ts";
|
|
48
48
|
|
|
49
|
+
import { isMissingPathError } from "./unknown-value.ts";
|
|
50
|
+
|
|
49
51
|
/** #338 compute-if-missing failure — issue identity + real cause (CLI → ControlledFailure). */
|
|
50
52
|
export class AnalystIssueComputeError extends Error {
|
|
51
53
|
readonly code = "analyst-issue-compute-failed" as const;
|
|
@@ -80,14 +82,6 @@ export class AnalystIssueComputeError extends Error {
|
|
|
80
82
|
}
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
function isMissingPathError(error: unknown): boolean {
|
|
84
|
-
return (
|
|
85
|
-
error instanceof Error
|
|
86
|
-
&& "code" in error
|
|
87
|
-
&& (error.code === "ENOENT" || error.code === "ENOTDIR")
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
85
|
/** Issue-mode typed input — book × ticket scope (#399). */
|
|
92
86
|
export type AnalystIssueModeInput = {
|
|
93
87
|
readonly mode: "issue";
|
|
@@ -123,7 +117,7 @@ export type AnalystIssueModeInput = {
|
|
|
123
117
|
* Sole sweep-mode input contract (#298/#329/#337).
|
|
124
118
|
* Schema is the single definition; TS types are derived (no parallel hand shape).
|
|
125
119
|
* projectRoot = string (not nonempty); changedLines optional finite non-negative;
|
|
126
|
-
* 0 remains typed
|
|
120
|
+
* 0 remains typed 空缺. Extra keys are not a reject reason.
|
|
127
121
|
*/
|
|
128
122
|
export const analystSweepModeInputSchema = Type.Object(
|
|
129
123
|
{
|
|
@@ -137,11 +131,9 @@ export const analystSweepModeInputSchema = Type.Object(
|
|
|
137
131
|
Type.Number({ minimum: 0, maximum: Number.MAX_VALUE }),
|
|
138
132
|
),
|
|
139
133
|
},
|
|
140
|
-
{ additionalProperties: false },
|
|
141
134
|
),
|
|
142
135
|
),
|
|
143
136
|
},
|
|
144
|
-
{ additionalProperties: false },
|
|
145
137
|
);
|
|
146
138
|
|
|
147
139
|
/** Sweep-mode typed input — 已并 PR 清单 + LOC → 补算缺页 + 维护全库索引. */
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
type LedgerSessionRow,
|
|
39
39
|
} from "./ledger-session-read.ts";
|
|
40
40
|
|
|
41
|
+
import { isRecord, errorText, isEnoent } from "./unknown-value.ts";
|
|
41
42
|
|
|
42
43
|
/** One completed gate round: direct officer receipt or historical province/officer pair. */
|
|
43
44
|
/** Honest origin discriminant: direct summons vs historical province dispatch. */
|
|
@@ -88,10 +89,6 @@ const OFFICER_ARG_ALIASES: Readonly<Record<string, "inspector" | "notary">> = {
|
|
|
88
89
|
notary: "notary",
|
|
89
90
|
};
|
|
90
91
|
|
|
91
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
92
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
92
|
function isParentAttemptBindingRow(row: LedgerSessionRow): boolean {
|
|
96
93
|
return row.type === "custom" && row.customType === AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE;
|
|
97
94
|
}
|
|
@@ -104,15 +101,6 @@ function intervalRowsForGateCall(
|
|
|
104
101
|
return intervalRowsAroundAnchor(rows, callRowIndex, isParentAttemptBindingRow).rows;
|
|
105
102
|
}
|
|
106
103
|
|
|
107
|
-
/** Only true absence (ENOENT). ENOTDIR is damaged topology — must stay loud. */
|
|
108
|
-
function isMissingDirectoryError(error: unknown): boolean {
|
|
109
|
-
return (
|
|
110
|
-
error instanceof Error
|
|
111
|
-
&& "code" in error
|
|
112
|
-
&& error.code === "ENOENT"
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
104
|
function normalizeOfficerArg(raw: unknown): "inspector" | "notary" | undefined {
|
|
117
105
|
if (typeof raw !== "string") return undefined;
|
|
118
106
|
return OFFICER_ARG_ALIASES[raw.trim()];
|
|
@@ -438,7 +426,7 @@ async function resolveOfficerSessionFromPointerFile(
|
|
|
438
426
|
raw = JSON.parse(await readFile(pointerPath, "utf8"));
|
|
439
427
|
} catch (error) {
|
|
440
428
|
throw new Error(
|
|
441
|
-
`direct officer run pointer unreadable in ${pointerPath}: ${
|
|
429
|
+
`direct officer run pointer unreadable in ${pointerPath}: ${errorText(error)}`,
|
|
442
430
|
{ cause: error },
|
|
443
431
|
);
|
|
444
432
|
}
|
|
@@ -490,7 +478,7 @@ export async function readAnalystGateCyclesFromAuditorRoles(
|
|
|
490
478
|
.map((e) => e.name)
|
|
491
479
|
.sort();
|
|
492
480
|
} catch (error) {
|
|
493
|
-
if (
|
|
481
|
+
if (isEnoent(error)) continue;
|
|
494
482
|
throw error;
|
|
495
483
|
}
|
|
496
484
|
for (const name of names) {
|
package/src/analyst-index.ts
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { open, readFile, unlink } from "node:fs/promises";
|
|
22
22
|
import { dirname, join } from "node:path";
|
|
23
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
23
24
|
|
|
24
25
|
import { writeFileAtomically } from "./atomic-write.ts";
|
|
25
26
|
import {
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
physicalPathIdentity,
|
|
29
30
|
} from "./activation-ledger-topology.ts";
|
|
30
31
|
import { resolveAnalystBookKey } from "./analyst-book-key.ts";
|
|
32
|
+
import { isMissingPathError } from "./unknown-value.ts";
|
|
31
33
|
import type {
|
|
32
34
|
AnalystIssueMetricsPage,
|
|
33
35
|
AnalystOptionalMetricNumber,
|
|
@@ -38,12 +40,6 @@ const LIBRARY_INDEX_LOCK_NAME = ".library-index.lock";
|
|
|
38
40
|
const LIBRARY_INDEX_LOCK_TIMEOUT_MS = 30_000;
|
|
39
41
|
const LIBRARY_INDEX_LOCK_RETRY_MS = 15;
|
|
40
42
|
|
|
41
|
-
function sleep(ms: number): Promise<void> {
|
|
42
|
-
return new Promise((resolve) => {
|
|
43
|
-
setTimeout(resolve, ms);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
43
|
/**
|
|
48
44
|
* Exclusive create lock for the library-index read→upsert→write critical section.
|
|
49
45
|
* Same-directory sibling of the index file; not a second index and not a daemon.
|
|
@@ -270,13 +266,7 @@ export async function readAnalystLibraryIndexPage(
|
|
|
270
266
|
try {
|
|
271
267
|
raw = await readFile(path, "utf8");
|
|
272
268
|
} catch (error) {
|
|
273
|
-
if (
|
|
274
|
-
error instanceof Error
|
|
275
|
-
&& "code" in error
|
|
276
|
-
&& (error.code === "ENOENT" || error.code === "ENOTDIR")
|
|
277
|
-
) {
|
|
278
|
-
return undefined;
|
|
279
|
-
}
|
|
269
|
+
if (isMissingPathError(error)) return undefined;
|
|
280
270
|
throw error;
|
|
281
271
|
}
|
|
282
272
|
const parsed: unknown = JSON.parse(raw);
|
package/src/analyst-ledger.ts
CHANGED
|
@@ -49,23 +49,9 @@ import {
|
|
|
49
49
|
type AnalystGateCycleRound,
|
|
50
50
|
} from "./analyst-gate-cycles-read.ts";
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
function isMissingPathError(error: unknown): boolean {
|
|
55
|
-
return (
|
|
56
|
-
error instanceof Error
|
|
57
|
-
&& "code" in error
|
|
58
|
-
&& (error.code === "ENOENT" || error.code === "ENOTDIR")
|
|
59
|
-
);
|
|
60
|
-
}
|
|
52
|
+
import { isRecord, errorText, isMissingPathError } from "./unknown-value.ts";
|
|
61
53
|
|
|
62
|
-
|
|
63
|
-
return error instanceof Error ? error.message : String(error);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
67
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
68
|
-
}
|
|
54
|
+
export type { AnalystGateCycleRound } from "./analyst-gate-cycles-read.ts";
|
|
69
55
|
|
|
70
56
|
/** Existing public-cli run-state live phases — not a new state machine. */
|
|
71
57
|
const LIVE_RUN_STATES = new Set(["admitted", "running", "resumable"]);
|
|
@@ -14,6 +14,8 @@ import { medianNumber } from "../analyst-median.ts";
|
|
|
14
14
|
import type { AnalystMetricFamilyModule } from "../analyst-metric-family.ts";
|
|
15
15
|
import { packagedAnalystTerminal } from "../packaged-role-registry.ts";
|
|
16
16
|
|
|
17
|
+
import { isRecord } from "../unknown-value.ts";
|
|
18
|
+
|
|
17
19
|
const WORKER_ROLES = new Set(["coder", "fixer"]);
|
|
18
20
|
|
|
19
21
|
/** Lawful acceptance vocabulary by role (PRD 受理终态映射). */
|
|
@@ -95,10 +97,6 @@ export type AnalystAcceptanceSuccessReworkSection = {
|
|
|
95
97
|
readonly rework: AnalystReworkLens;
|
|
96
98
|
};
|
|
97
99
|
|
|
98
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
99
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
100
|
/** Locate collector groups array: receipt.groups or top-level groups. */
|
|
103
101
|
function findCollectorGroups(body: Record<string, unknown>): unknown {
|
|
104
102
|
if (Array.isArray(body.groups)) return body.groups;
|
|
@@ -17,6 +17,8 @@ import type {
|
|
|
17
17
|
AnalystUnreadableRun,
|
|
18
18
|
} from "../analyst-page.ts";
|
|
19
19
|
|
|
20
|
+
import { isRecord } from "../unknown-value.ts";
|
|
21
|
+
|
|
20
22
|
export type AnalystRoundTimelineTerminal =
|
|
21
23
|
| {
|
|
22
24
|
readonly kind: "receipt";
|
|
@@ -66,10 +68,6 @@ export type AnalystRoundTimelineSection = {
|
|
|
66
68
|
readonly lanes: readonly AnalystRoundTimelineLane[];
|
|
67
69
|
};
|
|
68
70
|
|
|
69
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
70
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
71
|
function readClassCount(body: Record<string, unknown>): number | undefined {
|
|
74
72
|
if (!isRecord(body.outcome)) return undefined;
|
|
75
73
|
if (!isRecord(body.outcome.decisiveFacts)) return undefined;
|
package/src/analyst-page.ts
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* top-level sections via directory discovery — B/C waves add family files
|
|
7
7
|
* without forking the page writer or editing a shared registry list.
|
|
8
8
|
*/
|
|
9
|
-
import { createHash } from "node:crypto";
|
|
10
9
|
import { dirname, join } from "node:path";
|
|
11
10
|
|
|
11
|
+
import { sha256Hex } from "./sha256.ts";
|
|
12
12
|
import { writeFileAtomically } from "./atomic-write.ts";
|
|
13
13
|
import {
|
|
14
14
|
assertLedgerFileInsideHome,
|
|
@@ -161,7 +161,7 @@ export function analystIssuePageKey(address: AnalystIssuePageAddress): string {
|
|
|
161
161
|
} else if (address.scopeRootIdentity !== undefined) {
|
|
162
162
|
parts.push("root", physicalPathIdentity(address.scopeRootIdentity));
|
|
163
163
|
}
|
|
164
|
-
return
|
|
164
|
+
return sha256Hex(parts.join("\0")).slice(0, 32);
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
export function analystIssuePagePath(
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* books/<book>/navigator/<work-subject-digest>/. Callers must not re-hash
|
|
8
8
|
* subject or re-join book/navigator/digest themselves.
|
|
9
9
|
*/
|
|
10
|
-
import { createHash } from "node:crypto";
|
|
11
10
|
import { join } from "node:path";
|
|
12
11
|
|
|
12
|
+
import { sha256Hex } from "./sha256.ts";
|
|
13
13
|
import { resolveBookKeyFromGit } from "./activation-ledger-git.ts";
|
|
14
14
|
import {
|
|
15
15
|
activationBookDirectory,
|
|
@@ -52,7 +52,7 @@ export function resolveNavigatorWorkSubjectPlacement(input: {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
ledgerHome ??= resolveActivationLedgerHome(input.home);
|
|
55
|
-
const digest =
|
|
55
|
+
const digest = sha256Hex(input.subject).slice(0, 32);
|
|
56
56
|
return {
|
|
57
57
|
ledgerHome,
|
|
58
58
|
sessionDir: join(
|
package/src/audit-escalation.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { isRecord } from "./unknown-value.ts";
|
|
1
2
|
/** Historical audit-escalation discriminator retained for reading pre-#1057 run artifacts. */
|
|
2
3
|
export const AUDIT_ESCALATION_KIND = "audit_escalation" as const;
|
|
3
4
|
|
|
4
5
|
export function isAuditEscalationResult(value: unknown): value is { readonly kind: typeof AUDIT_ESCALATION_KIND } {
|
|
5
|
-
return
|
|
6
|
+
return isRecord(value)
|
|
6
7
|
&& (value as Record<string, unknown>).kind === AUDIT_ESCALATION_KIND;
|
|
7
8
|
}
|
package/src/auditor-soul.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { basename, join } from "node:path";
|
|
3
3
|
|
|
4
|
+
import { isRecord } from "./unknown-value.ts";
|
|
5
|
+
|
|
4
6
|
import {
|
|
5
7
|
joinPackageMaterials,
|
|
6
8
|
readPackageMaterial,
|
|
@@ -126,7 +128,7 @@ export async function readAuditorResumeBinding(runDirectory: string): Promise<
|
|
|
126
128
|
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
|
|
127
129
|
throw error;
|
|
128
130
|
}
|
|
129
|
-
if (
|
|
131
|
+
if (!isRecord(raw)) return undefined;
|
|
130
132
|
const record = raw as { sourceRunPath?: unknown; auditorSubject?: unknown };
|
|
131
133
|
if (typeof record.sourceRunPath !== "string" || record.sourceRunPath.trim() === "") return undefined;
|
|
132
134
|
const subject = isAuditorSoulRole(record.auditorSubject)
|
|
@@ -19,22 +19,11 @@ import {
|
|
|
19
19
|
readMigrationDerivedTicketNumber,
|
|
20
20
|
} from "./run-ticket-number.ts";
|
|
21
21
|
|
|
22
|
+
import { isRecord, isEnoent } from "./unknown-value.ts";
|
|
23
|
+
|
|
22
24
|
const TICKET_NUMBER_RE = /^[1-9][0-9]*$/;
|
|
23
25
|
const RUN_DIR_NAME_RE = /^([^@]+)@([^@]+)$/;
|
|
24
26
|
|
|
25
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
26
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Sole migration ENOENT projection — non-ENOENT must propagate. */
|
|
30
|
-
export function isMigrationEnoent(error: unknown): boolean {
|
|
31
|
-
return (
|
|
32
|
-
error instanceof Error &&
|
|
33
|
-
"code" in error &&
|
|
34
|
-
(error as NodeJS.ErrnoException).code === "ENOENT"
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
27
|
/** Book-key directories under a books root; missing root → []. */
|
|
39
28
|
export async function listMigrationBookKeys(
|
|
40
29
|
booksDirectory: string,
|
|
@@ -46,7 +35,7 @@ export async function listMigrationBookKeys(
|
|
|
46
35
|
.map((entry) => entry.name)
|
|
47
36
|
.sort((a, b) => a.localeCompare(b));
|
|
48
37
|
} catch (error) {
|
|
49
|
-
if (
|
|
38
|
+
if (isEnoent(error)) return [];
|
|
50
39
|
throw error;
|
|
51
40
|
}
|
|
52
41
|
}
|
|
@@ -59,7 +48,7 @@ export async function listMigrationDirents(
|
|
|
59
48
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
60
49
|
return [...entries].sort((a, b) => a.name.localeCompare(b.name));
|
|
61
50
|
} catch (error) {
|
|
62
|
-
if (
|
|
51
|
+
if (isEnoent(error)) return [];
|
|
63
52
|
throw error;
|
|
64
53
|
}
|
|
65
54
|
}
|
|
@@ -109,7 +98,7 @@ async function readProjectRootFromRun(
|
|
|
109
98
|
return { projectRoot, sourcePage: page };
|
|
110
99
|
}
|
|
111
100
|
} catch (error) {
|
|
112
|
-
if (
|
|
101
|
+
if (isEnoent(error)) continue;
|
|
113
102
|
throw error;
|
|
114
103
|
}
|
|
115
104
|
}
|
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
import {
|
|
26
26
|
findPlacedMigratingRun,
|
|
27
27
|
isFlatRunsRelative,
|
|
28
|
-
isMigrationEnoent,
|
|
29
28
|
isTicketNumberString,
|
|
30
29
|
listBackupRunLeaves,
|
|
31
30
|
listMigrationBookKeys,
|
|
@@ -37,6 +36,8 @@ import {
|
|
|
37
36
|
type RunDirectoryPathRewrite,
|
|
38
37
|
} from "./role-run-relocation.ts";
|
|
39
38
|
|
|
39
|
+
import { isRecord, errorText, isEnoent } from "./unknown-value.ts";
|
|
40
|
+
|
|
40
41
|
const AUDITOR_ROLES_PARTITION = "auditor-roles";
|
|
41
42
|
const ISSUES_PARTITION = "issues";
|
|
42
43
|
const DEPRECATED_KINDS_PARTITION = "deprecated-kinds";
|
|
@@ -101,7 +102,7 @@ async function directoryExists(path: string): Promise<boolean> {
|
|
|
101
102
|
const info = await stat(path);
|
|
102
103
|
return info.isDirectory();
|
|
103
104
|
} catch (error) {
|
|
104
|
-
if (
|
|
105
|
+
if (isEnoent(error)) return false;
|
|
105
106
|
throw error;
|
|
106
107
|
}
|
|
107
108
|
}
|
|
@@ -219,7 +220,7 @@ async function readJsonlParentSession(path: string): Promise<string | undefined>
|
|
|
219
220
|
try {
|
|
220
221
|
raw = await readFile(path, "utf8");
|
|
221
222
|
} catch (error) {
|
|
222
|
-
if (
|
|
223
|
+
if (isEnoent(error)) return undefined;
|
|
223
224
|
throw error;
|
|
224
225
|
}
|
|
225
226
|
const newline = raw.indexOf("\n");
|
|
@@ -227,7 +228,7 @@ async function readJsonlParentSession(path: string): Promise<string | undefined>
|
|
|
227
228
|
if (line.length === 0) return undefined;
|
|
228
229
|
try {
|
|
229
230
|
const header: unknown = JSON.parse(line);
|
|
230
|
-
if (
|
|
231
|
+
if (!isRecord(header)) {
|
|
231
232
|
return undefined;
|
|
232
233
|
}
|
|
233
234
|
const parentSession = (header as { parentSession?: unknown }).parentSession;
|
|
@@ -268,13 +269,13 @@ async function findTrueVolumeParent(
|
|
|
268
269
|
try {
|
|
269
270
|
ledgerRaw = await readFile(join(volumeDirectory, CURRENT_SESSION_LEDGER), "utf8");
|
|
270
271
|
} catch (error) {
|
|
271
|
-
if (
|
|
272
|
+
if (isEnoent(error)) return undefined;
|
|
272
273
|
throw error;
|
|
273
274
|
}
|
|
274
275
|
let sessionFileField: string;
|
|
275
276
|
try {
|
|
276
277
|
const ledger: unknown = JSON.parse(ledgerRaw);
|
|
277
|
-
if (
|
|
278
|
+
if (!isRecord(ledger)) {
|
|
278
279
|
return undefined;
|
|
279
280
|
}
|
|
280
281
|
const sessionFile = (ledger as { sessionFile?: unknown }).sessionFile;
|
|
@@ -298,7 +299,7 @@ async function findTrueVolumeParent(
|
|
|
298
299
|
const info = await lstat(liveSession);
|
|
299
300
|
if (!info.isFile()) return undefined;
|
|
300
301
|
} catch (error) {
|
|
301
|
-
if (
|
|
302
|
+
if (isEnoent(error)) return undefined;
|
|
302
303
|
throw error;
|
|
303
304
|
}
|
|
304
305
|
|
|
@@ -413,7 +414,7 @@ async function readSessionFileField(path: string): Promise<string | undefined> {
|
|
|
413
414
|
try {
|
|
414
415
|
raw = await readFile(path, "utf8");
|
|
415
416
|
} catch (error) {
|
|
416
|
-
if (
|
|
417
|
+
if (isEnoent(error)) return undefined;
|
|
417
418
|
throw error;
|
|
418
419
|
}
|
|
419
420
|
let parsed: unknown;
|
|
@@ -422,12 +423,12 @@ async function readSessionFileField(path: string): Promise<string | undefined> {
|
|
|
422
423
|
} catch (error) {
|
|
423
424
|
throw new Error(
|
|
424
425
|
`book topology migration cannot read current-session pointer at ${path}: invalid JSON (${
|
|
425
|
-
|
|
426
|
+
errorText(error)
|
|
426
427
|
})`,
|
|
427
428
|
{ cause: error },
|
|
428
429
|
);
|
|
429
430
|
}
|
|
430
|
-
if (
|
|
431
|
+
if (!isRecord(parsed)) {
|
|
431
432
|
throw new Error(
|
|
432
433
|
`book topology migration cannot read current-session pointer at ${path}: expected a JSON object`,
|
|
433
434
|
);
|
|
@@ -480,7 +481,7 @@ async function mergeRewrittenCurrentSession(
|
|
|
480
481
|
srcStat = await stat(srcPath);
|
|
481
482
|
srcRaw = await readFile(srcPath, "utf8");
|
|
482
483
|
} catch (error) {
|
|
483
|
-
if (
|
|
484
|
+
if (isEnoent(error)) return;
|
|
484
485
|
throw error;
|
|
485
486
|
}
|
|
486
487
|
|
|
@@ -488,7 +489,7 @@ async function mergeRewrittenCurrentSession(
|
|
|
488
489
|
let nextSessionFile: string | undefined;
|
|
489
490
|
try {
|
|
490
491
|
const parsed: unknown = JSON.parse(srcRaw);
|
|
491
|
-
if (
|
|
492
|
+
if (isRecord(parsed)) {
|
|
492
493
|
const record = parsed as Record<string, unknown>;
|
|
493
494
|
if (typeof record.sessionFile === "string") {
|
|
494
495
|
const rewritten = rewritePathIntoDestination(
|
|
@@ -510,7 +511,7 @@ async function mergeRewrittenCurrentSession(
|
|
|
510
511
|
try {
|
|
511
512
|
destStat = await stat(destPath);
|
|
512
513
|
} catch (error) {
|
|
513
|
-
if (!
|
|
514
|
+
if (!isEnoent(error)) throw error;
|
|
514
515
|
}
|
|
515
516
|
await writeWinningCurrentSession(
|
|
516
517
|
destPath,
|
|
@@ -656,7 +657,7 @@ async function copyIssuesNonRunTree(source: string, destination: string): Promis
|
|
|
656
657
|
`book topology migration refuses to overwrite ${to} with ${from}`,
|
|
657
658
|
);
|
|
658
659
|
} catch (error) {
|
|
659
|
-
if (!
|
|
660
|
+
if (!isEnoent(error)) throw error;
|
|
660
661
|
}
|
|
661
662
|
await cp(from, to, { preserveTimestamps: true });
|
|
662
663
|
}
|
|
@@ -719,7 +720,7 @@ async function unlinkIfPresent(path: string): Promise<void> {
|
|
|
719
720
|
try {
|
|
720
721
|
await unlink(path);
|
|
721
722
|
} catch (error) {
|
|
722
|
-
if (!
|
|
723
|
+
if (!isEnoent(error)) throw error;
|
|
723
724
|
}
|
|
724
725
|
}
|
|
725
726
|
|