diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a2a1dd..da30fd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 0.1.0 — stable + +- Promote the accepted three-operation component without runtime, WIT or + dependency changes; the stable manifest produces a new package identity. +- The official locked 0.1.0-rc.1 passed CAPI caller-replacement acceptance on + 2026-09-10: five profiles, ten scenarios, 319 unchanged assertions and both + exact expected-RED fingerprints. This is evidence for the RC; stable + publication and its exact artifact verification remain separate gates. +- Document the fixed Start identity, internal RPC aliases, per-filter History + flags and timeout ceilings, and empty-cache acquisition with an existing lock. +- Requires Sigil >=0.35.0, <0.36.0 and Host API 1.3; no compatibility claim for + Sigil 0.34.x or 0.36.x. + ## 0.1.0-rc.1 — release candidate - Bounded WebAssembly Temporal WorkflowService client for Start, Describe and @@ -11,5 +24,5 @@ - Main-only immutable keyless-provenance release pipeline, separate from explicitly non-gating local development packages. -This is not a claim of real CAPI caller replacement acceptance. That acceptance -must use the official locked RC and precedes stable Temporal promotion. +At RC publication, real CAPI caller replacement acceptance remained open. The +subsequent accepted run is recorded above; it used the ordinary official lock. diff --git a/README.md b/README.md index 1134478..e649bc2 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ # Sigil Temporal plugin -Release candidate for the measured three-operation `wasm.temporal` component: +The measured three-operation `wasm.temporal` component: start, describe and caller-paginated history. -The candidate is **0.1.0-rc.1**, requiring stable **Sigil 0.35.x** and Host API +Version **0.1.0** requires stable **Sigil 0.35.x** and Host API 1.3/schema 4. A version in this checkout is not evidence that its GitHub release -exists. CAPI's real caller replacement acceptance remains open; an official RC -enables that gate through normal project execution. Routing, authority, TLS policy, credentials and transport limits belong +exists. The official locked **0.1.0-rc.1** passed CAPI caller-replacement +acceptance on **2026-09-10**: five profiles, ten scenarios, 319 unchanged +assertions and both exact expected-RED fingerprints. Stable promotion preserves +the component bytes but creates a new manifest/package identity requiring its +own reviewed publication and verification. Routing, authority, TLS policy, +credentials and transport limits belong to the operator-frozen Sigil host profile. The component receives none of them. It performs no retries, redirects, reconnections, sleeps or implicit pagination. @@ -49,6 +53,51 @@ compatibility, Sigil's scenario-level sticky-fault behavior, or CAPI acceptance. The optional project-side polling helper and its caller obligations are in [`examples/README.md`](examples/README.md). It is not an additional WIT export. +## Operator and caller contract + +The Lua export names are `start-workflow-execution`, +`describe-workflow-execution`, and `get-workflow-execution-history`. The host +profile's RPC keys are a **different namespace**: the plugin sends exactly +`start`, `describe`, and `history`. Within an existing profile named `example`, +use these entries (this is a fragment, not a complete operator configuration): + +```toml +[plugins.grants.temporal.grpc.example.rpcs] +start = { path = "/temporal.api.workflowservice.v1.WorkflowService/StartWorkflowExecution", kind = "mutation" } +describe = { path = "/temporal.api.workflowservice.v1.WorkflowService/DescribeWorkflowExecution", kind = "read" } +history = { path = "/temporal.api.workflowservice.v1.WorkflowService/GetWorkflowExecutionHistory", kind = "read" } +``` + +The existing profile's `request_policy` must use +`identity = "sigil-temporal@0.1.0"`. This is the plugin's fixed protobuf Start +identity, including for RC packages, not an operator-chosen client name or the +host-owned `client-version` metadata. Keep namespace, workflow-id prefix, +workflow type and task queue confined to the intended caller. The scenario's +`profile` must name that same configured profile. + +Keep the network endpoint referenced by the gRPC profile: it supplies the +host-owned route, **not raw network authority for the guest**. Sigil 0.35.0 may +warn that this network grant is irrelevant even though the gRPC route is +retained. Do not remove the route or add raw `network`/`secrets` capability to +silence that warning; those capabilities cannot coexist with semantic +`grpc-unary`. Authorize only the official source for `grpc-unary`, without +widening installation or third-party policy. + +History accepts exactly these request shapes; each call returns one page: + +| `filter` | `wait-new-event` | `skip-archival` | Maximum `timeout-millis` | +| --- | --- | --- | --- | +| `close-event` | `true` | `true` | 65000 | +| `all-events` | `false` | `false` | 10000 | + +Start and Describe also cap `timeout-millis` at 10000. Timeouts must be positive; +the host's operator, scenario and outer-call deadlines can shorten them further. +A 10000 ms timeout is admissible for all operations, but is not a guarantee of +that much execution time. Close-event history long-polls; reading a still-running +workflow may reach the deadline. Follow nonempty page tokens explicitly, keep +Start single-shot with one caller-owned request ID, and propagate infrastructure +errors rather than converting them into expected product failures. + ## Local packaging (non-gating) `plugin.local.toml` is an explicit **local development manifest**, not release @@ -135,14 +184,22 @@ without altering protobuf payloads or the interface contract. does not rebuild. It verifies a draft readback, emits keyless GitHub OIDC provenance and checks the immutable public release and its asset hashes. Existing versions are burned rather than overwritten or republished. -5. Install the exact official RC from a fresh cache, then add and sync it: +5. Acquire the exact published version and verify its official lock. For an + existing project lock with an empty cache, sync that lock **before** add: ```sh - sigil plugin install temporal@0.1.0-rc.1 - sigil plugin add temporal@0.1.0-rc.1 + sigil plugin sync + sigil plugin install temporal@0.1.0 + sigil plugin add temporal@0.1.0 sigil plugin sync ``` + These commands require the stable release to exist. During RC acceptance, + use the exact approved RC version instead. Omit the first sync when there + is no existing lock; omit add when the lock already selects the intended + version. Add resolves the whole project lock, so first populate dependencies + already pinned there rather than assuming an empty cache contains them. + Sigil 0.35.0's `add` grants project access and can acquire a missing package through the verified remote-install path. Explicit install-first is recommended here to expose acquisition evidence separately and keep the diff --git a/RELEASING.md b/RELEASING.md index ddbc873..0f873c3 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -54,6 +54,10 @@ publication job or erase a draft/tag/attestation to reuse the identity. The public RC must be discoverable by exact version, installed from an empty cache with official-github-provenance-v1, then added to a project lock and synced. +When the project already has a lock, sync it first to populate its existing +dependencies before adding the intended version. Add resolves the whole lock; +omit add if it already selects that version. Apply the same acquisition order +when verifying the stable package, without substituting its RC package identity. Explicit install-first separates acquisition evidence for auditing and retains compatibility with older hosts; it is not a limitation of Sigil 0.35.0, whose `plugin add` can acquire a missing package through verified remote installation. @@ -65,3 +69,12 @@ expected-RED fingerprints with pinned service/rig identities. No non-gating After acceptance, stable 0.1.0 is a new independently reviewed candidate and publication. Promotion does not mutate the immutable prerelease or claim its package digest is unchanged when the manifest version changes. + +The official locked 0.1.0-rc.1 received CAPI caller-replacement acceptance on +2026-09-10 (five profiles, ten scenarios, 319 unchanged assertions, both exact +expected-RED fingerprints). This closes the RC service-acceptance prerequisite, +not the stable exact-candidate review or publication gate. Stable preparation +must reproduce the accepted component BLAKE3 +`b25139ed2e6eeab88ed26f8e306621cd83869670084f986e481143b57acc372d`; +retain the new package/manifest identities separately. The operator/caller +requirements that enabled acceptance are documented in the README. diff --git a/plugin.toml b/plugin.toml index bd0931f..9c347bb 100644 --- a/plugin.toml +++ b/plugin.toml @@ -1,7 +1,7 @@ -# Official release candidate; CAPI acceptance remains a separate gate. +# Stable release manifest; publication remains an independently reviewed gate. schema_version = 4 name = "temporal" -version = "0.1.0-rc.1" +version = "0.1.0" description = "Bounded Temporal WorkflowService client" license = "MIT" diff --git a/tests/test_release_pack.py b/tests/test_release_pack.py index 22b4640..efbfb51 100644 --- a/tests/test_release_pack.py +++ b/tests/test_release_pack.py @@ -19,11 +19,12 @@ class ReleaseContractTests(unittest.TestCase): def test_release_manifest_keeps_local_and_official_versions_separate(self): data = (ROOT / "plugin.toml").read_bytes() - self.assertEqual(release.validate_manifest(data)["version"], "0.1.0-rc.1") - release.validate_manifest(data.replace(b"0.1.0-rc.1", b"0.1.0")) + self.assertEqual(release.validate_manifest(data)["version"], "0.1.0") + version_line = b'version = "0.1.0"' + release.validate_manifest(data.replace(version_line, b'version = "0.1.0-rc.1"')) for version in (b"0.1.0-dev.1", b"0.1.0-rc.0", b"0.1.0-rc.01", b"0.1.0+build", b"0.2.0"): with self.subTest(version=version), self.assertRaises(ValueError): - release.validate_manifest(data.replace(b"0.1.0-rc.1", version)) + release.validate_manifest(data.replace(version_line, b'version = "' + version + b'"')) with self.assertRaises(ValueError): release.validate_manifest((ROOT / "plugin.local.toml").read_bytes()) with self.assertRaises(ValueError):