Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: fruwehq/determa-state-conformance
ref: 531468c59c7a2dc32f5cbe92cfabf89805d27f6a
ref: 99a4d9ad5256f7330e75b06d48f340cc7239a40d
path: .pinned/determa-state-conformance
- name: Check out pinned specification
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: fruwehq/determa-state-spec
ref: 2e33036563cb966b07124197db672159b4b7e1f4
ref: ee38796d5e38e67e350a06548fd50faa530cbb12
path: .pinned/determa-state-spec
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
Expand Down
19 changes: 11 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,22 @@ package so it can coexist with the umbrella `determa` launcher.
The implementation is conformant only when it passes the language-neutral suite.
The implementation uses these immutable inputs:

- specification: `2e33036563cb966b07124197db672159b4b7e1f4`;
- conformance: `531468c59c7a2dc32f5cbe92cfabf89805d27f6a` (114 core cases,
108 persistence vectors, 12 persistence-profile steps, 99 execution-checkpoint
vectors, 106 version-2 vectors, and 101 closed-code registry entries).
- specification: `ee38796d5e38e67e350a06548fd50faa530cbb12`;
- conformance: `99a4d9ad5256f7330e75b06d48f340cc7239a40d` (98 format-1
core cases, 162 version-2 vectors, 138 durable-host vectors, and 381 generated
version-2 artifacts).

The package metadata remains `0.2.0`.

## Boundaries

The pure public API remains `load_bundle`, `create`, and `dispatch`, plus validation
and error types exported by `determa.state`. The optional synchronous `ExecutionHost`
and execution-store APIs wrap that core without changing its exact `format: 1`
grammar. Do not restore abandoned draft field names or compatibility aliases.
The pure public API is `load_bundle`, `create`, `admit`, and `step`, plus validation
and error types exported by `determa.state`. Creation, admission, and stepping operate
only on queue-bearing aggregate state; the mailbox-free engine helpers are private.
Portable artifacts and checkpoints support
schema version 2 only. The optional synchronous `ExecutionHost` and execution-store
APIs wrap that core without changing its exact `format: 1` machine grammar. Do not
restore abandoned draft field names, artifact version 1 paths, or compatibility aliases.

