@fabricorg/platform-host 5.0.0 → 6.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,89 @@
1
1
  # @fabricorg/platform-host
2
2
 
3
+ ## 6.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - fb14d67: Add versioned PostgreSQL migrations and live PostgreSQL 16 certification. Fence and renew invocation
8
+ and outbox leases, bound publisher calls, reject stale finalization, and add deterministic Temporal
9
+ saga workflow binding with explicit parent lifecycle activities.
10
+
11
+ ### Minor Changes
12
+
13
+ - ef4e3eb: Classify adapter outcomes: add additive `AdapterOutcomeKind` (`success`, `transient_failure`,
14
+ `permanent_failure`, `timeout`, `ambiguous`) and `AdapterOutcomeEvidence` to the portable adapter
15
+ contract. The retry layer now retries only transient idempotent outcomes; permanent failures,
16
+ timeouts, and ambiguous results are never retried. Ambiguous outcomes are routed to
17
+ `reconciliation_required` with durable `ExternalReconciliation` evidence and an
18
+ `AdapterInvocationAmbiguous` lifecycle event. Add deadline and cancellation context to
19
+ `AdapterExecutionContext`. Backward compatible: adapters that omit `outcome` behave as before.
20
+ - 293561d: Close adapter and saga correctness gaps: deliver actual deadline and AbortSignal context to adapters, require the governed Host to classify thrown adapter failures before retrying, reject contradictory successful ambiguous outcomes, reserve parent lifecycle identity prefixes, and provide a durable Platform Host parent lifecycle adapter tested through public store seams.
21
+ - 9a145a3: Add assembly and initiating SDUI release digests to durable governance runtime
22
+ evidence. Hosts can attach both through trusted configuration, including a
23
+ per-submission release resolver, and validate digest shape before creating the
24
+ invocation. Composition-bound hosts verify their assembly lockfile against the
25
+ exact loaded capability artifacts before accepting submissions.
26
+ - 4e585a4: Close final production-maturity correctness gaps: expired leases can no longer be renewed,
27
+ saga parent transitions are serialized, atomic, terminal-safe, and retry-idempotent by lifecycle identity, hydrated
28
+ projections must meet requested checkpoints, and SDUI releases accept components only from
29
+ resolved document packs. The release gate now verifies every package and public subpath in
30
+ isolated ESM, CommonJS, and TypeScript consumers.
31
+ - 12ffe62: Add vendor-neutral lifecycle telemetry and tenant-safe health/readiness snapshots for workers,
32
+ leases, outbox delivery, reconciliation-required work, and approval waits. Health output contains
33
+ aggregate counts only and does not expose invocation parameters or secrets.
34
+ - fb14d67: Execute state guards with trusted Host context, correct filtered-ledger replay semantics, make
35
+ dispatcher failure retryable, fail closed for unsafe saga and non-idempotent recovery paths, preserve
36
+ outbox consistency, and bound and redact OPA HTTP failures.
37
+ - 5b6ccd5: Certification evidence now invokes real public Host/store/worker/outbox APIs instead of toy simulations. Fault and load certification exercises `createGovernedActionHost`, `MemoryPlatformHostStore`, `runPlatformActionWorkerCycle`, and `runOutboxRelayCycle` through their public seams. The machine-readable report includes a `pendingGates` section that distinguishes local passes from required external evidence. Publish and verify scripts include `@fabricorg/assembly` and `@fabricorg/sdui-release` in dependency-safe order. A new `pnpm verify:packed` script packs all public packages and installs them into clean ESM and CommonJS consumers using only declared dependencies.
38
+
39
+ ### Patch Changes
40
+
41
+ - Updated dependencies [62dd1ca]
42
+ - @fabricorg/assembly@0.2.0
43
+
44
+ ### Additional release notes
45
+
46
+ ### Major Changes
47
+
48
+ - Add checksummed forward-only PostgreSQL migrations, live PostgreSQL 16 CI certification, renewable
49
+ fenced invocation and outbox leases, transaction-scoped fenced finalization, and bounded outbox
50
+ publishing. Recoverable stores now implement lease renewal and fenced update operations, and
51
+ dispatch inputs include the action ID required for deterministic workflow binding. Host also
52
+ declares `@fabricorg/assembly` as a peer used to verify composition-bound module artifacts; see
53
+ `MIGRATION-6-PRODUCTION.md`.
54
+
55
+ ### Minor Changes
56
+
57
+ - Add vendor-neutral lifecycle telemetry and tenant-safe health/readiness snapshots for workers,
58
+ invocation and outbox leases, backlog, dead letters, reconciliation-required work, and approval
59
+ waits. Health output contains aggregate counts only and reports unavailable sources as unhealthy.
60
+ - Classify adapter outcomes: route ambiguous adapter results to `reconciliation_required` with
61
+ durable `ExternalReconciliation` evidence and an `AdapterInvocationAmbiguous` lifecycle event.
62
+ Permanent failures and timeouts are not retried. Add `adapter_reconciliation` column to the
63
+ PostgreSQL action invocations table.
64
+ - Deliver actual deadline and `AbortSignal` context to adapters via `adapterDeadlineMs` host option.
65
+ The governed Host explicitly classifies thrown adapter failures before retrying (`classifyThrownError`).
66
+ Reject contradictory successful ambiguous outcomes via `assertAdapterOutcomeConsistency`.
67
+ - Add `createDurableSagaParentLifecycle` adapter: a Platform Host store-backed implementation of
68
+ the saga parent lifecycle contract (progress, approval, cancellation, completion, failure) that
69
+ survives worker rollover through durable store seams.
70
+ - Refuse invocation and outbox lease renewal after expiry. Make saga parent lifecycle events and
71
+ parent status transitions atomic, idempotent by stable lifecycle identity, and contradictory-evidence
72
+ safe. Serialize concurrent transitions with a transaction-scoped parent lock and prevent distinct
73
+ transitions from rewriting terminal parent state.
74
+ - Evaluate state-machine guards with authoritative state and trusted context inside the mutation
75
+ unit of work, with an optional transaction-scoped state reader for custom stores.
76
+ - Keep failed dispatcher submissions pending and allow idempotent resubmission to retry the stable
77
+ workflow ID.
78
+ - Fail closed for direct saga execution and interrupted inline non-idempotent actions.
79
+ - Workers no longer claim unleased `running` rows created by inline execution, saga lifecycle retries
80
+ use an identity-scoped event lookup while holding the parent lock, and completed adapters clear
81
+ their deadline timers.
82
+ - Preserve provisional consistency when PostgreSQL outbox rows are hydrated.
83
+ - Persist verified assembly and initiating promoted-release digests with every
84
+ composition-bound invocation. Registry module artifacts are checked against
85
+ the approved lockfile before Host accepts submissions.
86
+
3
87
  ## 5.0.0
