@rex0220/ksql-flownet 1.0.0

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 (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +81 -0
  3. package/README.md +117 -0
  4. package/dist/bundle/bundle-builder.d.ts +49 -0
  5. package/dist/bundle/bundle-builder.js +208 -0
  6. package/dist/bundle/index.d.ts +4 -0
  7. package/dist/bundle/index.js +4 -0
  8. package/dist/bundle/upload.d.ts +28 -0
  9. package/dist/bundle/upload.js +72 -0
  10. package/dist/bundle/zip-store.d.ts +17 -0
  11. package/dist/bundle/zip-store.js +175 -0
  12. package/dist/cli/archive-run-command.d.ts +12 -0
  13. package/dist/cli/archive-run-command.js +131 -0
  14. package/dist/cli/cancel-run-command.d.ts +9 -0
  15. package/dist/cli/cancel-run-command.js +77 -0
  16. package/dist/cli/force-unlock-network-command.d.ts +8 -0
  17. package/dist/cli/force-unlock-network-command.js +129 -0
  18. package/dist/cli/index.d.ts +3 -0
  19. package/dist/cli/index.js +132 -0
  20. package/dist/cli/plan-command.d.ts +2 -0
  21. package/dist/cli/plan-command.js +100 -0
  22. package/dist/cli/poll-requests-command.d.ts +3 -0
  23. package/dist/cli/poll-requests-command.js +74 -0
  24. package/dist/cli/record-job-unlock-command.d.ts +11 -0
  25. package/dist/cli/record-job-unlock-command.js +78 -0
  26. package/dist/cli/resolve-node-command.d.ts +13 -0
  27. package/dist/cli/resolve-node-command.js +174 -0
  28. package/dist/cli/run-network-command.d.ts +14 -0
  29. package/dist/cli/run-network-command.js +418 -0
  30. package/dist/cli/status-command.d.ts +5 -0
  31. package/dist/cli/status-command.js +137 -0
  32. package/dist/cli/validate-command.d.ts +6 -0
  33. package/dist/cli/validate-command.js +17 -0
  34. package/dist/dag/descendants.d.ts +9 -0
  35. package/dist/dag/descendants.js +67 -0
  36. package/dist/dag/topological-sort.d.ts +7 -0
  37. package/dist/dag/topological-sort.js +32 -0
  38. package/dist/domain/business-key.d.ts +15 -0
  39. package/dist/domain/business-key.js +249 -0
  40. package/dist/domain/canonical-lock-key.d.ts +17 -0
  41. package/dist/domain/canonical-lock-key.js +60 -0
  42. package/dist/domain/canonical-record-key.d.ts +18 -0
  43. package/dist/domain/canonical-record-key.js +70 -0
  44. package/dist/domain/load-network.d.ts +4 -0
  45. package/dist/domain/load-network.js +47 -0
  46. package/dist/domain/network-definition.d.ts +47 -0
  47. package/dist/domain/network-definition.js +7 -0
  48. package/dist/domain/persistence-model.d.ts +198 -0
  49. package/dist/domain/persistence-model.js +2 -0
  50. package/dist/domain/run-aggregate.d.ts +3 -0
  51. package/dist/domain/run-aggregate.js +15 -0
  52. package/dist/domain/validate-network-path.d.ts +11 -0
  53. package/dist/domain/validate-network-path.js +33 -0
  54. package/dist/domain/validate-network.d.ts +3 -0
  55. package/dist/domain/validate-network.js +180 -0
  56. package/dist/executor/attempt-executor.d.ts +43 -0
  57. package/dist/executor/attempt-executor.js +236 -0
  58. package/dist/executor/job-log-reader.d.ts +37 -0
  59. package/dist/executor/job-log-reader.js +91 -0
  60. package/dist/executor/ksql-flow-cli.d.ts +86 -0
  61. package/dist/executor/ksql-flow-cli.js +161 -0
  62. package/dist/executor/preflight.d.ts +41 -0
  63. package/dist/executor/preflight.js +150 -0
  64. package/dist/executor/result-classifier.d.ts +69 -0
  65. package/dist/executor/result-classifier.js +237 -0
  66. package/dist/executor/run-subprocess.d.ts +64 -0
  67. package/dist/executor/run-subprocess.js +175 -0
  68. package/dist/io/input-baseline.d.ts +19 -0
  69. package/dist/io/input-baseline.js +214 -0
  70. package/dist/io/io-config.d.ts +6 -0
  71. package/dist/io/io-config.js +33 -0
  72. package/dist/io/io-path.d.ts +37 -0
  73. package/dist/io/io-path.js +279 -0
  74. package/dist/orchestration/archive-run.d.ts +53 -0
  75. package/dist/orchestration/archive-run.js +323 -0
  76. package/dist/orchestration/cancel-request.d.ts +18 -0
  77. package/dist/orchestration/cancel-request.js +67 -0
  78. package/dist/orchestration/ensure-run.d.ts +83 -0
  79. package/dist/orchestration/ensure-run.js +632 -0
  80. package/dist/orchestration/job-lock-audit.d.ts +24 -0
  81. package/dist/orchestration/job-lock-audit.js +123 -0
  82. package/dist/orchestration/orphan-attempt-adjudication.d.ts +39 -0
  83. package/dist/orchestration/orphan-attempt-adjudication.js +167 -0
  84. package/dist/orchestration/reconciliation.d.ts +41 -0
  85. package/dist/orchestration/reconciliation.js +526 -0
  86. package/dist/orchestration/resolve-node.d.ts +29 -0
  87. package/dist/orchestration/resolve-node.js +105 -0
  88. package/dist/orchestration/run-activity.d.ts +13 -0
  89. package/dist/orchestration/run-activity.js +14 -0
  90. package/dist/orchestration/sequential-scheduler.d.ts +79 -0
  91. package/dist/orchestration/sequential-scheduler.js +706 -0
  92. package/dist/orchestration/status.d.ts +91 -0
  93. package/dist/orchestration/status.js +171 -0
  94. package/dist/persistence/in-memory-repository.d.ts +41 -0
  95. package/dist/persistence/in-memory-repository.js +333 -0
  96. package/dist/persistence/kintone/client.d.ts +36 -0
  97. package/dist/persistence/kintone/client.js +93 -0
  98. package/dist/persistence/kintone/design-notes.d.ts +38 -0
  99. package/dist/persistence/kintone/design-notes.js +38 -0
  100. package/dist/persistence/kintone/repository.d.ts +44 -0
  101. package/dist/persistence/kintone/repository.js +836 -0
  102. package/dist/persistence/kintone/schema.d.ts +116 -0
  103. package/dist/persistence/kintone/schema.js +115 -0
  104. package/dist/persistence/network-lock-reader.d.ts +24 -0
  105. package/dist/persistence/network-lock-reader.js +43 -0
  106. package/dist/persistence/network-lock-recovery.d.ts +66 -0
  107. package/dist/persistence/network-lock-recovery.js +392 -0
  108. package/dist/persistence/network-lock.d.ts +97 -0
  109. package/dist/persistence/network-lock.js +408 -0
  110. package/dist/persistence/repository.d.ts +101 -0
  111. package/dist/persistence/repository.js +11 -0
  112. package/dist/persistence/state-transition.d.ts +4 -0
  113. package/dist/persistence/state-transition.js +25 -0
  114. package/dist/requests/flownet-child-client.d.ts +78 -0
  115. package/dist/requests/flownet-child-client.js +243 -0
  116. package/dist/requests/kintone-request-store.d.ts +44 -0
  117. package/dist/requests/kintone-request-store.js +202 -0
  118. package/dist/requests/poll-requests-config.d.ts +26 -0
  119. package/dist/requests/poll-requests-config.js +105 -0
  120. package/dist/requests/request-model.d.ts +57 -0
  121. package/dist/requests/request-model.js +305 -0
  122. package/dist/requests/request-poller.d.ts +33 -0
  123. package/dist/requests/request-poller.js +353 -0
  124. package/dist/requests/request-result.d.ts +18 -0
  125. package/dist/requests/request-result.js +215 -0
  126. package/dist/requests/start-request.d.ts +26 -0
  127. package/dist/requests/start-request.js +83 -0
  128. package/package.json +63 -0
  129. package/schemas/.gitkeep +1 -0
  130. package/schemas/network-definition.schema.json +121 -0
@@ -0,0 +1,123 @@
1
+ import { randomUUID } from "node:crypto";
2
+ const OUTCOMES = new Set([
3
+ "RELEASED",
4
+ "NOT_FOUND",
5
+ "NOT_RUNNING",
6
+ "CONFLICT",
7
+ "UNCONFIRMED",
8
+ ]);
9
+ export class JobLockAuditError extends Error {
10
+ code;
11
+ constructor(code, message) {
12
+ super(message);
13
+ this.name = "JobLockAuditError";
14
+ this.code = code;
15
+ }
16
+ }
17
+ /** Records the kSQL-Flow result only. This module has no Job lock mutation dependency. */
18
+ export async function recordJobUnlock(input) {
19
+ requireText("runId", input.runId);
20
+ requireText("nodeId", input.nodeId);
21
+ requireText("reason", input.reason);
22
+ requireText("evidenceRef", input.evidenceRef);
23
+ requireText("servicePrincipal", input.servicePrincipal);
24
+ requireText("requestedBy", input.requestedBy);
25
+ requireText("stopConfirmedBy", input.stopConfirmedBy);
26
+ const result = parseLockRecoveryResult(input.result);
27
+ const run = await input.repository.getRun(input.runId);
28
+ const states = (await input.repository.getNodeStates(input.runId)).filter(({ value }) => value.node_id === input.nodeId);
29
+ if (states.length !== 1) {
30
+ throw new JobLockAuditError("NODE_STATE_NOT_UNIQUE", `expected exactly one Node State for ${input.runId}/${input.nodeId}, found ${states.length}`);
31
+ }
32
+ const state = states[0].value;
33
+ const expectedJobKey = `${run.value.resolved_profile_snapshot.profile}:${state.job_id}`;
34
+ if (result.jobKey !== expectedJobKey) {
35
+ throw new JobLockAuditError("JOB_KEY_MISMATCH", `LOCK_RECOVERY_RESULT jobKey ${result.jobKey} does not match ${expectedJobKey}`);
36
+ }
37
+ const audit = {
38
+ event_id: `job_unlock_${(input.uuid ?? randomUUID)()}`,
39
+ event_type: "JOB_LOCK_FORCE_UNLOCK_RECORDED",
40
+ run_id: input.runId,
41
+ node_id: input.nodeId,
42
+ job_id: state.job_id,
43
+ service_principal: input.servicePrincipal.trim(),
44
+ requested_by: input.requestedBy.trim(),
45
+ stop_confirmed_by: input.stopConfirmedBy.trim(),
46
+ reason: input.reason.trim(),
47
+ evidence_ref: input.evidenceRef.trim(),
48
+ recorded_at: (input.now ?? (() => new Date()))().toISOString(),
49
+ lock_recovery_result: result,
50
+ };
51
+ await input.repository.appendOperationAudit(audit);
52
+ return audit;
53
+ }
54
+ export function parseLockRecoveryResult(value) {
55
+ const record = object(value, "LOCK_RECOVERY_RESULT must be a JSON object");
56
+ if (record.kind !== "LOCK_RECOVERY_RESULT" || record.formatVersion !== 1) {
57
+ throw new JobLockAuditError("INVALID_LOCK_RECOVERY_RESULT", "kind must be LOCK_RECOVERY_RESULT and formatVersion must be 1");
58
+ }
59
+ const jobKey = requiredString(record.jobKey, "jobKey");
60
+ const outcome = requiredString(record.outcome, "outcome");
61
+ if (!OUTCOMES.has(outcome)) {
62
+ throw new JobLockAuditError("INVALID_LOCK_RECOVERY_RESULT", `unsupported outcome ${outcome}`);
63
+ }
64
+ const recordId = nullableString(record.recordId, "recordId");
65
+ const executedAt = requiredTimestamp(record.executedAt, "executedAt");
66
+ let before = null;
67
+ if (record.before !== null) {
68
+ const beforeValue = object(record.before, "before must be an object or null");
69
+ before = {
70
+ batchId: requiredString(beforeValue.batchId, "before.batchId"),
71
+ startedAt: requiredTimestamp(beforeValue.startedAt, "before.startedAt"),
72
+ host: requiredString(beforeValue.host, "before.host"),
73
+ };
74
+ }
75
+ if (outcome === "NOT_FOUND" && (recordId !== null || before !== null)) {
76
+ throw new JobLockAuditError("INVALID_LOCK_RECOVERY_RESULT", "NOT_FOUND requires null recordId and before");
77
+ }
78
+ if ((outcome === "RELEASED" ||
79
+ outcome === "NOT_RUNNING" ||
80
+ outcome === "CONFLICT") &&
81
+ (recordId === null || before === null)) {
82
+ throw new JobLockAuditError("INVALID_LOCK_RECOVERY_RESULT", `${outcome} requires recordId and before`);
83
+ }
84
+ const nextAction = record.nextAction === undefined
85
+ ? undefined
86
+ : requiredString(record.nextAction, "nextAction");
87
+ return {
88
+ kind: "LOCK_RECOVERY_RESULT",
89
+ formatVersion: 1,
90
+ jobKey,
91
+ recordId,
92
+ outcome: outcome,
93
+ before,
94
+ executedAt,
95
+ ...(nextAction === undefined ? {} : { nextAction }),
96
+ };
97
+ }
98
+ function requireText(name, value) {
99
+ if (value.trim() === "")
100
+ throw new JobLockAuditError("AUDIT_INPUT_REQUIRED", `${name} is required`);
101
+ }
102
+ function object(value, message) {
103
+ if (typeof value !== "object" || value === null || Array.isArray(value))
104
+ throw new JobLockAuditError("INVALID_LOCK_RECOVERY_RESULT", message);
105
+ return value;
106
+ }
107
+ function requiredString(value, name) {
108
+ if (typeof value !== "string" || value.trim() === "")
109
+ throw new JobLockAuditError("INVALID_LOCK_RECOVERY_RESULT", `${name} must be a non-empty string`);
110
+ return value;
111
+ }
112
+ function nullableString(value, name) {
113
+ if (value === null)
114
+ return null;
115
+ return requiredString(value, name);
116
+ }
117
+ function requiredTimestamp(value, name) {
118
+ const timestamp = requiredString(value, name);
119
+ if (!Number.isFinite(Date.parse(timestamp)))
120
+ throw new JobLockAuditError("INVALID_LOCK_RECOVERY_RESULT", `${name} must be an ISO timestamp`);
121
+ return timestamp;
122
+ }
123
+ //# sourceMappingURL=job-lock-audit.js.map
@@ -0,0 +1,39 @@
1
+ import type { NodeAttempt, NodeAttemptStatus } from "../domain/persistence-model.js";
2
+ import type { JobLogAttemptResult, JobLogReader } from "../executor/job-log-reader.js";
3
+ import type { PersistenceRepository } from "../persistence/repository.js";
4
+ import type { Versioned } from "../persistence/repository.js";
5
+ export interface OrphanAttemptAdjudicationInput {
6
+ readonly runId: string;
7
+ readonly invocationId: string;
8
+ readonly repository: PersistenceRepository;
9
+ readonly jobLogReader: Pick<JobLogReader, "findAttemptResult">;
10
+ readonly confirmWrite: () => Promise<boolean>;
11
+ readonly now?: () => string;
12
+ readonly attempts?: readonly Versioned<NodeAttempt>[];
13
+ }
14
+ export interface OrphanAttemptAdjudication {
15
+ readonly attemptId: string;
16
+ readonly nodeId: string;
17
+ readonly status: Exclude<NodeAttemptStatus, "RUNNING">;
18
+ readonly resultCode: string;
19
+ }
20
+ export interface AbandonedInvocationFinalization {
21
+ readonly invocationId: string;
22
+ readonly previousStatus: string;
23
+ readonly auditEventId: string;
24
+ }
25
+ interface TerminalDecision {
26
+ readonly status: "SUCCESS" | "FAILED" | "CANCELLED" | "UNKNOWN";
27
+ readonly resultCode: string;
28
+ }
29
+ /**
30
+ * Resolves RUNNING attempts owned by an older invocation before resume selects
31
+ * nodes. Job-log transport/schema failures deliberately escape (fail-closed).
32
+ */
33
+ export declare function adjudicateOrphanRunningAttempts(input: OrphanAttemptAdjudicationInput): Promise<readonly OrphanAttemptAdjudication[]>;
34
+ /** Finalizes invocations made unable to continue by the current lock owner. */
35
+ export declare function finalizeAbandonedInvocations(input: Pick<OrphanAttemptAdjudicationInput, "runId" | "invocationId" | "repository" | "confirmWrite" | "now">): Promise<readonly AbandonedInvocationFinalization[]>;
36
+ export declare function decideOrphanResult(jobLog: JobLogAttemptResult | null): TerminalDecision;
37
+ export declare function orphanAdjudicationReason(adjudications: readonly OrphanAttemptAdjudication[]): string | undefined;
38
+ export {};
39
+ //# sourceMappingURL=orphan-attempt-adjudication.d.ts.map
@@ -0,0 +1,167 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { NO_EXECUTION_RESULT } from "../executor/result-classifier.js";
3
+ /**
4
+ * Resolves RUNNING attempts owned by an older invocation before resume selects
5
+ * nodes. Job-log transport/schema failures deliberately escape (fail-closed).
6
+ */
7
+ export async function adjudicateOrphanRunningAttempts(input) {
8
+ const [states, attempts] = await Promise.all([
9
+ input.repository.getNodeStates(input.runId),
10
+ input.attempts ?? input.repository.getAttempts(input.runId),
11
+ ]);
12
+ const attemptsById = new Map(attempts.map((attempt) => [attempt.value.node_attempt_id, attempt]));
13
+ const adjudications = [];
14
+ const now = input.now ?? (() => new Date().toISOString());
15
+ const candidates = [];
16
+ for (const state of states) {
17
+ if (state.value.status !== "RUNNING" ||
18
+ state.value.active_attempt_id === null)
19
+ continue;
20
+ const attempt = attemptsById.get(state.value.active_attempt_id);
21
+ if (attempt === undefined)
22
+ throw new Error(`RUNNING Node State '${state.value.node_id}' has no active Attempt '${state.value.active_attempt_id}'`);
23
+ if (attempt.value.status !== "RUNNING" ||
24
+ attempt.value.invocation_id === input.invocationId)
25
+ continue;
26
+ candidates.push({ state, attempt });
27
+ }
28
+ // Complete every read before the first write. A transport/schema failure
29
+ // therefore stops this adjudication pass without guessing any outcome.
30
+ const evidence = await Promise.all(candidates.map(async ({ state, attempt }) => ({
31
+ state,
32
+ attempt,
33
+ jobLog: await input.jobLogReader.findAttemptResult(attempt.value.node_attempt_id),
34
+ })));
35
+ for (const { state, attempt, jobLog } of evidence) {
36
+ const decision = decideOrphanResult(jobLog);
37
+ const finishedAt = jobLog?.finishedAt ?? now();
38
+ if (!(await input.confirmWrite()))
39
+ throw leaseInterrupted();
40
+ await input.repository.finalizeAttempt(attempt.value.node_attempt_id, attempt.revision, {
41
+ status: decision.status,
42
+ result_code: decision.resultCode,
43
+ // D-11 preserves the Attempt's durable start marker. The JOB log is
44
+ // evidence for adjudication, not a surrogate write to this marker.
45
+ runner_execution_started_at: attempt.value.runner_execution_started_at,
46
+ execution_id: attempt.value.execution_id ?? jobLog?.executionId ?? null,
47
+ finished_at: finishedAt,
48
+ duration_sec: attempt.value.duration_sec,
49
+ error_message: decision.status === "UNKNOWN"
50
+ ? "orphan RUNNING attempt had no terminal execution result"
51
+ : attempt.value.error_message,
52
+ read_count: attempt.value.read_count,
53
+ written_count: attempt.value.written_count,
54
+ last_successful_chunk_no: attempt.value.last_successful_chunk_no,
55
+ last_written_key: attempt.value.last_written_key,
56
+ });
57
+ if (!(await input.confirmWrite()))
58
+ throw leaseInterrupted();
59
+ await input.repository.upsertNodeState({
60
+ expected_revision: state.revision,
61
+ value: terminalState(state.value, decision.status, decision.resultCode, finishedAt),
62
+ });
63
+ adjudications.push({
64
+ attemptId: attempt.value.node_attempt_id,
65
+ nodeId: state.value.node_id,
66
+ status: decision.status,
67
+ resultCode: decision.resultCode,
68
+ });
69
+ }
70
+ return adjudications;
71
+ }
72
+ /** Finalizes invocations made unable to continue by the current lock owner. */
73
+ export async function finalizeAbandonedInvocations(input) {
74
+ const invocations = await input.repository.getInvocations(input.runId);
75
+ const now = input.now ?? (() => new Date().toISOString());
76
+ const results = [];
77
+ for (const invocation of invocations) {
78
+ const previousStatus = invocation.value.status;
79
+ if (invocation.value.invocation_id === input.invocationId ||
80
+ (previousStatus !== "RUNNING" && previousStatus !== "CREATED"))
81
+ continue;
82
+ if (!(await input.confirmWrite()))
83
+ throw leaseInterrupted();
84
+ const finishedAt = now();
85
+ const finalized = await input.repository.finalizeInvocation(invocation.value.invocation_id, invocation.revision, {
86
+ status: "CANCELLED",
87
+ result_code: "NETWORK_LEASE_INTERRUPTED",
88
+ finished_at: finishedAt,
89
+ reason: `${invocation.value.reason}; finalized as an abandoned invocation during reconciliation`,
90
+ });
91
+ if (!(await input.confirmWrite()))
92
+ throw leaseInterrupted();
93
+ const auditEventId = randomUUID();
94
+ const audit = {
95
+ event_id: auditEventId,
96
+ event_type: "RECONCILIATION_REPAIR",
97
+ repair_type: "INVOCATION_FINALIZED",
98
+ run_id: input.runId,
99
+ target_type: "RUN_INVOCATION",
100
+ target_id: invocation.value.invocation_id,
101
+ before: {
102
+ status: previousStatus,
103
+ result_code: invocation.value.result_code,
104
+ revision: invocation.revision,
105
+ },
106
+ after: {
107
+ status: finalized.value.status,
108
+ result_code: finalized.value.result_code,
109
+ revision: finalized.revision,
110
+ },
111
+ basis: "the current Network lock owner makes an older unterminated invocation unable to continue",
112
+ occurred_at: finishedAt,
113
+ };
114
+ await input.repository.appendOperationAudit(audit);
115
+ results.push({
116
+ invocationId: invocation.value.invocation_id,
117
+ previousStatus,
118
+ auditEventId,
119
+ });
120
+ }
121
+ return results;
122
+ }
123
+ export function decideOrphanResult(jobLog) {
124
+ if (jobLog === null)
125
+ return unknownDecision();
126
+ switch (jobLog.status.toUpperCase()) {
127
+ case "SUCCESS":
128
+ return { status: "SUCCESS", resultCode: "OK" };
129
+ case "NO_DATA":
130
+ return { status: "SUCCESS", resultCode: "NO_DATA" };
131
+ case "FAILED":
132
+ return { status: "FAILED", resultCode: "FAILED" };
133
+ case "ABORTED":
134
+ return { status: "FAILED", resultCode: "ASSERT_FAILED" };
135
+ case "TIMEOUT":
136
+ return { status: "FAILED", resultCode: "EXECUTION_TIMEOUT" };
137
+ case "CANCELLED":
138
+ return { status: "CANCELLED", resultCode: "CANCELLED" };
139
+ case "RUNNING":
140
+ default:
141
+ return unknownDecision();
142
+ }
143
+ }
144
+ export function orphanAdjudicationReason(adjudications) {
145
+ if (adjudications.length === 0)
146
+ return undefined;
147
+ return `orphan attempts adjudicated: ${adjudications
148
+ .map((item) => `${item.attemptId}=${item.status}(${item.resultCode})`)
149
+ .join(", ")}`;
150
+ }
151
+ function unknownDecision() {
152
+ return { status: "UNKNOWN", resultCode: NO_EXECUTION_RESULT };
153
+ }
154
+ function terminalState(state, status, resultCode, finishedAt) {
155
+ return {
156
+ ...state,
157
+ status,
158
+ active_attempt_id: null,
159
+ status_reason: resultCode,
160
+ finished_at: finishedAt,
161
+ updated_at: finishedAt,
162
+ };
163
+ }
164
+ function leaseInterrupted() {
165
+ return Object.assign(new Error("network lease was interrupted during orphan adjudication"), { code: "NETWORK_LEASE_INTERRUPTED" });
166
+ }
167
+ //# sourceMappingURL=orphan-attempt-adjudication.js.map
@@ -0,0 +1,41 @@
1
+ import type { ReconciliationOperationAudit } from "../domain/persistence-model.js";
2
+ import type { PersistenceRepository, StatusReadRepository } from "../persistence/repository.js";
3
+ export interface ReconciliationRepair {
4
+ type: ReconciliationOperationAudit["repair_type"];
5
+ targetType: ReconciliationOperationAudit["target_type"];
6
+ targetId: string;
7
+ before: Record<string, unknown>;
8
+ after: Record<string, unknown>;
9
+ basis: string;
10
+ auditEventId: string;
11
+ }
12
+ export interface ReconciliationInconsistency {
13
+ code: "ACTIVE_ATTEMPT_ID_MISSING" | "ACTIVE_ATTEMPT_NOT_FOUND" | "ATTEMPT_NODE_STATE_MISSING" | "STATE_TERMINAL_ATTEMPT_RUNNING" | "STATE_ATTEMPT_STATUS_MISMATCH" | "MULTIPLE_RUNNING_ATTEMPTS" | "REVISION_SERIES_INVERTED" | "REVISION_SERIES_UNDETERMINED" | "RESOLUTION_ATTEMPT_NOT_UNKNOWN" | "RESOLUTION_NOT_CURRENT_ATTEMPT" | "RESOLUTION_STATE_NOT_UNKNOWN" | "CONFLICTING_ATTEMPT_RESOLUTIONS";
14
+ nodeId: string;
15
+ detail: string;
16
+ attemptIds?: string[];
17
+ }
18
+ export interface ReconciliationResult {
19
+ repaired: ReconciliationRepair[];
20
+ inconsistencies: ReconciliationInconsistency[];
21
+ aggregateUpdated: boolean;
22
+ }
23
+ export interface ReconciliationDetection {
24
+ readonly inconsistencies: readonly ReconciliationDetectionItem[];
25
+ }
26
+ export interface ReconciliationDetectionItem {
27
+ readonly code: ReconciliationInconsistency["code"] | "TERMINAL_ATTEMPT_PENDING_APPLY" | "ATTEMPT_RESOLUTION_PENDING_APPLY" | "RUN_AGGREGATE_MISMATCH";
28
+ readonly nodeId?: string;
29
+ readonly detail: string;
30
+ readonly attemptIds?: readonly string[];
31
+ }
32
+ export declare class ReconciliationRequiredError extends Error {
33
+ readonly code = "RECONCILIATION_REQUIRED";
34
+ readonly result: ReconciliationResult;
35
+ constructor(result: ReconciliationResult);
36
+ }
37
+ /** Runs the D-09 inspections without exposing or invoking any repair method. */
38
+ export declare function detectReconciliation(repository: StatusReadRepository, runId: string): Promise<ReconciliationDetection>;
39
+ /** D-09 pre-DAG gate. Throws RECONCILIATION_REQUIRED after retaining safe repairs. */
40
+ export declare function reconcileRun(repository: PersistenceRepository, runId: string): Promise<ReconciliationResult>;
41
+ //# sourceMappingURL=reconciliation.d.ts.map