The core is a pure foreground transform over one root ownership aggregate. It has no
hidden queues, timers, stores, or standardized execution CLI. Portable aggregate
Expand Down
86 changes: 50 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ Python implementation of [Determa State](https://github.com/fruwehq/determa-stat
a language-agnostic statechart engine with a shared normative conformance suite.

This implementation supports Determa State `format: 1` at specification
commit `2e33036563cb966b07124197db672159b4b7e1f4`. Correctness is determined by
the 114-case core suite, 108 persistence vectors, 12 persistence-profile steps,
99 execution-checkpoint vectors, 106 version-2 vectors, and 101 closed-code registry
entries at conformance commit `531468c59c7a2dc32f5cbe92cfabf89805d27f6a`.
commit `ee38796d5e38e67e350a06548fd50faa530cbb12`. Correctness is determined by
98 format-1 core cases, 162 version-2 vectors, 138 durable-host vectors, and 381 generated version-2 artifacts
at conformance commit `99a4d9ad5256f7330e75b06d48f340cc7239a40d`.

The package metadata remains `0.2.0`. The implementation includes the portable
execution-checkpoint host, selected legacy artifact decoding, and authoritative portable
code sets.
The package metadata remains `0.2.0`. Artifact and checkpoint schema version 2 is the
only supported portable artifact format. Machine YAML remains `format: 1`.

## Install

Expand Down Expand Up @@ -73,8 +71,9 @@ names are not accepted.

## Use The Library

`create` and `dispatch` are pure foreground operations. They do not retain hidden
machine state or call queues, timers, databases, or remote services.
`create`, `admit`, and `step` are pure foreground operations over explicit,
queue-bearing aggregate state. They do not retain hidden machine state or call
databases or remote services.

```python
from pathlib import Path
Expand All @@ -91,38 +90,55 @@ created = ds.create(
)
state = created["state"]

resolver = ds.MemoryArtifactResolver(definitions={bundle.fingerprint: bundle})

target = {
"root": {
"root_instance_id": state["root_instance_id"],
"root_runtime_id": state["root_runtime_id"],
}
}
result = ds.dispatch(
bundle,
envelope = ds.portable_envelope(
"increment",
"counter-42:increment:1",
target,
{"amount": 2},
)
delivery = {
"delivery_mode": "input",
"envelope": envelope,
"envelope_digest": ds.delivery_request_digest(
"counter-42", "input", envelope
),
}
admitted = ds.admit(
state,
{
"input": {
"event": "increment",
"event_id": "counter-42:increment:1",
"target": target,
"payload": {"amount": 2},
}
},
[delivery],
resolver,
)
result = ds.step(admitted["state"], state["root_runtime_id"], resolver)

assert result["status"] == "running"
assert result["disposition"] == "handled"
state = result["state"]
root = state["runtimes"][state["root_runtime_id"]]
assert root["scopes"]["root"]["count"] == 2
root = next(
runtime
for runtime in state["runtimes"]
if runtime["runtime_id"] == state["root_runtime_id"]
)
count = next(
variable
for variable in root["variables"]
if variable["variable_declaration_pointer"].endswith("/count")
)
assert count["value"] == ["integer", "2"]
```

Both calls return all result fields: `status`, `disposition`, `state`, `emissions`,
`fault`, and `rejection` (`create` has a null disposition). The caller owns delivery:
the core processes at most one supplied envelope and does not place it in an internal
queue. Successful processing returns a new JSON-compatible logical aggregate while
leaving the supplied prior state unchanged. Rejections and unhandled deliveries return
the exact supplied state object.
`create` initializes the aggregate, `admit` atomically appends accepted deliveries to
its runtime mailboxes, and `step` processes at most the selected runtime's ready head.
Ready and deferred mailboxes are literal portable aggregate state, so queued work
survives serialization and restoration. Each operation returns a new JSON-compatible
logical aggregate while leaving the supplied prior state unchanged.

`load_bundle` also accepts a native Python mapping through the same structural and
semantic validation path. Native values must satisfy the same portable Unicode and
Expand All @@ -134,7 +150,7 @@ definitions.

## Persist And Migrate

`serialize_aggregate` produces the canonical §16 aggregate artifact. Restoration
`serialize_aggregate` produces the canonical schema-v2 aggregate artifact. Restoration
resolves its exact validated definition by fingerprint and fails closed when the
definition is absent or untrusted:

Expand All @@ -145,11 +161,9 @@ restored = ds.restore_aggregate(encoded, resolver)
```

`restore_aggregate_package` verifies a self-contained transport package and seeds a
mutable resolver without replacing existing content. `migrate_aggregate` applies an
exact trusted descriptor route as a pure operation. `migrate_and_dispatch` returns one
commit-ready migration, audit, dispatch, aggregate, and outbox-intent boundary. Failed
migrations return a deterministic `MigrationFailure` and do not mutate the supplied
artifact or resolver.
mutable resolver without replacing existing content. `migrate_aggregate_v2` applies an
exact trusted descriptor route as a pure operation. Failed migrations do not mutate the
supplied artifact or resolver.

Definition and descriptor resolvers are protocols, so applications can back them with
an immutable registry or a transaction-local cache.
Expand All @@ -167,7 +181,7 @@ store.setup_schema() # always explicit
resolver = ds.MemoryArtifactResolver(definitions={bundle.fingerprint: bundle})
host = ds.ExecutionHost(store, resolver)

created = host.create(
created = host.create_v2(
bundle,
machine_id="counter",
root_instance_id="counter-42",
Expand Down Expand Up @@ -241,9 +255,9 @@ configuration. Root checkpoint deletion is unsupported.
failure propagation, and cleanup cascades;
- atomic RTC rollback, deterministic identities/counters, pure inspection, and
incompatible or malformed prior-state rejection;
- canonical aggregate serialization/restoration, portable typed values, package
- schema-v2 aggregate serialization/restoration, portable typed values, package
attachments, exact definition resolution, trusted lazy migration, deterministic
audits, resource limits, and atomic migrate-and-dispatch results.
audits, and resource limits;
- strict portable execution-checkpoint parsing, canonical digests, semantic
validation, synchronous transaction/CAS/replay orchestration, receipts, pending
delivery, outbox lifecycle, replay retention, and root tombstones;
Expand Down
Loading