4
88
 
5
89
  ### Major Changes
@@ -0,0 +1,59 @@
1
+ # Migrating `@fabricorg/platform-host` 5.x to 6.x
2
+
3
+ Host 6 makes production recovery fencing explicit and adds optional,
4
+ assembly-verified runtime composition.
5
+
6
+ ## Install the composition peer
7
+
8
+ Host now imports `@fabricorg/assembly` for lockfile and runtime artifact
9
+ verification. Install a compatible release beside Host:
10
+
11
+ ```sh
12
+ pnpm add @fabricorg/platform-host@^6 @fabricorg/assembly@^0.3 @fabricorg/platform@^1.2
13
+ ```
14
+
15
+ ## Update recoverable stores and dispatchers
16
+
17
+ - Implement the new invocation and outbox lease-renewal and fenced-update
18
+ methods on custom recoverable stores.
19
+ - Implement `getEvent()` on custom atomic mutation transactions so saga
20
+ lifecycle retries perform an identity-scoped lookup instead of scanning the
21
+ tenant event ledger.
22
+ - Preserve monotonic lease tokens and reject writes from expired or superseded
23
+ claims.
24
+ - Include `actionId` in custom dispatcher inputs so workflow identity remains
25
+ deterministic.
26
+ - Run `PostgresPlatformHostStore.ensureSchema()` through your controlled
27
+ migration process before starting Host 6 workers.
28
+ - Treat applied migration checksums as immutable. Add a new migration version
29
+ for every schema change instead of editing the version 1 baseline.
30
+
31
+ Memory and PostgreSQL adapters included with Host already satisfy these
32
+ contracts.
33
+
34
+ ## Opt into verified composition
35
+
36
+ Existing hosts can omit `composition`. To bind runtime execution to an approved
37
+ application assembly:
38
+
39
+ 1. Set `version` and `manifestDigest` on every registered `FabricModule`.
40
+ 2. Pass the integrity-checked `AssemblyLockfile` through `composition.assembly`.
41
+ 3. Resolve an initiating promoted release digest from trusted gateway or release
42
+ registry context, never from action parameters.
43
+
44
+ ```ts
45
+ const host = createGovernedActionHost({
46
+ registry,
47
+ store,
48
+ authorization,
49
+ composition: {
50
+ assembly,
51
+ resolveInitiatingReleaseDigest: (submission) =>
52
+ releaseRegistry.resolveTrustedDigest(submission.provenance),
53
+ },
54
+ });
55
+ ```
56
+
57
+ Host refuses to start when a registered module's namespace, version, or
58
+ compiler manifest digest differs from the approved lockfile. Resolver failures
59
+ and invalid release digests fail before durable invocation creation.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  The canonical host for the `@fabricorg/platform` mutation pipeline.
4
4
 
