@sentropic/artifacts 0.1.3 → 0.2.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.
- package/README.md +74 -4
- package/dist/adapters/postgres-commit.d.ts +15 -0
- package/dist/adapters/postgres-commit.js +80 -0
- package/dist/adapters/postgres.d.ts +5 -2
- package/dist/adapters/postgres.js +39 -0
- package/dist/content/handler.js +79 -63
- package/dist/http.d.ts +42 -29
- package/dist/http.js +168 -491
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mcp-core.d.ts +52 -0
- package/dist/mcp-core.js +111 -0
- package/dist/mcp.d.ts +4 -60
- package/dist/mcp.js +7 -276
- package/dist/profile/contracts.d.ts +67 -0
- package/dist/profile/contracts.js +31 -0
- package/dist/profile/index.d.ts +6 -0
- package/dist/profile/index.js +117 -0
- package/dist/profile/json.d.ts +10 -0
- package/dist/profile/json.js +76 -0
- package/dist/profile/radar.d.ts +3 -0
- package/dist/profile/radar.js +21 -0
- package/dist/profile/schemas.d.ts +6 -0
- package/dist/profile/schemas.js +76 -0
- package/dist/profile/validation.d.ts +5 -0
- package/dist/profile/validation.js +104 -0
- package/dist/profile/validators.d.ts +3 -0
- package/dist/profile/validators.js +51 -0
- package/dist/server/authorized-replay.d.ts +33 -0
- package/dist/server/authorized-replay.js +31 -0
- package/dist/server/command-identity.d.ts +52 -0
- package/dist/server/command-identity.js +71 -0
- package/dist/server/commit/brands.d.ts +7 -0
- package/dist/server/commit/brands.js +3 -0
- package/dist/server/commit/coordinator.d.ts +2 -0
- package/dist/server/commit/coordinator.js +729 -0
- package/dist/server/commit/types.d.ts +379 -0
- package/dist/server/commit/types.js +94 -0
- package/dist/server/commit-contract.d.ts +4 -0
- package/dist/server/commit-contract.js +2 -0
- package/dist/server/failures.d.ts +84 -0
- package/dist/server/failures.js +133 -0
- package/dist/server/gc.d.ts +42 -1
- package/dist/server/gc.js +168 -5
- package/dist/server/idempotency.d.ts +4 -0
- package/dist/server/idempotency.js +14 -0
- package/dist/server/lifecycle.d.ts +2 -1
- package/dist/server/lifecycle.js +15 -1
- package/dist/server/object-intents.d.ts +14 -1
- package/dist/server/object-intents.js +31 -0
- package/dist/server/outcome-contract.d.ts +3 -0
- package/dist/server/outcome-contract.js +3 -0
- package/dist/server/outcome-projection.d.ts +21 -0
- package/dist/server/outcome-projection.js +29 -0
- package/dist/server/preparation-cleanup.d.ts +31 -0
- package/dist/server/preparation-cleanup.js +106 -0
- package/dist/server/publication-limits.d.ts +10 -0
- package/dist/server/publication-limits.js +38 -0
- package/dist/server/publication.d.ts +85 -1
- package/dist/server/publication.js +288 -31
- package/dist/server/service.d.ts +64 -1
- package/dist/server/service.js +187 -1
- package/dist/server/uploads.d.ts +4 -1
- package/dist/server/uploads.js +63 -35
- package/dist/testing.d.ts +50 -0
- package/dist/testing.js +86 -0
- package/docs/migration-l6.md +129 -0
- package/examples/radar-commit-setup.ts +299 -0
- package/l0-freeze/command-identity-contract.ts +303 -0
- package/l0-freeze/commit-contract.ts +421 -0
- package/l0-freeze/failure-contract.ts +174 -0
- package/l0-freeze/profile-contract.ts +219 -0
- package/migrations/0003_coordinator.sql +28 -0
- package/package.json +31 -1
- package/src/contracts.ts +125 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L0 frozen commit declaration (immo #861, ask 2 shape, spec r2 section 3.2).
|
|
3
|
+
*
|
|
4
|
+
* Frozen server-side public exports for the Radar acceptance target:
|
|
5
|
+
* request-bound invocation context, commit context, typed authorization
|
|
6
|
+
* decision with an opaque fence, opaque commit transaction and the
|
|
7
|
+
* coordinator role. Barrier points, lane lock order and app signatures
|
|
8
|
+
* follow in later slices. Runtime implementation lands in L3.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
CommandIdentity,
|
|
13
|
+
COMMAND_CODEC_VERSION,
|
|
14
|
+
EffectiveArtifactCommand,
|
|
15
|
+
InternalIdempotencyKey,
|
|
16
|
+
} from './command-identity-contract.js';
|
|
17
|
+
import type { ArtifactFailure, ArtifactOutcome, OrdinaryFailure } from './failure-contract.js';
|
|
18
|
+
import type { CompiledArtifactsProfile, CompiledValidators } from './profile-contract.js';
|
|
19
|
+
|
|
20
|
+
/** Version of the frozen commit-coordinator protocol. */
|
|
21
|
+
export const COMMIT_PROTOCOL_VERSION = 'artifacts-commit/v1' as const;
|
|
22
|
+
|
|
23
|
+
/** Outcome of a COMMIT acknowledgment wait. */
|
|
24
|
+
export const COMMIT_STATES = ['not_committed', 'committed', 'unknown'] as const;
|
|
25
|
+
|
|
26
|
+
export type CommitState = (typeof COMMIT_STATES)[number];
|
|
27
|
+
|
|
28
|
+
/** Purpose of one guard invocation: final decision or replayed receipt. */
|
|
29
|
+
export const COMMIT_PURPOSES = ['finalize', 'replay'] as const;
|
|
30
|
+
|
|
31
|
+
export type CommitPurpose = (typeof COMMIT_PURPOSES)[number];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Guard callback supplied by the trusted host through server context.
|
|
35
|
+
* The provider invokes it after lock, pool-connection and object waits,
|
|
36
|
+
* inside the finalization boundary, before committing success. No public
|
|
37
|
+
* caller supplies SQL or a connection through this seam.
|
|
38
|
+
*/
|
|
39
|
+
export type AuthorizeCommit = (
|
|
40
|
+
context: ArtifactCommitContext,
|
|
41
|
+
transaction: ArtifactCommitTransaction,
|
|
42
|
+
) => Promise<ArtifactAuthorizationDecision>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Frozen server-created invocation context. Neither body fields nor
|
|
46
|
+
* mutable app-global state may supply it; every publish/ensure invocation
|
|
47
|
+
* carries a fresh context binding the verified actor, the trusted storage
|
|
48
|
+
* scope and the request-bound guard.
|
|
49
|
+
*/
|
|
50
|
+
export interface ArtifactInvocationContext {
|
|
51
|
+
readonly invocationId: string;
|
|
52
|
+
readonly actorId: string;
|
|
53
|
+
readonly storageScopeId: string;
|
|
54
|
+
readonly authorizeCommit: AuthorizeCommit;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Commit-time context passed to the guard after all waits. It adds the
|
|
59
|
+
* operation, artifact/binding identity, computed command identity, policy
|
|
60
|
+
* revision and purpose to the invocation. The fence below, minted by the
|
|
61
|
+
* transaction-bound policy participant, is the only usable allowed
|
|
62
|
+
* credential; it cannot be synthesized from the revision string.
|
|
63
|
+
*/
|
|
64
|
+
export interface ArtifactCommitContext {
|
|
65
|
+
readonly invocationId: string;
|
|
66
|
+
readonly actorId: string;
|
|
67
|
+
readonly storageScopeId: string;
|
|
68
|
+
readonly operation: string;
|
|
69
|
+
readonly artifactId: string;
|
|
70
|
+
readonly bindingId: string;
|
|
71
|
+
readonly commandIdentity: CommandIdentity;
|
|
72
|
+
readonly policyRevision: string;
|
|
73
|
+
readonly purpose: CommitPurpose;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
declare const commitFenceBrand: unique symbol;
|
|
77
|
+
|
|
78
|
+
/** Opaque fence minted by the transaction-bound policy participant. */
|
|
79
|
+
export interface ArtifactAuthorizationFence {
|
|
80
|
+
readonly [commitFenceBrand]: true;
|
|
81
|
+
readonly fenceId: string;
|
|
82
|
+
readonly invocationId: string;
|
|
83
|
+
readonly policyRevision: string;
|
|
84
|
+
readonly transactionId: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Denial reasons for a refused finalization. */
|
|
88
|
+
export type AuthorizationDenialReason =
|
|
89
|
+
| 'denied' | 'revoked' | 'expired' | 'invalid_object' | 'unavailable';
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Typed authorization decision. Never an unqualified boolean: an allowed
|
|
93
|
+
* decision carries a usable guarded fence, a denial carries a typed
|
|
94
|
+
* reason plus the public projection (code, status, retry). Entry rejection
|
|
95
|
+
* stays distinguishable from a refusal raised inside finalization.
|
|
96
|
+
*/
|
|
97
|
+
export type ArtifactAuthorizationDecision =
|
|
98
|
+
| { readonly allowed: true; readonly fence: ArtifactAuthorizationFence }
|
|
99
|
+
| {
|
|
100
|
+
readonly allowed: false;
|
|
101
|
+
readonly reason: AuthorizationDenialReason;
|
|
102
|
+
readonly publicCode: string;
|
|
103
|
+
readonly status: number;
|
|
104
|
+
readonly retriable: boolean;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
declare const commitTransactionBrand: unique symbol;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Opaque transaction identity plus the host's registered
|
|
111
|
+
* transaction-bound policy/quota participant. It exposes no
|
|
112
|
+
* commit/rollback/release method to the guard. Policy registration and
|
|
113
|
+
* SQL table mapping are fixed at coordinator construction.
|
|
114
|
+
*/
|
|
115
|
+
export interface ArtifactCommitTransaction {
|
|
116
|
+
readonly [commitTransactionBrand]: true;
|
|
117
|
+
readonly transactionId: string;
|
|
118
|
+
readonly invocationId: string;
|
|
119
|
+
readonly policyRevision: string;
|
|
120
|
+
readonly participant: ArtifactCommitParticipant;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Transaction-bound policy/quota participant registered at coordinator
|
|
125
|
+
* construction. The guard reaches policy and quota reads only through
|
|
126
|
+
* this participant on its leased transaction; public tool callers never
|
|
127
|
+
* receive a connection or SQL capability.
|
|
128
|
+
*/
|
|
129
|
+
export interface ArtifactCommitParticipant {
|
|
130
|
+
readonly policyRevision: string;
|
|
131
|
+
readAdmission(input: AdmissionReadInput): Promise<AdmissionReadResult>;
|
|
132
|
+
reserveQuota(input: QuotaReserveInput): Promise<QuotaReserveResult>;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Policy read scoped to one guarded finalization attempt. */
|
|
136
|
+
export interface AdmissionReadInput {
|
|
137
|
+
readonly operation: string;
|
|
138
|
+
readonly artifactId: string;
|
|
139
|
+
readonly bindingId: string;
|
|
140
|
+
readonly actorId: string;
|
|
141
|
+
readonly policyRevision: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Policy read result; denial carries the typed refusal reason. */
|
|
145
|
+
export interface AdmissionReadResult {
|
|
146
|
+
readonly allowed: boolean;
|
|
147
|
+
readonly reason: AuthorizationDenialReason;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Quota reservation scoped to one guarded finalization attempt. */
|
|
151
|
+
export interface QuotaReserveInput {
|
|
152
|
+
readonly storageScopeId: string;
|
|
153
|
+
readonly operation: string;
|
|
154
|
+
readonly bytes: number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Quota reservation result for one guarded finalization attempt. */
|
|
158
|
+
export interface QuotaReserveResult {
|
|
159
|
+
readonly reserved: boolean;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Coordinator role owning the atomic finalization unit. The optional
|
|
164
|
+
* test observer receives barrier arrivals below the app with invocation
|
|
165
|
+
* correlation; it can never mark an operation authorized or successful.
|
|
166
|
+
* `finalizeOperation` owns the complete unit (key lock, participant
|
|
167
|
+
* locks, guard, commit/rollback); `replayOperation` re-reads the winner
|
|
168
|
+
* under the key lock and returns it through a fresh guarded replay.
|
|
169
|
+
*/
|
|
170
|
+
export interface ArtifactCommitCoordinator {
|
|
171
|
+
readonly protocolVersion: typeof COMMIT_PROTOCOL_VERSION;
|
|
172
|
+
readonly guardedOperations: readonly string[];
|
|
173
|
+
readonly testObserver?: CommitBarrierObserver;
|
|
174
|
+
finalizeOperation(
|
|
175
|
+
input: FinalizationInput,
|
|
176
|
+
): Promise<ArtifactOutcome<FinalizationRecord>>;
|
|
177
|
+
replayOperation(
|
|
178
|
+
input: ReplayInput,
|
|
179
|
+
): Promise<ArtifactOutcome<FinalizationRecord>>;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Coordinator completion record for one finalized or replayed operation. */
|
|
183
|
+
export interface FinalizationRecord {
|
|
184
|
+
readonly internalKey: InternalIdempotencyKey;
|
|
185
|
+
readonly commandHash: string;
|
|
186
|
+
readonly codecVersion: typeof COMMAND_CODEC_VERSION;
|
|
187
|
+
readonly replayed: boolean;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Finalization input: request, invocation context and effective command. */
|
|
191
|
+
export interface FinalizationInput {
|
|
192
|
+
readonly request: HttpRequest;
|
|
193
|
+
readonly invocation: ArtifactInvocationContext;
|
|
194
|
+
readonly command: EffectiveArtifactCommand;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Guarded replay input: invocation context and the durable internal key. */
|
|
198
|
+
export interface ReplayInput {
|
|
199
|
+
readonly invocation: ArtifactInvocationContext;
|
|
200
|
+
readonly internalKey: InternalIdempotencyKey;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Nine section 3.2 barrier points. Tests of actual contention hold the
|
|
205
|
+
* exact coordination/policy/quota/pointer row from a second raw client at
|
|
206
|
+
* these points; the coordinator rechecks authority after a resumed
|
|
207
|
+
* barrier. S3 preparation precedes the key-lock barrier.
|
|
208
|
+
*/
|
|
209
|
+
export const COMMIT_BARRIER_POINTS = [
|
|
210
|
+
'before_connection_acquire',
|
|
211
|
+
'before_key_lock',
|
|
212
|
+
'before_blob_put',
|
|
213
|
+
'after_blob_put_before_verify',
|
|
214
|
+
'before_blob_readback',
|
|
215
|
+
'before_finalization_row_lock',
|
|
216
|
+
'after_finalization_locks_before_guard',
|
|
217
|
+
'after_replay_lookup_before_guard',
|
|
218
|
+
'before_commit',
|
|
219
|
+
] as const;
|
|
220
|
+
|
|
221
|
+
export type CommitBarrierPoint = (typeof COMMIT_BARRIER_POINTS)[number];
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Test-only barrier seam. Bound once as the coordinator `testObserver`
|
|
225
|
+
* option; `waitUntilReached` resolves after arrival and `release`
|
|
226
|
+
* resumes only that invocation/point.
|
|
227
|
+
*/
|
|
228
|
+
export interface CommitBarrierObserver {
|
|
229
|
+
arrive(point: CommitBarrierPoint, invocationId: string): void;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export interface ArtifactCommitBarrier {
|
|
233
|
+
readonly observer: CommitBarrierObserver;
|
|
234
|
+
waitUntilReached(point: CommitBarrierPoint, invocationId: string): Promise<void>;
|
|
235
|
+
release(point: CommitBarrierPoint, invocationId: string): void;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export type CreateArtifactCommitBarrier = () => ArtifactCommitBarrier;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A4 participant lock order inside the finalization transaction, after
|
|
242
|
+
* the key lock: binding identity, artifact/lifecycle row, ACL row,
|
|
243
|
+
* current-pointer row, host authority/revision rows, quota rows, then
|
|
244
|
+
* blob-reference/deletion-claim rows. Multiple identities within one
|
|
245
|
+
* class lock in stable bytewise order. Artifact/lifecycle, ACL, pointer
|
|
246
|
+
* and quota rows use FOR UPDATE; authority rows use FOR SHARE. Binding
|
|
247
|
+
* uniqueness uses its own coordination row even when caller keys differ.
|
|
248
|
+
*/
|
|
249
|
+
export const PARTICIPANT_LOCK_ORDER = [
|
|
250
|
+
'binding-identity',
|
|
251
|
+
'artifact-lifecycle',
|
|
252
|
+
'acl',
|
|
253
|
+
'current-pointer',
|
|
254
|
+
'authority-revision',
|
|
255
|
+
'quota',
|
|
256
|
+
'blob-reference-deletion-claim',
|
|
257
|
+
] as const;
|
|
258
|
+
|
|
259
|
+
export type ParticipantLockClass = (typeof PARTICIPANT_LOCK_ORDER)[number];
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Frozen finalization order (ask 2, protocol A4). The host guard runs
|
|
263
|
+
* after lock, connection and object waits, inside the finalization
|
|
264
|
+
* boundary, before any committed success.
|
|
265
|
+
*/
|
|
266
|
+
export const FINALIZATION_PHASES = [
|
|
267
|
+
'admit', 'prepare', 'lock', 'provision', 'guard', 'commit',
|
|
268
|
+
] as const;
|
|
269
|
+
|
|
270
|
+
export type FinalizationPhase = (typeof FINALIZATION_PHASES)[number];
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Residue a refused finalization must never leave committed: no new
|
|
274
|
+
* ready release, pointer advance, attached provenance, ACL/binding or
|
|
275
|
+
* lifecycle change, consumed quota or replayable success.
|
|
276
|
+
*/
|
|
277
|
+
export const REFUSAL_RESIDUE_FORBIDDEN = [
|
|
278
|
+
'ready-release', 'pointer-advance', 'attached-provenance',
|
|
279
|
+
'acl-binding-change', 'lifecycle-change', 'consumed-quota', 'replayable-success',
|
|
280
|
+
] as const;
|
|
281
|
+
|
|
282
|
+
export type RefusalResidue = (typeof REFUSAL_RESIDUE_FORBIDDEN)[number];
|
|
283
|
+
|
|
284
|
+
/** Profiles requiring guarded writes must declare a coordinator at startup. */
|
|
285
|
+
export interface CoordinatorRequirement {
|
|
286
|
+
readonly requiresCoordinator: true;
|
|
287
|
+
readonly guardedOperations: readonly string[];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Transport-free HTTP request shape consumed by the frozen app. */
|
|
291
|
+
export interface HttpRequest {
|
|
292
|
+
readonly method: string;
|
|
293
|
+
readonly path: string;
|
|
294
|
+
readonly headers: Record<string, string>;
|
|
295
|
+
readonly body: unknown;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** Transport-free HTTP response shape produced by the frozen app. */
|
|
299
|
+
export interface HttpResponse {
|
|
300
|
+
readonly status: number;
|
|
301
|
+
readonly headers: Record<string, string>;
|
|
302
|
+
readonly body: unknown;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Required app dependencies: no automatic legacy fallback exists. */
|
|
306
|
+
export const HTTP_APP_REQUIRED_DEPS = ['profile', 'coordinator'] as const;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* App dependencies. Omitted profile, coordinator or write guard fails
|
|
310
|
+
* closed; the old split finalizer is removed, never selected implicitly.
|
|
311
|
+
*/
|
|
312
|
+
export interface HttpAppDeps {
|
|
313
|
+
readonly profile: CompiledArtifactsProfile;
|
|
314
|
+
readonly coordinator: ArtifactCommitCoordinator;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Frozen app surface. `execute` is the sole execution path returning the
|
|
319
|
+
* shared outcome with invocation, commit-state and replay correlation;
|
|
320
|
+
* `handle` calls it once, then sanitizes with the outcome projector.
|
|
321
|
+
* Hosts needing accounting call `execute` and the projector themselves,
|
|
322
|
+
* never both methods. Reads/entry rejection use `not_committed`; write
|
|
323
|
+
* success uses `committed`.
|
|
324
|
+
*/
|
|
325
|
+
export interface ArtifactApp {
|
|
326
|
+
readonly selectedCapabilities: readonly string[];
|
|
327
|
+
execute(
|
|
328
|
+
request: HttpRequest,
|
|
329
|
+
invocation: ArtifactInvocationContext,
|
|
330
|
+
): Promise<ArtifactOutcome<HttpResponse>>;
|
|
331
|
+
handle(
|
|
332
|
+
request: HttpRequest,
|
|
333
|
+
invocation: ArtifactInvocationContext,
|
|
334
|
+
): Promise<HttpResponse>;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Sanitized projection from outcome to HTTP response. */
|
|
338
|
+
export type ProjectArtifactHttpOutcome = (
|
|
339
|
+
outcome: ArtifactOutcome<HttpResponse>,
|
|
340
|
+
) => HttpResponse;
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Compile-time freeze assertions (P1-1). Checked by `npm run freeze-types`;
|
|
344
|
+
* type-only and erased at runtime. Each `@ts-expect-error` below must stay
|
|
345
|
+
* consumed: if a forged literal ever compiles, the gate fails.
|
|
346
|
+
*/
|
|
347
|
+
|
|
348
|
+
// Forged fences/transactions never satisfy the opaque contracts.
|
|
349
|
+
const _forgedFence = {
|
|
350
|
+
fenceId: 'fence-1',
|
|
351
|
+
invocationId: 'inv-1',
|
|
352
|
+
policyRevision: 'rev-1',
|
|
353
|
+
transactionId: 'tx-1',
|
|
354
|
+
};
|
|
355
|
+
// @ts-expect-error — fences are minted by the transaction-bound participant
|
|
356
|
+
const _fenceForgery: ArtifactAuthorizationFence = _forgedFence;
|
|
357
|
+
|
|
358
|
+
const _forgedTransaction = {
|
|
359
|
+
transactionId: 'tx-1',
|
|
360
|
+
invocationId: 'inv-1',
|
|
361
|
+
policyRevision: 'rev-1',
|
|
362
|
+
};
|
|
363
|
+
// @ts-expect-error — transactions are opened by the coordinator
|
|
364
|
+
const _transactionForgery: ArtifactCommitTransaction = _forgedTransaction;
|
|
365
|
+
|
|
366
|
+
// The guard reaches policy/quota only through the registered participant.
|
|
367
|
+
type _ParticipantAccess = ArtifactCommitTransaction['participant'] extends ArtifactCommitParticipant
|
|
368
|
+
? true
|
|
369
|
+
: never;
|
|
370
|
+
const _participantAccess: _ParticipantAccess = true;
|
|
371
|
+
|
|
372
|
+
// The coordinator exposes callable finalization and replay operations.
|
|
373
|
+
type _FinalizeCallable = ArtifactCommitCoordinator['finalizeOperation'] extends (
|
|
374
|
+
input: FinalizationInput,
|
|
375
|
+
) => Promise<ArtifactOutcome<FinalizationRecord>>
|
|
376
|
+
? true
|
|
377
|
+
: never;
|
|
378
|
+
const _finalizeCallable: _FinalizeCallable = true;
|
|
379
|
+
|
|
380
|
+
type _ReplayCallable = ArtifactCommitCoordinator['replayOperation'] extends (
|
|
381
|
+
input: ReplayInput,
|
|
382
|
+
) => Promise<ArtifactOutcome<FinalizationRecord>>
|
|
383
|
+
? true
|
|
384
|
+
: never;
|
|
385
|
+
const _replayCallable: _ReplayCallable = true;
|
|
386
|
+
|
|
387
|
+
// Compiled profiles carry semantic validators for the lanes to consume.
|
|
388
|
+
type _ValidatorsWired = CompiledArtifactsProfile['validators'] extends CompiledValidators
|
|
389
|
+
? true
|
|
390
|
+
: never;
|
|
391
|
+
const _validatorsWired: _ValidatorsWired = true;
|
|
392
|
+
|
|
393
|
+
// Ordinary categories do not belong to the operational failure branch.
|
|
394
|
+
const _ordinaryFields = {
|
|
395
|
+
kind: 'operational',
|
|
396
|
+
category: 'invalid_request',
|
|
397
|
+
httpStatus: 400,
|
|
398
|
+
publicCode: 'invalid_request',
|
|
399
|
+
retriable: false,
|
|
400
|
+
invocationId: 'inv-1',
|
|
401
|
+
operation: 'publish',
|
|
402
|
+
phase: 'entry',
|
|
403
|
+
commitState: 'not_committed',
|
|
404
|
+
cause: undefined,
|
|
405
|
+
} as const;
|
|
406
|
+
// @ts-expect-error — 'invalid_request' is an ordinary category, not operational
|
|
407
|
+
const _ordinaryAsOperational: ArtifactFailure = _ordinaryFields;
|
|
408
|
+
|
|
409
|
+
const _ordinaryPositive: OrdinaryFailure = {
|
|
410
|
+
kind: 'ordinary',
|
|
411
|
+
category: 'invalid_request',
|
|
412
|
+
httpStatus: 400,
|
|
413
|
+
publicCode: 'invalid_request',
|
|
414
|
+
retriable: false,
|
|
415
|
+
invocationId: 'inv-1',
|
|
416
|
+
operation: 'publish',
|
|
417
|
+
phase: 'entry',
|
|
418
|
+
commitState: 'not_committed',
|
|
419
|
+
cause: undefined,
|
|
420
|
+
};
|
|
421
|
+
const _ordinaryInOutcome: ArtifactOutcome<string> = { ok: false, failure: _ordinaryPositive };
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L0 frozen failure declaration (immo #861, ask 3 shape, spec r2 E2/E3).
|
|
3
|
+
*
|
|
4
|
+
* Stable internal outcome shape and category-to-public projection table.
|
|
5
|
+
* `capability_unselected` is a distinct pre-dispatch non-retriable 503
|
|
6
|
+
* with no write-ledger entry; the guard authority outage shares the
|
|
7
|
+
* commit category and ledger reason with policy denial but projects a
|
|
8
|
+
* retriable same-key 503. Driver detail stays in the private cause and
|
|
9
|
+
* never enters HTTP bodies, MCP results or metadata. The classifier and
|
|
10
|
+
* success replay flag land in L2.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { CommitState } from './commit-contract.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Internal failure categories, distinct from ordinary typed
|
|
17
|
+
* validation/not-found/gone outcomes whose existing public mappings
|
|
18
|
+
* remain supported.
|
|
19
|
+
*/
|
|
20
|
+
export const FAILURE_CATEGORIES = [
|
|
21
|
+
'capability_unselected', 'provider_unavailable', 'conflict',
|
|
22
|
+
'unauthorized_at_commit', 'provider_error',
|
|
23
|
+
] as const;
|
|
24
|
+
|
|
25
|
+
export type FailureCategory = (typeof FAILURE_CATEGORIES)[number];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Frozen category-to-public projection (ask 3, table E2). `variant`
|
|
29
|
+
* selects the projection row inside one category. An absent
|
|
30
|
+
* `radarLedgerReason` means the rejection happens before dispatch and
|
|
31
|
+
* creates no write-commit ledger entry. `phase`/`commitState` are the
|
|
32
|
+
* projection defaults; the per-incident phase travels on ArtifactFailure.
|
|
33
|
+
*/
|
|
34
|
+
export interface FailureProjection {
|
|
35
|
+
readonly category: FailureCategory;
|
|
36
|
+
readonly variant?: string;
|
|
37
|
+
readonly httpStatus: number;
|
|
38
|
+
readonly publicCode: string;
|
|
39
|
+
readonly radarLedgerReason?: string;
|
|
40
|
+
readonly retriable: boolean;
|
|
41
|
+
readonly phase: string;
|
|
42
|
+
readonly commitState: CommitState;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const ORDINARY_FAILURE_CATEGORIES = [
|
|
46
|
+
'invalid_request', 'not_found', 'gone', 'unprocessable', 'verification_failed',
|
|
47
|
+
] as const;
|
|
48
|
+
|
|
49
|
+
export type OrdinaryFailureCategory = (typeof ORDINARY_FAILURE_CATEGORIES)[number];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Frozen ordinary domain-failure projection. Ordinary validation,
|
|
53
|
+
* not-found, gone and verification outcomes are entry rejections: they
|
|
54
|
+
* never enter the write-commit ledger and keep the existing public
|
|
55
|
+
* mappings.
|
|
56
|
+
*/
|
|
57
|
+
export interface OrdinaryFailureProjection {
|
|
58
|
+
readonly category: OrdinaryFailureCategory;
|
|
59
|
+
readonly httpStatus: number;
|
|
60
|
+
readonly publicCode: string;
|
|
61
|
+
readonly retriable: false;
|
|
62
|
+
readonly phase: string;
|
|
63
|
+
readonly commitState: CommitState;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const ORDINARY_FAILURE_PROJECTIONS: readonly OrdinaryFailureProjection[] = [
|
|
67
|
+
{
|
|
68
|
+
category: 'invalid_request', httpStatus: 400, publicCode: 'invalid_request',
|
|
69
|
+
retriable: false, phase: 'entry', commitState: 'not_committed',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
category: 'not_found', httpStatus: 404, publicCode: 'not_found',
|
|
73
|
+
retriable: false, phase: 'entry', commitState: 'not_committed',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
category: 'gone', httpStatus: 410, publicCode: 'gone',
|
|
77
|
+
retriable: false, phase: 'entry', commitState: 'not_committed',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
category: 'unprocessable', httpStatus: 422, publicCode: 'unprocessable',
|
|
81
|
+
retriable: false, phase: 'entry', commitState: 'not_committed',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
category: 'verification_failed', httpStatus: 422, publicCode: 'verification_failed',
|
|
85
|
+
retriable: false, phase: 'entry', commitState: 'not_committed',
|
|
86
|
+
},
|
|
87
|
+
] as const;
|
|
88
|
+
|
|
89
|
+
export const FAILURE_PROJECTIONS: readonly FailureProjection[] = [
|
|
90
|
+
{
|
|
91
|
+
category: 'capability_unselected', variant: 'unselected_operation',
|
|
92
|
+
httpStatus: 503, publicCode: 'capability_unavailable',
|
|
93
|
+
retriable: false, phase: 'selection', commitState: 'not_committed',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
category: 'provider_unavailable', variant: 'dispatch_failure',
|
|
97
|
+
httpStatus: 503, publicCode: 'capability_unavailable',
|
|
98
|
+
radarLedgerReason: 'provider_unavailable',
|
|
99
|
+
retriable: true, phase: 'dispatch', commitState: 'not_committed',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
category: 'conflict', variant: 'key_contention',
|
|
103
|
+
httpStatus: 409, publicCode: 'conflict',
|
|
104
|
+
radarLedgerReason: 'provider_conflict',
|
|
105
|
+
retriable: false, phase: 'lock', commitState: 'not_committed',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
category: 'unauthorized_at_commit', variant: 'policy_denial',
|
|
109
|
+
httpStatus: 403, publicCode: 'access_denied',
|
|
110
|
+
radarLedgerReason: 'unauthorized_at_commit',
|
|
111
|
+
retriable: false, phase: 'guard', commitState: 'not_committed',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
category: 'unauthorized_at_commit', variant: 'authority_unavailable',
|
|
115
|
+
httpStatus: 503, publicCode: 'capability_unavailable',
|
|
116
|
+
radarLedgerReason: 'unauthorized_at_commit',
|
|
117
|
+
retriable: true, phase: 'guard', commitState: 'not_committed',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
category: 'provider_error', variant: 'unclassified',
|
|
121
|
+
httpStatus: 500, publicCode: 'internal_error',
|
|
122
|
+
radarLedgerReason: 'provider_error',
|
|
123
|
+
retriable: false, phase: 'operation', commitState: 'not_committed',
|
|
124
|
+
},
|
|
125
|
+
] as const;
|
|
126
|
+
|
|
127
|
+
/** Classified internal failure with private cause and commit state. */
|
|
128
|
+
export interface ArtifactFailure {
|
|
129
|
+
readonly kind: 'operational';
|
|
130
|
+
readonly category: FailureCategory;
|
|
131
|
+
readonly variant?: string;
|
|
132
|
+
readonly httpStatus: number;
|
|
133
|
+
readonly publicCode: string;
|
|
134
|
+
readonly retriable: boolean;
|
|
135
|
+
readonly invocationId: string;
|
|
136
|
+
readonly operation: string;
|
|
137
|
+
readonly phase: string;
|
|
138
|
+
readonly commitState: CommitState;
|
|
139
|
+
readonly cause: unknown;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Ordinary typed domain failure with retained private cause. */
|
|
143
|
+
export interface OrdinaryFailure {
|
|
144
|
+
readonly kind: 'ordinary';
|
|
145
|
+
readonly category: OrdinaryFailureCategory;
|
|
146
|
+
readonly httpStatus: number;
|
|
147
|
+
readonly publicCode: string;
|
|
148
|
+
readonly retriable: false;
|
|
149
|
+
readonly invocationId: string;
|
|
150
|
+
readonly operation: string;
|
|
151
|
+
readonly phase: string;
|
|
152
|
+
readonly commitState: CommitState;
|
|
153
|
+
readonly cause: unknown;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Required success correlation fields: invocation identity, commit state
|
|
158
|
+
* and replay flag travel on every success alongside the result. Both HTTP
|
|
159
|
+
* and MCP consume this same result type.
|
|
160
|
+
*/
|
|
161
|
+
export const OUTCOME_SUCCESS_FIELDS = [
|
|
162
|
+
'ok', 'result', 'invocationId', 'commitState', 'replayed',
|
|
163
|
+
] as const;
|
|
164
|
+
|
|
165
|
+
/** Internal discriminated outcome shared by HTTP and MCP projections. */
|
|
166
|
+
export type ArtifactOutcome<T> =
|
|
167
|
+
| {
|
|
168
|
+
readonly ok: true;
|
|
169
|
+
readonly result: T;
|
|
170
|
+
readonly invocationId: string;
|
|
171
|
+
readonly commitState: CommitState;
|
|
172
|
+
readonly replayed: boolean;
|
|
173
|
+
}
|
|
174
|
+
| { readonly ok: false; readonly failure: ArtifactFailure | OrdinaryFailure };
|