5
5
  ```bash
6
- pnpm add @fabricorg/platform@^1.0.0 @fabricorg/platform-host@^4.0.0
6
+ pnpm add @fabricorg/platform@^1.2.0 @fabricorg/platform-host@^6.0.0 @fabricorg/assembly@^0.3.0
7
7
  ```
8
8
 
9
9
  ## AI agent integration boundary
@@ -15,7 +15,7 @@ registration, and submit stable idempotent commands. The gateway derives tenant
15
15
  calls `submitAction()`; an agent must never call handlers, adapters, workflow internals, or database
16
16
  writes directly.
17
17
 
18
- See the [Platform Host 4.x agent integration
18
+ See the [Platform Host 6.x agent integration
19
19
  guide](https://platform.fabric.pro/docs/platform/reference/platform-host) for application wiring,
20
20
  execution-time authorization, the PostgreSQL transaction binder, recovery semantics, and the external
21
21
  gateway contract.
@@ -41,8 +41,10 @@ injected `now` clock is also passed to ordinary policy evaluation.
41
41
 
42
42
  Production polling workers can use `createStoreBackedActionDispatcher()` plus
43
43
  `runPlatformActionWorker()`. The pending invocation row is the durable queue item; workers claim
44
- bounded batches with an atomic lease and `FOR UPDATE SKIP LOCKED`, and an expired `running` lease is
45
- recoverable after interruption. Pass a stable `idempotencyKey` to `submitAction()` so retries resolve
44
+ bounded batches with an atomic, renewable lease and `FOR UPDATE SKIP LOCKED`. Every claim advances a
45
+ fencing token. A heartbeat may renew only a lease that has not expired; after expiry, the work must be
46
+ reclaimed so its token advances. An expired worker cannot finalize after another worker takes ownership. Pass a stable
47
+ `idempotencyKey` to `submitAction()` so retries resolve
46
48
  to the original tenant-scoped invocation instead of creating another mutation.
47
49
 
48
50
  New rows bind that key to the action version, actor/authority binding, and a versioned canonical
@@ -56,6 +58,13 @@ adapter that already reached `succeeded`, and event, policy, and adapter writes
56
58
  stale action that was not declared idempotent fails terminally for manual reconciliation instead of
57
59
  silently rerunning unknown side effects.
58
60
 
61
+ Adapters may classify outcomes as `success`, `transient_failure`, `permanent_failure`, `timeout`,
62
+ or `ambiguous`. The retry layer retries only transient idempotent outcomes; permanent failures,
63
+ timeouts, and ambiguous results are never retried. An ambiguous outcome — where the external effect
64
+ may or may not have been applied — is routed to `reconciliation_required` with durable
65
+ `ExternalReconciliation` evidence and an `AdapterInvocationAmbiguous` lifecycle event rather than
66
+ blindly retrying an unsafe effect.
67
+
59
68
  ## Execution authorization
60
69
 
61
70
  Submission authorization proves that a caller may create an invocation. Applications that delegate
@@ -121,6 +130,13 @@ application knows how its `TDb` is rebound to the transaction's SQL client.
121
130
  Stores without this additive capability retain the legacy boundary for compatibility and must not
122
131
  claim atomic domain-write/event persistence.
123
132
 
133
+ `createDurableSagaParentLifecycle()` requires this capability. Each parent lifecycle transition uses a
134
+ stable `lifecycleId` as its causation identity, appends its audit event, and updates the parent invocation
135
+ in the same transaction. The transaction locks the parent invocation row before checking prior
136
+ evidence, so concurrent transitions serialize. Retrying the same identity and evidence is a no-op;
137
+ reusing it with contradictory evidence fails closed, and a distinct transition cannot rewrite a
138
+ terminal parent. `createSagaParentActivities()` supplies deterministic identities for Temporal callers.
139
+
124
140
  Actions can optionally declare `resultSchema`. The Host validates public handler result data after
125
141
  the handler and before canonical event append or adapter execution. Invalid results fail the invocation,
126
142
  emit no domain/completion event, invoke no adapter, and persist only a sanitized validation failure.
@@ -191,9 +207,18 @@ observations. Provider-specific authentication and clients do not belong here. F
191
207
  the optional Platform integration exported by `@fabric-harness/databricks`.
192
208
 
193
209
  Every newly submitted invocation also records `runtimeEvidence`. The host always supplies the
194
- portable governance and host contract generations; applications should add the deployed host
195
- package version, policy ruleset version, and provider bridge identity. This makes an audit record
196
- explain which contract and provider adapter governed a mutation after dependencies have moved on.
210
+ portable governance and host contract generations; applications should add the
211
+ deployed host package version, policy ruleset version, and provider bridge
212
+ identity. A trusted `composition` configuration attaches the verified assembly
213
+ digest and can resolve the promoted SDUI release that initiated each submission.
214
+ Neither identity is accepted directly from caller input. This makes an audit
215
+ record explain which composition, release, contract, and provider adapter
216
+ governed a mutation after dependencies have moved on.
217
+
218
+ Composition-bound hosts require an explicit module registry. Every registered
219
+ module must carry its published `version` and compiler `manifestDigest`; Host
220
+ compares those identities directly with the integrity-checked assembly lockfile
221
+ before accepting any submission.
197
222
 
198
223
  ```ts
199
224
  const host = createGovernedActionHost({
@@ -203,19 +228,72 @@ const host = createGovernedActionHost({
203
228
  policyRulesetVersion: "gtm-rules.v8",
204
229
  providerBridge: { name: "@fabric-harness/databricks", version: "1" },
205
230
  },
231
+ composition: {
232
+ assembly: assemblyLockfile,
233
+ resolveInitiatingReleaseDigest: (submission) =>
234
+ releaseRegistry.resolveTrustedDigest(submission.provenance),
235
+ },
206
236
  });
207
237
  ```
208
238
 
209
239
  `MemoryPlatformHostStore` is for tests and local demos. Production control planes use
210
- `PostgresPlatformHostStore` with Databricks Lakebase (or standard Postgres), call
211
- `ensureSchema()` at startup, and hydrate projections from `listEvents()`.
240
+ `PostgresPlatformHostStore` with Databricks Lakebase (or standard Postgres), run `ensureSchema()` in a
241
+ controlled deployment migration step, and hydrate projections from `listEvents()`. `ensureSchema()`
242
+ uses ordered immutable checksums, a migration ledger, and a transaction-scoped advisory lock. It
243
+ remains safe to call at startup, but deployment-owned migrations are preferred.
244
+
245
+ State-machine transition checks run inside the Host mutation unit of work. Transaction-capable
246
+ stores should expose transaction-scoped `getEntityState()` so the guard reads the same database
247
+ snapshot used by the handler and event append. Existing custom stores without that optional
248
+ transaction method fall back to the store-level authoritative read.
249
+
250
+ ## Runtime observability and health
251
+
252
+ Platform Host exposes vendor-neutral lifecycle telemetry through the optional `telemetry` sink on
253
+ `createGovernedActionHost()`, `runPlatformActionWorkerCycle()`, and `runOutboxRelayCycle()`. A sink can
254
+ be a callback or `{ record }`; it receives stable event and metric names from
255
+ `PLATFORM_HOST_METRIC_NAMES`. Telemetry is best effort and never changes mutation or checkpoint
256
+ behavior.
257
+
258
+ ```ts
259
+ import {
260
+ getPlatformHostHealthSnapshot,
261
+ PLATFORM_HOST_METRIC_NAMES,
262
+ } from "@fabricorg/platform-host";
263
+
264
+ const health = await getPlatformHostHealthSnapshot({
265
+ store,
266
+ tenantId,
267
+ spaceId,
268
+ workers: [{ lastHeartbeatAt, staleAfterMs: 30_000 }],
269
+ telemetry: (record) => { metrics.record(record); },
270
+ });
271
+ ```
272
+
273
+ The snapshot contains only scoped aggregate counts for invocation backlog, running and expired
274
+ leases, approval waits, reconciliation-required work, outbox backlog, expired leases, and dead
275
+ letters. It never returns invocation parameters, results, actor data, payloads, or errors. An
276
+ unavailable health source is reported as `unhealthy` and not ready; stale workers and operational
277
+ lease/dead-letter/reconciliation findings are represented by readiness reason codes.
278
+
279
+ Dispatcher submission failures leave the durable invocation `pending`. Re-submit the same
280
+ idempotency key to retry the stable workflow ID, or use a store-backed worker. The Host never marks
281
+ a logical mutation failed merely because a queue or workflow service was temporarily unavailable.
282
+
283
+ Saga actions require a durable workflow dispatcher and a worker-side `SagaImplementation` mapping.
284
+ Calling `executeInvocation()` for a saga through the atomic Host path fails closed. Interrupted
285
+ inline actions that are not declared idempotent also fail closed and require manual reconciliation.
286
+
287
+ CI certifies concurrent migrations, command idempotency, and schema behavior against PostgreSQL 16.
288
+ Lakebase failover, backup, and restore remain adopter certification gates and are not implied by the
289
+ PostgreSQL CI result.
212
290
 
213
291
  ## Durable outbox egress
214
292
 
215
293
  Configure `outbox` on `createGovernedActionHost()` only with an `OutboxPlatformHostStore`.
216
294
  The Host then uses `appendEventWithOutbox` so the canonical event and delivery record share
217
- the proven event transaction. `runOutboxRelayCycle()` leases and publishes records with retry
218
- and dead-letter handling. Delivery is at least once: consumers deduplicate on immutable
295
+ the proven event transaction. `runOutboxRelayCycle()` uses renewable fenced leases, a bounded
296
+ publisher deadline, retry, and dead-letter handling. Delivery is at least once: consumers deduplicate on immutable
219
297
  `eventId`. Payload classification is metadata, not a redaction mechanism; event payloads must
220
298
  already be audit-safe.
221
299